audioManager3.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. export default {
  2. player: null,
  3. isApp: uni.getSystemInfoSync().platform === 'ios' || uni.getSystemInfoSync().platform === 'android',
  4. initPlayer() {
  5. if (!this.player) {
  6. this.player = uni.createInnerAudioContext()
  7. this.player.obeyMuteSwitch = false
  8. }
  9. return this.player
  10. },
  11. // 提取音频信息
  12. extractAudioInfo(wordData) {
  13. const audioInfo = {
  14. main: null,
  15. syllables: [],
  16. frequencies: []
  17. }
  18. if (wordData.yinpin) {
  19. audioInfo.main = {
  20. id: `word_${wordData.id}_main`,
  21. url: wordData.yinpin,
  22. type: 'main',
  23. text: wordData.name
  24. }
  25. }
  26. if (wordData.yinjie?.length) {
  27. audioInfo.syllables = wordData.yinjie.map((item, index) => ({
  28. id: `word_${wordData.id}_syllable_${index}`,
  29. url: item.yinpin,
  30. type: 'syllable',
  31. text: item.cigen,
  32. index
  33. })).filter(item => item.url)
  34. }
  35. if (wordData.pindu?.length) {
  36. audioInfo.frequencies = wordData.pindu.map((item, index) => ({
  37. id: `word_${wordData.id}_frequency_${index}`,
  38. url: item.yinpin,
  39. type: 'frequency',
  40. text: item.cigen,
  41. index
  42. })).filter(item => item.url)
  43. }
  44. return audioInfo
  45. },
  46. // 缓存单词的所有音频
  47. async cacheWordAudios(wordData) {
  48. try {
  49. const { main, syllables, frequencies } = this.extractAudioInfo(wordData)
  50. const allAudios = []
  51. if (main) allAudios.push(main)
  52. allAudios.push(...syllables)
  53. allAudios.push(...frequencies)
  54. // App环境才进行真实下载
  55. if (this.isApp) {
  56. await this.downloadAndSaveAudios(allAudios)
  57. }
  58. this.saveAudioInfoToStorage(wordData.id, { main, syllables, frequencies })
  59. return true
  60. } catch (error) {
  61. console.error('音频缓存失败:', error)
  62. return false
  63. }
  64. },
  65. // 下载并保存音频(仅App环境)
  66. async downloadAndSaveAudios(audioList) {
  67. const downloadPromises = audioList.map(audio => {
  68. return new Promise((resolve, reject) => {
  69. // 先检查是否已缓存
  70. const cachedPath = uni.getStorageSync(`audio_file_${audio.id}`)
  71. if (cachedPath) return resolve()
  72. uni.downloadFile({
  73. url: audio.url,
  74. success: (res) => {
  75. if (res.statusCode === 200) {
  76. uni.setStorageSync(`audio_file_${audio.id}`, res.tempFilePath)
  77. resolve()
  78. } else {
  79. reject(new Error(`下载失败,状态码: ${res.statusCode}`))
  80. }
  81. },
  82. fail: reject
  83. })
  84. }).catch(e => {
  85. console.warn(`音频 ${audio.id} 预加载失败:`, e)
  86. // 即使下载失败也继续其他下载
  87. })
  88. })
  89. await Promise.all(downloadPromises)
  90. },
  91. // 播放音频(优先本地,失败后回退网络)
  92. playAudio(audioInfo) {
  93. if (!audioInfo?.url) return
  94. const player = this.initPlayer()
  95. player.stop()
  96. // 在App环境尝试使用本地缓存
  97. if (this.isApp) {
  98. const cachedPath = uni.getStorageSync(`audio_file_${audioInfo.id}`)
  99. if (cachedPath) {
  100. player.src = cachedPath
  101. player.play()
  102. return player
  103. }
  104. }
  105. // 非App环境或缓存失败时使用网络
  106. console.warn(`使用网络音频: ${audioInfo.id}`)
  107. player.src = audioInfo.url
  108. player.play()
  109. // App环境下后台继续尝试缓存
  110. if (this.isApp) {
  111. this.downloadAndSaveAudios([audioInfo]).catch(console.error)
  112. }
  113. player.onError((err) => {
  114. console.error('播放失败:', err)
  115. uni.showToast({ title: '播放失败', icon: 'none' })
  116. })
  117. return player
  118. },
  119. // 其他方法保持不变...
  120. saveAudioInfoToStorage(wordId, audioInfo) {
  121. const cacheKey = `word_audio_${wordId}`
  122. uni.setStorageSync(cacheKey, {
  123. timestamp: Date.now(),
  124. ...audioInfo
  125. })
  126. },
  127. getAudioInfoFromStorage(wordId) {
  128. const cacheKey = `word_audio_${wordId}`
  129. return uni.getStorageSync(cacheKey) || null
  130. },
  131. clearWordCache(wordId) {
  132. uni.removeStorageSync(`word_audio_${wordId}`)
  133. const prefix = `word_${wordId}_`
  134. const keys = uni.getStorageInfoSync().keys
  135. keys.filter(key => key.startsWith('audio_file_') && key.includes(prefix))
  136. .forEach(key => uni.removeStorageSync(key))
  137. }
  138. }