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

236 lines
6.3 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 |
|------------|--------|---------------|--------|
| <20> SAFE | misc_fixes | Container/reorder detection | **Already Fixed** |
| <20> SAFE | stream_organizer | Subtitle extraction edge case | Mitigated |
| <20> 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**:
```javascript
// 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:
```javascript
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 = false``processFile: 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
```javascript
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
```javascript
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:
```javascript
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
```javascript
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:
```javascript
// 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**:
```javascript
// 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
```javascript
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.