refactor: Improve finding or creating collections in downloadVideo function

This commit is contained in:
Peifan Li
2025-12-21 17:14:35 -05:00
parent e4a34ac3ea
commit d366123a94
4 changed files with 108 additions and 20 deletions

View File

@@ -325,20 +325,6 @@ export const downloadVideo = async (
title || "Bilibili Video"
);
// Create a collection for the multi-part video if collectionName is provided
let collectionId: string | null = null;
if (collectionName) {
const newCollection = {
id: Date.now().toString(),
name: collectionName,
videos: [],
createdAt: new Date().toISOString(),
title: collectionName,
};
storageService.saveCollection(newCollection);
collectionId = newCollection.id;
}
// Start downloading the first part
const baseUrl = videoUrl.split("?")[0];
const firstPartUrl = `${baseUrl}?p=1`;
@@ -346,6 +332,46 @@ export const downloadVideo = async (
// Check if part 1 already exists
const existingPart1 = storageService.getVideoBySourceUrl(firstPartUrl);
let firstPartResult: DownloadResult;
let collectionId: string | null = null;
// Find or create collection
if (collectionName) {
// First, try to find if an existing part belongs to a collection
if (existingPart1?.id) {
const existingCollection = storageService.getCollectionByVideoId(existingPart1.id);
if (existingCollection) {
collectionId = existingCollection.id;
logger.info(
`Found existing collection "${existingCollection.name || existingCollection.title}" for this series`
);
}
}
// If no collection found from existing part, try to find by name
if (!collectionId) {
const collectionByName = storageService.getCollectionByName(collectionName);
if (collectionByName) {
collectionId = collectionByName.id;
logger.info(
`Found existing collection "${collectionName}" by name`
);
}
}
// If still no collection found, create a new one
if (!collectionId) {
const newCollection = {
id: Date.now().toString(),
name: collectionName,
videos: [],
createdAt: new Date().toISOString(),
title: collectionName,
};
storageService.saveCollection(newCollection);
collectionId = newCollection.id;
logger.info(`Created new collection "${collectionName}"`);
}
}
if (existingPart1) {
logger.info(
@@ -356,7 +382,7 @@ export const downloadVideo = async (
videoData: existingPart1,
};
// If we have a collection ID, make sure the existing video is in the collection
// 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)) {

View File

@@ -99,6 +99,56 @@ export function getCollectionById(id: string): Collection | undefined {
}
}
/**
* Find a collection that contains a specific video
*/
export function getCollectionByVideoId(videoId: string): Collection | undefined {
try {
const rows = db
.select({
c: collections,
cv: collectionVideos,
})
.from(collections)
.innerJoin(
collectionVideos,
eq(collections.id, collectionVideos.collectionId)
)
.where(eq(collectionVideos.videoId, videoId))
.all();
if (rows.length === 0) return undefined;
// Get the first collection that contains this video
const collectionId = rows[0].c.id;
return getCollectionById(collectionId);
} catch (error) {
logger.error(
"Error getting collection by video id",
error instanceof Error ? error : new Error(String(error))
);
return undefined;
}
}
/**
* Find a collection by name or title
*/
export function getCollectionByName(name: string): Collection | undefined {
try {
const allCollections = getCollections();
return allCollections.find(
(c) => c.name === name || c.title === name
);
} catch (error) {
logger.error(
"Error getting collection by name",
error instanceof Error ? error : new Error(String(error))
);
return undefined;
}
}
export function saveCollection(collection: Collection): Collection {
try {
db.transaction(() => {

View File

@@ -51,6 +51,8 @@ export {
export {
getCollections,
getCollectionById,
getCollectionByVideoId,
getCollectionByName,
saveCollection,
atomicUpdateCollection,
deleteCollection,

View File

@@ -60,16 +60,26 @@ const Collections: React.FC<CollectionsProps> = ({ collections, onItemClick }) =
<Folder fontSize="small" sx={{ mr: 1, color: 'secondary.main' }} />
<ListItemText
primary={collection.name}
primaryTypographyProps={{
variant: 'body2',
noWrap: true
slotProps={{
primary: {
variant: 'body2',
noWrap: true
}
}}
/>
<Chip
label={collection.videos.length}
label={`${Math.floor(collection.videos?.length || 0)}`}
size="small"
variant="outlined"
sx={{ height: 20, minWidth: 20, ml: 1 }}
sx={{
height: 20,
minWidth: 20,
ml: 1,
'& .MuiChip-label': {
padding: '0 4px',
fontSize: '0.75rem'
}
}}
/>
</ListItemButton>
))}