refactor: Update merge output format handling

This commit is contained in:
Peifan Li
2025-12-14 15:48:03 -05:00
parent 59a890aab9
commit 98694d5486
2 changed files with 76 additions and 40 deletions

View File

@@ -5,21 +5,21 @@ import { IMAGES_DIR, SUBTITLES_DIR, VIDEOS_DIR } from "../../config/paths";
import { DownloadCancelledError } from "../../errors/DownloadErrors"; import { DownloadCancelledError } from "../../errors/DownloadErrors";
import { bccToVtt } from "../../utils/bccToVtt"; import { bccToVtt } from "../../utils/bccToVtt";
import { import {
calculateDownloadedSize, calculateDownloadedSize,
formatBytes, formatBytes,
isCancellationError, isCancellationError,
isDownloadActive, isDownloadActive,
parseSize, parseSize,
} from "../../utils/downloadUtils"; } from "../../utils/downloadUtils";
import { import {
extractBilibiliVideoId, extractBilibiliVideoId,
formatVideoFilename, formatVideoFilename,
} from "../../utils/helpers"; } from "../../utils/helpers";
import { import {
executeYtDlpJson, executeYtDlpJson,
executeYtDlpSpawn, executeYtDlpSpawn,
getNetworkConfigFromUserConfig, getNetworkConfigFromUserConfig,
getUserYtDlpConfig, getUserYtDlpConfig,
} from "../../utils/ytDlpUtils"; } from "../../utils/ytDlpUtils";
import * as storageService from "../storageService"; import * as storageService from "../storageService";
import { Collection, Video } from "../storageService"; import { Collection, Video } from "../storageService";
@@ -214,7 +214,7 @@ export class BilibiliDownloader {
}> { }> {
try { try {
const videoUrl = `https://www.bilibili.com/video/${videoId}`; const videoUrl = `https://www.bilibili.com/video/${videoId}`;
// Get user config for network options // Get user config for network options
const userConfig = getUserYtDlpConfig(videoUrl); const userConfig = getUserYtDlpConfig(videoUrl);
const networkConfig = getNetworkConfigFromUserConfig(userConfig); const networkConfig = getNetworkConfigFromUserConfig(userConfig);
@@ -331,30 +331,39 @@ export class BilibiliDownloader {
); );
} }
// Prepare base flags from user config (excluding certain overridden options) // Prepare base flags from user config (excluding output options we manage)
const { const {
output: _output, output: _output,
o: _o, o: _o,
writeSubs: _writeSubs,
writeAutoSubs: _writeAutoSubs,
convertSubs: _convertSubs,
f: _f, f: _f,
format: _format, format: _format,
S: _S, S: _S,
formatSort: _formatSort, formatSort: _formatSort,
// Extract user subtitle preferences (use them if provided)
writeSubs: userWriteSubs,
writeAutoSubs: userWriteAutoSubs,
convertSubs: userConvertSubs,
// Extract user merge output format (use it if provided)
mergeOutputFormat: userMergeOutputFormat,
...safeUserConfig ...safeUserConfig
} = userConfig; } = userConfig;
// Determine merge output format: use user's choice or default to mp4
const mergeOutputFormat = userMergeOutputFormat || "mp4";
console.log(`Using merge output format: ${mergeOutputFormat}`);
// Prepare flags for yt-dlp - merge user config with required settings // Prepare flags for yt-dlp - merge user config with required settings
const flags: Record<string, any> = { const flags: Record<string, any> = {
...networkConfig, // Apply network settings ...networkConfig, // Apply network settings
...safeUserConfig, // Apply other user config ...safeUserConfig, // Apply other user config
output: outputTemplate, output: outputTemplate,
format: downloadFormat, format: downloadFormat,
mergeOutputFormat: "mp4", // Use user preferences if provided, otherwise use defaults
writeSubs: true, mergeOutputFormat: mergeOutputFormat,
writeAutoSubs: true, writeSubs: userWriteSubs !== undefined ? userWriteSubs : true,
convertSubs: "vtt", writeAutoSubs:
userWriteAutoSubs !== undefined ? userWriteAutoSubs : true,
convertSubs: userConvertSubs !== undefined ? userConvertSubs : "vtt",
ignoreErrors: true, // Continue even if subtitle download fails ignoreErrors: true, // Continue even if subtitle download fails
noWarnings: false, // Show warnings for debugging noWarnings: false, // Show warnings for debugging
}; };
@@ -857,12 +866,16 @@ export class BilibiliDownloader {
`Downloading Bilibili part ${partNumber}/${totalParts}: ${url}` `Downloading Bilibili part ${partNumber}/${totalParts}: ${url}`
); );
// Get user's yt-dlp configuration for merge output format
const userConfig = getUserYtDlpConfig(url);
const mergeOutputFormat = userConfig.mergeOutputFormat || "mp4";
// Create a safe base filename (without extension) // Create a safe base filename (without extension)
const timestamp = Date.now(); const timestamp = Date.now();
const safeBaseFilename = `video_${timestamp}`; const safeBaseFilename = `video_${timestamp}`;
// Add extensions for video and thumbnail // Add extensions for video and thumbnail (use user's format preference)
const videoFilename = `${safeBaseFilename}.mp4`; const videoFilename = `${safeBaseFilename}.${mergeOutputFormat}`;
const thumbnailFilename = `${safeBaseFilename}.jpg`; const thumbnailFilename = `${safeBaseFilename}.jpg`;
// Set full paths for video and thumbnail // Set full paths for video and thumbnail
@@ -926,7 +939,7 @@ export class BilibiliDownloader {
videoAuthor, videoAuthor,
videoDate videoDate
); );
const newVideoFilename = `${newSafeBaseFilename}.mp4`; const newVideoFilename = `${newSafeBaseFilename}.${mergeOutputFormat}`;
const newThumbnailFilename = `${newSafeBaseFilename}.jpg`; const newThumbnailFilename = `${newSafeBaseFilename}.jpg`;
// Rename the files // Rename the files

