feat(merge-split): add createMergeGroup IPC handler
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4750af2f97
commit
645d2f147b
83
src/main.ts
83
src/main.ts
@ -2387,7 +2387,8 @@ async function cutVideo(
|
||||
async function mergeVideos(
|
||||
inputFiles: string[],
|
||||
outputFile: string,
|
||||
onProgress: (percent: number) => void
|
||||
onProgress: (percent: number) => void,
|
||||
totalDurationSec?: number
|
||||
): Promise<boolean> {
|
||||
const ffmpegReady = await ensureFfmpegInstalled();
|
||||
if (!ffmpegReady) {
|
||||
@ -2397,7 +2398,10 @@ async function mergeVideos(
|
||||
|
||||
const ffmpeg = getFFmpegPath();
|
||||
const concatFile = path.join(app.getPath('temp'), `concat_${Date.now()}.txt`);
|
||||
const concatContent = inputFiles.map((filePath) => `file '${filePath.replace(/'/g, "'\\''")}'`).join('\n');
|
||||
const concatContent = inputFiles.map((filePath) => {
|
||||
const normalized = filePath.replace(/\\/g, '/');
|
||||
return `file '${normalized.replace(/'/g, "'\\''")}'`;
|
||||
}).join('\n');
|
||||
fs.writeFileSync(concatFile, concatContent);
|
||||
|
||||
let mergeInputBytes = 0;
|
||||
@ -3494,6 +3498,81 @@ ipcMain.handle('retry-failed-downloads', () => {
|
||||
return downloadQueue;
|
||||
});
|
||||
|
||||
ipcMain.handle('create-merge-group', (_, itemIds: string[]) => {
|
||||
const selectedItems = downloadQueue.filter(item => itemIds.includes(item.id));
|
||||
|
||||
if (selectedItems.length < 2) {
|
||||
return downloadQueue;
|
||||
}
|
||||
|
||||
// Validate all are pending
|
||||
if (selectedItems.some(item => item.status !== 'pending')) {
|
||||
return downloadQueue;
|
||||
}
|
||||
|
||||
// Sort chronologically by ISO timestamp (handles same-day different times)
|
||||
const sorted = [...selectedItems].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
|
||||
|
||||
// Calculate total duration
|
||||
const totalDurationSec = sorted.reduce((sum, item) => sum + parseDuration(item.duration_str), 0);
|
||||
const totalDurationStr = (() => {
|
||||
const h = Math.floor(totalDurationSec / 3600);
|
||||
const m = Math.floor((totalDurationSec % 3600) / 60);
|
||||
const s = totalDurationSec % 60;
|
||||
const parts: string[] = [];
|
||||
if (h > 0) parts.push(`${h}h`);
|
||||
if (m > 0) parts.push(`${m}m`);
|
||||
if (s > 0 || parts.length === 0) parts.push(`${s}s`);
|
||||
return parts.join('');
|
||||
})();
|
||||
|
||||
// Generate title (language-aware)
|
||||
const first = sorted[0];
|
||||
const isEnglish = config.language === 'en';
|
||||
const title = sorted.length === 2
|
||||
? `Merge: ${first.title} + ${sorted[1].title}`
|
||||
: `Merge: ${first.title} + ${sorted.length - 1} ${isEnglish ? 'more' : 'weitere'}`;
|
||||
|
||||
// Build merge group
|
||||
const mergeGroup: MergeGroup = {
|
||||
items: sorted.map(item => ({
|
||||
url: item.url,
|
||||
title: item.title,
|
||||
date: item.date,
|
||||
streamer: item.streamer,
|
||||
duration_str: item.duration_str
|
||||
})),
|
||||
mergePhase: 'downloading',
|
||||
currentItemIndex: 0,
|
||||
downloadedFiles: {},
|
||||
totalDurationSec
|
||||
};
|
||||
|
||||
// Create merged queue item
|
||||
const mergedItem: QueueItem = {
|
||||
id: generateQueueItemId(),
|
||||
title,
|
||||
url: first.url,
|
||||
date: first.date,
|
||||
streamer: first.streamer,
|
||||
duration_str: totalDurationStr,
|
||||
status: 'pending',
|
||||
progress: 0,
|
||||
mergeGroup
|
||||
};
|
||||
|
||||
// Find position of first selected item
|
||||
const firstIndex = downloadQueue.findIndex(item => itemIds.includes(item.id));
|
||||
|
||||
// Remove selected items and insert merged item at first position
|
||||
downloadQueue = downloadQueue.filter(item => !itemIds.includes(item.id));
|
||||
downloadQueue.splice(firstIndex >= 0 ? Math.min(firstIndex, downloadQueue.length) : downloadQueue.length, 0, mergedItem);
|
||||
|
||||
saveQueue(downloadQueue);
|
||||
emitQueueUpdated();
|
||||
return downloadQueue;
|
||||
});
|
||||
|
||||
ipcMain.handle('start-download', async () => {
|
||||
downloadQueue = downloadQueue.map((item) => item.status === 'paused' ? { ...item, status: 'pending' } : item);
|
||||
|
||||
|
||||
@ -123,6 +123,7 @@ contextBridge.exposeInMainWorld('api', {
|
||||
reorderQueue: (orderIds: string[]) => ipcRenderer.invoke('reorder-queue', orderIds),
|
||||
clearCompleted: () => ipcRenderer.invoke('clear-completed'),
|
||||
retryFailedDownloads: () => ipcRenderer.invoke('retry-failed-downloads'),
|
||||
createMergeGroup: (itemIds: string[]) => ipcRenderer.invoke('create-merge-group', itemIds),
|
||||
|
||||
// Download
|
||||
startDownload: () => ipcRenderer.invoke('start-download'),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user