Skip to content

G3MINI: web dashboard, validators, watcher state, compliance scanner & Docker#4

Draft
JohanDevl wants to merge 143 commits into
lantiumBot:mainfrom
JohanDevl:main
Draft

G3MINI: web dashboard, validators, watcher state, compliance scanner & Docker#4
JohanDevl wants to merge 143 commits into
lantiumBot:mainfrom
JohanDevl:main

Conversation

@JohanDevl
Copy link
Copy Markdown

Draft PRJohanDevl/Unit3Dup-G3MINI:main is 143 commits ahead, 2 behind lantiumBot/Unit3Dup-G3MINI:main.
This PR documents all the changes made in the fork. Git conflicts are intentionally not resolved — they will be handled in a follow-up.

Summary

A long-running fork with a comprehensive set of features and fixes targeted at the G3MINI tracker workflow. Highlights:

  • Full web dashboard (Starlette + HTMX) for upload approval/rejection, queue, history, compliance, mobile-responsive dark UI.
  • Compliance scanner module to audit past uploads against G3MINI naming/encoding rules (regen prez, recheck, delete & rescan, per-track language editor).
  • Validator suite (naming, encoding, upload) — rejects no-group / upscaled releases, classifies x264/x265/AV1 as encode.
  • Persistent watcher state with multi-folder support, dedicated dry-run output, and major perf cleanup.
  • Release normalizer rework (VFF/VFQ/VOSTFR mono-track, VOF/VOQ/VOB preservation, accent transliteration, NoTag fallback).
  • Docker support: docker-compose.yml, watcher mode, TZ env, arbitrary-UID friendly, non-interactive EOFError handling.
  • CI: GitHub Actions for Docker build/publish to GHCR + Docker Hub, plus auto-tag workflow.
  • TMDB: French language by default with English fallback, IMDb ID via external_ids, robust kwargs filtering.
  • Prez/BBCode description rewrite (style 3), width-based resolution classification.
  • Packaging: optional pip extras for web deps, VERSION file as source of truth.

Diff aggregate: 78 files, +12 591 / −611 lines.


Detailed changes by domain

1. Web Dashboard (Starlette + HTMX)

Initial scaffold then ~60 follow-up commits:

  • c7721ca feat(web): add web dashboard for upload approval workflow
  • 26354bf feat(web): modern dark dashboard with sidebar, interactive tables, responsive layout
  • 2735fa3 feat(web): add TMDB ID editing and rescan from item detail page
  • 8bdeac0 feat(web): add save button to persist edits without approving or rejecting
  • edd42a4 feat(web): add reason labels, status filter dropdown, fix date sorting
  • 9613f1f feat(web): add client-side pagination (25/50/100/250/All) on all tables
  • f357461 feat(web): add skip reason column with human-readable labels in pending table
  • 5885468 feat(web): show current page number in pagination
  • 2fbec21 feat(web): replace confirm alert with approve modal showing upload summary
  • 2cf3746 feat(web): add full rescan button on pending items
  • c0ac05d fix(api): make approve endpoint accept empty body to prevent 422 errors
  • cd7cba1 fix(web): format all dates as dd/mm/yyyy HH:MM instead of raw ISO timestamps
  • 220609a feat(web): fetch TMDB title in French (fr-FR) with English fallback on rescan
  • 2330b9d fix(web): complete mobile responsive overhaul (cards on mobile, no horizontal scroll)
  • ebe9a06 feat(web): add multi-select column filters and mobile sort dropdown
  • b1b84a1 fix(web): preserve pending list pagination across HTMX refreshes
  • 229cd03 feat(web): add similar torrents link on detail page
  • 60b8ee8 feat(web): add category adjustment on pending item detail page
  • ac44404 fix(web): preserve table sort state across HTMX pending refresh
  • 8a685ec feat(web): auto-update release name when TMDB ID is rescanned
  • 5400499 fix(web): redirect to pending page after approve/reject instead of history
  • 4b06c27 fix(web): use full rescan instead of retry for skipped/rejected items
  • 3abdd6d feat(web): add season/episode display and editing for TV series
  • 6fc1fcd feat(web): add bulk reject UI for pending items list
  • 70f1465 feat(web): add async upload queue with background worker and bulk approve modal
  • 1a3259a feat(web): add queue status page with sidebar badge and live refresh
  • 0687d1f feat(web): normalize separators in search bar for flexible name matching
  • 5bbdfe2 feat(web): add upload-cloud favicon matching sidebar icon
  • 5a15750 feat(web): add cancel queued and reset uploaded item actions
  • 790756e feat(web): add visual analyzing indicator for watcher processing
  • 114c081 feat(web): display human-readable category labels instead of raw values
  • d5dc25a feat(web): add season/episode editing to bulk approve modal for TV series
  • 18d4c90 feat(web): add track language editor and title-based language fallback
  • 3a48237 feat(web): add async bulk rescan queue with web visibility
  • 8f787b9 update(web): replace native confirm with proper modal for bulk rescan
  • 898a925 / bd72633 fix(web): batch all transitions before enqueueing in bulk rescan / approve
  • 60a8ca2 feat(web): link duplicate match on tracker and add force-rescan action
  • f820ed0 fix(web): persist tracker_name on duplicate skip and add similar-url fallback

