Bladeren bron

逐步歌词功能兼容这种格式[00:00.000]庐[00:00.638]州[00:01.276]月[00:01.914] [00:02.552]-[00:03.190] [00:03.828]许[00:04.466]嵩[00:05.104]
修复逐步歌词在播控中心显示的问题

onecold 1 jaar geleden
bovenliggende
commit
04c71f3122

+ 141 - 0
entry/src/main/ets/common/util/LyricUtil.ets

@@ -0,0 +1,141 @@
+import fs from '@ohos.file.fs';
+import common from '@ohos.app.ability.common';
+import fileIo from '@ohos.file.fs';
+import { LyricWord } from '@seagazer/cclyric/src/main/ets/bean/LyricWord';
+
+// 定义接口来描述返回值的类型
+interface ParseResult {
+  timeline: number;
+  words: LyricWord[];
+}
+
+class LyricUtil {
+
+  // 检测方括号格式的逐字歌词 [00:00.000]文[00:01.000]字
+  private isSquareBracketWordByWordLyric(line: string): boolean {
+    return /\[\d{2}:\d{2}\.\d{2,3}\]\S/.test(line);
+  }
+
+  // 解析方括号格式的逐字歌词行
+  private parseSquareBracketWordLine(line: string, offset: number): ParseResult {
+    const words: LyricWord[] = [];
+    let firstTimeline = -1;
+
+    // 正则匹配:[00:00.000]中文字
+    const regex = /\[(\d{2}:\d{2}\.\d{2,3})\]([^\[]*)/g;
+    // 显式指定 match 的类型
+    let match: RegExpExecArray | null;
+
+    while ((match = regex.exec(line))!==  null) {
+      const timeStr = match[1];   // 时间部分 00:00.000
+      const word = match[2].trim(); // 歌词文本
+
+      if (!word) continue; // 跳过空词
+
+      const timeline = this.parseTimeline2(timeStr)  - offset;
+      if (firstTimeline < 0) firstTimeline = timeline;
+
+      words.push(new  LyricWord(word, timeline, 0));
+    }
+
+    // 计算每个词的持续时间
+    for (let i = 0; i < words.length  - 1; i++) {
+      words[i].duration = words[i + 1].startTime - words[i].startTime;
+    }
+    if (words.length  > 0 && words[words.length - 1].duration === 0) {
+      words[words.length - 1].duration = 200; // 默认200ms
+    }
+
+    return { timeline: firstTimeline, words };
+  }
+
+  /******************** 时间解析增强 ********************/
+  private parseTimeline2(timeString: string): number {
+    // 增强支持毫秒/厘秒解析
+    const parts = timeString.split(':');
+    const minutes = parseInt(parts[0], 10);
+
+    const secondParts = parts[1].split('.');
+    const seconds = parseInt(secondParts[0], 10);
+    const fraction = parseInt(secondParts[1], 10);
+
+    // 根据小数位长度判断时间精度
+    const milliseconds = secondParts[1].length === 2 ?
+      fraction * 10 : // 厘秒转毫秒 (01 -> 10ms)
+      fraction;       // 毫秒直接使用
+
+    return minutes * 60000 + seconds * 1000 + milliseconds;
+  }
+
+  // 判断是否是逐字歌词行
+  private isWordByWordLyric(line: string): boolean {
+    const hasBracketTimestamp = /\(\d{2}:\d{2}\.\d{2,3}\)\S/.test(line);
+    const hasAngleTimestamp = /<\d{2}:\d{2}\.\d{2,3}>/.test(line);
+    return hasBracketTimestamp || hasAngleTimestamp;
+  }
+  /**
+   * 将整个逐字歌词内容转换为最简单的LRC格式
+   * @param lyricContent 整个歌词内容(字符串)
+   * @returns 转换后的LRC格式字符串
+   */
+  public convertLyricToSimpleLrc(lyricContent: string): string {
+    let lyrics = lyricContent.split('\n').map(line => line.trim());
+    const result: string[] = [];
+    const ignoredTags = ['ti', 'ar', 'al', 'by', 'offset', 'hash', 'sign', 'qq', 'total', 'Outro', 'tool'];
+
+    for (let i = 0; i < lyrics.length; i++) {
+      const line = lyrics[i].trim();
+
+      // 跳过空行和特定标签行
+      if (!line) continue;
+      if (ignoredTags.some(tag => line.startsWith(`[${tag}]`))) continue;
+
+      // 处理逐字歌词行
+      if (this.isSquareBracketWordByWordLyric(line) || this.isWordByWordLyric(line)) {
+        let firstTimestamp = "";
+        let fullText = "";
+
+        // 处理方括号格式:[00:00.000]文[00:01.000]字
+        if (this.isSquareBracketWordByWordLyric(line)) {
+          const regex = /\[(\d{2}:\d{2}\.\d{2,3})\]([^\[]*)/g;
+          let match: RegExpExecArray | null;
+
+          while ((match = regex.exec(line)) !== null) {
+            const timestamp = match[1];
+            const word = match[2].trim();
+
+            if (!firstTimestamp) firstTimestamp = timestamp;
+            fullText += word;
+          }
+        }
+        // 处理尖括号格式:<00:00.000>文<00:01.000>字
+        else if (this.isWordByWordLyric(line)) {
+          const regex = /<(\d{2}:\d{2}\.\d{2,3})>([^<]*)/g;
+          let match: RegExpExecArray | null;
+
+          while ((match = regex.exec(line)) !== null) {
+            const timestamp = match[1];
+            const word = match[2].trim();
+
+            if (!firstTimestamp) firstTimestamp = timestamp;
+            fullText += word;
+          }
+        }
+
+        // 添加到结果中
+        if (firstTimestamp && fullText) {
+          result.push(`[${firstTimestamp}]${fullText}`);
+        }
+      }
+      // 保留普通LRC行
+      else {
+        result.push(line);
+      }
+    }
+
+    // 将结果数组连接成单一字符串
+    return result.join('\n');
+  }
+}
+
+export default new LyricUtil();

+ 6 - 1
entry/src/main/ets/controller/AvSessionController.ets

@@ -23,6 +23,7 @@ import { ImageUtil, PreferencesUtil } from '@pura/harmony-utils';
 import { image } from '@kit.ImageKit';
 import { SettingPage } from '../pages/SettingPage';
 import { Utility } from '../common/util/Utility';
+import LyricUtil from '../common/util/LyricUtil';
 
 const TAG = 'onecold AvSessionController';
 
@@ -147,6 +148,10 @@ export class AvSessionController {
       } else {
         imagePixMap = value;
       }
+      let lyric = ''
+      if(lyricContent)
+        lyric = LyricUtil.convertLyricToSimpleLrc(lyricContent)
+
       hilog.info(0x0000, TAG, 'onecold SetAVMetadata successfully curSource.pixelMapPath '+curSource.pixelMapPath);
       let metadata: avSession.AVMetadata = {
         assetId: `${curSource.filePath}`,
@@ -155,7 +160,7 @@ export class AvSessionController {
         artist: curSource.artist,
         mediaImage: imagePixMap,
         duration: duration,
-        lyric:lyricContent,
+        lyric:lyric,
       };
 
       if (this.avSession) {

+ 0 - 615
entry/src/main/ets/controller/VideoController.ets

@@ -1,615 +0,0 @@
-/*
- * Copyright (c) 2023 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 { media } from '@kit.MediaKit';
-import { promptAction } from '@kit.ArkUI';
-import { window } from '@kit.ArkUI';
-import { resourceManager } from '@kit.LocalizationKit';
-import Logger from '../common/util/Logger';
-import DateFormatUtil from '../common/util/DateFormatUtil';
-import { CommonConstants, AvplayerStatus, Events, SliderMode } from '../common/constants/CommonConstants';
-import { PlayConstants } from '../common/constants/PlayConstants';
-import { GlobalContext } from '../common/util/GlobalContext';
-import { VideoItem } from '../viewmodel/VideoItem';
-import { PlayerModel } from '../common/model/PlayerModel';
-import { BackgroundUtil } from '../common/util/BackgroundUtil';
-import { common, wantAgent } from '@kit.AbilityKit';
-import { avSession } from '@kit.AVSessionKit';
-import { BusinessError } from '@kit.BasicServicesKit';
-import { Utility } from '../common/util/Utility';
-
-const TAG = 'VideoController';
-@Observed
-export class VideoController {
-  private context: common.UIAbilityContext | undefined  = AppStorage.get('context');
-  // private context: common.UIAbilityContext | undefined = undefined;
-  public playerModel: PlayerModel;
-  private avPlayer: media.AVPlayer | null = null;
-  private duration: number = 0;
-  private status: number = -1;
-  private loop: boolean = false;
-  private index: number = 0;
-  private url?: string='';
-  private name:string='';
-  private iUrl: string = '';
-  private surfaceId: string = '';
-  private session?: avSession.AVSession;
-  private isLandscape:boolean = false
-  private globalVideoList:Array<VideoItem> = []
-  private seekTime: number = PlayConstants.PROGRESS_SEEK_TIME;
-  private positionX: number = PlayConstants.POSITION_X;
-  private positionY: number = PlayConstants.POSITION_Y;
-  public onPlayCompleted?: (name:string) => void
-
-  private screenWidth: number =0
-  private screenHeight: number =0
-  private statusBarHeight: number =0
-  private isYesFull: boolean = false
-  private callBack: Function = () => {
-  };
-
-  constructor() {
-    this.playerModel = new PlayerModel();
-    this.createAVPlayer();
-    window.getLastWindow(getContext(this))
-      .then((windowClass: window.Window) => {
-        let area = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
-        this.screenWidth = px2fp(windowClass.getWindowProperties().windowRect.width);
-        this.screenHeight = px2fp(windowClass.getWindowProperties().windowRect.height);
-        this.statusBarHeight = px2fp(area.topRect.height);
-      })
-      .catch((error: Error) => {
-        Logger.error('[ScreenUtil] Failed to obtain the window size. Cause: ' + JSON.stringify(error));
-      })
-
-  }
-
-  public getcurrentName():string {
-    return this.name
-
-  }
-  /**
-   * Creates a videoPlayer object.
-   */
-  async createAVPlayer() {
-    let avPlayer: media.AVPlayer = await media.createAVPlayer();
-    this.avPlayer = avPlayer;
-    this.bindState();
-
-
-  }
-
-
-  async createSession() {
-    if (!this.context) {
-      return;
-    }
-    this.session = await avSession.createAVSession(this.context, 'SESSION_NAME', 'audio');
-    this.session.activate();
-    Logger.info(TAG, `session create done : sessionId : ${this.session.sessionId}`);
-    this.setAVMetadata();
-    let wantAgentInfo: wantAgent.WantAgentInfo = {
-      wants: [
-        {
-          bundleName: this.context.abilityInfo.bundleName,
-          abilityName: this.context.abilityInfo.name
-        }
-      ],
-      operationType: wantAgent.OperationType.START_ABILITIES,
-      requestCode: 0,
-      wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
-    }
-    wantAgent.getWantAgent(wantAgentInfo).then((agent) => {
-      if (this.session) {
-        this.session.setLaunchAbility(agent);
-      }
-    })
-    this.setListenerForMesFromController();
-  }
-
-  async setAVMetadata() {
-    let id = this.index;
-    try {
-      if (this.context) {
-
-        let metadata: avSession.AVMetadata = {
-          assetId: `${id}`,
-          title: this.name,
-          artist: '',
-          duration: this.duration
-        };
-        if (this.session) {
-          this.session.setAVMetadata(metadata).then(() => {
-            Logger.info(TAG, 'SetAVMetadata successfully');
-          }).catch((err: BusinessError) => {
-            Logger.error(TAG, `SetAVMetadata BusinessError: code: ${err.code}, message: ${err.message}`);
-          });
-        }
-      }
-    } catch (error) {
-      Logger.error(TAG, `SetAVMetadata try: code: ${(error as BusinessError).code},
-       message: ${(error as BusinessError).message}`);
-    }
-  }
-
-  async setListenerForMesFromController() {
-    if (!this.session) {
-      return;
-    }
-    this.session.on('play', this.playCall);
-    this.session.on('pause', this.pauseCall);
-    this.session.on('playNext', this.playNextCall);
-    this.session.on('playPrevious', this.playPreviousCall);
-  }
-  private playCall: () =>void = () => {
-    Logger.info(TAG, `on play , do play task`);
-    if (this.avPlayer !== null) {
-      this.avPlayer.play();
-    }
-  };
-  private pauseCall: () =>void = () => {
-    Logger.info(TAG, `on pause , do pause task`);
-    this.pause();
-  };
-  private playNextCall: () =>void = () => {
-    Logger.info(TAG, `on playNext , do playNext task`);
-    this.nextVideo()
-  };
-  private playPreviousCall: () =>void = () => {
-    Logger.info(TAG, `on playPrevious , do playPrevious task`);
-    this.previousVideo()
-  };
-  async unregisterSessionListener() {
-    if (!this.session) {
-      return;
-    }
-    this.session.off('play');
-    this.session.off('pause');
-    this.session.off('playNext');
-    this.session.off('playPrevious');
-  }
-
-  setIsLandscape(isLand:boolean){
-    this.isLandscape = isLand
-  }
-
-  /**
-   * AVPlayer binding event.
-   */
-  async bindState() {
-    if (this.avPlayer === null) {
-      return;
-    }
-    this.avPlayer.on(Events.STATE_CHANGE, async (state: media.AVPlayerState) => {
-      let avplayerStatus: string = state;
-      if (this.avPlayer === null) {
-        return;
-      }
-      switch (avplayerStatus) {
-        case AvplayerStatus.IDLE:
-          this.resetProgress();
-
-          this.avPlayer.url = this.url;
-
-          break;
-        case AvplayerStatus.INITIALIZED:
-          this.avPlayer.surfaceId = this.surfaceId;
-          this.avPlayer.prepare();
-          break;
-        case AvplayerStatus.PREPARED:
-          this.avPlayer.videoScaleType = 0;
-          this.setVideoSize();
-          this.avPlayer.play();
-          this.duration = this.avPlayer.duration;
-          // if(Utility.isMusicByExtension(this.name)){
-          //   this.createSession();
-          // }
-          break;
-        case AvplayerStatus.PLAYING:
-          this.avPlayer.setVolume(this.playerModel.volume);
-          this.setBright();
-          this.status = CommonConstants.STATUS_START;
-          this.watchStatus();
-          // BackgroundUtil.startContinuousTask(this.context);
-          break;
-        case AvplayerStatus.PAUSED://暂停
-          this.status = CommonConstants.STATUS_PAUSE;
-          this.watchStatus();
-          break;
-        case AvplayerStatus.COMPLETED:///播放完成
-          this.playerModel.playSpeed = PlayConstants.PLAY_SPEED;
-          this.duration = PlayConstants.PLAYER_DURATION;
-          if (!this.loop) {
-            let curIndex = this.index + PlayConstants.PLAYER_NEXT;
-            // let globalVideoList = GlobalContext.getContext().getObject('globalVideoList') as VideoItem[];
-            this.index = (curIndex === this.globalVideoList.length) ?
-            PlayConstants.PLAYER_FIRST : curIndex;
-
-            this.url = this.globalVideoList[this.index].src;
-            this.name = this.globalVideoList[this.index].name
-            this.onPlayCompleted?.(this.name)//播放完成的接口回调,在播放界面的title更新下一首的音乐或者视频名称
-          }
-          this.avPlayer.reset();
-          break;
-        case AvplayerStatus.RELEASED:
-          this.avPlayer.release();
-          this.status = CommonConstants.STATUS_STOP;
-          BackgroundUtil.stopContinuousTask(this.context);
-          this.watchStatus();
-          Logger.info('[PlayVideoModel] state released called');
-
-          break;
-        default:
-          Logger.info('[PlayVideoModel] unKnown state: ' + state);
-          break;
-      }
-    });
-    this.avPlayer.on(Events.TIME_UPDATE, (time: number) => {
-      this.initProgress(time);
-    });
-    this.avPlayer.on(Events.ERROR, () => {
-      this.playError();
-    })
-
-    // get video height and width
-    this.avPlayer.on('videoSizeChange', (width: number, height: number) => {
-      Logger.info('videoSizeChange called,and width is:' + width + ', height is :' + height);
-      this.callBack(height / width);
-    })
-  }
-
-  /**
-   * This method is triggered when the video playback page is displayed on the video list page.
-   */
-  async firstPlay(index: number, url:string, iUrl: string, surfaceId: string,globalVideoList:VideoItem[], callBack?: Function) {
-    this.index = index;
-    this.url = url;
-    this.iUrl = iUrl;
-    this.surfaceId = surfaceId;
-    this.globalVideoList = globalVideoList;
-
-
-
-    this.callBack = this.callBack
-    if (this.avPlayer === null) {
-      await this.createAVPlayer();
-    }
-    if (this.avPlayer !== null) {
-
-      this.avPlayer.url = this.url;
-      this.name = this.globalVideoList[index].name
-
-    }
-
-  }
-
-  /**
-   * Release the video player.
-   */
-  release() {
-    if (this.avPlayer !== null) {
-      this.avPlayer.release();
-    }
-  }
-
-  /**
-   * Pause Playing.
-   */
-  pause() {
-    if (this.avPlayer !== null) {
-      this.avPlayer.pause();
-    }
-  }
-
-  /**
-   * Playback mode. The options are as follows: true: playing a single video; false: playing a cyclic video.
-   */
-  setLoop() {
-    this.loop = !this.loop;
-  }
-
-  /**
-   * Set the playback speed.
-   *
-   * @param playSpeed Current playback speed.
-   */
-  setSpeed(playSpeed: number) {
-    if (this.avPlayer === null) {
-      return;
-    }
-    if (CommonConstants.OPERATE_STATE.indexOf(this.avPlayer.state) === -1) {
-      return;
-    }
-    this.playerModel.playSpeed = playSpeed;
-    this.avPlayer.setSpeed(this.playerModel.playSpeed);
-  }
-
-  /**
-   * Previous video.
-   */
-  previousVideo() {
-    if (this.avPlayer === null) {
-      return;
-    }
-    if (CommonConstants.OPERATE_STATE.indexOf(this.avPlayer.state) === -1) {
-      return;
-    }
-    this.playerModel.playSpeed = PlayConstants.PLAY_SPEED;
-    // let globalVideoList = GlobalContext.getContext().getObject('globalVideoList') as VideoItem[];
-    let curIndex = this.index - PlayConstants.CONTROL_NEXT;
-    this.index = (curIndex === -PlayConstants.CONTROL_NEXT) ?
-      (this.globalVideoList.length - PlayConstants.CONTROL_NEXT) : curIndex;
-
-    this.url = this.globalVideoList[this.index].src;
-    this.name = this.globalVideoList[this.index].name
-
-    this.avPlayer.reset();
-  }
-
-  /**
-   * Next video.
-   */
-  nextVideo() {
-    if (this.avPlayer === null) {
-      return;
-    }
-    if (CommonConstants.OPERATE_STATE.indexOf(this.avPlayer.state) === -1) {
-      return;
-    }
-    this.playerModel.playSpeed = PlayConstants.PLAY_SPEED;
-    // let globalVideoList = GlobalContext.getContext().getObject('globalVideoList') as VideoItem[];
-    let curIndex = this.index + PlayConstants.CONTROL_NEXT;
-
-    this.index = (curIndex === this.globalVideoList.length) ? PlayConstants.CONTROL_FIRST : curIndex;
-
-    this.url = this.globalVideoList[this.index].src;
-    this.name = this.globalVideoList[this.index].name
-
-    this.avPlayer.reset();
-  }
-
-  /**
-   * Switching Between Video Play and Pause.
-   */
-  switchPlayOrPause() {
-    if (this.avPlayer === null) {
-      return;
-    }
-    if (this.status === CommonConstants.STATUS_START) {
-      this.avPlayer.pause();
-    } else {
-      this.avPlayer.play();
-    }
-  }
-
-  /**
-   * Slide the progress bar to set the playback progress.
-   *
-   * @param value Value of the slider component.
-   * @param mode Slider component change event.
-   */
-  setSeekTime(value: number, mode: SliderChangeMode) {
-    if (mode === Number(SliderMode.MOVING)) {
-      this.playerModel.progressVal = value;
-      this.playerModel.currentTime = DateFormatUtil.secondToTime(Math.floor(value * this.duration /
-      CommonConstants.ONE_HUNDRED / CommonConstants.A_THOUSAND));
-    }
-    if (mode === Number(SliderMode.END) || mode === Number(SliderMode.CLICK)) {
-      this.seekTime = value * this.duration / CommonConstants.ONE_HUNDRED;
-      if (this.avPlayer !== null) {
-        this.avPlayer.seek(this.seekTime, media.SeekMode.SEEK_PREV_SYNC);
-      }
-    }
-  }
-
-
-  /**
-   * Setting the brightness.
-   */
-  setBright() {
-    let windowClass = GlobalContext.getContext().getObject('windowClass') as window.Window;
-    windowClass.setWindowBrightness(this.playerModel.bright);
-  }
-
-  /**
-   * Obtains the current video playing status.
-   */
-  getStatus() {
-    return this.status;
-  }
-
-  /**
-   * Initialization progress bar.
-   *
-   * @param time Current video playback time.
-   */
-  initProgress(time: number) {
-    let nowSeconds = Math.floor(time / CommonConstants.A_THOUSAND);
-    let totalSeconds = Math.floor(this.duration / CommonConstants.A_THOUSAND);
-    this.playerModel.currentTime = DateFormatUtil.secondToTime(nowSeconds);
-    this.playerModel.totalTime = DateFormatUtil.secondToTime(totalSeconds);
-    this.playerModel.progressVal = Math.floor(nowSeconds * CommonConstants.ONE_HUNDRED / totalSeconds);
-  }
-
-  /**
-   * Reset progress bar data.
-   */
-  resetProgress() {
-    this.seekTime = PlayConstants.PROGRESS_SEEK_TIME;
-    this.playerModel.currentTime = PlayConstants.PROGRESS_CURRENT_TIME;
-    this.playerModel.progressVal = PlayConstants.PROGRESS_PROGRESS_VAL;
-  }
-
-  /**
-   * Volume gesture method onActionStart.
-   *
-   * @param event Gesture event.
-   */
-  onVolumeActionStart(event?: GestureEvent) {
-    if (!event) {
-      return;
-    }
-    this.positionX = event.offsetX;
-  }
-
-  /**
-   * Bright gesture method onActionStart.
-   *
-   * @param event Gesture event.
-   */
-  onBrightActionStart(event?: GestureEvent) {
-    if (!event) {
-      return;
-    }
-    this.positionY = event.offsetY;
-  }
-
-  /**
-   * Gesture method onActionUpdate.
-   *
-   * @param event Gesture event.
-   */
-  onVolumeActionUpdate(event?: GestureEvent) {
-    if (!event) {
-      return;
-    }
-    if (this.avPlayer === null) {
-      return;
-    }
-    if (CommonConstants.OPERATE_STATE.indexOf(this.avPlayer.state) === -1) {
-      return;
-    }
-    if (this.playerModel.brightShow === false) {
-      this.playerModel.volumeShow = true;
-      let screenWidth = GlobalContext.getContext().getObject('screenWidth') as number;
-      let changeVolume = (event.offsetX - this.positionX) / screenWidth;
-      let volume: number = this.playerModel.volume;
-      let currentVolume = volume + changeVolume;
-      let volumeMinFlag = currentVolume <= PlayConstants.MIN_VALUE;
-      let volumeMaxFlag = currentVolume > PlayConstants.MAX_VALUE;
-      this.playerModel.volume = volumeMinFlag ? PlayConstants.MIN_VALUE :
-        (volumeMaxFlag ? PlayConstants.MAX_VALUE : currentVolume);
-      this.avPlayer.setVolume(this.playerModel.volume);
-      this.positionX = event.offsetX;
-    }
-  }
-
-  /**
-   * Gesture method onActionUpdate.
-   *
-   * @param event Gesture event.
-   */
-  onBrightActionUpdate(event?: GestureEvent) {
-    if (!event) {
-      return;
-    }
-    if (this.playerModel.volumeShow === false) {
-      this.playerModel.brightShow = true;
-      let screenHeight = GlobalContext.getContext().getObject('screenHeight') as number;
-      let changeBright = (this.positionY - event.offsetY) / screenHeight;
-      let bright: number = this.playerModel.bright;
-      let currentBright = bright + changeBright;
-      let brightMinFlag = currentBright <= PlayConstants.MIN_VALUE;
-      let brightMaxFlag = currentBright > PlayConstants.MAX_VALUE;
-      this.playerModel.bright = brightMinFlag ? PlayConstants.MIN_VALUE :
-        (brightMaxFlag ? PlayConstants.MAX_VALUE : currentBright);
-      Logger.info('onecold this.bright = ' + this.playerModel.bright)
-      this.setBright();
-      this.positionY = event.offsetY;
-    }
-  }
-
-  /**
-   * Gesture method onActionEnd.
-   */
-  onActionEnd() {
-    setTimeout(() => {
-      this.playerModel.volumeShow = false;
-      this.playerModel.brightShow = false;
-      this.positionX = PlayConstants.POSITION_X;
-      this.positionY = PlayConstants.POSITION_Y;
-    }, PlayConstants.DISAPPEAR_TIME);
-  }
-
-  /**
-   * Sets whether the screen is a constant based on the playback status.
-   */
-  watchStatus() {
-    let windowClass = GlobalContext.getContext().getObject('windowClass') as window.Window;
-    if (this.status === CommonConstants.STATUS_START) {
-      windowClass.setWindowKeepScreenOn(true);
-    } else {
-      windowClass.setWindowKeepScreenOn(false);
-    }
-  }
-
-  /**
-   * Sets the playback page size based on the video size.
-   */
-  setVideoSize() {
-    if (this.avPlayer === null) {
-      return;
-    }
-
-    let VideoWidth = this.avPlayer.width;
-    let VideoHeight = this.avPlayer.height;
-    let scale = VideoHeight/VideoWidth//视频的长高比例
-
-    let screenScale = this.screenHeight/ this.screenWidth//屏幕的长高比例
-    let screenScale2 = (this.screenHeight-this.statusBarHeight)/ this.screenWidth//屏幕的长高比例:竖屏模式下用这个screenScale2 有去掉状态栏高度
-
-
-    if(VideoHeight > VideoWidth){//竖屏视频
-
-      if(this.isLandscape){
-        this.playerModel.videoWidth = (1/(screenScale*scale))*100+'%';
-        // this.videoWidth = '26.5%';
-        this.playerModel.videoHeight = CommonConstants.FULL_PERCENT;;
-      }else{
-        this.playerModel.videoWidth = CommonConstants.FULL_PERCENT;
-        this.playerModel.videoHeight = CommonConstants.FULL_PERCENT;
-      }
-
-    }else{//横屏视频
-      if(this.isLandscape){
-        if(this.isYesFull){
-          this.playerModel.videoWidth = '100%';
-          this.playerModel.videoHeight = '100%';
-        }else{
-          // this.videoWidth = '85.8%';
-          this.playerModel.videoWidth = (1/(screenScale*scale))*100+'%';
-          this.playerModel.videoHeight = '100%';
-        }
-
-      }else{
-        this.playerModel.videoWidth = '100%';
-        // this.videoHeight = '27.5%';
-        this.playerModel.videoHeight = (scale/screenScale2)*100+'%';
-      }
-    }
-
-
-  }
-
-  /**
-   * An error is reported during network video playback.
-   */
-  playError() {
-    promptAction.showToast({
-      duration: PlayConstants.PLAY_ERROR_TIME,
-      message: $r('app.string.link_check_address_internet')
-    });
-  }
-}

+ 0 - 1
entry/src/main/ets/pages/MainIndex.ets

@@ -1,5 +1,4 @@
 
-import { SettingComponent } from '../view/SettingComponent'
 import { StreamContent } from '../view/StreamContent'
 import ScreenUtil from '../common/util/ScreenUtil'
 import { window } from '@kit.ArkUI'

+ 51 - 32
entry/src/main/ets/view/LocalMusic.ets

@@ -5902,7 +5902,7 @@ export struct LocalMusic {
               .onClick(() => {
                 this.setLyricAlignMode(index)
               })
-              .margin({ left: 8, right: 8 })
+              .margin({ left: 10, right: 8 })
           })
         }
         .width('76%')
@@ -5911,6 +5911,36 @@ export struct LocalMusic {
       }
       .margin({ top:10,bottom:10,right:20,left:20})
 
+      Row() {
+        Text(`高亮居中:`)
+          .fontSize(14)
+          .fontColor(Color.White)
+
+        Column(){
+          Toggle({ type: ToggleType.Switch, isOn: this.isHightLightCenter })
+            .selectedColor($r('app.color.tab_item_bg'))
+            .switchPointColor(Color.White)
+            .onChange((checked: boolean) => {
+              this.isHightLightCenter = checked;
+              PreferencesUtil.put(SettingPage.IS_SHOW_SIMI, this.isHightLightCenter)
+              this.lyricController.setHightLightCenter(this.isHightLightCenter)
+            })
+            .width(50)
+            .height(30)
+            .alignSelf(ItemAlign.Start)
+            .margin({ left: 12 })
+          Blank()
+
+        }
+        .width('76%')
+
+      }
+      .margin({
+        left: 20,
+        right: 15,
+        top: 10,
+        bottom: 10
+      })
       Row() {
         Text(`歌词颜色:`)
           .fontSize(14)
@@ -5918,19 +5948,27 @@ export struct LocalMusic {
         Text(this.currentLyricColor)
           .fontSize(15)
           .fontColor(Color.White)
-          .margin({ left: 10, right: 12 })
+          .margin({ left: 12, right: 12 })
+        Column(){}
+        .backgroundColor(this.currentLyricColor)
+        .margin({ right: 12 })
+        .borderRadius(20)
+        .height(28)
+        .width(28)
+
         Button({ type: ButtonType.Capsule, stateEffect: true }) {
           Row() {
             SymbolGlyph($r('sys.symbol.paintbrush'))
+              .fontColor([Color.White, Color.Green, Color.Black])
             Text(`选择颜色`)
               .fontSize(13)
               .margin({ left: 10 })
-              .fontColor('007DFF')
+              .fontColor($r('app.color.text_color'))
 
           }
         }
         .backgroundColor($r('app.color.bottom_control_background'))
-        .height(38)
+        .height(36)
         .width(110)
         .onClick(()=>{
           this.isShowSelectColor = !this.isShowSelectColor
@@ -5963,14 +6001,21 @@ export struct LocalMusic {
         Text(this.currentHighLightLyricColor)
           .fontSize(15)
           .fontColor(Color.White)
-          .margin({ left: 10, right: 12 })
+          .margin({ left: 12, right: 12 })
+        Column(){}
+        .backgroundColor(this.currentHighLightLyricColor)
+        .margin({ right: 12 })
+        .borderRadius(20)
+        .height(28)
+        .width(28)
         Button({ type: ButtonType.Capsule, stateEffect: true }) {
           Row() {
             SymbolGlyph($r('sys.symbol.paintbrush'))
+              .fontColor([Color.White, Color.Green, Color.Black])
             Text(`选择颜色`)
               .fontSize(13)
               .margin({ left: 10 })
-              .fontColor('007DFF')
+              .fontColor($r('app.color.text_color'))
 
           }
         }
@@ -6159,32 +6204,6 @@ export struct LocalMusic {
         bottom: 15
       })
 
-      Row() {
-        Text(`高亮居中:`)
-          .fontSize(14)
-          .fontColor(Color.White)
-        Column(){
-          Toggle({ type: ToggleType.Switch, isOn: this.isHightLightCenter })
-            .selectedColor($r('app.color.tab_item_bg'))
-            .switchPointColor(Color.White)
-            .onChange((checked: boolean) => {
-              this.isHightLightCenter = checked;
-              PreferencesUtil.put(SettingPage.IS_SHOW_SIMI, this.isHightLightCenter)
-              this.lyricController.setHightLightCenter(this.isHightLightCenter)
-            })
-            .width(50)
-            .height(30)
-        }
-        .justifyContent(FlexAlign.Start)
-          .width('76%')
-
-      }
-      .margin({
-        left: 20,
-        right: 15,
-        top: 5,
-        bottom: 15
-      })
 
       Row() {
         this.pushLyricButton($r('app.media.cut_current'), 0, '本地歌词')

+ 0 - 417
entry/src/main/ets/view/SettingComponent.ets

@@ -1,417 +0,0 @@
-import TitleBar from './TitleBar'
-import { Router } from '@ohos.arkui.UIContext'
-import { router } from '@kit.ArkUI'
-import { CommonConstants } from '../common/constants/CommonConstants'
-import { AppUtil } from '@pura/harmony-utils'
-import { productViewManager } from '@kit.StoreKit'
-import { common, Want } from '@kit.AbilityKit'
-import { BusinessError } from '@kit.BasicServicesKit'
-import { hilog } from '@kit.PerformanceAnalysisKit'
-import { systemShare } from '@kit.ShareKit'
-import { uniformTypeDescriptor as utd } from '@kit.ArkData';
-import { Utility } from '../common/util/Utility'
-
-@Preview
-@Component
-export struct SettingComponent{
-
-  @State titleBarModel: TitleBar.Model = new TitleBar.Model()
-    .setLeftIcon(null)
-    .setTitleTextStyle(FontStyle.Normal)
-    .setTitleName("我的")
-    .setTitleFontColor(Color.White)
-    .setTitleBarBackground($r('app.color.title_bar_bg'))
-    .setTitleBarBottomLineColor($r('app.color.title_bar_bg'))
-
-  @State bundleName:string =''
-  @State appName:string = ''
-  @State versionName:string =''
-
-  async aboutToAppear() {
-    this.bundleName = await  AppUtil.getBundleName()
-     this.versionName = await AppUtil.getVersionName();
-    Utility.getAppName(getContext(this)).then((appName:string)=>{
-      this.appName = appName
-    })
-  }
-  gotoMarket() {
-
-    const want: Want = {
-      uri: `store://appgallery.huawei.com/app/detail?id=${this.bundleName}`
-    };
-    const context = getContext(this) as common.UIAbilityContext;
-    context.startAbility(want).then(()=>{
-      //拉起成功
-    }).catch(()=>{
-      // 拉起失败
-    });
-
-  }
-
-  gotoShare(){
-    let shareData: systemShare.SharedData = new systemShare.SharedData({
-      utd: utd.UniformDataType.TEXT,
-      content: 'https://appgallery.huawei.com/app/detail?id='+this.bundleName,
-      title: this.appName, // 不传title字段时,显示content
-      description: '高品质无损音乐播放器',
-      // thumbnail: new Uint8Array() // 推荐传入适合的缩略图 不传则显示默认text图标
-    });
-
-    // 进行分享面板显示
-    let controller: systemShare.ShareController = new systemShare.ShareController(shareData);
-    let context = getContext(this) as common.UIAbilityContext;
-    controller.show(context, {
-      selectionMode: systemShare.SelectionMode.SINGLE,
-      previewMode: systemShare.SharePreviewMode.DETAIL,
-    }).then(() => {
-      console.info('ShareController show success.');
-    }).catch((error: BusinessError) => {
-      console.error(`ShareController show error. code: ${error.code}, message: ${error.message}`);
-    });
-  }
-
-
-  build() {
-    Scroll(){
-      Column() {
-
-        TitleBar({ model: $titleBarModel })
-
-        Column(){
-          Column() {
-            Image($r('app.media.icon'))
-              .visibility(Visibility.Visible)
-              .borderRadius('100%')
-              .clip(true)
-              .width(55)
-              .height(55)
-
-          }
-          .justifyContent(FlexAlign.Center)
-          .backgroundImage($r('app.media.ic_avatar5'))
-          .backgroundImageSize({ height: 222 })
-          // .backgroundImageSize(ImageSize.Contain)
-          .width('100%')
-          .height(166)
-
-          .borderRadius(30)
-        }
-
-        .margin({left:15,right:15,top:15,bottom:0})
-
-        .justifyContent(FlexAlign.Center)
-        // .borderRadius({bottomLeft:20,bottomRight:20})
-        Column() {
-
-          //通用设置
-          Row() {
-            Image($r('app.media.hm_gps'))
-              .width(22)
-              .height(22)
-              .alignSelf(ItemAlign.Center)
-              .margin({ left: 28 })
-            Text('通用设置')
-              .margin({ left: 10, right: 20 })
-              .fontSize(15)
-              .fontColor(Color.Gray)
-              .fontWeight(480)
-            Blank()
-            Image($r('app.media.arrow_right'))
-              .width(22)
-              .height(22)
-              .margin({ left: 20, right: 20 })
-              .align(Alignment.Center)
-
-          }
-          .width('100%')
-          .height(55)
-          .onClick(async () => {
-            router.pushUrl({
-              url: 'pages/SettingPage'
-            }, router.RouterMode.Single);
-
-
-          })
-
-
-          Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
-
-          //版本更新
-          Row() {
-            Image($r('app.media.lishi'))
-              .width(22)
-              .height(22)
-              .alignSelf(ItemAlign.Center)
-              .margin({ left: 28 })
-            Text('版本更新')
-              .margin({ left: 10, right: 20 })
-              .fontSize(15)
-              .fontColor(Color.Gray)
-              .fontWeight(480)
-              .id('text_update')
-            Blank()
-            Image($r('app.media.arrow_right'))
-              .width(22)
-              .height(22)
-              .margin({ left: 20, right: 20 })
-              .align(Alignment.Center)
-
-          }
-          .width('100%')
-          .height(55)
-          .onClick(async () => {
-            // this.gotoMarket()
-
-            AlertDialog.show({title:'版本更新',message:'当前版本为最新版本:'+this.versionName,
-              autoCancel:true, alignment:DialogAlignment.Center,
-              offset:{dx:0,dy:-20},//在Y轴方向上的编译量
-              confirm:{
-                value:'确定',fontColor:Color.White,backgroundColor:$r('app.color.title_bar_bg'),
-                action:()=>{
-
-                }
-
-              }
-            })
-
-          })
-
-
-          Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
-
-          //用户协议
-          Row() {
-            Image($r('app.media.hm_persion'))
-              .width(22)
-              .height(22)
-              .alignSelf(ItemAlign.Center)
-              .margin({ left: 28 })
-            Text('用户协议')
-              .margin({ left: 10, right: 20 })
-              .fontSize(15)
-              .fontColor(Color.Gray)
-              .fontWeight(480)
-              .id('text_update')
-            Blank()
-            Image($r('app.media.arrow_right'))
-              .width(22)
-              .height(22)
-              .margin({ left: 20, right: 20 })
-
-          }
-          .width('100%')
-          .height(55)
-          .onClick(() => {
-            router.pushUrl({
-              url: 'pages/WebIndex',
-              params: { titleName: '用户协议', webUrl: CommonConstants.NEW_DUTY }
-            });
-          })
-
-          Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
-
-
-          //隐私政策
-          Row() {
-            Image($r('app.media.icon_about'))
-              .width(22)
-              .height(22)
-              .alignSelf(ItemAlign.Center)
-              .margin({ left: 28 })
-            Text('隐私政策')
-              .margin({ left: 10, right: 20 })
-              .fontSize(15)
-              .fontColor(Color.Gray)
-              .fontWeight(480)
-              .id('text_update')
-            Blank()
-            Image($r('app.media.arrow_right'))
-              .width(22)
-              .height(22)
-              .margin({ left: 20, right: 20 })
-
-          }
-          .width('100%')
-          .height(55)
-          .onClick(() => {
-            router.pushUrl({
-              url: 'pages/WebIndex',
-              params: { titleName: '隐私政策', webUrl: CommonConstants.NEW_YS_HW }
-            });
-          })
-
-          Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
-
-          //赞助中心
-          Row() {
-            Image($r('app.media.vip'))
-              .width(22)
-              .height(22)
-              .alignSelf(ItemAlign.Center)
-              .margin({ left: 28 })
-            Text('赞助中心')
-              .margin({ left: 10, right: 20 })
-              .fontSize(15)
-              .fontColor(Color.Gray)
-              .fontWeight(480)
-              .id('text_update')
-            Blank()
-            Image($r('app.media.arrow_right'))
-              .width(22)
-              .height(22)
-              .margin({ left: 20, right: 20 })
-
-          }
-          .width('100%')
-          .height(55)
-          .onClick(() => {
-
-          })
-
-          Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
-
-
-          //给个好评
-          Row() {
-            Image($r('app.media.hm_flower'))
-              .width(22)
-              .height(22)
-              .alignSelf(ItemAlign.Center)
-              .margin({ left: 28 })
-            Text('给个好评')
-              .margin({ left: 10, right: 20 })
-              .fontSize(15)
-              .fontColor(Color.Gray)
-              .fontWeight(480)
-              .id('text_update')
-            Blank()
-            Image($r('app.media.arrow_right'))
-              .width(22)
-              .height(22)
-              .margin({ left: 20, right: 20 })
-
-
-          }
-          .width('100%')
-          .height(55)
-          .onClick(() => {
-            this.gotoMarket();
-          })
-
-
-          Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
-
-          //用户反馈
-          Row() {
-            Image($r('app.media.oh_yszc'))
-              .width(22)
-              .height(22)
-              .alignSelf(ItemAlign.Center)
-              .margin({ left: 28 })
-            Text('用户反馈')
-              .margin({ left: 10, right: 20 })
-              .fontSize(15)
-              .fontColor(Color.Gray)
-              .fontWeight(480)
-              .id('text_update')
-            Blank()
-            Image($r('app.media.arrow_right'))
-              .width(22)
-              .height(22)
-              .margin({ left: 20, right: 20 })
-
-
-          }
-          .width('100%')
-          .height(55)
-          .onClick(() => {
-            AlertDialog.show({title:'发送邮件',message:'感谢你的支持,如果有什么不支持的格式不能播放请反馈给我们修复,谢谢!有建议和反馈的请邮件联系我们:'+CommonConstants.CONTACT_MAIL,
-              autoCancel:true, alignment:DialogAlignment.Center,
-              offset:{dx:0,dy:-20},//在Y轴方向上的编译量
-              confirm:{
-                value:'确定',fontColor:Color.White,backgroundColor:$r('app.color.title_bar_bg'),
-                action:()=>{
-                  Utility.copyText(CommonConstants.CONTACT_MAIL)
-
-                }
-
-              }
-            })
-          })
-
-
-          Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
-
-          //关于我们
-          Row() {
-            Image($r('app.media.icon_heart'))
-              .width(22)
-              .height(22)
-              .alignSelf(ItemAlign.Center)
-              .margin({ left: 28 })
-            Text('关于我们')
-              .margin({ left: 10, right: 20 })
-              .fontSize(15)
-              .fontColor(Color.Gray)
-              .fontWeight(480)
-              .id('text_update')
-            Blank()
-            Image($r('app.media.arrow_right'))
-              .width(22)
-              .height(22)
-              .margin({ left: 20, right: 20 })
-
-          }
-          .width('100%')
-          .height(55)
-          .onClick(() => {
-            router.pushUrl({
-              url: 'pages/AboutPage'
-            }, router.RouterMode.Single);
-          })
-          Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
-          //分享给朋友
-          Row() {
-            Image($r('app.media.oh_share'))
-              .width(22)
-              .height(22)
-              .alignSelf(ItemAlign.Center)
-              .margin({ left: 28 })
-            Text('分享给朋友')
-              .margin({ left: 10, right: 20 })
-              .fontSize(15)
-              .fontColor(Color.Gray)
-              .fontWeight(480)
-              .id('text_update')
-            Blank()
-            Image($r('app.media.arrow_right'))
-              .width(22)
-              .height(22)
-              .margin({ left: 20, right: 20 })
-              .align(Alignment.Center)
-
-          }
-          .width('100%')
-          .height(55)
-          .onClick(async () => {
-            this.gotoShare()
-
-
-          })
-
-
-
-
-        }
-        .backgroundColor(Color.White)
-
-        .margin({left:15,right:15,top:20,bottom:10})
-        .borderRadius(30)
-      }
-      .backgroundColor($r('app.color.index_background'))
-      .height('100%')
-    }
-    .height(1024)
-  }
-
-}

+ 0 - 192
entry/src/main/ets/view/VideoIndex.ets

@@ -1,192 +0,0 @@
-
-import { LEDContent } from '../view/LEDContent'
-import { LocalVideo } from '../view/LocalVideo'
-import { OptimizeContent } from '../view/OptimizeComponent'
-import { SettingComponent } from '../view/SettingComponent'
-import { StreamContent } from '../view/StreamContent'
-import { ToolView } from '../view/ToolView'
-import {  WiFiView } from '../view/WiFiView'
-import { VideoItem } from '../viewmodel/VideoItem'
-import TitleBar from './TitleBar'
-import { photoAccessHelper } from '@kit.MediaLibraryKit'
-import { ArrayUtil, GlobalContext, JSONUtil, PickerUtil, StrUtil } from '@pura/harmony-utils'
-import { Utility } from '../common/util/Utility'
-import Logger from '../common/util/Logger'
-import { CommonConstants } from '../common/constants/CommonConstants'
-import { PreferencesUtil } from '@pura/harmony-utils'
-import { BusinessError } from '@kit.BasicServicesKit'
-import ScreenUtil from '../common/util/ScreenUtil'
-
-
-@Component
-export struct  VideoIndex{
-  @State currIndex:number = 0
-
-  // @State videoLocalList: Array<VideoItem> = [];
-  context = getContext(this);
-  @StorageLink('LocalList')  videoLocalList: Array<VideoItem> = []
-  @Provide('isZero') isZero:boolean = false;
-
-  @State titleBarModel: TitleBar.Model = new TitleBar.Model()
-    .setLeftIcon(null)
-    .setTitleTextStyle(FontStyle.Normal)
-    .setTitleBarStyle(TitleBar.BarStyle.TRANSPARENT)
-    .setTitleName("本地视频")
-    .setRightIcon($r('app.media.add'))
-    .setTitleFontColor(Color.White)
-    .setTitleBarBackground($r('app.color.title_bar_bg'))
-    .setTitleBarBottomLineColor($r('app.color.title_bar_bg'))
-    .setRightTitleBackground($r('app.color.title_bar_bg'))
-    .setOnRightClickListener(()=>{
-      this.goSelectVideo()
-    })
-
-  @Builder TabBarBuilder(index:number,selIcon:ResourceStr,normalIcon:ResourceStr,text:string){
-    Column(){
-      // Image(this.currIndex == index ? selIcon:normalIcon)
-      //   .width(24)
-      //   .visibility(Visibility.None)
-      Text(text)
-        .fontSize(14)
-        .fontColor(this.currIndex == index ? $r('app.color.title_bar_bg'):$r('app.color.tab_bar_normal') )
-    }
-
-  }
-
-  // 组件生命周期
-  async aboutToAppear() {
-    ScreenUtil.setScreenSize();
-    AppStorage.setOrCreate('LocalList',this.videoLocalList)
-    this.getPreData()
-   // let data1 =  new VideoItem('o10','fd://90','file://media/Photo/38/VID_1727586401_010/VID_010.mp4')
-   //  let data2 =  new VideoItem('o11','fd://90','file://media/Photo/38/VID_1727586401_010/VID_011.mp4')
-   //  let list: Array<VideoItem> = [];
-   //
-   //  list.push(data1)
-   //  list.push(data2)
-   //  this.videoLocalList = list
-   // setTimeout(()=>{
-   //   this.getPreData()
-   // },2000)
-
-
-
-    console.info('video Component aboutToAppear');
-  }
-
-
-  async getPreData(){
-    let videoListStr = PreferencesUtil.getStringSync(CommonConstants.VIDEO_LIST)
-    if(StrUtil.isNotEmpty( videoListStr)){
-      Logger.info('TAG', 'onPageShow result:' + videoListStr)
-      let array = JSONUtil.jsonToArray(VideoItem, videoListStr);
-      Logger.info('TAG', 'onPageShow result2:' + videoListStr)
-      let list: Array<VideoItem> = [];
-
-      for(let i=0;i<array.length;i++){
-        // let fd = await Utility.getFdDir(array[i].filePath)
-        // let item:VideoItem = new VideoItem( array[i].name, fd, array[i].filePath
-        //   ,await Utility.getFetchFrameByTime(array[i].filePath))
-        let item:VideoItem = await Utility.uriGetAssets(this.context,array[i].filePath,0);
-        list.push(item);
-      }
-
-      this.videoLocalList = list
-
-    }
-    this.setButtonStatus()
-
-  }
-
-
-  setButtonStatus(){
-    if(ArrayUtil.isNotEmpty(this.videoLocalList)){
-      this.isZero = false
-    }else{
-      this.isZero = true
-    }
-    GlobalContext.getContext().setObject('globalVideoList', this.videoLocalList);
-  }
-
-  build() {
-    Column(){
-      TitleBar({ model: $titleBarModel })
-      Tabs({
-        barPosition:BarPosition.Start,
-        index:this.currIndex
-      }){
-        //本地视频
-        TabContent(){
-          LocalVideo({currIndex:this.currIndex})
-        }
-        .tabBar(this.TabBarBuilder(0,
-          $r('app.media.icon_vip_select') , $r('app.media.icon_vip'),
-          "本地视频"))
-        //我的收藏
-        TabContent(){
-          // HomeTabContent()
-        }
-        .tabBar(this.TabBarBuilder(1,
-          $r('app.media.icon_updates_select') , $r('app.media.icon_updates'),
-          "我的收藏"))
-
-        //私密视频
-        TabContent(){
-          // LocalVideo()
-        }
-        .tabBar(this.TabBarBuilder(2,
-          $r('app.media.icon_happy_select') , $r('app.media.icon_happy'),
-          "私密视频"))
-
-
-      }
-      .vertical(false)
-      .scrollable(true)
-      .backgroundColor($r('app.color.index_background'))
-      .barMode(BarMode.Fixed)
-      .onChange((index)=>{
-        this.currIndex = index;
-      })
-    }
-
-  }
-
-  //拉起相册的选择刚刚截图的二维码图片进行扫描解析密码
-  goSelectVideo(){
-
-    let options = new photoAccessHelper.PhotoSelectOptions();
-    options.maxSelectNumber = 10;
-    options.MIMEType =photoAccessHelper.PhotoViewMIMETypes.VIDEO_TYPE;
-
-
-    PickerUtil.selectPhoto(options).then(async (uris) => {
-
-      if(ArrayUtil.isEmpty(uris))
-        return
-      for(let i=0;i<uris.length;i++){
-
-        // let fd = await Utility.getFdDir(uris[i])
-        // let item:VideoItem = new VideoItem( Utility.getMediaNameByUri(uris[i]), fd, uris[i],
-        //   await Utility.getFetchFrameByTime(uris[i]))
-        let item:VideoItem = await Utility.uriGetAssets(this.context,uris[i],0);
-
-        if(this.videoLocalList.find(i=>i.filePath === item.filePath) === undefined){//List没有这个视频,就push
-          this.videoLocalList.push(item);
-        }
-
-      }
-
-      Logger.info('TAG', 'scan select video result:' + uris)
-      // this.uriStr = `调用相册,返回uris:\n${uris.join('\n')}`
-
-      this.setButtonStatus()
-      // GlobalContext.getContext().setObject('videoLocalList', this.videoLocalList);
-      Logger.info('TAG', 'scan result:' + JSON.stringify(this.videoLocalList))
-      PreferencesUtil.putSync(CommonConstants.VIDEO_LIST,JSON.stringify(this.videoLocalList))
-    }).catch((err: BusinessError) => {
-      // this.uriStr = `调用相册,异常:\n${JSON.stringify(err)}`
-    })
-
-  }
-}
-

+ 69 - 1
lib/src/main/ets/parse/LyricParser.ts

@@ -38,6 +38,7 @@ export class LyricParser implements IParser {
                 printW("the lyric line is empty, carriage return or line feed, line index= " + i)
                 continue
             }
+
             // 检查是否是需要忽略的标签
             const shouldIgnore = ignoredTags.some(tag  => line.indexOf(tag)  > 0);
             if (shouldIgnore) {
@@ -56,7 +57,16 @@ export class LyricParser implements IParser {
                 offset = Number.parseInt(this.parseIdTag(line))
             } else {
 
-                // 新增逐字歌词解析逻辑
+                // 新增:逐字歌词[]检测方括号逐字歌词格式 [mm:ss.xxx] 文字
+                if (this.isSquareBracketWordByWordLyric(line))  {
+                    const { timeline, words } = this.parseSquareBracketWordLine(line,  offset);
+                    if (words.length  > 0) {
+                        lyricLines.push(new  LyricLine('', timeline, -1, words))
+                    }
+                    continue;
+                }
+
+                // 新增逐字歌词解析逻辑[mm:ss.xx] <mm:ss.xx>
                 if (this.isWordByWordLyric(line)) {
                     const { timeline, words } = this.parseWordByWordLine(line, offset);
                     lyricLines.push(new LyricLine('', timeline, -1, words))
@@ -100,6 +110,64 @@ export class LyricParser implements IParser {
         return result
     }
 
+
+    // 检测方括号格式的逐字歌词 [00:00.000]文[00:01.000]字
+    private isSquareBracketWordByWordLyric(line: string): boolean {
+        return /\[\d{2}:\d{2}\.\d{2,3}\]\S/.test(line);
+    }
+
+    // 解析方括号格式的逐字歌词行
+    private parseSquareBracketWordLine(line: string, offset: number):
+        { timeline: number, words: LyricWord[] } {
+
+        const words: LyricWord[] = [];
+        let firstTimeline = -1;
+
+        // 正则匹配:[00:00.000]中文字
+        const regex = /\[(\d{2}:\d{2}\.\d{2,3})\]([^\[]*)/g;
+        let match;
+
+        while ((match = regex.exec(line))  !== null) {
+            const timeStr = match[1];   // 时间部分 00:00.000
+            const word = match[2].trim(); // 歌词文本
+
+            if (!word) continue; // 跳过空词
+
+            const timeline = this.parseTimeline2(timeStr)  - offset;
+            if (firstTimeline < 0) firstTimeline = timeline;
+
+            words.push(new  LyricWord(word, timeline, 0));
+        }
+
+        // 计算每个词的持续时间
+        for (let i = 0; i < words.length  - 1; i++) {
+            words[i].duration = words[i + 1].startTime - words[i].startTime;
+        }
+        if (words.length  > 0 && words[words.length - 1].duration === 0) {
+            words[words.length - 1].duration = 200; // 默认200ms
+        }
+
+        return { timeline: firstTimeline, words };
+    }
+
+    /******************** 时间解析增强 ********************/
+    private parseTimeline2(timeString: string): number {
+        // 增强支持毫秒/厘秒解析
+        const parts = timeString.split(':');
+        const minutes = parseInt(parts[0], 10);
+
+        const secondParts = parts[1].split('.');
+        const seconds = parseInt(secondParts[0], 10);
+        const fraction = parseInt(secondParts[1], 10);
+
+        // 根据小数位长度判断时间精度
+        const milliseconds = secondParts[1].length === 2 ?
+            fraction * 10 : // 厘秒转毫秒 (01 -> 10ms)
+            fraction;       // 毫秒直接使用
+
+        return minutes * 60000 + seconds * 1000 + milliseconds;
+    }
+
     // 判断是否是逐字歌词行
     private isWordByWordLyric(line: string): boolean {
         const hasBracketTimestamp = /$$\d{2}:\d{2}\.\d{2,3}$$\S/.test(line);

+ 3 - 4
lib/src/main/ets/view/LyricView2.ets

@@ -291,23 +291,22 @@ export struct LyricView2 {
                 // const isHighlighted = this.currentMediaPosition >= word.startTime
 
                 Text(word.word)
-                    .fontSize(this.textSize)
+                    .fontSize(index == this.currentIndex ?this.textSize*1.2:this.textSize)
                     .fontColor(this.currentMediaPosition >= word.startTime ? this.textHighlightColor : this.textColor)
                     .fontWeight(this.currentMediaPosition >= word.startTime && this.isHighlightBold ? FontWeight.Bold : this.textWeight)
                     .margin(0)
-                    // .animation({ duration: 100, curve: Curve.Linear })
                     .animation({
                         // 动画播放速度
                         tempo: 0.8,
                         // 动画持续时间,单位是毫秒
                         duration: 777,
                         // 动画缓动函数
-                        curve: Curve.Linear
+                        curve: Curve.FastOutSlowIn
                     })
             })
         }
         .justifyContent(this.alignMode === 'center' ? FlexAlign.Center : FlexAlign.Start)
-        .width('100%')
+        .width(this.alignMode == 'center' ? '100%' : '76%')
     }
 
     private handleSeekAction() {