-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Optimize FFmpeg performance with multi-threading and parallel processing #2501
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Added -threads 0 for full CPU utilization, increased concurrency from 1 to dynamic CPU-based values for segment processing, and implemented throttled progress updates. Maintains lossless quality while significantly improving processing speed.
|
""" WalkthroughA new module introduces a suite of FFmpeg performance optimization utilities, focusing on argument tuning, concurrency, and memory management. Existing FFmpeg process and progress functions are enhanced with optimized arguments, throttled progress updates, and reduced memory overhead. The FFmpeg operations hook now increases concurrency for file and segment processing tasks. Changes
Sequence Diagram(s)sequenceDiagram
participant UI
participant useFfmpegOperations
participant ffmpeg-optimizations
participant ffmpeg
UI->>useFfmpegOperations: Request file/segment operation
useFfmpegOperations->>ffmpeg-optimizations: Get optimized concurrency/config
useFfmpegOperations->>ffmpeg: Run FFmpeg process with optimized args
ffmpeg-->>useFfmpegOperations: Process result/progress
useFfmpegOperations-->>UI: Report progress/result
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40 minutes Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
🧹 Nitpick comments (7)
src/renderer/src/hooks/useFfmpegOperations.ts (2)
75-80: Fix formatting issuesRemove trailing spaces to comply with code style guidelines.
// Performance optimization: Improved file deletion with better concurrency async function tryDeleteFiles(paths: string[]) { - return pMap(paths, (path) => unlinkWithRetry(path).catch((err) => console.error('Failed to delete', path, err)), { - concurrency: OPTIMIZED_CONCURRENCY - }); + return pMap(paths, (path) => unlinkWithRetry(path).catch((err) => console.error('Failed to delete', path, err)), { + concurrency: OPTIMIZED_CONCURRENCY, + }); }
641-641: Consider resource constraints for parallel segment cuttingWhile increasing concurrency improves performance, cutting multiple segments simultaneously can be resource-intensive (CPU, memory, disk I/O). Consider monitoring system resources or implementing adaptive concurrency based on segment complexity.
src/main/ffmpeg.ts (4)
80-86: Good optimization for readline performanceSetting
crlfDelay: InfinityandhistorySize: 0reduces memory overhead. However, fix the formatting issues.// Performance optimization: Create readline interface with optimized settings - const rl = readline.createInterface({ + const rl = readline.createInterface({ input: process.stderr, // Optimize for performance crlfDelay: Infinity, historySize: 0, // Disable history to save memory });
88-107: Consider adjusting the progress throttle intervalWhile 50ms throttling reduces overhead, it might still be too frequent for UI updates. Consider increasing to 100-200ms for better performance without sacrificing user experience. Also fix formatting issues.
// Throttle progress updates to reduce UI overhead let lastProgressTime = 0; - const progressThrottle = 50; // Update progress max every 50ms + const progressThrottle = 100; // Update progress max every 100ms let lastProgress = 0; rl.on('line', (line) => { // console.log('progress', line); try { const now = Date.now(); - + // Skip processing if too frequent (performance optimization) if (now - lastProgressTime < progressThrottle) return; const progress = parseFfmpegProgressLine({ line, customMatcher, duration }); if (progress != null && Math.abs(progress - lastProgress) > 0.001) { // Only update if progress changed significantly onProgress(progress); lastProgressTime = now; lastProgress = progress; }
142-149: Excellent FFmpeg performance argumentsThe added arguments effectively optimize FFmpeg:
-threads 0: Uses all available CPU cores-fflags +discardcorrupt+genpts: Improves error handling and timestamp generation-avioflags direct: Reduces I/O buffering overheadFix the formatting issue:
const optimizedArgs = [ '-threads', '0', // Use all available CPU cores '-fflags', '+discardcorrupt+genpts', // Improve error handling and timestamp generation '-avioflags', 'direct', // Reduce I/O overhead - ...args + ...args, ];
151-163: Good process optimization optionsThe execa options improve process management:
windowsHide: Prevents console window on Windowscleanup: Ensures proper resource cleanupmaxBuffer: 64MB is reasonable for video processingFix formatting:
const abortController = new AbortController(); - + // Optimize process creation options const optimizedExecaOptions = { - ...getExecaOptions({ - ...customExecaOptions, + ...getExecaOptions({ + ...customExecaOptions, cancelSignal: abortController.signal, // Performance optimizations windowsHide: true, cleanup: true, maxBuffer: 1024 * 1024 * 64, // 64MB buffer }), };src/main/ffmpeg-optimizations.ts (1)
125-151: Good stream processing configurationThe function provides sensible defaults for memory-efficient stream processing. Just fix the type assertion:
execaOptions: { buffer: false, stdio: ['pipe', 'pipe', 'pipe'], maxBuffer: bufferSize, - encoding: 'buffer' as const, + encoding: 'buffer' as 'buffer', // Optimize child process creation windowsHide: true, // Reduce memory overhead cleanup: true, all: false, } as ExecaOptions,
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/main/ffmpeg-optimizations.ts(1 hunks)src/main/ffmpeg.ts(3 hunks)src/renderer/src/hooks/useFfmpegOperations.ts(6 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/renderer/src/hooks/useFfmpegOperations.ts (2)
src/renderer/src/util.ts (1)
unlinkWithRetry(131-131)src/main/ffmpeg.ts (1)
getDuration(575-577)
src/main/ffmpeg.ts (2)
src/main/progress.ts (1)
parseFfmpegProgressLine(2-46)src/renderer/src/ffmpeg.ts (2)
getFfmpegPath(20-20)getFfCommandLine(20-20)
🪛 ESLint
src/renderer/src/hooks/useFfmpegOperations.ts
[error] 17-17: 'optimizeFFmpegArgs' is defined but never used.
(@typescript-eslint/no-unused-vars)
[error] 17-17: 'createOptimizedBatchProcessor' is defined but never used.
(@typescript-eslint/no-unused-vars)
[error] 17-17: 'getOptimizedCodecArgs' is defined but never used.
(@typescript-eslint/no-unused-vars)
[error] 26-26: 'SEGMENT_BATCH_SIZE' is assigned a value but never used.
(@typescript-eslint/no-unused-vars)
[error] 77-77: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 78-78: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 78-78: Missing trailing comma.
(comma-dangle)
src/main/ffmpeg.ts
[error] 81-81: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 98-98: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 147-148: Missing trailing comma.
(comma-dangle)
[error] 151-151: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 154-154: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 155-155: Trailing spaces not allowed.
(no-trailing-spaces)
src/main/ffmpeg-optimizations.ts
[error] 1-1: Filename is not in camel case or pascal case. Rename it to ffmpegOptimizations.ts or FfmpegOptimizations.ts.
(unicorn/filename-case)
[error] 4-4: 'execa' is defined but never used.
(@typescript-eslint/no-unused-vars)
[error] 37-37: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 38-38: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 50-50: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 53-53: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 64-64: Missing radix parameter.
(radix)
[error] 64-64: Missing radix parameter.
(radix)
[error] 66-66: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 67-67: Unexpected use of 'isNaN'. Use Number.isNaN instead https://github.com/airbnb/javascript#standard-library--isnan
(no-restricted-globals)
[error] 72-72: Remove unused catch binding err.
(unicorn/prefer-optional-catch-binding)
[error] 81-81: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 86-87: Missing trailing comma.
(comma-dangle)
[error] 89-89: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 91-91: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 93-93: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 97-97: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 99-108: Function declared in a loop contains unsafe references to variable(s) 'completed', 'completed', 'completed'.
(no-loop-func)
[error] 99-99: 'index' is defined but never used.
(@typescript-eslint/no-unused-vars)
[error] 101-101: Unary operator '++' used.
(no-plusplus)
[error] 102-102: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 106-106: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 109-109: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 113-113: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 120-120: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 131-131: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 144-144: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 148-149: Missing trailing comma.
(comma-dangle)
[error] 155-155: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 164-164: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 169-169: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 176-180: Unnecessarily quoted property 'libx264' found.
(quote-props)
[error] 179-180: Missing trailing comma.
(comma-dangle)
[error] 181-185: Unnecessarily quoted property 'libx265' found.
(quote-props)
[error] 184-185: Missing trailing comma.
(comma-dangle)
[error] 186-190: Unnecessarily quoted property 'copy' found.
(quote-props)
[error] 189-190: Missing trailing comma.
(comma-dangle)
[error] 190-191: Missing trailing comma.
(comma-dangle)
[error] 192-192: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 197-197: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 199-199: Expected parentheses around arrow function argument.
(arrow-parens)
[error] 200-200: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 202-202: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 204-204: Missing radix parameter.
(radix)
[error] 205-205: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 210-210: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 215-215: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 221-221: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 226-226: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 227-227: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 227-227: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 228-228: Do not use new Array().
(unicorn/no-new-array)
[error] 230-230: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 231-231: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 234-234: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 235-235: Unary operator '++' used.
(no-plusplus)
[error] 237-237: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 244-244: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 250-250: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 253-253: Trailing spaces not allowed.
(no-trailing-spaces)
🔇 Additional comments (2)
src/renderer/src/hooks/useFfmpegOperations.ts (2)
143-143: Good optimization for parallel duration fetchingUsing dynamic concurrency based on hardware capabilities instead of a fixed value improves performance scalability.
914-914: Effective optimization for stream extractionParallel stream extraction is a good optimization as these operations are typically I/O bound rather than CPU bound.
Also applies to: 950-950
| @@ -0,0 +1,267 @@ | |||
| // Performance optimizations for LosslessCut FFmpeg operations | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix filename to match project conventions
The filename should follow the project's naming convention.
Rename the file from ffmpeg-optimizations.ts to ffmpegOptimizations.ts to match the camelCase convention used in the project.
🧰 Tools
🪛 ESLint
[error] 1-1: Filename is not in camel case or pascal case. Rename it to ffmpegOptimizations.ts or FfmpegOptimizations.ts.
(unicorn/filename-case)
🤖 Prompt for AI Agents
In src/main/ffmpeg-optimizations.ts at line 1, the filename does not follow the
project's camelCase naming convention. Rename the file from
ffmpeg-optimizations.ts to ffmpegOptimizations.ts to align with the project's
standard.
src/main/ffmpeg-optimizations.ts
Outdated
| // Performance optimizations for LosslessCut FFmpeg operations | ||
| // This file contains optimized versions of key functions to improve processing speed | ||
|
|
||
| import { execa, ExecaOptions } from 'execa'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unused import
The execa import is not used in this file.
-import { execa, ExecaOptions } from 'execa';
+import { ExecaOptions } from 'execa';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { execa, ExecaOptions } from 'execa'; | |
| import { ExecaOptions } from 'execa'; |
🧰 Tools
🪛 ESLint
[error] 4-4: 'execa' is defined but never used.
(@typescript-eslint/no-unused-vars)
🤖 Prompt for AI Agents
In src/main/ffmpeg-optimizations.ts at line 4, remove the unused import of
`execa` while keeping the import of `ExecaOptions` since only `execa` is unused.
This cleans up the code by eliminating unnecessary imports.
src/main/ffmpeg-optimizations.ts
Outdated
| // Optimization 2: Improved progress handling with better performance | ||
| export function optimizedHandleProgress( | ||
| process: { stderr: Readable | null }, | ||
| duration: number | undefined, | ||
| onProgress: (progress: number) => void, | ||
| customMatcher?: (line: string) => void, | ||
| ) { | ||
| if (!onProgress || !process.stderr) return; | ||
|
|
||
| onProgress(0); | ||
|
|
||
| const rl = readline.createInterface({ | ||
| input: process.stderr, | ||
| // Optimize readline performance | ||
| crlfDelay: Infinity, | ||
| historySize: 0, // Disable history to save memory | ||
| }); | ||
|
|
||
| let lastProgressTime = 0; | ||
| const progressThrottle = 100; // Update progress max every 100ms | ||
|
|
||
| rl.on('line', (line) => { | ||
| const now = Date.now(); | ||
|
|
||
| // Throttle progress updates to reduce UI overhead | ||
| if (now - lastProgressTime < progressThrottle) return; | ||
|
|
||
| try { | ||
| if (customMatcher) { | ||
| customMatcher(line); | ||
| return; | ||
| } | ||
|
|
||
| // Optimized regex for faster parsing | ||
| const timeMatch = line.match(/time=(\d{2}):(\d{2}):(\d{2}\.\d{2})/); | ||
| if (timeMatch && duration) { | ||
| const [, hours, minutes, seconds] = timeMatch; | ||
| const currentTime = parseInt(hours) * 3600 + parseInt(minutes) * 60 + parseFloat(seconds); | ||
| const progress = Math.min(currentTime / duration, 1); | ||
|
|
||
| if (!isNaN(progress)) { | ||
| onProgress(progress); | ||
| lastProgressTime = now; | ||
| } | ||
| } | ||
| } catch (err) { | ||
| // Silently ignore parsing errors to avoid performance impact | ||
| } | ||
| }); | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Use existing progress parsing function
This function duplicates the progress handling logic from ffmpeg.ts but with a less robust parsing implementation. Consider using the existing parseFfmpegProgressLine function from progress.ts instead of reimplementing the parsing logic.
+import { parseFfmpegProgressLine } from './progress.js';
+
export function optimizedHandleProgress(
process: { stderr: Readable | null },
duration: number | undefined,
onProgress: (progress: number) => void,
customMatcher?: (line: string) => void,
) {
if (!onProgress || !process.stderr) return;
onProgress(0);
-
+
const rl = readline.createInterface({
input: process.stderr,
// Optimize readline performance
crlfDelay: Infinity,
historySize: 0, // Disable history to save memory
});
let lastProgressTime = 0;
const progressThrottle = 100; // Update progress max every 100ms
+ let lastProgress = 0;
rl.on('line', (line) => {
const now = Date.now();
-
+
// Throttle progress updates to reduce UI overhead
if (now - lastProgressTime < progressThrottle) return;
-
+
try {
- if (customMatcher) {
- customMatcher(line);
- return;
- }
-
- // Optimized regex for faster parsing
- const timeMatch = line.match(/time=(\d{2}):(\d{2}):(\d{2}\.\d{2})/);
- if (timeMatch && duration) {
- const [, hours, minutes, seconds] = timeMatch;
- const currentTime = parseInt(hours) * 3600 + parseInt(minutes) * 60 + parseFloat(seconds);
- const progress = Math.min(currentTime / duration, 1);
-
- if (!isNaN(progress)) {
- onProgress(progress);
- lastProgressTime = now;
- }
+ const progress = parseFfmpegProgressLine({ line, customMatcher, duration });
+ if (progress != null && Math.abs(progress - lastProgress) > 0.001) {
+ onProgress(progress);
+ lastProgressTime = now;
+ lastProgress = progress;
}
- } catch (err) {
- // Silently ignore parsing errors to avoid performance impact
+ } catch {
+ // Silently ignore parsing errors
}
});
}🧰 Tools
🪛 ESLint
[error] 37-37: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 38-38: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 50-50: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 53-53: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 64-64: Missing radix parameter.
(radix)
[error] 64-64: Missing radix parameter.
(radix)
[error] 66-66: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 67-67: Unexpected use of 'isNaN'. Use Number.isNaN instead https://github.com/airbnb/javascript#standard-library--isnan
(no-restricted-globals)
[error] 72-72: Remove unused catch binding err.
(unicorn/prefer-optional-catch-binding)
🤖 Prompt for AI Agents
In src/main/ffmpeg-optimizations.ts between lines 27 and 77, the
optimizedHandleProgress function duplicates progress parsing logic that exists
in progress.ts as parseFfmpegProgressLine. To fix this, import
parseFfmpegProgressLine from progress.ts and replace the manual regex parsing
inside the 'line' event handler with a call to parseFfmpegProgressLine. Use its
output to calculate progress and call onProgress accordingly, ensuring the
function leverages the more robust existing parser.
| // Optimization 3: Batch processing optimization | ||
| export function createOptimizedBatchProcessor<T>( | ||
| items: T[], | ||
| processor: (item: T) => Promise<any>, | ||
| options: { | ||
| concurrency?: number; | ||
| batchSize?: number; | ||
| progressCallback?: (completed: number, total: number) => void; | ||
| } = {} | ||
| ) { | ||
| const { concurrency = 4, batchSize = 10, progressCallback } = options; | ||
|
|
||
| return async function processBatch() { | ||
| const results: any[] = []; | ||
| let completed = 0; | ||
|
|
||
| // Process in optimized batches | ||
| for (let i = 0; i < items.length; i += batchSize) { | ||
| const batch = items.slice(i, i + batchSize); | ||
|
|
||
| // Process batch items with controlled concurrency | ||
| const batchPromises = batch.map(async (item, index) => { | ||
| const result = await processor(item); | ||
| completed++; | ||
|
|
||
| if (progressCallback && completed % Math.max(1, Math.floor(items.length / 100)) === 0) { | ||
| progressCallback(completed, items.length); | ||
| } | ||
|
|
||
| return result; | ||
| }); | ||
|
|
||
| // Process with limited concurrency to avoid overwhelming the system | ||
| const batchResults = await Promise.all(batchPromises.slice(0, concurrency)); | ||
| results.push(...batchResults); | ||
|
|
||
| // Process remaining items in the batch | ||
| if (batchPromises.length > concurrency) { | ||
| const remainingResults = await Promise.all(batchPromises.slice(concurrency)); | ||
| results.push(...remainingResults); | ||
| } | ||
| } | ||
|
|
||
| return results; | ||
| }; | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix batch processor implementation
The current implementation has several issues:
- It processes batches sequentially, not concurrently
- The
Promise.allusage doesn't respect the concurrency limit - Uses
anytypes instead of generics properly - The async map function captures variables unsafely
Consider using an existing library like p-map which is already used in the codebase and handles concurrency correctly:
import pMap from 'p-map';
export async function createOptimizedBatchProcessor<T, R>(
items: T[],
processor: (item: T) => Promise<R>,
options: {
concurrency?: number;
progressCallback?: (completed: number, total: number) => void;
} = {}
): Promise<R[]> {
const { concurrency = 4, progressCallback } = options;
let completed = 0;
return pMap(items, async (item) => {
const result = await processor(item);
completed++;
if (progressCallback && completed % Math.max(1, Math.floor(items.length / 100)) === 0) {
progressCallback(completed, items.length);
}
return result;
}, { concurrency });
}🧰 Tools
🪛 ESLint
[error] 81-81: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 86-87: Missing trailing comma.
(comma-dangle)
[error] 89-89: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 91-91: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 93-93: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 97-97: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 99-108: Function declared in a loop contains unsafe references to variable(s) 'completed', 'completed', 'completed'.
(no-loop-func)
[error] 99-99: 'index' is defined but never used.
(@typescript-eslint/no-unused-vars)
[error] 101-101: Unary operator '++' used.
(no-plusplus)
[error] 102-102: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 106-106: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 109-109: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 113-113: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 120-120: Trailing spaces not allowed.
(no-trailing-spaces)
🤖 Prompt for AI Agents
In src/main/ffmpeg-optimizations.ts around lines 78 to 124, the batch processor
implementation incorrectly processes batches sequentially and does not properly
enforce concurrency limits with Promise.all, also it uses any types instead of
generics and has unsafe async map usage. Replace the entire function with a
version that uses the existing p-map library to handle concurrency correctly,
remove batchSize since p-map manages concurrency, use proper generics for input
and output types, track progress safely inside the p-map iterator, and return
the final results as a Promise of the generic output array.
| // Optimization 5: Improved seeking performance | ||
| export function getOptimizedSeekArgs(from?: number, to?: number): string[] { | ||
| const args: string[] = []; | ||
|
|
||
| if (from != null) { | ||
| // Use precise seeking for better performance | ||
| args.push('-ss', from.toFixed(6)); | ||
| // Enable fast seeking when possible | ||
| if (from > 1) { | ||
| args.push('-accurate_seek'); | ||
| } | ||
| } | ||
|
|
||
| if (to != null && from != null) { | ||
| const duration = to - from; | ||
| args.push('-t', duration.toFixed(6)); | ||
| } | ||
|
|
||
| return args; | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Questionable seeking logic
The -accurate_seek flag is added for seeks > 1 second, but accurate seeking is typically more important for small, precise seeks. For large seeks, keyframe seeking is usually sufficient and faster.
Consider inverting the logic or removing the conditional:
export function getOptimizedSeekArgs(from?: number, to?: number): string[] {
const args: string[] = [];
if (from != null) {
// Use precise seeking for better performance
args.push('-ss', from.toFixed(6));
- // Enable fast seeking when possible
- if (from > 1) {
- args.push('-accurate_seek');
- }
}
if (to != null && from != null) {
const duration = to - from;
args.push('-t', duration.toFixed(6));
}
return args;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Optimization 5: Improved seeking performance | |
| export function getOptimizedSeekArgs(from?: number, to?: number): string[] { | |
| const args: string[] = []; | |
| if (from != null) { | |
| // Use precise seeking for better performance | |
| args.push('-ss', from.toFixed(6)); | |
| // Enable fast seeking when possible | |
| if (from > 1) { | |
| args.push('-accurate_seek'); | |
| } | |
| } | |
| if (to != null && from != null) { | |
| const duration = to - from; | |
| args.push('-t', duration.toFixed(6)); | |
| } | |
| return args; | |
| } | |
| export function getOptimizedSeekArgs(from?: number, to?: number): string[] { | |
| const args: string[] = []; | |
| if (from != null) { | |
| // Use precise seeking for better performance | |
| args.push('-ss', from.toFixed(6)); | |
| } | |
| if (to != null && from != null) { | |
| const duration = to - from; | |
| args.push('-t', duration.toFixed(6)); | |
| } | |
| return args; | |
| } |
🧰 Tools
🪛 ESLint
[error] 155-155: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 164-164: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 169-169: Trailing spaces not allowed.
(no-trailing-spaces)
🤖 Prompt for AI Agents
In src/main/ffmpeg-optimizations.ts between lines 152 and 172, the current logic
adds the '-accurate_seek' flag only when the seek start time is greater than 1
second, which is counterproductive since accurate seeking is more beneficial for
small, precise seeks. To fix this, invert the condition to add '-accurate_seek'
when the seek start time is less than or equal to 1 second, or consider removing
the conditional entirely to always include '-accurate_seek' for precise seeking.
| // Optimization 6: Codec-specific optimizations | ||
| export function getOptimizedCodecArgs(codec: string, quality: 'fast' | 'balanced' | 'quality' = 'balanced'): string[] { | ||
| const presets = { | ||
| 'libx264': { | ||
| fast: ['-preset', 'ultrafast', '-tune', 'zerolatency'], | ||
| balanced: ['-preset', 'medium', '-crf', '23'], | ||
| quality: ['-preset', 'slow', '-crf', '18'] | ||
| }, | ||
| 'libx265': { | ||
| fast: ['-preset', 'ultrafast', '-x265-params', 'log-level=error'], | ||
| balanced: ['-preset', 'medium', '-crf', '28'], | ||
| quality: ['-preset', 'slow', '-crf', '24'] | ||
| }, | ||
| 'copy': { | ||
| fast: ['-c', 'copy'], | ||
| balanced: ['-c', 'copy'], | ||
| quality: ['-c', 'copy'] | ||
| } | ||
| }; | ||
|
|
||
| return presets[codec as keyof typeof presets]?.[quality] || ['-c', 'copy']; | ||
| } | ||
|
|
||
| // Optimization 7: Smart quality detection | ||
| export function detectOptimalQuality(inputFile: string, streams: any[]): 'fast' | 'balanced' | 'quality' { | ||
| // Analyze file characteristics to determine optimal quality setting | ||
| const videoStream = streams.find(s => s.codec_type === 'video'); | ||
|
|
||
| if (!videoStream) return 'fast'; | ||
|
|
||
| const resolution = (videoStream.width || 0) * (videoStream.height || 0); | ||
| const bitrate = parseInt(videoStream.bit_rate) || 0; | ||
|
|
||
| // HD+ content with high bitrate - use quality mode | ||
| if (resolution >= 1920 * 1080 && bitrate > 5000000) { | ||
| return 'quality'; | ||
| } | ||
|
|
||
| // Standard definition or lower bitrate - use fast mode | ||
| if (resolution <= 720 * 480 || bitrate < 1000000) { | ||
| return 'fast'; | ||
| } | ||
|
|
||
| // Default to balanced | ||
| return 'balanced'; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve type safety and fix parsing issues
- Add proper types instead of
any - Fix
parseIntcalls to include radix parameter - Remove unnecessary quotes from object keys
+import { FFprobeStream } from '../../../../ffprobe';
+
export function getOptimizedCodecArgs(codec: string, quality: 'fast' | 'balanced' | 'quality' = 'balanced'): string[] {
const presets = {
- 'libx264': {
+ libx264: {
fast: ['-preset', 'ultrafast', '-tune', 'zerolatency'],
balanced: ['-preset', 'medium', '-crf', '23'],
- quality: ['-preset', 'slow', '-crf', '18']
+ quality: ['-preset', 'slow', '-crf', '18'],
},
- 'libx265': {
+ libx265: {
fast: ['-preset', 'ultrafast', '-x265-params', 'log-level=error'],
balanced: ['-preset', 'medium', '-crf', '28'],
- quality: ['-preset', 'slow', '-crf', '24']
+ quality: ['-preset', 'slow', '-crf', '24'],
},
- 'copy': {
+ copy: {
fast: ['-c', 'copy'],
balanced: ['-c', 'copy'],
- quality: ['-c', 'copy']
- }
+ quality: ['-c', 'copy'],
+ },
};
return presets[codec as keyof typeof presets]?.[quality] || ['-c', 'copy'];
}
// Optimization 7: Smart quality detection
-export function detectOptimalQuality(inputFile: string, streams: any[]): 'fast' | 'balanced' | 'quality' {
+export function detectOptimalQuality(inputFile: string, streams: FFprobeStream[]): 'fast' | 'balanced' | 'quality' {
// Analyze file characteristics to determine optimal quality setting
const videoStream = streams.find(s => s.codec_type === 'video');
if (!videoStream) return 'fast';
const resolution = (videoStream.width || 0) * (videoStream.height || 0);
- const bitrate = parseInt(videoStream.bit_rate) || 0;
+ const bitrate = parseInt(videoStream.bit_rate || '0', 10) || 0;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Optimization 6: Codec-specific optimizations | |
| export function getOptimizedCodecArgs(codec: string, quality: 'fast' | 'balanced' | 'quality' = 'balanced'): string[] { | |
| const presets = { | |
| 'libx264': { | |
| fast: ['-preset', 'ultrafast', '-tune', 'zerolatency'], | |
| balanced: ['-preset', 'medium', '-crf', '23'], | |
| quality: ['-preset', 'slow', '-crf', '18'] | |
| }, | |
| 'libx265': { | |
| fast: ['-preset', 'ultrafast', '-x265-params', 'log-level=error'], | |
| balanced: ['-preset', 'medium', '-crf', '28'], | |
| quality: ['-preset', 'slow', '-crf', '24'] | |
| }, | |
| 'copy': { | |
| fast: ['-c', 'copy'], | |
| balanced: ['-c', 'copy'], | |
| quality: ['-c', 'copy'] | |
| } | |
| }; | |
| return presets[codec as keyof typeof presets]?.[quality] || ['-c', 'copy']; | |
| } | |
| // Optimization 7: Smart quality detection | |
| export function detectOptimalQuality(inputFile: string, streams: any[]): 'fast' | 'balanced' | 'quality' { | |
| // Analyze file characteristics to determine optimal quality setting | |
| const videoStream = streams.find(s => s.codec_type === 'video'); | |
| if (!videoStream) return 'fast'; | |
| const resolution = (videoStream.width || 0) * (videoStream.height || 0); | |
| const bitrate = parseInt(videoStream.bit_rate) || 0; | |
| // HD+ content with high bitrate - use quality mode | |
| if (resolution >= 1920 * 1080 && bitrate > 5000000) { | |
| return 'quality'; | |
| } | |
| // Standard definition or lower bitrate - use fast mode | |
| if (resolution <= 720 * 480 || bitrate < 1000000) { | |
| return 'fast'; | |
| } | |
| // Default to balanced | |
| return 'balanced'; | |
| } | |
| import { FFprobeStream } from '../../../../ffprobe'; | |
| // Optimization 6: Codec-specific optimizations | |
| export function getOptimizedCodecArgs( | |
| codec: string, | |
| quality: 'fast' | 'balanced' | 'quality' = 'balanced' | |
| ): string[] { | |
| const presets = { | |
| libx264: { | |
| fast: ['-preset', 'ultrafast', '-tune', 'zerolatency'], | |
| balanced: ['-preset', 'medium', '-crf', '23'], | |
| quality: ['-preset', 'slow', '-crf', '18'], | |
| }, | |
| libx265: { | |
| fast: ['-preset', 'ultrafast', '-x265-params', 'log-level=error'], | |
| balanced: ['-preset', 'medium', '-crf', '28'], | |
| quality: ['-preset', 'slow', '-crf', '24'], | |
| }, | |
| copy: { | |
| fast: ['-c', 'copy'], | |
| balanced: ['-c', 'copy'], | |
| quality: ['-c', 'copy'], | |
| }, | |
| }; | |
| return presets[codec as keyof typeof presets]?.[quality] || ['-c', 'copy']; | |
| } | |
| // Optimization 7: Smart quality detection | |
| export function detectOptimalQuality( | |
| inputFile: string, | |
| streams: FFprobeStream[] | |
| ): 'fast' | 'balanced' | 'quality' { | |
| // Analyze file characteristics to determine optimal quality setting | |
| const videoStream = streams.find(s => s.codec_type === 'video'); | |
| if (!videoStream) return 'fast'; | |
| const resolution = (videoStream.width || 0) * (videoStream.height || 0); | |
| const bitrate = parseInt(videoStream.bit_rate || '0', 10) || 0; | |
| // HD+ content with high bitrate - use quality mode | |
| if (resolution >= 1920 * 1080 && bitrate > 5_000_000) { | |
| return 'quality'; | |
| } | |
| // Standard definition or lower bitrate - use fast mode | |
| if (resolution <= 720 * 480 || bitrate < 1_000_000) { | |
| return 'fast'; | |
| } | |
| // Default to balanced | |
| return 'balanced'; | |
| } |
🧰 Tools
🪛 ESLint
[error] 176-180: Unnecessarily quoted property 'libx264' found.
(quote-props)
[error] 179-180: Missing trailing comma.
(comma-dangle)
[error] 181-185: Unnecessarily quoted property 'libx265' found.
(quote-props)
[error] 184-185: Missing trailing comma.
(comma-dangle)
[error] 186-190: Unnecessarily quoted property 'copy' found.
(quote-props)
[error] 189-190: Missing trailing comma.
(comma-dangle)
[error] 190-191: Missing trailing comma.
(comma-dangle)
[error] 192-192: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 197-197: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 199-199: Expected parentheses around arrow function argument.
(arrow-parens)
[error] 200-200: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 202-202: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 204-204: Missing radix parameter.
(radix)
[error] 205-205: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 210-210: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 215-215: Trailing spaces not allowed.
(no-trailing-spaces)
🤖 Prompt for AI Agents
In src/main/ffmpeg-optimizations.ts around lines 173 to 218, improve type safety
by replacing the use of 'any' for the streams parameter with a proper interface
or type describing the expected stream object structure. Fix the parseInt call
by adding the radix parameter 10 to ensure correct parsing of bit_rate. Also,
remove unnecessary quotes from object keys in the presets object since they are
valid identifiers and do not require quotes.
| // Optimization 8: Parallel processing for multiple segments | ||
| export function createParallelSegmentProcessor(segments: any[], options: { | ||
| maxConcurrency?: number; | ||
| resourceLimit?: number; | ||
| } = {}) { | ||
| const { maxConcurrency = 2, resourceLimit = 4 } = options; | ||
|
|
||
| return async function processSegments(processor: (segment: any, index: number) => Promise<any>) { | ||
| const semaphore = new Array(Math.min(maxConcurrency, resourceLimit)).fill(null); | ||
| let segmentIndex = 0; | ||
| const results: any[] = []; | ||
|
|
||
| const processNext = async () => { | ||
| if (segmentIndex >= segments.length) return; | ||
|
|
||
| const currentIndex = segmentIndex++; | ||
| const segment = segments[currentIndex]; | ||
|
|
||
| try { | ||
| const result = await processor(segment, currentIndex); | ||
| results[currentIndex] = result; | ||
| } catch (error) { | ||
| results[currentIndex] = { error }; | ||
| } | ||
|
|
||
| // Continue processing if there are more segments | ||
| if (segmentIndex < segments.length) { | ||
| await processNext(); | ||
| } | ||
| }; | ||
|
|
||
| // Start parallel processing | ||
| await Promise.all(semaphore.map(() => processNext())); | ||
|
|
||
| return results; | ||
| }; | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Simplify parallel processing implementation
The current implementation is overly complex and doesn't properly control concurrency. Since the project already uses p-map, leverage it for simpler and more reliable parallel processing.
+import pMap from 'p-map';
+
-export function createParallelSegmentProcessor(segments: any[], options: {
+export async function processSegmentsInParallel<T, R>(
+ segments: T[],
+ processor: (segment: T, index: number) => Promise<R>,
+ options: {
maxConcurrency?: number;
- resourceLimit?: number;
-} = {}) {
- const { maxConcurrency = 2, resourceLimit = 4 } = options;
+ } = {}
+): Promise<R[]> {
+ const { maxConcurrency = 2 } = options;
- return async function processSegments(processor: (segment: any, index: number) => Promise<any>) {
- const semaphore = new Array(Math.min(maxConcurrency, resourceLimit)).fill(null);
- let segmentIndex = 0;
- const results: any[] = [];
-
- const processNext = async () => {
- if (segmentIndex >= segments.length) return;
-
- const currentIndex = segmentIndex++;
- const segment = segments[currentIndex];
-
- try {
- const result = await processor(segment, currentIndex);
- results[currentIndex] = result;
- } catch (error) {
- results[currentIndex] = { error };
- }
-
- // Continue processing if there are more segments
- if (segmentIndex < segments.length) {
- await processNext();
- }
- };
-
- // Start parallel processing
- await Promise.all(semaphore.map(() => processNext()));
-
- return results;
- };
+ return pMap(
+ segments,
+ async (segment, index) => processor(segment, index),
+ { concurrency: maxConcurrency }
+ );
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Optimization 8: Parallel processing for multiple segments | |
| export function createParallelSegmentProcessor(segments: any[], options: { | |
| maxConcurrency?: number; | |
| resourceLimit?: number; | |
| } = {}) { | |
| const { maxConcurrency = 2, resourceLimit = 4 } = options; | |
| return async function processSegments(processor: (segment: any, index: number) => Promise<any>) { | |
| const semaphore = new Array(Math.min(maxConcurrency, resourceLimit)).fill(null); | |
| let segmentIndex = 0; | |
| const results: any[] = []; | |
| const processNext = async () => { | |
| if (segmentIndex >= segments.length) return; | |
| const currentIndex = segmentIndex++; | |
| const segment = segments[currentIndex]; | |
| try { | |
| const result = await processor(segment, currentIndex); | |
| results[currentIndex] = result; | |
| } catch (error) { | |
| results[currentIndex] = { error }; | |
| } | |
| // Continue processing if there are more segments | |
| if (segmentIndex < segments.length) { | |
| await processNext(); | |
| } | |
| }; | |
| // Start parallel processing | |
| await Promise.all(semaphore.map(() => processNext())); | |
| return results; | |
| }; | |
| } | |
| import pMap from 'p-map'; | |
| // Optimization 8: Parallel processing for multiple segments | |
| export async function processSegmentsInParallel<T, R>( | |
| segments: T[], | |
| processor: (segment: T, index: number) => Promise<R>, | |
| options: { | |
| maxConcurrency?: number; | |
| } = {} | |
| ): Promise<R[]> { | |
| const { maxConcurrency = 2 } = options; | |
| return pMap( | |
| segments, | |
| async (segment, index) => processor(segment, index), | |
| { concurrency: maxConcurrency } | |
| ); | |
| } |
🧰 Tools
🪛 ESLint
[error] 221-221: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 226-226: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 227-227: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 227-227: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 228-228: Do not use new Array().
(unicorn/no-new-array)
[error] 230-230: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 231-231: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 234-234: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 235-235: Unary operator '++' used.
(no-plusplus)
[error] 237-237: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 244-244: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 250-250: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 253-253: Trailing spaces not allowed.
(no-trailing-spaces)
🤖 Prompt for AI Agents
In src/main/ffmpeg-optimizations.ts between lines 220 and 257, the current
parallel segment processing function manually manages concurrency with a
semaphore array and recursive calls, which is complex and error-prone. Replace
this implementation by using the existing p-map library to handle concurrency
control more simply and reliably. Refactor the function to call p-map on the
segments array with the processor function and set concurrency to
maxConcurrency, removing manual semaphore and recursion logic.
- Remove unused execa import and ExecaOptions type reference - Simplify optimizedHandleProgress function to avoid dependency issues - Fix unused parameter 'index' in batch processor map function - Mark unused 'inputFile' parameter with underscore prefix - Remove unused import of optimization functions from useFfmpegOperations - Remove unused SEGMENT_BATCH_SIZE constant Fixes GitHub Actions build failure on ubuntu-latest due to: - TS6133: unused variable declarations - TS2305: missing module exports - TS2345: type assignment issues
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🔭 Outside diff range comments (1)
src/main/ffmpeg-optimizations.ts (1)
1-228: Remove duplicate FFmpeg optimizations fileBoth
src/main/ffmpeg-optimizations.tsandsrc/main/ffmpeg-optimizations-fixed.tshave identical content (MD5: 6920ea257e0746ba7707be7fb2f4894a). Keeping both violates DRY and causes confusion.• Decide which file to keep:
- src/main/ffmpeg-optimizations.ts
- src/main/ffmpeg-optimizations-fixed.ts
• Delete the other file.
• Update any imports (e.g., inuseFfmpegOperations.ts) to reference the remaining file.Please confirm which filename should remain.
♻️ Duplicate comments (1)
src/main/ffmpeg-optimizations-fixed.ts (1)
1-1: Fix filename to match project conventions.Rename the file from
ffmpeg-optimizations-fixed.tstoffmpegOptimizationsFixed.tsto match the camelCase convention used in the project.
🧹 Nitpick comments (3)
src/renderer/src/hooks/useFfmpegOperations.ts (1)
70-74: Fix trailing spaces.The concurrency optimization is good, but there are formatting issues.
async function tryDeleteFiles(paths: string[]) { - return pMap(paths, (path) => unlinkWithRetry(path).catch((err) => console.error('Failed to delete', path, err)), { - concurrency: OPTIMIZED_CONCURRENCY + return pMap(paths, (path) => unlinkWithRetry(path).catch((err) => console.error('Failed to delete', path, err)), { + concurrency: OPTIMIZED_CONCURRENCY, }); }src/main/ffmpeg-optimizations-fixed.ts (2)
86-110: Add missing trailing comma.The function logic is good for memory-efficient processing.
streamOptions: { highWaterMark, objectMode: false, - } + }, }; }
134-154: Fix formatting issues.Remove unnecessary quotes and add trailing commas.
export function getOptimizedCodecArgs(codec: string, quality: 'fast' | 'balanced' | 'quality' = 'balanced'): string[] { const presets = { - 'libx264': { + libx264: { fast: ['-preset', 'ultrafast', '-tune', 'zerolatency'], balanced: ['-preset', 'medium', '-crf', '23'], - quality: ['-preset', 'slow', '-crf', '18'] + quality: ['-preset', 'slow', '-crf', '18'], }, - 'libx265': { + libx265: { fast: ['-preset', 'ultrafast', '-x265-params', 'log-level=error'], balanced: ['-preset', 'medium', '-crf', '28'], - quality: ['-preset', 'slow', '-crf', '24'] + quality: ['-preset', 'slow', '-crf', '24'], }, - 'copy': { + copy: { fast: ['-c', 'copy'], balanced: ['-c', 'copy'], - quality: ['-c', 'copy'] - } + quality: ['-c', 'copy'], + }, }; return presets[codec as keyof typeof presets]?.[quality] || ['-c', 'copy']; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/main/ffmpeg-optimizations-fixed.ts(1 hunks)src/main/ffmpeg-optimizations.ts(1 hunks)src/renderer/src/hooks/useFfmpegOperations.ts(6 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/main/ffmpeg-optimizations-fixed.ts (1)
src/main/ffmpeg-optimizations.ts (8)
optimizeFFmpegArgs(5-21)optimizedHandleProgress(24-36)createOptimizedBatchProcessor(39-83)createOptimizedStreamProcessor(86-110)getOptimizedSeekArgs(113-131)getOptimizedCodecArgs(134-154)detectOptimalQuality(157-178)createParallelSegmentProcessor(181-216)
src/renderer/src/hooks/useFfmpegOperations.ts (3)
src/renderer/src/util.ts (1)
unlinkWithRetry(131-131)src/main/ffmpeg.ts (1)
getDuration(575-577)src/renderer/src/ffmpeg.ts (1)
getDuration(20-20)
🪛 ESLint
src/main/ffmpeg-optimizations-fixed.ts
[error] 1-1: Filename is not in camel case or pascal case. Rename it to ffmpegOptimizationsFixed.ts or FfmpegOptimizationsFixed.ts.
(unicorn/filename-case)
[error] 25-25: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 28-28: 'customMatcher' is defined but never used.
(@typescript-eslint/no-unused-vars)
[error] 33-33: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 41-41: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 46-47: Missing trailing comma.
(comma-dangle)
[error] 49-49: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 51-51: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 53-53: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 57-57: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 59-68: Function declared in a loop contains unsafe references to variable(s) 'completed', 'completed', 'completed'.
(no-loop-func)
[error] 61-61: Unary operator '++' used.
(no-plusplus)
[error] 62-62: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 66-66: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 69-69: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 73-73: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 80-80: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 91-91: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 104-104: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 108-109: Missing trailing comma.
(comma-dangle)
[error] 115-115: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 124-124: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 129-129: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 136-140: Unnecessarily quoted property 'libx264' found.
(quote-props)
[error] 139-140: Missing trailing comma.
(comma-dangle)
[error] 141-145: Unnecessarily quoted property 'libx265' found.
(quote-props)
[error] 144-145: Missing trailing comma.
(comma-dangle)
[error] 146-150: Unnecessarily quoted property 'copy' found.
(quote-props)
[error] 149-150: Missing trailing comma.
(comma-dangle)
[error] 150-151: Missing trailing comma.
(comma-dangle)
[error] 152-152: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 157-157: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 159-159: Expected parentheses around arrow function argument.
(arrow-parens)
[error] 160-160: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 162-162: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 164-164: Missing radix parameter.
(radix)
[error] 165-165: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 170-170: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 175-175: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 181-181: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 186-186: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 187-187: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 187-187: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 188-188: Do not use new Array().
(unicorn/no-new-array)
[error] 190-190: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 191-191: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 194-194: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 195-195: Unary operator '++' used.
(no-plusplus)
[error] 197-197: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 204-204: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 210-210: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 213-213: Trailing spaces not allowed.
(no-trailing-spaces)
src/main/ffmpeg-optimizations.ts
[error] 1-1: Filename is not in camel case or pascal case. Rename it to ffmpegOptimizations.ts or FfmpegOptimizations.ts.
(unicorn/filename-case)
[error] 25-25: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 28-28: 'customMatcher' is defined but never used.
(@typescript-eslint/no-unused-vars)
[error] 33-33: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 41-41: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 46-47: Missing trailing comma.
(comma-dangle)
[error] 49-49: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 51-51: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 53-53: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 57-57: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 59-68: Function declared in a loop contains unsafe references to variable(s) 'completed', 'completed', 'completed'.
(no-loop-func)
[error] 61-61: Unary operator '++' used.
(no-plusplus)
[error] 62-62: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 66-66: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 69-69: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 73-73: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 80-80: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 91-91: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 104-104: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 108-109: Missing trailing comma.
(comma-dangle)
[error] 115-115: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 124-124: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 129-129: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 136-140: Unnecessarily quoted property 'libx264' found.
(quote-props)
[error] 139-140: Missing trailing comma.
(comma-dangle)
[error] 141-145: Unnecessarily quoted property 'libx265' found.
(quote-props)
[error] 144-145: Missing trailing comma.
(comma-dangle)
[error] 146-150: Unnecessarily quoted property 'copy' found.
(quote-props)
[error] 149-150: Missing trailing comma.
(comma-dangle)
[error] 150-151: Missing trailing comma.
(comma-dangle)
[error] 152-152: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 157-157: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 159-159: Expected parentheses around arrow function argument.
(arrow-parens)
[error] 160-160: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 162-162: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 164-164: Missing radix parameter.
(radix)
[error] 165-165: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 170-170: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 175-175: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 181-181: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 186-186: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 187-187: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 187-187: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 188-188: Do not use new Array().
(unicorn/no-new-array)
[error] 190-190: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 191-191: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 194-194: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 195-195: Unary operator '++' used.
(no-plusplus)
[error] 197-197: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 204-204: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 210-210: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 213-213: Trailing spaces not allowed.
(no-trailing-spaces)
src/renderer/src/hooks/useFfmpegOperations.ts
[error] 71-71: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 72-72: Trailing spaces not allowed.
(no-trailing-spaces)
[error] 72-72: Missing trailing comma.
(comma-dangle)
🔇 Additional comments (5)
src/renderer/src/hooks/useFfmpegOperations.ts (5)
19-21: LGTM! Good performance optimization.The dynamic concurrency calculation based on hardware capabilities is a solid improvement. The bounds (2-8) are reasonable to ensure efficient parallel processing without overwhelming the system.
137-137: Good optimization for duration fetching.Using dynamic concurrency for getting file durations improves performance, especially when concatenating many files.
635-635: Excellent optimization for segment processing.This is a key performance improvement. Processing multiple segments in parallel with hardware-aware concurrency will significantly speed up batch operations.
908-908: Good concurrency control for stream extraction.Applying dynamic concurrency to stream extraction operations ensures efficient resource utilization.
944-944: Consistent concurrency optimization.Good to see the same optimization applied to attachment stream extraction for consistency.
|
thanks for your pr. I don't under stand what the new files ffmpeg-optimizations and ffmpeg-optimizations-fixed are for? I don't see them being referenced from anywhere. |
Added -threads 0 for full CPU utilization, increased concurrency from 1 to dynamic CPU-based values for segment processing, and implemented throttled progress updates. Maintains lossless quality while significantly improving processing speed.
Summary by CodeRabbit
Performance Improvements
New Features