Fix: Automatic multi-directory video path resolution for torrent in-browser preview streaming

This commit is contained in:
david
2026-08-17 09:59:07 -04:00
parent 8fa9572dea
commit 5ba899b955
2 changed files with 106 additions and 5 deletions
+15 -1
View File
@@ -4002,7 +4002,7 @@ Return ONLY a valid JSON object with an array named "results":
renderCurrentVideoPreview(); renderCurrentVideoPreview();
} }
function openVideoPreviewForTorrent(hash) { async function openVideoPreviewForTorrent(hash) {
const query = searchInput.value.trim().toLowerCase(); const query = searchInput.value.trim().toLowerCase();
const filtered = qbState.torrents.filter(t => { const filtered = qbState.torrents.filter(t => {
if (!query) return true; if (!query) return true;
@@ -4012,6 +4012,11 @@ Return ONLY a valid JSON object with an array named "results":
const idx = filtered.findIndex(t => t.hash === hash); const idx = filtered.findIndex(t => t.hash === hash);
if (idx === -1) return; if (idx === -1) return;
const item = filtered[idx];
if (!item.innerFiles || item.innerFiles.length === 0) {
item.innerFiles = await fetchTorrentFiles(item.hash);
}
previewState.activeType = 'qb'; previewState.activeType = 'qb';
previewState.currentList = filtered; previewState.currentList = filtered;
previewState.currentIndex = idx; previewState.currentIndex = idx;
@@ -4034,9 +4039,18 @@ Return ONLY a valid JSON object with an array named "results":
streamPath = item.path; streamPath = item.path;
} else { } else {
// qb torrent // qb torrent
if (item.innerFiles && item.innerFiles.length > 0) {
const videoFile = item.innerFiles.find(f => /\.(mp4|mkv|avi|mov|webm|m4v|ts)$/i.test(f.name));
if (videoFile) {
const base = (item.save_path || '/downloads').replace(/\/+$/, '');
streamPath = `${base}/${videoFile.name}`;
}
}
if (!streamPath) {
const base = (item.save_path || '/downloads').replace(/\/+$/, ''); const base = (item.save_path || '/downloads').replace(/\/+$/, '');
streamPath = `${base}/${item.name}`; streamPath = `${base}/${item.name}`;
} }
}
videoTitle.textContent = `${previewState.activeType === 'smb' ? 'SMB Video' : 'Torrent'}: ${titleName}`; videoTitle.textContent = `${previewState.activeType === 'smb' ? 'SMB Video' : 'Torrent'}: ${titleName}`;
+89 -2
View File
@@ -427,6 +427,91 @@ def ensure_smb_mounted():
return False, f"Auto-mount error: {str(e)}" return False, f"Auto-mount error: {str(e)}"
return os.path.exists(DEFAULT_SMB_MOUNT), "Mount status checked" return os.path.exists(DEFAULT_SMB_MOUNT), "Mount status checked"
# ==================== Video Streaming Helper (Range 206) ====================
def resolve_stream_video_path(requested_path):
if not requested_path:
return None
ensure_smb_mounted()
# 1. Direct path check
if os.path.isfile(requested_path):
return requested_path
basename = os.path.basename(requested_path)
stem = os.path.splitext(basename)[0]
candidate_paths = [
requested_path.replace('/isolation/videos/', '/mnt/isolation/videos/'),
requested_path.replace('/isolation/', '/mnt/isolation/'),
requested_path.replace('/downloads/', '/mnt/isolation/videos/unsorted/'),
requested_path.replace('/downloads/', '/mnt/isolation/videos/videodownloader/'),
requested_path.replace('/downloads/', '/mnt/isolation/videos/'),
requested_path.replace('/downloads/', '/mnt/isolation/incomplete/'),
requested_path.replace('/downloads/', '/mnt/isolation/downloads/'),
os.path.join('/mnt/isolation/videos/unsorted', basename),
os.path.join('/mnt/isolation/videos/videodownloader', basename),
os.path.join('/mnt/isolation/incomplete', basename),
os.path.join('/mnt/isolation/downloads', basename)
]
video_exts = ('.mp4', '.mkv', '.avi', '.mov', '.webm', '.ts', '.m4v')
for p in candidate_paths:
if os.path.isfile(p):
return p
elif os.path.isdir(p):
try:
best_file = None
best_size = -1
for root, _, files in os.walk(p):
for f in files:
if f.lower().endswith(video_exts):
full = os.path.join(root, f)
sz = os.path.getsize(full)
if sz > best_size:
best_size = sz
best_file = full
if best_file:
return best_file
except Exception:
pass
# 2. Check top-level directories
try:
search_dirs = [
'/mnt/isolation/videos/unsorted',
'/mnt/isolation/videos/videodownloader',
'/mnt/isolation/incomplete',
'/mnt/isolation/downloads'
]
for s_dir in search_dirs:
if os.path.exists(s_dir) and os.path.isdir(s_dir):
target = os.path.join(s_dir, basename)
if os.path.isfile(target):
return target
for entry in os.scandir(s_dir):
if entry.is_file() and (entry.name == basename or os.path.splitext(entry.name)[0] == stem):
return entry.path
elif entry.is_dir():
sub_target = os.path.join(entry.path, basename)
if os.path.isfile(sub_target):
return sub_target
except Exception as e:
print(f"Error scanning directories in streaming resolver: {e}")
# 3. Fallback search inside performer folders in /mnt/isolation/videos
try:
if os.path.exists('/mnt/isolation/videos'):
for p_entry in os.scandir('/mnt/isolation/videos'):
if p_entry.is_dir():
candidate = os.path.join(p_entry.path, basename)
if os.path.isfile(candidate):
return candidate
except Exception:
pass
return None
class RobustProxyHandler(http.server.SimpleHTTPRequestHandler): class RobustProxyHandler(http.server.SimpleHTTPRequestHandler):
def send_cors_headers(self): def send_cors_headers(self):
origin = self.headers.get('Origin') origin = self.headers.get('Origin')
@@ -699,11 +784,13 @@ class RobustProxyHandler(http.server.SimpleHTTPRequestHandler):
self.wfile.write(json.dumps({"error": f"Failed batch saving to DB: {str(e)}"}).encode('utf-8')) self.wfile.write(json.dumps({"error": f"Failed batch saving to DB: {str(e)}"}).encode('utf-8'))
# ==================== Video Streaming Handlers (Range 206) ==================== # ==================== Video Streaming Handlers (Range 206) ====================
def handle_stream_video(self, head_only=False): def handle_stream_video(self):
head_only = False
parsed = urllib.parse.urlparse(self.path) parsed = urllib.parse.urlparse(self.path)
params = urllib.parse.parse_qs(parsed.query) params = urllib.parse.parse_qs(parsed.query)
file_path = params.get('path', [''])[0] raw_path = params.get('path', [''])[0]
file_path = resolve_stream_video_path(raw_path)
if not file_path or not os.path.exists(file_path) or not os.path.isfile(file_path): if not file_path or not os.path.exists(file_path) or not os.path.isfile(file_path):
self.send_response(404) self.send_response(404)
self.send_cors_headers() self.send_cors_headers()