Przeglądaj źródła

优化分页查询

chendeben 7 miesięcy temu
rodzic
commit
a88978bf56

+ 76 - 176
entry/src/main/ets/common/util/MediaTable.ets

@@ -1292,97 +1292,7 @@ export default  class MediaTable {
    * 返回艺术家封面列表的代表性歌曲
    */
   public async queryArtistsPaged(options: PageQueryOptions): Promise<PageQueryResult> {
-    try {
-      Logger.info('heanup MediaTable', `queryArtistsPaged: 开始分页查询 page=${options.pageIndex}, size=${options.pageSize}`);
-      
-      // 艺术家模式:返回每个艺术家的第一首歌作为代表
-      // 先查询所有不同的艺术家
-      const artistPredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
-      artistPredicates.isNotNull(DB_COLUMNS.ARTIST);
-      artistPredicates.notEqualTo(DB_COLUMNS.ARTIST, '');
-      artistPredicates.distinct();
-      
-      return new Promise((resolve, reject) => {
-        this.accountTable.query(artistPredicates, async (resultSet: relationalStore.ResultSet) => {
-          try {
-            const artists: string[] = this.parseDistinctColumn(resultSet, DB_COLUMNS.ARTIST);
-            
-            // 应用搜索过滤
-            let filteredArtists = artists;
-            if (options.searchKeyword) {
-              const keyword = options.searchKeyword.toLowerCase();
-              filteredArtists = artists.filter(artist => 
-                artist.toLowerCase().includes(keyword)
-              );
-            }
-            
-            // 排序
-            let allCountMap: Map<string, number> | undefined = undefined;
-            if (options.sortType === 8 || options.sortType === 9) {
-              allCountMap = new Map<string, number>();
-              for (const artist of filteredArtists) {
-                const count = await this.getCountByColumn(DB_COLUMNS.ARTIST, artist);
-                allCountMap.set(artist, count);
-              }
-              filteredArtists.sort((a, b) => {
-                const countA = allCountMap?.get(a) ?? 0;
-                const countB = allCountMap?.get(b) ?? 0;
-                const diff = options.sortType === 8 ? countA - countB : countB - countA;
-                return diff !== 0 ? diff : a.localeCompare(b);
-              });
-            } else if (options.sortType === 5) {
-              filteredArtists.sort((a, b) => b.localeCompare(a));
-            } else {
-              filteredArtists.sort((a, b) => a.localeCompare(b));
-            }
-            
-            const totalCount = filteredArtists.length;
-            const offset = options.pageIndex * options.pageSize;
-            const pagedArtists = filteredArtists.slice(offset, offset + options.pageSize);
-            
-            // 为每个艺术家查询第一首歌,作为封面来源
-            const items: VideoItem[] = [];
-            const countMap = new Map<string, number>();
-            for (const artist of pagedArtists) {
-              const songPredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
-              songPredicates.equalTo(DB_COLUMNS.ARTIST, artist);
-              songPredicates.limitAs(1);
-              
-              let coverPath = '';
-              await new Promise<void>((resolveInner) => {
-                this.accountTable.query(songPredicates, (songResultSet: relationalStore.ResultSet) => {
-                  if (songResultSet.rowCount > 0) {
-                    songResultSet.goToFirstRow();
-                    const item = this.buildVideoItem(songResultSet);
-                    coverPath = item.pixelMapPath ?? '';
-                  }
-                  songResultSet.close();
-                  resolveInner();
-                });
-              });
-              const artistSongCount = allCountMap?.get(artist) ?? await this.getCountByColumn(DB_COLUMNS.ARTIST, artist);
-              countMap.set(artist, artistSongCount);
-              const artistItem = new VideoItem(artist, artist, artist, CommonConstants.TYPE_IS_ARTIST, 0, '');
-              artistItem.pixelMapPath = coverPath;
-              artistItem.artist = artist;
-              items.push(artistItem);
-            }
-            
-            const hasMore = (offset + items.length) < totalCount;
-            Logger.info('heanup MediaTable', `queryArtistsPaged: 查询完成 items=${items.length}, total=${totalCount}, hasMore=${hasMore}`);
-            resolve({ items, totalCount, hasMore, countMap });
-          } catch (err) {
-            const error = err as Error;
-            Logger.error('heanup MediaTable', `queryArtistsPaged error: ${error.message}`);
-            reject(err);
-          }
-        });
-      });
-    } catch (err) {
-      const error = err as Error;
-      Logger.error('heanup MediaTable', `queryArtistsPaged failed: ${error.message}`);
-      throw error;
-    }
+    return await this.queryGroupedPaged(DB_COLUMNS.ARTIST, CommonConstants.TYPE_IS_ARTIST, options);
   }
 
   /**
@@ -1390,95 +1300,85 @@ export default  class MediaTable {
    * 返回专辑封面列表的代表性歌曲
    */
   public async queryAlbumsPaged(options: PageQueryOptions): Promise<PageQueryResult> {
+    return await this.queryGroupedPaged(DB_COLUMNS.ALBUM, CommonConstants.TYPE_IS_ALBUM, options);
+  }
+
+  private async queryGroupedPaged(columnName: string, itemType: number, options: PageQueryOptions): Promise<PageQueryResult> {
     try {
-      Logger.info('heanup MediaTable', `queryAlbumsPaged: 开始分页查询 page=${options.pageIndex}, size=${options.pageSize}`);
-      
-      // 专辑模式:返回每个专辑的第一首歌作为代表
-      // 先查询所有不同的专辑
-      const albumPredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
-      albumPredicates.isNotNull(DB_COLUMNS.ALBUM);
-      albumPredicates.notEqualTo(DB_COLUMNS.ALBUM, '');
-      albumPredicates.distinct();
-      
-      return new Promise((resolve, reject) => {
-        this.accountTable.query(albumPredicates, async (resultSet: relationalStore.ResultSet) => {
-          try {
-            const albums: string[] = this.parseDistinctColumn(resultSet, DB_COLUMNS.ALBUM);
-            
-            // 应用搜索过滤
-            let filteredAlbums = albums;
-            if (options.searchKeyword) {
-              const keyword = options.searchKeyword.toLowerCase();
-              filteredAlbums = albums.filter(album => 
-                album.toLowerCase().includes(keyword)
-              );
-            }
-            
-            // 排序
-            let allCountMap: Map<string, number> | undefined = undefined;
-            if (options.sortType === 8 || options.sortType === 9) {
-              allCountMap = new Map<string, number>();
-              for (const album of filteredAlbums) {
-                const count = await this.getCountByColumn(DB_COLUMNS.ALBUM, album);
-                allCountMap.set(album, count);
-              }
-              filteredAlbums.sort((a, b) => {
-                const countA = allCountMap?.get(a) ?? 0;
-                const countB = allCountMap?.get(b) ?? 0;
-                const diff = options.sortType === 8 ? countA - countB : countB - countA;
-                return diff !== 0 ? diff : a.localeCompare(b);
-              });
-            } else if (options.sortType === 5) {
-              filteredAlbums.sort((a, b) => b.localeCompare(a));
-            } else {
-              filteredAlbums.sort((a, b) => a.localeCompare(b));
-            }
-            
-            const totalCount = filteredAlbums.length;
-            const offset = options.pageIndex * options.pageSize;
-            const pagedAlbums = filteredAlbums.slice(offset, offset + options.pageSize);
-            
-            // 为每个专辑查询第一首歌,作为封面来源
-            const items: VideoItem[] = [];
-            const countMap = new Map<string, number>();
-            for (const album of pagedAlbums) {
-              const songPredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
-              songPredicates.equalTo(DB_COLUMNS.ALBUM, album);
-              songPredicates.limitAs(1);
-              
-              let coverPath = '';
-              await new Promise<void>((resolveInner) => {
-                this.accountTable.query(songPredicates, (songResultSet: relationalStore.ResultSet) => {
-                  if (songResultSet.rowCount > 0) {
-                    songResultSet.goToFirstRow();
-                    const item = this.buildVideoItem(songResultSet);
-                    coverPath = item.pixelMapPath ?? '';
-                  }
-                  songResultSet.close();
-                  resolveInner();
-                });
-              });
-              const albumSongCount = allCountMap?.get(album) ?? await this.getCountByColumn(DB_COLUMNS.ALBUM, album);
-              countMap.set(album, albumSongCount);
-              const albumItem = new VideoItem(album, album, album, CommonConstants.TYPE_IS_ALBUM, 0, '');
-              albumItem.pixelMapPath = coverPath;
-              albumItem.album = album;
-              items.push(albumItem);
-            }
-            
-            const hasMore = (offset + items.length) < totalCount;
-            Logger.info('heanup MediaTable', `queryAlbumsPaged: 查询完成 items=${items.length}, total=${totalCount}, hasMore=${hasMore}`);
-            resolve({ items, totalCount, hasMore, countMap });
-          } catch (err) {
-            const error = err as Error;
-            Logger.error('heanup MediaTable', `queryAlbumsPaged error: ${error.message}`);
-            reject(err);
+      const keyword = options.searchKeyword?.trim();
+      const tableName = RdbUtils.MEDIA_TABLE.tableName;
+      const whereClauses = [
+        `${columnName} IS NOT NULL`,
+        `${columnName} != ''`,
+        `${DB_COLUMNS.TYPE} = ?`
+      ];
+      const params: Array<string | number> = [CommonConstants.TYPE_LOCAL];
+      if (keyword) {
+        whereClauses.push(`${columnName} LIKE ?`);
+        params.push(`%${keyword}%`);
+      }
+      const whereSql = whereClauses.join(' AND ');
+
+      const totalSql = `SELECT COUNT(DISTINCT ${columnName}) AS totalCount FROM ${tableName} WHERE ${whereSql}`;
+      const totalResultSet = await this.accountTable.querySql(totalSql, params);
+      let totalCount = 0;
+      if (totalResultSet.goToFirstRow()) {
+        totalCount = totalResultSet.getLong(totalResultSet.getColumnIndex('totalCount'));
+      }
+      totalResultSet.close();
+
+      const sortType = options.sortType ?? 9;
+      let orderBy = `${columnName} ASC`;
+      if (sortType === 5) {
+        orderBy = `${columnName} DESC`;
+      } else if (sortType === 8) {
+        orderBy = `songCount ASC, ${columnName} ASC`;
+      } else if (sortType === 9) {
+        orderBy = `songCount DESC, ${columnName} ASC`;
+      }
+
+      const offset = options.pageIndex * options.pageSize;
+      const pageSql =
+        `SELECT ${columnName} AS name, COUNT(*) AS songCount, ` +
+        `MAX(${DB_COLUMNS.PIXEL_MAP_PATH}) AS coverPath ` +
+        `FROM ${tableName} WHERE ${whereSql} ` +
+        `GROUP BY ${columnName} ` +
+        `HAVING COUNT(*) > 0 ` +
+        `ORDER BY ${orderBy} ` +
+        `LIMIT ? OFFSET ?`;
+      const pageParams = [...params, options.pageSize, offset];
+      const resultSet = await this.accountTable.querySql(pageSql, pageParams);
+
+      const items: VideoItem[] = [];
+      const countMap = new Map<string, number>();
+      if (resultSet.rowCount > 0) {
+        resultSet.goToFirstRow();
+        for (let i = 0; i < resultSet.rowCount; i++) {
+          const name = resultSet.getString(resultSet.getColumnIndex('name'));
+          const songCount = resultSet.getLong(resultSet.getColumnIndex('songCount'));
+          const coverPath = resultSet.getString(resultSet.getColumnIndex('coverPath')) || '';
+          countMap.set(name, songCount);
+          const item = new VideoItem(name, name, name, itemType, 0, '');
+          item.pixelMapPath = coverPath;
+          if (itemType === CommonConstants.TYPE_IS_ARTIST) {
+            item.artist = name;
+          } else {
+            item.album = name;
           }
-        });
-      });
+          items.push(item);
+          if (i < resultSet.rowCount - 1) {
+            resultSet.goToNextRow();
+          }
+        }
+      }
+      resultSet.close();
+
+      const hasMore = (offset + items.length) < totalCount;
+      Logger.info('heanup MediaTable', `queryGroupedPaged: 查询完成 items=${items.length}, total=${totalCount}, hasMore=${hasMore}`);
+      return { items, totalCount, hasMore, countMap };
     } catch (err) {
       const error = err as Error;
-      Logger.error('heanup MediaTable', `queryAlbumsPaged failed: ${error.message}`);
+      Logger.error('heanup MediaTable', `queryGroupedPaged failed: ${error.message}`);
       throw error;
     }
   }

+ 36 - 1
entry/src/main/ets/common/util/PlaylistTable.ets

@@ -213,6 +213,41 @@ export default class PlaylistTable {
     }
   }
 
+  /**
+   * 根据名称查询最近更新的歌单
+   */
+  async queryPlaylistByName(name: string): Promise<Playlist | null> {
+    await this.ensureInitialized();
+    if (!this.rdbStore) {
+      Logger.error('heanup PlaylistTable', '数据库未初始化');
+      return null;
+    }
+
+    try {
+      const sql = 'SELECT * FROM playlistTable WHERE name = ? ORDER BY updateTime DESC LIMIT 1';
+      const resultSet = await this.rdbStore.querySql(sql, [name]);
+      if (resultSet.goToFirstRow()) {
+        const playlist = new Playlist(
+          resultSet.getString(resultSet.getColumnIndex('id')),
+          resultSet.getString(resultSet.getColumnIndex('name')),
+          resultSet.getString(resultSet.getColumnIndex('createTime')),
+          resultSet.getString(resultSet.getColumnIndex('updateTime')),
+          resultSet.getLong(resultSet.getColumnIndex('songCount')),
+          resultSet.getLong(resultSet.getColumnIndex('sortOrder')),
+          resultSet.getString(resultSet.getColumnIndex('coverPath')),
+          resultSet.getString(resultSet.getColumnIndex('description'))
+        );
+        resultSet.close();
+        return playlist;
+      }
+      resultSet.close();
+      return null;
+    } catch (error) {
+      Logger.error('heanup PlaylistTable', `queryPlaylistByName失败: ${error.message}`);
+      return null;
+    }
+  }
+
   /**
    * 根据ID查询歌单
    */
@@ -608,4 +643,4 @@ export default class PlaylistTable {
       return false;
     }
   }
