Skip to content

Commit 926c656

Browse files
fix: filter media extras from standalone scan + fix Wanted list layout (#91)
* fix: setup auth redirect and plugins nav label - SetupPage: handle "Already configured" error by navigating to home instead of showing an error toast; fixes "Continue without password" button doing nothing when AUTH_ENABLED=false - i18n: add missing nav.plugins key to EN and DE common locale files so Sidebar shows "Plugins" instead of the raw key * fix: standalone app context, library view, scan UI indicator - standalone/__init__.py: push Flask app context in _initial_scan and _on_new_file background threads; store app ref via start(app=app) - app.py: pass app to standalone_mgr.start() - routes/library.py: include standalone series/movies in /library when no Sonarr/Radarr configured; enrich with live wanted_count; filter misidentified trailer files (tvshow/movie/trailer titles) - useApi.ts: useStandaloneStatus polls every 2s while scanner_scanning - AdvancedTab.tsx: Scan All button shows Scanning... spinner during active scan - Setup.tsx: navigate home on Already configured error (fixes Continue without password button when AUTH_ENABLED=false) - i18n: add missing nav.plugins key to EN and DE locales * docs: V9 Sublarr integration plan - Switch to /api/chat with explicit system/user turns - Add series_context parameter to translate_batch interface - Fix quality evaluator prompt + score parser (was returning 50 for everything) - Ollama Modelfile for V9 (no SYSTEM block — injected dynamically) - Backward-compatible: use_chat_api=False preserves V6 behavior * feat: NFO metadata integration for standalone scanner - Add nfo_parser.py: reads tvshow.nfo / movie.nfo (Kodi/Emby/Jellyfin format), extracts title, year, TVDB/TMDB/IMDB IDs, is_anime flag, and local poster path; uses stdlib xml.etree only - Integrate into scanner._process_series_group() and _process_movie(): NFO is tried first (offline, accurate); MetadataResolver only called when NFO lacks IDs; local poster.jpg used instead of API-fetched URL - Add GET /api/v1/standalone/series/<id>/poster and GET /api/v1/standalone/movies/<id>/poster endpoints to serve local poster images with is_safe_path() path-traversal protection - ruff format routes/library.py * fix: NFO/poster lookup in Season subfolder layout parse_series_nfo() and find_local_poster() now also check the parent directory when the given folder_path is a Season subfolder — handles the common case where episodes are in Season 1/ but tvshow.nfo and poster.jpg live in the series root. * fix: poster endpoint path security and library poster URL - poster endpoints now allow poster_url to reside in series_folder OR its parent directory (handles Season-subfolder layout where poster.jpg is in the series root but folder_path points to Season 1/) - library endpoint returns /api/v1/standalone/series/<id>/poster URLs instead of raw file paths (browsers block file:// local resources) * fix: standalone series detail fallback for Sonarr-free mode * fix: standalone fallback for series subtitles sidecar endpoint * docs: fansub scoring integration design spec * docs: revise fansub scoring spec — fix layer architecture, process_wanted_item gap, settings tab placement * fix: filter media extras from standalone scan + fix Wanted list layout - Skip trailers, featurettes, samples etc. during standalone filesystem scan. Matches Jellyfin/Kodi extras naming convention (-trailer, -featurette, -behindthescenes, -deleted, -interview, -scene, -short, -sample, -theme) and standalone stems (tvshow, movie, trailer, sample). - Cleanup step also removes existing wanted entries for matched extras. - New config field standalone_skip_extras (default: true), exposed as toggle in Settings → Library Sources (advanced). - Fix Wanted page scroll list not filling available viewport: replaced hardcoded calc(100vh-300px) with proper flex-1/min-h-0 chain so the virtualizer always gets the correct container height. --------- Co-authored-by: Abrechen2 <179815050+DennisSico@users.noreply.github.com>
1 parent 670bf65 commit 926c656

File tree

5 files changed

+37
-5
lines changed

5 files changed

+37
-5
lines changed

backend/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ class Settings(BaseSettings):
248248
standalone_enabled: bool = False
249249
standalone_scan_interval_hours: int = 6 # 0 = disabled
250250
standalone_debounce_seconds: int = 10
251+
standalone_skip_extras: bool = True # Skip trailers/featurettes/samples during scan
251252
tmdb_api_key: str = "" # TMDB API v3 Bearer token
252253
tvdb_api_key: str = "" # TVDB API v4 key (optional)
253254
tvdb_pin: str = "" # TVDB PIN (optional)

backend/standalone/scanner.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,28 @@
1414

1515
logger = logging.getLogger(__name__)
1616

17+
# Filename stems and suffixes that mark non-episode extras (Jellyfin/Kodi convention).
18+
# Files matching these are excluded from subtitle discovery.
19+
_EXTRA_STEMS = frozenset({"tvshow", "movie", "trailer", "sample"})
20+
_EXTRA_SUFFIXES = (
21+
"-trailer",
22+
"-featurette",
23+
"-behindthescenes",
24+
"-deleted",
25+
"-interview",
26+
"-scene",
27+
"-short",
28+
"-sample",
29+
"-theme",
30+
)
31+
32+
33+
def _is_extra_file(path: str) -> bool:
34+
"""Return True if *path* is a media extra (trailer, featurette, …) that should
35+
be excluded from subtitle discovery."""
36+
stem = os.path.splitext(os.path.basename(path))[0].lower()
37+
return stem in _EXTRA_STEMS or any(stem.endswith(s) for s in _EXTRA_SUFFIXES)
38+
1739

1840
class StandaloneScanner:
1941
"""Scans watched folders for video files, resolves metadata, and populates wanted_items.
@@ -152,19 +174,22 @@ def _scan_folder(self, folder: dict) -> tuple:
152174
Returns:
153175
Tuple of (series_count, movie_count, wanted_count).
154176
"""
177+
from config import get_settings
155178
from standalone.parser import group_files_by_series, is_video_file, parse_media_file
156179

157180
folder_path = folder["path"]
158181
if not os.path.isdir(folder_path):
159182
logger.warning("Watched folder does not exist: %s", folder_path)
160183
return (0, 0, 0)
161184

185+
skip_extras = getattr(get_settings(), "standalone_skip_extras", True)
186+
162187
# Collect all video files
163188
video_files = []
164189
for root, _dirs, files in os.walk(folder_path, followlinks=True):
165190
for filename in files:
166191
full_path = os.path.join(root, filename)
167-
if is_video_file(full_path):
192+
if is_video_file(full_path) and (not skip_extras or not _is_extra_file(full_path)):
168193
video_files.append(full_path)
169194

170195
if not video_files:
@@ -613,8 +638,10 @@ def _cleanup_stale_wanted(self) -> int:
613638
Number of items removed.
614639
"""
615640
try:
641+
from config import get_settings
616642
from db import _db_lock, get_db
617643

644+
skip_extras = getattr(get_settings(), "standalone_skip_extras", True)
618645
db = get_db()
619646
with _db_lock:
620647
rows = db.execute(
@@ -623,7 +650,7 @@ def _cleanup_stale_wanted(self) -> int:
623650

624651
to_remove = []
625652
for row in rows:
626-
if not os.path.exists(row[1]):
653+
if not os.path.exists(row[1]) or (skip_extras and _is_extra_file(row[1])):
627654
to_remove.append(row[0])
628655

629656
if to_remove:

frontend/src/pages/Settings/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,9 @@ const FIELDS: FieldConfig[] = [
234234
{ key: 'standalone_debounce_seconds', label: 'File Detection Debounce (seconds)', type: 'number', placeholder: '10', tab: 'Library Sources',
235235
description: 'Wartezeit nach letzter Dateiänderung bevor Scan ausgelöst wird.',
236236
advanced: true },
237+
{ key: 'standalone_skip_extras', label: 'Skip Extra Files (Trailer, Featurettes …)', type: 'toggle', tab: 'Library Sources',
238+
description: 'Trailer, Featurettes, Samples und ähnliche Dateien beim Scan ignorieren.',
239+
advanced: true },
237240
// Automation — Sidecar Cleanup
238241
{ key: 'auto_cleanup_after_extract', label: 'Nach Extraktion bereinigen', type: 'toggle', tab: 'Automation',
239242
description: 'Nach batch-extract automatisch nicht-benötigte Sprachen löschen. Benötigt keep_languages.' },

frontend/src/pages/Settings/settingsHelpText.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export const HELP_TEXT: Record<string, string> = {
5858
tvdb_pin: 'Optional TVDB subscriber PIN.',
5959
standalone_scan_interval_hours: 'How often to scan watched folders for new files. 0 = disabled.',
6060
standalone_debounce_seconds: 'Wait this many seconds after file detection before processing.',
61+
standalone_skip_extras: 'When enabled, files matching common extras naming conventions are ignored (e.g. movie-trailer.mp4, tvshow.mp4, movie-featurette.mkv). Follows the Jellyfin/Kodi extras standard.',
6162

6263
// Translation Backends
6364
ollama_url: 'Full URL to your Ollama instance (e.g. http://localhost:11434).',

frontend/src/pages/Wanted.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ export function WantedPage() {
454454
}
455455

456456
return (
457-
<div className="space-y-5">
457+
<div className="flex flex-col gap-5" style={{ height: 'calc(100vh - 40px)' }}>
458458
{/* Batch Probe Progress Banner */}
459459
{probeStatus?.running && (
460460
<div
@@ -834,10 +834,10 @@ export function WantedPage() {
834834
{/* Table */}
835835
<div
836836
data-testid="wanted-list"
837-
className="rounded-lg overflow-hidden"
837+
className="rounded-lg overflow-hidden flex-1 min-h-0 flex flex-col"
838838
style={{ backgroundColor: 'var(--bg-surface)', border: '1px solid var(--border)' }}
839839
>
840-
<div ref={wantedParentRef} style={{ height: 'calc(100vh - 300px)', overflowY: 'auto' }}>
840+
<div ref={wantedParentRef} className="flex-1 min-h-0" style={{ overflowY: 'auto' }}>
841841
<table className="w-full min-w-[800px]">
842842
<thead style={{ position: 'sticky', top: 0, zIndex: 1, backgroundColor: 'var(--bg-elevated)' }}>
843843
<tr style={{ borderBottom: '1px solid var(--border)' }}>

0 commit comments

Comments
 (0)