|
|
@@ -18,7 +18,8 @@ import { Utility } from '../common/util/Utility';
|
|
|
import { Constants } from '../Constants';
|
|
|
import { EventConstants } from '../common/constants/EventConstants';
|
|
|
import { emitter } from '@kit.BasicServicesKit';
|
|
|
-import { setNavidromePlaylist } from '../common/util/NavidromePlaylistStore';
|
|
|
+import { setNavidromePlaylist, appendToNavidromePlaylist, setNavidromeTotalCount } from '../common/util/NavidromePlaylistStore';
|
|
|
+import { registerLoadMoreCallback, unregisterLoadMoreCallback } from '../common/util/NavidromeRandomLoader';
|
|
|
import { navidromeApi, NavidromeSong } from '../common/network/NavidromeApi';
|
|
|
import { ServerLogUtil } from '../common/util/ServerLogUtil';
|
|
|
import { RemoteDriveManager } from '../common/util/RemoteDriveManager';
|
|
|
@@ -108,7 +109,7 @@ interface AccountData {
|
|
|
}
|
|
|
|
|
|
@Component
|
|
|
-export struct NavidromePage {
|
|
|
+export struct RemoteMusicPage {
|
|
|
@State isShowTitleBar: boolean = true //是否显示分类导航条
|
|
|
private scroller: Scroller = new Scroller()
|
|
|
private prevOffsetY: number = 0; // 记录上一次的Y轴偏移量
|
|
|
@@ -139,6 +140,7 @@ export struct NavidromePage {
|
|
|
@State isArtistPageLoading: boolean = false;
|
|
|
@State isAlbumPageLoading: boolean = false;
|
|
|
@State isPlaylistPageLoading: boolean = false;
|
|
|
+ @State serverTotalSongCount: number = 0; // 服务端返回的歌曲总数
|
|
|
|
|
|
// 新增:详情视图状态
|
|
|
@Link isDetailView: boolean; // 是否在艺术家/专辑详情视图
|
|
|
@@ -228,7 +230,7 @@ export struct NavidromePage {
|
|
|
const examplePath = `${cacheRoot}/navidrome_preview.cache`;
|
|
|
return { cacheRoot, examplePath };
|
|
|
} catch (error) {
|
|
|
- Logger.error('NavidromePage', `获取缓存预览路径失败: ${(error as Error).message}`);
|
|
|
+ Logger.error('RemoteMusicPage', `获取缓存预览路径失败: ${(error as Error).message}`);
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
|
@@ -269,7 +271,7 @@ export struct NavidromePage {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- //切换不同的NavidromePage
|
|
|
+ //切换不同的RemoteMusicPage
|
|
|
async onSwitchAccount() {
|
|
|
await this.refreshNavidromeData(true);
|
|
|
}
|
|
|
@@ -296,7 +298,7 @@ export struct NavidromePage {
|
|
|
// 监听手势返回事件
|
|
|
let eventBackSwipeBack: emitter.InnerEvent = { eventId: EventConstants.EVENT_SWIPE_BACK_NAVID }
|
|
|
emitter.on(eventBackSwipeBack, (eventData: emitter.EventData) => {
|
|
|
- console.info('heanup', 'NavidromePage 收到 EVENT_SWIPE_BACK_NAVID 事件');
|
|
|
+ console.info('heanup', 'RemoteMusicPage 收到 EVENT_SWIPE_BACK_NAVID 事件');
|
|
|
// 如果在详情视图模式,退出详情视图
|
|
|
if (this.isDetailView) {
|
|
|
this.getUIContext().animateTo({ duration: 555 }, () => {
|
|
|
@@ -306,9 +308,68 @@ export struct NavidromePage {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
+ // 注册加载更多页回调(用于渐进式随机播放)
|
|
|
+ this.registerRandomLoadCallback();
|
|
|
+
|
|
|
this.refreshNavidromeData();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 注册加载更多页回调函数(供LocalMusic的randomPlay调用)
|
|
|
+ */
|
|
|
+ private registerRandomLoadCallback(): void {
|
|
|
+ registerLoadMoreCallback(
|
|
|
+ // 加载一页数据并返回新增歌曲
|
|
|
+ async (): Promise<VideoItem[]> => {
|
|
|
+ const account = this.resolveActiveAccount();
|
|
|
+ if (!account) {
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 记录加载前的数量
|
|
|
+ const beforeCount = this.allVideos.length;
|
|
|
+
|
|
|
+ // 根据账户类型调用对应的加载方法
|
|
|
+ await this.loadNextSongPageByAccountType(account);
|
|
|
+
|
|
|
+ // 获取新增的歌曲
|
|
|
+ const newItems = this.allVideos.slice(beforeCount);
|
|
|
+
|
|
|
+ // 同步到播放列表存储
|
|
|
+ if (newItems.length > 0) {
|
|
|
+ appendToNavidromePlaylist(newItems);
|
|
|
+ void ServerLogUtil.info('NavidromeRandom', `加载更多: 新增${newItems.length}首,总数${this.allVideos.length}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ return newItems;
|
|
|
+ },
|
|
|
+ // 检查是否还有更多数据
|
|
|
+ (): boolean => {
|
|
|
+ return this.songNextStart !== null && !this.isSongPageLoading;
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据账户类型调用对应的歌曲加载方法
|
|
|
+ */
|
|
|
+ private async loadNextSongPageByAccountType(account: WebDavAccount): Promise<void> {
|
|
|
+ if (this.isJellyfinAccount(account)) {
|
|
|
+ await this.loadNextJellyfinSongPage(account);
|
|
|
+ } else if (this.isEmbyAccount(account)) {
|
|
|
+ await this.loadNextEmbySongPage(account);
|
|
|
+ } else if (this.isAudioStationAccount(account)) {
|
|
|
+ await this.loadNextAudioStationSongPage(account);
|
|
|
+ } else if (this.isPlexAccount(account)) {
|
|
|
+ await this.loadNextPlexSongPage(account);
|
|
|
+ } else if (this.isDaoLiYuAccount(account)) {
|
|
|
+ await this.loadNextDaoLiYuSongPage(account);
|
|
|
+ } else {
|
|
|
+ // 默认使用 Navidrome
|
|
|
+ await this.loadNextSongPage(account);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private async refreshNavidromeData(showToastWhenMissing: boolean = false): Promise<void> {
|
|
|
const account = this.resolveActiveAccount();
|
|
|
if (!account) {
|
|
|
@@ -367,6 +428,7 @@ export struct NavidromePage {
|
|
|
this.isArtistPageLoading = false;
|
|
|
this.isAlbumPageLoading = false;
|
|
|
this.isPlaylistPageLoading = false;
|
|
|
+ this.serverTotalSongCount = 0;
|
|
|
}
|
|
|
|
|
|
private async loadMediaLibrary(account: WebDavAccount): Promise<void> {
|
|
|
@@ -808,12 +870,18 @@ export struct NavidromePage {
|
|
|
return;
|
|
|
}
|
|
|
const currentTicket = ticket ?? this.loadTicket;
|
|
|
+ const isFirstPage = this.songNextStart === 0;
|
|
|
this.isSongPageLoading = true;
|
|
|
try {
|
|
|
const response = await jellyfinApi.getSongsPage(account, this.songNextStart, this.REMOTE_PAGE_SIZE);
|
|
|
if (currentTicket !== this.loadTicket) {
|
|
|
return;
|
|
|
}
|
|
|
+ // 首次加载时保存服务端总数
|
|
|
+ if (isFirstPage && response.total !== undefined && response.total > 0) {
|
|
|
+ this.serverTotalSongCount = response.total;
|
|
|
+ void ServerLogUtil.info('NavidromeLoad', `Jellyfin 服务端歌曲总数: ${this.serverTotalSongCount}`);
|
|
|
+ }
|
|
|
const uniqueItems: JellyfinSong[] = [];
|
|
|
for (let i = 0; i < response.items.length; i++) {
|
|
|
const item = response.items[i];
|
|
|
@@ -835,7 +903,7 @@ export struct NavidromePage {
|
|
|
}
|
|
|
this.allVideos = this.dedupRemoteSongsByKey([...this.allVideos, ...videoItems]);
|
|
|
this.songNextStart = response.nextStart;
|
|
|
- void ServerLogUtil.info('NavidromeLoad', `Jellyfin 歌曲追加: 本次 ${videoItems.length} 首, 总数 ${this.allVideos.length}`);
|
|
|
+ void ServerLogUtil.info('NavidromeLoad', `Jellyfin 歌曲追加: 本次 ${videoItems.length} 首, 已加载 ${this.allVideos.length}, 服务端总数 ${this.serverTotalSongCount}`);
|
|
|
} finally {
|
|
|
this.isSongPageLoading = false;
|
|
|
}
|
|
|
@@ -892,12 +960,18 @@ export struct NavidromePage {
|
|
|
return;
|
|
|
}
|
|
|
const currentTicket = ticket ?? this.loadTicket;
|
|
|
+ const isFirstPage = this.songNextStart === 0;
|
|
|
this.isSongPageLoading = true;
|
|
|
try {
|
|
|
const response = await embyApi.getSongsPage(account, this.songNextStart, this.REMOTE_PAGE_SIZE);
|
|
|
if (currentTicket !== this.loadTicket) {
|
|
|
return;
|
|
|
}
|
|
|
+ // 首次加载时保存服务端总数
|
|
|
+ if (isFirstPage && response.total !== undefined && response.total > 0) {
|
|
|
+ this.serverTotalSongCount = response.total;
|
|
|
+ void ServerLogUtil.info('NavidromeLoad', `Emby 服务端歌曲总数: ${this.serverTotalSongCount}`);
|
|
|
+ }
|
|
|
const uniqueItems: EmbySong[] = [];
|
|
|
for (let i = 0; i < response.items.length; i++) {
|
|
|
const item = response.items[i];
|
|
|
@@ -919,7 +993,7 @@ export struct NavidromePage {
|
|
|
}
|
|
|
this.allVideos = this.dedupRemoteSongsByKey([...this.allVideos, ...videoItems]);
|
|
|
this.songNextStart = response.nextStart;
|
|
|
- void ServerLogUtil.info('NavidromeLoad', `Emby 歌曲追加: 本次 ${videoItems.length} 首, 总数 ${this.allVideos.length}`);
|
|
|
+ void ServerLogUtil.info('NavidromeLoad', `Emby 歌曲追加: 本次 ${videoItems.length} 首, 已加载 ${this.allVideos.length}, 服务端总数 ${this.serverTotalSongCount}`);
|
|
|
} finally {
|
|
|
this.isSongPageLoading = false;
|
|
|
}
|
|
|
@@ -976,12 +1050,18 @@ export struct NavidromePage {
|
|
|
return;
|
|
|
}
|
|
|
const currentTicket = ticket ?? this.loadTicket;
|
|
|
+ const isFirstPage = this.songNextStart === 0; // 在调用前保存是否为首页
|
|
|
this.isSongPageLoading = true;
|
|
|
try {
|
|
|
const response = await audioStationApi.getSongsPage(account, this.songNextStart, this.REMOTE_PAGE_SIZE);
|
|
|
if (currentTicket !== this.loadTicket) {
|
|
|
return;
|
|
|
}
|
|
|
+ // 首次加载时保存服务端总数
|
|
|
+ if (isFirstPage && response.total !== undefined && response.total > 0) {
|
|
|
+ this.serverTotalSongCount = response.total;
|
|
|
+ void ServerLogUtil.info('NavidromeLoad', `AudioStation 服务端歌曲总数: ${this.serverTotalSongCount}`);
|
|
|
+ }
|
|
|
const restSongs = this.convertAudioStationSongsToRestSongs(response.items);
|
|
|
const videoItems = await this.convertSongsToVideoItems(restSongs, account);
|
|
|
if (currentTicket !== this.loadTicket) {
|
|
|
@@ -989,7 +1069,7 @@ export struct NavidromePage {
|
|
|
}
|
|
|
this.allVideos = this.dedupRemoteSongsByKey([...this.allVideos, ...videoItems]);
|
|
|
this.songNextStart = response.nextStart;
|
|
|
- void ServerLogUtil.info('NavidromeLoad', `AudioStation 歌曲追加: 本次 ${videoItems.length} 首, 总数 ${this.allVideos.length}`);
|
|
|
+ void ServerLogUtil.info('NavidromeLoad', `AudioStation 歌曲追加: 本次 ${videoItems.length} 首, 已加载 ${this.allVideos.length}, 服务端总数 ${this.serverTotalSongCount}`);
|
|
|
} finally {
|
|
|
this.isSongPageLoading = false;
|
|
|
}
|
|
|
@@ -1069,12 +1149,18 @@ export struct NavidromePage {
|
|
|
return;
|
|
|
}
|
|
|
const currentTicket = ticket ?? this.loadTicket;
|
|
|
+ const isFirstPage = this.songNextStart === 0;
|
|
|
this.isSongPageLoading = true;
|
|
|
try {
|
|
|
const response = await plexApi.getSongsPage(account, this.songNextStart, this.REMOTE_PAGE_SIZE);
|
|
|
if (currentTicket !== this.loadTicket) {
|
|
|
return;
|
|
|
}
|
|
|
+ // 首次加载时保存服务端总数
|
|
|
+ if (isFirstPage && response.total !== undefined && response.total > 0) {
|
|
|
+ this.serverTotalSongCount = response.total;
|
|
|
+ void ServerLogUtil.info('NavidromeLoad', `Plex 服务端歌曲总数: ${this.serverTotalSongCount}`);
|
|
|
+ }
|
|
|
const restSongs = this.convertPlexSongsToRestSongs(response.items);
|
|
|
const videoItems = await this.convertSongsToVideoItems(restSongs, account);
|
|
|
if (currentTicket !== this.loadTicket) {
|
|
|
@@ -1082,7 +1168,7 @@ export struct NavidromePage {
|
|
|
}
|
|
|
this.allVideos = this.dedupRemoteSongsByKey([...this.allVideos, ...videoItems]);
|
|
|
this.songNextStart = response.nextStart;
|
|
|
- void ServerLogUtil.info('NavidromeLoad', `Plex 歌曲追加: 本次 ${videoItems.length} 首, 总数 ${this.allVideos.length}`);
|
|
|
+ void ServerLogUtil.info('NavidromeLoad', `Plex 歌曲追加: 本次 ${videoItems.length} 首, 已加载 ${this.allVideos.length}, 服务端总数 ${this.serverTotalSongCount}`);
|
|
|
} finally {
|
|
|
this.isSongPageLoading = false;
|
|
|
}
|
|
|
@@ -1162,12 +1248,18 @@ export struct NavidromePage {
|
|
|
return;
|
|
|
}
|
|
|
const currentTicket = ticket ?? this.loadTicket;
|
|
|
+ const isFirstPage = this.songNextStart === 0;
|
|
|
this.isSongPageLoading = true;
|
|
|
try {
|
|
|
const response = await daoLiYuApi.getTracksPage(account, this.songNextStart, this.REMOTE_PAGE_SIZE);
|
|
|
if (currentTicket !== this.loadTicket) {
|
|
|
return;
|
|
|
}
|
|
|
+ // 首次加载时保存服务端总数
|
|
|
+ if (isFirstPage && response.total !== undefined && response.total > 0) {
|
|
|
+ this.serverTotalSongCount = response.total;
|
|
|
+ void ServerLogUtil.info('NavidromeLoad', `道理鱼 服务端歌曲总数: ${this.serverTotalSongCount}`);
|
|
|
+ }
|
|
|
const restSongs = this.convertDaoLiYuSongsToRestSongs(response.items);
|
|
|
const videoItems = await this.convertSongsToVideoItems(restSongs, account);
|
|
|
if (currentTicket !== this.loadTicket) {
|
|
|
@@ -1175,7 +1267,7 @@ export struct NavidromePage {
|
|
|
}
|
|
|
this.allVideos = [...this.allVideos, ...videoItems];
|
|
|
this.songNextStart = response.nextStart;
|
|
|
- void ServerLogUtil.info('NavidromeLoad', `道理鱼 歌曲追加: 本次 ${videoItems.length} 首, 总数 ${this.allVideos.length}`);
|
|
|
+ void ServerLogUtil.info('NavidromeLoad', `道理鱼 歌曲追加: 本次 ${videoItems.length} 首, 已加载 ${this.allVideos.length}, 服务端总数 ${this.serverTotalSongCount}`);
|
|
|
} finally {
|
|
|
this.isSongPageLoading = false;
|
|
|
}
|
|
|
@@ -1401,11 +1493,11 @@ export struct NavidromePage {
|
|
|
let generatedCoverCount = 0;
|
|
|
let failedCoverCount = 0;
|
|
|
|
|
|
- void ServerLogUtil.info('NavidromePageCover', `📀 开始处理 ${albums.length} 张专辑的封面 - 账号: ${ServerLogUtil.sanitizeAccount(account)}`);
|
|
|
+ void ServerLogUtil.info('RemoteMusicPageCover', `📀 开始处理 ${albums.length} 张专辑的封面 - 账号: ${ServerLogUtil.sanitizeAccount(account)}`);
|
|
|
|
|
|
for (let i = 0; i < albums.length; i++) {
|
|
|
const album = albums[i];
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `处理专辑 [${i + 1}/${albums.length}] - id: ${album.id}, name: ${album.name}`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `处理专辑 [${i + 1}/${albums.length}] - id: ${album.id}, name: ${album.name}`);
|
|
|
|
|
|
tasks.push((async () => {
|
|
|
try {
|
|
|
@@ -1415,33 +1507,33 @@ export struct NavidromePage {
|
|
|
// if (directUrl) {
|
|
|
// map.set(album.id, directUrl);
|
|
|
// directCoverCount++;
|
|
|
- // void ServerLogUtil.info('NavidromePageCover', `✅ 专辑使用直接封面 - name: ${album.name}, url: ${directUrl}`);
|
|
|
+ // void ServerLogUtil.info('RemoteMusicPageCover', `✅ 专辑使用直接封面 - name: ${album.name}, url: ${directUrl}`);
|
|
|
// return;
|
|
|
// }
|
|
|
|
|
|
// 生成封面URL
|
|
|
const coverId = album.coverArt ?? album.coverArtId ?? (album.id ? `al-${album.id}` : undefined);
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `专辑封面ID - name: ${album.name}, coverArt: ${album.coverArt}, coverArtId: ${album.coverArtId}, 最终coverId: ${coverId}`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `专辑封面ID - name: ${album.name}, coverArt: ${album.coverArt}, coverArtId: ${album.coverArtId}, 最终coverId: ${coverId}`);
|
|
|
|
|
|
const url = await this.buildCoverUrl(account, coverId);
|
|
|
if (url) {
|
|
|
map.set(album.id, url);
|
|
|
generatedCoverCount++;
|
|
|
- void ServerLogUtil.info('NavidromePageCover', `✅ 专辑生成封面成功 - name: ${album.name}, coverId: ${coverId}, url: ${url}`);
|
|
|
+ void ServerLogUtil.info('RemoteMusicPageCover', `✅ 专辑生成封面成功 - name: ${album.name}, coverId: ${coverId}, url: ${url}`);
|
|
|
} else {
|
|
|
failedCoverCount++;
|
|
|
- void ServerLogUtil.error('NavidromePageCover', `❌ 专辑封面失败 - name: ${album.name}, coverId: ${coverId}, 原始数据: ${JSON.stringify({coverArt: album.coverArt, coverArtId: album.coverArtId, id: album.id})}`);
|
|
|
+ void ServerLogUtil.error('RemoteMusicPageCover', `❌ 专辑封面失败 - name: ${album.name}, coverId: ${coverId}, 原始数据: ${JSON.stringify({coverArt: album.coverArt, coverArtId: album.coverArtId, id: album.id})}`);
|
|
|
}
|
|
|
} catch (error) {
|
|
|
failedCoverCount++;
|
|
|
- void ServerLogUtil.error('NavidromePageCover', `❌ 专辑封面解析异常 - name: ${album.name}, error: ${(error as Error).message}`);
|
|
|
+ void ServerLogUtil.error('RemoteMusicPageCover', `❌ 专辑封面解析异常 - name: ${album.name}, error: ${(error as Error).message}`);
|
|
|
}
|
|
|
})());
|
|
|
}
|
|
|
|
|
|
await Promise.all(tasks);
|
|
|
|
|
|
- void ServerLogUtil.info('NavidromePageCover', `📊 专辑封面处理完成 - 总数: ${albums.length}, 直接嵌入: ${directCoverCount}, 生成URL: ${generatedCoverCount}, 失败: ${failedCoverCount}`);
|
|
|
+ void ServerLogUtil.info('RemoteMusicPageCover', `📊 专辑封面处理完成 - 总数: ${albums.length}, 直接嵌入: ${directCoverCount}, 生成URL: ${generatedCoverCount}, 失败: ${failedCoverCount}`);
|
|
|
return map;
|
|
|
}
|
|
|
|
|
|
@@ -1452,11 +1544,11 @@ export struct NavidromePage {
|
|
|
let generatedCoverCount = 0;
|
|
|
let failedCoverCount = 0;
|
|
|
|
|
|
- void ServerLogUtil.info('NavidromePageCover', `🎤 开始处理 ${artists.length} 位艺术家的封面 - 账号: ${ServerLogUtil.sanitizeAccount(account)}`);
|
|
|
+ void ServerLogUtil.info('RemoteMusicPageCover', `🎤 开始处理 ${artists.length} 位艺术家的封面 - 账号: ${ServerLogUtil.sanitizeAccount(account)}`);
|
|
|
|
|
|
for (let i = 0; i < artists.length; i++) {
|
|
|
const artist = artists[i];
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `处理艺术家 [${i + 1}/${artists.length}] - id: ${artist.id}, name: ${artist.name}`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `处理艺术家 [${i + 1}/${artists.length}] - id: ${artist.id}, name: ${artist.name}`);
|
|
|
|
|
|
tasks.push((async () => {
|
|
|
try {
|
|
|
@@ -1465,33 +1557,33 @@ export struct NavidromePage {
|
|
|
if (directUrl) {
|
|
|
map.set(artist.id, directUrl);
|
|
|
directCoverCount++;
|
|
|
- void ServerLogUtil.info('NavidromePageCover', `✅ 艺术家使用已有图片 - name: ${artist.name}, url: ${directUrl}`);
|
|
|
+ void ServerLogUtil.info('RemoteMusicPageCover', `✅ 艺术家使用已有图片 - name: ${artist.name}, url: ${directUrl}`);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 生成封面URL
|
|
|
const coverId = artist.coverArt ?? artist.coverArtId ?? (artist.id ? `ar-${artist.id}` : undefined);
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `艺术家封面ID - name: ${artist.name}, coverArt: ${artist.coverArt}, coverArtId: ${artist.coverArtId}, 最终coverId: ${coverId}`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `艺术家封面ID - name: ${artist.name}, coverArt: ${artist.coverArt}, coverArtId: ${artist.coverArtId}, 最终coverId: ${coverId}`);
|
|
|
|
|
|
const url = await this.buildCoverUrl(account, coverId, 256);
|
|
|
if (url) {
|
|
|
map.set(artist.id, url);
|
|
|
generatedCoverCount++;
|
|
|
- void ServerLogUtil.info('NavidromePageCover', `✅ 艺术家生成封面成功 - name: ${artist.name}, coverId: ${coverId}, url: ${url}`);
|
|
|
+ void ServerLogUtil.info('RemoteMusicPageCover', `✅ 艺术家生成封面成功 - name: ${artist.name}, coverId: ${coverId}, url: ${url}`);
|
|
|
} else {
|
|
|
failedCoverCount++;
|
|
|
- void ServerLogUtil.error('NavidromePageCover', `❌ 艺术家封面失败 - name: ${artist.name}, coverId: ${coverId}, 原始数据: ${JSON.stringify({coverArt: artist.coverArt, coverArtId: artist.coverArtId, id: artist.id})}`);
|
|
|
+ void ServerLogUtil.error('RemoteMusicPageCover', `❌ 艺术家封面失败 - name: ${artist.name}, coverId: ${coverId}, 原始数据: ${JSON.stringify({coverArt: artist.coverArt, coverArtId: artist.coverArtId, id: artist.id})}`);
|
|
|
}
|
|
|
} catch (error) {
|
|
|
failedCoverCount++;
|
|
|
- void ServerLogUtil.error('NavidromePageCover', `❌ 艺术家封面解析异常 - name: ${artist.name}, error: ${(error as Error).message}`);
|
|
|
+ void ServerLogUtil.error('RemoteMusicPageCover', `❌ 艺术家封面解析异常 - name: ${artist.name}, error: ${(error as Error).message}`);
|
|
|
}
|
|
|
})());
|
|
|
}
|
|
|
|
|
|
await Promise.all(tasks);
|
|
|
|
|
|
- void ServerLogUtil.info('NavidromePageCover', `📊 艺术家封面处理完成 - 总数: ${artists.length}, 直接链接: ${directCoverCount}, 生成URL: ${generatedCoverCount}, 失败: ${failedCoverCount}`);
|
|
|
+ void ServerLogUtil.info('RemoteMusicPageCover', `📊 艺术家封面处理完成 - 总数: ${artists.length}, 直接链接: ${directCoverCount}, 生成URL: ${generatedCoverCount}, 失败: ${failedCoverCount}`);
|
|
|
return map;
|
|
|
}
|
|
|
|
|
|
@@ -2042,39 +2134,39 @@ export struct NavidromePage {
|
|
|
}
|
|
|
|
|
|
private async resolveSongCover(song: NavidromeRestSong, account: WebDavAccount): Promise<string | undefined> {
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `🎵 解析歌曲封面 - title: ${song.title}, albumId: ${song.albumId}, id: ${song.id}`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `🎵 解析歌曲封面 - title: ${song.title}, albumId: ${song.albumId}, id: ${song.id}`);
|
|
|
|
|
|
// 注释掉这个代码可以解决NavidRome部分账号没有封面问题
|
|
|
// if (this.isNavidromeAccount(account) && song.albumId) {
|
|
|
// const albumCover = song.albumId ? this.albumCoverLookup.get(song.albumId) : undefined;
|
|
|
// if (albumCover) {
|
|
|
- // void ServerLogUtil.debug('NavidromePageCover', `✅ 歌曲使用专辑封面缓存 - title: ${song.title}, albumCover: ${albumCover}`);
|
|
|
+ // void ServerLogUtil.debug('RemoteMusicPageCover', `✅ 歌曲使用专辑封面缓存 - title: ${song.title}, albumCover: ${albumCover}`);
|
|
|
// return albumCover;
|
|
|
// }
|
|
|
// }
|
|
|
|
|
|
if (!this.isNavidromeAccount(account)) {
|
|
|
const fallbackId = song.coverArtId ?? song.albumId ?? song.id;
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `歌曲使用非Navidrome账号封面 - title: ${song.title}, fallbackId: ${fallbackId}`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `歌曲使用非Navidrome账号封面 - title: ${song.title}, fallbackId: ${fallbackId}`);
|
|
|
return this.buildCoverUrl(account, fallbackId);
|
|
|
}
|
|
|
|
|
|
const directUrl = this.resolveEmbedCover(account, song.embedArtPath ?? song.coverArtPath);
|
|
|
if (directUrl) {
|
|
|
- void ServerLogUtil.info('NavidromePageCover', `✅ 歌曲使用直接封面 - title: ${song.title}, url: ${directUrl}`);
|
|
|
+ void ServerLogUtil.info('RemoteMusicPageCover', `✅ 歌曲使用直接封面 - title: ${song.title}, url: ${directUrl}`);
|
|
|
return directUrl;
|
|
|
}
|
|
|
|
|
|
const coverId = song.coverArt ?? song.coverArtId ?? song.id;
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `歌曲生成封面URL - title: ${song.title}, coverArt: ${song.coverArt}, coverArtId: ${song.coverArtId}, 最终coverId: ${coverId}`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `歌曲生成封面URL - title: ${song.title}, coverArt: ${song.coverArt}, coverArtId: ${song.coverArtId}, 最终coverId: ${coverId}`);
|
|
|
return this.buildCoverUrl(account, coverId);
|
|
|
}
|
|
|
|
|
|
private async buildCoverUrl(account: WebDavAccount, coverId?: string, size: number = 300): Promise<string | undefined> {
|
|
|
- void ServerLogUtil.info('NavidromePageCover', `开始构建封面URL - coverId: ${coverId}, size: ${size}, 账号: ${ServerLogUtil.sanitizeAccount(account)}`);
|
|
|
+ void ServerLogUtil.info('RemoteMusicPageCover', `开始构建封面URL - coverId: ${coverId}, size: ${size}, 账号: ${ServerLogUtil.sanitizeAccount(account)}`);
|
|
|
|
|
|
if (!coverId || coverId.trim().length === 0) {
|
|
|
- void ServerLogUtil.warn('NavidromePageCover', `封面ID为空,跳过构建 - coverId: "${coverId}"`);
|
|
|
+ void ServerLogUtil.warn('RemoteMusicPageCover', `封面ID为空,跳过构建 - coverId: "${coverId}"`);
|
|
|
return undefined;
|
|
|
}
|
|
|
|
|
|
@@ -2084,67 +2176,67 @@ export struct NavidromePage {
|
|
|
if (this.isNavidromeAccount(account)) {
|
|
|
const cached = this.coverUrlCache.get(cacheKey);
|
|
|
if (cached) {
|
|
|
- void ServerLogUtil.info('NavidromePageCover', `✅ 缓存命中 - coverId: ${coverId}, size: ${size}, url: ${cached}`);
|
|
|
+ void ServerLogUtil.info('RemoteMusicPageCover', `✅ 缓存命中 - coverId: ${coverId}, size: ${size}, url: ${cached}`);
|
|
|
return cached;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- void ServerLogUtil.info('NavidromePageCover', `⚡ 调用API生成封面URL - coverId: ${coverId}, size: ${size}`);
|
|
|
+ void ServerLogUtil.info('RemoteMusicPageCover', `⚡ 调用API生成封面URL - coverId: ${coverId}, size: ${size}`);
|
|
|
let url: string | undefined = undefined;
|
|
|
|
|
|
try {
|
|
|
if (this.isNavidromeAccount(account)) {
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `[开始] 调用 navidromeApi.buildCoverArtUrl - coverId: ${normalizedId}, size: ${size}`);
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `[账号信息] host=${account.host}, port=${account.port}, enableHttps=${account.enableHttps}`);
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `[账号信息] basePath=${account.navidromeBasePath}`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `[开始] 调用 navidromeApi.buildCoverArtUrl - coverId: ${normalizedId}, size: ${size}`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `[账号信息] host=${account.host}, port=${account.port}, enableHttps=${account.enableHttps}`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `[账号信息] basePath=${account.navidromeBasePath}`);
|
|
|
|
|
|
url = await navidromeApi.buildCoverArtUrl(account, normalizedId, size);
|
|
|
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `[完成] navidromeApi.buildCoverArtUrl 返回 - coverId: ${normalizedId}, 返回值: "${url}"`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `[完成] navidromeApi.buildCoverArtUrl 返回 - coverId: ${normalizedId}, 返回值: "${url}"`);
|
|
|
|
|
|
if (url) {
|
|
|
- void ServerLogUtil.info('NavidromePageCover', `✅ API返回URL成功 - coverId: ${coverId}, url: ${url}`);
|
|
|
+ void ServerLogUtil.info('RemoteMusicPageCover', `✅ API返回URL成功 - coverId: ${coverId}, url: ${url}`);
|
|
|
} else {
|
|
|
- void ServerLogUtil.warn('NavidromePageCover', `⚠️ API返回空值 - coverId: ${coverId}`);
|
|
|
+ void ServerLogUtil.warn('RemoteMusicPageCover', `⚠️ API返回空值 - coverId: ${coverId}`);
|
|
|
}
|
|
|
} else if (this.isJellyfinAccount(account)) {
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `调用 jellyfinApi.buildPrimaryImageUrl - coverId: ${normalizedId}, size: ${size}`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `调用 jellyfinApi.buildPrimaryImageUrl - coverId: ${normalizedId}, size: ${size}`);
|
|
|
url = await jellyfinApi.buildPrimaryImageUrl(account, normalizedId, size, size);
|
|
|
} else if (this.isEmbyAccount(account)) {
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `调用 embyApi.buildPrimaryImageUrl - coverId: ${normalizedId}, size: ${size}`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `调用 embyApi.buildPrimaryImageUrl - coverId: ${normalizedId}, size: ${size}`);
|
|
|
url = await embyApi.buildPrimaryImageUrl(account, normalizedId, size, size);
|
|
|
} else if (this.isAudioStationAccount(account)) {
|
|
|
if (this.isAudioStationSongCoverId(normalizedId)) {
|
|
|
const songId = this.stripAudioStationSongCoverId(normalizedId);
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `调用 audioStationApi.buildSongCoverUrl - songId=${songId}`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `调用 audioStationApi.buildSongCoverUrl - songId=${songId}`);
|
|
|
url = await audioStationApi.buildSongCoverUrl(account, songId);
|
|
|
} else {
|
|
|
const key = this.parseAudioStationAlbumKey(normalizedId);
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `调用 audioStationApi.buildAlbumCoverUrl - album=${key.name}, artist=${key.artist}`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `调用 audioStationApi.buildAlbumCoverUrl - album=${key.name}, artist=${key.artist}`);
|
|
|
url = await audioStationApi.buildAlbumCoverUrl(account, key.name, key.artist);
|
|
|
}
|
|
|
} else if (this.isPlexAccount(account)) {
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `调用 plexApi.buildImageUrl - coverId: ${normalizedId}`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `调用 plexApi.buildImageUrl - coverId: ${normalizedId}`);
|
|
|
url = plexApi.buildImageUrl(account, normalizedId);
|
|
|
} else if (this.isDaoLiYuAccount(account)) {
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `调用 daoLiYuApi.buildImageUrl - coverId: ${normalizedId}`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `调用 daoLiYuApi.buildImageUrl - coverId: ${normalizedId}`);
|
|
|
url = daoLiYuApi.buildImageUrl(account, normalizedId);
|
|
|
}
|
|
|
|
|
|
if (url && this.isNavidromeAccount(account)) {
|
|
|
this.coverUrlCache.set(cacheKey, url);
|
|
|
- void ServerLogUtil.info('NavidromePageCover', `✅ 封面URL生成成功并已缓存 - coverId: ${coverId}, url: ${url}`);
|
|
|
- void ServerLogUtil.debug('NavidromePageCover', `缓存键值 - cacheKey: ${cacheKey}`);
|
|
|
+ void ServerLogUtil.info('RemoteMusicPageCover', `✅ 封面URL生成成功并已缓存 - coverId: ${coverId}, url: ${url}`);
|
|
|
+ void ServerLogUtil.debug('RemoteMusicPageCover', `缓存键值 - cacheKey: ${cacheKey}`);
|
|
|
} else if (!url) {
|
|
|
- void ServerLogUtil.error('NavidromePageCover', `❌ 封面URL生成失败(返回空) - coverId: ${coverId}, size: ${size}`);
|
|
|
+ void ServerLogUtil.error('RemoteMusicPageCover', `❌ 封面URL生成失败(返回空) - coverId: ${coverId}, size: ${size}`);
|
|
|
} else {
|
|
|
- void ServerLogUtil.info('NavidromePageCover', `✅ 封面URL生成成功(非Navidrome账号) - coverId: ${coverId}, url: ${url}`);
|
|
|
+ void ServerLogUtil.info('RemoteMusicPageCover', `✅ 封面URL生成成功(非Navidrome账号) - coverId: ${coverId}, url: ${url}`);
|
|
|
}
|
|
|
} catch (error) {
|
|
|
const err = error as Error;
|
|
|
- void ServerLogUtil.error('NavidromePageCover', `❌ 封面URL生成异常 - coverId: ${coverId}, error: ${err.message}`);
|
|
|
- void ServerLogUtil.error('NavidromePageCover', `错误类型: ${err.name || 'Unknown'}`);
|
|
|
- void ServerLogUtil.error('NavidromePageCover', `错误堆栈: ${err.stack || '无'}`);
|
|
|
+ void ServerLogUtil.error('RemoteMusicPageCover', `❌ 封面URL生成异常 - coverId: ${coverId}, error: ${err.message}`);
|
|
|
+ void ServerLogUtil.error('RemoteMusicPageCover', `错误类型: ${err.name || 'Unknown'}`);
|
|
|
+ void ServerLogUtil.error('RemoteMusicPageCover', `错误堆栈: ${err.stack || '无'}`);
|
|
|
}
|
|
|
|
|
|
return url;
|
|
|
@@ -3224,28 +3316,32 @@ export struct NavidromePage {
|
|
|
}
|
|
|
|
|
|
// 记录播放详细信息
|
|
|
- void ServerLogUtil.info('NavidromePlay', `开始播放歌曲: ${song.name} (#${index})`);
|
|
|
- void ServerLogUtil.info('NavidromePlay', `歌曲信息:`);
|
|
|
- void ServerLogUtil.info('NavidromePlay', `- ID: ${song.id}`);
|
|
|
- void ServerLogUtil.info('NavidromePlay', `- 艺术家: ${song.artist || '未知'}`);
|
|
|
- void ServerLogUtil.info('NavidromePlay', `- 专辑: ${song.album || '未知'}`);
|
|
|
- void ServerLogUtil.info('NavidromePlay', `- 文件大小: ${song.size || '未知'}`);
|
|
|
- void ServerLogUtil.info('NavidromePlay', `- 时长: ${song.duration || '未知'}`);
|
|
|
- void ServerLogUtil.info('NavidromePlay', `- 封面: ${song.pixelMapPath ? '有' : '无'}`);
|
|
|
- void ServerLogUtil.info('NavidromePlay', `- 播放路径: ${song.filePath}`);
|
|
|
+ void ServerLogUtil.info('StreamingPlay', `开始播放歌曲: ${song.name} (#${index})`);
|
|
|
+ void ServerLogUtil.info('StreamingPlay', `歌曲信息:`);
|
|
|
+ void ServerLogUtil.info('StreamingPlay', `- ID: ${song.id}`);
|
|
|
+ void ServerLogUtil.info('StreamingPlay', `- 艺术家: ${song.artist || '未知'}`);
|
|
|
+ void ServerLogUtil.info('StreamingPlay', `- 专辑: ${song.album || '未知'}`);
|
|
|
+ void ServerLogUtil.info('StreamingPlay', `- 文件大小: ${song.size || '未知'}`);
|
|
|
+ void ServerLogUtil.info('StreamingPlay', `- 时长: ${song.duration || '未知'}`);
|
|
|
+ void ServerLogUtil.info('StreamingPlay', `- 封面: ${song.pixelMapPath ? '有' : '无'}`);
|
|
|
+ void ServerLogUtil.info('StreamingPlay', `- 播放路径: ${song.filePath}`);
|
|
|
|
|
|
const playlistSource = this.getVisibleSongs().length > 0 ? this.getVisibleSongs() : this.allVideos;
|
|
|
const targetIndex = playlistSource.findIndex(item => item.id === song.id);
|
|
|
const startIndex = targetIndex >= 0 ? targetIndex : Math.min(index, Math.max(playlistSource.length - 1, 0));
|
|
|
|
|
|
- void ServerLogUtil.info('NavidromePlay', `播放列表设置: 起始索引 ${startIndex}, 总数 ${playlistSource.length}`);
|
|
|
+ void ServerLogUtil.info('StreamingPlay', `播放列表设置: 起始索引 ${startIndex}, 已加载 ${playlistSource.length}, 服务端总数 ${this.serverTotalSongCount}`);
|
|
|
|
|
|
setNavidromePlaylist(playlistSource, startIndex);
|
|
|
+ // 保存服务端总数到全局存储
|
|
|
+ if (this.serverTotalSongCount > 0) {
|
|
|
+ setNavidromeTotalCount(this.serverTotalSongCount);
|
|
|
+ }
|
|
|
|
|
|
const playlistData: PlaylistEventData = {
|
|
|
playlistId: NAVIDROME_PLAYLIST_ID,
|
|
|
playlistName: `${getRemoteDriveDisplayLabel(account.webType)} - ${account.name ?? '未知账户'}`,
|
|
|
- songCount: playlistSource.length,
|
|
|
+ songCount: this.serverTotalSongCount > 0 ? this.serverTotalSongCount : playlistSource.length,
|
|
|
startIndex,
|
|
|
isJump: isJump,//设置true会弹出播放页
|
|
|
songFilePaths: playlistSource.map(item => item.filePath)
|
|
|
@@ -3254,11 +3350,11 @@ export struct NavidromePage {
|
|
|
const eventPlaylistPlay: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_PLAY };
|
|
|
emitter.emit(eventPlaylistPlay, { data: playlistData });
|
|
|
|
|
|
- void ServerLogUtil.info('NavidromePlay', `播放事件已发送`);
|
|
|
- void ServerLogUtil.debug('NavidromePlay', `播放事件详情: 列表ID=${playlistData.playlistId}, 列表名称=${playlistData.playlistName}, 歌曲数=${playlistData.songCount}, 起始索引=${playlistData.startIndex}, 是否跳转=${playlistData.isJump}`);
|
|
|
+ void ServerLogUtil.info('StreamingPlay', `播放事件已发送`);
|
|
|
+ void ServerLogUtil.debug('StreamingPlay', `播放事件详情: 列表ID=${playlistData.playlistId}, 列表名称=${playlistData.playlistName}, 歌曲数=${playlistData.songCount}, 起始索引=${playlistData.startIndex}, 是否跳转=${playlistData.isJump}`);
|
|
|
|
|
|
if(!this.isNoJumpToHome){
|
|
|
- void ServerLogUtil.debug('NavidromePlay', '跳转到首页播放器');
|
|
|
+ void ServerLogUtil.debug('StreamingPlay', '跳转到首页播放器');
|
|
|
// 跳转到首页播放器
|
|
|
this.getUIContext()?.animateTo({ duration: 555 }, () => {
|
|
|
this.mType = 0
|
|
|
@@ -3267,8 +3363,8 @@ export struct NavidromePage {
|
|
|
|
|
|
} catch (error) {
|
|
|
const err = error as Error;
|
|
|
- void ServerLogUtil.error('NavidromePlay', `播放失败: ${err.message}`);
|
|
|
- void ServerLogUtil.error('NavidromePlay', `失败歌曲信息: ${song.name} (${song.id})`);
|
|
|
+ void ServerLogUtil.error('StreamingPlay', `播放失败: ${err.message}`);
|
|
|
+ void ServerLogUtil.error('StreamingPlay', `失败歌曲信息: ${song.name} (${song.id})`);
|
|
|
ToastUtil.showToast('播放失败');
|
|
|
}
|
|
|
}
|