onecold преди 1 година
родител
ревизия
ecbbb29d03

+ 2 - 2
AppScope/app.json5

@@ -2,8 +2,8 @@
   "app": {
     "bundleName": "com.xgplayer.ttmusic.hm",
     "vendor": "example",
-    "versionCode": 20250806,
-    "versionName": "1.5.0",
+    "versionCode": 20250810,
+    "versionName": "1.5.1",
     "icon": "$media:app_icon",
     "label": "$string:app_name",
     "multiAppMode": {

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

@@ -898,9 +898,14 @@ export class Utility {
             try {
               if (hasCover) {
                   //提取封面
-                  await getFFmpegCover(inputPath, imagePath);
-                  imagePath = fileUri.getUriFromPath(imagePath)
-                  videoItem.pixelMapPath  = imagePath;
+                  let isSuccess:boolean= await getFFmpegCover(inputPath, imagePath);
+
+                  if(isSuccess){
+                    imagePath = fileUri.getUriFromPath(imagePath)
+                  }else{
+                    imagePath = ''
+                  }
+                videoItem.pixelMapPath  = imagePath;
               }else if(Utility.isVideoByExtension(inputPath)){
                 //提取封面
                 await getVideoFFmpegCover(inputPath, imagePath);
@@ -1565,22 +1570,51 @@ function getFileNameWithoutExtension(filePath: string): string {
  * @param inputPath 音乐文件路径
  * @returns Promise<void>
  */
-async function getFFmpegCover(inputPath: string, outputPath: string) {
+// async function getFFmpegCover(inputPath: string, outputPath: string) : Promise<boolean>{
+//   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}`)
+//     },
+//     progressCallback: (message: string) => {
+//       console.info(`[FFmpeg progress]${JSON.stringify(FFProgressMessageParser.parse(message))}`)
+//     },
+//   })
+//     .then(() => {
+//       return true
+//       console.info("FFmpeg execution succeeded.");
+//     })
+//     .catch((error: Error) => {
+//       return false
+//       console.error(`FFmpeg execution failed with error: ${error.message}`);
+//     });
+//   return ;
+// }
+
+/**
+ * 从音乐文件中提取封面
+ * @param inputPath 音乐文件路径
+ * @param outputPath 封面输出路径
+ * @returns Promise<boolean> 表示操作是否成功
+ */
+async function getFFmpegCover(inputPath: string, outputPath: string): Promise<boolean> {
   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}`)
-    },
-    progressCallback: (message: string) => {
-      console.info(`[FFmpeg progress]${JSON.stringify(FFProgressMessageParser.parse(message))}`)
-    },
-  })
-    .then(() => {
-      console.info("FFmpeg execution succeeded.");
-    })
-    .catch((error: Error) => {
-      console.error(`FFmpeg execution failed with error: ${error.message}`);
+
+  try {
+    await FFmpeg.execute(commands,  {
+      logCallback: (logLevel: number, logMessage: string) => {
+        console.info(`[FFmpeg  LOG] [${logLevel}] ${logMessage}`);
+      },
+      progressCallback: (message: string) => {
+        console.info(`[FFmpeg  progress] ${JSON.stringify(FFProgressMessageParser.parse(message))}`);
+      },
     });
+    console.info("FFmpeg  execution succeeded.");
+    return true;
+  } catch (error) {
+    console.error(`FFmpeg  execution failed with error: ${error instanceof Error ? error.message  : String(error)}`);
+    return false;
+  }
 }
 /**
  * 从视频文件中提取封面 提前视频第5帧的封面
@@ -1589,7 +1623,17 @@ async function getFFmpegCover(inputPath: string, outputPath: string) {
  */
 async function getVideoFFmpegCover(inputPath: string, outputPath: string) {
 
-  let commands = ["ffmpeg", "-y","-i", inputPath, "-ss", "00:00:00", "-t", "1","-r",'1','-q:v','2','-f','image2', outputPath];
+  // 更可靠的命令,专门提取视频第一帧作为封面
+  const commands = [
+    "ffmpeg",
+    "-y",                  // 覆盖输出文件(如果存在)
+    "-i", inputPath,       // 输入文件
+    "-ss", "00:00:00",     // 定位到开始位置
+    "-vframes", "1",       // 只提取1帧
+    "-q:v", "2",           // 高质量JPEG(1-31,2是最高质量)
+    "-f", "image2",        // 强制输出为图像格式
+    outputPath
+  ];
   FFmpeg.execute(commands, {
     logCallback: (logLevel: number, logMessage: string) => {
       console.info(`[FFmpegX LOG] [${logLevel}]${logMessage}`)

+ 3 - 2
entry/src/main/ets/pages/SettingPage.ets

@@ -38,7 +38,7 @@ export struct SettingPage {
   static readonly IS_CUSTOMIZE_BG: string = 'is_customize_bg';
   static readonly IS_GRID_MUSIC: string = 'is_grid_music';
   static readonly IS_CUSTOMIZE_BG_PATH: string = 'is_customize_bg_path';
-  static readonly IS_SCROLL_HIDE: string = 'isScrollHide';
+  static readonly IS_SCROLL_HIDE: string = 'isAutoScrollHide';
   static readonly IS_SAMETIME_PLAY: string = 'isSameTimePlay';
   static readonly CUSTOMIZE_BG_BLUR: string = 'customize_bg_blur';
   static readonly BG_BRIGHTNESS: string = 'bg_brightness';
@@ -674,10 +674,11 @@ export struct SettingPage {
                 .height(30);
             }
             .height(55)
+            .visibility(Visibility.None)
             .clickEffect({ level: ClickEffectLevel.HEAVY })
 
             Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
-
+              .visibility(Visibility.None)
             // 网格布局
             Row() {
               Text('网格模式')

+ 1 - 1
entry/src/main/ets/pages/UserCenter.ets

@@ -437,7 +437,7 @@ export struct UserCenter {
       Scroll() {
         Column() {
           this.buildUserInfoCard()
-          if(!Utility.isNoble()||!Utility.isForever()){
+          if(!Utility.isForever()){
             this.buildVipPlans()
           }
           this.buildVipFeatures()

+ 13 - 10
entry/src/main/ets/view/LocalMusic.ets

@@ -55,7 +55,6 @@ import { PlayStatus } from '../common/PlayStatus';
 import fs from '@ohos.file.fs';
 import { image } from '@kit.ImageKit';
 import { AVCastPicker, AVCastPickerState, AVCastPickerStyle, avSession } from '@kit.AVSessionKit';
-import * as chardet from '@changwei/chardet';
 import { UniversalDetector } from '@ohos/juniversalchardet';
 import { SettingPage } from '../pages/SettingPage';
 import { secondToTime } from '../common/util/CommUtils';
@@ -2596,6 +2595,7 @@ export struct LocalMusic {
                   $r('app.color.text_color'))
                   .fontWeight(500)
                   .borderRadius(12)
+                  .visibility(StrUtil.isEmpty(item.md5Str)?Visibility.None:Visibility.Visible)
                   .margin({ left: this.twoFingerType == 3 ? 6 : 8 })
                   .backgroundColor('#FFC107')
               }
@@ -3618,7 +3618,7 @@ export struct LocalMusic {
           Image(StrUtil.isEmpty(item.pixelMapPath) ? Utility.getMusisBg2(index) : item.pixelMapPath)
             .height(this.getGridHeight())
             .width(this.getGridWight())
-            .alt($r('app.media.ic_avatar6'))
+            .alt($r('app.media.ic_avatar2'))
             .clip(true)
             .borderRadius({
               topLeft: 12,
@@ -3674,16 +3674,16 @@ export struct LocalMusic {
                     .borderRadius(12)
                     .margin({ left: this.twoFingerType == 3 ? 6 : 8 })
                     .backgroundColor('#FFC107')
+                    .visibility(StrUtil.isEmpty(item.md5Str)?Visibility.None:Visibility.Visible)
                 }
                 .margin({ top: 2 ,right:6})
                 .visibility((this.modeType == 3 && !this.isCanBack)||(this.modeType == 2 && !this.isCanBack)
                   ||(this.modeType == 0&&item.type==CommonConstants.TYPE_IS_DIR)? Visibility.None : Visibility.Visible)
                 Text(StrUtil.isEmpty(item.artist) ? item.duration: item.artist)
-                  .fontSize(11)
-                  .fontSize(this.twoFingerType == 1 ? 10 : this.twoFingerType == 2 ? 12 : 14)
+                  .fontSize(this.twoFingerType == 1 ? 10 : this.twoFingerType == 2 ? 12 : 13)
                   .maxLines(1)
                   .textOverflow({ overflow: TextOverflow.MARQUEE })//超长滚动
-                  .margin({ top: 2 ,right:18})
+                  .margin({ top: 2})
                   .visibility(item.type == CommonConstants.TYPE_IS_ARTIST
                     || item.type == CommonConstants.TYPE_IS_ALBUM
                     || item.type == CommonConstants.TYPE_IS_DIR ? Visibility.None : Visibility.Visible)
@@ -4521,6 +4521,8 @@ export struct LocalMusic {
               Text(this.currentSong?.artist)
                 .margin({ top: 2 })
                 .fontSize(13)
+                .maxLines(1)
+                .textOverflow({ overflow: TextOverflow.MARQUEE })//超长滚动
                 .fontColor($r('app.color.text_color'))
             }
           }
@@ -6481,8 +6483,9 @@ export struct LocalMusic {
         console.info("onecold 找到本地歌词 ");
       }
       // 3.读取文件内容并指定编码为 UTF-8
-      let file = fs.openSync(realLyricPath, fs.OpenMode.READ_WRITE);
-      let arrayBuffer = new ArrayBuffer(4096);
+      let file = fs.openSync(realLyricPath,  fs.OpenMode.READ_ONLY);
+      const stat = await fileIo.stat(file.fd);
+      const arrayBuffer = new ArrayBuffer(stat.size);
       fs.read(file.fd, arrayBuffer)
         .then((readLen: number) => {
           console.info("read file data succeed");
@@ -6621,8 +6624,9 @@ export struct LocalMusic {
         console.info("onecold 找到本地歌词");
       }
 
-      let file = fs.openSync(realLyricPath, fs.OpenMode.READ_WRITE);
-      let arrayBuffer = new ArrayBuffer(4096);
+      let file = fs.openSync(realLyricPath,  fs.OpenMode.READ_ONLY);
+      const stat = await fileIo.stat(file.fd);
+      const arrayBuffer = new ArrayBuffer(stat.size);
 
       const readLen = await fs.read(file.fd, arrayBuffer);
       console.info("read file data succeed");
@@ -9546,7 +9550,6 @@ export struct LocalMusic {
       bufferSize = 25 * 1024 * 1024; // 设置缓冲区大小为25MB
     }
     // 使用软件解码(通常比硬件解码更节省内存)
-    // this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER,  "mediacodec", '0');
     // this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER,  "mediacodec-auto-rotate", '0');
     // this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER,  "mediacodec-handle-resolution-change", '0');
     // this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT,  "probesize", '1024');