|
|
@@ -24,21 +24,21 @@ import { ServerLogUtil } from '../common/util/ServerLogUtil';
|
|
|
import { RemoteDriveManager } from '../common/util/RemoteDriveManager';
|
|
|
import { SettingPage } from '../pages/SettingPage';
|
|
|
import { TitleBarPointLightButton } from './PointLight/TitleBarPointLightButton';
|
|
|
-import { jellyfinApi, JellyfinAlbum, JellyfinArtist, JellyfinSong } from '../common/network/JellyfinApi';
|
|
|
-import { embyApi, EmbyAlbum, EmbyArtist, EmbySong } from '../common/network/EmbyApi';
|
|
|
+import { jellyfinApi, JellyfinAlbum, JellyfinArtist, JellyfinPlaylist, JellyfinSong } from '../common/network/JellyfinApi';
|
|
|
+import { embyApi, EmbyAlbum, EmbyArtist, EmbyPlaylist, EmbySong } from '../common/network/EmbyApi';
|
|
|
import { audioStationApi, AudioStationAlbum, AudioStationArtist, AudioStationPlaylist, AudioStationSong } from '../common/network/AudioStationApi';
|
|
|
import { plexApi, PlexAlbum, PlexArtist, PlexPlaylist, PlexSong } from '../common/network/PlexApi';
|
|
|
import { daoLiYuApi, DaoLiYuAlbum, DaoLiYuArtist, DaoLiYuTrack, DaoLiYuPlaylist } from '../common/network/DaoLiYuApi';
|
|
|
-import { getRemoteDriveDisplayLabel } from '../common/util/RemoteDriveLabel';
|
|
|
import NavidromeListCache, { AllCacheData } from '../common/util/NavidromeListCache';
|
|
|
import { LazyDataSource } from '../common/util/LazyDataSource';
|
|
|
import { taskpool } from '@kit.ArkTS';
|
|
|
import { SearchHistoryUtil } from '../common/util/SearchHistoryUtil';
|
|
|
import { PlayingIndicator } from './PlayingIndicator';
|
|
|
import { hdsEffect } from '@kit.UIDesignKit';
|
|
|
-import { PlaylistPlayRequest, savePendingPlaylistPlay } from '../common/util/PlaylistPlayRequestStore';
|
|
|
+import { filterAlbumsByKeyword, filterArtistsByKeyword, filterPlaylistsByKeyword } from '../common/util/RemoteMusicSearchHelper';
|
|
|
+import { MusicPlaybackController } from '../controller/MusicPlaybackController';
|
|
|
+import { shouldRenderRemoteMusicSongView } from '../common/util/RemoteMusicViewModeHelper';
|
|
|
|
|
|
-const NAVIDROME_PLAYLIST_ID = 'navidrome-playlist';
|
|
|
const NAVIDROME_SEARCH_LIMIT = 500;
|
|
|
|
|
|
const ITEM_HEIGHT_SMALL: number = 48; // 列表项小高度
|
|
|
@@ -341,6 +341,7 @@ export struct RemoteMusicPage {
|
|
|
@State sortType: number = 0; // 排序类型 0:名称升序 1:名称降序 2:时间升序 3:时间降序 4:大小升序 5:大小降序
|
|
|
@State isSearchLoading: boolean = false;
|
|
|
private readonly REMOTE_SEARCH_LIMIT: number = 500;
|
|
|
+ private playbackController: MusicPlaybackController = MusicPlaybackController.getInstance()
|
|
|
|
|
|
// SegmentButton选项
|
|
|
@State tabOptions: SegmentButtonOptions = this.createTabOptions();
|
|
|
@@ -356,17 +357,17 @@ export struct RemoteMusicPage {
|
|
|
this.clearFilter();
|
|
|
}
|
|
|
|
|
|
- // 从艺术家或专辑切换回全部时,清除筛选状态
|
|
|
- if (this.selectedTab === 0 && this.filterType !== NavFilterType.None) {
|
|
|
- // 不清除筛选,保持筛选状态
|
|
|
- } else if (this.selectedTab !== 0) {
|
|
|
- // 切换到艺术家或专辑标签页时,清除筛选和搜索状态
|
|
|
+ if (this.selectedTab !== 0) {
|
|
|
this.clearFilter();
|
|
|
- this.isSearchMode = false;
|
|
|
- this.searchText = '';
|
|
|
- this.filteredList = [];
|
|
|
- this.isSearchLoading = false;
|
|
|
- this.searchTicket++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.isSearchMode && this.searchText.length > 0) {
|
|
|
+ if (this.selectedTab === 0) {
|
|
|
+ void this.onSearchInput(this.searchText);
|
|
|
+ } else {
|
|
|
+ this.isSearchLoading = false;
|
|
|
+ this.refreshVisibleDataSources();
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -591,10 +592,14 @@ export struct RemoteMusicPage {
|
|
|
|
|
|
// 更新所有 LazyForEach 数据源
|
|
|
private updateAllDataSources(): void {
|
|
|
+ this.refreshVisibleDataSources();
|
|
|
+ }
|
|
|
+
|
|
|
+ private refreshVisibleDataSources(): void {
|
|
|
this.songDataSource.pushArrayData(this.getVisibleSongs());
|
|
|
- this.artistDataSource.pushArrayData(this.artists);
|
|
|
- this.albumDataSource.pushArrayData(this.albums);
|
|
|
- this.playlistDataSource.pushArrayData(this.playlists);
|
|
|
+ this.artistDataSource.pushArrayData(this.getVisibleArtists());
|
|
|
+ this.albumDataSource.pushArrayData(this.getVisibleAlbums());
|
|
|
+ this.playlistDataSource.pushArrayData(this.getVisiblePlaylists());
|
|
|
}
|
|
|
|
|
|
private async loadMediaLibrary(account: WebDavAccount): Promise<void> {
|
|
|
@@ -771,14 +776,21 @@ export struct RemoteMusicPage {
|
|
|
this.songNextStart = 0;
|
|
|
this.artistNextStart = 0;
|
|
|
this.albumNextStart = 0;
|
|
|
- this.playlistNextStart = null;
|
|
|
+ this.playlistNextStart = 0;
|
|
|
+ const playlistLoad = this.loadNextJellyfinPlaylistPage(account, ticket).catch((error: Error) => {
|
|
|
+ if (ticket === this.loadTicket) {
|
|
|
+ this.playlistNextStart = null;
|
|
|
+ void ServerLogUtil.warn('NavidromeLoad', `Jellyfin 歌单首屏加载失败,已跳过: ${error.message}`);
|
|
|
+ }
|
|
|
+ });
|
|
|
await Promise.all([
|
|
|
this.loadNextJellyfinSongPage(account, ticket),
|
|
|
this.loadNextJellyfinArtistPage(account, ticket),
|
|
|
- this.loadNextJellyfinAlbumPage(account, ticket)
|
|
|
+ this.loadNextJellyfinAlbumPage(account, ticket),
|
|
|
+ playlistLoad
|
|
|
]);
|
|
|
if (ticket === this.loadTicket) {
|
|
|
- void ServerLogUtil.info('NavidromeLoad', `Jellyfin 首屏完成: 歌曲 ${this.allVideos.length} / 艺术家 ${this.artists.length} / 专辑 ${this.albums.length}`);
|
|
|
+ void ServerLogUtil.info('NavidromeLoad', `Jellyfin 首屏完成: 歌曲 ${this.allVideos.length} / 艺术家 ${this.artists.length} / 专辑 ${this.albums.length} / 歌单 ${this.playlists.length}`);
|
|
|
}
|
|
|
} catch (error) {
|
|
|
if (ticket === this.loadTicket) {
|
|
|
@@ -802,14 +814,21 @@ export struct RemoteMusicPage {
|
|
|
this.songNextStart = 0;
|
|
|
this.artistNextStart = 0;
|
|
|
this.albumNextStart = 0;
|
|
|
- this.playlistNextStart = null;
|
|
|
+ this.playlistNextStart = 0;
|
|
|
+ const playlistLoad = this.loadNextEmbyPlaylistPage(account, ticket).catch((error: Error) => {
|
|
|
+ if (ticket === this.loadTicket) {
|
|
|
+ this.playlistNextStart = null;
|
|
|
+ void ServerLogUtil.warn('NavidromeLoad', `Emby 歌单首屏加载失败,已跳过: ${error.message}`);
|
|
|
+ }
|
|
|
+ });
|
|
|
await Promise.all([
|
|
|
this.loadNextEmbySongPage(account, ticket),
|
|
|
this.loadNextEmbyArtistPage(account, ticket),
|
|
|
- this.loadNextEmbyAlbumPage(account, ticket)
|
|
|
+ this.loadNextEmbyAlbumPage(account, ticket),
|
|
|
+ playlistLoad
|
|
|
]);
|
|
|
if (ticket === this.loadTicket) {
|
|
|
- void ServerLogUtil.info('NavidromeLoad', `Emby 首屏完成: 歌曲 ${this.allVideos.length} / 艺术家 ${this.artists.length} / 专辑 ${this.albums.length}`);
|
|
|
+ void ServerLogUtil.info('NavidromeLoad', `Emby 首屏完成: 歌曲 ${this.allVideos.length} / 艺术家 ${this.artists.length} / 专辑 ${this.albums.length} / 歌单 ${this.playlists.length}`);
|
|
|
}
|
|
|
} catch (error) {
|
|
|
if (ticket === this.loadTicket) {
|
|
|
@@ -1136,6 +1155,30 @@ export struct RemoteMusicPage {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private async loadNextJellyfinPlaylistPage(account: WebDavAccount, ticket?: number): Promise<void> {
|
|
|
+ if (this.playlistNextStart === null || this.isPlaylistPageLoading) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const currentTicket = ticket ?? this.loadTicket;
|
|
|
+ this.isPlaylistPageLoading = true;
|
|
|
+ try {
|
|
|
+ const response = await jellyfinApi.getPlaylistsPage(account, this.playlistNextStart, this.REMOTE_PAGE_SIZE);
|
|
|
+ if (currentTicket !== this.loadTicket) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const processed = await this.convertJellyfinPlaylistsToRest(response.items, account);
|
|
|
+ if (currentTicket !== this.loadTicket) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.playlists = [...this.playlists, ...processed];
|
|
|
+ this.updateAllDataSources();
|
|
|
+ this.playlistNextStart = response.nextStart;
|
|
|
+ void ServerLogUtil.info('NavidromeLoad', `Jellyfin 歌单追加: 本次 ${processed.length} 个, 总数 ${this.playlists.length}`);
|
|
|
+ } finally {
|
|
|
+ this.isPlaylistPageLoading = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private async loadNextEmbySongPage(account: WebDavAccount, ticket?: number): Promise<void> {
|
|
|
if (this.songNextStart === null || this.isSongPageLoading) {
|
|
|
return;
|
|
|
@@ -1229,6 +1272,30 @@ export struct RemoteMusicPage {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private async loadNextEmbyPlaylistPage(account: WebDavAccount, ticket?: number): Promise<void> {
|
|
|
+ if (this.playlistNextStart === null || this.isPlaylistPageLoading) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const currentTicket = ticket ?? this.loadTicket;
|
|
|
+ this.isPlaylistPageLoading = true;
|
|
|
+ try {
|
|
|
+ const response = await embyApi.getPlaylistsPage(account, this.playlistNextStart, this.REMOTE_PAGE_SIZE);
|
|
|
+ if (currentTicket !== this.loadTicket) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const processed = await this.convertEmbyPlaylistsToRest(response.items, account);
|
|
|
+ if (currentTicket !== this.loadTicket) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.playlists = [...this.playlists, ...processed];
|
|
|
+ this.updateAllDataSources();
|
|
|
+ this.playlistNextStart = response.nextStart;
|
|
|
+ void ServerLogUtil.info('NavidromeLoad', `Emby 歌单追加: 本次 ${processed.length} 个, 总数 ${this.playlists.length}`);
|
|
|
+ } finally {
|
|
|
+ this.isPlaylistPageLoading = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private async loadNextAudioStationSongPage(account: WebDavAccount, ticket?: number): Promise<void> {
|
|
|
if (this.songNextStart === null || this.isSongPageLoading) {
|
|
|
return;
|
|
|
@@ -1608,6 +1675,9 @@ export struct RemoteMusicPage {
|
|
|
case 2:
|
|
|
await this.loadNextJellyfinAlbumPage(account);
|
|
|
break;
|
|
|
+ case 3:
|
|
|
+ await this.loadNextJellyfinPlaylistPage(account);
|
|
|
+ break;
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
@@ -1624,6 +1694,9 @@ export struct RemoteMusicPage {
|
|
|
case 2:
|
|
|
await this.loadNextEmbyAlbumPage(account);
|
|
|
break;
|
|
|
+ case 3:
|
|
|
+ await this.loadNextEmbyPlaylistPage(account);
|
|
|
+ break;
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
@@ -1892,6 +1965,40 @@ export struct RemoteMusicPage {
|
|
|
return results;
|
|
|
}
|
|
|
|
|
|
+ private async convertJellyfinPlaylistsToRest(playlists: JellyfinPlaylist[], account: WebDavAccount): Promise<NavidromeRestPlaylist[]> {
|
|
|
+ const results: NavidromeRestPlaylist[] = [];
|
|
|
+ for (let i = 0; i < playlists.length; i++) {
|
|
|
+ const playlist = playlists[i];
|
|
|
+ const coverUrl = await this.buildCoverUrl(account, playlist.id, 300);
|
|
|
+ const restPlaylist: NavidromeRestPlaylist = {
|
|
|
+ id: playlist.id,
|
|
|
+ name: playlist.name,
|
|
|
+ duration: playlist.durationSeconds,
|
|
|
+ songCount: playlist.songCount,
|
|
|
+ coverUrl: coverUrl
|
|
|
+ };
|
|
|
+ results.push(restPlaylist);
|
|
|
+ }
|
|
|
+ return results;
|
|
|
+ }
|
|
|
+
|
|
|
+ private async convertEmbyPlaylistsToRest(playlists: EmbyPlaylist[], account: WebDavAccount): Promise<NavidromeRestPlaylist[]> {
|
|
|
+ const results: NavidromeRestPlaylist[] = [];
|
|
|
+ for (let i = 0; i < playlists.length; i++) {
|
|
|
+ const playlist = playlists[i];
|
|
|
+ const coverUrl = await this.buildCoverUrl(account, playlist.id, 300);
|
|
|
+ const restPlaylist: NavidromeRestPlaylist = {
|
|
|
+ id: playlist.id,
|
|
|
+ name: playlist.name,
|
|
|
+ duration: playlist.durationSeconds,
|
|
|
+ songCount: playlist.songCount,
|
|
|
+ coverUrl: coverUrl
|
|
|
+ };
|
|
|
+ results.push(restPlaylist);
|
|
|
+ }
|
|
|
+ return results;
|
|
|
+ }
|
|
|
+
|
|
|
private convertAudioStationArtistsToRest(artists: AudioStationArtist[]): NavidromeRestArtist[] {
|
|
|
return artists.map(artist => {
|
|
|
const name = artist.name ?? '';
|
|
|
@@ -2187,6 +2294,20 @@ export struct RemoteMusicPage {
|
|
|
return results;
|
|
|
}
|
|
|
|
|
|
+ private async fetchAllEmbyPlaylistSongs(account: WebDavAccount, playlistId: string): Promise<EmbySong[]> {
|
|
|
+ const results: EmbySong[] = [];
|
|
|
+ let startIndex = 0;
|
|
|
+ while (true) {
|
|
|
+ const response = await embyApi.getPlaylistSongsPage(account, playlistId, startIndex, this.REMOTE_PAGE_SIZE);
|
|
|
+ results.push(...response.items);
|
|
|
+ if (response.nextStart === null) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ startIndex = response.nextStart;
|
|
|
+ }
|
|
|
+ return results;
|
|
|
+ }
|
|
|
+
|
|
|
private async fetchAllAudioStationArtistSongs(account: WebDavAccount, artistName: string): Promise<AudioStationSong[]> {
|
|
|
if (!artistName || artistName.trim().length === 0) {
|
|
|
return [];
|
|
|
@@ -2199,6 +2320,10 @@ export struct RemoteMusicPage {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ private async fetchAllJellyfinPlaylistSongs(account: WebDavAccount, playlistId: string): Promise<JellyfinSong[]> {
|
|
|
+ return jellyfinApi.getPlaylistSongs(account, playlistId);
|
|
|
+ }
|
|
|
+
|
|
|
private async fetchAllAudioStationPlaylistSongs(account: WebDavAccount, playlistId: string): Promise<AudioStationSong[]> {
|
|
|
const results: AudioStationSong[] = [];
|
|
|
let startIndex = 0;
|
|
|
@@ -2722,7 +2847,7 @@ export struct RemoteMusicPage {
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
- .visibility(this.selectedTab==0?Visibility.Visible:Visibility.None)
|
|
|
+ .visibility(this.isDetailView ? Visibility.None : Visibility.Visible)
|
|
|
|
|
|
// 排序按钮
|
|
|
TitleBarPointLightButton({
|
|
|
@@ -2923,7 +3048,7 @@ export struct RemoteMusicPage {
|
|
|
this.loadSearchHistory();
|
|
|
this.filteredList = [];
|
|
|
this.isSearchLoading = false;
|
|
|
- this.songDataSource.pushArrayData(this.getVisibleSongs());
|
|
|
+ this.refreshVisibleDataSources();
|
|
|
void ServerLogUtil.info('NavidromeSearch', '搜索已清空,显示所有歌曲');
|
|
|
return;
|
|
|
}
|
|
|
@@ -2931,6 +3056,14 @@ export struct RemoteMusicPage {
|
|
|
if (!this.isSearchMode) {
|
|
|
this.isSearchMode = true;
|
|
|
}
|
|
|
+
|
|
|
+ if (this.selectedTab !== 0) {
|
|
|
+ this.isSearchLoading = false;
|
|
|
+ this.refreshVisibleDataSources();
|
|
|
+ void ServerLogUtil.info('NavidromeSearch', `本地名称过滤完成: "${keyword}"`);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
const account = this.resolveActiveAccount();
|
|
|
if (!account) {
|
|
|
this.filteredList = [];
|
|
|
@@ -2971,7 +3104,7 @@ export struct RemoteMusicPage {
|
|
|
const searchTime = Date.now() - startTime;
|
|
|
if (!restSongs || restSongs.length === 0) {
|
|
|
this.filteredList = [];
|
|
|
- this.songDataSource.pushArrayData(this.getVisibleSongs());
|
|
|
+ this.refreshVisibleDataSources();
|
|
|
void ServerLogUtil.warn('NavidromeSearch', `搜索无结果: "${keyword}"`);
|
|
|
void ServerLogUtil.info('NavidromeSearch', `- 搜索耗时: ${searchTime}ms`);
|
|
|
return;
|
|
|
@@ -2991,7 +3124,7 @@ export struct RemoteMusicPage {
|
|
|
return;
|
|
|
}
|
|
|
this.filteredList = [];
|
|
|
- this.songDataSource.pushArrayData(this.getVisibleSongs());
|
|
|
+ this.refreshVisibleDataSources();
|
|
|
const message = (error as Error).message ?? 'Navidrome 搜索失败';
|
|
|
ToastUtil.showToast(message);
|
|
|
void ServerLogUtil.error('NavidromeSearch', `搜索失败: ${message}`);
|
|
|
@@ -3006,11 +3139,11 @@ export struct RemoteMusicPage {
|
|
|
private getCurrentCount(): number {
|
|
|
switch (this.selectedTab) {
|
|
|
case 1:
|
|
|
- return this.artists.length;
|
|
|
+ return this.getVisibleArtists().length;
|
|
|
case 2:
|
|
|
- return this.albums.length;
|
|
|
+ return this.getVisibleAlbums().length;
|
|
|
case 3:
|
|
|
- return this.playlists.length;
|
|
|
+ return this.getVisiblePlaylists().length;
|
|
|
default:
|
|
|
return this.getVisibleSongs().length;
|
|
|
}
|
|
|
@@ -3019,11 +3152,11 @@ export struct RemoteMusicPage {
|
|
|
private getEmptyTitle(): string {
|
|
|
switch (this.selectedTab) {
|
|
|
case 1:
|
|
|
- return '暂无艺术家';
|
|
|
+ return this.isSearchMode && this.searchText.length > 0 ? '没有匹配的艺术家' : '暂无艺术家';
|
|
|
case 2:
|
|
|
- return '暂无专辑';
|
|
|
+ return this.isSearchMode && this.searchText.length > 0 ? '没有匹配的专辑' : '暂无专辑';
|
|
|
case 3:
|
|
|
- return '暂无歌单';
|
|
|
+ return this.isSearchMode && this.searchText.length > 0 ? '没有匹配的歌单' : '暂无歌单';
|
|
|
default:
|
|
|
if (this.isSearchMode && this.searchText.length > 0) {
|
|
|
return this.isSearchLoading ? '正在搜索远程歌曲' : '没有匹配的远程歌曲';
|
|
|
@@ -3038,11 +3171,11 @@ export struct RemoteMusicPage {
|
|
|
private getEmptySubtitle(): string {
|
|
|
switch (this.selectedTab) {
|
|
|
case 1:
|
|
|
- return '当前筛选没有找到艺术家';
|
|
|
+ return this.isSearchMode && this.searchText.length > 0 ? '换个艺术家关键词再试试吧' : '当前筛选没有找到艺术家';
|
|
|
case 2:
|
|
|
- return '当前筛选没有找到专辑';
|
|
|
+ return this.isSearchMode && this.searchText.length > 0 ? '换个专辑关键词再试试吧' : '当前筛选没有找到专辑';
|
|
|
case 3:
|
|
|
- return '当前筛选没有找到歌单';
|
|
|
+ return this.isSearchMode && this.searchText.length > 0 ? '换个歌单关键词再试试吧' : '当前筛选没有找到歌单';
|
|
|
default:
|
|
|
if (this.isSearchMode && this.searchText.length > 0) {
|
|
|
return this.isSearchLoading ? '请稍候,正在通过 API 搜索' : '换个关键词再试试吧';
|
|
|
@@ -3466,6 +3599,27 @@ export struct RemoteMusicPage {
|
|
|
return this.allVideos;
|
|
|
}
|
|
|
|
|
|
+ private getVisibleArtists(): NavidromeRestArtist[] {
|
|
|
+ if (this.isSearchMode && this.searchText.length > 0 && !this.isDetailView) {
|
|
|
+ return filterArtistsByKeyword(this.artists, this.searchText);
|
|
|
+ }
|
|
|
+ return this.artists;
|
|
|
+ }
|
|
|
+
|
|
|
+ private getVisibleAlbums(): NavidromeRestAlbum[] {
|
|
|
+ if (this.isSearchMode && this.searchText.length > 0 && !this.isDetailView) {
|
|
|
+ return filterAlbumsByKeyword(this.albums, this.searchText);
|
|
|
+ }
|
|
|
+ return this.albums;
|
|
|
+ }
|
|
|
+
|
|
|
+ private getVisiblePlaylists(): NavidromeRestPlaylist[] {
|
|
|
+ if (this.isSearchMode && this.searchText.length > 0 && !this.isDetailView) {
|
|
|
+ return filterPlaylistsByKeyword(this.playlists, this.searchText);
|
|
|
+ }
|
|
|
+ return this.playlists;
|
|
|
+ }
|
|
|
+
|
|
|
private onArtistSelected(artist: NavidromeRestArtist): void {
|
|
|
if (!artist || !artist.id) {
|
|
|
return;
|
|
|
@@ -3587,7 +3741,13 @@ export struct RemoteMusicPage {
|
|
|
restSongs = this.convertDaoLiYuSongsToRestSongs(songs);
|
|
|
}
|
|
|
} else if (this.filterType === NavFilterType.Playlist) {
|
|
|
- if (this.isAudioStationAccount(account)) {
|
|
|
+ if (this.isJellyfinAccount(account)) {
|
|
|
+ const songs = await this.fetchAllJellyfinPlaylistSongs(account, this.filterId);
|
|
|
+ restSongs = this.convertJellyfinSongsToRestSongs(songs);
|
|
|
+ } else if (this.isEmbyAccount(account)) {
|
|
|
+ const songs = await this.fetchAllEmbyPlaylistSongs(account, this.filterId);
|
|
|
+ restSongs = this.convertEmbySongsToRestSongs(songs);
|
|
|
+ } else if (this.isAudioStationAccount(account)) {
|
|
|
const songs = await this.fetchAllAudioStationPlaylistSongs(account, this.filterId);
|
|
|
restSongs = this.convertAudioStationSongsToRestSongs(songs);
|
|
|
} else if (this.isPlexAccount(account)) {
|
|
|
@@ -3682,22 +3842,16 @@ export struct RemoteMusicPage {
|
|
|
if (this.serverTotalSongCount > 0) {
|
|
|
setNavidromeTotalCount(this.serverTotalSongCount);
|
|
|
}
|
|
|
-
|
|
|
- const playlistData = new PlaylistPlayRequest(
|
|
|
- NAVIDROME_PLAYLIST_ID,
|
|
|
- `${getRemoteDriveDisplayLabel(account.webType)} - ${account.name ?? '未知账户'}`,
|
|
|
- this.serverTotalSongCount > 0 ? this.serverTotalSongCount : playlistSource.length,
|
|
|
+ void this.playbackController.playQueue(
|
|
|
+ playlistSource.slice(),
|
|
|
startIndex,
|
|
|
- playlistSource.map(item => item.filePath),
|
|
|
- isJump
|
|
|
- );
|
|
|
-
|
|
|
- const eventPlaylistPlay: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_PLAY };
|
|
|
- savePendingPlaylistPlay(playlistData);
|
|
|
- emitter.emit(eventPlaylistPlay, { data: playlistData });
|
|
|
-
|
|
|
- void ServerLogUtil.info('StreamingPlay', `播放事件已发送`);
|
|
|
- void ServerLogUtil.debug('StreamingPlay', `播放事件详情: 列表ID=${playlistData.playlistId}, 列表名称=${playlistData.playlistName}, 歌曲数=${playlistData.songCount}, 起始索引=${playlistData.startIndex}, 是否跳转=${playlistData.isJump}`);
|
|
|
+ isJump ? 'controller-open' : 'remote-music'
|
|
|
+ ).then(() => {
|
|
|
+ void ServerLogUtil.info('StreamingPlay', '播放请求已通过 controller 发送');
|
|
|
+ }).catch((error: Error) => {
|
|
|
+ void ServerLogUtil.error('StreamingPlay', `播放失败: ${error.message}`);
|
|
|
+ ToastUtil.showToast('播放失败');
|
|
|
+ });
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
@@ -3846,7 +4000,7 @@ export struct RemoteMusicPage {
|
|
|
}
|
|
|
|
|
|
|
|
|
- if (this.selectedTab === 0||this.isDetailView) {
|
|
|
+ if (shouldRenderRemoteMusicSongView(this.selectedTab, this.isDetailView)) {
|
|
|
LazyForEach(this.songDataSource, (item: VideoItem, index: number) => {
|
|
|
GridItem() {
|
|
|
this.buildSongItemGrid(item, index)
|
|
|
@@ -4419,7 +4573,7 @@ export struct RemoteMusicPage {
|
|
|
getListView() {
|
|
|
List({scroller:this.scroller, space: 8 }) {
|
|
|
// 详情视图模式下显示筛选后的歌曲,否则根据标签页显示对应内容
|
|
|
- if (this.selectedTab === 0||this.isDetailView) {
|
|
|
+ if (shouldRenderRemoteMusicSongView(this.selectedTab, this.isDetailView)) {
|
|
|
LazyForEach(this.songDataSource, (item: VideoItem, index: number) => {
|
|
|
ListItem() {
|
|
|
this.buildSongItem(item, index)
|
|
|
@@ -4582,7 +4736,7 @@ export struct RemoteMusicPage {
|
|
|
layoutMode: WaterFlowLayoutMode.SLIDING_WINDOW
|
|
|
}) {
|
|
|
|
|
|
- if (this.selectedTab === 0) {
|
|
|
+ if (shouldRenderRemoteMusicSongView(this.selectedTab, this.isDetailView)) {
|
|
|
// 歌曲瀑布流
|
|
|
LazyForEach(this.songDataSource, (item: VideoItem, index: number) => {
|
|
|
FlowItem() {
|