-}
+}

+ 23 - 0
entry/src/main/ets/common/util/RdbUtils.ets

@@ -162,6 +162,7 @@ export default class RdbUtils {
       try {
         // 检查表结构
         this.checkAndUpdateTableColumns();
+        this.ensureIndexes();
         // Logger.info(RdbUtils.RDB_TAG, `数据库表结构检查完成`);
       } catch (e) {
         Logger.error(RdbUtils.RDB_TAG, `检查表结构时出错: ${e.message}`);
@@ -275,6 +276,21 @@ export default class RdbUtils {
     }
   }
 
+  private ensureIndexes() {
+    if (!this.rdbStore || this.tableName !== RdbUtils.MEDIA_TABLE.tableName) {
+      return;
+    }
+    const indexSqlList = [
+      `CREATE INDEX IF NOT EXISTS idx_media_type ON ${this.tableName} (mtype)`,
+      `CREATE INDEX IF NOT EXISTS idx_media_type_artist ON ${this.tableName} (mtype, artist)`,
+      `CREATE INDEX IF NOT EXISTS idx_media_type_album ON ${this.tableName} (mtype, album)`,
+      `CREATE INDEX IF NOT EXISTS idx_media_parent ON ${this.tableName} (parentPath)`
+    ];
+    indexSqlList.forEach(sql => {
+      this.rdbStore?.executeSql(sql);
+    });
+  }
+
   //检查是否存在相同id的记录,存在则不插入,进一步判断pixelMapPath字段,如果旧值为空而新值不为空,则更新该字段。
   async insertData(data: relationalStore.ValuesBucket, callback: Function = () => {},cover_api?:string) {
     if (!callback || typeof callback !== 'function') {
@@ -434,4 +450,11 @@ export default class RdbUtils {
     }
   }
 
+  async querySql(sql: string, params: Array<string | number | boolean | null> = []): Promise<relationalStore.ResultSet> {
+    if (!this.rdbStore) {
+      throw new Error('rdbStore 未初始化');
+    }
+    return await this.rdbStore.querySql(sql, params);
+  }
+
 }

+ 2 - 4
entry/src/main/ets/pages/WebDavMainPage.ets

@@ -794,7 +794,7 @@ export struct WebDavMainPage {
       }
       Logger.info(TAG, `heanup WebDAV歌曲入库完成: 成功 ${upsertSuccess}/${this.songs.length}`);
       // 先查询是否已有同名歌单,避免重复创建导致混淆
-      const existing = (await playlistTable.queryAllPlaylists()).find(p => p.name === playlistName);
+      const existing = await playlistTable.queryPlaylistByName(playlistName);
       if (existing) {
         this.getUIContext().getPromptAction().showToast({ message: '歌单已存在,直接追加歌曲' });
         const filePathsExist: string[] = this.songs.map(s => s.filePath);
@@ -810,8 +810,7 @@ export struct WebDavMainPage {
       }
 
       // 查询刚创建的歌单ID
-      const playlists = await playlistTable.queryAllPlaylists();
-      const target = playlists.reverse().find(p => p.name === playlistName); // 取最近创建的同名歌单
+      const target = await playlistTable.queryPlaylistByName(playlistName);
       if (!target) {
         this.getUIContext().getPromptAction().showToast({ message: '无法找到新建歌单' });
         return;
@@ -1736,4 +1735,3 @@ export struct WebDavMainPage {
 }
 
 
-

+ 90 - 79
entry/src/main/ets/view/LocalMusic.ets

@@ -450,8 +450,8 @@ export struct LocalMusic {
   @State private hasMoreData: boolean = true      // 是否还有更多数据
   @State private isLoadingPage: boolean = false   // 是否正在加载分页数据
   private pageCache: Map<number, VideoItem[]> = new Map()  // 页面缓存(最多保留3页)
-  private fullSongList: VideoItem[] = []          // 完整的播放列表(延迟加载,只在播放时加载)
-  private isFullSongListLoaded: boolean = false   // 标记完整列表是否已加载
+  private prefetchingPages: Set<number> = new Set()
+  private playQueueScope: 'view' | 'history' | 'fav' | 'playlist' | 'single' = 'view'
   @StorageProp('isLandscape') @Watch('onIsLandscapeChange')  isLandscape: boolean = false;
   @StorageProp('topSafeHeight') topSafeHeight: number = 0;
   @StorageProp('bottomSafeHeight') bottomSafeHeight: number = 0;
@@ -550,11 +550,19 @@ export struct LocalMusic {
         break
       case 2:
         this.rightTopImage = $r('sys.symbol.sort')
+        if (this.sortType === 4) {
+          this.sortType = 9
+          PreferencesUtil.putSync(SettingPage.SORT_TYPE, 9)
+        }
         this.titleName = Utility.resourceToString(this.context,$r('app.string.artist'))
         break
 
       case 3:
         this.rightTopImage = $r('sys.symbol.sort')
+        if (this.sortType === 4) {
+          this.sortType = 9
+          PreferencesUtil.putSync(SettingPage.SORT_TYPE, 9)
+        }
         this.titleName = Utility.resourceToString(this.context,$r('app.string.album'))
         break
     }
@@ -6580,6 +6588,7 @@ export struct LocalMusic {
           this.songList = []
           this.songList.push(item)
           this.sonDataSource.pushArrayData(this.songList)
+          this.playQueueScope = 'single'
         }else if(isFromSonPlayList){ //点击来自右下角的播放列表
           this.currentSong = item
           if (index !== undefined) {
@@ -6587,23 +6596,17 @@ export struct LocalMusic {
           }
           // 确保播放列表是完整的歌单歌曲列表(已在finishLoadingPlaylist中设置)
           Logger.info(`heanup isFromSonPlayList分支 - 当前播放列表长度: ${this.songList.length}, 当前索引: ${this.curIndex}`)
+          this.playQueueScope = 'playlist'
         }else {
-          // 延迟加载完整播放列表
-          let globalVideoList: VideoItem[] = await this.loadFullSongList();
-          
-          // 如果延迟加载失败或返回空列表,回退到原逻辑
-          if (globalVideoList.length === 0) {
-            globalVideoList = Utility.getGlobalList(this.videoLocalList, CommonConstants.TYPE_LOCAL) as VideoItem[];
-            Logger.info(`heanup doPlay回退到videoLocalList - 列表长度: ${globalVideoList.length}`);
-          } else {
-            Logger.info(`heanup doPlay使用完整播放列表 - 列表长度: ${globalVideoList.length}`);
-          }
-          
+          const globalVideoList = this.buildPlayQueueFromView();
           this.curIndex = Utility.getCurIndexFromGlobalList(globalVideoList, item.filePath)
+          if (this.curIndex < 0) {
+            globalVideoList.push(item);
+            this.curIndex = globalVideoList.length - 1;
+          }
           this.songList = globalVideoList
-
           this.sonDataSource.pushArrayData(this.songList)
-          this.currentSong = globalVideoList[this.curIndex]
+          this.currentSong = this.songList[this.curIndex]
           Logger.info(`heanup doPlay设置播放列表 - 总歌曲数: ${this.songList.length}, 当前索引: ${this.curIndex}, 当前歌曲: ${this.currentSong?.name}`)
         }
 
@@ -6981,7 +6984,7 @@ export struct LocalMusic {
       this.dataSource.pushArrayData(this.videoLocalList);
       this.setButtonStatus();
       this.setAlphaBet();
-      
+      void this.prefetchNextPage(pageIndex + 1);
       this.isLoadingPage = false;
     } catch (err) {
       const error = err as Error;
@@ -7012,6 +7015,48 @@ export struct LocalMusic {
     }
   }
 
+  private async prefetchNextPage(nextPage: number): Promise<void> {
+    if (this.modeType !== 2 && this.modeType !== 3) {
+      return;
+    }
+    if (this.isCanBack || this.isHistory || this.isFavMusic) {
+      return;
+    }
+    if (!this.hasMoreData || this.pageCache.has(nextPage) || this.prefetchingPages.has(nextPage)) {
+      return;
+    }
+    this.prefetchingPages.add(nextPage);
+    try {
+      const options: PageQueryOptions = {
+        pageIndex: nextPage,
+        pageSize: this.pageSize,
+        sortType: this.sortType,
+        searchKeyword: this.isSearchMode ? this.searchText : undefined
+      };
+      let result: PageQueryResult = { items: [], totalCount: 0, hasMore: false };
+      if (this.modeType === 2) {
+        result = await this.table.queryArtistsPaged(options);
+        if (result.countMap) {
+          this.mergeArtistCountMap(result.countMap);
+        }
+      } else if (this.modeType === 3) {
+        result = await this.table.queryAlbumsPaged(options);
+        if (result.countMap) {
+          this.mergeAlbumCountMap(result.countMap);
+        }
+      }
+      if (result.items.length > 0) {
+        this.pageCache.set(nextPage, result.items);
+        this.cleanOldCache(nextPage);
+      }
+    } catch (err) {
+      const error = err as Error;
+      Logger.warn('heanup LocalMusic', `prefetchNextPage error: ${error.message}`);
+    } finally {
+      this.prefetchingPages.delete(nextPage);
+    }
+  }
+
   private mergeArtistCountMap(countMap: Map<string, number>): void {
     const nextMap = new Map(this.artistSongCountMap);
     countMap.forEach((count, artist) => {
@@ -7091,75 +7136,40 @@ export struct LocalMusic {
   }
 
   /**
-   * 延迟加载完整播放列表(仅在播放时调用)
-   * 一次性加载当前模式/文件夹下的所有音乐文件
+   * 构建播放队列(避免一次性加载全量)
    */
-  private async loadFullSongList(): Promise<VideoItem[]> {
-    // 如果已经加载过,直接返回
-    if (this.isFullSongListLoaded && this.fullSongList.length > 0) {
-      Logger.info('heanup LocalMusic', `loadFullSongList: 使用缓存 列表长度=${this.fullSongList.length}`);
-      return this.fullSongList;
+  private buildPlayQueueFromView(): Array<VideoItem> {
+    if (this.modeType === 4) {
+      this.playQueueScope = 'playlist';
+      return this.currentSongList;
     }
-
     if (this.isHistory) {
+      this.playQueueScope = 'history';
       return this.historyList;
     }
     if (this.isFavMusic) {
+      this.playQueueScope = 'fav';
       return this.favList;
     }
-    if (this.isCanBack && (this.modeType === 2 || this.modeType === 3)) {
-      return this.videoLocalList.filter((item: VideoItem) => item.type === CommonConstants.TYPE_LOCAL);
-    }
-
-    try {
-      Logger.info('heanup LocalMusic', `loadFullSongList: 开始加载完整列表 mode=${this.modeType}`);
-      
-      const options: PageQueryOptions = {
-        pageIndex: 0,
-        pageSize: 10000,  // 一次性加载全部(设置足够大的值)
-        sortType: this.sortType,
-        searchKeyword: this.isSearchMode ? this.searchText : undefined
-      };
-
-      if (this.modeType === 0) {
-        options.parentPath = this.currentPath;
-      }
-
-      let result: PageQueryResult = { items: [], totalCount: 0, hasMore: false };
-      
-      switch (this.modeType) {
-        case 0:
-          result = await this.table.queryByParentPathPaged(options);
-          break;
-        case 1:
-          result = await this.table.queryMediaLibraryPaged(options);
-          break;
-        case 2:
-          result = await this.table.queryArtistsPaged(options);
-          break;
-        case 3:
-          result = await this.table.queryAlbumsPaged(options);
-          break;
-        default:
-          result = await this.table.queryMediaLibraryPaged(options);
-          break;
-      }
+    this.playQueueScope = 'view';
+    return this.videoLocalList.filter((item: VideoItem) => item.type === CommonConstants.TYPE_LOCAL);
+  }
 
-      // 过滤出音乐文件(排除文件夹、艺术家、专辑等)
-      this.fullSongList = result.items.filter((item: VideoItem) => 
-        item.type !== CommonConstants.TYPE_IS_DIR && 
-        item.type !== CommonConstants.TYPE_IS_ARTIST && 
-        item.type !== CommonConstants.TYPE_IS_ALBUM
-      );
-      
-      this.isFullSongListLoaded = true;
-      Logger.info('heanup LocalMusic', `loadFullSongList: 加载完成 总歌曲数=${this.fullSongList.length}`);
-      
-      return this.fullSongList;
-    } catch (err) {
-      const error = err as Error;
-      Logger.error('heanup LocalMusic', `loadFullSongList error: ${error.message}`);
-      return [];
+  private async tryLoadNextPageForPlayback(): Promise<void> {
+    if (this.playQueueScope !== 'view') {
+      return;
+    }
+    if (this.isCanBack || this.isHistory || this.isFavMusic) {
+      return;
+    }
+    if (!this.hasMoreData || this.isLoadingPage) {
+      return;
+    }
+    await this.loadNextPage();
+    const nextList = this.videoLocalList.filter((item: VideoItem) => item.type === CommonConstants.TYPE_LOCAL);
+    if (nextList.length > this.songList.length) {
+      this.songList = nextList;
+      this.sonDataSource.pushArrayData(this.songList);
     }
   }
 
@@ -7170,8 +7180,6 @@ export struct LocalMusic {
     Logger.info('heanup LocalMusic', 'resetAndLoadFirstPage: 重置分页状态');
     this.currentPage = 0;
     this.videoLocalList = [];
-    this.fullSongList = [];  // 清空完整播放列表
-    this.isFullSongListLoaded = false;  // 重置加载标记
     this.pageCache.clear();
     this.hasMoreData = true;
     this.totalCount = 0;
@@ -14441,7 +14449,10 @@ export struct LocalMusic {
       return;
     }
     if (ArrayUtil.isNotEmpty(this.songList)) {
-      if (this.curIndex == this.songList.length - 1) {
+      if (this.curIndex >= this.songList.length - 1) {
+        await this.tryLoadNextPageForPlayback();
+      }
+      if (this.curIndex >= this.songList.length - 1) {
         this.curIndex = 0;
       } else {
         this.curIndex++;

+ 52 - 114
entry/src/main/ets/workers/Worker.ets

@@ -2,7 +2,7 @@ import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit
 import { FileUtil, StrUtil } from '@pura/harmony-utils';
 import { CommonConstants } from '../common/constants/CommonConstants';
 import Logger from '../common/util/Logger';
-import MediaTable from '../common/util/MediaTable';
+import MediaTable, { PageQueryOptions } from '../common/util/MediaTable';
 import { Utility } from '../common/util/Utility';
 import { VideoItem } from '../viewmodel/VideoItem';
 import { changeMusicCover, repairAudioMetadata } from '../common/util/MusicTagUtils';
@@ -165,23 +165,14 @@ async function queryMediaKuList(context: Context,packageName: string) {
       });
     });
 
-    // 2. 查询媒体库(isVideo=1)音乐 - 只获取前100条用于缓存
-    const originalList: VideoItem[] = await new Promise((resolve, reject) => {
-      table.query(0,  (result: VideoItem[] | null, err?: Error) => {
-        if (err) {
-          reject(err);
-        } else {
-          resolve(result || []);
-        }
-      });
-    });
+    // 2. 查询媒体库分页 - 只获取前100条用于缓存
+    const options: PageQueryOptions = { pageIndex: 0, pageSize: 100, sortType: 9 };
+    const result = await table.queryMediaLibraryPaged(options);
     
-    // 获取总数但不发送完整列表
-    const totalCount = originalList.length;
+    const totalCount = result.totalCount;
     Logger.info(`heanup Worker queryMediaKuList: 总歌曲数=${totalCount}`);
     
-    // 只发送前100条用于缓存和快速显示
-    const cachedList = originalList.slice(0, 100);
+    const cachedList = result.items;
     workerPort.postMessage({  code: 102, data: cachedList, totalCount: totalCount });
     
     // 3. 并行校验文件有效性 - 只校验前100条
@@ -208,7 +199,9 @@ async function queryMediaKuList(context: Context,packageName: string) {
     // 4. 过滤已删除项并返回结果
     const finalList = validList.filter(Boolean)  as VideoItem[];
     if(cachedList&&finalList&&cachedList.length!=finalList.length){
-      workerPort.postMessage({  code: 102, data: finalList, totalCount: finalList.length });
+      const deletedCount = cachedList.length - finalList.length;
+      const updatedTotal = Math.max(totalCount - deletedCount, finalList.length);
+      workerPort.postMessage({  code: 102, data: finalList, totalCount: updatedTotal });
     }
 
   }catch (err) {
@@ -223,59 +216,31 @@ async function  queryArtistTask(context:Context) {
   try {
     const table: MediaTable = new MediaTable(context);
 
-    table.getRdbStore(context,() => {
-
-
-      console.log(` onecold testtag queryArtistTask:`);
-      table.queryArtistsWithSongs((resultMap:  Map<string, VideoItem[]>) => {
-        // 不再保存完整的artistMap到内存,只生成艺术家列表
-        let artistList:Array<VideoItem> = []
-        console.log(` onecold testtag 当前艺术家数量:${resultMap.size}`);
-        
-        resultMap.forEach((songs,  artist) => {
-          let item = new VideoItem(artist,artist,artist,CommonConstants.TYPE_IS_ARTIST,0,'')
-          // 新增遍历逻辑
-          let targetPixelMap: string | null = null;
-          for (const song of songs) { // 使用for-of确保break生效
-            if (song?.pixelMapPath) {
-              targetPixelMap = song.pixelMapPath;
-              break;
-            }
-          }
-          item.pixelMapPath  = targetPixelMap ?? ''; // Nullish coalescing兜底
-
-          // console.log(` onecold testtag 艺术家:${artist}`);
-          artistList.push(item)
-        });
-        
-        // 核心排序逻辑
-        artistList.sort((a,  b) => {
-          const countA = resultMap.get(a.name)?.length  || 0;
-          const countB = resultMap.get(b.name)?.length  || 0;
-          return countB - countA; // 商量降序排列
-        });
-
-        // 只保存前60条艺术家的歌曲Map用于缓存
-        const topArtists = artistList.slice(0, 60);
-        const cachedArtistMap = new Map<string, VideoItem[]>();
-        topArtists.forEach(artist => {
-          const songs = resultMap.get(artist.name);
-          if (songs) {
-            cachedArtistMap.set(artist.name, songs);
-          }
-        });
-
-        // 只发送前100条艺术家列表和前60条的Map
-        const cachedArtistList = artistList.slice(0, 100);
-        workerPort.postMessage({ 
-          code: 103, 
-          data1: cachedArtistMap,  // 只包含前60个艺术家的歌曲
-          data2: cachedArtistList, // 只包含前100个艺术家
-          totalCount: artistList.length  // 总艺术家数量
-        });
-
+    await new Promise<void>((resolve, reject) => {
+      table.getRdbStore(context, (err: Error) => {
+        err ? reject(err) : resolve();
       });
+    });
 
+    console.log(` onecold testtag queryArtistTask:`);
+    const options: PageQueryOptions = { pageIndex: 0, pageSize: 100, sortType: 9 };
+    const result = await table.queryArtistsPaged(options);
+    const cachedArtistList = result.items;
+    const topArtists = cachedArtistList.slice(0, 60);
+    const cachedArtistMap = new Map<string, VideoItem[]>();
+
+    for (const artistItem of topArtists) {
+      const songs = await table.querySongsByArtist(artistItem.name, 4);
+      if (songs.length > 0) {
+        cachedArtistMap.set(artistItem.name, songs);
+      }
+    }
+
+    workerPort.postMessage({
+      code: 103,
+      data1: cachedArtistMap,  // 只包含前60个艺术家的歌曲
+      data2: cachedArtistList, // 只包含前100个艺术家
+      totalCount: result.totalCount  // 总艺术家数量
     });
 
 
@@ -291,57 +256,30 @@ async function  queryArtistTask(context:Context) {
 async function queryAlbumTask(context:Context) {
   try {
     const table: MediaTable = new MediaTable(context);
-    table.getRdbStore(context, async () => {
-
-
-      // 1. 执行数据库查询
-      const resultMap = await new Promise<Map<string, VideoItem[]>>(resolve => {
-        table.queryAlbumsWithSongs(resolve);
-      });
-
-      // 2. 构建专辑列表
-      const albumList: VideoItem[] = [];
-      resultMap.forEach((songs, albumName) => {
-        let item = new VideoItem(albumName, albumName, albumName, CommonConstants.TYPE_IS_ALBUM, 0, '')
-        // 新增遍历逻辑
-        let targetPixelMap: string | null = null;
-        for (const song of songs) { // 使用for-of确保break生效
-          if (song?.pixelMapPath) {
-            targetPixelMap = song.pixelMapPath;
-            break;
-          }
-        }
-        item.pixelMapPath  = targetPixelMap ?? ''; // Nullish coalescing兜底
-        albumList.push(item);
-      });
-
-      // 3. 按专辑歌曲数量降序排序
-      albumList.sort((a, b) => {
-        const aCount = resultMap.get(a.name)?.length || 0;
-        const bCount = resultMap.get(b.name)?.length || 0;
-        return bCount - aCount;
-      });
-      
-      // 只保存前60条专辑的歌曲Map用于缓存
-      const topAlbums = albumList.slice(0, 60);
-      const cachedAlbumMap = new Map<string, VideoItem[]>();
-      topAlbums.forEach(album => {
-        const songs = resultMap.get(album.name);
-        if (songs) {
-          cachedAlbumMap.set(album.name, songs);
-        }
+    await new Promise<void>((resolve, reject) => {
+      table.getRdbStore(context, (err: Error) => {
+        err ? reject(err) : resolve();
       });
+    });
 
-      // 只发送前100条专辑列表和前60条的Map
-      const cachedAlbumList = albumList.slice(0, 100);
-      workerPort.postMessage({ 
-        code: 104, 
-        data1: cachedAlbumMap,   // 只包含前60个专辑的歌曲
-        data2: cachedAlbumList,  // 只包含前100个专辑
-        totalCount: albumList.length  // 总专辑数量
-      });
+    const options: PageQueryOptions = { pageIndex: 0, pageSize: 100, sortType: 4 };
+    const result = await table.queryAlbumsPaged(options);
+    const cachedAlbumList = result.items;
+    const topAlbums = cachedAlbumList.slice(0, 60);
+    const cachedAlbumMap = new Map<string, VideoItem[]>();
 
+    for (const albumItem of topAlbums) {
+      const songs = await table.querySongsByAlbum(albumItem.name, 4);
+      if (songs.length > 0) {
+        cachedAlbumMap.set(albumItem.name, songs);
+      }
+    }
 
+    workerPort.postMessage({
+      code: 104,
+      data1: cachedAlbumMap,   // 只包含前60个专辑的歌曲
+      data2: cachedAlbumList,  // 只包含前100个专辑
+      totalCount: result.totalCount  // 总专辑数量
     });
 
   }catch (err) {