View File

@@ -321,8 +321,8 @@ export class YtDlpDownloader {
const newVideoPath = path.join(VIDEOS_DIR, finalVideoFilename); const newVideoPath = path.join(VIDEOS_DIR, finalVideoFilename);
const newThumbnailPath = path.join(IMAGES_DIR, finalThumbnailFilename); const newThumbnailPath = path.join(IMAGES_DIR, finalThumbnailFilename);
// Download the video // Note: newVideoPath will be updated below based on merge output format
console.log("Downloading video to:", newVideoPath); console.log("Preparing video download path:", newVideoPath);
if (downloadId) { if (downloadId) {
storageService.updateActiveDownload(downloadId, { storageService.updateActiveDownload(downloadId, {
@@ -348,32 +348,55 @@ export class YtDlpDownloader {
console.log("Using user-specified format:", userFormat); console.log("Using user-specified format:", userFormat);
} }
// Prepare base flags from user config (excluding certain overridden options) // Prepare base flags from user config (excluding output options we manage)
const { const {
output: _output, // Ignore user output template (we manage this) output: _output, // Ignore user output template (we manage this)
o: _o, o: _o,
writeSubs: _writeSubs, // We always enable subtitles
writeAutoSubs: _writeAutoSubs,
convertSubs: _convertSubs,
f: _f, // Format is handled specially above f: _f, // Format is handled specially above
format: _format, format: _format,
S: userFormatSort, // Format sort is handled specially S: userFormatSort, // Format sort is handled specially
formatSort: userFormatSort2, formatSort: userFormatSort2,
// Extract user subtitle preferences (use them if provided)
writeSubs: userWriteSubs,
writeAutoSubs: userWriteAutoSubs,
convertSubs: userConvertSubs,
// Extract user merge output format (use it if provided)
mergeOutputFormat: userMergeOutputFormat,
...safeUserConfig ...safeUserConfig
} = userConfig; } = userConfig;
// Get format sort option if user specified it // Get format sort option if user specified it
const formatSortValue = userFormatSort || userFormatSort2; const formatSortValue = userFormatSort || userFormatSort2;
// Prepare flags - user config first, then our required overrides // Determine merge output format: use user's choice or default to mp4
const mergeOutputFormat = userMergeOutputFormat || "mp4";
// Update the video path to use the correct extension based on merge format
const videoExtension = mergeOutputFormat;
const newVideoPathWithFormat = newVideoPath.replace(
/\.mp4$/,
`.${videoExtension}`
);
finalVideoFilename = finalVideoFilename.replace(
/\.mp4$/,
`.${videoExtension}`
);
console.log(
`Using merge output format: ${mergeOutputFormat}, downloading to: ${newVideoPathWithFormat}`
);
// Prepare flags - defaults first, then user config to allow overrides
const flags: Record<string, any> = { const flags: Record<string, any> = {
...safeUserConfig, // Apply user config first ...safeUserConfig, // Apply user config
output: newVideoPath, // Always use our output path output: newVideoPathWithFormat, // Always use our output path with correct extension
format: defaultFormat, format: defaultFormat,
mergeOutputFormat: "mp4", // Use user preferences if provided, otherwise use defaults
writeSubs: true, mergeOutputFormat: mergeOutputFormat,
writeAutoSubs: true, writeSubs: userWriteSubs !== undefined ? userWriteSubs : true,
convertSubs: "vtt", writeAutoSubs:
userWriteAutoSubs !== undefined ? userWriteAutoSubs : true,
convertSubs: userConvertSubs !== undefined ? userConvertSubs : "vtt",
// Only add PO token provider if configured // Only add PO token provider if configured
...(PROVIDER_SCRIPT ...(PROVIDER_SCRIPT
? { ? {
@@ -427,7 +450,7 @@ export class YtDlpDownloader {
// Clean up partial files // Clean up partial files
console.log("Cleaning up partial files..."); console.log("Cleaning up partial files...");
cleanupPartialVideoFiles(newVideoPath); cleanupPartialVideoFiles(newVideoPathWithFormat);
cleanupPartialVideoFiles(newThumbnailPath); cleanupPartialVideoFiles(newThumbnailPath);
cleanupSubtitleFiles(newSafeBaseFilename); cleanupSubtitleFiles(newSafeBaseFilename);
}); });
@@ -487,7 +510,7 @@ export class YtDlpDownloader {
} catch (error: any) { } catch (error: any) {
if (isCancellationError(error)) { if (isCancellationError(error)) {
console.log("Download was cancelled"); console.log("Download was cancelled");
cleanupPartialVideoFiles(newVideoPath); cleanupPartialVideoFiles(newVideoPathWithFormat);
cleanupSubtitleFiles(newSafeBaseFilename); cleanupSubtitleFiles(newSafeBaseFilename);
throw DownloadCancelledError.create(); throw DownloadCancelledError.create();
} }
@@ -501,7 +524,7 @@ export class YtDlpDownloader {
if (isSubtitleError) { if (isSubtitleError) {
// Check if video file was successfully downloaded // Check if video file was successfully downloaded
if (fs.existsSync(newVideoPath)) { if (fs.existsSync(newVideoPathWithFormat)) {
console.warn( console.warn(
"Subtitle download failed, but video was downloaded successfully. Continuing...", "Subtitle download failed, but video was downloaded successfully. Continuing...",
error.message error.message
@@ -524,7 +547,7 @@ export class YtDlpDownloader {
// Check if download was cancelled (it might have been removed from active downloads) // Check if download was cancelled (it might have been removed from active downloads)
if (!isDownloadActive(downloadId)) { if (!isDownloadActive(downloadId)) {
console.log("Download was cancelled (no longer in active downloads)"); console.log("Download was cancelled (no longer in active downloads)");
cleanupPartialVideoFiles(newVideoPath); cleanupPartialVideoFiles(newVideoPathWithFormat);
cleanupSubtitleFiles(newSafeBaseFilename); cleanupSubtitleFiles(newSafeBaseFilename);
throw DownloadCancelledError.create(); throw DownloadCancelledError.create();
} }