From 286401cb3aa23926486fa8cac8eb56634de0688a Mon Sep 17 00:00:00 2001 From: Peifan Li Date: Sat, 27 Dec 2025 23:00:40 -0500 Subject: [PATCH] refactor: refactor collection --- .../services/downloadService.test.ts | 2 +- backend/src/controllers/cookieController.ts | 1 - .../controllers/videoDownloadController.ts | 12 ++++- backend/src/services/downloadService.ts | 6 ++- .../downloaders/BilibiliDownloader.ts | 6 ++- .../bilibili/bilibiliCollection.ts | 17 ++++++- .../downloaders/bilibili/bilibiliSubtitle.ts | 17 +++++-- .../downloaders/bilibili/bilibiliVideo.ts | 47 ++++++++++++++----- 8 files changed, 84 insertions(+), 24 deletions(-) diff --git a/backend/src/__tests__/services/downloadService.test.ts b/backend/src/__tests__/services/downloadService.test.ts index 3f9941b..0b69a5d 100644 --- a/backend/src/__tests__/services/downloadService.test.ts +++ b/backend/src/__tests__/services/downloadService.test.ts @@ -41,7 +41,7 @@ describe('DownloadService', () => { it('should call BilibiliDownloader.downloadSinglePart', async () => { await downloadService.downloadSingleBilibiliPart('url', 1, 2, 'title'); - expect(BilibiliDownloader.downloadSinglePart).toHaveBeenCalledWith('url', 1, 2, 'title', undefined, undefined); + expect(BilibiliDownloader.downloadSinglePart).toHaveBeenCalledWith('url', 1, 2, 'title', undefined, undefined, undefined); }); it('should call BilibiliDownloader.downloadCollection', async () => { diff --git a/backend/src/controllers/cookieController.ts b/backend/src/controllers/cookieController.ts index 20d308e..dd02a13 100644 --- a/backend/src/controllers/cookieController.ts +++ b/backend/src/controllers/cookieController.ts @@ -43,4 +43,3 @@ export const deleteCookies = async ( cookieService.deleteCookies(); res.json(successMessage("Cookies deleted successfully")); }; - diff --git a/backend/src/controllers/videoDownloadController.ts b/backend/src/controllers/videoDownloadController.ts index 9789958..fd8323c 100644 --- a/backend/src/controllers/videoDownloadController.ts +++ b/backend/src/controllers/videoDownloadController.ts @@ -359,6 +359,15 @@ export const downloadVideo = async ( } } } else { + // Get collection name if collectionId is provided + let collectionName: string | undefined; + if (collectionId) { + const collection = storageService.getCollectionById(collectionId); + if (collection) { + collectionName = collection.name || collection.title; + } + } + // Download the first part firstPartResult = await downloadService.downloadSingleBilibiliPart( @@ -367,7 +376,8 @@ export const downloadVideo = async ( videosNumber, title || "Bilibili Video", downloadId, - registerCancel + registerCancel, + collectionName ); // Add to collection if needed diff --git a/backend/src/services/downloadService.ts b/backend/src/services/downloadService.ts index 6200da0..f3f4f70 100644 --- a/backend/src/services/downloadService.ts +++ b/backend/src/services/downloadService.ts @@ -131,7 +131,8 @@ export async function downloadSingleBilibiliPart( totalParts: number, seriesTitle: string, downloadId?: string, - onStart?: (cancel: () => void) => void + onStart?: (cancel: () => void) => void, + collectionName?: string ): Promise { return BilibiliDownloader.downloadSinglePart( url, @@ -139,7 +140,8 @@ export async function downloadSingleBilibiliPart( totalParts, seriesTitle, downloadId, - onStart + onStart, + collectionName ); } diff --git a/backend/src/services/downloaders/BilibiliDownloader.ts b/backend/src/services/downloaders/BilibiliDownloader.ts index 313a081..0704aed 100644 --- a/backend/src/services/downloaders/BilibiliDownloader.ts +++ b/backend/src/services/downloaders/BilibiliDownloader.ts @@ -127,7 +127,8 @@ export class BilibiliDownloader extends BaseDownloader { totalParts: number, seriesTitle: string, downloadId?: string, - onStart?: (cancel: () => void) => void + onStart?: (cancel: () => void) => void, + collectionName?: string ): Promise { return bilibiliVideo.downloadSinglePart( url, @@ -135,7 +136,8 @@ export class BilibiliDownloader extends BaseDownloader { totalParts, seriesTitle, downloadId, - onStart + onStart, + collectionName ); } diff --git a/backend/src/services/downloaders/bilibili/bilibiliCollection.ts b/backend/src/services/downloaders/bilibili/bilibiliCollection.ts index cc21026..444af5f 100644 --- a/backend/src/services/downloaders/bilibili/bilibiliCollection.ts +++ b/backend/src/services/downloaders/bilibili/bilibiliCollection.ts @@ -224,7 +224,9 @@ export async function downloadCollection( videoNumber, videos.length, title || "Collection", - downloadId + downloadId, + undefined, // onStart + mytubeCollection.name || mytubeCollection.title // collectionName ); // If download was successful, add to collection @@ -360,13 +362,24 @@ export async function downloadRemainingParts( ); } + // Get collection name if collectionId is provided + let collectionName: string | undefined; + if (collectionId) { + const collection = storageService.getCollectionById(collectionId); + if (collection) { + collectionName = collection.name || collection.title; + } + } + // Download this part const result = await downloadSinglePart( partUrl, part, totalParts, seriesTitle, - downloadId + downloadId, + undefined, // onStart + collectionName ); if (result.success && result.videoData) { diff --git a/backend/src/services/downloaders/bilibili/bilibiliSubtitle.ts b/backend/src/services/downloaders/bilibili/bilibiliSubtitle.ts index ed344bf..78584f1 100644 --- a/backend/src/services/downloaders/bilibili/bilibiliSubtitle.ts +++ b/backend/src/services/downloaders/bilibili/bilibiliSubtitle.ts @@ -12,7 +12,8 @@ import { getCookieHeader } from "./bilibiliCookie"; */ export async function downloadSubtitles( videoUrl: string, - baseFilename: string + baseFilename: string, + collectionName?: string ): Promise> { try { const videoId = extractBilibiliVideoId(videoUrl); @@ -94,8 +95,16 @@ export async function downloadSubtitles( const savedSubtitles = []; + // Determine subtitle directory based on collection name + const subtitleDir = collectionName + ? path.join(SUBTITLES_DIR, collectionName) + : SUBTITLES_DIR; + const subtitlePathPrefix = collectionName + ? `/subtitles/${collectionName}` + : `/subtitles`; + // Ensure subtitles directory exists - fs.ensureDirSync(SUBTITLES_DIR); + fs.ensureDirSync(subtitleDir); // Process subtitles (matching v1.5.14 approach - simple and direct) for (const sub of subtitlesData) { @@ -127,7 +136,7 @@ export async function downloadSubtitles( if (vttContent) { const subFilename = `${baseFilename}.${lang}.vtt`; - const subPath = path.join(SUBTITLES_DIR, subFilename); + const subPath = path.join(subtitleDir, subFilename); fs.writeFileSync(subPath, vttContent); logger.info(`Saved subtitle file: ${subPath}`); @@ -135,7 +144,7 @@ export async function downloadSubtitles( savedSubtitles.push({ language: lang, filename: subFilename, - path: `/subtitles/${subFilename}`, + path: `${subtitlePathPrefix}/${subFilename}`, }); } else { logger.warn(`Failed to convert subtitle to VTT format for ${lang}`); diff --git a/backend/src/services/downloaders/bilibili/bilibiliVideo.ts b/backend/src/services/downloaders/bilibili/bilibiliVideo.ts index e083af7..ef75552 100644 --- a/backend/src/services/downloaders/bilibili/bilibiliVideo.ts +++ b/backend/src/services/downloaders/bilibili/bilibiliVideo.ts @@ -376,7 +376,8 @@ export async function downloadSinglePart( totalParts: number, seriesTitle: string, downloadId?: string, - onStart?: (cancel: () => void) => void + onStart?: (cancel: () => void) => void, + collectionName?: string ): Promise { try { logger.info( @@ -395,9 +396,21 @@ export async function downloadSinglePart( const videoFilename = `${safeBaseFilename}.${mergeOutputFormat}`; const thumbnailFilename = `${safeBaseFilename}.jpg`; + // Determine directories based on collection name + const videoDir = collectionName + ? path.join(VIDEOS_DIR, collectionName) + : VIDEOS_DIR; + const imageDir = collectionName + ? path.join(IMAGES_DIR, collectionName) + : IMAGES_DIR; + + // Ensure directories exist + fs.ensureDirSync(videoDir); + fs.ensureDirSync(imageDir); + // Set full paths for video and thumbnail - const videoPath = path.join(VIDEOS_DIR, videoFilename); - const thumbnailPath = path.join(IMAGES_DIR, thumbnailFilename); + const videoPath = path.join(videoDir, videoFilename); + const thumbnailPath = path.join(imageDir, thumbnailFilename); let videoTitle, videoAuthor, @@ -528,9 +541,9 @@ export async function downloadSinglePart( const newVideoFilename = `${newSafeBaseFilename}.${mergeOutputFormat}`; const newThumbnailFilename = `${newSafeBaseFilename}.jpg`; - // Rename the files - const newVideoPath = path.join(VIDEOS_DIR, newVideoFilename); - const newThumbnailPath = path.join(IMAGES_DIR, newThumbnailFilename); + // Rename the files (use same directories as before) + const newVideoPath = path.join(videoDir, newVideoFilename); + const newThumbnailPath = path.join(imageDir, newThumbnailFilename); // Check if download was cancelled before processing files const downloader = new BilibiliDownloaderHelper(); @@ -601,7 +614,11 @@ export async function downloadSinglePart( }> = []; try { logger.info("Attempting to download subtitles..."); - subtitles = await downloadSubtitles(url, newSafeBaseFilename); + subtitles = await downloadSubtitles( + url, + newSafeBaseFilename, + collectionName + ); logger.info(`Downloaded ${subtitles.length} subtitles`); } catch (e) { // If it's a cancellation error, re-throw it @@ -646,9 +663,13 @@ export async function downloadSinglePart( thumbnailFilename: thumbnailSaved ? finalThumbnailFilename : undefined, subtitles: subtitles.length > 0 ? subtitles : undefined, thumbnailUrl: thumbnailUrl || undefined, - videoPath: `/videos/${finalVideoFilename}`, + videoPath: collectionName + ? `/videos/${collectionName}/${finalVideoFilename}` + : `/videos/${finalVideoFilename}`, thumbnailPath: thumbnailSaved - ? `/images/${finalThumbnailFilename}` + ? collectionName + ? `/images/${collectionName}/${finalThumbnailFilename}` + : `/images/${finalThumbnailFilename}` : null, duration: duration, fileSize: fileSize, @@ -679,12 +700,16 @@ export async function downloadSinglePart( const updatedVideo = storageService.updateVideo(existingVideo.id, { subtitles: subtitles.length > 0 ? subtitles : undefined, videoFilename: finalVideoFilename, - videoPath: `/videos/${finalVideoFilename}`, + videoPath: collectionName + ? `/videos/${collectionName}/${finalVideoFilename}` + : `/videos/${finalVideoFilename}`, thumbnailFilename: thumbnailSaved ? finalThumbnailFilename : existingVideo.thumbnailFilename, thumbnailPath: thumbnailSaved - ? `/images/${finalThumbnailFilename}` + ? collectionName + ? `/images/${collectionName}/${finalThumbnailFilename}` + : `/images/${finalThumbnailFilename}` : existingVideo.thumbnailPath, duration: duration, fileSize: fileSize,