Ver código fonte

增加风格和音轨号字段,获取一些元数据字段的修复

onecold 1 ano atrás
pai
commit
a167a85106

+ 10 - 0
entry/src/main/ets/common/util/MediaTable.ets

@@ -238,6 +238,8 @@ export default class MediaTable {
       obj.year  = resultSet.getString(resultSet.getColumnIndex('year'));
       obj.nb_streams  = resultSet.getDouble(resultSet.getColumnIndex('nb_streams'));
       obj.nb_programs  = resultSet.getDouble(resultSet.getColumnIndex('nb_programs'));
+      obj.genre  = resultSet.getString(resultSet.getColumnIndex('genre'));
+      obj.track  = resultSet.getString(resultSet.getColumnIndex('track'));
 
       const valueBucket: relationalStore.ValuesBucket = obj
       // Step 4: 执行更新
@@ -562,6 +564,8 @@ export default class MediaTable {
     item.year = safeGet('year');
     item.nb_streams = safeGetNumber('nb_streams');
     item.nb_programs = safeGetNumber('nb_programs');
+    item.genre = safeGet('genre');
+    item.track = safeGet('track');
 
     return item;
   }
@@ -650,6 +654,12 @@ function generateBucket(item: VideoItem): relationalStore.ValuesBucket {
   if(item.nb_programs){
     obj.nb_programs = item.nb_programs;
   }
+  if(item.genre){
+    obj.genre = item.genre;
+  }
+  if(item.track){
+    obj.track = item.track;
+  }
 
   return obj;
 }

+ 6 - 1
entry/src/main/ets/common/util/RdbUtils.ets

@@ -67,13 +67,15 @@ export default class RdbUtils {
       '        year TEXT,\n' +
       '        nb_streams INTEGER DEFAULT 0,\n' +
       '        nb_programs INTEGER DEFAULT 0,\n' +
+      '        genre TEXT,\n' +
+      '        track TEXT,\n' +
 
       '        mimeType TEXT' +
       ')',
     columns: ['id', 'name', 'filePath','mtype', 'videoSize', 'cTime','size', 'artist', 'album',
       'fileName','parentPath','isFav','pixelMapPath','pixelMapToString',
       'duration',   'sampleRate','playCount',  'lastPlayedStr', 'trackCount',
-      'lyricContent','md5Str','extra_json','pyStr','bit_rate','probe_score','year','nb_streams','nb_programs','mimeType']
+      'lyricContent','md5Str','extra_json','pyStr','bit_rate','probe_score','year','nb_streams','nb_programs','genre','track','mimeType']
   };
 
   constructor(tableName: string, sqlCreateTable: string, columns: Array<string>) {
@@ -153,6 +155,9 @@ export default class RdbUtils {
             'year': 'TEXT',
             'nb_streams': 'INTEGER DEFAULT 0',
             'nb_programs': 'INTEGER DEFAULT 0',
+            'genre': 'TEXT',
+            'track': 'TEXT',
+
           };
           
           // 逐个添加列,不依赖于检查结果

+ 42 - 18
entry/src/main/ets/common/util/Utility.ets

@@ -25,13 +25,22 @@ import { VipData } from '../../viewmodel/VipData';
 import { LocalMusic } from '../../view/LocalMusic';
 import { VipPage } from '../../pages/VipPage';
 import { FFmpeg, FFProgressMessageParser } from '@sj/ffmpeg';
+
 interface FFMpegTags {
   album?: string;
+  ALBUM?: string;
   artist?: string;
+  ARTIST?: string;
+  TITLE?: string;
   title?: string;
   track?: string;
+  TRACK?: string;
   TYER?: string;
+  year?: string;
+  genre?:string;
+  GENRE?:string;
   date?: string;
+  DATE?: string;
   LYRICS?: string;
   lyrics?: string;       // 小写变体
   USLT?: string;         // ID3v2同步歌词
@@ -668,6 +677,8 @@ export class Utility {
 
   //获取音乐资源的属性值,
   static async uriGetMusicAssetsFromFile(context:Context,uri:string,type:number,isLoadPixelMap?:boolean): Promise<VideoItem> {
+    return Utility.readMetaInfoFFmpeg(context,uri,type)
+
     //华为官方不支持的格式 不支持内嵌歌词Dsf,aif,aiff,wav  不支持内嵌封面dsf,aif,aiff
     if(StrUtil.isNotEmpty(uri)){
       if(uri.toLowerCase().endsWith('.dsf')
@@ -881,9 +892,26 @@ export class Utility {
             let addTime = Utility.getFormatDateStr(new Date(),'yyyy-MM-dd HH:mm');
             // Extract artist and title from tags
             const tags = format.tags  || {};
-            const artist = tags.artist  || '';
-            const title = tags.title  || '';
-            const album = tags.album  || '';
+            let artist = tags.artist ||tags.ARTIST || '';
+            let title = tags.title ||tags.TITLE || '';
+            const album = tags.album ||tags.ALBUM|| '';
+
+
+            if(StrUtil.isEmpty(title)){
+              //自动解析像这样的获取不到元数据的 周杰伦-七里香.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}`);   // 输出:七里香
+                title = musicData.title
+                if(artist==''||artist==undefined)
+                  artist = musicData.artist
+              } else {
+                title = file.name
+                // console.log("onecold 文件名格式不符合要求");
+              }
+            }
 
             let name: string = title;
             if (!name) {
@@ -913,9 +941,10 @@ export class Utility {
             videoItem.probe_score  = format.probe_score;
             videoItem.nb_streams  = format.nb_streams;
             videoItem.nb_programs  = format.nb_programs;
-            videoItem.year  = tags.TYER || tags.date  || 'unknown'; // try different tag names for year
+            videoItem.year  = tags.TYER || tags.date ||tags.DATE|| 'unknown'; // try different tag names for year
             videoItem.lyricContent  = tags.LYRICS || tags.lyrics  ||  tags.USLT || tags.UNSYNCEDLYRICS || '';
-
+            videoItem.genre  = tags.genre||tags.GENRE||'unknown';
+            videoItem.track  = tags.track||tags.TRACK||'';
             // 检查是否有封面图片流
             const hasCover = metadata.streams.some(stream  =>
             stream.disposition?.attached_pic  === 1
@@ -926,18 +955,13 @@ export class Utility {
 
               try {
                 let md5Name = await MD5.digestSync(inputPath)
-                // const imageName = `${md5Name}.jpg`;
-                const imagePath = `${context.filesDir}${FileUtil.separator}${md5Name}`;
+                let imagePath = `${context.filesDir}${FileUtil.separator}${md5Name}.jpg`;
                 console.info('readMetaInfoFFmpeg asset imagePath: ', imagePath);
-                // 提取封面图片
-                // await extractCoverImage(inputPath, imagePath);
-                await FFmpegCover(inputPath, imagePath);
-
-                // 检查图片是否生成成功
-                if (fs.accessSync(imagePath))  {
-                  videoItem.pixelMapPath  = imagePath;
+                //提取封面
+                await getFFmpegCover(inputPath, imagePath);
+                imagePath = fileUri.getUriFromPath(imagePath)
+                videoItem.pixelMapPath  = imagePath;
 
-                }
               } catch (error) {
                 console.warn(' 提取封面图片失败:', error.message);
               }
@@ -1601,7 +1625,7 @@ function getFileNameWithoutExtension(filePath: string): string {
 }
 
 /**
- * 从音乐文件中提取封面
+ * 从视频文件中提取封面
  * @param inputPath 音乐文件路径
  * @returns Promise<void>
  */
@@ -1635,8 +1659,8 @@ async function extractCoverImage(inputPath: string, outputPath: string): Promise
  * @param inputPath 音乐文件路径
  * @returns Promise<void>
  */
-async function FFmpegCover(inputPath: string, outputPath: string) {
-  let commands = ["ffmpeg", "-i", inputPath, "-map", "0:v", "-c:v", "copy", outputPath];
+async function getFFmpegCover(inputPath: string, outputPath: string) {
+  let commands = ["ffmpeg", "-y","-i", inputPath, "-map", "0:v", "-c:v", "copy", outputPath];
   FFmpeg.execute(commands, {
     logCallback: (logLevel: number, logMessage: string) => {
       console.info(`[FFmpeg LOG] [${logLevel}]${logMessage}`)

+ 0 - 1
entry/src/main/ets/entryability/EntryAbility.ets

@@ -80,7 +80,6 @@ export default class EntryAbility extends UIAbility {
     async onCreate(want:Want, launchParam:AbilityConstant.LaunchParam) {
         AppStorage.setOrCreate('context', this.context);
         hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
-
         setTimeout(async ()=>{
             this.loadDoWant(want)
             await this.handleParam(want)

+ 27 - 24
entry/src/main/ets/pages/ScanFilePage.ets

@@ -66,10 +66,7 @@ export struct ScanFilePage{
 
   }
   onPageShow() {
-    this.initLottie(this.path,true)
-    setTimeout(()=>{
-      lottie.pause()
-    },88)
+
 
   }
   // 组件生命周期
@@ -88,6 +85,10 @@ export struct ScanFilePage{
 
     this.lockPath =  this.rootPath +'/'+ LocalMusic.STR_LOCK_VIDEO
     LogUtil.info('onecold 文件扫描 aboutToAppear')
+    this.initLottie(this.path,true)
+    setTimeout(()=>{
+      lottie.pause()
+    },88)
 
   }
   onColorModeChange() {
@@ -224,12 +225,26 @@ export struct ScanFilePage{
 
           Text(this.strText)
             .height(50)
-            .margin({ right: 20 })
             .fontSize(16)
             .fontColor($r('app.color.text_color'))
             .fontWeight(480)
             .visibility(this.textVisi)
 
+          Button($r('app.string.start_scan'), { type: ButtonType.Capsule, stateEffect: false })
+
+            .width(180)
+            .height(55)
+            .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
+            .margin({ top: 10, bottom: 10 })
+            .backgroundColor(this.themeColor)
+            .enabled(this.isStart ?false:true)
+            .onClick(() => {
+              this.doOptimize(false)
+
+            })
+            .alignSelf(ItemAlign.Center)
+            .transition(TransitionEffect.move(TransitionEdge.BOTTOM)
+              .animation({ duration: 380, curve: Curve.Ease,delay:40 }))
 
           Column() {
             Row() {
@@ -303,26 +318,14 @@ export struct ScanFilePage{
           .transition(TransitionEffect.move(TransitionEdge.BOTTOM)
             .animation({ duration: 380, curve: Curve.Ease,delay:60 }))
 
-          Column() {
-            Button($r('app.string.start_scan'), { type: ButtonType.Capsule, stateEffect: false })
-
-              .width(180)
-              .height(55)
-              .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
-              .margin({ top: 20, bottom: 10 })
-              .backgroundColor(this.themeColor)
-              .enabled(this.isStart ?false:true)
-              .onClick(() => {
-                this.doOptimize(false)
+          Row() {
 
-              })
-              .alignSelf(ItemAlign.Center)
 
             Button($r('app.string.onekey_cover'), { type: ButtonType.Capsule, stateEffect: false })
-              .width(180)
+              .width(150)
               .height(55)
               .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
-              .margin({ top: 10, bottom: 10 })
+              .margin({ top: 10, bottom: 10,right:10 })
               .backgroundColor(this.themeColor)
               .enabled(this.isStartCover ?false:true)
               .onClick(() => {
@@ -336,10 +339,10 @@ export struct ScanFilePage{
               .alignSelf(ItemAlign.Center)
 
             Button($r('app.string.sync_data'), { type: ButtonType.Capsule, stateEffect: false })
-              .width(180)
+              .width(150)
               .height(55)
               .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
-              .margin({ top: 10, bottom: 20 })
+              .margin({ left:10,top: 10, bottom: 10 })
               .backgroundColor(this.themeColor)
               .enabled(this.isStartSync ?false:true)
               .onClick(() => {
@@ -411,7 +414,7 @@ export struct ScanFilePage{
           .backgroundColor($r('app.color.title_bar_bg'))//.linearGradient( { direction: GradientDirection.Right, colors:[['#ff37a0fc',0.0],['#67e667',0.5],['#f5856e',1.0]]})
           .height(50)
           .layoutWeight(1)
-          .stateEffect(true)
+          .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.7})
           .margin({ right: 6 })
           .onClick(() => {
             DialogHelper.closeDialog('tips'); //关闭弹框
@@ -421,7 +424,7 @@ export struct ScanFilePage{
           .layoutWeight(1)
           .height(50)
           .backgroundColor($r('app.color.title_bar_bg'))
-          .stateEffect(true)
+          .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.7})
           .margin({ left: 6 })
           .onClick(() => {
             DialogHelper.closeDialog('tips'); //关闭弹框

+ 36 - 35
entry/src/main/ets/view/LocalMusic.ets

@@ -1054,41 +1054,7 @@ export struct LocalMusic {
 
   }
 
-  //获取视频缩略图头像
-  async updatePixelMaps(curPath: string) {
-    console.info('updatePixelMaps: ', curPath);
-    const updatedList = [...this.videoLocalList]; // 创建一个新的列表
-
-    for (const videoItem of updatedList) {
-      if (videoItem.type === CommonConstants.TYPE_LOCAL) {
-        let pixelMap: image.PixelMap | undefined = undefined;
-        const uri = videoItem.filePath;
-
-        // 获取缩略图
-        // if (Utility.isVideoByExtension(uri)) {
-        //   pixelMap = await Utility.getFetchFrameByTime(uri);
-        // } else {
-        //   pixelMap = await Utility.getFetchMetadataFromFdSrcByPromise(uri);
-        // }
-        let name = await MD5.digestSync(uri)
-        let imagePath = this.context.filesDir + FileUtil.separator + name
-        console.info('onecold release success. name= ' + name);
-        imagePath = fileUri.getUriFromPath(imagePath)
-        videoItem.pixelMapPath = imagePath
-        // videoItem.pixelMap = pixelMap;
-        // videoItem.pixelMapToString = pixelMap ? await ImageUtil.pixelMapToBase64StrBig(pixelMap) : undefined;
-      }
 
-    }
-    // console.info('updatePixelMaps完毕: ', updatedList.length);
-    this.updateListData(updatedList)
-    // this.videoLocalList = updatedList; // 重新赋值以触发更新
-    // 缓存结果
-    this.addCache(curPath, updatedList);
-    await this.saveCacheToStorage(); // 保存缓存至本地存储
-
-
-  }
 
   //排序模式和多选模式
   showPupDialog() {
@@ -4370,7 +4336,7 @@ export struct LocalMusic {
           this.isCanBack = true
           this.titleBarModel.setTitleName(item.name)
           this.currentTitleName = '专辑:' + item.name
-          this.currentYear = '发行时间:' + item.year
+          this.currentYear = '发行时间:' + albumList[0].year
           if (item.pixelMapPath) {
             this.currentTitleCover = item.pixelMapPath
           }
@@ -5053,6 +5019,21 @@ export struct LocalMusic {
         .margin({ top: 10, bottom: 10 })
         .justifyContent(FlexAlign.Start)
 
+        Row() {
+          Text('风格:')
+            .fontSize(14)
+            .fontColor(Color.White)
+            .margin({ left: 22 })
+          Text(currentItem.genre)
+            .fontSize(14)
+            .margin({ left: 10 })
+            .fontColor(Color.White)
+            .layoutWeight(1)
+        }
+        .width('100%')
+        .margin({ top: 10, bottom: 10 })
+        .justifyContent(FlexAlign.Start)
+
         Row() {
           Text('发行时间:')
             .fontSize(14)
@@ -5083,6 +5064,21 @@ export struct LocalMusic {
         .margin({ top: 10, bottom: 10 })
         .justifyContent(FlexAlign.Start)
 
+        Row() {
+          Text('音轨号:')
+            .fontSize(14)
+            .fontColor(Color.White)
+            .margin({ left: 22 })
+          Text(currentItem.track)
+            .fontSize(14)
+            .margin({ left: 10 })
+            .fontColor(Color.White)
+            .layoutWeight(1)
+        }
+        .width('100%')
+        .margin({ top: 10, bottom: 10 })
+        .justifyContent(FlexAlign.Start)
+
         Row() {
           Text('音轨数:')
             .fontSize(14)
@@ -5128,6 +5124,8 @@ export struct LocalMusic {
         .margin({ top: 10, bottom: 10 })
         .justifyContent(FlexAlign.Start)
 
+
+
         Row() {
           Text('流数量:')
             .fontSize(14)
@@ -5454,6 +5452,7 @@ export struct LocalMusic {
       .width(40)
       .height(40)
       .type(ButtonType.Circle)
+      .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.7})
       .backgroundColor(this.themeColor)
       .margin(5)
       .onClick(() => {
@@ -5467,6 +5466,7 @@ export struct LocalMusic {
       }
       .width(40)
       .height(40)
+      .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.7})
       .type(ButtonType.Circle)
       .backgroundColor(this.themeColor)
       .margin(5)
@@ -5482,6 +5482,7 @@ export struct LocalMusic {
       }
       .width(40)
       .height(40)
+      .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.7})
       .type(ButtonType.Circle)
       .backgroundColor(this.themeColor)
       .margin(5)

+ 3 - 0
entry/src/main/ets/viewmodel/VideoItem.ets

@@ -54,6 +54,9 @@ export class VideoItem  {
   nb_streams?:number//流数量
   nb_programs?:number//节目数量
 
+  genre?:string//风格
+  track?:string//音轨号
+
   constructor(name: string, id: string, filePath: string, type:number,videoSize:number,cTime: string,pixelMap?: image.PixelMap,
     size?: string,pixelMapPath?: string,artist?: string,album?: string,fileName?: string, lastPlayed?:Date) {
     this.name = name;