refactor: refactor collection

This commit is contained in:
Peifan Li
2025-12-27 23:00:40 -05:00
parent 56662e5a1e
commit 286401cb3a
8 changed files with 84 additions and 24 deletions

View File

@@ -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 () => {

View File

@@ -43,4 +43,3 @@ export const deleteCookies = async (
cookieService.deleteCookies();
res.json(successMessage("Cookies deleted successfully"));
};

View File

@@ -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

View File

@@ -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<DownloadResult> {
return BilibiliDownloader.downloadSinglePart(
url,
@@ -139,7 +140,8 @@ export async function downloadSingleBilibiliPart(
totalParts,
seriesTitle,
downloadId,
onStart
onStart,
collectionName
);
}

View File

@@ -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<DownloadResult> {
return bilibiliVideo.downloadSinglePart(
url,
@@ -135,7 +136,8 @@ export class BilibiliDownloader extends BaseDownloader {
totalParts,
seriesTitle,
downloadId,
onStart
onStart,
collectionName
);
}

View File

@@ -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) {

View File

@@ -12,7 +12,8 @@ import { getCookieHeader } from "./bilibiliCookie";
*/
export async function downloadSubtitles(
videoUrl: string,
baseFilename: string
baseFilename: string,
collectionName?: string
): Promise<Array<{ language: string; filename: string; path: string }>> {
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}`);

View File

@@ -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<DownloadResult> {
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,