Files
tdarr-plugs/agent_notes/infinite_loop_analysis.md
Tdarr Plugin Developer aa71eb96d7 Initial commit: Tdarr plugin stack
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
2025-12-15 11:33:36 -08:00

6.3 KiB
Raw Blame History

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:

  • needsReorder is only true if 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 = false on second pass

Verified Behavior

  1. First pass: File needs reorder → processFile: true, reorders
  2. Second pass: Video already first → needsReorder = falseprocessFile: false
  3. 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:

  1. See file doesn't exist (or is tiny)
  2. Attempt extraction again
  3. 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

  1. File has 5.1 audio only
  2. Plugin creates stereo downmix → reQueueAfter: true
  3. On re-queue, file now has stereo
  4. 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 false
  • skip_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:

  1. Lock file mechanism
  2. File existence check using originalLibraryFile.file
  3. 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.