|
@@ -1,320 +0,0 @@
|
|
|
-export default {
|
|
|
- // 音频播放器实例
|
|
|
- player: null,
|
|
|
-
|
|
|
- // 当前运行环境检测
|
|
|
- isApp: null,
|
|
|
-
|
|
|
- // 初始化方法
|
|
|
- init() {
|
|
|
- // 检测运行环境
|
|
|
- const systemInfo = uni.getSystemInfoSync();
|
|
|
- this.isApp = systemInfo.platform === 'ios' || systemInfo.platform === 'android';
|
|
|
-
|
|
|
- // 初始化播放器
|
|
|
- if (!this.player) {
|
|
|
- this.player = uni.createInnerAudioContext();
|
|
|
- this.player.obeyMuteSwitch = false; // iOS静音模式下也可播放
|
|
|
- this.player.onError((err) => {
|
|
|
- console.error('音频播放器错误:', err);
|
|
|
- uni.showToast({
|
|
|
- title: '播放失败,请稍后再试',
|
|
|
- icon: 'none',
|
|
|
- duration: 2000
|
|
|
- });
|
|
|
- });
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 从单词数据中提取所有音频信息(完整实现)
|
|
|
- extractAudioInfo(wordData) {
|
|
|
- if (!wordData || typeof wordData !== 'object') {
|
|
|
- console.error('无效的单词数据');
|
|
|
- return {
|
|
|
- main: null,
|
|
|
- syllables: [],
|
|
|
- frequencies: []
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- const audioInfo = {
|
|
|
- main: null,
|
|
|
- syllables: [],
|
|
|
- frequencies: []
|
|
|
- };
|
|
|
-
|
|
|
- // 处理主音频
|
|
|
- if (wordData.yinpin && typeof wordData.yinpin === 'string') {
|
|
|
- audioInfo.main = {
|
|
|
- id: `word_${wordData.id}_main`,
|
|
|
- url: this.normalizeAudioUrl(wordData.yinpin),
|
|
|
- type: 'main',
|
|
|
- text: wordData.name || '未知单词',
|
|
|
- lastCached: 0 // 最后缓存时间戳
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- // 处理音节音频
|
|
|
- if (Array.isArray(wordData.yinjie)) {
|
|
|
- audioInfo.syllables = wordData.yinjie
|
|
|
- .filter(item => item && typeof item === 'object')
|
|
|
- .map((item, index) => ({
|
|
|
- id: `word_${wordData.id}_syllable_${index}`,
|
|
|
- url: this.normalizeAudioUrl(item.yinpin),
|
|
|
- type: 'syllable',
|
|
|
- text: item.cigen || `音节${index + 1}`,
|
|
|
- index,
|
|
|
- lastCached: 0
|
|
|
- }))
|
|
|
- .filter(item => item.url);
|
|
|
- }
|
|
|
-
|
|
|
- // 处理频度音频
|
|
|
- if (Array.isArray(wordData.pindu)) {
|
|
|
- audioInfo.frequencies = wordData.pindu
|
|
|
- .filter(item => item && typeof item === 'object')
|
|
|
- .map((item, index) => ({
|
|
|
- id: `word_${wordData.id}_frequency_${index}`,
|
|
|
- url: this.normalizeAudioUrl(item.yinpin),
|
|
|
- type: 'frequency',
|
|
|
- text: item.cigen || `频度${index + 1}`,
|
|
|
- index,
|
|
|
- lastCached: 0
|
|
|
- }))
|
|
|
- .filter(item => item.url);
|
|
|
- }
|
|
|
-
|
|
|
- return audioInfo;
|
|
|
- },
|
|
|
-
|
|
|
- // 标准化音频URL(完整实现)
|
|
|
- normalizeAudioUrl(url) {
|
|
|
- if (!url || typeof url !== 'string') return null;
|
|
|
-
|
|
|
- // 已经是完整URL
|
|
|
- if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('blob:')) {
|
|
|
- return url;
|
|
|
- }
|
|
|
-
|
|
|
- // 处理相对路径(根据实际项目配置)
|
|
|
- const baseUrl = 'https://your-audio-server.com/'; // 替换为你的音频服务器地址
|
|
|
- return baseUrl + url.replace(/^\//, '');
|
|
|
- },
|
|
|
-
|
|
|
- // 缓存单词的所有音频(完整实现)
|
|
|
- async cacheWordAudios(wordData) {
|
|
|
- try {
|
|
|
- if (!wordData || !wordData.id) {
|
|
|
- throw new Error('无效的单词数据');
|
|
|
- }
|
|
|
-
|
|
|
- // 提取音频信息
|
|
|
- const audioInfo = this.extractAudioInfo(wordData);
|
|
|
- const allAudios = [];
|
|
|
-
|
|
|
- if (audioInfo.main) allAudios.push(audioInfo.main);
|
|
|
- if (audioInfo.syllables.length) allAudios.push(...audioInfo.syllables);
|
|
|
- if (audioInfo.frequencies.length) allAudios.push(...audioInfo.frequencies);
|
|
|
-
|
|
|
- // 真实下载音频文件(仅App环境)
|
|
|
- if (this.isApp) {
|
|
|
- await this.downloadAudioFiles(allAudios);
|
|
|
- }
|
|
|
-
|
|
|
- // 保存音频信息到缓存
|
|
|
- this.saveAudioInfoToStorage(wordData.id, audioInfo);
|
|
|
-
|
|
|
- return true;
|
|
|
- } catch (error) {
|
|
|
- console.error('缓存单词音频失败:', error);
|
|
|
- return false;
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 下载音频文件(完整实现)
|
|
|
- async downloadAudioFiles(audioList) {
|
|
|
- if (!Array.isArray(audioList)) return;
|
|
|
-
|
|
|
- const downloadTasks = audioList.map(audio => {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- // 检查是否已有缓存
|
|
|
- const cacheInfo = this.getAudioCacheInfo(audio.id);
|
|
|
- if (cacheInfo && cacheInfo.cached) {
|
|
|
- audio.lastCached = cacheInfo.timestamp;
|
|
|
- return resolve();
|
|
|
- }
|
|
|
-
|
|
|
- // 开始下载
|
|
|
- uni.downloadFile({
|
|
|
- url: audio.url,
|
|
|
- success: (res) => {
|
|
|
- if (res.statusCode === 200) {
|
|
|
- // 保存到缓存
|
|
|
- uni.setStorageSync(`audio_file_${audio.id}`, res.tempFilePath);
|
|
|
- uni.setStorageSync(`audio_cache_time_${audio.id}`, Date.now());
|
|
|
-
|
|
|
- audio.lastCached = Date.now();
|
|
|
- console.log(`音频缓存成功: ${audio.id}`);
|
|
|
- resolve();
|
|
|
- } else {
|
|
|
- reject(new Error(`下载失败,状态码: ${res.statusCode}`));
|
|
|
- }
|
|
|
- },
|
|
|
- fail: (err) => {
|
|
|
- reject(new Error(`下载失败: ${err.errMsg}`));
|
|
|
- }
|
|
|
- });
|
|
|
- }).catch(err => {
|
|
|
- console.warn(`音频下载失败 ${audio.id}:`, err);
|
|
|
- // 单个失败不影响其他
|
|
|
- });
|
|
|
- });
|
|
|
-
|
|
|
- await Promise.all(downloadTasks);
|
|
|
- },
|
|
|
-
|
|
|
- // 获取音频缓存信息(完整实现)
|
|
|
- getAudioCacheInfo(audioId) {
|
|
|
- if (!audioId) return null;
|
|
|
-
|
|
|
- const cachedPath = uni.getStorageSync(`audio_file_${audioId}`);
|
|
|
- if (!cachedPath) return null;
|
|
|
-
|
|
|
- return {
|
|
|
- cached: true,
|
|
|
- path: cachedPath,
|
|
|
- timestamp: uni.getStorageSync(`audio_cache_time_${audioId}`) || 0
|
|
|
- };
|
|
|
- },
|
|
|
-
|
|
|
- // 保存音频信息到Storage(完整实现)
|
|
|
- saveAudioInfoToStorage(wordId, audioInfo) {
|
|
|
- if (!wordId || !audioInfo) return;
|
|
|
-
|
|
|
- const cacheKey = `word_audio_${wordId}`;
|
|
|
- const cacheData = {
|
|
|
- ...audioInfo,
|
|
|
- timestamp: Date.now(),
|
|
|
- wordId: wordId
|
|
|
- };
|
|
|
-
|
|
|
- try {
|
|
|
- uni.setStorageSync(cacheKey, cacheData);
|
|
|
- } catch (e) {
|
|
|
- console.error('存储音频信息失败:', e);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 从Storage获取音频信息(完整实现)
|
|
|
- getAudioInfoFromStorage(wordId) {
|
|
|
- if (!wordId) return null;
|
|
|
-
|
|
|
- const cacheKey = `word_audio_${wordId}`;
|
|
|
- try {
|
|
|
- return uni.getStorageSync(cacheKey) || null;
|
|
|
- } catch (e) {
|
|
|
- console.error('读取音频信息失败:', e);
|
|
|
- return null;
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 播放音频(完整实现)
|
|
|
- playAudio(audioInfo) {
|
|
|
- if (!audioInfo || !audioInfo.url) {
|
|
|
- console.error('无效的音频信息');
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- // 确保播放器初始化
|
|
|
- this.init();
|
|
|
-
|
|
|
- // 停止当前播放
|
|
|
- this.player.stop();
|
|
|
-
|
|
|
- // 在App环境下优先使用缓存
|
|
|
- if (this.isApp) {
|
|
|
- const cacheInfo = this.getAudioCacheInfo(audioInfo.id);
|
|
|
- if (cacheInfo && cacheInfo.cached) {
|
|
|
- console.log(`使用缓存播放: ${audioInfo.id}`);
|
|
|
- this.player.src = cacheInfo.path;
|
|
|
- this.player.play();
|
|
|
- return this.player;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 无缓存或非App环境,使用网络播放
|
|
|
- console.log(`使用网络播放: ${audioInfo.id}`);
|
|
|
- this.player.src = audioInfo.url;
|
|
|
- this.player.play();
|
|
|
-
|
|
|
- // App环境下后台尝试缓存
|
|
|
- if (this.isApp) {
|
|
|
- this.downloadAudioFiles([audioInfo]).then(() => {
|
|
|
- console.log(`音频后台缓存成功: ${audioInfo.id}`);
|
|
|
- }).catch(err => {
|
|
|
- console.warn(`音频后台缓存失败: ${audioInfo.id}`, err);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- return this.player;
|
|
|
- },
|
|
|
-
|
|
|
- // // 清理指定单词的缓存(完整实现)
|
|
|
- // clearWordCache(wordId) {
|
|
|
- // if (!wordId) return;
|
|
|
-
|
|
|
- // // 清理音频信息
|
|
|
- // uni.removeStorageSync(`word_audio_${wordId}`);
|
|
|
-
|
|
|
- // // 清理所有相关音频文件
|
|
|
- // const prefix = `word_${wordId}_`;
|
|
|
- // const keys = uni.getStorageInfoSync().keys;
|
|
|
-
|
|
|
- // keys.forEach(key => {
|
|
|
- // if (key.startsWith('audio_file_') && key.includes(prefix)) {
|
|
|
- // uni.removeStorageSync(key);
|
|
|
- // }
|
|
|
- // if (key.startsWith('audio_cache_time_') && key.includes(prefix)) {
|
|
|
- // uni.removeStorageSync(key);
|
|
|
- // }
|
|
|
- // });
|
|
|
- // },
|
|
|
-
|
|
|
- // 清理所有缓存(完整实现)
|
|
|
- clearAllCache() {
|
|
|
- const keys = uni.getStorageInfoSync().keys;
|
|
|
-
|
|
|
- // 清理所有单词音频信息
|
|
|
- keys.filter(key => key.startsWith('word_audio_'))
|
|
|
- .forEach(key => uni.removeStorageSync(key));
|
|
|
-
|
|
|
- // 清理所有音频文件
|
|
|
- keys.filter(key => key.startsWith('audio_file_'))
|
|
|
- .forEach(key => uni.removeStorageSync(key));
|
|
|
- },
|
|
|
-
|
|
|
- // // 获取缓存大小(完整实现)
|
|
|
- // getCacheSize() {
|
|
|
- // const keys = uni.getStorageInfoSync().keys;
|
|
|
- // let size = 0;
|
|
|
-
|
|
|
- // keys.filter(key => key.startsWith('audio_file_'))
|
|
|
- // .forEach(key => {
|
|
|
- // try {
|
|
|
- // const filePath = uni.getStorageSync(key);
|
|
|
- // if (filePath && typeof filePath === 'string') {
|
|
|
- // // 注意:uni-app中无法直接获取文件大小,这里返回文件数量
|
|
|
- // size += 1;
|
|
|
- // }
|
|
|
- // } catch (e) {
|
|
|
- // console.error(`获取缓存大小失败: ${key}`, e);
|
|
|
- // }
|
|
|
- // });
|
|
|
-
|
|
|
- // return {
|
|
|
- // fileCount: size,
|
|
|
- // // 实际项目中可以添加更精确的大小计算
|
|
|
- // estimatedSize: size * 1024 * 100 // 假设每个文件约100KB
|
|
|
- // };
|
|
|
- // }
|
|
|
-};
|