|
@@ -1241,8 +1241,10 @@ export struct LocalMusic {
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
this.eventHub.on('onStateChange', (fg: boolean) => {
|
|
this.eventHub.on('onStateChange', (fg: boolean) => {
|
|
|
|
|
+ this.isAppForeground = fg;
|
|
|
if (fg && this.curState === 'STARTED') {
|
|
if (fg && this.curState === 'STARTED') {
|
|
|
this.stopPip();
|
|
this.stopPip();
|
|
|
|
|
+ this.syncLyricPositionNow();
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
@@ -1381,6 +1383,8 @@ 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)
|
|
|
|
|
+ const volumeBoostPercent = PreferencesUtil.getNumberSync(this.volumeBoostPreferenceKey, 0)
|
|
|
|
|
+ this.volumeBoostPercent = Math.max(0, Math.min(volumeBoostPercent, this.volumeBoostMaxPercent))
|
|
|
this.isSwipe = PreferencesUtil.getBooleanSync(SettingPage.IS_SWIPE, true)
|
|
this.isSwipe = PreferencesUtil.getBooleanSync(SettingPage.IS_SWIPE, true)
|
|
|
this.openSkipSongAnimate = PreferencesUtil.getBooleanSync(SettingPage.OPEN_SKIPSONG_ANIMATE, true)
|
|
this.openSkipSongAnimate = PreferencesUtil.getBooleanSync(SettingPage.OPEN_SKIPSONG_ANIMATE, true)
|
|
|
this.isPlayPageSwipeNext = PreferencesUtil.getBooleanSync(SettingPage.IS_PLAYPAGE_SWIPE_NEXT, true)
|
|
this.isPlayPageSwipeNext = PreferencesUtil.getBooleanSync(SettingPage.IS_PLAYPAGE_SWIPE_NEXT, true)
|
|
@@ -2087,6 +2091,7 @@ export struct LocalMusic {
|
|
|
this.deletionWatcher?.stop();
|
|
this.deletionWatcher?.stop();
|
|
|
this.knockController?.immersiveDisableListening();
|
|
this.knockController?.immersiveDisableListening();
|
|
|
this.curIndex = 0
|
|
this.curIndex = 0
|
|
|
|
|
+ this.isAppForeground = false;
|
|
|
emitter.off(EventConstants.EVENT_AUDIO_OPEN);
|
|
emitter.off(EventConstants.EVENT_AUDIO_OPEN);
|
|
|
emitter.off(EventConstants.EVENT_SCAN_UPDATE);
|
|
emitter.off(EventConstants.EVENT_SCAN_UPDATE);
|
|
|
emitter.off(EventConstants.EVENT_SWIPE_BACK_UPDATE);
|
|
emitter.off(EventConstants.EVENT_SWIPE_BACK_UPDATE);
|
|
@@ -2111,6 +2116,7 @@ export struct LocalMusic {
|
|
|
this.getUIContext().getHostContext()!.eventHub.off('playNext');
|
|
this.getUIContext().getHostContext()!.eventHub.off('playNext');
|
|
|
this.getUIContext().getHostContext()!.eventHub.off('showPlayerView');
|
|
this.getUIContext().getHostContext()!.eventHub.off('showPlayerView');
|
|
|
this.getUIContext().getHostContext()!.eventHub.off('openPlayList');
|
|
this.getUIContext().getHostContext()!.eventHub.off('openPlayList');
|
|
|
|
|
+ this.eventHub.off('onStateChange');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async getSortedFiles(curPath: string, isWorkerPost?: boolean, destPath?: string, isOpen?: boolean,
|
|
async getSortedFiles(curPath: string, isWorkerPost?: boolean, destPath?: string, isOpen?: boolean,
|
|
@@ -3053,6 +3059,10 @@ export struct LocalMusic {
|
|
|
item.pixelMapPath = imagePath
|
|
item.pixelMapPath = imagePath
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if (isRemoteCloudType(item.type)) {
|
|
|
|
|
+ return FileUtil.getFilePath(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()
|
|
@@ -8710,6 +8720,10 @@ export struct LocalMusic {
|
|
|
//内嵌音乐标签不能开启监听
|
|
//内嵌音乐标签不能开启监听
|
|
|
// this.deletionWatcher?.stop();
|
|
// this.deletionWatcher?.stop();
|
|
|
this.loadingDialogId = DialogHelper.showLoadingDialog()
|
|
this.loadingDialogId = DialogHelper.showLoadingDialog()
|
|
|
|
|
+ if (isRemoteCloudType(item.type)) {
|
|
|
|
|
+ await this.saveRemoteSongEdit(item);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
await PermissionUtil.activatePermission(item.filePath)
|
|
await PermissionUtil.activatePermission(item.filePath)
|
|
|
let tempOutPath = ''
|
|
let tempOutPath = ''
|
|
|
// 如果不是 packName 包下的文件,则直接路径用this.currentPath
|
|
// 如果不是 packName 包下的文件,则直接路径用this.currentPath
|
|
@@ -8761,7 +8775,7 @@ export struct LocalMusic {
|
|
|
true,
|
|
true,
|
|
|
tempOutPath
|
|
tempOutPath
|
|
|
);
|
|
);
|
|
|
-
|
|
|
|
|
|
|
+ // ToastUtil.showToast('result ='+ result)
|
|
|
if (result||isWav) {
|
|
if (result||isWav) {
|
|
|
if(!item.filePath.includes(this.packName)){
|
|
if(!item.filePath.includes(this.packName)){
|
|
|
//内嵌成功的歌路径如果不是包含包名,则要入库
|
|
//内嵌成功的歌路径如果不是包含包名,则要入库
|
|
@@ -8846,6 +8860,82 @@ export struct LocalMusic {
|
|
|
this.isShowEdit = false
|
|
this.isShowEdit = false
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private normalizeEditedCoverPath(path: string): string {
|
|
|
|
|
+ if (StrUtil.isEmpty(path)) {
|
|
|
|
|
+ return '';
|
|
|
|
|
+ }
|
|
|
|
|
+ const lowerPath = path.toLowerCase();
|
|
|
|
|
+ if (lowerPath.startsWith('http://') || lowerPath.startsWith('https://')
|
|
|
|
|
+ || lowerPath.startsWith('file://') || lowerPath.startsWith('data:')) {
|
|
|
|
|
+ return path;
|
|
|
|
|
+ }
|
|
|
|
|
+ return fileUri.getUriFromPath(path);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private emitRemoteMetadataUpdated(item: VideoItem): void {
|
|
|
|
|
+ if (!item || StrUtil.isEmpty(item.filePath)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const payload: WebDavMetadataUpdatePayload = {
|
|
|
|
|
+ filePath: item.filePath,
|
|
|
|
|
+ pixelMapPath: item.pixelMapPath,
|
|
|
|
|
+ name: item.name,
|
|
|
|
|
+ artist: item.artist
|
|
|
|
|
+ };
|
|
|
|
|
+ const eventUpdate: emitter.InnerEvent = { eventId: EventConstants.EVENT_WEBDAV_METADATA_UPDATED };
|
|
|
|
|
+ emitter.emit(eventUpdate, { data: [payload] });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private syncRemoteManagerSong(item: VideoItem): void {
|
|
|
|
|
+ if (!item || StrUtil.isEmpty(item.filePath)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const remoteManager = RemoteDriveManager.getInstance();
|
|
|
|
|
+ const target = Utility.getItemByFilePath(remoteManager.webDavSongs, item.filePath);
|
|
|
|
|
+ if (!target) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.mergeVideoItemFromSource(target, item);
|
|
|
|
|
+ if (StrUtil.isNotEmpty(item.pixelMapPath)) {
|
|
|
|
|
+ target.pixelMapPath = item.pixelMapPath;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private async saveRemoteSongEdit(item: VideoItem): Promise<void> {
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.syncEditedFields(item);
|
|
|
|
|
+ if (StrUtil.isNotEmpty(this.imagePathStr)) {
|
|
|
|
|
+ item.pixelMapPath = this.normalizeEditedCoverPath(this.imagePathStr);
|
|
|
|
|
+ }
|
|
|
|
|
+ this.name = this.titleStr;
|
|
|
|
|
+ if (this.currentSong && item.filePath === this.currentSong.filePath) {
|
|
|
|
|
+ this.syncEditedFields(this.currentSong);
|
|
|
|
|
+ if (StrUtil.isNotEmpty(item.pixelMapPath)) {
|
|
|
|
|
+ this.currentSong.pixelMapPath = item.pixelMapPath;
|
|
|
|
|
+ this.cover = item.pixelMapPath;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.artist = this.currentSong.artist ?? '';
|
|
|
|
|
+ this.currentSong = cloneVideoItem(this.currentSong);
|
|
|
|
|
+ AppStorage.setOrCreate('currentSong', this.currentSong);
|
|
|
|
|
+ }
|
|
|
|
|
+ this.syncListsAfterEdit(item.filePath);
|
|
|
|
|
+ this.syncRemoteManagerSong(item);
|
|
|
|
|
+ await this.persistRemoteMetadataToDb(item);
|
|
|
|
|
+ this.emitRemoteMetadataUpdated(item);
|
|
|
|
|
+ ToastUtil.showToast(StrUtil.isNotEmpty(item.pixelMapPath) ? '网盘歌曲信息已保存' : '网盘歌曲标签已保存');
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ Logger.error(TAG, `saveRemoteSongEdit 失败: ${(error as Error).message}`);
|
|
|
|
|
+ ToastUtil.showToast('网盘歌曲编辑失败');
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ DialogHelper.closeDialog(this.loadingDialogId);
|
|
|
|
|
+ this.longItemFilePath = '';
|
|
|
|
|
+ this.tempLyricContent = '';
|
|
|
|
|
+ this.isShowEdit = false;
|
|
|
|
|
+ this.imagePathStr = '';
|
|
|
|
|
+ this.isShowMoreView = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 内嵌标签更新当前内嵌的Item
|
|
// 内嵌标签更新当前内嵌的Item
|
|
|
async doUpateEditedFields(item: VideoItem) {
|
|
async doUpateEditedFields(item: VideoItem) {
|
|
|
// 找到当前项在 videoLocalList 中的索引
|
|
// 找到当前项在 videoLocalList 中的索引
|
|
@@ -10609,6 +10699,7 @@ export struct LocalMusic {
|
|
|
.width('100%')
|
|
.width('100%')
|
|
|
.onDisAppear(() => {
|
|
.onDisAppear(() => {
|
|
|
this.translateY = 0;
|
|
this.translateY = 0;
|
|
|
|
|
+ this.playDragUiOpacity = 1
|
|
|
})
|
|
})
|
|
|
.translate({ y: this.translateY })
|
|
.translate({ y: this.translateY })
|
|
|
.gesture(
|
|
.gesture(
|
|
@@ -10622,6 +10713,9 @@ export struct LocalMusic {
|
|
|
if (isSwipeUp) {
|
|
if (isSwipeUp) {
|
|
|
// 向上滑动:不应用位移动画,保持 translateY = 0
|
|
// 向上滑动:不应用位移动画,保持 translateY = 0
|
|
|
this.translateY = 0;
|
|
this.translateY = 0;
|
|
|
|
|
+ this.scaleValueImage = 1
|
|
|
|
|
+ this.scaleValueText = 1
|
|
|
|
|
+ this.playDragUiOpacity = 1
|
|
|
} else {
|
|
} else {
|
|
|
if(this.isGeKongPlayNext){
|
|
if(this.isGeKongPlayNext){
|
|
|
return
|
|
return
|
|
@@ -10634,9 +10728,10 @@ export struct LocalMusic {
|
|
|
duration: 500,
|
|
duration: 500,
|
|
|
curve: Curve.Sharp
|
|
curve: Curve.Sharp
|
|
|
}, () => {
|
|
}, () => {
|
|
|
- this.scaleValueImage = Math.min(1, Math.max(0.38, 1 - this.translateY / 500));
|
|
|
|
|
|
|
+ this.scaleValueImage = Math.min(1, Math.max(0.74, 1 - this.translateY / 880));
|
|
|
console.info('onecold scaleValueImage:', this.scaleValueImage)
|
|
console.info('onecold scaleValueImage:', this.scaleValueImage)
|
|
|
- this.scaleValueText = Math.min(1, Math.max(0.6, 1 - this.translateY / 700));
|
|
|
|
|
|
|
+ this.scaleValueText = Math.min(1, Math.max(0.88, 1 - this.translateY / 2000));
|
|
|
|
|
+ this.playDragUiOpacity = Math.min(1, Math.max(0, 1 - this.translateY / 300));
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -10678,6 +10773,7 @@ export struct LocalMusic {
|
|
|
this.isShowPlay = false;
|
|
this.isShowPlay = false;
|
|
|
this.scaleValueImage = 1
|
|
this.scaleValueImage = 1
|
|
|
this.scaleValueText = 1
|
|
this.scaleValueText = 1
|
|
|
|
|
+ this.playDragUiOpacity = 1
|
|
|
this.translateY = 0;
|
|
this.translateY = 0;
|
|
|
})
|
|
})
|
|
|
} else {
|
|
} else {
|
|
@@ -10688,6 +10784,7 @@ export struct LocalMusic {
|
|
|
}, () => {
|
|
}, () => {
|
|
|
this.scaleValueImage = 1
|
|
this.scaleValueImage = 1
|
|
|
this.scaleValueText = 1
|
|
this.scaleValueText = 1
|
|
|
|
|
+ this.playDragUiOpacity = 1
|
|
|
this.translateY = 0;
|
|
this.translateY = 0;
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
@@ -10908,6 +11005,7 @@ export struct LocalMusic {
|
|
|
Column() {
|
|
Column() {
|
|
|
this.PlayTitle()
|
|
this.PlayTitle()
|
|
|
}
|
|
}
|
|
|
|
|
+ .opacity(this.playDragUiOpacity)
|
|
|
.position({ x: 0, y: this.isCoverOpacity() ? 15 : 40 })
|
|
.position({ x: 0, y: this.isCoverOpacity() ? 15 : 40 })
|
|
|
.width('100%')
|
|
.width('100%')
|
|
|
.margin({ top: 0 })
|
|
.margin({ top: 0 })
|
|
@@ -11069,10 +11167,13 @@ export struct LocalMusic {
|
|
|
private positionY: number = PlayConstants.POSITION_Y;
|
|
private positionY: number = PlayConstants.POSITION_Y;
|
|
|
private windowClass: window.Window = globalThis.windowClass
|
|
private windowClass: window.Window = globalThis.windowClass
|
|
|
@State volume: number = 0.8;
|
|
@State volume: number = 0.8;
|
|
|
|
|
+ @State volumeBoostPercent: number = 0
|
|
|
@State volumeSmall: boolean = false
|
|
@State volumeSmall: boolean = false
|
|
|
@State autoParseMusicName: boolean = true
|
|
@State autoParseMusicName: boolean = true
|
|
|
@State isShowFileName: boolean = false
|
|
@State isShowFileName: boolean = false
|
|
|
@State isLongNameRoLL: boolean = true//长歌名滚动
|
|
@State isLongNameRoLL: boolean = true//长歌名滚动
|
|
|
|
|
+ private readonly volumeBoostPreferenceKey: string = 'volumeBoostPercent';
|
|
|
|
|
+ private readonly volumeBoostMaxPercent: number = 500;
|
|
|
|
|
|
|
|
@State volumeShow: boolean = PlayConstants.VOLUME_SHOW;
|
|
@State volumeShow: boolean = PlayConstants.VOLUME_SHOW;
|
|
|
@State bright: number = PlayConstants.BRIGHT;
|
|
@State bright: number = PlayConstants.BRIGHT;
|
|
@@ -11086,6 +11187,9 @@ export struct LocalMusic {
|
|
|
@State isVideoLock: boolean = false; // Whether the video playback is locked
|
|
@State isVideoLock: boolean = false; // Whether the video playback is locked
|
|
|
private preloadNextRandomIndex: number = -1; // 预缓存的随机下一首索引
|
|
private preloadNextRandomIndex: number = -1; // 预缓存的随机下一首索引
|
|
|
eventHub = getContext().eventHub;
|
|
eventHub = getContext().eventHub;
|
|
|
|
|
+ private isAppForeground: boolean = true;
|
|
|
|
|
+ private lastBufferingLogTs: number = 0;
|
|
|
|
|
+ private readonly bufferingLogIntervalMs: number = 5000;
|
|
|
//投播组件
|
|
//投播组件
|
|
|
private avSessionController: AvSessionController = AvSessionController.getInstance(false);
|
|
private avSessionController: AvSessionController = AvSessionController.getInstance(false);
|
|
|
// 新的投播控制器(封装了官方示例的逻辑)
|
|
// 新的投播控制器(封装了官方示例的逻辑)
|
|
@@ -11903,6 +12007,7 @@ export struct LocalMusic {
|
|
|
.height(82)
|
|
.height(82)
|
|
|
.margin({ bottom: 20 })
|
|
.margin({ bottom: 20 })
|
|
|
}
|
|
}
|
|
|
|
|
+ .opacity(this.playDragUiOpacity)
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -11924,6 +12029,7 @@ export struct LocalMusic {
|
|
|
}
|
|
}
|
|
|
.justifyContent(FlexAlign.Center)
|
|
.justifyContent(FlexAlign.Center)
|
|
|
.alignItems(HorizontalAlign.Center)
|
|
.alignItems(HorizontalAlign.Center)
|
|
|
|
|
+ .opacity(this.playDragUiOpacity)
|
|
|
.position({ bottom: this.isHiCarSmall()?50:(this.isCoverOpacity() ? 55 : 70) }) // 将 固定在底部
|
|
.position({ bottom: this.isHiCarSmall()?50:(this.isCoverOpacity() ? 55 : 70) }) // 将 固定在底部
|
|
|
|
|
|
|
|
}
|
|
}
|
|
@@ -12454,6 +12560,7 @@ export struct LocalMusic {
|
|
|
// 添加控制缩放的状态变量
|
|
// 添加控制缩放的状态变量
|
|
|
@State scaleValueImage: number = 1
|
|
@State scaleValueImage: number = 1
|
|
|
@State scaleValueText: number = 1
|
|
@State scaleValueText: number = 1
|
|
|
|
|
+ @State playDragUiOpacity: number = 1
|
|
|
@Builder
|
|
@Builder
|
|
|
CoverInfo() {
|
|
CoverInfo() {
|
|
|
Column() {
|
|
Column() {
|
|
@@ -12752,6 +12859,7 @@ export struct LocalMusic {
|
|
|
}
|
|
}
|
|
|
.width(this.isLandscape?'83%':'90%')
|
|
.width(this.isLandscape?'83%':'90%')
|
|
|
.scale({ x: this.scaleValueText, y: this.scaleValueText }) // 添加缩放效果
|
|
.scale({ x: this.scaleValueText, y: this.scaleValueText }) // 添加缩放效果
|
|
|
|
|
+ .opacity(this.playDragUiOpacity)
|
|
|
.margin({ top:this.isCoverRectangle?10: 0 })
|
|
.margin({ top:this.isCoverRectangle?10: 0 })
|
|
|
.visibility(this.isCoverOpacity() || isHidden ? Visibility.None : Visibility.Visible)
|
|
.visibility(this.isCoverOpacity() || isHidden ? Visibility.None : Visibility.Visible)
|
|
|
}
|
|
}
|
|
@@ -14028,10 +14136,39 @@ export struct LocalMusic {
|
|
|
.visibility(more.id === 9 ? Visibility.Visible : Visibility.None)
|
|
.visibility(more.id === 9 ? Visibility.Visible : Visibility.None)
|
|
|
.onChange((value: number) => {
|
|
.onChange((value: number) => {
|
|
|
this.volume = value;
|
|
this.volume = value;
|
|
|
- this.mIjkMediaPlayer.setVolume(this.volume.toString(), this.volume.toString());
|
|
|
|
|
|
|
+ this.applyCurrentVolume();
|
|
|
PreferencesUtil.putSync('DefalutVolume',this.volume)
|
|
PreferencesUtil.putSync('DefalutVolume',this.volume)
|
|
|
})
|
|
})
|
|
|
.layoutWeight(1)
|
|
.layoutWeight(1)
|
|
|
|
|
+ Slider({
|
|
|
|
|
+ value: this.volumeBoostPercent,
|
|
|
|
|
+ min: 0,
|
|
|
|
|
+ max: this.volumeBoostMaxPercent,
|
|
|
|
|
+ step: 1,
|
|
|
|
|
+ style: SliderStyle.OutSet
|
|
|
|
|
+ })
|
|
|
|
|
+ .margin({ left: 18, right: 8 })
|
|
|
|
|
+ .blockColor(this.themeColor)
|
|
|
|
|
+ .trackColor($r('app.color.speed_text_color'))
|
|
|
|
|
+ .selectedColor(Color.White)
|
|
|
|
|
+ .trackThickness(9)
|
|
|
|
|
+ .visibility(more.id === 28 ? Visibility.Visible : Visibility.None)
|
|
|
|
|
+ .onChange((value: number) => {
|
|
|
|
|
+ this.volumeBoostPercent = Math.round(value);
|
|
|
|
|
+ this.applyCurrentVolume();
|
|
|
|
|
+ PreferencesUtil.putSync(this.volumeBoostPreferenceKey, this.volumeBoostPercent);
|
|
|
|
|
+ })
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ Text(`${Math.round(this.volumeBoostPercent)}%`)
|
|
|
|
|
+ .fontSize(13)
|
|
|
|
|
+ .fontColor(Color.White)
|
|
|
|
|
+ .margin({ left: 8, right: 8 })
|
|
|
|
|
+ .visibility(more.id === 28 ? Visibility.Visible : Visibility.None)
|
|
|
|
|
+ Text(`${Math.round(this.volume*100)}%`)
|
|
|
|
|
+ .fontSize(13)
|
|
|
|
|
+ .fontColor(Color.White)
|
|
|
|
|
+ .margin({ left: 8, right: 8 })
|
|
|
|
|
+ .visibility(more.id === 9 ? Visibility.Visible : Visibility.None)
|
|
|
Blank()
|
|
Blank()
|
|
|
Toggle({ type: ToggleType.Switch, isOn: this.isMusicMemoryPlay })
|
|
Toggle({ type: ToggleType.Switch, isOn: this.isMusicMemoryPlay })
|
|
|
.selectedColor(this.themeColor)
|
|
.selectedColor(this.themeColor)
|
|
@@ -14301,7 +14438,8 @@ export struct LocalMusic {
|
|
|
|
|
|
|
|
{ id: 4, image: $r('app.media.share2'), title: '分享' },
|
|
{ id: 4, image: $r('app.media.share2'), title: '分享' },
|
|
|
|
|
|
|
|
- { id: 9, image: $r('app.media.volume_white'), title: '调整音量' },
|
|
|
|
|
|
|
+ { id: 9, image: $r('app.media.volume_white'), title: '音量减弱' },
|
|
|
|
|
+ { id: 28, image: $r('sys.symbol.speaker_wave_3'), title: '音量增强' },
|
|
|
|
|
|
|
|
{ id: 25, image: $r('sys.symbol.ranking'), title: '音乐频谱' },
|
|
{ id: 25, image: $r('sys.symbol.ranking'), title: '音乐频谱' },
|
|
|
{ id: 26, image: $r('sys.symbol.eight_diagram'), title: '频谱类型' },
|
|
{ id: 26, image: $r('sys.symbol.eight_diagram'), title: '频谱类型' },
|
|
@@ -14338,6 +14476,29 @@ export struct LocalMusic {
|
|
|
@State isShowSpectrum: boolean = false//是否显示频谱
|
|
@State isShowSpectrum: boolean = false//是否显示频谱
|
|
|
@State spectrumModeIndex: number = 0 //选中的频谱特效索引
|
|
@State spectrumModeIndex: number = 0 //选中的频谱特效索引
|
|
|
|
|
|
|
|
|
|
+ private getVolumeBoostFactor(): number {
|
|
|
|
|
+ return 1 + this.volumeBoostPercent / 100;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private getCurrentOutputVolume(): number {
|
|
|
|
|
+ const baseVolume = Math.max(0, Math.min(this.volume, 1));
|
|
|
|
|
+ const boostedVolume = baseVolume * this.getVolumeBoostFactor();
|
|
|
|
|
+ const maxOutputVolume = 1 + this.volumeBoostMaxPercent / 100;
|
|
|
|
|
+ return Math.max(0, Math.min(boostedVolume, maxOutputVolume));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private applyCurrentVolume(forceDsfSafeVolume: boolean = false): void {
|
|
|
|
|
+ if (!this.mIjkMediaPlayer) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (forceDsfSafeVolume && this.volumeSmall && this.videoUrl.toLowerCase().endsWith('.dsf')) {
|
|
|
|
|
+ this.mIjkMediaPlayer.setVolume('1', '1');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const outputVolume = this.getCurrentOutputVolume();
|
|
|
|
|
+ this.mIjkMediaPlayer.setVolume(outputVolume.toString(), outputVolume.toString());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
async setRingTone() {
|
|
async setRingTone() {
|
|
|
if (StrUtil.isEmpty(this.videoUrl) || StrUtil.isEmpty(this.name)) {
|
|
if (StrUtil.isEmpty(this.videoUrl) || StrUtil.isEmpty(this.name)) {
|
|
|
return
|
|
return
|
|
@@ -14757,16 +14918,27 @@ export struct LocalMusic {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Update pixel map path but always return res regardless of success
|
|
|
|
|
- this.table.updatePixelMapPath(item.filePath, res, (success: boolean, error?: string) => {
|
|
|
|
|
- if (success) {
|
|
|
|
|
- this.doUpdateData();
|
|
|
|
|
- console.log("onecold 更新音乐封面成功,数据库已同步");
|
|
|
|
|
- } else {
|
|
|
|
|
- console.error("onecold 更新音乐封面数据库失败原因: " + error);
|
|
|
|
|
|
|
+ if (isRemoteCloudType(item.type)) {
|
|
|
|
|
+ item.pixelMapPath = res;
|
|
|
|
|
+ this.syncRemoteManagerSong(item);
|
|
|
|
|
+ try {
|
|
|
|
|
+ await this.persistRemoteMetadataToDb(item);
|
|
|
|
|
+ this.emitRemoteMetadataUpdated(item);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ Logger.error(TAG, `searchCover 持久化网盘封面失败: ${(error as Error).message}`);
|
|
|
}
|
|
}
|
|
|
- // Note: We don't resolve/reject here because we already returned res
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Update pixel map path but always return res regardless of success
|
|
|
|
|
+ this.table.updatePixelMapPath(item.filePath, res, (success: boolean, error?: string) => {
|
|
|
|
|
+ if (success) {
|
|
|
|
|
+ this.doUpdateData();
|
|
|
|
|
+ console.log("onecold 更新音乐封面成功,数据库已同步");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ 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);
|
|
|
}
|
|
}
|
|
@@ -15187,6 +15359,9 @@ export struct LocalMusic {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private updateLyricPosition(lyricPosition: number): void {
|
|
private updateLyricPosition(lyricPosition: number): void {
|
|
|
|
|
+ if (!this.isAppForeground || !this.isPageVisible) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
const shouldUpdateMainLyric = this.shouldUpdateMainLyricPanel()
|
|
const shouldUpdateMainLyric = this.shouldUpdateMainLyricPanel()
|
|
|
const shouldUpdatePipLyric = this.showPipLyric
|
|
const shouldUpdatePipLyric = this.showPipLyric
|
|
|
const shouldUpdateSingleLineLyric = this.isShowSingleLineLyric
|
|
const shouldUpdateSingleLineLyric = this.isShowSingleLineLyric
|
|
@@ -15213,6 +15388,7 @@ export struct LocalMusic {
|
|
|
if (this.castControllerWrapper && this.isCastPlaying) {
|
|
if (this.castControllerWrapper && this.isCastPlaying) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
+ const shouldRefreshUi = this.isAppForeground && this.isPageVisible;
|
|
|
let position = this.mIjkMediaPlayer.getCurrentPosition();
|
|
let position = this.mIjkMediaPlayer.getCurrentPosition();
|
|
|
let duration = this.mIjkMediaPlayer.getDuration();
|
|
let duration = this.mIjkMediaPlayer.getDuration();
|
|
|
if (duration <= 0 && this.duration > 0) {
|
|
if (duration <= 0 && this.duration > 0) {
|
|
@@ -15223,41 +15399,47 @@ export struct LocalMusic {
|
|
|
this.slideEnable = true;
|
|
this.slideEnable = true;
|
|
|
let curPercent = position / duration;
|
|
let curPercent = position / duration;
|
|
|
pos = curPercent * 100;
|
|
pos = curPercent * 100;
|
|
|
- if (pos > this.PROGRESS_MAX_VALUE) {
|
|
|
|
|
- this.progressValue = this.PROGRESS_MAX_VALUE
|
|
|
|
|
- } else {
|
|
|
|
|
- this.progressValue = pos;
|
|
|
|
|
|
|
+ if (shouldRefreshUi) {
|
|
|
|
|
+ if (pos > this.PROGRESS_MAX_VALUE) {
|
|
|
|
|
+ this.progressValue = this.PROGRESS_MAX_VALUE
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.progressValue = pos;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
// LogUtils.getInstance()
|
|
// LogUtils.getInstance()
|
|
|
// .LOGI("setProgress position:" + position + ",duration:" + duration + ",progressValue:" + pos);
|
|
// .LOGI("setProgress position:" + position + ",duration:" + duration + ",progressValue:" + pos);
|
|
|
- this.totalTime = this.stringForTime(duration);
|
|
|
|
|
|
|
+ if (shouldRefreshUi) {
|
|
|
|
|
+ this.totalTime = this.stringForTime(duration);
|
|
|
|
|
+ }
|
|
|
if (position > duration) {
|
|
if (position > duration) {
|
|
|
position = duration;
|
|
position = duration;
|
|
|
}
|
|
}
|
|
|
- const lyricPosition = position + this.timeOffset * 1000;
|
|
|
|
|
- this.isCurrentTime = true;
|
|
|
|
|
- this.updateLyricPosition(lyricPosition);
|
|
|
|
|
-
|
|
|
|
|
- // 蓝牙歌词更新逻辑
|
|
|
|
|
- if (this.bluetoothLyricEnabled && this.isBluetoothConnected && this.lyricContent && this.currentSong) {
|
|
|
|
|
- const currentLyricLine = LyricUtil.getCurrentLyricLine(this.lyricContent, lyricPosition);
|
|
|
|
|
- // 仅在歌词行变化时更新,避免频繁调用
|
|
|
|
|
- if (currentLyricLine !== this.lastBluetoothLyricLine) {
|
|
|
|
|
- this.lastBluetoothLyricLine = currentLyricLine;
|
|
|
|
|
- this.avSessionController.updateBluetoothLyric(
|
|
|
|
|
- currentLyricLine,
|
|
|
|
|
- this.currentSong.name || '',
|
|
|
|
|
- this.currentSong.artist || '',
|
|
|
|
|
- this.bluetoothLyricMode
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ if (shouldRefreshUi) {
|
|
|
|
|
+ const lyricPosition = position + this.timeOffset * 1000;
|
|
|
|
|
+ this.isCurrentTime = true;
|
|
|
|
|
+ this.updateLyricPosition(lyricPosition);
|
|
|
|
|
+
|
|
|
|
|
+ // 蓝牙歌词更新逻辑
|
|
|
|
|
+ if (this.bluetoothLyricEnabled && this.isBluetoothConnected && this.lyricContent && this.currentSong) {
|
|
|
|
|
+ const currentLyricLine = LyricUtil.getCurrentLyricLine(this.lyricContent, lyricPosition);
|
|
|
|
|
+ // 仅在歌词行变化时更新,避免频繁调用
|
|
|
|
|
+ if (currentLyricLine !== this.lastBluetoothLyricLine) {
|
|
|
|
|
+ this.lastBluetoothLyricLine = currentLyricLine;
|
|
|
|
|
+ this.avSessionController.updateBluetoothLyric(
|
|
|
|
|
+ currentLyricLine,
|
|
|
|
|
+ this.currentSong.name || '',
|
|
|
|
|
+ this.currentSong.artist || '',
|
|
|
|
|
+ this.bluetoothLyricMode
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- this.currentTime = this.stringForTime(position);
|
|
|
|
|
- this.isCurrentTime = false
|
|
|
|
|
|
|
+ this.currentTime = this.stringForTime(position);
|
|
|
|
|
+ this.isCurrentTime = false
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
|
|
|
if (this.mIjkMediaPlayer.isPlaying()) {
|
|
if (this.mIjkMediaPlayer.isPlaying()) {
|
|
@@ -15316,6 +15498,10 @@ export struct LocalMusic {
|
|
|
|
|
|
|
|
private startProgressTask() {
|
|
private startProgressTask() {
|
|
|
let that = this;
|
|
let that = this;
|
|
|
|
|
+ if (this.updateProgressTimer) {
|
|
|
|
|
+ clearInterval(this.updateProgressTimer);
|
|
|
|
|
+ this.updateProgressTimer = 0;
|
|
|
|
|
+ }
|
|
|
this.updateProgressTimer = setInterval(() => {
|
|
this.updateProgressTimer = setInterval(() => {
|
|
|
//LogUtils.getInstance().LOGI("startProgressTask");
|
|
//LogUtils.getInstance().LOGI("startProgressTask");
|
|
|
if (!that.mDestroyPage) {
|
|
if (!that.mDestroyPage) {
|
|
@@ -15327,6 +15513,7 @@ export struct LocalMusic {
|
|
|
private stopProgressTask() {
|
|
private stopProgressTask() {
|
|
|
LogUtils.getInstance().LOGI("stopProgressTask");
|
|
LogUtils.getInstance().LOGI("stopProgressTask");
|
|
|
clearInterval(this.updateProgressTimer);
|
|
clearInterval(this.updateProgressTimer);
|
|
|
|
|
+ this.updateProgressTimer = 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private showLoadIng() {
|
|
private showLoadIng() {
|
|
@@ -15443,15 +15630,7 @@ export struct LocalMusic {
|
|
|
//初始化配置
|
|
//初始化配置
|
|
|
this.mIjkMediaPlayer.native_setup();
|
|
this.mIjkMediaPlayer.native_setup();
|
|
|
// 初始化配置后需要重新设置音频流音量,否则音量为默认值1.0 针对dsf格式设置最大值1.0
|
|
// 初始化配置后需要重新设置音频流音量,否则音量为默认值1.0 针对dsf格式设置最大值1.0
|
|
|
- if(this.volumeSmall){
|
|
|
|
|
- if(this.videoUrl.toLowerCase().endsWith('.dsf')){
|
|
|
|
|
- this.mIjkMediaPlayer.setVolume('1', '1');
|
|
|
|
|
- }else{
|
|
|
|
|
- this.mIjkMediaPlayer.setVolume(this.volume.toString(), this.volume.toString());
|
|
|
|
|
- }
|
|
|
|
|
- }else{
|
|
|
|
|
- this.mIjkMediaPlayer.setVolume(this.volume.toString(), this.volume.toString());
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ this.applyCurrentVolume(true);
|
|
|
|
|
|
|
|
// 构建规范的HTTP请求头(统一一次性设置,避免未带认证提前发起连接)
|
|
// 构建规范的HTTP请求头(统一一次性设置,避免未带认证提前发起连接)
|
|
|
const headers = new Map<string, string>();
|
|
const headers = new Map<string, string>();
|
|
@@ -15920,27 +16099,13 @@ export struct LocalMusic {
|
|
|
|
|
|
|
|
let mOnBufferingUpdateListener: OnBufferingUpdateListener = new ImplOnBufferingUpdateListener(
|
|
let mOnBufferingUpdateListener: OnBufferingUpdateListener = new ImplOnBufferingUpdateListener(
|
|
|
(percent: number) => {
|
|
(percent: number) => {
|
|
|
-
|
|
|
|
|
- LogUtils.getInstance().LOGI("OnBufferingUpdateListener-->go:" + percent);
|
|
|
|
|
- let MediaInfo = this.mIjkMediaPlayer.getMediaInfo()
|
|
|
|
|
- LogUtils.getInstance().LOGI('getMediaInfo---' + MediaInfo);
|
|
|
|
|
- let VideoWidth = this.mIjkMediaPlayer.getVideoWidth()
|
|
|
|
|
- LogUtils.getInstance().LOGI('getVideoWidth---' + VideoWidth);
|
|
|
|
|
-
|
|
|
|
|
- let VideoHeight = this.mIjkMediaPlayer.getVideoHeight()
|
|
|
|
|
- LogUtils.getInstance().LOGI('getVideoHeight---' + VideoHeight);
|
|
|
|
|
-
|
|
|
|
|
- let VideoSarNum = this.mIjkMediaPlayer.getVideoSarNum()
|
|
|
|
|
- LogUtils.getInstance().LOGI('getVideoSarNum---' + VideoSarNum);
|
|
|
|
|
-
|
|
|
|
|
- let VideoSarDen = this.mIjkMediaPlayer.getVideoSarDen()
|
|
|
|
|
- LogUtils.getInstance().LOGI('getVideoSarDen---' + VideoSarDen);
|
|
|
|
|
-
|
|
|
|
|
- let AudioSessionId = this.mIjkMediaPlayer.getAudioSessionId()
|
|
|
|
|
- LogUtils.getInstance().LOGI('getAudioSessionId---' + AudioSessionId);
|
|
|
|
|
-
|
|
|
|
|
- let Looping = this.mIjkMediaPlayer.isLooping()
|
|
|
|
|
- LogUtils.getInstance().LOGI('isLooping---' + Looping);
|
|
|
|
|
|
|
+ if (this.isDebug) {
|
|
|
|
|
+ const now = Date.now();
|
|
|
|
|
+ if (now - this.lastBufferingLogTs > this.bufferingLogIntervalMs) {
|
|
|
|
|
+ this.lastBufferingLogTs = now;
|
|
|
|
|
+ Logger.info(TAG, `OnBufferingUpdateListener percent=${percent}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
);
|
|
);
|
|
|
this.mIjkMediaPlayer.setOnBufferingUpdateListener(mOnBufferingUpdateListener);
|
|
this.mIjkMediaPlayer.setOnBufferingUpdateListener(mOnBufferingUpdateListener);
|
|
@@ -17030,8 +17195,12 @@ export struct LocalMusic {
|
|
|
} else if (seekPos < 0) {
|
|
} else if (seekPos < 0) {
|
|
|
seekPos = 0;
|
|
seekPos = 0;
|
|
|
}
|
|
}
|
|
|
|
|
+ if (isRemoteSong && duration <= 0 && seekPos > 0) {
|
|
|
|
|
+ Logger.warn(TAG, `seekTo 跳过未知时长远程seek: ${seekPos}ms`);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
if (isRemoteSong && duration <= 0) {
|
|
if (isRemoteSong && duration <= 0) {
|
|
|
- Logger.info(TAG, `seekTo 远程歌曲时长未知,直接尝试seek: ${seekPos}ms`);
|
|
|
|
|
|
|
+ Logger.info(TAG, `seekTo 远程歌曲时长未知,仅允许起点seek: ${seekPos}ms`);
|
|
|
}
|
|
}
|
|
|
this.isSeekTo = true
|
|
this.isSeekTo = true
|
|
|
try {
|
|
try {
|