Plugins: - misc_fixes v2.8: Pre-processing, container remux, stream conforming - stream_organizer v4.8: English priority, subtitle extraction, SRT conversion - combined_audio_standardizer v1.13: AAC/Opus encoding, downmix creation - av1_svt_converter v2.22: AV1 video encoding via SVT-AV1 Structure: - Local/ - Plugin .js files (mount in Tdarr) - agent_notes/ - Development documentation - Latest-Reports/ - Error logs for analysis
6.3 KiB
Infinite Loop Scenario Analysis
Date: 2025-12-15
Plugin Versions: misc_fixes v2.7, stream_organizer v4.7, av1_converter v2.22, audio_standardizer v1.13
Executive Summary
Analyzed all 4 plugins for potential infinite loop conditions. Found 1 confirmed risk, 2 potential risks, and 3 low/theoretical risks.
| Risk Level | Plugin | Loop Scenario | Status |
|---|---|---|---|
| <EFBFBD> SAFE | misc_fixes | Container/reorder detection | Already Fixed |
| <EFBFBD> SAFE | stream_organizer | Subtitle extraction edge case | Mitigated |
| <EFBFBD> SAFE | audio_standardizer | Downmix creation detection | Safe |
| 🟢 SAFE | av1_converter | Force transcode disabled | Safe |
| 🟢 SAFE | stream_organizer | CC extraction | Safe |
✅ VERIFIED SAFE: misc_fixes Container/Reorder Detection
Analysis
Upon code review, the reorder detection fix is ALREADY IMPLEMENTED:
// Line 228-229 of misc_fixes.js
const firstStreamIsVideo = file.ffProbeData.streams[0]?.codec_type === 'video';
const needsReorder = inputs.ensure_video_first === 'true' && !firstStreamIsVideo;
Protection Mechanism:
needsReorderis onlytrueif video is NOT first- After reordering, video IS first →
needsReorder = false - No infinite loop occurs
Container Remux Logic
Also safe:
if (currentContainer !== targetContainer) {
needsRemux = true;
}
- After remux to MKV,
currentContainer === 'mkv' targetContainer === 'mkv'needsRemux = falseon second pass
Verified Behavior
- First pass: File needs reorder →
processFile: true, reorders - Second pass: Video already first →
needsReorder = false→processFile: false - Loop terminates
Status: ✅ SAFE - No fix needed.
🟡 MEDIUM RISK: stream_organizer Subtitle Extraction
The Problem
Subtitle extraction is protected by needsSubtitleExtraction() but has edge cases.
Edge Case: Extraction Fails Silently
if (needsSubtitleExtraction(subsFile, baseFile, fs)) {
extractCommand += ...
extractedFiles.add(subsFile);
}
Problem: If FFmpeg fails to create the file (returns success but file is corrupt), the plugin will:
- See file doesn't exist (or is tiny)
- Attempt extraction again
- Loop
Current Mitigation
const attempts = extractionAttempts.get(attemptKey) || 0;
if (attempts >= MAX_EXTRACTION_ATTEMPTS) {
response.infoLog += `⚠️ Skipping - extraction failed ${MAX_EXTRACTION_ATTEMPTS} times.`;
continue;
}
Status: Protected by attempt counter (MAX = 3). Mitigated.
Remaining Risk
- Counter is in-memory, resets on Tdarr restart
- If Tdarr restarts during processing, attempts reset to 0
🟡 MEDIUM RISK: audio_standardizer Downmix Detection
The Problem
Plugin creates downmix tracks if they don't exist:
if (inputs.create_downmix === 'true') {
const hasStereo = audioStreams.some(s => s.channels === 2);
if (!hasStereo) {
// Create 2ch downmix
}
}
Potential Loop Scenario
- File has 5.1 audio only
- Plugin creates stereo downmix →
reQueueAfter: true - On re-queue, file now has stereo
- Should stop... but does it?
Analysis
if (needsTranscoding(stream, inputs, targetCodec)) {
needsTranscode = true;
}
Question: Does the NEW stereo track created in step 2 get detected as "already Opus"?
Finding: Likely safe because:
- New track is Opus (target codec)
needsTranscoding()should return falseskip_if_compatible === 'true'by default
Recommendation: Add explicit check:
// Skip if we just created a downmix (Opus stereo exists)
const hasOpusStereo = audioStreams.some(s =>
s.channels === 2 && s.codec_name === 'opus'
);
if (hasOpusStereo && inputs.create_downmix === 'true') {
response.infoLog += 'ℹ️ Stereo downmix already exists (Opus). ';
}
🟢 LOW RISK: av1_converter
Analysis
The AV1 converter has proper exit conditions:
// Already AV1 → Skip
if (isAV1 && sanitized.force_transcode !== 'enabled') {
response.processFile = false;
response.infoLog += 'File is already AV1 encoded and force_transcode is disabled. Skipping.\n';
return response;
}
Status: ✅ Safe. Clear skip when codec matches.
Theoretical Risk
Only if: User enables force_transcode = enabled
- Then every run will transcode
- This is intentional (user wants to re-encode)
🟢 LOW RISK: CC Extraction Loop
Analysis
CC extraction is protected by:
- Lock file mechanism
- File existence check using
originalLibraryFile.file - Explicit skip when file exists
if (ccExists) {
ccActuallyExtracted = false;
response.infoLog += `ℹ️ ${baseName}.cc.srt already exists. `;
}
Status: ✅ Safe.
Recommendations
No Immediate Fixes Needed
All plugins have proper loop termination conditions:
- misc_fixes: Already checks if video is first before reordering
- stream_organizer: Has extraction attempt counter (max 3)
- audio_standardizer: Detects existing codec (skip_if_compatible)
- av1_converter: Checks if already AV1 before processing
### Short-term (Priority 2)
**Add processing fingerprint to prevent duplicate runs:**
```javascript
// At start of plugin
const fingerprint = md5(JSON.stringify({
container: file.container,
streamOrder: file.ffProbeData.streams.map(s => s.codec_type),
imageCounts: // count of image streams
}));
// Store in file metadata or temp file
if (previousFingerprint === fingerprint) {
response.infoLog += '⚠️ File unchanged since last run, skipping.';
return response;
}
Long-term (Priority 3)
Add maximum run counter per plugin:
- Tdarr maintains internal counter per file per plugin
- If counter > 3, flag file for manual review
- Prevents any unexpected loops
Summary
| Plugin | Loop Risk | Current Protection | Recommendation |
|---|---|---|---|
| misc_fixes | HIGH | None | Add order check |
| stream_organizer | LOW | Attempt counter | Already mitigated |
| audio_standardizer | LOW | Codec detection | Add explicit check |
| av1_converter | NONE | isAV1 check | None needed |
Next Step: Implement the misc_fixes reorder detection fix.