Kaynağa Gözat

Merge remote-tracking branch 'origin/master'

chendeben 1 yıl önce
ebeveyn
işleme
b2566e4963

+ 2 - 2
AppScope/app.json5

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

+ 63 - 3
entry/src/main/ets/common/util/MediaTable.ets

@@ -36,7 +36,7 @@ interface DBColumnsInterface {
  */
  */
 const DB_COLUMNS: DBColumnsInterface = {
 const DB_COLUMNS: DBColumnsInterface = {
   ID: 'id',
   ID: 'id',
-  NAME: 'name', 
+  NAME: 'name',
   FILE_PATH: 'filePath',
   FILE_PATH: 'filePath',
   TYPE: 'mtype',
   TYPE: 'mtype',
   VIDEO_SIZE: 'videoSize',
   VIDEO_SIZE: 'videoSize',
@@ -95,7 +95,7 @@ export default class MediaTable {
     this.accountTable.deleteData(predicates, callback);
     this.accountTable.deleteData(predicates, callback);
   }
   }
 
 
-
+  //更新音乐封面地址
   public updatePixelMapPath(filePath: string, newPixelMapPath: string, callback: Function) {
   public updatePixelMapPath(filePath: string, newPixelMapPath: string, callback: Function) {
     // Step 1: 构建查询条件验证文件存在性
     // Step 1: 构建查询条件验证文件存在性
     const queryPredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
     const queryPredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
@@ -133,7 +133,10 @@ export default class MediaTable {
   }
   }
 
 
   //编辑歌曲的信息更新数据库
   //编辑歌曲的信息更新数据库
-  public updateMediaInfo(filePath: string, title: string, artist: string, album: string, callback: Function) {
+  public updateMediaInfo(filePath: string, title: string, artist: string, album: string,
+    lyricContent:string,year:string,genre:string,track:string,
+    ALBUMARTIST:string,COMPOSER:string,LYRICIST:string,COMMENT:string,disc:string,
+    callback: Function) {
     if (!callback || typeof callback !== 'function') {
     if (!callback || typeof callback !== 'function') {
       Logger.info(RdbUtils.RDB_TAG, 'updateMediaInfo() has no valid callback!');
       Logger.info(RdbUtils.RDB_TAG, 'updateMediaInfo() has no valid callback!');
       return;
       return;
@@ -169,6 +172,34 @@ export default class MediaTable {
       if (album !== '') {
       if (album !== '') {
         valuesToUpdate.album = album;
         valuesToUpdate.album = album;
       }
       }
+      if (lyricContent !== '') {
+        valuesToUpdate.lyricContent = lyricContent;
+      }
+      if (year !== '') {
+        valuesToUpdate.year = year;
+      }
+      if (genre !== '') {
+        valuesToUpdate.genre = genre;
+      }
+      if (track !== '') {
+        valuesToUpdate.track = track;
+      }
+
+      if (ALBUMARTIST !== '') {
+        valuesToUpdate.ALBUMARTIST = ALBUMARTIST;
+      }
+      if (COMPOSER !== '') {
+        valuesToUpdate.COMPOSER = COMPOSER;
+      }
+      if (LYRICIST !== '') {
+        valuesToUpdate.LYRICIST = LYRICIST;
+      }
+      if (COMMENT !== '') {
+        valuesToUpdate.COMMENT = COMMENT;
+      }
+      if (disc !== '') {
+        valuesToUpdate.disc = disc;
+      }
 
 
       resultSet.close();
       resultSet.close();
 
 
@@ -246,6 +277,12 @@ export default class MediaTable {
       obj.channel_layout  = resultSet.getString(resultSet.getColumnIndex('channel_layout'));
       obj.channel_layout  = resultSet.getString(resultSet.getColumnIndex('channel_layout'));
       obj.start_time  = resultSet.getString(resultSet.getColumnIndex('start_time'));
       obj.start_time  = resultSet.getString(resultSet.getColumnIndex('start_time'));
 
 
+      obj.ALBUMARTIST  = resultSet.getString(resultSet.getColumnIndex('ALBUMARTIST'));
+      obj.COMPOSER  = resultSet.getString(resultSet.getColumnIndex('COMPOSER'));
+      obj.LYRICIST  = resultSet.getString(resultSet.getColumnIndex('LYRICIST'));
+      obj.COMMENT  = resultSet.getString(resultSet.getColumnIndex('COMMENT'));
+      obj.disc  = resultSet.getString(resultSet.getColumnIndex('disc'));
+
       const valueBucket: relationalStore.ValuesBucket = obj
       const valueBucket: relationalStore.ValuesBucket = obj
       // Step 4: 执行更新
       // Step 4: 执行更新
       const updatePredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
       const updatePredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
@@ -577,6 +614,12 @@ export default class MediaTable {
     item.channel_layout = safeGet('channel_layout');
     item.channel_layout = safeGet('channel_layout');
     item.start_time = safeGet('start_time');
     item.start_time = safeGet('start_time');
 
 
+    item.ALBUMARTIST = safeGet('ALBUMARTIST');
+    item.COMPOSER = safeGet('COMPOSER');
+    item.LYRICIST = safeGet('LYRICIST');
+    item.COMMENT = safeGet('COMMENT');
+    item.disc = safeGet('disc');
+
     return item;
     return item;
   }
   }
 
 
@@ -684,5 +727,22 @@ function generateBucket(item: VideoItem): relationalStore.ValuesBucket {
     obj.start_time = item.start_time;
     obj.start_time = item.start_time;
   }
   }
 
 
+  if(item.ALBUMARTIST){
+    obj.ALBUMARTIST = item.ALBUMARTIST;
+  }
+  if(item.COMPOSER){
+    obj.COMPOSER = item.COMPOSER;
+  }
+  if(item.LYRICIST){
+    obj.LYRICIST = item.LYRICIST;
+  }
+  if(item.COMMENT){
+    obj.COMMENT = item.COMMENT;
+  }
+  if(item.disc){
+    obj.disc = item.disc;
+  }
+
+
   return obj;
   return obj;
 }
 }

+ 460 - 0
entry/src/main/ets/common/util/MusicTagUtils.ets

@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { FFmpeg, FFProgressMessageParser } from "@sj/ffmpeg";
+import fs from '@ohos.file.fs';
+import { FFMpegTags } from "./Utility";
+import { http } from "@kit.NetworkKit";
+import { BusinessError } from "@kit.BasicServicesKit";
+import ResponseCode from '@ohos.net.http';
+import { LogUtil, PreferencesUtil, StrUtil } from "@pura/harmony-utils";
+import { CommonConstants } from "../constants/CommonConstants";
+
+/**
+ * 修复音频文件的元数据标签
+ * @param inputPath 输入文件路径
+ * @param metadata 要更新的元数据对象
+ * @param overwrite 是否覆盖源文件(可选,默认false)
+ * @param outputPath 指定输出路径(可选,优先级高于overwrite)
+ * @returns Promise<boolean> 成功返回true,失败返回false
+ */
+export async function repairAudioMetadata(
+  inputPath: string,
+  lyrics:string,
+  metadata: FFMpegTags,
+  overwrite: boolean = false,
+  outputPath: string = ''
+): Promise<boolean> {
+  // 确保metadata是对象类型
+  if (typeof metadata !== 'object' || metadata === null) {
+    console.error('元数据参数必须是一个对象');
+    return false;
+  }
+
+  // 确定输出路径
+  let finalOutputPath: string = '';
+  let useTempFile: boolean = false;
+
+  if (outputPath && outputPath.length > 0) {
+    finalOutputPath = outputPath;
+  } else if (overwrite) {
+    // 使用临时文件方式处理覆盖,保持原文件扩展名
+    const timestamp = new Date().getTime();
+    const lastDotIndex = inputPath.lastIndexOf('.');
+    if (lastDotIndex >= 0) {
+      finalOutputPath = inputPath.substring(0, lastDotIndex) + '_' + timestamp + inputPath.substring(lastDotIndex);
+    } else {
+      finalOutputPath = inputPath + '_' + timestamp;
+    }
+    useTempFile = true;
+  } else {
+    const lastDotIndex = inputPath.lastIndexOf('.');
+    if (lastDotIndex >= 0) {
+      finalOutputPath = inputPath.substring(0, lastDotIndex) + '_tagged' + inputPath.substring(lastDotIndex);
+    } else {
+      finalOutputPath = inputPath + '_tagged';
+    }
+  }
+
+  // 构建FFmpeg命令
+  const commands: string[] = [
+    "ffmpeg",
+    "-i", inputPath
+  ];
+
+  // 添加元数据参数
+  const dynamicMetadata = metadata as Record<string, string>;
+  Object.keys(dynamicMetadata).forEach((key) => {
+    const value = dynamicMetadata[key];
+    if (value) {
+      commands.push("-metadata");
+      commands.push(`${key}=${value}`);
+    }
+  });
+  //额外再次添加下lyrics-XXX的歌词参数,以便其他音乐播放器可以识别歌词
+  // 额外添加歌词自定义标签(修复后的核心代码)
+  if (StrUtil.isNotEmpty(lyrics)) { // 确保歌词内容存在时才添加
+    // 多种歌词标签格式
+    const lyricTags = [
+      `LYRICS=${lyrics}`,
+      `lyrics-XXX=${lyrics}`,
+      `USLT::XXX=${lyrics}`,
+    // `UNSYNCEDLYRICS=${lyrics}`
+    ];
+
+    lyricTags.forEach(tag  => {
+      commands.push("-metadata");
+      commands.push(tag);
+    });
+  }
+
+
+  commands.push(
+    "-map", "0",
+    "-map_metadata", "0",
+    "-id3v2_version", "3",
+    "-codec", "copy",
+    "-y",
+    finalOutputPath
+  );
+
+  try {
+    await FFmpeg.execute(commands, {
+      logCallback: (logLevel: number, logMessage: string) => {
+        //console.log(`onecold logCallback [${logLevel}]${logMessage}`);
+      },
+      progressCallback: (message: string) => {
+        //console.log(`onecold progressCallback [progress]${JSON.stringify(FFProgressMessageParser.parse(message))}`);
+      },
+    });
+
+    // 如果是使用临时文件,需要替换原文件
+    if (useTempFile) {
+      try {
+        console.info(`onecold unlinkSync`);
+        // 先删除原文件,再重命名临时文件
+        await fs.unlinkSync(inputPath);
+        console.info(`onecold unlinkSync2`);
+        fs.renameSync(finalOutputPath, inputPath);
+        console.info(`onecold renameSync`);
+        finalOutputPath = inputPath; // 更新为最终路径
+      } catch (renameError) {
+        console.error(`onecold 文件替换失败: ${JSON.stringify(renameError)}`);
+        return false;
+      }
+    }
+
+    console.info(`onecold 元数据修复成功,保存路径: ${finalOutputPath}`);
+
+    // 验证文件是否存在
+    try {
+      const file = await fs.open(finalOutputPath, fs.OpenMode.READ_ONLY);
+      await fs.close(file.fd);
+      return true;
+    } catch (e) {
+      console.error(`onecold 文件验证失败: ${JSON.stringify(e)}`);
+      return false;
+    }
+
+  } catch (error) {
+    let errorMsg: string = '';
+    if (error instanceof Error) {
+      errorMsg = error.message;
+    } else {
+      errorMsg = String(error);
+    }
+    console.error(`onecold元数据修复失败 ${errorMsg}`);
+
+    // 清理临时文件(如果存在)
+    if (useTempFile) {
+      try {
+        if (fs.accessSync(finalOutputPath)) {
+          fs.unlinkSync(finalOutputPath);
+        }
+      } catch (cleanupError) {
+        console.warn('清理临时文件失败:', cleanupError);
+      }
+    }
+
+    return false;
+  }
+}
+
+
+
+
+/**
+ * 修改音乐文件的封面
+ * @param inputPath 音乐文件路径
+ * @param coverImagePath 封面图片路径(支持HTTP地址或本地地址)
+ * @param overwrite 是否覆盖源文件(可选,默认false)
+ * @param outputPath 指定输出路径(可选,优先级高于overwrite)
+ * @returns Promise<boolean> 成功返回true,失败返回false
+ */
+/**
+ * 修改音乐文件的封面
+ * @param context 上下文对象
+ * @param inputPath 音乐文件路径
+ * @param coverImagePath 封面图片路径(支持HTTP地址或本地地址)
+ * @param overwrite 是否覆盖源文件(可选,默认false)
+ * @param outputPath 指定输出路径(可选,优先级高于overwrite)
+ * @returns Promise<boolean> 成功返回true,失败返回false
+ */
+export async function changeMusicCover(
+  context: Context,
+  inputPath: string,
+  coverImagePath: string,
+  overwrite: boolean = false,
+  outputPath: string = ''
+): Promise<boolean> {
+  // 确定输出路径
+  let finalOutputPath: string = '';
+  let useTempFile: boolean = false;
+
+  if (outputPath && outputPath.length > 0) {
+    finalOutputPath = outputPath;
+  } else if (overwrite) {
+    // 使用临时文件方式处理覆盖,保持原文件扩展名
+    const timestamp = new Date().getTime();
+    const lastDotIndex = inputPath.lastIndexOf('.');
+    if (lastDotIndex >= 0) {
+      finalOutputPath = inputPath.substring(0, lastDotIndex) + '_' + timestamp + inputPath.substring(lastDotIndex);
+    } else {
+      finalOutputPath = inputPath + '_' + timestamp;
+    }
+    useTempFile = true;
+  } else {
+    const lastDotIndex = inputPath.lastIndexOf('.');
+    if (lastDotIndex >= 0) {
+      finalOutputPath = inputPath.substring(0, lastDotIndex) + '_covered' + inputPath.substring(lastDotIndex);
+    } else {
+      finalOutputPath = inputPath + '_covered';
+    }
+  }
+
+  // 如果是网络图片,先下载到临时文件
+  let tempCoverPath = coverImagePath;
+  console.log(`onecold 开始下载 coverImagePath = [${coverImagePath}]`);
+  if (coverImagePath.startsWith('http')) {
+    try {
+      // 创建临时文件路径
+      const tempDir = context.filesDir + '/'; // 默认缓存目录
+      const tempFileName = 'temp_cover.jpg';
+      tempCoverPath = `${tempDir}${tempFileName}`;
+
+      // 下载图片
+      const result = await loadImageWithUrl(coverImagePath, tempCoverPath);
+      if (!result) {
+        console.error('onecold 下载封面图片失败');
+        // 清理可能已创建的临时文件
+        if (tempCoverPath !== coverImagePath) {
+          try {
+            fs.unlinkSync(tempCoverPath);
+          } catch (unlinkError) {
+            console.warn('onecold 清理临时文件失败:', unlinkError);
+          }
+        }
+        return false;
+      }
+    } catch (error) {
+      console.error('onecold 下载封面图片时出错:', error);
+      // 清理可能已创建的临时文件
+      if (tempCoverPath !== coverImagePath) {
+        try {
+          fs.unlinkSync(tempCoverPath);
+        } catch (unlinkError) {
+          console.warn('onecold 清理临时文件失败:', unlinkError);
+        }
+      }
+      return false;
+    }
+  }
+  console.log(`onecold 开始下载 tempCoverPath = [${tempCoverPath}]`);
+  // 构建FFmpeg命令
+  const commands: string[] = [
+    "ffmpeg",
+    "-i", inputPath,
+    "-i", tempCoverPath,
+    "-map", "0:0",           // 映射音频流
+    "-map", "1:0",           // 映射封面图片流
+    "-c", "copy",            // 复制音频流
+    "-id3v2_version", "3",   // ID3v2版本
+    "-y",                    // 覆盖输出文件
+    finalOutputPath
+  ];
+
+  try {
+    await FFmpeg.execute(commands, {
+      logCallback: (logLevel: number, logMessage: string) => {
+        //console.log(`onecold [FFmpeg LOG] [${logLevel}]${logMessage}`);
+      },
+      progressCallback: (message: string) => {
+        //console.log(`onecold [FFmpeg progress]${JSON.stringify(FFProgressMessageParser.parse(message))}`);
+      },
+    });
+
+    // 如果是使用临时文件,需要替换原文件
+    if (useTempFile) {
+      try {
+        // 先删除原文件,再重命名临时文件
+        await fs.unlinkSync(inputPath);
+        fs.renameSync(finalOutputPath, inputPath);
+        finalOutputPath = inputPath; // 更新为最终路径
+      } catch (renameError) {
+        console.error(`onecold 文件替换失败: ${JSON.stringify(renameError)}`);
+        return false;
+      }
+    }
+
+    console.info(`onecold 封面修改成功,保存路径: ${finalOutputPath}`);
+
+    // 验证文件是否存在
+    try {
+      const file = await fs.open(finalOutputPath, fs.OpenMode.READ_ONLY);
+      await fs.close(file.fd);
+
+      // 如果是下载的临时图片,清理临时文件
+      if (tempCoverPath !== coverImagePath) {
+        try {
+          fs.unlinkSync(tempCoverPath);
+        } catch (unlinkError) {
+          console.warn('onecold 清理临时文件失败:', unlinkError);
+        }
+      }
+
+      return true;
+    } catch (e) {
+      console.error(`onecold 文件验证失败: ${JSON.stringify(e)}`);
+      return false;
+    }
+
+  } catch (error) {
+    let errorMsg: string = '';
+    if (error instanceof Error) {
+      errorMsg = error.message;
+    } else {
+      errorMsg = String(error);
+    }
+    console.error(`onecold封面修改失败: ${errorMsg}`);
+
+    // 清理临时文件
+    if (tempCoverPath !== coverImagePath) {
+      try {
+        fs.unlinkSync(tempCoverPath);
+      } catch (unlinkError) {
+        console.warn('onecold 清理临时文件失败:', unlinkError);
+      }
+    }
+
+    // 如果使用了临时文件但处理失败,也需要清理
+    if (useTempFile && fs.accessSync(finalOutputPath)) {
+      try {
+        fs.unlinkSync(finalOutputPath);
+      } catch (cleanupError) {
+        console.warn('onecold 清理临时输出文件失败:', cleanupError);
+      }
+    }
+
+    return false;
+  }
+}
+
+
+/**
+ * 下载图片到指定路径
+ * @param url 图片URL
+ * @param outputPath 输出路径
+ * @returns Promise<boolean> 成功返回true,失败返回false
+ */
+export async function loadImageWithUrl( url: string, outputPath: string,): Promise<boolean > {
+  return new Promise((resolve, reject) => {
+    http.createHttp().request(url, { method: http.RequestMethod.GET, connectTimeout: 60000, readTimeout: 60000 },
+      async (error: BusinessError, data: http.HttpResponse) => {
+        if (error) {
+          console.error(`http request failed with. Code: ${error.code}, message: ${error.message}`);
+          reject(false);
+        } else {
+          if (ResponseCode.ResponseCode.OK === data.responseCode) {
+            let imageBuffer: ArrayBuffer = data.result as ArrayBuffer;
+            try {
+              // 获取相册路径
+              let file = await fs.open(outputPath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
+              // 写入文件
+              await fs.write(file.fd, imageBuffer);
+              // 关闭文件
+              await fs.close(file.fd);
+
+              // 返回文件标识符
+              resolve(true);
+            } catch (error) {
+              console.error("error is " + JSON.stringify(error));
+              reject(false);
+            }
+          } else {
+            console.error("error occurred when image downloaded!");
+            reject(false);
+          }
+        }
+      });
+  });
+}
+
+
+export function getApiLyric(apiUrl:string,title: string, artist: string, isApi2: boolean): Promise<string> {
+  return new Promise(async (resolve) => {
+    try {
+      if (StrUtil.isNotEmpty(title)  && title === '全世界最好的你') {
+        artist = '';
+      }
+
+      // let baseUrl = PreferencesUtil.getStringSync('LRC_API',  '');
+      if (apiUrl == '') {
+        LogUtil.debug("Heanup  未设置API");
+        resolve('');
+        return;
+      }
+
+      if (apiUrl.includes(CommonConstants.LRC_API_2))  {
+        isApi2 = true;
+      }
+
+      let requestUrl = apiUrl +
+        '?title=' + encodeURIComponent(title.trim())  +
+        '&artist=' + encodeURIComponent(artist.trim());
+      console.info(`onecold requestUrl = `+requestUrl);
+      const httpRequest = http.createHttp();
+      const options: http.HttpRequestOptions = {
+        method: http.RequestMethod.GET,
+        readTimeout: 3000,
+        connectTimeout: 3000,
+      };
+
+      const response: http.HttpResponse = await httpRequest.request(requestUrl,  options);
+      console.info(`onecold requestUrl 00= `+response.responseCode);
+      if (response.responseCode  === 200) {
+        let res = response.result  as string;
+        let fileContent = '';
+
+        if (isApi2) {
+          fileContent = res;
+          console.info(`onecold fileContent 1111= `+fileContent);
+        } else {
+          console.info(`onecold fileContent 22= `+fileContent);
+          let parsedData: lyricInfo[] = JSON.parse(res)  as lyricInfo[];
+          fileContent = parsedData[0].lyrics || '';
+        }
+
+        LogUtil.debug("onecold  fileContent 11 =" + fileContent);
+        resolve(fileContent);
+        return;
+      }
+
+      console.log('onecold  getLyric--失败', JSON.stringify(response));
+      resolve('');
+    } catch (error) {
+      console.error('onecold  getLyric catch--失败' + JSON.stringify(error));
+      resolve('');
+    }
+  });
+}
+
+
+interface lyricInfo{
+  code:number
+  album:number
+  artist:string
+  lyrics:string
+  cover_url:string
+  status:string
+}

+ 25 - 12
entry/src/main/ets/common/util/RdbUtils.ets

@@ -75,13 +75,21 @@ export default class RdbUtils {
       '        channel_layout TEXT,\n' +
       '        channel_layout TEXT,\n' +
       '        start_time TEXT,\n' +
       '        start_time TEXT,\n' +
 
 
+      '        ALBUMARTIST TEXT,\n' +
+      '        COMPOSER TEXT,\n' +
+      '        LYRICIST TEXT,\n' +
+      '        COMMENT TEXT,\n' +
+      '        disc TEXT,\n' +
+
       '        mimeType TEXT' +
       '        mimeType TEXT' +
       ')',
       ')',
     columns: ['id', 'name', 'filePath','mtype', 'videoSize', 'cTime','size', 'artist', 'album',
     columns: ['id', 'name', 'filePath','mtype', 'videoSize', 'cTime','size', 'artist', 'album',
       'fileName','parentPath','isFav','pixelMapPath','pixelMapToString',
       'fileName','parentPath','isFav','pixelMapPath','pixelMapToString',
       'duration',   'sampleRate','playCount',  'lastPlayedStr', 'trackCount',
       'duration',   'sampleRate','playCount',  'lastPlayedStr', 'trackCount',
       'lyricContent','md5Str','extra_json','pyStr','bit_rate','probe_score','year','nb_streams','nb_programs',
       'lyricContent','md5Str','extra_json','pyStr','bit_rate','probe_score','year','nb_streams','nb_programs',
-      'genre','track',  'bits_per_raw_sample','channels',  'channel_layout','start_time',  'mimeType']
+      'genre','track',  'bits_per_raw_sample','channels',  'channel_layout','start_time',
+      'ALBUMARTIST',  'COMPOSER','LYRICIST','COMMENT','COMMENT','disc',
+      'mimeType']
   };
   };
 
 
   constructor(tableName: string, sqlCreateTable: string, columns: Array<string>) {
   constructor(tableName: string, sqlCreateTable: string, columns: Array<string>) {
@@ -103,22 +111,22 @@ export default class RdbUtils {
       callback();
       callback();
       return
       return
     }
     }
-    
+
     relationalStore.getRdbStore(context, RdbUtils.STORE_CONFIG, (err, rdb) => {
     relationalStore.getRdbStore(context, RdbUtils.STORE_CONFIG, (err, rdb) => {
       if (err) {
       if (err) {
         Logger.error(RdbUtils.RDB_TAG, `gerRdbStore() failed, err: ${err}`);
         Logger.error(RdbUtils.RDB_TAG, `gerRdbStore() failed, err: ${err}`);
         return;
         return;
       }
       }
       this.rdbStore = rdb;
       this.rdbStore = rdb;
-      
+
       // 先创建表(如果不存在)
       // 先创建表(如果不存在)
       this.rdbStore.executeSql(this.sqlCreateTable);
       this.rdbStore.executeSql(this.sqlCreateTable);
-      
+
       // 检查数据库版本并更新列
       // 检查数据库版本并更新列
       try {
       try {
         // 检查表结构
         // 检查表结构
         this.checkAndUpdateTableColumns();
         this.checkAndUpdateTableColumns();
-        Logger.info(RdbUtils.RDB_TAG, `数据库表结构检查完成`);
+        // Logger.info(RdbUtils.RDB_TAG, `数据库表结构检查完成`);
       } catch (e) {
       } catch (e) {
         Logger.error(RdbUtils.RDB_TAG, `检查表结构时出错: ${e.message}`);
         Logger.error(RdbUtils.RDB_TAG, `检查表结构时出错: ${e.message}`);
       }
       }
@@ -126,7 +134,7 @@ export default class RdbUtils {
       callback();
       callback();
     });
     });
   }
   }
-  
+
   /**
   /**
    * 检查并更新表列结构
    * 检查并更新表列结构
    * 使用表信息查询和ALTER TABLE添加缺失的列
    * 使用表信息查询和ALTER TABLE添加缺失的列
@@ -136,7 +144,7 @@ export default class RdbUtils {
       Logger.error(RdbUtils.RDB_TAG, `数据库连接未初始化`);
       Logger.error(RdbUtils.RDB_TAG, `数据库连接未初始化`);
       return;
       return;
     }
     }
-    
+
     try {
     try {
       // 查询表信息获取现有列
       // 查询表信息获取现有列
       const tableInfoQuery = `PRAGMA table_info(${this.tableName})`;
       const tableInfoQuery = `PRAGMA table_info(${this.tableName})`;
@@ -169,8 +177,13 @@ export default class RdbUtils {
             'channel_layout': 'TEXT',
             'channel_layout': 'TEXT',
             'start_time': 'TEXT',
             'start_time': 'TEXT',
 
 
+            'ALBUMARTIST': 'TEXT',
+            'COMPOSER': 'TEXT',
+            'LYRICIST': 'TEXT',
+            'COMMENT': 'TEXT',
+            'disc': 'TEXT',
           };
           };
-          
+
           // 逐个添加列,不依赖于检查结果
           // 逐个添加列,不依赖于检查结果
           if (this.rdbStore) {
           if (this.rdbStore) {
             // 遍历映射
             // 遍历映射
@@ -178,7 +191,7 @@ export default class RdbUtils {
             for (let i = 0; i < columnEntries.length; i++) {
             for (let i = 0; i < columnEntries.length; i++) {
               const column = columnEntries[i];
               const column = columnEntries[i];
               const type = requiredColumns[column];
               const type = requiredColumns[column];
-              
+
               const alterSql = `ALTER TABLE ${this.tableName} ADD COLUMN ${column} ${type}`;
               const alterSql = `ALTER TABLE ${this.tableName} ADD COLUMN ${column} ${type}`;
               try {
               try {
                 // 对每个列使用Promise模式
                 // 对每个列使用Promise模式
@@ -188,17 +201,17 @@ export default class RdbUtils {
                   })
                   })
                   .catch((alterErr: Error) => {
                   .catch((alterErr: Error) => {
                     // 列可能已经存在,这是预期的错误
                     // 列可能已经存在,这是预期的错误
-                    Logger.info(RdbUtils.RDB_TAG, `列 ${column} 可能已存在: ${alterErr.message}`);
+                    // Logger.info(RdbUtils.RDB_TAG, `列 ${column} 可能已存在: ${alterErr.message}`);
                   });
                   });
               } catch (e) {
               } catch (e) {
                 Logger.error(RdbUtils.RDB_TAG, `添加列 ${column} 出错: ${e.message}`);
                 Logger.error(RdbUtils.RDB_TAG, `添加列 ${column} 出错: ${e.message}`);
               }
               }
             }
             }
-            
+
             // 设置数据库版本
             // 设置数据库版本
             if (this.rdbStore) {
             if (this.rdbStore) {
               this.rdbStore.version = 1;
               this.rdbStore.version = 1;
-              Logger.info(RdbUtils.RDB_TAG, `数据库升级完成,版本设置为 1`);
+              // Logger.info(RdbUtils.RDB_TAG, `数据库升级完成,版本设置为 1`);
             }
             }
           }
           }
         })
         })

+ 18 - 19
entry/src/main/ets/common/util/UserUtil.ets

@@ -383,25 +383,24 @@ export default class UserUtil {
     }
     }
   }
   }
 
 
-  // 华为登录错误处理
-  static dealHuaweiLoginError(error: BusinessError): void {
-    if (error.code === 1001) {
-      ToastUtil.showToast('未登录华为账号');
-    } else if (error.code === 1002) {
-      ToastUtil.showToast('网络异常');
-    } else if (error.code === 1003) {
-      ToastUtil.showToast('内部错误');
-    } else if (error.code === 1004) {
-      ToastUtil.showToast('用户取消授权');
-    } else if (error.code === 1005) {
-      ToastUtil.showToast('系统服务异常');
-    } else if (error.code === 1006) {
-      ToastUtil.showToast('请求被拒绝');
-    } else if (error.code === 1007) {
-      ToastUtil.showToast('无权限');
-    } else {
-      ToastUtil.showToast('登录失败');
-    }
+
+  /**
+   * 退出登录
+   * 清除用户登录状态和相关信息
+   */
+  static logout(): boolean {
+    // 清除登录状态
+    PreferencesUtil.deleteSync('isLogin');
+    PreferencesUtil.deleteSync('userId');
+    PreferencesUtil.deleteSync('userName');
+    PreferencesUtil.deleteSync('userAvatarUrl');
+    PreferencesUtil.deleteSync('subscriptionName');
+    PreferencesUtil.deleteSync('subscriptionEndDate');
+    PreferencesUtil.deleteSync('hasActiveSubscription');
+    PreferencesUtil.deleteSync('userToken');
+    PreferencesUtil.deleteSync('isForever');
+    // 显示退出登录提示
+    return true;
   }
   }
 
 
   /**
   /**

+ 55 - 15
entry/src/main/ets/common/util/Utility.ets

@@ -25,7 +25,7 @@ import { VipData } from '../../viewmodel/VipData';
 import { VipPage } from '../../pages/VipPage';
 import { VipPage } from '../../pages/VipPage';
 import { FFmpeg, FFProgressMessageParser } from '@sj/ffmpeg';
 import { FFmpeg, FFProgressMessageParser } from '@sj/ffmpeg';
 
 
-interface FFMpegTags {
+export interface FFMpegTags {
   album?: string;
   album?: string;
   ALBUM?: string;
   ALBUM?: string;
   artist?: string;
   artist?: string;
@@ -45,6 +45,19 @@ interface FFMpegTags {
   USLT?: string;         // ID3v2同步歌词
   USLT?: string;         // ID3v2同步歌词
   UNSYNCEDLYRICS?: string; // ID3v2非同步歌词
   UNSYNCEDLYRICS?: string; // ID3v2非同步歌词
   // Add any other tag properties you expect
   // Add any other tag properties you expect
+  ALBUMARTIST?:string//专辑艺术家
+  COMPOSER?:string//作曲家
+  LYRICIST?:string//作词家
+  COMMENT?:string//注释
+  albumartist?:string//专辑艺术家
+  TPE2?:string//专辑艺术家
+  composer?:string//作曲家
+  lyricist?:string//作词家
+  TEXT?:string//作词家
+  comment?:string//注释
+  COMM?:string//注释
+  comm?:string//注释
+  disc?:string//音碟号
 }
 }
 
 
 interface FFprobeFormat {
 interface FFprobeFormat {
@@ -898,10 +911,35 @@ export class Utility {
             videoItem.probe_score  = format.probe_score;
             videoItem.probe_score  = format.probe_score;
             videoItem.nb_streams  = format.nb_streams;
             videoItem.nb_streams  = format.nb_streams;
             videoItem.nb_programs  = format.nb_programs;
             videoItem.nb_programs  = format.nb_programs;
-            videoItem.year  = tags.TYER || tags.date ||tags.DATE|| Utility.resourceToString(context, $r('app.string.unknown')); // try different tag names for year
+            videoItem.year  = tags.TYER || tags.date ||tags.DATE|| ''; // try different tag names for year
             videoItem.lyricContent  = tags.LYRICS || tags.lyrics  ||  tags.USLT || tags.UNSYNCEDLYRICS || '';
             videoItem.lyricContent  = tags.LYRICS || tags.lyrics  ||  tags.USLT || tags.UNSYNCEDLYRICS || '';
-            videoItem.genre  = tags.genre||tags.GENRE|| Utility.resourceToString(context, $r('app.string.unknown'));
-            videoItem.track  = tags.track||tags.TRACK|| Utility.resourceToString(context, $r('app.string.unknown'));
+            //console.info('readMetaInfoFFmpeg   videoItem.lyricContent: ',  videoItem.lyricContent);
+            // 如果标准字段没有歌词,则尝试解析 `lyrics-` 字段
+            // tags 必须是 Record<string, string | undefined>(或 any)
+            // 如果还是没有歌词内容,尝试查找自定义的lyrics-开头的属性(电脑版音乐标签内嵌歌词就是lyrics-XXX)
+            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;
+                }
+              }
+            }
+
+            videoItem.genre  = tags.genre||tags.GENRE|| '';
+            videoItem.track  = tags.track||tags.TRACK|| '';
+
+            videoItem.ALBUMARTIST  = tags.ALBUMARTIST||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.bits_per_raw_sample = bits_per_raw_sample ||'1'
             videoItem.channel_layout = channel_layout || ''
             videoItem.channel_layout = channel_layout || ''
@@ -920,14 +958,14 @@ export class Utility {
             // 如果有封面图片,则提取
             // 如果有封面图片,则提取
             try {
             try {
               if (hasCover) {
               if (hasCover) {
-                  //提取封面
-                  let isSuccess:boolean= await getFFmpegCover(inputPath, imagePath);
-
-                  if(isSuccess){
-                    imagePath = fileUri.getUriFromPath(imagePath)
-                  }else{
-                    imagePath = ''
-                  }
+                //提取封面
+                let isSuccess:boolean= await getFFmpegCover(inputPath, imagePath);
+
+                if(isSuccess){
+                  imagePath = fileUri.getUriFromPath(imagePath)
+                }else{
+                  imagePath = ''
+                }
                 videoItem.pixelMapPath  = imagePath;
                 videoItem.pixelMapPath  = imagePath;
               }else if(Utility.isVideoByExtension(inputPath)){
               }else if(Utility.isVideoByExtension(inputPath)){
                 pixelMap = await Utility.getFetchFrameByTime(inputPath)
                 pixelMap = await Utility.getFetchFrameByTime(inputPath)
@@ -1077,7 +1115,7 @@ export class Utility {
     for(let i=0;i<localList.length;i++){
     for(let i=0;i<localList.length;i++){
       let pix = localList[i].pixelMapPath
       let pix = localList[i].pixelMapPath
       if(pix){
       if(pix){
-       return pix
+        return pix
       }
       }
 
 
     }
     }
@@ -1464,7 +1502,7 @@ function fetchAlbumCover(avMetadataExtractor: media.AVMetadataExtractor): Promis
       } else {
       } else {
         resolve(pixelMap);
         resolve(pixelMap);
       }
       }
-       avMetadataExtractor.release();
+      avMetadataExtractor.release();
     });
     });
   });
   });
 }
 }
@@ -1950,4 +1988,6 @@ function getFileFormatByPath(filePath: string): string {
 
 
   // 返回点号后的部分(转换为小写)
   // 返回点号后的部分(转换为小写)
   return filePath.substring(lastDotIndex  + 1).toLowerCase();
   return filePath.substring(lastDotIndex  + 1).toLowerCase();
-}
+}
+
+

+ 5 - 11
entry/src/main/ets/pages/UserCenter.ets

@@ -129,6 +129,7 @@ export struct UserCenter {
   @State hasActiveSubscription: boolean = false;
   @State hasActiveSubscription: boolean = false;
   @State subscriptionName: string = '';
   @State subscriptionName: string = '';
   @State subscriptionEndDate: string = '';
   @State subscriptionEndDate: string = '';
+  @State isForever: boolean = false;
   @StorageProp('currentBreakpoint') currentBreakpoint: string = BreakpointTypeEnum.MD;
   @StorageProp('currentBreakpoint') currentBreakpoint: string = BreakpointTypeEnum.MD;
   @State vipFeatures: VipFeature[] = [
   @State vipFeatures: VipFeature[] = [
     {
     {
@@ -437,7 +438,7 @@ export struct UserCenter {
       Scroll() {
       Scroll() {
         Column() {
         Column() {
           this.buildUserInfoCard()
           this.buildUserInfoCard()
-          if(!Utility.isForever()){
+          if(!this.isForever){
             this.buildVipPlans()
             this.buildVipPlans()
           }
           }
           this.buildVipFeatures()
           this.buildVipFeatures()
@@ -552,15 +553,8 @@ export struct UserCenter {
                 this.hasActiveSubscription = false;
                 this.hasActiveSubscription = false;
                 this.subscriptionName = '';
                 this.subscriptionName = '';
                 this.subscriptionEndDate = '';
                 this.subscriptionEndDate = '';
-                PreferencesUtil.putSync('isLogin', false);
-                PreferencesUtil.putSync('userId', 0);
-                PreferencesUtil.putSync('userName', '未登录用户');
-                PreferencesUtil.putSync('userAvatarUrl', '');
-                PreferencesUtil.putSync('subscriptionName', '');
-                PreferencesUtil.putSync('subscriptionEndDate', '');
-                PreferencesUtil.putSync('hasActiveSubscription', false);
-                PreferencesUtil.putSync('userToken', '');
-                PreferencesUtil.putSync('isForever', false);
+                this.isForever=false;
+                UserUtil.logout();
                 emitter.emit({ eventId: 1001 }, {})
                 emitter.emit({ eventId: 1001 }, {})
                 ToastUtil.showToast('已退出登录')
                 ToastUtil.showToast('已退出登录')
               })
               })
@@ -1178,7 +1172,7 @@ export struct UserCenter {
     this.subscriptionName = PreferencesUtil.getStringSync('subscriptionName', '');
     this.subscriptionName = PreferencesUtil.getStringSync('subscriptionName', '');
     this.subscriptionEndDate = PreferencesUtil.getStringSync('subscriptionEndDate', '');
     this.subscriptionEndDate = PreferencesUtil.getStringSync('subscriptionEndDate', '');
     this.hasActiveSubscription = PreferencesUtil.getBooleanSync('hasActiveSubscription', false);
     this.hasActiveSubscription = PreferencesUtil.getBooleanSync('hasActiveSubscription', false);
-    // console.log('Heanup hasActiveSubscription:' + this.hasActiveSubscription)
+    this.isForever= PreferencesUtil.getBooleanSync('isForever', false);
     // 新增:同步线上会员状态到本地,便于 Utility.isNoble 全局判断
     // 新增:同步线上会员状态到本地,便于 Utility.isNoble 全局判断
     PreferencesUtil.putSync('hasActiveSubscription', this.hasActiveSubscription);
     PreferencesUtil.putSync('hasActiveSubscription', this.hasActiveSubscription);
   }
   }

+ 536 - 141
entry/src/main/ets/view/LocalMusic.ets

@@ -21,7 +21,6 @@ import { unifiedDataChannel, uniformDataStruct, uniformTypeDescriptor } from '@k
 import { CommonConstants } from '../common/constants/CommonConstants';
 import { CommonConstants } from '../common/constants/CommonConstants';
 import Logger from '../common/util/Logger';
 import Logger from '../common/util/Logger';
 import { photoAccessHelper } from '@kit.MediaLibraryKit';
 import { photoAccessHelper } from '@kit.MediaLibraryKit';
-import { Utility } from '../common/util/Utility';
 import { BusinessError, emitter } from '@kit.BasicServicesKit';
 import { BusinessError, emitter } from '@kit.BasicServicesKit';
 import { BubbleBean } from '../viewmodel/BubbleBean';
 import { BubbleBean } from '../viewmodel/BubbleBean';
 import { PopupPosition, XPopup } from '@chinalike/popup';
 import { PopupPosition, XPopup } from '@chinalike/popup';
@@ -36,6 +35,8 @@ import { effectKit } from '@kit.ArkGraphics2D';
 import { ColorConversion } from '../common/util/ColorConversion';
 import { ColorConversion } from '../common/util/ColorConversion';
 import { LyricController, LyricParser, LyricView2 } from '@seagazer/cclyric';
 import { LyricController, LyricParser, LyricView2 } from '@seagazer/cclyric';
 import { AvSessionController } from '../controller/AvSessionController';
 import { AvSessionController } from '../controller/AvSessionController';
+import { repairAudioMetadata,  getApiLyric,changeMusicCover} from '../common/util/MusicTagUtils';
+import { FFMpegTags,Utility } from '../common/util/Utility';
 import {
 import {
   // DeviceChangeReason,
   // DeviceChangeReason,
   IjkMediaPlayer,
   IjkMediaPlayer,
@@ -625,7 +626,7 @@ export struct LocalMusic {
     this.isPlayListBgGrass = PreferencesUtil.getBooleanSync(SettingPage.IS_PLAYLIST_BG_GRASS, true)
     this.isPlayListBgGrass = PreferencesUtil.getBooleanSync(SettingPage.IS_PLAYLIST_BG_GRASS, true)
     this.isShowHeader = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_HEADER, true)
     this.isShowHeader = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_HEADER, true)
     this.volume = PreferencesUtil.getNumberSync('DefalutVolume', this.volume)
     this.volume = PreferencesUtil.getNumberSync('DefalutVolume', this.volume)
-    this.isSwipe = PreferencesUtil.getBooleanSync(SettingPage.IS_SWIPE, true)
+    this.isSwipe = PreferencesUtil.getBooleanSync(SettingPage.IS_SWIPE, false)
     this.openSkipSongAnimate = PreferencesUtil.getBooleanSync(SettingPage.OPEN_SKIPSONG_ANIMATE, true)
     this.openSkipSongAnimate = PreferencesUtil.getBooleanSync(SettingPage.OPEN_SKIPSONG_ANIMATE, true)
     //如果是hicar连接状态,这些都是false
     //如果是hicar连接状态,这些都是false
     if(this.isHiCar()){
     if(this.isHiCar()){
@@ -1583,58 +1584,57 @@ export struct LocalMusic {
   //
   //
   // }
   // }
 
 
-  goSelectImage(item: VideoItem) {
+  async goSelectImage(item: VideoItem): Promise<string | undefined> {
     if (!item) {
     if (!item) {
-      return
+      return undefined;
     }
     }
-    let selectUris: Array<string> = [];
-    let photoPicker = new photoAccessHelper.PhotoViewPicker();
-    let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
-    photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE; // 过滤选择媒体文件类型为IMAGE
-    photoSelectOptions.maxSelectNumber = 1; // 选择媒体文件的最大数目
 
 
-    photoPicker.select(photoSelectOptions).then(async (photoSelectResult: photoAccessHelper.PhotoSelectResult) => {
+    try {
+      let selectUris: Array<string> = [];
+      let photoPicker = new photoAccessHelper.PhotoViewPicker();
+      let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
+      photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
+      photoSelectOptions.maxSelectNumber  = 1;
 
 
-      //用一个全局变量存储返回的uri
+      const photoSelectResult = await photoPicker.select(photoSelectOptions);
       selectUris = photoSelectResult.photoUris;
       selectUris = photoSelectResult.photoUris;
-      console.info('photoViewPicker.select to file succeed and uris are:' + selectUris);
-      //使用fs.openSync接口,通过uri打开这个文件得到fd
-      let file = fs.openSync(selectUris[0], fs.OpenMode.READ_ONLY);
-      console.info('file fd: ' + file.fd);
-      let name = await MD5.digestSync(this.videoUrl)
-      let imagePath = this.context.filesDir + FileUtil.separator + name
+      console.info('photoViewPicker.select  to file succeed and uris are:' + selectUris);
+
+      let file = fs.openSync(selectUris[0],  fs.OpenMode.READ_ONLY);
+      console.info('file  fd: ' + file.fd);
+
+      let name = await MD5.digestSync(this.videoUrl)+'.jpg'
+      let imagePath = this.context.filesDir  + FileUtil.separator  + name
       imagePath = fileUri.getUriFromPath(imagePath)
       imagePath = fileUri.getUriFromPath(imagePath)
-      let file2 = fileIo.openSync(imagePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE)
-      fileIo.copyFileSync(file.fd, file2.fd)
+
+      let file2 = fileIo.openSync(imagePath,  fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE)
+      fileIo.copyFileSync(file.fd,  file2.fd)
       fileIo.closeSync(file);
       fileIo.closeSync(file);
       fileIo.closeSync(file2);
       fileIo.closeSync(file2);
 
 
-      if (item.filePath == this.currentSong?.filePath) {
-        this.cover = imagePath
+      if (item.filePath  == this.currentSong?.filePath)  {
+        this.cover  = imagePath
       }
       }
       if (item) {
       if (item) {
-        item.pixelMapPath = imagePath
+        item.pixelMapPath  = imagePath
       }
       }
 
 
-      this.table.updatePixelMapPath(item.filePath, imagePath, (success: boolean, error?: string) => {
+      this.table.updatePixelMapPath(item.filePath,  imagePath, (success: boolean, error?: string) => {
         if (success) {
         if (success) {
           this.doUpdateData()
           this.doUpdateData()
-
-          console.log(" onecold 更新音乐封面成功,数据库已同步");
-
+          console.log("onecold  更新音乐封面成功,数据库已同步");
         } else {
         } else {
-          console.error(" onecold 更新音乐封面数据库失败原因: " + error);
+          console.error("onecold  更新音乐封面数据库失败原因: " + error);
         }
         }
       });
       });
-      LogUtil.debug("onecold this.cover =" + this.cover)
-
-    }).catch((err: BusinessError) => {
-
-      console.error(`Invoke photoViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
-
-    })
 
 
+      LogUtil.debug("onecold  this.cover  =" + this.cover)
+      return FileUtil.getFilePath(imagePath); // Return the image path here
 
 
+    } catch (err) {
+      console.error(`Invoke  photoViewPicker.select  failed, code is ${err.code},  message is ${err.message}`);
+      return undefined;
+    }
   }
   }
 
 
   // 拉起picker选择文件管理器
   // 拉起picker选择文件管理器
@@ -3667,7 +3667,7 @@ export struct LocalMusic {
               bottomRight: 0
               bottomRight: 0
             })
             })
             .bindSheet(this.longItemFilePath== item.filePath, this.editSheet(item), {
             .bindSheet(this.longItemFilePath== item.filePath, this.editSheet(item), {
-              height: this.isCoverOpacity() ? '95%' : '88%',
+              height:  '99%' ,
               dragBar: true,
               dragBar: true,
               onDisappear: () => {
               onDisappear: () => {
                 this.longItemFilePath = '';
                 this.longItemFilePath = '';
@@ -3675,7 +3675,7 @@ export struct LocalMusic {
               },
               },
               showClose: true,
               showClose: true,
               preferType: SheetType.CENTER ,
               preferType: SheetType.CENTER ,
-              title: { title: '编辑信息' }
+              title: { title: '编辑标签' }
             })
             })
             .draggable(false)
             .draggable(false)
             .opacity(this.opacityItem)// 绑定透明度
             .opacity(this.opacityItem)// 绑定透明度
@@ -3901,7 +3901,7 @@ export struct LocalMusic {
   MenuBuilder(item: VideoItem, index: number, filePath: string) {
   MenuBuilder(item: VideoItem, index: number, filePath: string) {
     if(
     if(
       ((this.modeType===2||this.modeType==3)&&!this.isCanBack)
       ((this.modeType===2||this.modeType==3)&&!this.isCanBack)
-      ||(item.name === LocalMusic.STR_LOCK_VIDEO || item.name === LocalMusic.STR_FAC_VIDEO ||
+        ||(item.name === LocalMusic.STR_LOCK_VIDEO || item.name === LocalMusic.STR_FAC_VIDEO ||
         item.name === LocalMusic.STR_HISTORY_MUSIC)
         item.name === LocalMusic.STR_HISTORY_MUSIC)
     ){
     ){
       //如果是专辑和艺术家的首页,不能长按
       //如果是专辑和艺术家的首页,不能长按
@@ -3937,17 +3937,18 @@ export struct LocalMusic {
 
 
             MenuItem({
             MenuItem({
               symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.square_and_pencil')),
               symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.square_and_pencil')),
-              content: '编辑信息'
+              content: '编辑标签'
             })
             })
               .bindSheet($$this.isShowEdit, this.editSheet(item), {
               .bindSheet($$this.isShowEdit, this.editSheet(item), {
-                height: this.isCoverOpacity() ? '95%' : '88%',
+                height: '99%',
                 dragBar: true,
                 dragBar: true,
                 showClose: true,
                 showClose: true,
                 preferType: SheetType.CENTER ,
                 preferType: SheetType.CENTER ,
-                title: { title: '编辑信息' }
+                title: { title: '编辑标签' }
               })
               })
               .visibility(item.type === CommonConstants.TYPE_IS_DIR ? Visibility.None : Visibility.Visible)
               .visibility(item.type === CommonConstants.TYPE_IS_DIR ? Visibility.None : Visibility.Visible)
               .onClick(async() => {
               .onClick(async() => {
+                this.setEditStrEmpty()
                 this.tempLyricContent = await this.getLyricContent(item);
                 this.tempLyricContent = await this.getLyricContent(item);
                 // console.info('onecold 长按this.tempLyricContent' +this.tempLyricContent)
                 // console.info('onecold 长按this.tempLyricContent' +this.tempLyricContent)
                 if(this.isGridMusic){
                 if(this.isGridMusic){
@@ -4782,9 +4783,20 @@ export struct LocalMusic {
   @State artistStr: string = ''
   @State artistStr: string = ''
   @State lyricConStr: string = ''
   @State lyricConStr: string = ''
   @State currentEditItem: string = ''
   @State currentEditItem: string = ''
+  @State yearStr: string  = ''
+  @State trackStr: string  = ''
+  @State genreStr: string  = ''
+  @State imagePathStr: string  = ''
+
+  @State albumArtistStr: string  = ''
+  @State composerStr: string  = ''
+  @State lyricistStr: string  = ''
+  @State commentStr: string  = ''
+  @State discStr: string  = ''
 
 
   doUpdateData() {
   doUpdateData() {
     setTimeout(() => {
     setTimeout(() => {
+
       if (this.modeType === 0) {
       if (this.modeType === 0) {
         this.deleteCache(this.currentPath)
         this.deleteCache(this.currentPath)
         this.getSortedFiles(this.currentPath)
         this.getSortedFiles(this.currentPath)
@@ -4796,7 +4808,7 @@ export struct LocalMusic {
   }
   }
 
 
   //编辑信息
   //编辑信息
-  doEdit(item: VideoItem) {
+  async doEdit(item: VideoItem) {
     if (!item) {
     if (!item) {
       return
       return
     }
     }
@@ -4805,63 +4817,102 @@ export struct LocalMusic {
       return
       return
     }
     }
 
 
-    this.table.updateMediaInfo(item.filePath, this.titleStr, this.artistStr, this.ablumStr,
-      (success: boolean, error?: string) => {
-        // this.isShowEdit = false
-        if(this.isGridMusic){
-          this.longItemFilePath =''
-        }else{
-          this.isShowEdit = false
-        }
-        if (success) {
-          console.log(" onecold 编辑信息成功,数据库已同步");
-          ToastUtil.showToast('保存成功')
-          this.isShowMoreView = false
-          this.name = this.titleStr
-          if (item.filePath == this.currentSong?.filePath) {
-            this.currentSong.name = this.titleStr
-            this.artist = this.artistStr
-            this.currentSong.artist = this.artistStr
-            this.currentSong.album = this.ablumStr
+    // let doChangeLyric = false
+    // if(item.filePath==this.currentSong?.filePath){
+    //   if(this.lyricConStr !== this.lyricContent){
+    //     doChangeLyric = true
+    //   }
+    // }else if(this.lyricConStr !== this.tempLyricContent){
+    //   doChangeLyric = true
+    // }
+
+    //用户更改了封面,那就内嵌下封面
+    if(StrUtil.isNotEmpty(this.imagePathStr)){
+      const resultCover:boolean = await changeMusicCover(this.context,
+        item.filePath,this.imagePathStr,true);
+      this.imagePathStr = ''
+      if(resultCover){
+        ToastUtil.showToast('内嵌封面成功')
+      }
+
+    }
+
+    const metadata:FFMpegTags = {
+      title: this.titleStr,
+      artist: this.artistStr,
+      album: this.ablumStr,
+      // LYRICS:this.lyricConStr,
+      // USLT:this.lyricConStr,
+      TYER:this.yearStr,
+      genre:this.genreStr,
+      track:this.trackStr,
+      albumartist:this.albumArtistStr,
+      TPE2:this.albumArtistStr,//ID3v2 中通常没有专门的 "专辑艺术家" 字段,但有些软件会使用 TPE2 来表示专辑艺
+      COMPOSER:this.composerStr,
+      lyricist:this.lyricistStr,
+      TEXT:this.lyricistStr,//ID3v2 使用 TEXT 字段来表示作词者
+      comment:this.commentStr,
+      // comm:this.commentStr,//ID3v2:使用 COMM(Comment)字段。
+      disc:this.discStr,
+    };
+
+    //内嵌下标签的值,设置overwrite为true会直接修改原文件
+    const result:boolean = await repairAudioMetadata(
+      item.filePath,
+      this.lyricConStr,
+      metadata,
+      true
+    );
+
+    if (result) {
+      this.table.updateMediaInfo(item.filePath, this.titleStr, this.artistStr, this.ablumStr,this.lyricConStr,
+        this.yearStr,this.genreStr,this.trackStr,this.albumArtistStr,this.composerStr, this.lyricistStr,this.commentStr,this.discStr,
+        (success: boolean, error?: string) => {
+          // this.isShowEdit = false
+          if(this.isGridMusic){
+            this.longItemFilePath =''
+          }else{
+            this.isShowEdit = false
           }
           }
+          if (success) {
+            console.log(" onecold 编辑信息成功,数据库已同步");
+            ToastUtil.showToast('内嵌音乐标签成功')
+            this.isShowMoreView = false
+            this.name = this.titleStr
+            if (item.filePath == this.currentSong?.filePath) {
+              this.currentSong.name = this.titleStr
+              this.artist = this.artistStr
+              this.currentSong.artist = this.artistStr
+              this.currentSong.album = this.ablumStr
+              this.currentSong.year = this.yearStr
+              this.currentSong.genre = this.genreStr
+              this.currentSong.track = this.trackStr
+
+              this.currentSong.ALBUMARTIST = this.albumArtistStr
+              this.currentSong.COMPOSER = this.composerStr
+              this.currentSong.LYRICIST = this.lyricistStr
+              this.currentSong.COMMENT = this.commentStr
+            }
 
 
-          this.doUpdateData()
+            this.doUpdateData()
+          } else {
+            // ToastUtil.showToast('保存失败' + error?.toString())
+            console.error(" onecold  编辑信息数据库失败原因: " + error);
+          }
 
 
-        } else {
-          ToastUtil.showToast('保存失败' + error?.toString())
-          console.error(" onecold  编辑信息数据库失败原因: " + error);
-        }
 
 
+        });
+      console.log(' onecold 元数据标签更新成功,');
 
 
-      });
-    let doChangeLyric = false
-    if (item.filePath == this.currentSong?.filePath) {
-      if (this.lyricConStr !== this.lyricContent) {
-        doChangeLyric = true
-      }
-    } else if (this.lyricConStr !== this.tempLyricContent) {
-      doChangeLyric = true
+
+    } else {
+      ToastUtil.showToast('内嵌音乐标签失败')
+      console.log('onecold 内嵌音乐标签失败');
     }
     }
 
 
-    if (doChangeLyric) {
-      let lyricPath = item.filePath.substring(0, this.videoUrl.lastIndexOf('.')) + '.lrc'
-      let jiaMilyricPath = lyricPath.substring(0, lyricPath.lastIndexOf('.')) + '.lrcc'
 
 
-      let isJiaMi = false;
-      let realLyricPath = lyricPath
-      if (FileUtil.accessSync(jiaMilyricPath)) {
-        isJiaMi = true
-        realLyricPath = jiaMilyricPath
-        console.info("onecold doEdit找到加密本地歌词 ");
-      } else {
-        isJiaMi = false
-        realLyricPath = lyricPath
 
 
-        console.info("onecold doEdit本地歌词 ");
-      }
-      this.saveDataToFile(this.lyricConStr, realLyricPath, isJiaMi)
 
 
-    }
 
 
   }
   }
 
 
@@ -4880,6 +4931,8 @@ export struct LocalMusic {
     .height('100%')
     .height('100%')
   }
   }
 
 
+
+
   @Builder
   @Builder
   editDetail(item: VideoItem) {
   editDetail(item: VideoItem) {
     Column() {
     Column() {
@@ -4887,17 +4940,27 @@ export struct LocalMusic {
         Row() {
         Row() {
           Stack() {
           Stack() {
 
 
-            Image(StrUtil.isEmpty(item.pixelMapPath) ? $r('app.media.music_red') :
-            item.pixelMapPath)
+            Image(StrUtil.isNotEmpty(this.imagePathStr)?this.imagePathStr:
+              StrUtil.isEmpty(item.pixelMapPath) ? $r('app.media.music_red') :
+              item.pixelMapPath)
               .fillColor(this.themeColor)
               .fillColor(this.themeColor)
-              .height(33)
-              .width(33)
+              .height(55)
+              .width(55)
               .borderRadius('100%')
               .borderRadius('100%')
               .clip(true)
               .clip(true)
               .opacity(this.opacityItem)// 绑定透明度
               .opacity(this.opacityItem)// 绑定透明度
-              .margin({ left: 20 })
+              .margin({ left: 25 })
           }
           }
-          .width('15%')
+          .onClick(async () => {
+
+            const imagePath:string | undefined = await this.goSelectImage(item);
+            if (imagePath) {
+              //用户更改过图片
+              this.imagePathStr = imagePath
+            }
+
+          })
+          .width('18%')
 
 
           Row() {
           Row() {
             Column() {
             Column() {
@@ -4911,7 +4974,7 @@ export struct LocalMusic {
                 })
                 })
 
 
                 .fontColor(this.themeColor)
                 .fontColor(this.themeColor)
-                .margin({ left: 10 })
+                .margin({ left: 18 })
               Row() {
               Row() {
                 Text(item.artist)
                 Text(item.artist)
                   .fontSize(11)
                   .fontSize(11)
@@ -4919,7 +4982,7 @@ export struct LocalMusic {
                   .textOverflow({ overflow: TextOverflow.MARQUEE })//超长滚动
                   .textOverflow({ overflow: TextOverflow.MARQUEE })//超长滚动
                   .padding({ top: 8 })
                   .padding({ top: 8 })
                   .fontColor(this.themeColor)
                   .fontColor(this.themeColor)
-                  .margin({ left: 10 })
+                  .margin({ left: 18 })
 
 
               }
               }
 
 
@@ -4930,30 +4993,67 @@ export struct LocalMusic {
             .justifyContent(FlexAlign.Center)
             .justifyContent(FlexAlign.Center)
             .alignItems(HorizontalAlign.Start)
             .alignItems(HorizontalAlign.Start)
 
 
-            Button('本地封面')
+            Button('获取封面')
               .fontColor(Color.White)
               .fontColor(Color.White)
               .fontSize(12)
               .fontSize(12)
               .height(38)
               .height(38)
               .width(88)
               .width(88)
+              .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
               .backgroundColor(this.themeColor)
               .backgroundColor(this.themeColor)
               .stateEffect(true)
               .stateEffect(true)
               .margin({ right: 5, left: 12 })
               .margin({ right: 5, left: 12 })
               .onClick(async () => {
               .onClick(async () => {
+                if (PreferencesUtil.getStringSync('COVER_API',  '') === '') {
+                  this.isShowMoreView  = false;
+                  this.showTipsDialog();
+                  return; // Return empty string when no API is configured
+                }
+
+                const imagePath:string | undefined = await this.doSearchCover(item);
+                console.info('onecold imagePath = '+imagePath)
+                if (imagePath) {
+                  //用户更改过图片
+                  this.imagePathStr = imagePath
+                }else{
+                  ToastUtil.showToast('获取封面失败,请重试')
+                }
 
 
-                this.goSelectImage(item)
 
 
               })
               })
 
 
-            Button('获取封面')
+            Button('获取歌词')
               .fontColor(Color.White)
               .fontColor(Color.White)
               .fontSize(12)
               .fontSize(12)
               .height(38)
               .height(38)
               .width(88)
               .width(88)
+              .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
               .backgroundColor(this.themeColor)
               .backgroundColor(this.themeColor)
               .stateEffect(true)
               .stateEffect(true)
               .margin({ right: 12, left: 5 })
               .margin({ right: 12, left: 5 })
               .onClick(async () => {
               .onClick(async () => {
-                this.doSearchCover(item)
+                //判断有没有设置api
+                let apiUrl = PreferencesUtil.getStringSync('LRC_API', '')
+                if (StrUtil.isEmpty(apiUrl)) {
+                  this.isLyricSetting = false
+                  this.showTipsDialog()
+                  return
+                }
+                let artist = item.artist
+                let title = item.name
+                if(!artist)
+                  artist = ''
+                if(!title)
+                  title = ''
+                let res: string | undefined = await getApiLyric(apiUrl,title, artist, false);
+                if (res && StrUtil.isNotEmpty(res) && res !== 'unknown' && res !== 'Timeout was reached') {
+                  this.lyricConStr = res;
+                }else{
+                  ToastUtil.showToast('获取歌词失败,请重试')
+                }
+
+
+                // let res : string | undefined = await getApiLyric(title, artist, false);
+
 
 
               })
               })
 
 
@@ -5030,13 +5130,196 @@ export struct LocalMusic {
         .justifyContent(FlexAlign.Start)
         .justifyContent(FlexAlign.Start)
 
 
         Row() {
         Row() {
-          Text('歌词:')
+          Text('年份:')
+            .fontSize(14)
+            .fontColor($r('app.color.text_color'))
+            .margin({ left: 22 })
+          TextInput({ text: item.year })
+            .height(40)
+            .maxLines(1)
+            .fontSize(14)
+            .type(InputType.Number)
+            .layoutWeight(1)
+            .fontColor($r('app.color.text_color'))
+            .margin({ right: 20, left: 10 })
+            .onChange((val: string) => {
+              this.yearStr = val
+            })
+        }
+        .width('100%')
+        .margin({ top: 10, bottom: 10 })
+        .justifyContent(FlexAlign.Start)
+        Row() {
+          Text('音轨号:')
+            .fontSize(14)
+            .fontColor($r('app.color.text_color'))
+            .margin({ left: 22 })
+          TextInput({ text:item.track=='未知'?'': item.track })
+            .height(40)
+            .maxLines(1)
+            .fontSize(14)
+            .type(InputType.Number)
+            .layoutWeight(1)
+            .fontColor($r('app.color.text_color'))
+            .margin({ right: 20, left: 10 })
+            .onChange((val: string) => {
+              this.trackStr = val
+            })
+        }
+        .width('100%')
+        .margin({ top: 10, bottom: 10 })
+        .justifyContent(FlexAlign.Start)
+
+        Row() {
+          Text('碟号:')
+            .fontSize(14)
+            .fontColor($r('app.color.text_color'))
+            .margin({ left: 22 })
+          TextInput({ text:item.disc })
+            .height(40)
+            .maxLines(1)
+            .fontSize(14)
+            .type(InputType.Number)
+            .layoutWeight(1)
+            .fontColor($r('app.color.text_color'))
+            .margin({ right: 20, left: 10 })
+            .onChange((val: string) => {
+              this.discStr = val
+            })
+        }
+        .width('100%')
+        .margin({ top: 10, bottom: 10 })
+        .justifyContent(FlexAlign.Start)
+
+        Row() {
+          Text('风格:')
+            .fontSize(14)
+            .fontColor($r('app.color.text_color'))
+            .margin({ left: 22 })
+          TextInput({ text: item.genre })
+            .height(40)
+            .maxLines(1)
+            .fontSize(14)
+            .layoutWeight(1)
+            .fontColor($r('app.color.text_color'))
+            .margin({ right: 20, left: 10 })
+            .onChange((val: string) => {
+              this.genreStr = val
+            })
+        }
+        .width('100%')
+        .margin({ top: 10, bottom: 10 })
+        .justifyContent(FlexAlign.Start)
+
+        Row() {
+          Text('专辑艺术家:')
             .fontSize(14)
             .fontSize(14)
             .fontColor($r('app.color.text_color'))
             .fontColor($r('app.color.text_color'))
             .margin({ left: 22 })
             .margin({ left: 22 })
-          TextArea({ text: item.filePath === this.currentSong?.filePath ? this.lyricContent : this.tempLyricContent })
+          TextInput({ text: item.ALBUMARTIST })
+            .height(40)
+            .maxLines(1)
+            .fontSize(14)
+            .layoutWeight(1)
+            .fontColor($r('app.color.text_color'))
+            .margin({ right: 20, left: 10 })
+            .onChange((val: string) => {
+              this.albumArtistStr = val
+            })
+        }
+        .width('100%')
+        .margin({ top: 10, bottom: 10 })
+        .justifyContent(FlexAlign.Start)
+
+        Row() {
+          Text('作曲:')
+            .fontSize(14)
+            .fontColor($r('app.color.text_color'))
+            .margin({ left: 22 })
+          TextInput({ text: item.COMPOSER })
+            .height(40)
+            .maxLines(1)
+            .fontSize(14)
+            .layoutWeight(1)
+            .fontColor($r('app.color.text_color'))
+            .margin({ right: 20, left: 10 })
+            .onChange((val: string) => {
+              this.composerStr = val
+            })
+        }
+        .width('100%')
+        .margin({ top: 10, bottom: 10 })
+        .justifyContent(FlexAlign.Start)
+
+        Row() {
+          Text('作词:')
+            .fontSize(14)
+            .fontColor($r('app.color.text_color'))
+            .margin({ left: 22 })
+          TextInput({ text: item.LYRICIST })
+            .height(40)
+            .maxLines(1)
+            .fontSize(14)
+            .layoutWeight(1)
+            .fontColor($r('app.color.text_color'))
+            .margin({ right: 20, left: 10 })
+            .onChange((val: string) => {
+              this.lyricistStr = val
+            })
+        }
+        .width('100%')
+        .margin({ top: 10, bottom: 10 })
+        .justifyContent(FlexAlign.Start)
+
+        Row() {
+          Text('注释:')
+            .fontSize(14)
+            .fontColor($r('app.color.text_color'))
+            .margin({ left: 22 })
+          TextInput({ text: item.COMMENT })
+            .height(40)
+            .maxLines(1)
+            .fontSize(14)
+            .layoutWeight(1)
+            .fontColor($r('app.color.text_color'))
+            .margin({ right: 20, left: 10 })
+            .onChange((val: string) => {
+              this.commentStr = val
+            })
+
+        }
+        .width('100%')
+        .margin({ top: 10, bottom: 10 })
+        .justifyContent(FlexAlign.Start)
+
+        Row() {
+          Column(){
+            Text('歌词:')
+              .fontSize(14)
+              .fontColor($r('app.color.text_color'))
+              .margin({ left: 22 })
+
+            Button({ type: ButtonType.Capsule, stateEffect: true }) {
+              SymbolGlyph($r('sys.symbol.trash_fill'))
+                .fontSize(25)
+                .alignSelf(ItemAlign.Center)
+                .margin({ left: 22,top:15})
+            }
+            .backgroundColor(Color.Transparent)
+            .visibility(StrUtil.isEmpty(this.tempLyricContent)&&StrUtil.isEmpty(this.lyricConStr)?
+            Visibility.None:Visibility.Visible)
+            .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
+            .onClick(()=>{
+              this.lyricConStr = ''
+              this.lyricContent = ''
+              this.tempLyricContent = ''
+            })
+          }
+
+          TextArea({ text: StrUtil.isNotEmpty(this.lyricConStr)?this.lyricConStr:
+          this.tempLyricContent })
             .type(TextAreaType.NORMAL)
             .type(TextAreaType.NORMAL)
-            .height(275)
+            .height('auto')
             .fontSize(14)
             .fontSize(14)
             .layoutWeight(1)
             .layoutWeight(1)
             .fontColor($r('app.color.text_color'))
             .fontColor($r('app.color.text_color'))
@@ -5057,11 +5340,13 @@ export struct LocalMusic {
             .layoutWeight(1)
             .layoutWeight(1)
             .height(50)
             .height(50)
             .width(100)
             .width(100)
+            .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
             .backgroundColor(this.themeColor)
             .backgroundColor(this.themeColor)
             .stateEffect(true)
             .stateEffect(true)
             .margin({ right: 20, bottom: 20 })
             .margin({ right: 20, bottom: 20 })
             .onClick(async () => {
             .onClick(async () => {
               this.doEdit(item)
               this.doEdit(item)
+
             })
             })
 
 
           Button('取消')
           Button('取消')
@@ -5069,16 +5354,21 @@ export struct LocalMusic {
             .layoutWeight(1)
             .layoutWeight(1)
             .height(50)
             .height(50)
             .width(100)
             .width(100)
+            .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
             .backgroundColor(this.themeColor)
             .backgroundColor(this.themeColor)
             .stateEffect(true)
             .stateEffect(true)
             .margin({ left: 20, bottom: 20 })
             .margin({ left: 20, bottom: 20 })
             .onClick(async () => {
             .onClick(async () => {
-
               if(this.isGridMusic){
               if(this.isGridMusic){
                 this.longItemFilePath =''
                 this.longItemFilePath =''
               }else{
               }else{
                 this.isShowEdit = false
                 this.isShowEdit = false
               }
               }
+              this.setEditStrEmpty()
+              if(this.isShowPlay){
+                this.isShowEdit = false
+              }
+
             })
             })
         }
         }
         .margin({
         .margin({
@@ -5096,6 +5386,22 @@ export struct LocalMusic {
     .margin({ bottom: 20 })
     .margin({ bottom: 20 })
   }
   }
 
 
+  setEditStrEmpty(){
+    this.imagePathStr = ''
+    this.lyricConStr = ''
+    this.artistStr = ''
+    this.titleStr = ''
+    this.ablumStr = ''
+    this.genreStr = ''
+    this.yearStr = ''
+    this.trackStr = ''
+    this.albumArtistStr = ''
+    this.composerStr = ''
+    this.lyricistStr = ''
+    this.commentStr = ''
+    this.discStr = ''
+  }
+
   @Builder
   @Builder
   detailSheet(item:VideoItem) {
   detailSheet(item:VideoItem) {
     Scroll() {
     Scroll() {
@@ -5348,6 +5654,80 @@ export struct LocalMusic {
         .width('100%')
         .width('100%')
         .margin({ top: 10, bottom: 10 })
         .margin({ top: 10, bottom: 10 })
         .justifyContent(FlexAlign.Start)
         .justifyContent(FlexAlign.Start)
+        Row() {
+          Text('碟号:')
+            .fontSize(14)
+            .fontColor($r('app.color.text_color'))
+            .margin({ left: 22 })
+          Text(currentItem.disc)
+            .fontSize(14)
+            .margin({ left: 10 })
+            .fontColor($r('app.color.text_color'))
+            .layoutWeight(1)
+        }
+        .width('100%')
+        .margin({ top: 10, bottom: 10 })
+        .justifyContent(FlexAlign.Start)
+
+        Row() {
+          Text('专辑艺术家:')
+            .fontSize(14)
+            .fontColor($r('app.color.text_color'))
+            .margin({ left: 22 })
+          Text(currentItem.ALBUMARTIST)
+            .fontSize(14)
+            .margin({ left: 10 })
+            .fontColor($r('app.color.text_color'))
+            .layoutWeight(1)
+        }
+        .width('100%')
+        .margin({ top: 10, bottom: 10 })
+        .justifyContent(FlexAlign.Start)
+
+        Row() {
+          Text('作曲家:')
+            .fontSize(14)
+            .fontColor($r('app.color.text_color'))
+            .margin({ left: 22 })
+          Text(currentItem.COMPOSER)
+            .fontSize(14)
+            .margin({ left: 10 })
+            .fontColor($r('app.color.text_color'))
+            .layoutWeight(1)
+        }
+        .width('100%')
+        .margin({ top: 10, bottom: 10 })
+        .justifyContent(FlexAlign.Start)
+
+        Row() {
+          Text('作词家:')
+            .fontSize(14)
+            .fontColor($r('app.color.text_color'))
+            .margin({ left: 22 })
+          Text(currentItem.LYRICIST)
+            .fontSize(14)
+            .margin({ left: 10 })
+            .fontColor($r('app.color.text_color'))
+            .layoutWeight(1)
+        }
+        .width('100%')
+        .margin({ top: 10, bottom: 10 })
+        .justifyContent(FlexAlign.Start)
+
+        Row() {
+          Text('注释:')
+            .fontSize(14)
+            .fontColor($r('app.color.text_color'))
+            .margin({ left: 22 })
+          Text(currentItem.COMMENT)
+            .fontSize(14)
+            .margin({ left: 10 })
+            .fontColor($r('app.color.text_color'))
+            .layoutWeight(1)
+        }
+        .width('100%')
+        .margin({ top: 10, bottom: 10 })
+        .justifyContent(FlexAlign.Start)
 
 
 
 
         Row() {
         Row() {
@@ -8674,7 +9054,7 @@ export struct LocalMusic {
           this.callFilePickerSelectFileForLyric()
           this.callFilePickerSelectFileForLyric()
           break;
           break;
         case 1:
         case 1:
-          //判断是不是赞助会员
+          //判断有没有设置api
           if (PreferencesUtil.getStringSync('LRC_API', '') === '') {
           if (PreferencesUtil.getStringSync('LRC_API', '') === '') {
             this.isLyricSetting = false
             this.isLyricSetting = false
             this.showTipsDialog()
             this.showTipsDialog()
@@ -8801,8 +9181,18 @@ export struct LocalMusic {
           .margin({ left: 6 })
           .margin({ left: 6 })
           .onClick(() => {
           .onClick(() => {
             DialogHelper.closeDialog('tips'); //关闭弹框
             DialogHelper.closeDialog('tips'); //关闭弹框
+            this.isShowEdit = false
+            this.longItemFilePath = ''
+            // if(this.isGridMusic&&!this.isShowPlay){
+            //   this.longItemFilePath== ''
+            // }else{
+            //   this.isShowEdit = false
+            // }
+
             this.isShowPlay = false
             this.isShowPlay = false
             this.mType = 3
             this.mType = 3
+
+
             // router.pushUrl({
             // router.pushUrl({
             //   url: 'pages/SettingPage'
             //   url: 'pages/SettingPage'
             // }, router.RouterMode.Single);
             // }, router.RouterMode.Single);
@@ -8849,7 +9239,7 @@ export struct LocalMusic {
                     backgroundColor: Color.Transparent,
                     backgroundColor: Color.Transparent,
                     title: { title: '歌曲信息' }
                     title: { title: '歌曲信息' }
                   })
                   })
-              } else if (more.id === 15 && this.currentSong) { //编辑信息
+              } else if (more.id === 15 && this.currentSong) { //编辑标签
                 Image(more.image)
                 Image(more.image)
                   .width(24)
                   .width(24)
                   .height(24)
                   .height(24)
@@ -8858,13 +9248,13 @@ export struct LocalMusic {
                   .fontSize(16)
                   .fontSize(16)
                   .fontColor(Color.White)
                   .fontColor(Color.White)
                   .bindSheet($$this.isShowEdit, this.editSheet(this.currentSong), {
                   .bindSheet($$this.isShowEdit, this.editSheet(this.currentSong), {
-                    height: this.isCoverOpacity() ? '95%' : '93%',
+                    height:  '99%',
                     dragBar: true,
                     dragBar: true,
                     showClose: true,
                     showClose: true,
                     preferType: this.currentBreakpoint !== BreakpointTypeEnum.SM ? SheetType.CENTER : SheetType.BOTTOM,
                     preferType: this.currentBreakpoint !== BreakpointTypeEnum.SM ? SheetType.CENTER : SheetType.BOTTOM,
                     // blurStyle:BlurStyle.Thin,
                     // blurStyle:BlurStyle.Thin,
                     // backgroundColor:Color.Transparent,
                     // backgroundColor:Color.Transparent,
-                    title: { title: '编辑信息' }
+                    title: { title: '编辑标签' }
                   })
                   })
               } else if (more.id === 5) { //定时关闭
               } else if (more.id === 5) { //定时关闭
                 Image(more.image)
                 Image(more.image)
@@ -9140,7 +9530,7 @@ export struct LocalMusic {
 
 
     { id: 14, image: $r('app.media.cover_online'), title: '获取封面' },
     { id: 14, image: $r('app.media.cover_online'), title: '获取封面' },
 
 
-    { id: 15, image: $r('app.media.rename2'), title: '编辑信息' },
+    { id: 15, image: $r('app.media.rename2'), title: '编辑标签' },
 
 
     { id: 5, image: $r('app.media.time_close'), title: '定时关闭' },
     { id: 5, image: $r('app.media.time_close'), title: '定时关闭' },
 
 
@@ -9230,7 +9620,9 @@ export struct LocalMusic {
         }
         }
         this.isShowMoreView = false
         this.isShowMoreView = false
         break;
         break;
-      case 15: //编辑信息
+      case 15: //编辑标签
+        this.setEditStrEmpty()
+        this.tempLyricContent = this.lyricContent
         this.isShowEdit = !this.isShowEdit;
         this.isShowEdit = !this.isShowEdit;
         break;
         break;
       case 16: //悬浮歌词
       case 16: //悬浮歌词
@@ -9393,50 +9785,53 @@ export struct LocalMusic {
    *
    *
    */
    */
 
 
-  doSearchCover(item: VideoItem) {
-    if (PreferencesUtil.getStringSync('COVER_API', '') === '') {
-      this.isShowMoreView = false
-      this.showTipsDialog()
-      return
+  async doSearchCover(item: VideoItem): Promise<string> {
+    if (PreferencesUtil.getStringSync('COVER_API',  '') === '') {
+      this.isShowMoreView  = false;
+      this.showTipsDialog();
+      return ''; // Return empty string when no API is configured
     }
     }
-    if (item !== undefined) {
-      let artist = item?.artist
-      if (StrUtil.isEmpty(artist) || artist === undefined) {
-        artist = ''
-      }
-      this.searchCover(item, item.name, artist)
-    }
-  }
-
-  searchCover(item: VideoItem, title: string, artist: string) {
 
 
+    if (!item) {
+      return ''; // Return empty string if item is undefined
+    }
 
 
-    NetAxiosUtil.getLyricCover(title, artist, PreferencesUtil.getStringSync('COVER_API', '')).then(async (res) => {
+    const artist = item?.artist || ''; // Use empty string if artist is missing
+    return this.searchCover(item,  item.name,  artist); // Await is not needed here as we return the promise directly
+  }
 
 
-      LogUtil.debug("onecold res =" + res)
-      if (StrUtil.isNotEmpty(res) && res !== 'unknown' && res !== 'Timeout was reached') {
-        if (item.filePath == this.currentSong?.filePath) {
-          this.cover = res
-          if (this.currentSong) {
-            this.currentSong.pixelMapPath = res
+  /**
+   *api搜索封面
+   *item,根据title和artist
+   */
+  searchCover(item: VideoItem, title: string, artist: string): Promise<string> {
+    return NetAxiosUtil.getLyricCover(title,  artist, PreferencesUtil.getStringSync('COVER_API',  '')).then(async (res) => {
+      LogUtil.debug("onecold  res =" + res);
+
+      if (StrUtil.isNotEmpty(res)  && res !== 'unknown' && res !== 'Timeout was reached') {
+        if (item.filePath  == this.currentSong?.filePath)  {
+          this.cover  = res;
+          if (this.currentSong)  {
+            this.currentSong.pixelMapPath  = res;
           }
           }
         }
         }
 
 
-
-        this.table.updatePixelMapPath(item.filePath, res, (success: boolean, error?: string) => {
+        // Update pixel map path but always return res regardless of success
+        this.table.updatePixelMapPath(item.filePath,  res, (success: boolean, error?: string) => {
           if (success) {
           if (success) {
-            this.doUpdateData()
-            console.log(" onecold 更新音乐封面成功,数据库已同步");
-
+            this.doUpdateData();
+            console.log("onecold  更新音乐封面成功,数据库已同步");
           } else {
           } else {
-            console.error(" onecold 更新音乐封面数据库失败原因: " + error);
+            console.error("onecold  更新音乐封面数据库失败原因: " + error);
           }
           }
+          // Note: We don't resolve/reject here because we already returned res
         });
         });
-        LogUtil.debug("onecold this.cover =" + this.cover)
-      }
 
 
+        LogUtil.debug("onecold  this.cover  =" + this.cover);
+      }
 
 
-    })
+      return res; // Always return res regardless of updatePixelMapPath result
+    });
   }
   }
 
 
   @State jumpTopTime: number = 0
   @State jumpTopTime: number = 0

+ 5 - 1
entry/src/main/ets/viewmodel/VideoItem.ets

@@ -62,7 +62,11 @@ export class VideoItem  {
   channel_layout?:string//声道布局:stereo为标准立体声(左+右)
   channel_layout?:string//声道布局:stereo为标准立体声(左+右)
   start_time?:string//起始时间:
   start_time?:string//起始时间:
 
 
-
+  ALBUMARTIST?:string//专辑艺术家
+  COMPOSER?:string//作曲家
+  LYRICIST?:string//作词家
+  COMMENT?:string//注释
+  disc?:string//碟号
 
 
   constructor(name: string, id: string, filePath: string, type:number,videoSize:number,cTime: string,pixelMap?: image.PixelMap,
   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) {
     size?: string,pixelMapPath?: string,artist?: string,album?: string,fileName?: string, lastPlayed?:Date) {