Fix: Make torrent fetching instant and non-blocking with ultra-fast batched Stash GraphQL subqueries
This commit is contained in:
@@ -2627,19 +2627,20 @@
|
||||
qbTabBadge.textContent = qbState.torrents.length;
|
||||
renderTable();
|
||||
updateStats();
|
||||
showToast(`Loaded ${qbState.torrents.length} torrents (${cachedLoadedCount} restored from SQLite cache)`, 'success');
|
||||
|
||||
// Auto Stash lookup
|
||||
// Auto Stash lookup in background (non-blocking)
|
||||
if (stashAutoLookupSelect && stashAutoLookupSelect.value === 'true') {
|
||||
const unanalyzed = qbState.torrents.filter(t => !t.analysis && t.moveStatus !== 'moved');
|
||||
if (unanalyzed.length > 0) {
|
||||
const stashMatches = await matchItemsWithStash(unanalyzed, 'qb');
|
||||
if (stashMatches > 0) {
|
||||
showToast(`⚡ Stash matched ${stashMatches} / ${unanalyzed.length} torrents!`, 'success');
|
||||
}
|
||||
setTimeout(async () => {
|
||||
const stashMatches = await matchItemsWithStash(unanalyzed, 'qb');
|
||||
if (stashMatches > 0) {
|
||||
showToast(`⚡ Stash matched ${stashMatches} / ${unanalyzed.length} torrents!`, 'success');
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
|
||||
showToast(`Loaded ${qbState.torrents.length} torrents (${cachedLoadedCount} restored from SQLite cache)`, 'success');
|
||||
} catch (err) {
|
||||
showToast(err.message, 'error');
|
||||
} finally {
|
||||
@@ -3321,19 +3322,20 @@ Return ONLY valid JSON matching:
|
||||
smbTabBadge.textContent = smbState.files.length;
|
||||
renderSmbTable();
|
||||
updateSmbStats();
|
||||
showToast(`Scanned ${smbState.files.length} SMB video files (${cachedLoadedCount} restored from SQLite cache)`, 'success');
|
||||
|
||||
// Auto Stash lookup
|
||||
// Auto Stash lookup in background (non-blocking)
|
||||
if (stashAutoLookupSelect && stashAutoLookupSelect.value === 'true') {
|
||||
const unanalyzed = smbState.files.filter(f => !f.analysis && f.moveStatus !== 'moved');
|
||||
if (unanalyzed.length > 0) {
|
||||
const stashMatches = await matchItemsWithStash(unanalyzed, 'smb');
|
||||
if (stashMatches > 0) {
|
||||
showToast(`⚡ Stash matched ${stashMatches} / ${unanalyzed.length} video files!`, 'success');
|
||||
}
|
||||
setTimeout(async () => {
|
||||
const stashMatches = await matchItemsWithStash(unanalyzed, 'smb');
|
||||
if (stashMatches > 0) {
|
||||
showToast(`⚡ Stash matched ${stashMatches} / ${unanalyzed.length} video files!`, 'success');
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
|
||||
showToast(`Scanned ${smbState.files.length} SMB video files (${cachedLoadedCount} restored from SQLite cache)`, 'success');
|
||||
} catch (err) {
|
||||
showToast(`SMB scan failed: ${err.message}`, 'error');
|
||||
} finally {
|
||||
|
||||
@@ -322,40 +322,64 @@ def query_stash_batch(items, stash_url=None):
|
||||
if not items:
|
||||
return []
|
||||
|
||||
queries = []
|
||||
for idx, item in enumerate(items):
|
||||
file_path = item.get('path') or item.get('name')
|
||||
basename = os.path.basename(file_path)
|
||||
p = f'/data/videodownloader/{basename}'
|
||||
queries.append(f'item_{idx}: findScenes(scene_filter: {{path: {{value: {json.dumps(p)}, modifier: EQUALS}}}}) {{ scenes {{ id title performers {{ name gender }} studio {{ name }} }} }}')
|
||||
|
||||
batch_q = '{\n' + '\n'.join(queries) + '\n}'
|
||||
results = [None] * len(items)
|
||||
unmatched_indices = []
|
||||
|
||||
try:
|
||||
res = execute_stash_graphql(batch_q, stash_url, timeout=6)
|
||||
data = res.get('data', {})
|
||||
for idx in range(len(items)):
|
||||
scenes = data.get(f'item_{idx}', {}).get('scenes', [])
|
||||
if scenes and scenes[0].get('performers'):
|
||||
parsed = parse_stash_scene_data(scenes[0], 'exact_path')
|
||||
if parsed:
|
||||
results[idx] = parsed
|
||||
if not results[idx]:
|
||||
unmatched_indices.append(idx)
|
||||
except Exception as e:
|
||||
print(f"Batch exact Stash query failed: {e}")
|
||||
unmatched_indices = list(range(len(items)))
|
||||
# 1. First pass: Batch exact path queries in chunks of 30
|
||||
chunk_size = 30
|
||||
for c_start in range(0, len(items), chunk_size):
|
||||
c_items = items[c_start:c_start + chunk_size]
|
||||
queries = []
|
||||
for i_idx, item in enumerate(c_items):
|
||||
file_path = item.get('path') or item.get('name') or ''
|
||||
basename = os.path.basename(file_path)
|
||||
p = f'/data/videodownloader/{basename}'
|
||||
queries.append(f'item_{i_idx}: findScenes(scene_filter: {{path: {{value: {json.dumps(p)}, modifier: EQUALS}}}}) {{ scenes {{ id title performers {{ id name gender image_path }} studio {{ name }} }} }}')
|
||||
|
||||
batch_q = '{\n' + '\n'.join(queries) + '\n}'
|
||||
try:
|
||||
res = execute_stash_graphql(batch_q, stash_url, timeout=4)
|
||||
data = res.get('data', {})
|
||||
for i_idx in range(len(c_items)):
|
||||
global_idx = c_start + i_idx
|
||||
scenes = data.get(f'item_{i_idx}', {}).get('scenes', [])
|
||||
if scenes and scenes[0].get('performers'):
|
||||
parsed = parse_stash_scene_data(scenes[0], 'exact_path')
|
||||
if parsed:
|
||||
results[global_idx] = parsed
|
||||
if not results[global_idx]:
|
||||
unmatched_indices.append(global_idx)
|
||||
except Exception as e:
|
||||
print(f"Batch exact Stash query failed: {e}")
|
||||
for i_idx in range(len(c_items)):
|
||||
unmatched_indices.append(c_start + i_idx)
|
||||
|
||||
# Second pass for unmatched items
|
||||
for idx in unmatched_indices:
|
||||
item = items[idx]
|
||||
file_name = item.get('name', '')
|
||||
file_path = item.get('path', '')
|
||||
item_type = item.get('item_type', 'smb')
|
||||
match = query_stash_single(file_name, file_path, item_type, stash_url)
|
||||
results[idx] = match
|
||||
# 2. Second pass: Batch cleaned title queries for all unmatched items in chunks of 30
|
||||
if unmatched_indices:
|
||||
for c_start in range(0, len(unmatched_indices), chunk_size):
|
||||
c_indices = unmatched_indices[c_start:c_start + chunk_size]
|
||||
queries = []
|
||||
for sub_idx, g_idx in enumerate(c_indices):
|
||||
item = items[g_idx]
|
||||
raw_name = item.get('name') or os.path.basename(item.get('path') or '')
|
||||
cleaned_title = clean_torrent_title_for_stash(raw_name)
|
||||
queries.append(f'title_{sub_idx}: findScenes(filter: {{q: {json.dumps(cleaned_title[:35])}, per_page: 2}}) {{ scenes {{ id title performers {{ id name gender image_path }} studio {{ name }} }} }}')
|
||||
|
||||
batch_q = '{\n' + '\n'.join(queries) + '\n}'
|
||||
try:
|
||||
res = execute_stash_graphql(batch_q, stash_url, timeout=4)
|
||||
data = res.get('data', {})
|
||||
for sub_idx, g_idx in enumerate(c_indices):
|
||||
scenes = data.get(f'title_{sub_idx}', {}).get('scenes', [])
|
||||
if scenes:
|
||||
for s in scenes:
|
||||
if s.get('performers'):
|
||||
parsed = parse_stash_scene_data(s, 'title_match')
|
||||
if parsed:
|
||||
results[g_idx] = parsed
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"Batch title Stash query failed: {e}")
|
||||
|
||||
return results
|
||||
|
||||
|
||||
Reference in New Issue
Block a user