2. Compliance Module

  • 967a6da feat(web): add compliance menu to audit past upload naming against G3MINI rules
  • 650d413 fix(compliance): align normalizer & validators with G3MINI docs rules
  • 23ee647 feat(web): show description & mediainfo in compliance detail modal
  • 4855637 feat(web): regenerate prez-format description in compliance modal
  • 90a8c2c fix(compliance): read UnIT3D media_info/bd_info snake_case API keys
  • 4a0bb2b fix(compliance): flatten unwrapped torrent response and modal-ify recheck
  • 7a265ec update(web): auto-reload compliance page after single-row recheck
  • 0733e7c feat(compliance): add regenerate button and per-track language editor
  • af5867d feat(compliance): add ack/ignore buttons in modal with in-place status update
  • ffcf7d4 feat(compliance): add delete & rescan action with bulk selection
  • 6d1abed fix(web): copy-on-click compliance names + keep H.264/H.265 codec at end

3. Validator Suite

  • 7782e62 feat(validation): add tracker rule validators, dynamic piece sizing, and 3D/DTS-HD HRA support
  • f1de5d3 feat(validator): reject uploads without a real release group
  • bfa7f2a feat(validator): reject upscaled releases with ERROR severity
  • 9e41948 fix(validator): treat x264/x265/AV1 tags as encode, not untouched
  • 41ef372 fix(tmdb): filter unknown kwargs before dataclass init

4. Watcher State & Performance

  • 3d0a322 feat(watcher): add persistent state tracking to avoid reprocessing
  • 9031645 fix(watcher): skip state persistence in dry-run mode (-noup/-noseed)
  • d176202 feat(watcher): write dry-run results to separate watcher_dryrun.json
  • 333640a fix(watcher): treat already_in_archive as uploaded and atomic state saves
  • 125b331 feat(watcher): add multi-folder support with per-folder qBittorrent category
  • 0d21689 perf(watcher): reduce countdown loop wake-ups from 100/s to 1/s
  • 3dda7c6 fix(watcher): break infinite loop on already_in_archive in web mode
  • 0702130 fix(watcher): handle stale torrent archive causing false 'already uploaded' for series
  • 8b946a4 fix(watcher): skip non-media files (.nfo, .txt, etc.) in watcher scan
  • 4f1c4ed fix(watcher): preserve validation reports on dry-run re-runs (41c49d4)
  • d6a8df7 feat(watcher): add release source type to watcher JSON
  • 4a03f7a feat(watcher): include validation report details in watcher JSON

5. Release Normalizer

  • 568c771 feat(normalizer): use single-lang tags (VFF/VFQ/VOSTFR) for mono-track releases
  • 5b8b8db fix(naming): preserve VOF/VOQ/VOB tags in release names
  • ef10a65 fix(naming): prioritize MediaInfo Title VO markers over Language code
  • ade3a7c feat(naming): inject TMDB year into series release names
  • 7a22d6f fix(normalizer): resolve 22 anomalies in release name normalization
  • 60afe99 fix(normalizer): add NoTag fallback and clean separator/bitrate artifacts
  • adff990 fix(normalizer): transliterate accented characters instead of stripping them
  • 462bf4d feat(category): detect animated content via TMDB genre and assign separate tracker categories
  • ca8a23d fix(normalizer): normalize SUBFRENCH to VOSTFR in release titles
  • deaacc4 fix(normalizer): align release naming with updated tracker rules

6. TMDB / External Services

  • c54952f fix(tmdb): use French language for API requests and fix rescan title extraction
  • 73584cd feat(tmdb): fetch IMDb ID during discover via details/external_ids endpoints
  • 6cc7ed0 fix(tmdb): add missing fields to ExternalIds dataclass for TMDB API compatibility
  • 41ef372 fix(tmdb): filter unknown kwargs before dataclass init
  • ada37d7 feat: add TMDB-based documentary detection mirroring anime pattern
  • 930cd2b fix(prez): pre-select detected track language and add fr-CA/fr-BE
  • 00f7247 fix(prez): normalize language tags with region and english names
  • f2f933d fix(mediainfo): strip full path from Complete name in displayed mediainfo

