diff --git a/src/app/api/admin/data_migration/export/route.ts b/src/app/api/admin/data_migration/export/route.ts index ace7451..dc84755 100644 --- a/src/app/api/admin/data_migration/export/route.ts +++ b/src/app/api/admin/data_migration/export/route.ts @@ -115,21 +115,21 @@ export async function POST(req: NextRequest) { favorites, searchHistory, skipConfigs, - musicPlayRecords, + musicV2History, playlists ] = await Promise.all([ db.getAllPlayRecords(username), db.getAllFavorites(username), db.getSearchHistory(username), db.getAllSkipConfigs(username), - db.getAllMusicPlayRecords(username), - db.getUserMusicPlaylists(username) + db.listMusicV2History(username), + db.listMusicV2Playlists(username) ]); // 并行获取所有歌单的歌曲 const playlistsWithSongs = await Promise.all( playlists.map(async (playlist) => { - const songs = await db.getPlaylistSongs(playlist.id); + const songs = await db.listMusicV2PlaylistItems(playlist.id); return { ...playlist, songs }; }) ); @@ -141,8 +141,8 @@ export async function POST(req: NextRequest) { favorites, searchHistory, skipConfigs, - musicPlayRecords, - musicPlaylists: playlistsWithSongs, + musicV2History, + musicV2Playlists: playlistsWithSongs, passwordV2: finalPasswordV2 } }; diff --git a/src/app/api/admin/data_migration/import/route.ts b/src/app/api/admin/data_migration/import/route.ts index 203cc73..f36aee6 100644 --- a/src/app/api/admin/data_migration/import/route.ts +++ b/src/app/api/admin/data_migration/import/route.ts @@ -295,30 +295,43 @@ export async function POST(req: NextRequest) { } })(), - // 导入音乐播放记录(批量) + // 导入音乐 V2 播放记录(批量) (async () => { - if (user.musicPlayRecords) { - const entries = Object.entries(user.musicPlayRecords); - for (let j = 0; j < entries.length; j += DATA_BATCH_SIZE) { - const batch = entries.slice(j, j + DATA_BATCH_SIZE); - await Promise.all( - batch.map(([key, record]) => { - const [platform, id] = key.split('+'); - if (platform && id) { - return db.saveMusicPlayRecord(username, platform, id, record as any); - } - return Promise.resolve(); - }) + const historyRecords = Array.isArray(user.musicV2History) + ? user.musicV2History + : []; + + if (historyRecords.length > 0) { + for (let j = 0; j < historyRecords.length; j += DATA_BATCH_SIZE) { + const batch = historyRecords.slice(j, j + DATA_BATCH_SIZE); + await db.batchUpsertMusicV2History( + username, + batch.map((record: any) => ({ + ...record, + source: record.source, + songId: record.songId, + name: record.name, + artist: record.artist, + playProgressSec: record.playProgressSec || 0, + lastPlayedAt: record.lastPlayedAt || Date.now(), + playCount: record.playCount || 1, + createdAt: record.createdAt || Date.now(), + updatedAt: record.updatedAt || Date.now(), + })) ); } } })(), - // 导入音乐歌单 + // 导入音乐 V2 歌单 (async () => { - if (user.musicPlaylists && Array.isArray(user.musicPlaylists)) { - for (const playlist of user.musicPlaylists) { - await db.createMusicPlaylist(username, { + const playlists = Array.isArray(user.musicV2Playlists) + ? user.musicV2Playlists + : []; + + if (playlists.length > 0) { + for (const playlist of playlists) { + await db.createMusicV2Playlist(username, { id: playlist.id, name: playlist.name, description: playlist.description, @@ -330,15 +343,27 @@ export async function POST(req: NextRequest) { for (let j = 0; j < playlist.songs.length; j += DATA_BATCH_SIZE) { const batch = playlist.songs.slice(j, j + DATA_BATCH_SIZE); await Promise.all( - batch.map((song: any) => - db.addSongToPlaylist(playlist.id, { - platform: song.platform, - id: song.id, + batch.map((song: any, index: number) => + db.addMusicV2PlaylistItem(playlist.id, { + playlistId: playlist.id, + songId: song.songId || song.id, + source: song.source || song.platform, + songmid: song.songmid, name: song.name, artist: song.artist, album: song.album, - pic: song.pic, - duration: song.duration || 0, + cover: song.cover || song.pic, + durationSec: song.durationSec || song.duration || 0, + durationText: song.durationText, + hash: song.hash, + copyrightId: song.copyrightId, + albumId: song.albumId, + lrcUrl: song.lrcUrl, + mrcUrl: song.mrcUrl, + trcUrl: song.trcUrl, + sortOrder: song.sortOrder ?? (j + index), + addedAt: song.addedAt || Date.now(), + updatedAt: song.updatedAt || Date.now(), }) ) ); diff --git a/src/app/api/admin/music/route.ts b/src/app/api/admin/music/route.ts index 52faafe..324e285 100644 --- a/src/app/api/admin/music/route.ts +++ b/src/app/api/admin/music/route.ts @@ -3,7 +3,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { getAuthInfoFromCookie } from '@/lib/auth'; -import { getConfig } from '@/lib/config'; +import { getConfig, setCachedConfig } from '@/lib/config'; import { db } from '@/lib/db'; export const runtime = 'nodejs'; @@ -66,6 +66,7 @@ export async function POST(request: NextRequest) { // 写入数据库 await db.saveAdminConfig(adminConfig); + await setCachedConfig(adminConfig); return NextResponse.json( { ok: true }, diff --git a/src/lib/d1.db.ts b/src/lib/d1.db.ts index c489a32..021e4ca 100644 --- a/src/lib/d1.db.ts +++ b/src/lib/d1.db.ts @@ -2236,6 +2236,12 @@ export class D1Storage implements IStorage { 'favorites', 'search_history', 'skip_configs', + 'music_play_records', + 'music_playlists', + 'music_playlist_songs', + 'music_v2_history', + 'music_v2_playlists', + 'music_v2_playlist_items', 'danmaku_filter_configs', 'notifications', 'movie_requests', diff --git a/src/lib/postgres.db.ts b/src/lib/postgres.db.ts index 310c273..af4a8e2 100644 --- a/src/lib/postgres.db.ts +++ b/src/lib/postgres.db.ts @@ -2206,6 +2206,12 @@ export class PostgresStorage implements IStorage { 'favorites', 'search_history', 'skip_configs', + 'music_play_records', + 'music_playlists', + 'music_playlist_songs', + 'music_v2_history', + 'music_v2_playlists', + 'music_v2_playlist_items', 'danmaku_filter_configs', 'notifications', 'movie_requests', diff --git a/src/lib/redis-base.db.ts b/src/lib/redis-base.db.ts index fea6a7c..dee55b6 100644 --- a/src/lib/redis-base.db.ts +++ b/src/lib/redis-base.db.ts @@ -1044,6 +1044,22 @@ export abstract class BaseRedisStorage implements IStorage { } // 删除用户的歌单列表 await this.withRetry(() => this.adapter.del(this.musicPlaylistsKey(userName))); + + // 删除音乐 V2 播放记录 + await this.withRetry(() => this.adapter.del(this.musicV2HistoryKey(userName))); + + // 删除音乐 V2 歌单 + const musicV2PlaylistIds = await this.withRetry(() => + this.adapter.zRange(this.musicV2PlaylistsKey(userName), 0, -1) + ); + if (musicV2PlaylistIds && musicV2PlaylistIds.length > 0) { + for (const playlistId of musicV2PlaylistIds) { + const id = ensureString(playlistId); + await this.withRetry(() => this.adapter.del(this.musicV2PlaylistKey(id))); + await this.withRetry(() => this.adapter.del(this.musicV2PlaylistItemsKey(id))); + } + } + await this.withRetry(() => this.adapter.del(this.musicV2PlaylistsKey(userName))); } // ---------- 新版用户存储(使用Hash和Sorted Set) ---------- @@ -1683,7 +1699,7 @@ export abstract class BaseRedisStorage implements IStorage { // 删除所有用户及其数据 for (const username of allUsers) { - await this.deleteUser(username); + await this.deleteUserV2(username); } // 删除管理员配置