refactor: Improve video part skipping and processing
This commit is contained in:
@@ -3,6 +3,7 @@ import { ValidationError } from "../errors/DownloadErrors";
|
||||
import downloadManager from "../services/downloadManager";
|
||||
import * as downloadService from "../services/downloadService";
|
||||
import * as storageService from "../services/storageService";
|
||||
import { DownloadResult } from "../services/downloaders/bilibili/types";
|
||||
import {
|
||||
extractBilibiliVideoId,
|
||||
extractSourceVideoId,
|
||||
@@ -342,26 +343,56 @@ export const downloadVideo = async (
|
||||
const baseUrl = videoUrl.split("?")[0];
|
||||
const firstPartUrl = `${baseUrl}?p=1`;
|
||||
|
||||
// Download the first part
|
||||
const firstPartResult =
|
||||
await downloadService.downloadSingleBilibiliPart(
|
||||
firstPartUrl,
|
||||
1,
|
||||
videosNumber,
|
||||
title || "Bilibili Video",
|
||||
downloadId,
|
||||
registerCancel
|
||||
);
|
||||
// Check if part 1 already exists
|
||||
const existingPart1 = storageService.getVideoBySourceUrl(firstPartUrl);
|
||||
let firstPartResult: DownloadResult;
|
||||
|
||||
// Add to collection if needed
|
||||
if (collectionId && firstPartResult.videoData) {
|
||||
storageService.atomicUpdateCollection(
|
||||
collectionId,
|
||||
(collection) => {
|
||||
collection.videos.push(firstPartResult.videoData!.id);
|
||||
return collection;
|
||||
}
|
||||
if (existingPart1) {
|
||||
logger.info(
|
||||
`Part 1/${videosNumber} already exists, skipping. Video ID: ${existingPart1.id}`
|
||||
);
|
||||
firstPartResult = {
|
||||
success: true,
|
||||
videoData: existingPart1,
|
||||
};
|
||||
|
||||
// If we have a collection ID, make sure the existing video is in the collection
|
||||
if (collectionId && existingPart1.id) {
|
||||
const collection = storageService.getCollectionById(collectionId);
|
||||
if (collection && !collection.videos.includes(existingPart1.id)) {
|
||||
storageService.atomicUpdateCollection(
|
||||
collectionId,
|
||||
(collection) => {
|
||||
if (!collection.videos.includes(existingPart1.id)) {
|
||||
collection.videos.push(existingPart1.id);
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Download the first part
|
||||
firstPartResult =
|
||||
await downloadService.downloadSingleBilibiliPart(
|
||||
firstPartUrl,
|
||||
1,
|
||||
videosNumber,
|
||||
title || "Bilibili Video",
|
||||
downloadId,
|
||||
registerCancel
|
||||
);
|
||||
|
||||
// Add to collection if needed
|
||||
if (collectionId && firstPartResult.videoData) {
|
||||
storageService.atomicUpdateCollection(
|
||||
collectionId,
|
||||
(collection) => {
|
||||
collection.videos.push(firstPartResult.videoData!.id);
|
||||
return collection;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Set up background download for remaining parts
|
||||
|
||||
@@ -306,9 +306,51 @@ export async function downloadRemainingParts(
|
||||
}
|
||||
|
||||
let successCount = 0;
|
||||
let skippedCount = 0;
|
||||
let failedParts: number[] = [];
|
||||
let skippedParts: number[] = [];
|
||||
|
||||
for (let part = startPart; part <= totalParts; part++) {
|
||||
// Construct URL for this part
|
||||
const partUrl = `${baseUrl}?p=${part}`;
|
||||
|
||||
// Check if this part already exists
|
||||
const existingVideo = storageService.getVideoBySourceUrl(partUrl);
|
||||
if (existingVideo) {
|
||||
skippedCount++;
|
||||
skippedParts.push(part);
|
||||
logger.info(
|
||||
`Part ${part}/${totalParts} already exists, skipping. Video ID: ${existingVideo.id}`
|
||||
);
|
||||
|
||||
// If we have a collection ID, make sure the existing video is in the collection
|
||||
if (collectionId && existingVideo.id) {
|
||||
try {
|
||||
const collection = storageService.getCollectionById(collectionId);
|
||||
if (collection && !collection.videos.includes(existingVideo.id)) {
|
||||
storageService.atomicUpdateCollection(
|
||||
collectionId,
|
||||
(collection: Collection) => {
|
||||
if (!collection.videos.includes(existingVideo.id)) {
|
||||
collection.videos.push(existingVideo.id);
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
);
|
||||
logger.info(
|
||||
`Added existing part ${part}/${totalParts} to collection ${collectionId}`
|
||||
);
|
||||
}
|
||||
} catch (collectionError) {
|
||||
logger.error(
|
||||
`Error adding existing part ${part}/${totalParts} to collection:`,
|
||||
collectionError
|
||||
);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.info(`Starting download of part ${part}/${totalParts}`);
|
||||
// Update status to show which part is being downloaded
|
||||
if (downloadId) {
|
||||
@@ -318,9 +360,6 @@ export async function downloadRemainingParts(
|
||||
);
|
||||
}
|
||||
|
||||
// Construct URL for this part
|
||||
const partUrl = `${baseUrl}?p=${part}`;
|
||||
|
||||
// Download this part
|
||||
const result = await downloadSinglePart(
|
||||
partUrl,
|
||||
@@ -372,13 +411,19 @@ export async function downloadRemainingParts(
|
||||
|
||||
// Log appropriate message based on results
|
||||
const remainingPartsCount = totalParts - startPart + 1;
|
||||
if (failedParts.length === 0) {
|
||||
const totalProcessed = successCount + skippedCount;
|
||||
|
||||
if (failedParts.length === 0 && skippedParts.length === 0) {
|
||||
logger.info(
|
||||
`All remaining parts (${startPart}-${totalParts}) of "${seriesTitle}" downloaded successfully`
|
||||
);
|
||||
} else if (failedParts.length === 0) {
|
||||
logger.info(
|
||||
`Processed ${totalProcessed}/${remainingPartsCount} remaining parts (${startPart}-${totalParts}) of "${seriesTitle}". Downloaded: ${successCount}, Skipped (already exist): ${skippedCount} (parts: ${skippedParts.join(", ")})`
|
||||
);
|
||||
} else {
|
||||
logger.warn(
|
||||
`Downloaded ${successCount}/${remainingPartsCount} remaining parts (${startPart}-${totalParts}) of "${seriesTitle}". Failed parts: ${failedParts.join(", ")}`
|
||||
`Processed ${totalProcessed}/${remainingPartsCount} remaining parts (${startPart}-${totalParts}) of "${seriesTitle}". Downloaded: ${successCount}, Skipped: ${skippedCount} (parts: ${skippedParts.join(", ")}), Failed: ${failedParts.length} (parts: ${failedParts.join(", ")})`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -84,6 +84,16 @@ export function getVideoById(id: string): import("./types").Video | undefined {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a video part already exists by sourceUrl
|
||||
* Returns the existing video if found, undefined otherwise
|
||||
*/
|
||||
export function getVideoPartBySourceUrl(
|
||||
sourceUrl: string
|
||||
): import("./types").Video | undefined {
|
||||
return getVideoBySourceUrl(sourceUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format legacy filenames to the new standard format: Title-Author-YYYY
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user