7. Prez / BBCode Description

  • bc944ae feat(prez): replace screenshot description with BBCode prez style 3
  • 8078da3 fix(prez): use width-based resolution classification for quality label
  • 2cc0ba9 fix(web): show generated NFO (cleaned mediainfo) instead of source NFO file
  • fcd3a4f fix(web): remove NFO source display from dashboard, avoid confusion with mediainfo

8. Docker / Deployment

  • f3052bc feat(docker): add docker-compose.yml with watcher mode and first-run setup
  • b15c42d feat(docker): add TZ environment variable for timezone support
  • 455bbd2 fix(docker): make home directory traversable for arbitrary UIDs
  • a1eb35c fix(docker): make venv world-executable for arbitrary UIDs
  • 33812f1 fix(docker): set HOME env for arbitrary UIDs and remove unused destination path
  • d4eb555 update(docker): remove done volume and document watcher state files
  • 95391a2 fix(docker): handle EOFError on input() calls for non-interactive containers
  • cafe4da fix(cache): use absolute paths for diskcache and prez file output

9. CI / Release Automation

  • 668c6f2 feat(ci): add GitHub Actions for Docker build and publish to GHCR and Docker Hub
  • Auto-tag workflow added (.github/workflows/auto-tag.yml)

10. Packaging / Settings

  • b81cacf feat(packaging): make web dependencies optional via pip extras
  • 7f3d30c fix(settings): read version from VERSION file instead of hardcoded value
  • 724964b fix(settings): use importlib.metadata for version in installed packages
  • 682a153 fix(settings): handle empty string for WEB_PORT in existing configs
  • 33e29bd fix(settings): handle empty string for WEB_HOST in existing configs
  • 5b6db02 fix(settings): handle empty string for QBIT_SKIP_HASH_CHECK in existing configs
  • c1a5e7c fix(settings): use model defaults for new torrent config attributes
  • 0c9907f feat(torrent): add skip hash check option for qBittorrent
  • b9ec363 fix(qbittorrent): let category handle save path instead of forcing it

11. Other Fixes

  • 71abe94 fix(media): accept season 0 and episode 0 for TV series uploads
  • 70b720f fix(video): guard against None db.result before calling get_title()
  • a803176 fix(trackers): correct video TYPE_ID mappings and infer source from resolution
  • d473845 fix(trackers): pass title instead of full path to filter_type to prevent directory names matching TYPE_ID keys
  • 5aafc81 fix(db): use localtime for discovered_at default to respect TZ env variable
  • ed97a51 fix(db): use Python datetime.now() for discovered_at instead of SQLite default
  • ce55ac8 fix(web): restrict field editing (resolution, source, TMDB) to pending status only
  • 5a94838 feat(web): add resolution correction dropdown with exact tracker values
  • 94f4f85 fix(web): use exact tracker type_id values with optgroups in source dropdown
  • f2bc095 feat(web): add source type (type_id) correction dropdown in item detail
  • ee7fa88 feat(upload): add EXCLUDED_TAGS to skip uploads by team tag
  • eddaecc feat(dry-run): count processed media as uploaded and handle all EOFError cases
  • 10f5446 feat(dry-run): store normalized release name in watcher state

Note on the 2 commits behind upstream

The 2 upstream commits not yet merged into the fork are both about the initial Docker-stack support:

The fork has reworked Docker more deeply (docker-compose.yml, multi-folder watcher, arbitrary UIDs, TZ, EOF handling). These commits will be reconciled when conflicts are resolved.

Test plan

  • Maintainer review of the categorized changes
  • Decide which feature areas should land first (web dashboard, validators, watcher state, compliance, etc.)
  • Resolve conflicts (followed up separately by fork author)
  • Run smoke tests on watcher mode (with and without web dashboard)
  • Validate Docker build via the GitHub Actions workflow once merged

JohanDevl and others added 30 commits March 18, 2026 09:39
Add blacklist mechanism for team tags, mirroring the existing
TAGS_TEAM whitelist system. Releases with a team tag matching
EXCLUDED_TAGS are skipped before upload. Applied to Video, Game,
and Docu content types.
feat: tracker rule validators, dynamic piece sizing, and team tag exclusion
Replace destination_path with a JSON state file (watcher_state.json) in
the config directory. Tracks uploaded and skipped entries with reasons
(validation_error, duplicate, excluded_tag, etc.) so the watcher skips
already-processed items instantly instead of re-running the full pipeline.

Also fix indentation bugs in GameManager and DocuManager where the
continue statement was at the wrong level, causing all games and docs
to be skipped in watcher mode.
…ategory

