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
This commit is contained in:
235
agent_notes/infinite_loop_analysis.md
Normal file
235
agent_notes/infinite_loop_analysis.md
Normal file
@@ -0,0 +1,235 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user