Просмотр исходного кода

修复概率性扫描卡住的bug

onecold 7 месяцев назад
Родитель
Сommit
fea10c8470
2 измененных файлов с 222 добавлено и 393 удалено
  1. 210 381
      entry/src/main/ets/common/util/Utility.ets
  2. 12 12
      entry/src/main/ets/view/LocalMusic.ets

+ 210 - 381
entry/src/main/ets/common/util/Utility.ets

@@ -120,6 +120,15 @@ export interface FFprobeMetadata {
   format: FFprobeFormat;
 }
 
+// 音频流信息接口
+interface AudioStreamInfo {
+  sampleRate: string;
+  bits_per_raw_sample: string;
+  channels: string;
+  channel_layout: string;
+  start_time: string;
+}
+
 export class Utility {
 
   private constructor() {}
@@ -705,20 +714,6 @@ export class Utility {
   static async uriGetMusicAssetsFromFile(context:Context,uri:string,type:number
     ,autoParseMusicName?:boolean,originFileName?:string): Promise<VideoItem> {
     await ReqPermissionUtil.persistPermission(uri);
-    //这里加个判断,如果文件的大小是0K,这个文件是空的,直接返回
-    try {
-      const file = fs.openSync(uri, fs.OpenMode.READ_ONLY | fs.OpenMode.CREATE)
-      const fileStat = await fs.stat(file.fd);
-      if (fileStat.size < 1024) {// 检查是否为有效文件(>1KB)
-        console.warn(`File ${uri} is empty (0KB). Returning default VideoItem.`);
-        // Return a default or empty VideoItem object
-        return new VideoItem(FileUtil.getFileName(uri), uri, uri, type, 0, '');
-      }
-    } catch (error) {
-      console.error(`Failed to get stats for file ${uri}:`, error);
-      // If we can't read the file, still return a default item
-      return new VideoItem(FileUtil.getFileName(uri), uri, uri, type, 0, '');
-    }
 
     if(StrUtil.isNotEmpty(uri)&&uri.toLowerCase().endsWith('.cue')){
       const cueItem = new VideoItem(FileUtil.getFileName(uri),uri,uri,type,0,'')
@@ -726,393 +721,233 @@ export class Utility {
       return  cueItem
     }
     return Utility.readMetaInfoFFmpeg(context,uri,type,autoParseMusicName,originFileName)
+  }
 
-    //华为官方不支持的格式 不支持内嵌歌词Dsf,aif,aiff,wav  不支持内嵌封面dsf,aif,aiff
-    if(StrUtil.isNotEmpty(uri)){
-      if(uri.toLowerCase().endsWith('.dsf')
-        ||uri.toLowerCase().endsWith('.aif')
-        // ||uri.toLowerCase().endsWith('.wav')
-        ||uri.toLowerCase().endsWith('.aiff')){
-        return Utility.readMetaInfoFFmpeg(context,uri,type,autoParseMusicName)
-      }
-    }
-
-
-    let item:VideoItem = new VideoItem('',uri,uri,type,0,'')
-    try {
-      console.info('asset file.uri: ', uri);
-
-
-      let file = fs.openSync(uri, fs.OpenMode.READ_ONLY | fs.OpenMode.CREATE)
-      console.info("file.fd " + file.fd);
-      let fdfd = 'fd://' + file.fd
-      //3、通过fs.stat方法获取stat对象
-      console.info('asset file.name: ', file.name);
-      console.info('asset file.uri: ', uri);
-      console.info('asset file.fd: ', file.fd);
-      console.info('asset file.path: ', file.path);
-      item = new VideoItem(file.name,uri,uri,type,0,'')
-      await fs.stat(file.fd).then(async (stat: fs.Stat) => {
-        console.info("get file info succeed, the size of file is " + stat.size);
-        let videoSize =  stat.size
-        // let videoTime = stat.ctime
-
-        let fileSize = Utility.formatFSize(videoSize)
-        //按照添加时间
-        let addTime = Utility.getFormatDateStr(new Date(),'yyyy-MM-dd HH:mm');
-        // console.info('onecold asset addTime: ', addTime);
-        // let cTime = Utility.getFormatDateStr(stat.mtime,'yyyy-MM-dd HH:mm')
-
-        // console.info('asset stat.ctime: ', stat.ctime);
-
-        let musicName:string | undefined = file.name
-        let artist:string | undefined = ''
-        let album:string | undefined = ''
-        let pixelMap:image.PixelMap|undefined|null = undefined
-        let imagePath = ''
-
-        let duration:string | undefined = ''
-        let mimeType:string | undefined = ''
-        let trackCount:string | undefined = ''//轨道数量
-        let sampleRate:string | undefined = ''//音频的采样率单位为Hz
-
-        if (canIUse("SystemCapability.Multimedia.Media.AVMetadataExtractor")) {
-          try {
-            // 创建AVMetadataExtractor对象
-            const avMetadataExtractor: media.AVMetadataExtractor = await media.createAVMetadataExtractor();
-            // 设置fdSrc
-            const fd = await fs.openSync(uri, fs.OpenMode.READ_ONLY);
-            avMetadataExtractor.fdSrc = fd;
-
-            // 获取元数据(promise模式)
-            const metadata = await avMetadataExtractor.fetchMetadata();
-            if(StrUtil.isNotEmpty(metadata.title)){
-              musicName = metadata.title
-            }else{
-              //自动解析像这样的获取不到元数据的 周杰伦-七里香.mp3文件
-              console.log(`onecold musicName为空:${file.name}`);
-              const musicData = parseMusicFileName(file.name);
-              if (musicData.isValid)  {
-                // console.log(`onecold 艺术家:${musicData.artist}`);   // 输出:周杰伦
-                // console.log(`onecold 歌曲名:${musicData.title}`);   // 输出:七里香
-                musicName = musicData.title
-                if(artist==''||artist==undefined)
-                  artist = musicData.artist
-              } else {
-                musicName = file.name
-                // console.log("onecold 文件名格式不符合要求");
-              }
-            }
-
-
-            if(StrUtil.isNotEmpty(metadata.artist)){
-              artist = metadata.artist
-            }
-            if(artist==undefined)
-              artist = ''
-
-            if(StrUtil.isNotEmpty(metadata.album)){
-              album = metadata.album
-            }else{
-              album = ''
-            }
+  static async readMetaInfoFFmpeg(context: Context, inputPath: string, type: number,
+    autoParseMusicName?: boolean, originFileName?: string): Promise<VideoItem> {
+    // 辅助函数:创建默认的VideoItem
+    const createDefaultItem = (filePath: string): VideoItem => {
+      return new VideoItem(FileUtil.getFileName(filePath), filePath, filePath, type, 0, '');
+    };
 
-            if(StrUtil.isNotEmpty(metadata.duration)){
-              if(metadata.duration)
-                duration = convertSecondsToTime(metadata.duration.toString())
-            }
-            if(StrUtil.isNotEmpty(metadata.mimeType)){
-              mimeType = metadata.mimeType
+    // 辅助函数:提取歌词(处理标准字段和lyrics-开头的自定义字段)
+    const extractLyrics = (tags: FFMpegTags): string => {
+      // 1. 尝试标准字段
+      let lyrics = tags.LYRICS || tags.lyrics || tags.USLT || tags.UNSYNCEDLYRICS || '';
+
+      // 2. 如果标准字段为空,尝试lyrics-开头的自定义字段
+      if (StrUtil.isEmpty(lyrics)) {
+        const tagsRecord = tags as Record<string, string>;
+        const possibleKeys = Object.keys(tagsRecord);
+        for (let i = 0; i < possibleKeys.length; i++) {
+          const key = possibleKeys[i];
+          if (key && key.toLowerCase().startsWith('lyrics-')) {
+            lyrics = tagsRecord[key];
+            if (lyrics) {
+              break;
             }
-            if(StrUtil.isNotEmpty(metadata.trackCount)){
-              trackCount = metadata.trackCount
-            }
-            if(StrUtil.isNotEmpty(metadata.sampleRate)){
-              sampleRate = metadata.sampleRate
-            }
-
-            let name = await MD5.digestSync(uri)
-
-            // if(isLoadPixelMap){
-              // 获取专辑封面(promise模式)
-              // pixelMap = await avMetadataExtractor.fetchAlbumCover();
-              // // 释放资源(promise模式)
-              // await avMetadataExtractor.release();
-
-              pixelMap = await fetchAlbumCover(avMetadataExtractor)
-
-              // console.info('onecold release success. name= '+musicName);
-              if(pixelMap!==undefined&&pixelMap!==null){
-                // console.info('onecold pixelMap is not empty= '+musicName);
-                imagePath = await ImageUtil.savePixelMap(pixelMap,context.filesDir,name)
-                imagePath = fileUri.getUriFromPath(imagePath)
-              }else{
-                // console.info('onecold pixelMap is  empty= '+musicName);
-                imagePath = ''
-                // if(StrUtil.isNotEmpty(artist))
-                //   imagePath =  await NetAxiosUtil.getLyricCover(musicName,artist)
-              }
-
+          }
+        }
+      }
 
-            // }else{
-            //   imagePath = context.filesDir + FileUtil.separator + name
-            //   imagePath = fileUri.getUriFromPath(imagePath)
-            // }
-            console.info('onecold release success. imagePath= '+imagePath);
+      return lyrics;
+    };
 
+    // 辅助函数:处理封面提取
+    const extractCover = async (hasCover: boolean, filePath: string, name: string): Promise<string> => {
+      const md5Name = await MD5.digestSync(filePath);
+      let imagePath = `${context.filesDir}${FileUtil.separator}${md5Name}.jpg`;
 
-          } catch (error) {
-            console.error('Error during metadata extraction:', error);
+      try {
+        if (hasCover) {
+          const isSuccess = await getFFmpegCover(filePath, imagePath);
+          if (isSuccess) {
+            return fileUri.getUriFromPath(imagePath);
+          } else {
+            Logger.warn('readMetaInfoFFmpeg', 'FFmpeg提取封面失败,使用华为接口');
+            return await extractCoverWithHwInterface(filePath, context, name);
+          }
+        } else if (Utility.isVideoByExtension(filePath)) {
+          const pixelMap = await Utility.getFetchFrameByTime(filePath);
+          if (pixelMap) {
+            imagePath = await ImageUtil.savePixelMap(pixelMap, context.filesDir, md5Name);
+            return fileUri.getUriFromPath(imagePath);
           }
-        } else {
-          console.warn('AVMetadataExtractor capability is not supported.');
         }
+      } catch (error) {
+        Logger.error('readMetaInfoFFmpeg', `提取封面失败: ${error instanceof Error ? error.message : String(error)}`);
+      }
 
+      return '';
+    };
 
-        if(musicName==undefined)
-          musicName = file.name
-
-        item = new VideoItem(musicName,uri ,uri,type,videoSize,addTime,fileSize,
-          imagePath,artist,album,file.name)
-        item.duration = duration+'';
-        item.mimeType = mimeType;
-        item.trackCount = trackCount;
-        item.sampleRate = sampleRate;
-        item.isFav = 0;
-        item.playCount = 0;
-        item.pyStr = pinyin4js.getShortPinyin(musicName)
-
-        // item.lyricContent = await extractLyricsContent(uri)
-        let metaItem = await parseAudioMetadata(uri)
-        if(metaItem){
-          item.lyricContent = metaItem.lyricContent
-          item.bit_rate = formatBitrateToKbps(metaItem.bit_rate  || "0");
-          item.year = metaItem.year ||'unknown'
-          item.probe_score = metaItem.probe_score
-          item.nb_streams = metaItem.nb_streams
-          item.nb_programs = metaItem.nb_programs
+    // 辅助函数:解析音频流信息
+    const parseAudioStream = (metadata: FFprobeMetadata): AudioStreamInfo => {
+      let sampleRate = '';
+      let bits_per_raw_sample = '';
+      let channels = '';
+      let channel_layout = '';
+      let start_time = '';
+
+      if (metadata.streams && metadata.streams.length > 0) {
+        const audioStream = metadata.streams.find(stream => StrUtil.isNotEmpty(stream.sample_rate));
+        if (audioStream) {
+          sampleRate = audioStream.sample_rate || '';
+          bits_per_raw_sample = audioStream.bits_per_raw_sample || '';
+          channel_layout = audioStream.channel_layout || '';
+          channels = audioStream.channels || '';
+          start_time = audioStream.start_time || '';
         }
+      }
 
-      })
-    } catch (error) {
-      console.error('uriGetAssetsFromFile failed with err: ' + JSON.stringify(error));
-    }
-
-    return item
-
-  }
+      return { sampleRate, bits_per_raw_sample, channels, channel_layout, start_time };
+    };
 
+    try {
+      inputPath = FileUtil.getFilePath(inputPath);
+      Logger.info('readMetaInfoFFmpeg', `正在获取元数据: ${inputPath}`)
 
-  static async  readMetaInfoFFmpeg(context:Context,inputPath: string,type:number,
-    autoParseMusicName?:boolean,originFileName?:string): Promise<VideoItem> {
-    return new Promise((resolve, reject) => {
-      inputPath = FileUtil.getFilePath(inputPath)
-      let commands = ["ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", inputPath];
+      const commands = ["ffprobe", "-v", "quiet", "-print_format",
+        "json", "-show_format", "-show_streams", inputPath];
       let outputJson = "";
 
+      // 使用Promise方式执行FFprobe命令
+      await new Promise<void>((resolve, reject) => {
+        FFmpeg.execute(commands, {
+          outputCallback: (message: string) => {
+            outputJson += message;
+          },
+        })
+          .then(() => {
+            Logger.info('readMetaInfoFFmpeg', `获取元数据成功, JSON长度: ${outputJson.length}`);
+            resolve();
+          })
+          .catch((error: Error) => {
+            Logger.error('readMetaInfoFFmpeg', `FFprobe执行失败: ${error.message}`);
+            reject(error);
+          });
+      });
+      // 解析元数据
+      const metadata: FFprobeMetadata = JSON.parse(outputJson);
+      const format = metadata.format;
+      const tags = format.tags || {};
 
-      FFmpeg.execute(commands,  {
-        logCallback: (logLevel: number, logMessage: string) => console.log(`[${logLevel}]${logMessage}`),
-        outputCallback: (message: string) => {
-          outputJson += message;
-        },
-      }).then(async () => {
-        try {
-          const metadata: FFprobeMetadata = JSON.parse(outputJson);
-          const format = metadata.format;
-          console.log(`onecold outputJson:${outputJson}`);
-          let file = fs.openSync(inputPath, fs.OpenMode.READ_ONLY | fs.OpenMode.CREATE)
-          console.info('readMetaInfoFFmpeg asset file.path: ', file.path);
-          let videoItem = new VideoItem(file.name,inputPath,inputPath,type,0,'')
-          await fs.stat(file.fd).then(async (stat: fs.Stat) => {
-
-            // 获取音频流的采样率
-            let sampleRate:string|undefined = '';
-            let bits_per_raw_sample:string|undefined = '';
-            let channels:string|undefined = '';
-            let channel_layout:string|undefined = '';
-            let start_time:string|undefined = '';
-            if (metadata.streams  && metadata.streams.length  > 0) {
-              // 查找第一个音频流
-              const audioStream = metadata.streams.find(stream  => StrUtil.isNotEmpty(stream.sample_rate));
-              if (audioStream)  {
-                sampleRate = audioStream.sample_rate;
-                bits_per_raw_sample = audioStream.bits_per_raw_sample
-                channel_layout = audioStream.channel_layout
-                channels = audioStream.channels
-                start_time = audioStream.start_time
-              }
-            }
-
-            let videoSize =  stat.size
-            const modifyTime = Utility.getFormatDateStr(stat.mtime,'yyyy-MM-dd HH:mm')
-            let fileSize = Utility.formatFSize(videoSize)
-            //按照添加时间
-            let addTime = Utility.getFormatDateStr(new Date(),'yyyy-MM-dd HH:mm');
-            // Extract artist and title from tags
-            const tags = format.tags  || {};
-            let artist = tags.artist ||tags.ARTIST || '';
-            let title = tags.title ||tags.TITLE || '';
-            let album = tags.album ||tags.ALBUM|| '';
-            Logger.info('readMetaInfoFFmpeg', `原始标签 title="${title}", artist="${artist}", album="${album}"`);
-            console.log(`onecold tags.tags:${JSON.stringify(tags)}`);
-            // 检查是否有乱码
-            if (hasGarbledText(tags))  {
-              // 第二次尝试用华为的接口提取元数据
-              let  hwTags = await extractHwMediaMetadata(inputPath)
-              artist = hwTags?.artist|| '';
-              title = hwTags?.title|| '';
-              album = hwTags?.album|| '';
-              console.info('onecold 乱码修正 artist='+artist);
-              console.info('onecold 乱码修正 title='+title);
-              console.info('onecold 乱码修正 album='+album);
-            }
-            Logger.info('readMetaInfoFFmpeg', `最终标签 title="${title}", artist="${artist}", album="${album}", autoParse=${autoParseMusicName}`);
-            if(StrUtil.isEmpty(title)){
-              //自动解析像这样的获取不到元数据的 周杰伦-七里香.mp3文件
-              console.log(`onecold musicName为空:${file.name}`);
-              // let autoParseMusicName = PreferencesUtil.getBooleanSync('autoParseMusicName',false)
-              if(autoParseMusicName){
-                const musicData = parseMusicFileName(file.name);
-                if (musicData.isValid)  {
-                  console.log(`onecold 艺术家:${musicData.artist}`);   // 输出:周杰伦
-                  console.log(`onecold 歌曲名:${musicData.title}`);   // 输出:七里香
-                  title = musicData.title
-                  if(artist==''||artist==undefined)
-                    artist = musicData.artist
-                } else {
-
-                  title = originFileName||file.name
-                }
-              }else{
-                title =  originFileName||file.name
-              }
+      // 获取文件信息
+      let file: fs.File;
+      try {
+        file = fs.openSync(inputPath, fs.OpenMode.READ_ONLY);
+      } catch (error) {
+        Logger.error('readMetaInfoFFmpeg', `打开文件失败: ${inputPath}`);
+        return createDefaultItem(inputPath);
+      }
 
-            }
+      try {
+        const stat = await fs.stat(file.fd);
+        const videoSize = stat.size;
+        const addTime = Utility.getFormatDateStr(new Date(), 'yyyy-MM-dd HH:mm');
+        const fileSize = Utility.formatFSize(videoSize);
+
+        // 解析音频流信息
+        const audioStreamInfo = parseAudioStream(metadata);
+
+        // 提取基础标签信息
+        let artist = tags.artist || tags.ARTIST || '';
+        let title = tags.title || tags.TITLE || '';
+        let album = tags.album || tags.ALBUM || '';
+
+        Logger.info('readMetaInfoFFmpeg', `原始标签 title="${title}", artist="${artist}", album="${album}"`);
+
+        // 检查并修正乱码
+        if (hasGarbledText(tags)) {
+          const hwTags = await extractHwMediaMetadata(inputPath);
+          artist = hwTags?.artist || artist;
+          title = hwTags?.title || title;
+          album = hwTags?.album || album;
+          Logger.info('readMetaInfoFFmpeg', `乱码修正后的标签 title="${title}", artist="${artist}"`);
+        }
 
-            let name: string = title;
-            if (!name) {
-              name = getFileNameWithoutExtension(inputPath);
-            }
-            let pixelMap:image.PixelMap|undefined|null = undefined
-            // Create VideoItem
-            videoItem = new VideoItem(
-              name,
-              inputPath, // id can be generated or left empty
-              inputPath,
-              type, // assuming it's local
-              videoSize,
-              addTime // convert to ISO string
-            );
-
-            // Set additional properties from format metadata
-            videoItem.artist  = artist;
-            videoItem.album  = album;
-            videoItem.sampleRate = sampleRate
-            videoItem.pyStr = pinyin4js.getShortPinyin(name)
-            videoItem.fileName  = FileUtil.getFileName(inputPath);
-            if(format.duration)
-              videoItem.duration = formatDuration(format.duration.toString()||'00:00')
-            videoItem.size  = fileSize;
-            videoItem.bit_rate  =formatBitrateToKbps(format.bit_rate  || "0");
-            videoItem.probe_score  = format.probe_score;
-            videoItem.nb_streams  = format.nb_streams;
-            videoItem.nb_programs  = format.nb_programs;
-            videoItem.year  = tags.TYER || tags.date ||tags.DATE|| ''; // try different tag names for year
-            videoItem.lyricContent  = tags.LYRICS || tags.lyrics  ||  tags.USLT || tags.UNSYNCEDLYRICS || '';
-            //console.info('readMetaInfoFFmpeg   videoItem.lyricContent: ',  videoItem.lyricContent);
-            // 如果标准字段没有歌词,则尝试解析 `lyrics-` 字段
-            // tags 必须是 Record<string, string | undefined>(或 any)
-            // 如果还是没有歌词内容,尝试查找自定义的lyrics-开头的属性(电脑版音乐标签内嵌歌词就是lyrics-XXX)
-            if(StrUtil.isEmpty( videoItem.lyricContent)){
-              const tagsRecord = tags as Record<string, string>;
-              const possibleKeys = Object.keys(tagsRecord);
-              for (let i = 0; i < possibleKeys.length;  i++) {
-                const key = possibleKeys[i];
-                if (key && key.toLowerCase().startsWith('lyrics-'))  {
-                  //console.info('readMetaInfoFFmpeg  videoItem.key:  ', key);
-                  // 通过转换后的 Record 类型安全访问属性
-                  videoItem.lyricContent  = tagsRecord[key];
-                  //console.info('readMetaInfoFFmpeg  videoItem.lyricContent2:  ', videoItem.lyricContent);
-                  if (videoItem.lyricContent)  {
-                    break;
-                  }
-                }
+        // 处理空标题的情况
+        if (StrUtil.isEmpty(title)) {
+          if (autoParseMusicName) {
+            const musicData = parseMusicFileName(file.name);
+            if (musicData.isValid) {
+              title = musicData.title;
+              if (StrUtil.isEmpty(artist)) {
+                artist = musicData.artist;
               }
+            } else {
+              title = originFileName || file.name;
             }
+          } else {
+            title = originFileName || file.name;
+          }
+        }
 
+        // 确保有有效的名称
+        let name = title || getFileNameWithoutExtension(inputPath);
+
+        // 构建VideoItem
+        const videoItem = new VideoItem(name, inputPath, inputPath, type, videoSize, addTime);
+        videoItem.artist = artist;
+        videoItem.album = album;
+        videoItem.sampleRate = audioStreamInfo.sampleRate;
+        videoItem.pyStr = pinyin4js.getShortPinyin(name);
+        videoItem.fileName = FileUtil.getFileName(inputPath);
+        videoItem.duration = format.duration ? formatDuration(format.duration.toString()) : '00:00';
+        videoItem.size = fileSize;
+        videoItem.bit_rate = formatBitrateToKbps(format.bit_rate || "0");
+        videoItem.probe_score = format.probe_score;
+        videoItem.nb_streams = format.nb_streams;
+        videoItem.nb_programs = format.nb_programs;
+        videoItem.year = tags.TYER || tags.date || tags.DATE || '';
+        videoItem.lyricContent = extractLyrics(tags);
+        videoItem.genre = tags.genre || tags.GENRE || '';
+        videoItem.track = tags.track || tags.TRACK || '';
+        videoItem.ALBUMARTIST = tags.ALBUMARTIST || tags.album_artist || tags.albumartist || tags.TPE2 || '';
+        videoItem.COMPOSER = tags.COMPOSER || tags.composer || '';
+        videoItem.LYRICIST = tags.LYRICIST || tags.lyricist || tags.TEXT || '';
+        videoItem.COMMENT = tags.COMMENT || tags.comment || tags.COMM || '';
+        videoItem.disc = tags.disc || '';
+        videoItem.bits_per_raw_sample = audioStreamInfo.bits_per_raw_sample || '1';
+        videoItem.channel_layout = audioStreamInfo.channel_layout || '';
+        videoItem.channels = audioStreamInfo.channels || '0';
+        videoItem.start_time = audioStreamInfo.start_time || '00:00';
+        videoItem.mimeType = format.format_name;
+
+        // 检查并提取封面
+        const hasCover = metadata.streams.some(stream => stream.disposition?.attached_pic === 1);
+        videoItem.pixelMapPath = await extractCover(hasCover, inputPath, name);
+
+        // 确定音质等级
+        if (audioStreamInfo.sampleRate && format.bit_rate) {
+          videoItem.md5Str = determineAudioQuality(
+            videoItem.mimeType,
+            Number(format.bit_rate),
+            Number(audioStreamInfo.sampleRate)
+          );
+        }
 
-            videoItem.genre  = tags.genre||tags.GENRE|| '';
-            videoItem.track  = tags.track||tags.TRACK|| '';
-
-            videoItem.ALBUMARTIST  = tags.ALBUMARTIST||tags.album_artist||tags.albumartist||tags.TPE2|| '';
-            videoItem.COMPOSER  = tags.COMPOSER||tags.composer|| '';
-            videoItem.LYRICIST  = tags.LYRICIST||tags.lyricist||tags.TEXT ||'';
-            videoItem.COMMENT  = tags.COMMENT||tags.comment||tags.COMM|| '';
-            videoItem.disc  = tags.disc|| '';
-
-            videoItem.bits_per_raw_sample = bits_per_raw_sample ||'1'
-            videoItem.channel_layout = channel_layout || ''
-            videoItem.channels = channels ||'0'
-            videoItem.start_time = start_time || '00:00'
-
-            videoItem.mimeType = format.format_name
-            // 检查是否有封面图片流
-            const hasCover = metadata.streams.some(stream  =>
-            stream.disposition?.attached_pic  === 1
-            );
-            console.info('onecold readMetaInfoFFmpeg asset hasCover: ', hasCover);
-            let md5Name = await MD5.digestSync(inputPath)
-            let imagePath = `${context.filesDir}${FileUtil.separator}${md5Name}.jpg`;
-            console.info('onecold readMetaInfoFFmpeg asset imagePath: ', imagePath);
-            // 如果有封面图片,则提取
-            try {
-              if (hasCover) {
-                //提取封面
-                let isSuccess:boolean= await getFFmpegCover(inputPath, imagePath);
-
-                if(isSuccess){
-                  imagePath = fileUri.getUriFromPath(imagePath)
-                }else{
-                  // 使用华为接口提取封面(备用方案)
-                  console.warn('onecold 提取封面图片失败,使用华为接口提取封面');
-                  imagePath = await extractCoverWithHwInterface(inputPath, context, name);
-                }
-                videoItem.pixelMapPath  = imagePath;
-              }else if(Utility.isVideoByExtension(inputPath)){
-                pixelMap = await Utility.getFetchFrameByTime(inputPath)
-                if(pixelMap!==undefined&&pixelMap!==null){
-                  imagePath = await ImageUtil.savePixelMap(pixelMap,context.filesDir,md5Name)
-                  imagePath = fileUri.getUriFromPath(imagePath)
-                }else{
-                  imagePath = ''
-                }
-                imagePath = fileUri.getUriFromPath(imagePath)
-                videoItem.pixelMapPath  = imagePath;
-              }
-            } catch (error) {
-              console.warn('onecold 提取封面图片失败:', error.message);
-            }
-            if(sampleRate&&format.bit_rate){
-              videoItem.md5Str = determineAudioQuality( videoItem.mimeType,Number(format.bit_rate),Number(sampleRate))
-            }
-            console.info('onecold Successfully  parsed metadata:', videoItem);
-            resolve(videoItem);
-          })
-
+        Logger.info('readMetaInfoFFmpeg', `成功解析元数据: ${name}`);
+        return videoItem;
 
+      } finally {
+        // 确保文件句柄被关闭
+        try {
+          fs.closeSync(file);
         } catch (error) {
-          console.error('onecold Failed  to parse metadata:', error);
-          reject(new Error('onecold Failed to parse metadata: ' + error.message));
+          Logger.error('readMetaInfoFFmpeg', `关闭文件失败: ${error instanceof Error ? error.message : String(error)}`);
         }
-      }).catch((error: Error) => {
-        console.error(`onecold Execution  failed with error: ${error.message}`);
-        reject(error);
-      });
-    });
+      }
+
+    } catch (error) {
+      Logger.error('onecold readMetaInfoFFmpeg', `解析失败: ${JSON.stringify(error)})`);
+      return createDefaultItem(inputPath);
+    }
   }
 
 
+
+
   private completionNum(num: number): string | number {
     if (num < 10) {
       return '0' + num;
@@ -1949,9 +1784,7 @@ async function getFFmpegCover(inputPath: string, outputPath: string): Promise<bo
 
   try {
     await FFmpeg.execute(commands,  {
-      logCallback: (logLevel: number, logMessage: string) => {
-        console.info(`[FFmpeg  LOG] [${logLevel}] ${logMessage}`);
-      },
+      logCallback: (message) => console.info(`[log] ${message}`),
       progressCallback: (message: string) => {
         console.info(`[FFmpeg  progress] ${JSON.stringify(FFProgressMessageParser.parse(message))}`);
       },
@@ -1969,9 +1802,7 @@ async function getFFmpegCover(inputPath: string, outputPath: string): Promise<bo
         outputPath
       ];
       await FFmpeg.execute(primaryCommands,  {
-        logCallback: (logLevel: number, logMessage: string) => {
-          console.info(`[FFmpeg  LOG] [${logLevel}] ${logMessage}`);
-        },
+        logCallback: (message) => console.info(`[log] ${message}`),
         progressCallback: (message: string) => {
           console.info(`[FFmpeg  progress] ${JSON.stringify(FFProgressMessageParser.parse(message))}`);
         },
@@ -2003,9 +1834,7 @@ async function getVideoFFmpegCover(inputPath: string, outputPath: string) {
     outputPath
   ];
   FFmpeg.execute(commands, {
-    logCallback: (logLevel: number, logMessage: string) => {
-      console.info(`[FFmpegX LOG] [${logLevel}]${logMessage}`)
-    },
+    logCallback: (message) => console.info(`[log]${message}`),
     progressCallback: (message: string) => {
       console.info(`[FFmpegX progress]${JSON.stringify(FFProgressMessageParser.parse(message))}`)
     },

+ 12 - 12
entry/src/main/ets/view/LocalMusic.ets

@@ -812,6 +812,7 @@ const ITEM_HEIGHT_BIG: number = 78; // 列表项大高度
 @Preview
 @Component
 export struct LocalMusic {
+  @State isCoverRectangle: boolean = true;
   @State showZMIndex: boolean = false //是否右侧显示字母索引
   private prevOffsetY: number = 0; // 记录上一次的Y轴偏移量
   @State autoHideTitle: boolean = true //滚动自动隐藏标题栏
@@ -3435,8 +3436,7 @@ export struct LocalMusic {
           })
       }
       .bindContentCover($$this.isShowPlay, this.MusicPlayBuilder(), {
-        modalTransition: this.isCoverRectangle?ModalTransition.DEFAULT:ModalTransition.NONE,
-        transition: this.isCoverRectangle?null:AnimationHelper.transitionInDown(500),
+        modalTransition:ModalTransition.DEFAULT,
         onWillDisappear: () => {
           this.setShowPlayFalse()
         },
@@ -6717,7 +6717,7 @@ export struct LocalMusic {
         ) //拖动List关键代码结束
 
       }, (item: VideoItem) => item.filePath + '_' + this.listRefreshKey)
-      // }
+
     }
     .onScrollStart(() => {
       this.isScrolling = true
@@ -10947,7 +10947,7 @@ export struct LocalMusic {
   }
 
   @State is_auto_hide_progress: boolean = false //手机横屏自动隐藏播放进退条,点击屏幕可以显示,倒计时4秒后又自动隐藏
-  @State isCoverRectangle: boolean = false
+
   // 添加控制缩放的状态变量
   @State scaleValueImage: number = 1
   @State scaleValueText: number = 1
@@ -13924,8 +13924,8 @@ export struct LocalMusic {
     let mOnErrorListener: OnErrorListener = {
       onError: (what: number, extra: number) => {
         this.stopProgressTask();
-        LogUtils.getInstance().LOGI("heanup OnErrorListener-->go:" + what + "===" + extra + " this.videoUrl=" + this.videoUrl)
-        LogUtils.getInstance().LOGI('heanup 播放错误,歌曲详情:' + JSON.stringify(this.currentSong))
+        console.info("heanup OnErrorListener-->go:" + what + "===" + extra + " this.videoUrl=" + this.videoUrl)
+        console.info('heanup 播放错误,歌曲详情:' + JSON.stringify(this.currentSong))
         // 检查是否为WebDAV播放错误
         let isWebDavError = false;
         if (this.currentSong && isRemoteCloudType(this.currentSong.type) && this.currentSong.filePath) {
@@ -13941,11 +13941,11 @@ export struct LocalMusic {
         }
 
         if (isWebDavError) {
-          Logger.error(`heanup WebDAV播放错误 - what: ${what}, extra: ${extra}, URL: ${this.videoUrl}`);
+          console.info(`heanup WebDAV播放错误 - what: ${what}, extra: ${extra}, URL: ${this.videoUrl}`);
 
           // 播放失败时清空实例变量中的WebDAV认证信息,防止错误账户持续使用
           // this.currentWebDavAuthInfo = null;
-          Logger.info('heanup WebDAV播放失败,保留认证信息用于后续歌曲切换');
+          console.info('heanup WebDAV播放失败,保留认证信息用于后续歌曲切换');
 
           // 根据错误代码提供更具体的错误信息
           let errorMessage = "WebDAV播放失败";
@@ -13962,14 +13962,14 @@ export struct LocalMusic {
           ToastUtil.showToast(errorMessage);
         } else {
           if (StrUtil.isNotEmpty(this.videoUrl) &&
-            !this.videoUrl.startsWith('http://') &&
-            !this.videoUrl.startsWith('https://') &&
+            !this.videoUrl.startsWith('http') &&
+            this.currentSong &&this.currentSong.type==CommonConstants.TYPE_LOCAL&&
             !FileUtil.accessSync(this.videoUrl)) {
-            ToastUtil.showToast(Utility.resourceToString(getContext(), $r('app.string.file_not_exist')))
+            console.info('heanup 播放.文件不存在');
+            ToastUtil.showToast(Utility.resourceToString(this.context, $r('app.string.file_not_exist')))
           }
 
         }
-
         that.hideLoadIng();
       }
     }