Support multiple source folders in watcher mode via WATCHER_PATHS config,
each with an optional qBittorrent category for cross-seed organization.
Backward compatible with legacy single WATCHER_PATH format.
Apply 9 fixes from updated .claude/ rules: H.265/H.264 with dots for
WEB/HDTV, DTS-HDMA single token, preserve 10bit/12bit, add OPUS audio
detection, DD→AC3 alias, HDTV/HDLight/4KLight codec conventions, 4.0/6.1
channel support, and NoGRP/NoTAG validation for teamless releases.
- preserve "Max" in titles by removing HBO MAX from streaming cleanup
- support compound team names with dash and ampersand (Tsundere-Raws, TsundereRaws&T3KASHi)
- prevent codecs from being used as false team tags (x264-x264)
- strip parenthesized non-year content early to prevent title pollution
- add BRRip as distinct source type, normalize HE-AAC/EAC3/AC3@bitrate
- remove AD from extras when in title position, keep when after year
- clean 6CH/HEVC/EN/KO/READNFO/DUAL residual tags from title
- fix 10bits plural, FR-EN compound, duplicate language tags
- preserve 6CH as valid audio channel format per tracker rules
Upscaled content is strictly forbidden by tracker rules (encodage.md,
upload.md). Add release-name-based detection that blocks uploads
containing the UpScaled tag, complementing the existing height-based
check. Also extract UpScaled as a proper extras tag in the normalizer
so it no longer pollutes the title.
Add Check 5 in NamingValidator to block releases where the team is
a placeholder (NoTag, NoGRP) or a year mistakenly used as team name.
Severity ERROR ensures the upload is skipped entirely.
Store the full validation report (severity, rule, message, doc) in
watcher_state.json and watcher_dryrun.json for both skipped entries
(validation errors) and uploaded entries (warnings/infos).

Bump version to 0.9.7.
Include the guessit source (BluRay, WEB-DL, WEBRip, HDTV, BDRip, etc.)
in watcher_state.json and watcher_dryrun.json for both uploaded and
skipped entries.

Bump version to 0.9.8.
…aves

Items whose torrent file already exists in the archive are now marked
as "uploaded" instead of "skipped", since the torrent presence means
the content was previously uploaded. Also makes state file writes
atomic (tmp + os.replace) to prevent corruption on interrupt, and
logs a warning instead of silently ignoring corrupt state files.
Single source of truth for the version: the VERSION file used by
pyproject.toml. The hardcoded string in settings.py was stale (0.8.x)
and never referenced externally. Bump to 0.9.9.
JohanDevl and others added 30 commits April 9, 2026 21:22
Move fastapi, uvicorn, jinja2, python-multipart to [project.optional-dependencies]
so CLI-only users don't need to install them. Install with pip install Unit3Dup[web]
for web dashboard support.
Infer subtitle/audio language from track title when the language field
is missing (e.g. Title: French → fr) and add a web UI to edit track
languages with prez regeneration before upload approval.
- Click on current/proposed name (list row or detail modal) copies to clipboard;
  hover reveals a copy icon and success flashes the cell green.
- Pre-fuse H.264/H.265 before the dot→space step so the codec is no longer
  split into "H" + "264" tokens that leak into the title; codec stays at the
  canonical tail position per G3MINI naming.
- normalizer: SUBFRENCH kept distinct from VOSTFR, add SUBFORCED;
  VFI now routed through FRENCH disambiguation (VFF/VFQ via MediaInfo);
  add COMPLETE, 4K.REMASTER, DoVi, HDTVRip, BDMV, DVD9, DVD5 tokens
- naming: NoTAG/NoGRP accepted as placeholder per upload.md §1
- naming: add HARDSUB→SUBFRENCH check and MULTi (VO+VF+FR subs) invariant
- encoding: add source-reencoded detection (untouched tag + x264/x265 library)
  and basic crop heuristic (16:9 stored + cinema DAR)
- upload: add pack consistency (multi-team, mix FRENCH/MULTi)
- compliance: extract subtitle/library/dimensions from MediaInfo text,
  extend _FakeMediaFile, run EncodingValidator alongside NamingValidator
Previously, VOF/VOQ/VOB (Version Originale) tags were stripped or
replaced by VFF when combined with MULTi or FRENCH:
- MULTi.VOF was reduced to bare MULTi
- FRENCH.VOF was overwritten to VFF
- MULTi.FRENCH.VOF became MULTi.VFF

VO tags carry distinct information (original audio, not a dub) and
must never be lost or downgraded to a dub indicator.
When an audio track has Title=VOF but Language=French (FR), the previous
logic returned VFF because the language code was checked first. Audio with
an explicit VO title is original French audio, not a dub — VOF must win.

Title-based VOF/VOQ/VOB now take priority over the generic
Language: French (FR)/(CA) signal.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant