ソースを参照

修复网盘播放列表数量显示问题

chendeben 6 ヶ月 前
コミット
8fdc5425e3

+ 3 - 1
entry/src/main/ets/common/network/AudioStationApi.ets

@@ -123,6 +123,7 @@ interface AudioStationSearchData {
 interface AudioStationPagedResponse<T> {
   items: T[];
   nextStart: number | null;
+  total?: number;  // 服务端返回的总数
 }
 
 interface AudioStationLoginRequestBody {
@@ -530,7 +531,8 @@ export class AudioStationApi {
     const nextStart = offset + items.length < total ? offset + items.length : null;
     const result: AudioStationPagedResponse<AudioStationSong> = {
       items,
-      nextStart
+      nextStart,
+      total
     };
     return result;
   }

+ 3 - 1
entry/src/main/ets/common/network/DaoLiYuApi.ets

@@ -195,6 +195,7 @@ export interface DaoLiYuPlaylist {
 export interface DaoLiYuPagedResponse<T> {
   items: T[];
   nextStart: number | null;
+  total?: number;
 }
 
 export class DaoLiYuApi {
@@ -253,7 +254,8 @@ export class DaoLiYuApi {
       .filter(item => item.id)
       .map(item => this.mapTrack(item));
     const nextStart = this.resolveNextStart(data, start, items.length);
-    return { items, nextStart };
+    const total = typeof data.total === 'number' ? data.total : undefined;
+    return { items, nextStart, total };
   }
 
   async getPlaylistsPage(account: WebDavAccount, start: number = 0, size: number = 200): Promise<DaoLiYuPagedResponse<DaoLiYuPlaylist>> {

+ 3 - 1
entry/src/main/ets/common/network/EmbyApi.ets

@@ -54,6 +54,7 @@ interface EmbyItemsResponse {
 export interface EmbyPagedResponse<T> {
   items: T[];
   nextStart: number | null;
+  total?: number;
 }
 
 interface EmbyPerson {
@@ -315,7 +316,8 @@ export class EmbyApi {
       }
     }
     const nextStart = items.length < limit ? null : startIndex + items.length;
-    const result: EmbyPagedResponse<EmbySong> = { items: songs, nextStart: nextStart };
+    const total = response.TotalRecordCount;
+    const result: EmbyPagedResponse<EmbySong> = { items: songs, nextStart: nextStart, total: total };
     return result;
   }
 

+ 4 - 1
entry/src/main/ets/common/network/JellyfinApi.ets

@@ -48,11 +48,13 @@ interface JellyfinItem {
 
 interface JellyfinItemsResponse {
   Items?: JellyfinItem[];
+  TotalRecordCount?: number;
 }
 
 export interface JellyfinPagedResponse<T> {
   items: T[];
   nextStart: number | null;
+  total?: number;
 }
 
 interface JellyfinPerson {
@@ -291,7 +293,8 @@ export class JellyfinApi {
       }
     }
     const nextStart = items.length < limit ? null : startIndex + items.length;
-    const result: JellyfinPagedResponse<JellyfinSong> = { items: songs, nextStart: nextStart };
+    const total = response.TotalRecordCount;
+    const result: JellyfinPagedResponse<JellyfinSong> = { items: songs, nextStart: nextStart, total: total };
     return result;
   }
 

+ 3 - 1
entry/src/main/ets/common/network/PlexApi.ets

@@ -78,6 +78,7 @@ export interface PlexPlaylist {
 export interface PlexPagedResponse<T> {
   items: T[];
   nextStart: number | null;
+  total?: number;
 }
 
 export class PlexApi {
@@ -160,7 +161,8 @@ export class PlexApi {
     const container = this.parseContainer(xml);
     const entries = this.parseTracks(xml);
     const nextStart = this.computeNextStart(container, entries.length, start);
-    return { items: entries, nextStart };
+    const total = container.totalSize ?? container.size;
+    return { items: entries, nextStart, total };
   }
 
   async getAlbumsPage(account: WebDavAccount, start: number = 0, size: number = 200): Promise<PlexPagedResponse<PlexAlbum>> {

+ 20 - 0
entry/src/main/ets/common/util/NavidromePlaylistStore.ets

@@ -4,6 +4,8 @@ let navidromeVideoItems: VideoItem[] = [];
 let navidromeCurrentPlayIndex: number = 0;
 // 已播放歌曲路径集合(用于渐进式随机播放)
 let playedFilePaths: Set<string> = new Set();
+// 服务端返回的歌曲总数(用于正确显示播放列表总数)
+let navidromeTotalCount: number = 0;
 
 export function setNavidromePlaylist(items: VideoItem[], startIndex: number): void {
   navidromeVideoItems = items.slice();
@@ -22,6 +24,7 @@ export function clearNavidromePlaylist(): void {
   navidromeVideoItems = [];
   navidromeCurrentPlayIndex = 0;
   playedFilePaths.clear();
+  navidromeTotalCount = 0;
 }
 
 // ============ 渐进式随机播放支持 ============
@@ -78,3 +81,20 @@ export function clearPlayedHistory(): void {
 export function getPlayedCount(): number {
   return playedFilePaths.size;
 }
+
+// ============ 服务端总数支持 ============
+
+/**
+ * 设置服务端返回的歌曲总数
+ */
+export function setNavidromeTotalCount(count: number): void {
+  navidromeTotalCount = count;
+}
+
+/**
+ * 获取服务端返回的歌曲总数
+ * 如果未设置,返回已加载的歌曲数量
+ */
+export function getNavidromeTotalCount(): number {
+  return navidromeTotalCount > 0 ? navidromeTotalCount : navidromeVideoItems.length;
+}

+ 3 - 3
entry/src/main/ets/common/util/NavidromeRandomLoader.ets

@@ -14,14 +14,14 @@ let isLoadingMore: boolean = false;
 // 加载回调函数类型定义
 type LoadMoreCallback = () => Promise<VideoItem[]>;
 
-// 加载更多页的回调(由NavidromePage注册)
+// 加载更多页的回调(由RemoteMusicPage注册)
 let loadMoreCallback: LoadMoreCallback | null = null;
 
 // 检查是否还有更多数据可加载
 let hasMoreDataCallback: (() => boolean) | null = null;
 
 /**
- * 注册加载更多页的回调函数(由NavidromePage在aboutToAppear中调用)
+ * 注册加载更多页的回调函数(由RemoteMusicPage在aboutToAppear中调用)
  * @param callback 加载一页数据并返回新增歌曲的函数
  * @param hasMoreChecker 检查是否还有更多数据的函数
  */
@@ -35,7 +35,7 @@ export function registerLoadMoreCallback(
 }
 
 /**
- * 注销加载回调(由NavidromePage在aboutToDisappear中调用)
+ * 注销加载回调(由RemoteMusicPage在aboutToDisappear中调用)
  */
 export function unregisterLoadMoreCallback(): void {
   loadMoreCallback = null;

+ 4 - 4
entry/src/main/ets/pages/NewIndex.ets

@@ -54,7 +54,7 @@ import { getCloudDiskIcon, RemoteDriveAccountDialog } from '../dialog/RemoteDriv
 import ReqPermissionUtil from '../common/util/ReqPermissionUtil';
 import { getRemoteDriveAccountLabel, getRemoteDriveDisplayLabel, getRemoteDriveProtocolLabel } from '../common/util/RemoteDriveLabel';
 import { RemoteDriveType } from '../common/enums/RemoteDriveType';
-import { NavidromePage } from '../view/NavidromePage';
+import { RemoteMusicPage } from '../view/RemoteMusicPage';
 import { ServerLogUtil } from '../common/util/ServerLogUtil';
 import { PlayStatus } from '../common/PlayStatus';
 
@@ -198,8 +198,8 @@ struct NewIndex {
       const eventData: emitter.EventData = {};
       emitter.emit({ eventId: EventConstants.EVENT_SWIPE_BACK_UPDATE }, eventData); // 发送音频广播通知更新doSwipBack
     } else if(this.mType === 7&&this.isDetailView) {
-      // NavidromePage/Jellyfin/Emby 页面,发送手势返回事件
-      console.info('onecold NavidromePage 返回键处理:发送手势返回事件');
+      // RemoteMusicPage(流媒体) 页面,发送手势返回事件
+      console.info('onecold RemoteMusicPage 返回键处理:发送手势返回事件');
       const eventData: emitter.EventData = {};
       emitter.emit({ eventId: EventConstants.EVENT_SWIPE_BACK_NAVID}, eventData);
     } else if(this.mType === 6) {
@@ -456,7 +456,7 @@ struct NewIndex {
       }
 
       if(this.mType === 7){
-        NavidromePage({
+        RemoteMusicPage({
           offsetX:this.offsetX,
           isDetailView:this.isDetailView,
           isShowDrawer:this.isShowDrawer,

+ 101 - 13
entry/src/main/ets/view/LocalMusic.ets

@@ -3,14 +3,16 @@ import { curves, display, MenuModifier,PiPWindow, promptAction, router, SymbolGl
 import { VideoItem } from '../viewmodel/VideoItem';
 import {  LengthMetrics, SegmentButton,SegmentButtonOptions } from '@kit.ArkUI';
 import { getWebdavVideoItems, getWebdavCurrentPlayIndex } from '../pages/WebDavMainPage';
-import { 
-  getNavidromeVideoItems, 
+import {
+  getNavidromeVideoItems,
   getNavidromeCurrentPlayIndex,
   getUnplayedSongs,
   getUnplayedCount,
   markAsPlayed,
   clearPlayedHistory,
-  getPlayedCount
+  getPlayedCount,
+  getNavidromeTotalCount,
+  setNavidromeTotalCount
 } from '../common/util/NavidromePlaylistStore';
 import { triggerLoadMoreSongs, hasMoreData } from '../common/util/NavidromeRandomLoader';
 import {
@@ -126,7 +128,8 @@ import {
   isPlexType,
   isRemoteCloudType,
   WorkerEditMusicResult,
-  WebDavMetadataUpdatePayload
+  WebDavMetadataUpdatePayload,
+  isDaoLiYuType
 } from '../common/util/RemotePlayerUtil';
 
 import { HSBColorPicker, HSBColorPickerLayout } from '@keke/color-picker'
@@ -909,6 +912,7 @@ export struct LocalMusic {
           playlistData.songFilePaths,
           playlistData.startIndex,
           playlistData.isJump,
+          playlistData.songCount
         )
         return
       }
@@ -2187,11 +2191,28 @@ export struct LocalMusic {
       return this.favList.length;
     }
     if (this.playQueueScope === 'playlist') {
+      // 检测是否是流媒体播放(Navidrome/Jellyfin/Emby/AudioStation/Plex/道理鱼等)
+      // 如果是,优先返回服务端总数
+      const navidromeTotalCount = getNavidromeTotalCount();
+      Logger.info('heanup getPlayListDisplayCount', `playQueueScope=playlist, navidromeTotalCount=${navidromeTotalCount}, currentSong=${this.currentSong?.name}, type=${this.currentSong?.type}`);
+      if (navidromeTotalCount > 0 && this.currentSong && (
+        isNavidromeType(this.currentSong.type) ||
+        isJellyfinType(this.currentSong.type) ||
+        isEmbyType(this.currentSong.type) ||
+        isAudioStationType(this.currentSong.type) ||
+        isPlexType(this.currentSong.type) ||
+        isDaoLiYuType(this.currentSong.type)
+      )) {
+        Logger.info('heanup getPlayListDisplayCount', `返回服务端总数: ${navidromeTotalCount}`);
+        return navidromeTotalCount;
+      }
+      Logger.info('heanup getPlayListDisplayCount', `返回 currentSongList.length: ${this.currentSongList.length}`);
       return this.currentSongList.length;
     }
     if (this.playQueueScope === 'view' && this.modeType === 1 && this.totalCount > 0) {
       return this.totalCount;
     }
+    Logger.info('heanup getPlayListDisplayCount', `返回 songList.length: ${this.songList.length}, playQueueScope=${this.playQueueScope}`);
     return this.songList.length;
   }
 
@@ -7055,8 +7076,10 @@ export struct LocalMusic {
           if (index !== undefined) {
             this.curIndex = index
           }
+          // 设置播放队列作用域为歌单
+          this.playQueueScope = 'playlist'
           // 确保播放列表是完整的歌单歌曲列表(已在finishLoadingPlaylist中设置)
-          Logger.info(`heanup 网络音频 isFromSonPlayList分支 - 当前播放列表长度: ${this.songList.length}, 当前索引: ${this.curIndex}`)
+          Logger.info(`heanup 网络音频 isFromSonPlayList分支 - 当前播放列表长度: ${this.songList.length}, 当前索引: ${this.curIndex}, playQueueScope=${this.playQueueScope}`)
         } else {
           // 从全局网络音频列表中查找
           let globalVideoList = this.videoLocalList.filter(video => isRemoteCloudType(video.type)) as VideoItem[];
@@ -7641,6 +7664,40 @@ export struct LocalMusic {
     }
   }
 
+  /**
+   * 加载更多流媒体歌曲(Navidrome/Jellyfin/Emby/AudioStation/Plex)
+   * 当播放列表弹窗滚动到底部时调用
+   */
+  private async loadMoreNavidromeSongs(): Promise<void> {
+    const beforeCount = this.songList.length;
+    const hasMore = hasMoreData();
+    Logger.info('heanup', `开始加载更多流媒体歌曲, 当前数量=${beforeCount}, hasMoreData=${hasMore}`);
+
+    if (!hasMore) {
+      Logger.info('heanup', '没有更多数据可加载(hasMoreData=false)');
+      return;
+    }
+
+    // 触发加载更多(会更新NavidromePlaylistStore中的数据)
+    const success = await triggerLoadMoreSongs(1);
+    Logger.info('heanup', `triggerLoadMoreSongs返回: ${success}`);
+
+    if (success) {
+      // 获取更新后的完整列表
+      const updatedList = getNavidromeVideoItems();
+      Logger.info('heanup', `更新后列表长度: ${updatedList.length}`);
+      if (updatedList.length > beforeCount) {
+        Logger.info('heanup', `流媒体歌曲加载成功: ${beforeCount} -> ${updatedList.length}`);
+        this.songList = updatedList;
+        this.sonDataSource.pushArrayData(this.songList);
+      } else {
+        Logger.info('heanup', '列表长度未增加');
+      }
+    } else {
+      Logger.info('heanup', '流媒体歌曲加载未返回新数据(triggerLoadMoreSongs=false)');
+    }
+  }
+
   /**
    * 重置分页状态并加载首页
    */
@@ -9384,7 +9441,20 @@ export struct LocalMusic {
     .onReachEnd(() => {
       if(this.modeType === 4 )
         return;
-      void this.tryLoadNextPageForPlayback();
+      // 检测是否是流媒体类型(Navidrome/Jellyfin/Emby/AudioStation/Plex),如果是则调用网盘加载更多逻辑
+      const isStreamingSong = this.currentSong && (
+        isNavidromeType(this.currentSong.type) ||
+        isJellyfinType(this.currentSong.type) ||
+        isEmbyType(this.currentSong.type) ||
+        isAudioStationType(this.currentSong.type) ||
+        isPlexType(this.currentSong.type)
+      );
+      if (isStreamingSong && hasMoreData()) {
+        Logger.info('heanup', '播放列表滚动到底部,加载更多流媒体歌曲');
+        void this.loadMoreNavidromeSongs();
+      } else {
+        void this.tryLoadNextPageForPlayback();
+      }
     })
     // .transition(TransitionEffect.move(TransitionEdge.BOTTOM)
     //   .animation({ duration: 500, curve: Curve.Ease }))
@@ -16467,9 +16537,10 @@ export struct LocalMusic {
    * 处理歌单播放请求
    */
   private async handlePlaylistPlayRequest(playlistId: string, playlistName:
-    string, songFilePaths: string[], startIndex: number,isJump: boolean) {
+    string, songFilePaths: string[], startIndex: number, isJump: boolean, songCount?: number) {
     try {
-      Logger.info(`heanup 处理歌单播放请求: ${playlistName}, 歌曲数量: ${songFilePaths.length}, 开始索引: ${startIndex}`)
+      const totalCount = songCount ?? songFilePaths.length;
+      Logger.info(`heanup 处理歌单播放请求: ${playlistName}, 已加载: ${songFilePaths.length}, 服务端总数: ${totalCount}, 开始索引: ${startIndex}`)
 
       // 检查是否为WebDAV播放请求
       if (playlistId === 'webdav-playlist') {
@@ -16488,7 +16559,7 @@ export struct LocalMusic {
           if(isJump){
             this.setShowPlayTrue()//显示播放页
           }
-          this.finishLoadingPlaylist(videoItems, currentPlayIndex, playlistName)
+          this.finishLoadingPlaylist(videoItems, currentPlayIndex, playlistName, totalCount)
           return
         } else {
           return
@@ -16499,12 +16570,12 @@ export struct LocalMusic {
         Logger.info(`heanup 检测到Navidrome播放请求,从内存读取videoItems`);
         const videoItems = getNavidromeVideoItems();
         const currentPlayIndex = getNavidromeCurrentPlayIndex();
-        Logger.info(`heanup Navidrome歌曲列表长度: ${videoItems.length}`);
+        Logger.info(`heanup Navidrome歌曲列表长度: ${videoItems.length}, 服务端总数: ${totalCount}`);
         if (videoItems && videoItems.length > 0) {
           if(isJump){
             this.setShowPlayTrue()//显示播放页
           }
-          this.finishLoadingPlaylist(videoItems, currentPlayIndex, playlistName);
+          this.finishLoadingPlaylist(videoItems, currentPlayIndex, playlistName, totalCount);
           return;
         } else {
           Logger.warn('heanup Navidrome播放请求缺少歌曲数据');
@@ -16585,13 +16656,14 @@ export struct LocalMusic {
   /**
    * 完成歌单加载并开始播放
    */
-  private finishLoadingPlaylist(songs: VideoItem[], startIndex: number, playlistName: string) {
+  private finishLoadingPlaylist(songs: VideoItem[], startIndex: number, playlistName: string, totalCount?: number) {
     if (songs.length === 0) {
       Logger.error('heanup 没有找到任何可播放的歌曲')
       ToastUtil.showToast('没有找到可播放的歌曲')
       return
     }
     this.songList = songs
+    this.currentSongList = songs  // 同步更新 currentSongList
 
     this.sonDataSource.pushArrayData(songs)
 
@@ -16601,7 +16673,23 @@ export struct LocalMusic {
 
     // 保存歌单的歌曲路径列表,用于随机播放
     this.currentPlaylistSongFilePaths = songs.map(song => song.filePath);
-    Logger.info('heanup finishLoadingPlaylist', `保存歌单歌曲路径列表,数量=${this.currentPlaylistSongFilePaths.length}`);
+    const actualTotal = totalCount ?? songs.length;
+    
+    // 如果是网盘播放,保存服务端总数到全局存储
+    if (totalCount && totalCount > 0 && songs.length > 0) {
+      const firstSong = songs[0];
+      if (firstSong && (isNavidromeType(firstSong.type) || 
+          isJellyfinType(firstSong.type) || 
+          isEmbyType(firstSong.type) || 
+          isAudioStationType(firstSong.type) ||
+          isPlexType(firstSong.type) ||
+          isDaoLiYuType(firstSong.type))) {
+        setNavidromeTotalCount(totalCount);
+        Logger.info('heanup finishLoadingPlaylist', `已保存服务端总数到全局存储: ${totalCount}`);
+      }
+    }
+    
+    Logger.info('heanup finishLoadingPlaylist', `保存歌单歌曲路径列表,已加载=${this.currentPlaylistSongFilePaths.length}, 服务端总数=${actualTotal}`);
 
     if (songs[startIndex]) {
       // 确保当前播放的歌曲也更新到存储

+ 133 - 77
entry/src/main/ets/view/NavidromePage.ets → entry/src/main/ets/view/RemoteMusicPage.ets

@@ -18,7 +18,7 @@ import { Utility } from '../common/util/Utility';
 import { Constants } from '../Constants';
 import { EventConstants } from '../common/constants/EventConstants';
 import { emitter } from '@kit.BasicServicesKit';
-import { setNavidromePlaylist, appendToNavidromePlaylist } 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';
@@ -109,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轴偏移量
@@ -140,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; // 是否在艺术家/专辑详情视图
@@ -229,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;
     }
   }
@@ -270,7 +271,7 @@ export struct NavidromePage {
     }
   }
 
-  //切换不同的NavidromePage
+  //切换不同的RemoteMusicPage
   async onSwitchAccount() {
     await this.refreshNavidromeData(true);
   }
@@ -297,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 }, () => {
@@ -324,22 +325,22 @@ export struct NavidromePage {
         if (!account) {
           return [];
         }
-        
+
         // 记录加载前的数量
         const beforeCount = this.allVideos.length;
-        
-        // 加载一页数据(会更新this.allVideos)
-        await this.loadNextSongPage(account);
-        
+
+        // 根据账户类型调用对应的加载方法
+        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;
       },
       // 检查是否还有更多数据
@@ -349,6 +350,26 @@ export struct NavidromePage {
     );
   }
 
+  /**
+   * 根据账户类型调用对应的歌曲加载方法
+   */
+  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) {
@@ -407,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> {
@@ -848,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];
@@ -875,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;
     }
@@ -932,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];
@@ -959,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;
     }
@@ -1016,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) {
@@ -1029,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;
     }
@@ -1109,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) {
@@ -1122,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;
     }
@@ -1202,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) {
@@ -1215,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;
     }
@@ -1441,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 {
@@ -1455,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;
   }
 
@@ -1492,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 {
@@ -1505,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;
   }
 
@@ -2082,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;
     }
 
@@ -2124,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;
@@ -3264,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)
@@ -3294,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
@@ -3307,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('播放失败');
     }
   }