|
@@ -14,8 +14,12 @@ import {
|
|
|
getNavidromeTotalCount,
|
|
getNavidromeTotalCount,
|
|
|
setNavidromeTotalCount
|
|
setNavidromeTotalCount
|
|
|
} from '../common/util/NavidromePlaylistStore';
|
|
} from '../common/util/NavidromePlaylistStore';
|
|
|
-import { getFindVideoItems, getFindCurrentPlayIndex } from '../common/util/FindPlaylistStore';
|
|
|
|
|
|
|
+import { FindPlaylistMeta, getFindVideoItems, getFindCurrentPlayIndex, getFindPlaylistMeta } from '../common/util/FindPlaylistStore';
|
|
|
import { triggerLoadMoreSongs, hasMoreData } from '../common/util/NavidromeRandomLoader';
|
|
import { triggerLoadMoreSongs, hasMoreData } from '../common/util/NavidromeRandomLoader';
|
|
|
|
|
+import {
|
|
|
|
|
+ DEFAULT_LOCAL_RANDOM_QUEUE_PAGE_SIZE,
|
|
|
|
|
+ isFindLocalRandomPagedPlaylist
|
|
|
|
|
+} from '../common/util/LocalRandomPagedQueueHelper';
|
|
|
import {
|
|
import {
|
|
|
ImplOnBufferingUpdateListener,
|
|
ImplOnBufferingUpdateListener,
|
|
|
ImplOnCompletionListener,
|
|
ImplOnCompletionListener,
|
|
@@ -51,6 +55,7 @@ import { CommonConstants } from '../common/constants/CommonConstants';
|
|
|
import { BaiduConstants } from '../common/constants/BaiduConstants';
|
|
import { BaiduConstants } from '../common/constants/BaiduConstants';
|
|
|
import { EventConstants } from '../common/constants/EventConstants';
|
|
import { EventConstants } from '../common/constants/EventConstants';
|
|
|
import Logger from '../common/util/Logger';
|
|
import Logger from '../common/util/Logger';
|
|
|
|
|
+import { filterPlaylistSongsByKeyword, findSongIndexByFilePath } from '../common/util/PlaylistSearchHelper';
|
|
|
import { http } from '@kit.NetworkKit';
|
|
import { http } from '@kit.NetworkKit';
|
|
|
import { photoAccessHelper } from '@kit.MediaLibraryKit';
|
|
import { photoAccessHelper } from '@kit.MediaLibraryKit';
|
|
|
import { BusinessError, emitter } from '@kit.BasicServicesKit';
|
|
import { BusinessError, emitter } from '@kit.BasicServicesKit';
|
|
@@ -140,9 +145,9 @@ import {
|
|
|
import {
|
|
import {
|
|
|
PlaylistPlayRequest,
|
|
PlaylistPlayRequest,
|
|
|
consumePendingPlaylistPlay,
|
|
consumePendingPlaylistPlay,
|
|
|
- clearPendingPlaylistPlay,
|
|
|
|
|
- setPlaylistPlayConsumerReady
|
|
|
|
|
|
|
+ clearPendingPlaylistPlay
|
|
|
} from '../common/util/PlaylistPlayRequestStore';
|
|
} from '../common/util/PlaylistPlayRequestStore';
|
|
|
|
|
+import { resolvePlaylistDisplayCount } from '../common/util/PlaylistPlayDispatchHelper'
|
|
|
|
|
|
|
|
import { HSBColorPicker, HSBColorPickerLayout } from '@keke/color-picker'
|
|
import { HSBColorPicker, HSBColorPickerLayout } from '@keke/color-picker'
|
|
|
import app from '@system.app';
|
|
import app from '@system.app';
|
|
@@ -471,6 +476,7 @@ export struct LocalMusic {
|
|
|
@State maxRefreshingHeight: number = 100.0;
|
|
@State maxRefreshingHeight: number = 100.0;
|
|
|
@State isPlayerLoading: boolean = false; // 播放器加载状态
|
|
@State isPlayerLoading: boolean = false; // 播放器加载状态
|
|
|
private contentNode?: ComponentContent<Object> = undefined;
|
|
private contentNode?: ComponentContent<Object> = undefined;
|
|
|
|
|
+ private skipNextPlaylistPersist: boolean = false
|
|
|
|
|
|
|
|
// WebDAV相关实例变量(现在改为从内存读取,不再需要存储)
|
|
// WebDAV相关实例变量(现在改为从内存读取,不再需要存储)
|
|
|
@State ratioL: number = 1;
|
|
@State ratioL: number = 1;
|
|
@@ -617,8 +623,16 @@ export struct LocalMusic {
|
|
|
private coverThumbCache: CoverThumbCache = new CoverThumbCache(1, 960)
|
|
private coverThumbCache: CoverThumbCache = new CoverThumbCache(1, 960)
|
|
|
private coverThumbCacheReady: boolean = false
|
|
private coverThumbCacheReady: boolean = false
|
|
|
private pendingCoverThumbRefresh: boolean = false
|
|
private pendingCoverThumbRefresh: boolean = false
|
|
|
- private playQueueScope: 'view' | 'history' | 'fav' | 'playlist' | 'single' = 'view'
|
|
|
|
|
|
|
+ private playQueueScope: 'view' | 'history' | 'fav' | 'playlist' | 'single' | 'find_paged_local' = 'view'
|
|
|
private isHydratingPlayList: boolean = false
|
|
private isHydratingPlayList: boolean = false
|
|
|
|
|
+ private preserveExistingQueueOnNextPlay: boolean = false
|
|
|
|
|
+ private nextPlayQueueScope: 'view' | 'history' | 'fav' | 'playlist' | 'single' | 'find_paged_local' = 'view'
|
|
|
|
|
+ private findPagedLocalQueuePageIndex: number = 0
|
|
|
|
|
+ private findPagedLocalQueuePageSize: number = DEFAULT_LOCAL_RANDOM_QUEUE_PAGE_SIZE
|
|
|
|
|
+ private findPagedLocalQueueTotalCount: number = 0
|
|
|
|
|
+ private findPagedLocalQueueHasMore: boolean = false
|
|
|
|
|
+ private findPagedLocalQueueSortType: number = 4
|
|
|
|
|
+ private isLoadingFindPagedLocalQueue: boolean = false
|
|
|
@StorageProp('isLandscape') @Watch('onIsLandscapeChange') isLandscape: boolean = false;
|
|
@StorageProp('isLandscape') @Watch('onIsLandscapeChange') isLandscape: boolean = false;
|
|
|
@StorageProp('topSafeHeight') topSafeHeight: number = 0;
|
|
@StorageProp('topSafeHeight') topSafeHeight: number = 0;
|
|
|
@StorageProp('bottomSafeHeight') bottomSafeHeight: number = 0;
|
|
@StorageProp('bottomSafeHeight') bottomSafeHeight: number = 0;
|
|
@@ -1098,7 +1112,6 @@ export struct LocalMusic {
|
|
|
|
|
|
|
|
Logger.error('heanup eventPlaylistPlay: 接收到无效的数据结构')
|
|
Logger.error('heanup eventPlaylistPlay: 接收到无效的数据结构')
|
|
|
});
|
|
});
|
|
|
- setPlaylistPlayConsumerReady(true);
|
|
|
|
|
Promise.resolve().then(async (): Promise<void> => {
|
|
Promise.resolve().then(async (): Promise<void> => {
|
|
|
const pendingPlaylistRequest = consumePendingPlaylistPlay();
|
|
const pendingPlaylistRequest = consumePendingPlaylistPlay();
|
|
|
if (!pendingPlaylistRequest) {
|
|
if (!pendingPlaylistRequest) {
|
|
@@ -1307,7 +1320,7 @@ export struct LocalMusic {
|
|
|
ToastUtil.showToast('当前播放列表为空,请先导入音乐。');
|
|
ToastUtil.showToast('当前播放列表为空,请先导入音乐。');
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- this.setShowPlayTrue(true)
|
|
|
|
|
|
|
+ this.setShowPlayTrue()
|
|
|
this.showPlayerView();
|
|
this.showPlayerView();
|
|
|
});
|
|
});
|
|
|
this.getUIContext().getHostContext()!.eventHub.on('dismissPlayerView', () => {
|
|
this.getUIContext().getHostContext()!.eventHub.on('dismissPlayerView', () => {
|
|
@@ -2288,7 +2301,6 @@ export struct LocalMusic {
|
|
|
this.knockController?.immersiveDisableListening();
|
|
this.knockController?.immersiveDisableListening();
|
|
|
this.curIndex = 0
|
|
this.curIndex = 0
|
|
|
this.isAppForeground = false;
|
|
this.isAppForeground = false;
|
|
|
- setPlaylistPlayConsumerReady(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);
|
|
@@ -2753,6 +2765,12 @@ export struct LocalMusic {
|
|
|
if (this.playQueueScope === 'fav') {
|
|
if (this.playQueueScope === 'fav') {
|
|
|
return this.favList.length;
|
|
return this.favList.length;
|
|
|
}
|
|
}
|
|
|
|
|
+ if (this.playQueueScope === 'find_paged_local') {
|
|
|
|
|
+ const pagedCount = this.findPagedLocalQueueTotalCount > 0 ? this.findPagedLocalQueueTotalCount : this.totalCount;
|
|
|
|
|
+ if (pagedCount > 0) {
|
|
|
|
|
+ return pagedCount;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
if (this.playQueueScope === 'playlist') {
|
|
if (this.playQueueScope === 'playlist') {
|
|
|
// 检测是否是流媒体播放(Navidrome/Jellyfin/Emby/AudioStation/Plex/道理鱼等)
|
|
// 检测是否是流媒体播放(Navidrome/Jellyfin/Emby/AudioStation/Plex/道理鱼等)
|
|
|
// 如果是,优先返回服务端总数
|
|
// 如果是,优先返回服务端总数
|
|
@@ -2769,8 +2787,10 @@ export struct LocalMusic {
|
|
|
Logger.info('heanup getPlayListDisplayCount', `返回服务端总数: ${navidromeTotalCount}`);
|
|
Logger.info('heanup getPlayListDisplayCount', `返回服务端总数: ${navidromeTotalCount}`);
|
|
|
return navidromeTotalCount;
|
|
return navidromeTotalCount;
|
|
|
}
|
|
}
|
|
|
- Logger.info('heanup getPlayListDisplayCount', `返回 currentSongList.length: ${this.currentSongList.length}`);
|
|
|
|
|
- return this.currentSongList.length;
|
|
|
|
|
|
|
+ const displayCount = resolvePlaylistDisplayCount(this.currentSongList.length, this.songList.length);
|
|
|
|
|
+ Logger.info('heanup getPlayListDisplayCount',
|
|
|
|
|
+ `返回歌单数量: currentSongList=${this.currentSongList.length}, songList=${this.songList.length}, display=${displayCount}`);
|
|
|
|
|
+ return displayCount;
|
|
|
}
|
|
}
|
|
|
if (this.playQueueScope === 'view' && this.modeType === 1 && this.totalCount > 0) {
|
|
if (this.playQueueScope === 'view' && this.modeType === 1 && this.totalCount > 0) {
|
|
|
return this.totalCount;
|
|
return this.totalCount;
|
|
@@ -2781,7 +2801,8 @@ export struct LocalMusic {
|
|
|
|
|
|
|
|
private restoreLastPlayContext(): void {
|
|
private restoreLastPlayContext(): void {
|
|
|
const scope = PreferencesUtil.getStringSync('LastPlayQueueScope', 'view') as string;
|
|
const scope = PreferencesUtil.getStringSync('LastPlayQueueScope', 'view') as string;
|
|
|
- if (scope === 'history' || scope === 'fav' || scope === 'playlist' || scope === 'single') {
|
|
|
|
|
|
|
+ if (scope === 'history' || scope === 'fav' || scope === 'playlist' || scope === 'single' ||
|
|
|
|
|
+ scope === 'find_paged_local') {
|
|
|
this.playQueueScope = scope;
|
|
this.playQueueScope = scope;
|
|
|
} else {
|
|
} else {
|
|
|
this.playQueueScope = 'view';
|
|
this.playQueueScope = 'view';
|
|
@@ -2791,6 +2812,49 @@ export struct LocalMusic {
|
|
|
if (lastModeType === 1 && this.totalCount === 0 && lastTotalCount > 0) {
|
|
if (lastModeType === 1 && this.totalCount === 0 && lastTotalCount > 0) {
|
|
|
this.totalCount = lastTotalCount;
|
|
this.totalCount = lastTotalCount;
|
|
|
}
|
|
}
|
|
|
|
|
+ if (this.playQueueScope === 'find_paged_local' && lastTotalCount > 0) {
|
|
|
|
|
+ this.findPagedLocalQueueTotalCount = lastTotalCount;
|
|
|
|
|
+ this.findPagedLocalQueueHasMore = this.songList.length < lastTotalCount;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private resetFindPagedLocalQueueState(): void {
|
|
|
|
|
+ // 退出“随心所欲”分页队列时,避免旧分页状态污染后续普通播放队列。
|
|
|
|
|
+ this.findPagedLocalQueuePageIndex = 0;
|
|
|
|
|
+ this.findPagedLocalQueuePageSize = DEFAULT_LOCAL_RANDOM_QUEUE_PAGE_SIZE;
|
|
|
|
|
+ this.findPagedLocalQueueTotalCount = 0;
|
|
|
|
|
+ this.findPagedLocalQueueHasMore = false;
|
|
|
|
|
+ this.findPagedLocalQueueSortType = 4;
|
|
|
|
|
+ this.isLoadingFindPagedLocalQueue = false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private applyFindPagedLocalQueueState(meta?: FindPlaylistMeta, totalCount?: number): void {
|
|
|
|
|
+ // 播放器只持有当前页数据,但总数、页码和排序要单独记住,后面才能继续按页补队列。
|
|
|
|
|
+ this.findPagedLocalQueuePageIndex = meta?.pageIndex ?? 0;
|
|
|
|
|
+ this.findPagedLocalQueuePageSize = meta?.pageSize && meta.pageSize > 0
|
|
|
|
|
+ ? Math.max(1, Math.floor(meta.pageSize))
|
|
|
|
|
+ : DEFAULT_LOCAL_RANDOM_QUEUE_PAGE_SIZE;
|
|
|
|
|
+ this.findPagedLocalQueueSortType = meta?.sortType !== undefined ? meta.sortType : 4;
|
|
|
|
|
+ const actualTotalCount = meta?.totalCount && meta.totalCount > 0
|
|
|
|
|
+ ? meta.totalCount
|
|
|
|
|
+ : (totalCount ?? 0);
|
|
|
|
|
+ this.findPagedLocalQueueTotalCount = actualTotalCount;
|
|
|
|
|
+ this.findPagedLocalQueueHasMore =
|
|
|
|
|
+ (this.findPagedLocalQueuePageIndex + 1) * this.findPagedLocalQueuePageSize < actualTotalCount;
|
|
|
|
|
+ if (actualTotalCount > 0) {
|
|
|
|
|
+ this.totalCount = actualTotalCount;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private isFindPagedLocalQueueActive(): boolean {
|
|
|
|
|
+ return this.playQueueScope === 'find_paged_local';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private shouldHydrateFullMediaPlayListOnOpen(): boolean {
|
|
|
|
|
+ // 特殊分页队列禁止打开播放列表时自动补全全量歌曲,否则会回到 3000+ 首卡顿问题。
|
|
|
|
|
+ return !!this.currentSong &&
|
|
|
|
|
+ this.currentSong.type === CommonConstants.TYPE_LOCAL &&
|
|
|
|
|
+ !this.isFindPagedLocalQueueActive();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private async hydrateFullMediaPlayListIfNeeded(isSonList:boolean = true): Promise<void> {
|
|
private async hydrateFullMediaPlayListIfNeeded(isSonList:boolean = true): Promise<void> {
|
|
@@ -3811,9 +3875,7 @@ export struct LocalMusic {
|
|
|
|
|
|
|
|
build() {
|
|
build() {
|
|
|
Stack() {
|
|
Stack() {
|
|
|
- if (this.mType === 0) {
|
|
|
|
|
- this.ContentBuild()
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ this.ContentBuild()
|
|
|
Stack() {
|
|
Stack() {
|
|
|
Column()
|
|
Column()
|
|
|
.bindSheet($$this.isShowSheet, this.PlayListSheet(), {
|
|
.bindSheet($$this.isShowSheet, this.PlayListSheet(), {
|
|
@@ -3829,16 +3891,12 @@ export struct LocalMusic {
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
- if (this.isShowPlay) {
|
|
|
|
|
- Stack() {
|
|
|
|
|
- this.MusicPlayBuilder()
|
|
|
|
|
- }
|
|
|
|
|
- .width('100%')
|
|
|
|
|
- .height('100%')
|
|
|
|
|
- .zIndex(99)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
|
|
+ .bindContentCover($$this.isShowPlay, this.MusicPlayBuilder(), {
|
|
|
|
|
+ modalTransition: ModalTransition.DEFAULT,
|
|
|
|
|
+ onWillDisappear: () => {
|
|
|
|
|
+ this.setShowPlayFalse()
|
|
|
|
|
+ },
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
.alignContent(Alignment.Bottom)
|
|
.alignContent(Alignment.Bottom)
|
|
|
.width('100%')
|
|
.width('100%')
|
|
@@ -5913,22 +5971,11 @@ export struct LocalMusic {
|
|
|
private DirItem(item: VideoItem, index?: number) {
|
|
private DirItem(item: VideoItem, index?: number) {
|
|
|
Button({ type: ButtonType.Capsule, stateEffect: true }) {
|
|
Button({ type: ButtonType.Capsule, stateEffect: true }) {
|
|
|
Row() {
|
|
Row() {
|
|
|
- Image(this.modeType !== 0 && StrUtil.isNotEmpty(item.pixelMapPath) ?
|
|
|
|
|
- this.getListImageSource(item, index ?? -1, this.coverThumbVersion) :
|
|
|
|
|
- $r('app.media.music_group'))
|
|
|
|
|
- .height(this.twoFingerType == 3 ? 55 : this.twoFingerType == 2 ? 50 : 40)
|
|
|
|
|
- .width(this.twoFingerType == 3 ? 55 : this.twoFingerType == 2 ? 50 : 40)
|
|
|
|
|
- .fillColor(this.themeColor)// .fontSize(33)
|
|
|
|
|
- .alt($r('app.media.music_group'))
|
|
|
|
|
- .borderRadius(this.isCoverRectangle?9:'100%')
|
|
|
|
|
- .clip(true)
|
|
|
|
|
- .shadow({
|
|
|
|
|
- radius: 2,
|
|
|
|
|
- type: ShadowType.BLUR,
|
|
|
|
|
- color: 'on_primary'
|
|
|
|
|
- })
|
|
|
|
|
- .sourceSize({width:38, height:38})
|
|
|
|
|
- .margin({ left: 20, top: 8, bottom: 8 })
|
|
|
|
|
|
|
+ SymbolGlyph($r('sys.symbol.folder'))
|
|
|
|
|
+ .fontSize(this.twoFingerType==3?55:this.twoFingerType==2?50:45)
|
|
|
|
|
+ .fontColor([this.themeColor])
|
|
|
|
|
+ .alignSelf(ItemAlign.Center)
|
|
|
|
|
+ .margin({ left: 20 ,top:8,bottom:8})
|
|
|
Column() {
|
|
Column() {
|
|
|
Text(item.name.startsWith('.') ? item.name.replace(/\./g, '') : item.name)
|
|
Text(item.name.startsWith('.') ? item.name.replace(/\./g, '') : item.name)
|
|
|
.fontSize(this.twoFingerType == 1 ? 12 : this.twoFingerType == 2 ? 15 : 18)
|
|
.fontSize(this.twoFingerType == 1 ? 12 : this.twoFingerType == 2 ? 15 : 18)
|
|
@@ -6329,6 +6376,27 @@ export struct LocalMusic {
|
|
|
|
|
|
|
|
Logger.info('heanup LocalMusic', `onSearchInput: keyword="${this.searchText}", mode=${this.modeType}`);
|
|
Logger.info('heanup LocalMusic', `onSearchInput: keyword="${this.searchText}", mode=${this.modeType}`);
|
|
|
|
|
|
|
|
|
|
+ // 歌单模式不走媒体库分页查询,直接在当前歌单中筛选,避免混入非歌单歌曲并打乱顺序。
|
|
|
|
|
+ if (this.modeType === 4) {
|
|
|
|
|
+ if (this.searchText === '') {
|
|
|
|
|
+ this.loadSearchHistory()
|
|
|
|
|
+ this.filteredList = []
|
|
|
|
|
+ this.updateListData(this.currentSongList, true, true)
|
|
|
|
|
+ Logger.info('heanup LocalMusic', `onSearchInput: restore playlist list, count=${this.currentSongList.length}`)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!this.isSearchMode) {
|
|
|
|
|
+ this.isSearchMode = true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const filteredSongs = filterPlaylistSongsByKeyword(this.currentSongList, this.searchText)
|
|
|
|
|
+ this.filteredList = filteredSongs
|
|
|
|
|
+ this.updateListData(filteredSongs, true, true)
|
|
|
|
|
+ Logger.info('heanup LocalMusic', `onSearchInput: playlist filter applied, keyword="${this.searchText}", count=${filteredSongs.length}`)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 清空搜索文本时保持搜索态,只重置为不带关键词的结果列表
|
|
// 清空搜索文本时保持搜索态,只重置为不带关键词的结果列表
|
|
|
if (this.searchText === '') {
|
|
if (this.searchText === '') {
|
|
|
this.loadSearchHistory()
|
|
this.loadSearchHistory()
|
|
@@ -8108,8 +8176,13 @@ export struct LocalMusic {
|
|
|
console.info('kkMusic doPlay stop =' )
|
|
console.info('kkMusic doPlay stop =' )
|
|
|
this.stop();
|
|
this.stop();
|
|
|
}
|
|
}
|
|
|
|
|
+ const useExistingQueueForPlayback = this.preserveExistingQueueOnNextPlay;
|
|
|
|
|
+ const nextQueueScope = this.nextPlayQueueScope;
|
|
|
|
|
+ this.preserveExistingQueueOnNextPlay = false;
|
|
|
|
|
+ this.nextPlayQueueScope = 'view';
|
|
|
|
|
|
|
|
if (isOpen) {
|
|
if (isOpen) {
|
|
|
|
|
+ this.resetFindPagedLocalQueueState();
|
|
|
this.currentSong = item
|
|
this.currentSong = item
|
|
|
if (index !== undefined) {
|
|
if (index !== undefined) {
|
|
|
this.curIndex = index
|
|
this.curIndex = index
|
|
@@ -8123,17 +8196,32 @@ export struct LocalMusic {
|
|
|
if (index !== undefined) {
|
|
if (index !== undefined) {
|
|
|
this.curIndex = index
|
|
this.curIndex = index
|
|
|
}
|
|
}
|
|
|
- // 确保播放列表是完整的歌单歌曲列表(已在finishLoadingPlaylist中设置)
|
|
|
|
|
Logger.info(`heanup isFromSonPlayList分支 - 当前播放列表长度: ${this.songList.length}, 当前索引: ${this.curIndex}`)
|
|
Logger.info(`heanup isFromSonPlayList分支 - 当前播放列表长度: ${this.songList.length}, 当前索引: ${this.curIndex}`)
|
|
|
- this.playQueueScope = 'playlist'
|
|
|
|
|
- // 确保歌单路径列表已设置(从songList重新构建,以防被清空)
|
|
|
|
|
- if (this.currentPlaylistSongFilePaths.length === 0 && this.songList.length > 0) {
|
|
|
|
|
- this.currentPlaylistSongFilePaths = this.songList.map(song => song.filePath);
|
|
|
|
|
- Logger.info(`heanup isFromSonPlayList - 重新构建歌单路径列表,数量=${this.currentPlaylistSongFilePaths.length}`);
|
|
|
|
|
|
|
+ if (this.isFindPagedLocalQueueActive()) {
|
|
|
|
|
+ // 这类队列来自发现页分页随机,点击播放列表里的歌也要继续保持“分页队列”身份。
|
|
|
|
|
+ this.playQueueScope = 'find_paged_local'
|
|
|
|
|
+ this.currentPlaylistSongFilePaths = [];
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.resetFindPagedLocalQueueState();
|
|
|
|
|
+ this.playQueueScope = 'playlist'
|
|
|
|
|
+ if (this.currentPlaylistSongFilePaths.length === 0 && this.songList.length > 0) {
|
|
|
|
|
+ this.currentPlaylistSongFilePaths = this.songList.map(song => song.filePath);
|
|
|
|
|
+ Logger.info(`heanup isFromSonPlayList - 重新构建歌单路径列表,数量=${this.currentPlaylistSongFilePaths.length}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }else if (useExistingQueueForPlayback) {
|
|
|
|
|
+ // 特殊分页队列初始化时,直接复用 finishLoadingPlaylist 准备好的当前页,不再按视图重建全量队列。
|
|
|
|
|
+ this.currentSong = item
|
|
|
|
|
+ if (index !== undefined) {
|
|
|
|
|
+ this.curIndex = index
|
|
|
}
|
|
}
|
|
|
|
|
+ this.playQueueScope = nextQueueScope
|
|
|
|
|
+ this.currentPlaylistSongFilePaths = [];
|
|
|
|
|
+ this.sonDataSource.pushArrayData(this.songList)
|
|
|
}else {
|
|
}else {
|
|
|
|
|
+ this.resetFindPagedLocalQueueState();
|
|
|
const globalVideoList = this.buildPlayQueueFromView();
|
|
const globalVideoList = this.buildPlayQueueFromView();
|
|
|
- this.curIndex = Utility.getCurIndexFromGlobalList(globalVideoList, item.filePath)
|
|
|
|
|
|
|
+ this.curIndex = findSongIndexByFilePath(globalVideoList, item.filePath)
|
|
|
if (this.curIndex < 0) {
|
|
if (this.curIndex < 0) {
|
|
|
globalVideoList.push(item);
|
|
globalVideoList.push(item);
|
|
|
this.curIndex = globalVideoList.length - 1;
|
|
this.curIndex = globalVideoList.length - 1;
|
|
@@ -8189,6 +8277,9 @@ export struct LocalMusic {
|
|
|
if (this.CONTROL_PlayStatus !== PlayStatus.INIT) {
|
|
if (this.CONTROL_PlayStatus !== PlayStatus.INIT) {
|
|
|
this.stop();
|
|
this.stop();
|
|
|
}
|
|
}
|
|
|
|
|
+ this.resetFindPagedLocalQueueState();
|
|
|
|
|
+ this.preserveExistingQueueOnNextPlay = false;
|
|
|
|
|
+ this.nextPlayQueueScope = 'view';
|
|
|
|
|
|
|
|
if (isOpen) {
|
|
if (isOpen) {
|
|
|
this.currentSong = item
|
|
this.currentSong = item
|
|
@@ -9119,54 +9210,24 @@ export struct LocalMusic {
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- setShowPlayTrue(fromMiniBar: boolean = false){
|
|
|
|
|
- this.clearPlayDismissTimer()
|
|
|
|
|
- this.clearPlayOpenTimer()
|
|
|
|
|
- if (this.isShowPlay) {
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- if (fromMiniBar) {
|
|
|
|
|
- this.preparePlayerViewOpenFromMiniBar()
|
|
|
|
|
- } else {
|
|
|
|
|
- this.resetMusicPlayDismissState()
|
|
|
|
|
- }
|
|
|
|
|
- this.isShowPlay = true;
|
|
|
|
|
- this.showCoverScaleGuideIfNeeded()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private closePlayerViewWithMiniBarAnimation(): void {
|
|
|
|
|
- if (!this.isShowPlay || this.playDismissOpacity < 1) {
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- const target = this.resolveMiniBarDismissTarget()
|
|
|
|
|
-
|
|
|
|
|
- this.clearPlayDismissTimer()
|
|
|
|
|
- this.clearPlayOpenTimer()
|
|
|
|
|
|
|
+ setShowPlayTrue(){
|
|
|
this.getUIContext()?.animateTo({
|
|
this.getUIContext()?.animateTo({
|
|
|
- duration: this.playDismissContentDuration + 40,
|
|
|
|
|
- curve: Curve.Sharp
|
|
|
|
|
|
|
+ duration: 500,
|
|
|
|
|
+ curve: Curve.Friction
|
|
|
}, () => {
|
|
}, () => {
|
|
|
- this.translateY = target.translateY
|
|
|
|
|
- this.playDismissTranslateX = 0
|
|
|
|
|
- this.playDismissScale = 0.9
|
|
|
|
|
- this.playDismissScaleY = 0.8
|
|
|
|
|
- this.playDismissOpacity = 0.04
|
|
|
|
|
- this.playDismissBorderRadius = 48
|
|
|
|
|
- this.playDismissProxySize = 0
|
|
|
|
|
- this.playDismissProxyOpacity = 0
|
|
|
|
|
- this.playDragUiOpacity = 0
|
|
|
|
|
- this.scaleValueImage = 0.8
|
|
|
|
|
- this.scaleValueText = 0.86
|
|
|
|
|
- })
|
|
|
|
|
- this.playDismissTimer = setTimeout(() => {
|
|
|
|
|
- this.playDismissTimer = -1
|
|
|
|
|
- this.isShowPlay = false
|
|
|
|
|
- this.isShowCoverScaleGuide = false
|
|
|
|
|
- }, this.playDismissContentDuration + 56)
|
|
|
|
|
|
|
+ this.isShowPlay = true;
|
|
|
|
|
+ this.showCoverScaleGuideIfNeeded()
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
setShowPlayFalse(){
|
|
setShowPlayFalse(){
|
|
|
- this.closePlayerViewWithMiniBarAnimation()
|
|
|
|
|
|
|
+ this.getUIContext()?.animateTo({
|
|
|
|
|
+ duration: 500,
|
|
|
|
|
+ curve: Curve.Friction
|
|
|
|
|
+ }, () => {
|
|
|
|
|
+ this.isShowPlay = false;
|
|
|
|
|
+ });
|
|
|
|
|
+ this.isShowCoverScaleGuide = false
|
|
|
}
|
|
}
|
|
|
//打开播放页
|
|
//打开播放页
|
|
|
showPlayerView() {
|
|
showPlayerView() {
|
|
@@ -9190,8 +9251,7 @@ export struct LocalMusic {
|
|
|
LogUtil.info('onecold scrollToIndex = ' + this.curIndex)
|
|
LogUtil.info('onecold scrollToIndex = ' + this.curIndex)
|
|
|
|
|
|
|
|
this.scrollIndex()
|
|
this.scrollIndex()
|
|
|
- // 只有在播放本地音乐时才自动加载完整列表
|
|
|
|
|
- if (this.currentSong && this.currentSong.type === CommonConstants.TYPE_LOCAL) {
|
|
|
|
|
|
|
+ if (this.shouldHydrateFullMediaPlayListOnOpen()) {
|
|
|
void this.hydrateFullMediaPlayListIfNeeded();
|
|
void this.hydrateFullMediaPlayListIfNeeded();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -9782,6 +9842,11 @@ export struct LocalMusic {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private async tryLoadNextPageForPlayback(): Promise<void> {
|
|
private async tryLoadNextPageForPlayback(): Promise<void> {
|
|
|
|
|
+ if (this.playQueueScope === 'find_paged_local') {
|
|
|
|
|
+ // 发现页的本地随机分页队列单独补页,不去动主媒体库列表状态。
|
|
|
|
|
+ await this.loadMoreFindPagedLocalSongs();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
if (this.playQueueScope !== 'view') {
|
|
if (this.playQueueScope !== 'view') {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -9799,6 +9864,49 @@ export struct LocalMusic {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private async loadMoreFindPagedLocalSongs(): Promise<void> {
|
|
|
|
|
+ if (!this.findPagedLocalQueueHasMore || this.isLoadingFindPagedLocalQueue) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.isLoadingFindPagedLocalQueue = true;
|
|
|
|
|
+ const nextPage = this.findPagedLocalQueuePageIndex + 1;
|
|
|
|
|
+ Logger.info(TAG,
|
|
|
|
|
+ `loadMoreFindPagedLocalSongs: page=${nextPage}, size=${this.findPagedLocalQueuePageSize}, ` +
|
|
|
|
|
+ `loaded=${this.songList.length}, total=${this.findPagedLocalQueueTotalCount}`);
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 继续按发现页建立队列时的排序规则查询下一页,保证后续页和初始页顺序一致。
|
|
|
|
|
+ const result = await this.table.queryMediaLibraryPaged({
|
|
|
|
|
+ pageIndex: nextPage,
|
|
|
|
|
+ pageSize: this.findPagedLocalQueuePageSize,
|
|
|
|
|
+ sortType: this.findPagedLocalQueueSortType,
|
|
|
|
|
+ isShowFileName: this.isShowFileName
|
|
|
|
|
+ });
|
|
|
|
|
+ this.findPagedLocalQueuePageIndex = nextPage;
|
|
|
|
|
+ this.findPagedLocalQueueHasMore = result.hasMore;
|
|
|
|
|
+ if (result.totalCount > 0) {
|
|
|
|
|
+ this.findPagedLocalQueueTotalCount = result.totalCount;
|
|
|
|
|
+ this.totalCount = result.totalCount;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!result.items || result.items.length === 0) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const existingPaths = new Set(this.songList.map((item: VideoItem) => item.filePath));
|
|
|
|
|
+ // 随机播放过程中可能已经把“未加载页里的歌曲”临时插进队列,这里要去重后再追加。
|
|
|
|
|
+ const newItems = result.items.filter((item: VideoItem) => !existingPaths.has(item.filePath));
|
|
|
|
|
+ if (newItems.length === 0) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.songList.push(...newItems);
|
|
|
|
|
+ this.currentSongList = this.songList;
|
|
|
|
|
+ this.sonDataSource.appendArrayData(newItems);
|
|
|
|
|
+ PreferencesUtil.putSync('LastMusicList', this.songList);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ Logger.error(TAG, `loadMoreFindPagedLocalSongs failed: ${(error as Error).message}`);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.isLoadingFindPagedLocalQueue = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 加载更多流媒体歌曲(Navidrome/Jellyfin/Emby/AudioStation/Plex)
|
|
* 加载更多流媒体歌曲(Navidrome/Jellyfin/Emby/AudioStation/Plex)
|
|
|
* 当播放列表弹窗滚动到底部时调用
|
|
* 当播放列表弹窗滚动到底部时调用
|
|
@@ -12167,7 +12275,7 @@ export struct LocalMusic {
|
|
|
.width('100%')
|
|
.width('100%')
|
|
|
.height('100%')
|
|
.height('100%')
|
|
|
.onReachEnd(() => {
|
|
.onReachEnd(() => {
|
|
|
- if(this.modeType === 4 )
|
|
|
|
|
|
|
+ if(this.modeType === 4 && !this.isFindPagedLocalQueueActive())
|
|
|
return;
|
|
return;
|
|
|
// 检测是否是流媒体类型(Navidrome/Jellyfin/Emby/AudioStation/Plex),如果是则调用网盘加载更多逻辑
|
|
// 检测是否是流媒体类型(Navidrome/Jellyfin/Emby/AudioStation/Plex),如果是则调用网盘加载更多逻辑
|
|
|
const isStreamingSong = this.currentSong && (
|
|
const isStreamingSong = this.currentSong && (
|
|
@@ -12353,47 +12461,35 @@ export struct LocalMusic {
|
|
|
|
|
|
|
|
@Builder
|
|
@Builder
|
|
|
MusicPlayBuilder() {
|
|
MusicPlayBuilder() {
|
|
|
- Stack({ alignContent: Alignment.Center }) {
|
|
|
|
|
- Column() {
|
|
|
|
|
- this.IjkMusicPlayerView()
|
|
|
|
|
- }
|
|
|
|
|
- .visualEffect(deviceInfo.sdkApiVersion>=20&&this.bgController&&this.playDismissOpacity>=1
|
|
|
|
|
- && this.playDismissProxySize<=0 && this.playDismissBorderRadius<=0 ?
|
|
|
|
|
- new hdsEffect.HdsEffectBuilder()
|
|
|
|
|
- .shaderEffect({
|
|
|
|
|
- effectType: hdsEffect.EffectType.UV_BACKGROUND_FLOW_LIGHT,
|
|
|
|
|
- animation: {
|
|
|
|
|
- duration: 10000,
|
|
|
|
|
- iterations: -1,
|
|
|
|
|
- autoPlay: true,
|
|
|
|
|
- onFinish: ()=> {
|
|
|
|
|
- console.info('Succeeded in finishing');
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- controller: this.bgController,
|
|
|
|
|
- })
|
|
|
|
|
- .buildEffect():null)
|
|
|
|
|
-
|
|
|
|
|
- .height('100%')
|
|
|
|
|
- .width('100%')
|
|
|
|
|
- .borderRadius(this.playDismissBorderRadius)
|
|
|
|
|
- .clip(true)
|
|
|
|
|
- .translate({ x: this.playDismissTranslateX, y: this.translateY })
|
|
|
|
|
- .scale({ x: this.playDismissScale, y: this.playDismissScaleY })
|
|
|
|
|
- .opacity(this.playDismissOpacity)
|
|
|
|
|
-
|
|
|
|
|
- if (this.playDismissProxySize > 0) {
|
|
|
|
|
- this.MusicPlayDismissProxyDot()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ this.IjkMusicPlayerView()
|
|
|
|
|
+ }
|
|
|
|
|
+ .transition(TransitionEffect.asymmetric(
|
|
|
|
|
+ TransitionEffect.opacity(1),
|
|
|
|
|
+ TransitionEffect.OPACITY
|
|
|
|
|
+ ))
|
|
|
|
|
+ .visualEffect(deviceInfo.sdkApiVersion>=20&&this.bgController?
|
|
|
|
|
+ new hdsEffect.HdsEffectBuilder()
|
|
|
|
|
+ .shaderEffect({
|
|
|
|
|
+ effectType: hdsEffect.EffectType.UV_BACKGROUND_FLOW_LIGHT,
|
|
|
|
|
+ animation: {
|
|
|
|
|
+ duration: 10000,
|
|
|
|
|
+ iterations: -1,
|
|
|
|
|
+ autoPlay: true,
|
|
|
|
|
+ onFinish: ()=> {
|
|
|
|
|
+ console.info('Succeeded in finishing');
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ controller: this.bgController,
|
|
|
|
|
+ })
|
|
|
|
|
+ .buildEffect():null)
|
|
|
.height('100%')
|
|
.height('100%')
|
|
|
.width('100%')
|
|
.width('100%')
|
|
|
- .onAppear(() => {
|
|
|
|
|
- this.startPlayerViewOpenAnimationIfNeeded()
|
|
|
|
|
- })
|
|
|
|
|
.onDisAppear(() => {
|
|
.onDisAppear(() => {
|
|
|
- this.resetMusicPlayDismissState()
|
|
|
|
|
|
|
+ this.translateY = 0;
|
|
|
|
|
+ this.playDragUiOpacity = 1
|
|
|
})
|
|
})
|
|
|
|
|
+ .translate({ y: this.translateY })
|
|
|
.gesture(
|
|
.gesture(
|
|
|
PanGesture(this.panOption)
|
|
PanGesture(this.panOption)
|
|
|
.onActionUpdate((event?: GestureEvent) => {
|
|
.onActionUpdate((event?: GestureEvent) => {
|
|
@@ -12420,7 +12516,7 @@ export struct LocalMusic {
|
|
|
duration: 500,
|
|
duration: 500,
|
|
|
curve: Curve.Sharp
|
|
curve: Curve.Sharp
|
|
|
}, () => {
|
|
}, () => {
|
|
|
- this.scaleValueImage = Math.min(1, Math.max(0.46, 1 - this.translateY / 900));
|
|
|
|
|
|
|
+ this.scaleValueImage = Math.min(1, Math.max(0.5, 1 - this.translateY / 880));
|
|
|
console.info('onecold scaleValueImage:', this.scaleValueImage)
|
|
console.info('onecold scaleValueImage:', this.scaleValueImage)
|
|
|
this.scaleValueText = Math.min(1, Math.max(0.88, 1 - this.translateY / 2000));
|
|
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));
|
|
this.playDragUiOpacity = Math.min(1, Math.max(0, 1 - this.translateY / 300));
|
|
@@ -12457,7 +12553,17 @@ export struct LocalMusic {
|
|
|
const shouldClose = this.translateY > minDistance || velocity > minVelocity;
|
|
const shouldClose = this.translateY > minDistance || velocity > minVelocity;
|
|
|
|
|
|
|
|
if (shouldClose) {
|
|
if (shouldClose) {
|
|
|
- this.closePlayerViewWithMiniBarAnimation()
|
|
|
|
|
|
|
+ // 添加关闭动画
|
|
|
|
|
+ this.getUIContext().animateTo({
|
|
|
|
|
+ duration: 600,
|
|
|
|
|
+ curve: Curve.Friction
|
|
|
|
|
+ }, () => {
|
|
|
|
|
+ this.isShowPlay = false;
|
|
|
|
|
+ this.scaleValueImage = 1
|
|
|
|
|
+ this.scaleValueText = 1
|
|
|
|
|
+ this.playDragUiOpacity = 1
|
|
|
|
|
+ this.translateY = 0;
|
|
|
|
|
+ })
|
|
|
} else {
|
|
} else {
|
|
|
// 回弹动画
|
|
// 回弹动画
|
|
|
this.getUIContext().animateTo({
|
|
this.getUIContext().animateTo({
|
|
@@ -14145,8 +14251,7 @@ export struct LocalMusic {
|
|
|
this.isFrontWhite = false
|
|
this.isFrontWhite = false
|
|
|
}
|
|
}
|
|
|
this.scrollIndex()
|
|
this.scrollIndex()
|
|
|
- // 只有在播放本地音乐时才自动加载完整列表
|
|
|
|
|
- if (this.currentSong && this.currentSong.type === CommonConstants.TYPE_LOCAL) {
|
|
|
|
|
|
|
+ if (this.shouldHydrateFullMediaPlayListOnOpen()) {
|
|
|
void this.hydrateFullMediaPlayListIfNeeded();
|
|
void this.hydrateFullMediaPlayListIfNeeded();
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
@@ -14646,7 +14751,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)
|
|
.opacity(this.playDragUiOpacity)
|
|
|
- .margin({ top:this.isCoverRectangle?10: 0 })
|
|
|
|
|
|
|
+ .margin({ top:this.isCoverRectangle?15: 0 })
|
|
|
.visibility(this.isCoverOpacity() || isHidden ? Visibility.None : Visibility.Visible)
|
|
.visibility(this.isCoverOpacity() || isHidden ? Visibility.None : Visibility.Visible)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -18416,7 +18521,12 @@ export struct LocalMusic {
|
|
|
//保存最后播放的那首歌和已经对应的播放列表
|
|
//保存最后播放的那首歌和已经对应的播放列表
|
|
|
saveLastPlayList() {
|
|
saveLastPlayList() {
|
|
|
PreferencesUtil.putSync('LastMusicInfo', this.currentSong)
|
|
PreferencesUtil.putSync('LastMusicInfo', this.currentSong)
|
|
|
- PreferencesUtil.putSync('LastMusicList', this.songList)
|
|
|
|
|
|
|
+ // 新队列刚刚完成落盘时,首帧 prepared 会立刻再进一次这里,跳过这次重复写入即可。
|
|
|
|
|
+ if (this.skipNextPlaylistPersist) {
|
|
|
|
|
+ this.skipNextPlaylistPersist = false
|
|
|
|
|
+ } else {
|
|
|
|
|
+ PreferencesUtil.putSync('LastMusicList', this.songList)
|
|
|
|
|
+ }
|
|
|
PreferencesUtil.putSync('LastPlayQueueScope', this.playQueueScope)
|
|
PreferencesUtil.putSync('LastPlayQueueScope', this.playQueueScope)
|
|
|
PreferencesUtil.putSync('LastPlayModeType', this.modeType)
|
|
PreferencesUtil.putSync('LastPlayModeType', this.modeType)
|
|
|
PreferencesUtil.putSync('LastPlayTotalCount', this.totalCount)
|
|
PreferencesUtil.putSync('LastPlayTotalCount', this.totalCount)
|
|
@@ -20731,6 +20841,7 @@ export struct LocalMusic {
|
|
|
this.currentPlaylistSongFilePaths = this.songList.map(song => song.filePath);
|
|
this.currentPlaylistSongFilePaths = this.songList.map(song => song.filePath);
|
|
|
this.curIndex = targetIndex;
|
|
this.curIndex = targetIndex;
|
|
|
PreferencesUtil.putSync('LastMusicList', this.songList);
|
|
PreferencesUtil.putSync('LastMusicList', this.songList);
|
|
|
|
|
+ this.skipNextPlaylistPersist = true
|
|
|
Logger.info(TAG, `WebDAV播放队列已刷新,当前索引=${this.curIndex}, 队列长度=${this.songList.length}`);
|
|
Logger.info(TAG, `WebDAV播放队列已刷新,当前索引=${this.curIndex}, 队列长度=${this.songList.length}`);
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
Logger.error(TAG, `刷新WebDAV播放队列失败: ${(error as Error).message}`);
|
|
Logger.error(TAG, `刷新WebDAV播放队列失败: ${(error as Error).message}`);
|
|
@@ -20788,6 +20899,7 @@ export struct LocalMusic {
|
|
|
Logger.info(`heanup 检测到发现页播放请求,从内存读取videoItems`)
|
|
Logger.info(`heanup 检测到发现页播放请求,从内存读取videoItems`)
|
|
|
const videoItems = getFindVideoItems();
|
|
const videoItems = getFindVideoItems();
|
|
|
const currentPlayIndex = getFindCurrentPlayIndex();
|
|
const currentPlayIndex = getFindCurrentPlayIndex();
|
|
|
|
|
+ const findPlaylistMeta = getFindPlaylistMeta();
|
|
|
Logger.info(`heanup 发现页歌曲列表长度: ${videoItems.length}, 索引: ${currentPlayIndex}`)
|
|
Logger.info(`heanup 发现页歌曲列表长度: ${videoItems.length}, 索引: ${currentPlayIndex}`)
|
|
|
Logger.info(
|
|
Logger.info(
|
|
|
TAG,
|
|
TAG,
|
|
@@ -20798,7 +20910,11 @@ export struct LocalMusic {
|
|
|
if (isJump) {
|
|
if (isJump) {
|
|
|
this.setShowPlayTrue()
|
|
this.setShowPlayTrue()
|
|
|
}
|
|
}
|
|
|
- this.finishLoadingPlaylist(videoItems, currentPlayIndex, playlistName, totalCount)
|
|
|
|
|
|
|
+ // 只有“随心所欲分页随机”才读取分页元数据,其他发现页队列仍按原有歌单模式处理。
|
|
|
|
|
+ const findMeta = isFindLocalRandomPagedPlaylist(playlistId) && findPlaylistMeta.isPagedLocalQueue
|
|
|
|
|
+ ? findPlaylistMeta
|
|
|
|
|
+ : undefined;
|
|
|
|
|
+ this.finishLoadingPlaylist(videoItems, currentPlayIndex, playlistName, totalCount, findMeta)
|
|
|
return
|
|
return
|
|
|
} else {
|
|
} else {
|
|
|
Logger.warn('heanup 发现页播放请求缺少歌曲数据')
|
|
Logger.warn('heanup 发现页播放请求缺少歌曲数据')
|
|
@@ -20880,27 +20996,33 @@ export struct LocalMusic {
|
|
|
/**
|
|
/**
|
|
|
* 完成歌单加载并开始播放
|
|
* 完成歌单加载并开始播放
|
|
|
*/
|
|
*/
|
|
|
- private finishLoadingPlaylist(songs: VideoItem[], startIndex: number, playlistName: string, totalCount?: number) {
|
|
|
|
|
|
|
+ private finishLoadingPlaylist(songs: VideoItem[], startIndex: number, playlistName: string, totalCount?: number,
|
|
|
|
|
+ findMeta?: FindPlaylistMeta) {
|
|
|
if (songs.length === 0) {
|
|
if (songs.length === 0) {
|
|
|
Logger.error('heanup 没有找到任何可播放的歌曲')
|
|
Logger.error('heanup 没有找到任何可播放的歌曲')
|
|
|
ToastUtil.showToast('没有找到可播放的歌曲')
|
|
ToastUtil.showToast('没有找到可播放的歌曲')
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
+ const isFindPagedLocalQueue = !!findMeta?.isPagedLocalQueue;
|
|
|
this.songList = songs
|
|
this.songList = songs
|
|
|
this.currentSongList = songs // 同步更新 currentSongList
|
|
this.currentSongList = songs // 同步更新 currentSongList
|
|
|
|
|
|
|
|
this.sonDataSource.pushArrayData(songs)
|
|
this.sonDataSource.pushArrayData(songs)
|
|
|
|
|
|
|
|
- this.sonDataSource.notifyDataReload()
|
|
|
|
|
-
|
|
|
|
|
this.curIndex = startIndex
|
|
this.curIndex = startIndex
|
|
|
|
|
|
|
|
- // 保存歌单的歌曲路径列表,用于随机播放
|
|
|
|
|
- this.currentPlaylistSongFilePaths = songs.map(song => song.filePath);
|
|
|
|
|
const actualTotal = totalCount ?? songs.length;
|
|
const actualTotal = totalCount ?? songs.length;
|
|
|
|
|
+ if (isFindPagedLocalQueue) {
|
|
|
|
|
+ // 这类队列只保存当前页歌曲,总数和分页游标放到独立状态里继续滚动加载。
|
|
|
|
|
+ this.applyFindPagedLocalQueueState(findMeta, actualTotal);
|
|
|
|
|
+ this.currentPlaylistSongFilePaths = [];
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.resetFindPagedLocalQueueState();
|
|
|
|
|
+ this.currentPlaylistSongFilePaths = songs.map(song => song.filePath);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// 如果是网盘播放,保存服务端总数到全局存储
|
|
// 如果是网盘播放,保存服务端总数到全局存储
|
|
|
- if (totalCount && totalCount > 0 && songs.length > 0) {
|
|
|
|
|
|
|
+ if (!isFindPagedLocalQueue && totalCount && totalCount > 0 && songs.length > 0) {
|
|
|
const firstSong = songs[0];
|
|
const firstSong = songs[0];
|
|
|
if (firstSong && (isNavidromeType(firstSong.type) ||
|
|
if (firstSong && (isNavidromeType(firstSong.type) ||
|
|
|
isJellyfinType(firstSong.type) ||
|
|
isJellyfinType(firstSong.type) ||
|
|
@@ -20923,11 +21045,19 @@ export struct LocalMusic {
|
|
|
if (songs[startIndex]) {
|
|
if (songs[startIndex]) {
|
|
|
// 确保当前播放的歌曲也更新到存储
|
|
// 确保当前播放的歌曲也更新到存储
|
|
|
AppStorage.setOrCreate('currentSong', songs[startIndex])
|
|
AppStorage.setOrCreate('currentSong', songs[startIndex])
|
|
|
- // 设置isFromSonPlayList=true,避免doPlay方法重新从全局列表构建播放列表
|
|
|
|
|
- this.doPlay(songs[startIndex], startIndex, true)
|
|
|
|
|
|
|
+ if (isFindPagedLocalQueue) {
|
|
|
|
|
+ // 不要把发现页分页随机队列降级成普通歌单,否则后面又会触发全量 hydrate。
|
|
|
|
|
+ this.preserveExistingQueueOnNextPlay = true
|
|
|
|
|
+ this.nextPlayQueueScope = 'find_paged_local'
|
|
|
|
|
+ this.doPlay(songs[startIndex], startIndex, false)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 设置isFromSonPlayList=true,避免doPlay方法重新从全局列表构建播放列表
|
|
|
|
|
+ this.doPlay(songs[startIndex], startIndex, true)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
PreferencesUtil.putSync('LastMusicList', this.songList)
|
|
PreferencesUtil.putSync('LastMusicList', this.songList)
|
|
|
|
|
+ this.skipNextPlaylistPersist = true
|
|
|
|
|
|
|
|
// ToastUtil.showToast(`开始播放歌单: ${playlistName}`)
|
|
// ToastUtil.showToast(`开始播放歌单: ${playlistName}`)
|
|
|
}
|
|
}
|