riplex 0.3.3__tar.gz → 0.3.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- riplex-0.3.4/LICENSE +21 -0
- {riplex-0.3.3/src/riplex.egg-info → riplex-0.3.4}/PKG-INFO +3 -1
- {riplex-0.3.3 → riplex-0.3.4}/docs/getting-started/installation.md +7 -0
- riplex-0.3.4/issues/orchestrate-dvdcompare-fallback.md +45 -0
- riplex-0.3.4/issues/planned-features.md +77 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/config.py +7 -1
- {riplex-0.3.3 → riplex-0.3.4/src/riplex.egg-info}/PKG-INFO +3 -1
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex.egg-info/SOURCES.txt +3 -3
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_cli/commands/setup.py +9 -4
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_cli/main.py +8 -2
- riplex-0.3.3/MONOREPO_PLAN.md +0 -115
- riplex-0.3.3/PLANNED_FEATURES.md +0 -416
- riplex-0.3.3/plex_naming_rules.md +0 -154
- {riplex-0.3.3 → riplex-0.3.4}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/.github/agents/riplex.agent.md +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/.github/copilot-instructions.md +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/.github/workflows/publish.yml +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/.github/workflows/release.yml +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/.gitignore +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/README.md +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/REFACTOR_PLAN.md +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/docs/architecture.md +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/docs/changelog.md +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/docs/getting-started/configuration.md +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/docs/guide/lookup.md +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/docs/guide/orchestrate.md +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/docs/guide/organize.md +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/docs/guide/workflow.md +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/docs/index.md +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/docs/naming-rules.md +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/docs/reference/cli.md +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/issues/cross-disc-dvdcompare-matching.md +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/mkdocs.yml +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/pyproject.toml +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/setup.cfg +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/__init__.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/cache.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/dedup.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/detect.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/disc/__init__.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/disc/analysis.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/disc/makemkv.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/disc/provider.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/formatter.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/lookup.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/manifest.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/matcher.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/metadata/__init__.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/metadata/planner.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/metadata/provider.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/metadata/sources/__init__.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/metadata/sources/tmdb.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/models.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/normalize.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/organizer.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/scanner.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/snapshot.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/splitter.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/tagger.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/title.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex/ui.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex.egg-info/dependency_links.txt +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex.egg-info/entry_points.txt +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex.egg-info/requires.txt +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex.egg-info/top_level.txt +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_app/__init__.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_app/main.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_app/screens/__init__.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_app/screens/disc_detection.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_app/screens/done.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_app/screens/folder_picker.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_app/screens/metadata.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_app/screens/organize_done.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_app/screens/organize_preview.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_app/screens/progress.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_app/screens/release.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_app/screens/selection.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_app/screens/welcome.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_app/updater.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_cli/__init__.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_cli/commands/__init__.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_cli/commands/lookup.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_cli/commands/orchestrate.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_cli/commands/organize.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_cli/commands/rip.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/src/riplex_cli/formatting.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/__init__.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/fixtures/makemkvcon_frozen_planet_ii_d2.txt +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/fixtures/makemkvcon_list.txt +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/snapshots/Batman Begins.snapshot.json +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/snapshots/Blade Runner (Blu-ray 4k).snapshot.json +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/snapshots/Blade Runner The Final Cut.snapshot.json +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/snapshots/Seven Worlds One Planet.snapshot.json +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/snapshots/The Dark Knight Rises.snapshot.json +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/snapshots/The Dark Knight.snapshot.json +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/snapshots/Waterworld.snapshot.json +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_cache.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_cli_utils.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_config.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_dedup.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_detect.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_disc_analysis.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_disc_provider.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_formatter.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_makemkv.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_matcher.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_normalize.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_organizer.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_planner.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_rip_guide.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_scanner.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_snapshot.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_splitter.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_tagger.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_ui.py +0 -0
- {riplex-0.3.3 → riplex-0.3.4}/tests/test_updater.py +0 -0
riplex-0.3.4/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AnyCredit5518
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: riplex
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Automates the tedious manual work around MakeMKV: figuring out what to rip, which MKV files are actually what, and organizing everything into Plex-compatible folder structures.
|
|
5
5
|
License: MIT
|
|
6
6
|
Requires-Python: >=3.11
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
8
9
|
Requires-Dist: httpx>=0.27
|
|
9
10
|
Requires-Dist: dvdcompare-scraper>=0.1.6
|
|
10
11
|
Requires-Dist: platformdirs>=4.0
|
|
@@ -14,6 +15,7 @@ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
|
14
15
|
Requires-Dist: respx>=0.21; extra == "dev"
|
|
15
16
|
Provides-Extra: gui
|
|
16
17
|
Requires-Dist: flet>=0.84; extra == "gui"
|
|
18
|
+
Dynamic: license-file
|
|
17
19
|
|
|
18
20
|
# riplex
|
|
19
21
|
|
|
@@ -75,6 +75,13 @@ riplex setup
|
|
|
75
75
|
The setup wizard will:
|
|
76
76
|
|
|
77
77
|
1. Ask for your TMDb API key (free at https://www.themoviedb.org/settings/api)
|
|
78
|
+
|
|
79
|
+
> [!TIP]
|
|
80
|
+
> TMDb asks for an app name and URL when you request a key. You can just
|
|
81
|
+
> enter "riplex" as the app name and `https://github.com/AnyCredit5518/riplex`
|
|
82
|
+
> as the URL. The rest of the form can be filled with basic info - it doesn't
|
|
83
|
+
> need to be a real business. The key is approved instantly.
|
|
84
|
+
|
|
78
85
|
2. Ask where your Plex library and MakeMKV rip folders are
|
|
79
86
|
3. Check for required tools (MakeMKV, ffprobe, mkvmerge, mkvpropedit)
|
|
80
87
|
4. Offer to install any missing tools for you (via winget on Windows, Homebrew on macOS, or apt on Linux)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Orchestrate: dvdcompare fallback for missing discs
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
When `riplex orchestrate` can't find a disc on dvdcompare, it exits with a
|
|
6
|
+
fatal error instead of falling back to duration-based title selection. The
|
|
7
|
+
`rip` command handles this correctly (warns and continues), but `orchestrate`
|
|
8
|
+
does `return 1`.
|
|
9
|
+
|
|
10
|
+
## Affected users
|
|
11
|
+
|
|
12
|
+
Anyone using `orchestrate` with a disc that isn't listed on dvdcompare (niche
|
|
13
|
+
releases, old DVDs, foreign titles, etc).
|
|
14
|
+
|
|
15
|
+
## Workaround
|
|
16
|
+
|
|
17
|
+
Use `riplex rip` instead of `orchestrate` for single-disc titles not on
|
|
18
|
+
dvdcompare. It warns about the missing dvdcompare data and falls back to
|
|
19
|
+
selecting the main feature by TMDb runtime matching.
|
|
20
|
+
|
|
21
|
+
## Proposed fix
|
|
22
|
+
|
|
23
|
+
Change `orchestrate.py` to warn instead of error when dvdcompare fails, then:
|
|
24
|
+
|
|
25
|
+
1. Skip the multi-disc loop entirely (no disc data to iterate)
|
|
26
|
+
2. Fall through to a single-disc rip using the same heuristics as `rip.py`:
|
|
27
|
+
- `build_dvd_entries([])` returns empty entries
|
|
28
|
+
- `select_rippable_titles()` uses TMDb runtime to pick the main feature
|
|
29
|
+
3. After ripping, proceed to the organize step normally
|
|
30
|
+
|
|
31
|
+
### Complications
|
|
32
|
+
|
|
33
|
+
The downstream code in orchestrate's disc loop (`disc_order`, disc swap
|
|
34
|
+
prompts, `detect_disc_number`, `_print_disc_overview`) all assume `discs` is
|
|
35
|
+
populated. A full fix needs to either:
|
|
36
|
+
|
|
37
|
+
- Create a synthetic single-disc `PlannedDisc` entry when dvdcompare fails
|
|
38
|
+
- Or branch early into a simplified single-disc rip path (essentially
|
|
39
|
+
delegating to `rip`-like behavior) before hitting the disc loop
|
|
40
|
+
|
|
41
|
+
### Files involved
|
|
42
|
+
|
|
43
|
+
- `src/riplex_cli/commands/orchestrate.py` (lines ~249-258): the error/return
|
|
44
|
+
- Downstream: disc loop starting at ~line 285, `_print_disc_overview`,
|
|
45
|
+
`detect_disc_number`, disc swap prompts
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Planned Features
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## Multi-Resolution Support (4K + Standard Blu-ray)
|
|
5
|
+
|
|
6
|
+
Many 4K boxsets include standard Blu-ray discs with the same content at 1080p.
|
|
7
|
+
Some users may want to rip both so Plex can serve the lower-resolution version
|
|
8
|
+
to mobile devices without transcoding.
|
|
9
|
+
|
|
10
|
+
**Movies**: Supported via Plex "Multi-Version Movies" — multiple files with
|
|
11
|
+
different resolution suffixes in the same folder collapse into one item.
|
|
12
|
+
|
|
13
|
+
**TV Shows**: NOT officially supported by Plex. No documented multi-version
|
|
14
|
+
episode collapsing.
|
|
15
|
+
|
|
16
|
+
### Plan
|
|
17
|
+
|
|
18
|
+
- Support ripping both 4K and standard Blu-ray discs for movies, naming with
|
|
19
|
+
resolution suffixes during organize.
|
|
20
|
+
- For TV shows, warn users and offer to skip (explain limitation), allow
|
|
21
|
+
override for users with separate libraries.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Orchestrate Flow (GUI)
|
|
25
|
+
|
|
26
|
+
The GUI has rip and organize flows. The orchestrate flow (multi-disc pipeline
|
|
27
|
+
with disc-swap prompts) is not yet implemented.
|
|
28
|
+
|
|
29
|
+
### Key pieces needed
|
|
30
|
+
|
|
31
|
+
- Disc swap prompt screen
|
|
32
|
+
- Session state tracking across disc swaps (accumulated rip results, metadata)
|
|
33
|
+
- Disc number auto-detection after each swap
|
|
34
|
+
- Error recovery (retry/skip failed disc)
|
|
35
|
+
- Cancel mid-flow and organize what's been ripped so far
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
## Bug Report Submission
|
|
39
|
+
|
|
40
|
+
### Problem
|
|
41
|
+
|
|
42
|
+
Snapshot files for debugging are mixed in with media files and have
|
|
43
|
+
inconsistent formats. Users must hunt for debug artifacts across multiple
|
|
44
|
+
locations when filing bug reports.
|
|
45
|
+
|
|
46
|
+
### Plan
|
|
47
|
+
|
|
48
|
+
1. Move all debug artifacts into a `_riplex/` subfolder (Plex-ignored).
|
|
49
|
+
2. Consistent v2 snapshot envelope format with type discriminator.
|
|
50
|
+
3. GUI writes same snapshots as CLI.
|
|
51
|
+
4. "Report a Bug" button in GUI: opens pre-filled GitHub issue, copies
|
|
52
|
+
debug folder path to clipboard.
|
|
53
|
+
5. `.github/ISSUE_TEMPLATE/bug_report.yml` for structured reports.
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
## Interactive Lookup Command
|
|
57
|
+
|
|
58
|
+
`riplex lookup` currently auto-picks the first TMDb match and default
|
|
59
|
+
dvdcompare release without confirmation. Add interactive selection (reuse
|
|
60
|
+
existing `_pick_best` prompt from planner). Keep `--auto` flag for scripting.
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
## Multi-Language Track Selection
|
|
64
|
+
|
|
65
|
+
Users in multilingual households want to keep multiple audio and subtitle
|
|
66
|
+
tracks when ripping, not just the default/English track.
|
|
67
|
+
|
|
68
|
+
### Plan
|
|
69
|
+
|
|
70
|
+
- Add config options for preferred audio and subtitle languages (e.g.
|
|
71
|
+
`audio_languages = ["en", "es"]`, `subtitle_languages = ["en", "es", "fr"]`)
|
|
72
|
+
- During rip, pass language preferences to MakeMKV/mkvmerge so all selected
|
|
73
|
+
tracks are retained
|
|
74
|
+
- During organize, preserve all selected tracks when remuxing
|
|
75
|
+
- GUI: add language selection to setup/config screen
|
|
76
|
+
- Default behavior: keep all tracks (current MakeMKV default) — only filter
|
|
77
|
+
if the user explicitly configures preferred languages
|
|
@@ -33,7 +33,13 @@ def load_config() -> dict[str, Any]:
|
|
|
33
33
|
for path in _candidate_paths():
|
|
34
34
|
if path.is_file():
|
|
35
35
|
with open(path, "rb") as f:
|
|
36
|
-
|
|
36
|
+
try:
|
|
37
|
+
return tomllib.load(f)
|
|
38
|
+
except tomllib.TOMLDecodeError as exc:
|
|
39
|
+
raise SystemExit(
|
|
40
|
+
f"Error: invalid config file {path}\n {exc}\n"
|
|
41
|
+
f"Run 'riplex setup --force' to delete it and start fresh."
|
|
42
|
+
) from None
|
|
37
43
|
return {}
|
|
38
44
|
|
|
39
45
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: riplex
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Automates the tedious manual work around MakeMKV: figuring out what to rip, which MKV files are actually what, and organizing everything into Plex-compatible folder structures.
|
|
5
5
|
License: MIT
|
|
6
6
|
Requires-Python: >=3.11
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
8
9
|
Requires-Dist: httpx>=0.27
|
|
9
10
|
Requires-Dist: dvdcompare-scraper>=0.1.6
|
|
10
11
|
Requires-Dist: platformdirs>=4.0
|
|
@@ -14,6 +15,7 @@ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
|
14
15
|
Requires-Dist: respx>=0.21; extra == "dev"
|
|
15
16
|
Provides-Extra: gui
|
|
16
17
|
Requires-Dist: flet>=0.84; extra == "gui"
|
|
18
|
+
Dynamic: license-file
|
|
17
19
|
|
|
18
20
|
# riplex
|
|
19
21
|
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
.gitignore
|
|
2
|
-
|
|
3
|
-
PLANNED_FEATURES.md
|
|
2
|
+
LICENSE
|
|
4
3
|
README.md
|
|
5
4
|
REFACTOR_PLAN.md
|
|
6
5
|
mkdocs.yml
|
|
7
|
-
plex_naming_rules.md
|
|
8
6
|
pyproject.toml
|
|
9
7
|
.github/copilot-instructions.md
|
|
10
8
|
.github/ISSUE_TEMPLATE/bug_report.yml
|
|
@@ -23,6 +21,8 @@ docs/guide/organize.md
|
|
|
23
21
|
docs/guide/workflow.md
|
|
24
22
|
docs/reference/cli.md
|
|
25
23
|
issues/cross-disc-dvdcompare-matching.md
|
|
24
|
+
issues/orchestrate-dvdcompare-fallback.md
|
|
25
|
+
issues/planned-features.md
|
|
26
26
|
src/riplex/__init__.py
|
|
27
27
|
src/riplex/cache.py
|
|
28
28
|
src/riplex/config.py
|
|
@@ -53,7 +53,7 @@ def _offer_install(missing: list[str]) -> None:
|
|
|
53
53
|
print("\n Installation complete. You may need to restart your terminal for PATH changes to take effect.")
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
def run_setup() -> int:
|
|
56
|
+
def run_setup(force: bool = False) -> int:
|
|
57
57
|
"""Interactive setup wizard to create or update the riplex config file."""
|
|
58
58
|
import shutil
|
|
59
59
|
|
|
@@ -62,9 +62,14 @@ def run_setup() -> int:
|
|
|
62
62
|
print("riplex setup")
|
|
63
63
|
print(f"Config file: {config_path}\n")
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
print("(Existing config
|
|
65
|
+
if force and config_path.is_file():
|
|
66
|
+
config_path.unlink()
|
|
67
|
+
print("(Existing config deleted. Starting fresh.)\n")
|
|
68
|
+
existing: dict[str, str] = {}
|
|
69
|
+
else:
|
|
70
|
+
existing = load_config()
|
|
71
|
+
if existing:
|
|
72
|
+
print("(Existing config found. Press Enter to keep current values.)\n")
|
|
68
73
|
|
|
69
74
|
def prompt(key: str, label: str, hint: str = "", mask: bool = False) -> str:
|
|
70
75
|
current = existing.get(key, "")
|
|
@@ -325,10 +325,16 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
325
325
|
)
|
|
326
326
|
|
|
327
327
|
# --- setup ---
|
|
328
|
-
subs.add_parser(
|
|
328
|
+
setup_parser = subs.add_parser(
|
|
329
329
|
"setup",
|
|
330
330
|
help="Interactive setup wizard to create or update the config file.",
|
|
331
331
|
)
|
|
332
|
+
setup_parser.add_argument(
|
|
333
|
+
"--force",
|
|
334
|
+
action="store_true",
|
|
335
|
+
default=False,
|
|
336
|
+
help="Delete existing config and start fresh.",
|
|
337
|
+
)
|
|
332
338
|
|
|
333
339
|
return parser
|
|
334
340
|
|
|
@@ -343,7 +349,7 @@ async def _run(args: argparse.Namespace) -> int:
|
|
|
343
349
|
if args.command == "orchestrate":
|
|
344
350
|
return await run_orchestrate(args)
|
|
345
351
|
if args.command == "setup":
|
|
346
|
-
return run_setup()
|
|
352
|
+
return run_setup(force=getattr(args, "force", False))
|
|
347
353
|
return 1
|
|
348
354
|
|
|
349
355
|
|
riplex-0.3.3/MONOREPO_PLAN.md
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
# Monorepo Refactor Plan
|
|
2
|
-
|
|
3
|
-
## Project Context
|
|
4
|
-
|
|
5
|
-
riplex automates disc ripping and file organization for Plex. It reads physical discs via MakeMKV, identifies what's on them using TMDb and dvdcompare.net metadata, rips the right titles, then deduplicates, matches, renames, and moves files into the exact folder structure Plex expects.
|
|
6
|
-
|
|
7
|
-
There are currently two repos:
|
|
8
|
-
- **riplex** (`C:\Users\asher\Projects\anycredit5518\riplex`): the library and CLI, published to PyPI
|
|
9
|
-
- **riplex-app** (`C:\Users\asher\Projects\anycredit5518\riplex-app`): a Flet-based GUI that depends on riplex as a pip package
|
|
10
|
-
|
|
11
|
-
## Problem
|
|
12
|
-
|
|
13
|
-
The current two-repo setup has issues:
|
|
14
|
-
|
|
15
|
-
1. **Shared logic is trapped in `cli.py`**: Functions like disc format detection, release scoring, disc number inference, and title extraction are private helpers in the CLI module. The GUI needs the same logic but can't access it, so it duplicates it.
|
|
16
|
-
|
|
17
|
-
2. **Circular development friction**: Changing a shared algorithm means updating both repos, keeping them in sync, and dealing with version pinning.
|
|
18
|
-
|
|
19
|
-
3. **Distribution complexity**: Users who want the GUI must install two packages. Building the GUI .exe requires a working riplex install.
|
|
20
|
-
|
|
21
|
-
## Goal
|
|
22
|
-
|
|
23
|
-
Consolidate into a single repo with a clean separation of concerns:
|
|
24
|
-
|
|
25
|
-
- **Library layer**: all business logic, reusable by any frontend
|
|
26
|
-
- **CLI layer**: thin wrapper that handles argument parsing, terminal formatting, and interactive prompts
|
|
27
|
-
- **GUI layer**: thin wrapper that handles Flet screens and user interaction
|
|
28
|
-
|
|
29
|
-
Both the CLI and GUI should be thin consumers of the library. Neither should contain business logic.
|
|
30
|
-
|
|
31
|
-
## Requirements
|
|
32
|
-
|
|
33
|
-
1. **Single repo**: all code lives in one place, one set of tests, one CI pipeline
|
|
34
|
-
2. **Clean library API**: shared orchestration logic must be importable as a public module (not private `_functions` buried in a CLI file)
|
|
35
|
-
3. **Independent installability**: `pip install riplex` gets you library + CLI (no GUI dependencies). GUI is optional via `pip install riplex[gui]` or standalone binary.
|
|
36
|
-
4. **Zero CLI regression**: the `riplex` command must behave identically after the refactor. All existing tests must pass at every step.
|
|
37
|
-
5. **Standalone GUI binary**: the GUI can be packaged as a .exe/.app for users who don't have Python
|
|
38
|
-
6. **Atomic commits**: each commit should be a self-contained logical change that doesn't break the build
|
|
39
|
-
|
|
40
|
-
## Target Architecture
|
|
41
|
-
|
|
42
|
-
```
|
|
43
|
-
riplex/
|
|
44
|
-
src/
|
|
45
|
-
riplex/ # Shared library (all business logic)
|
|
46
|
-
riplex_cli/ # CLI thin wrapper (argparse, terminal formatting, prompts)
|
|
47
|
-
riplex_app/ # GUI thin wrapper (Flet screens, no business logic)
|
|
48
|
-
tests/
|
|
49
|
-
docs/
|
|
50
|
-
pyproject.toml
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### Distribution
|
|
54
|
-
|
|
55
|
-
| Install method | What you get |
|
|
56
|
-
|---|---|
|
|
57
|
-
| `pip install riplex` | Library + CLI |
|
|
58
|
-
| `pip install riplex[gui]` | Library + CLI + GUI (flet dependency) |
|
|
59
|
-
| GitHub Release .exe/.app | Standalone GUI binary |
|
|
60
|
-
|
|
61
|
-
## Scope of Work
|
|
62
|
-
|
|
63
|
-
The core of this refactor is extracting shared logic out of `cli.py` so both frontends can use it, then bringing the GUI source into this repo.
|
|
64
|
-
|
|
65
|
-
Key areas to address:
|
|
66
|
-
- `cli.py` is ~2800 lines mixing business logic with CLI presentation. The reusable pipeline logic (disc detection, format inference, release selection/scoring, disc number detection, title inference, folder creation, manifest handling) needs to become a proper public module.
|
|
67
|
-
- The GUI currently duplicates some of this logic (release scoring, format detection). After extraction, remove the duplicates.
|
|
68
|
-
- `pyproject.toml` needs to declare multiple packages, entry points for both CLI and GUI, and an optional `[gui]` dependency group.
|
|
69
|
-
- CI needs a new workflow to build GUI binaries on tagged releases.
|
|
70
|
-
- The standalone `riplex-app` repo should be archived with a pointer to the monorepo.
|
|
71
|
-
|
|
72
|
-
## Implementation Recommendations
|
|
73
|
-
|
|
74
|
-
These are suggested approaches, not prescriptive steps. Use judgment on the exact module boundaries and function signatures.
|
|
75
|
-
|
|
76
|
-
### Extracting shared logic
|
|
77
|
-
|
|
78
|
-
Create a module (e.g. `riplex.orchestrate` or similar) to house the pipeline functions currently private in `cli.py`. Candidates include:
|
|
79
|
-
- Disc format detection from resolution metadata
|
|
80
|
-
- Title inference from volume labels and MKV title tags
|
|
81
|
-
- Media type inference (movie vs TV)
|
|
82
|
-
- Release scoring by duration matching
|
|
83
|
-
- Disc number auto-detection
|
|
84
|
-
- Rip folder creation and manifest handling
|
|
85
|
-
|
|
86
|
-
### Splitting the CLI
|
|
87
|
-
|
|
88
|
-
Move argparse setup, command dispatch, terminal-specific formatting (progress bars, dry-run banners, rip guide printing), and the interactive setup wizard into `src/riplex_cli/`. The CLI imports from `riplex.*` for all logic.
|
|
89
|
-
|
|
90
|
-
### Bringing in the GUI
|
|
91
|
-
|
|
92
|
-
The GUI source at `riplex-app/src/riplex_app/` already imports from `riplex.*` so it should mostly work after copying. Replace any duplicated logic with imports from the new shared module.
|
|
93
|
-
|
|
94
|
-
### Suggested work order
|
|
95
|
-
|
|
96
|
-
1. Extract the shared module from `cli.py` first (independently testable, doesn't break anything)
|
|
97
|
-
2. Create `riplex_cli/` and move CLI wrapper code
|
|
98
|
-
3. Verify all tests pass and CLI behaves identically
|
|
99
|
-
4. Copy GUI source in, remove duplicates, update imports
|
|
100
|
-
5. Update `pyproject.toml`
|
|
101
|
-
6. Add GUI build workflow
|
|
102
|
-
7. Merge to main when stable
|
|
103
|
-
|
|
104
|
-
## Constraints
|
|
105
|
-
|
|
106
|
-
- Work on the `monorepo` branch until stable
|
|
107
|
-
- All existing tests must pass at every step
|
|
108
|
-
- The GUI does not need its own test suite yet (manual testing is fine)
|
|
109
|
-
- Do not change CLI behavior unless explicitly intended
|
|
110
|
-
|
|
111
|
-
## GUI Feedback (to address post-merge)
|
|
112
|
-
|
|
113
|
-
- Done screen: should auto-update when ripping finishes (currently requires a click)
|
|
114
|
-
- Done screen: add "Organize" button as a next step after ripping
|
|
115
|
-
- Multi-disc flow: prompt to insert next disc instead of showing the final "done" screen
|