音乐数据v2导入导出适配

This commit is contained in:
mtvpls
2026-04-14 14:18:02 +08:00
parent ab356bf8fc
commit 1dcf27d1f8
6 changed files with 85 additions and 31 deletions
@@ -115,21 +115,21 @@ export async function POST(req: NextRequest) {
favorites, favorites,
searchHistory, searchHistory,
skipConfigs, skipConfigs,
musicPlayRecords, musicV2History,
playlists playlists
] = await Promise.all([ ] = await Promise.all([
db.getAllPlayRecords(username), db.getAllPlayRecords(username),
db.getAllFavorites(username), db.getAllFavorites(username),
db.getSearchHistory(username), db.getSearchHistory(username),
db.getAllSkipConfigs(username), db.getAllSkipConfigs(username),
db.getAllMusicPlayRecords(username), db.listMusicV2History(username),
db.getUserMusicPlaylists(username) db.listMusicV2Playlists(username)
]); ]);
// 并行获取所有歌单的歌曲 // 并行获取所有歌单的歌曲
const playlistsWithSongs = await Promise.all( const playlistsWithSongs = await Promise.all(
playlists.map(async (playlist) => { playlists.map(async (playlist) => {
const songs = await db.getPlaylistSongs(playlist.id); const songs = await db.listMusicV2PlaylistItems(playlist.id);
return { ...playlist, songs }; return { ...playlist, songs };
}) })
); );
@@ -141,8 +141,8 @@ export async function POST(req: NextRequest) {
favorites, favorites,
searchHistory, searchHistory,
skipConfigs, skipConfigs,
musicPlayRecords, musicV2History,
musicPlaylists: playlistsWithSongs, musicV2Playlists: playlistsWithSongs,
passwordV2: finalPasswordV2 passwordV2: finalPasswordV2
} }
}; };
@@ -295,30 +295,43 @@ export async function POST(req: NextRequest) {
} }
})(), })(),
// 导入音乐播放记录(批量) // 导入音乐 V2 播放记录(批量)
(async () => { (async () => {
if (user.musicPlayRecords) { const historyRecords = Array.isArray(user.musicV2History)
const entries = Object.entries(user.musicPlayRecords); ? user.musicV2History
for (let j = 0; j < entries.length; j += DATA_BATCH_SIZE) { : [];
const batch = entries.slice(j, j + DATA_BATCH_SIZE);
await Promise.all( if (historyRecords.length > 0) {
batch.map(([key, record]) => { for (let j = 0; j < historyRecords.length; j += DATA_BATCH_SIZE) {
const [platform, id] = key.split('+'); const batch = historyRecords.slice(j, j + DATA_BATCH_SIZE);
if (platform && id) { await db.batchUpsertMusicV2History(
return db.saveMusicPlayRecord(username, platform, id, record as any); username,
} batch.map((record: any) => ({
return Promise.resolve(); ...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 () => { (async () => {
if (user.musicPlaylists && Array.isArray(user.musicPlaylists)) { const playlists = Array.isArray(user.musicV2Playlists)
for (const playlist of user.musicPlaylists) { ? user.musicV2Playlists
await db.createMusicPlaylist(username, { : [];
if (playlists.length > 0) {
for (const playlist of playlists) {
await db.createMusicV2Playlist(username, {
id: playlist.id, id: playlist.id,
name: playlist.name, name: playlist.name,
description: playlist.description, 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) { for (let j = 0; j < playlist.songs.length; j += DATA_BATCH_SIZE) {
const batch = playlist.songs.slice(j, j + DATA_BATCH_SIZE); const batch = playlist.songs.slice(j, j + DATA_BATCH_SIZE);
await Promise.all( await Promise.all(
batch.map((song: any) => batch.map((song: any, index: number) =>
db.addSongToPlaylist(playlist.id, { db.addMusicV2PlaylistItem(playlist.id, {
platform: song.platform, playlistId: playlist.id,
id: song.id, songId: song.songId || song.id,
source: song.source || song.platform,
songmid: song.songmid,
name: song.name, name: song.name,
artist: song.artist, artist: song.artist,
album: song.album, album: song.album,
pic: song.pic, cover: song.cover || song.pic,
duration: song.duration || 0, 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(),
}) })
) )
); );
+2 -1
View File
@@ -3,7 +3,7 @@
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { getAuthInfoFromCookie } from '@/lib/auth'; import { getAuthInfoFromCookie } from '@/lib/auth';
import { getConfig } from '@/lib/config'; import { getConfig, setCachedConfig } from '@/lib/config';
import { db } from '@/lib/db'; import { db } from '@/lib/db';
export const runtime = 'nodejs'; export const runtime = 'nodejs';
@@ -66,6 +66,7 @@ export async function POST(request: NextRequest) {
// 写入数据库 // 写入数据库
await db.saveAdminConfig(adminConfig); await db.saveAdminConfig(adminConfig);
await setCachedConfig(adminConfig);
return NextResponse.json( return NextResponse.json(
{ ok: true }, { ok: true },
+6
View File
@@ -2236,6 +2236,12 @@ export class D1Storage implements IStorage {
'favorites', 'favorites',
'search_history', 'search_history',
'skip_configs', 'skip_configs',
'music_play_records',
'music_playlists',
'music_playlist_songs',
'music_v2_history',
'music_v2_playlists',
'music_v2_playlist_items',
'danmaku_filter_configs', 'danmaku_filter_configs',
'notifications', 'notifications',
'movie_requests', 'movie_requests',
+6
View File
@@ -2206,6 +2206,12 @@ export class PostgresStorage implements IStorage {
'favorites', 'favorites',
'search_history', 'search_history',
'skip_configs', 'skip_configs',
'music_play_records',
'music_playlists',
'music_playlist_songs',
'music_v2_history',
'music_v2_playlists',
'music_v2_playlist_items',
'danmaku_filter_configs', 'danmaku_filter_configs',
'notifications', 'notifications',
'movie_requests', 'movie_requests',
+17 -1
View File
@@ -1044,6 +1044,22 @@ export abstract class BaseRedisStorage implements IStorage {
} }
// 删除用户的歌单列表 // 删除用户的歌单列表
await this.withRetry(() => this.adapter.del(this.musicPlaylistsKey(userName))); 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 ---------- // ---------- 新版用户存储(使用Hash和Sorted Set ----------
@@ -1683,7 +1699,7 @@ export abstract class BaseRedisStorage implements IStorage {
// 删除所有用户及其数据 // 删除所有用户及其数据
for (const username of allUsers) { for (const username of allUsers) {
await this.deleteUser(username); await this.deleteUserV2(username);
} }
// 删除管理员配置 // 删除管理员配置