riplex 0.3.3__tar.gz → 0.3.5__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.
Files changed (117) hide show
  1. riplex-0.3.5/LICENSE +21 -0
  2. {riplex-0.3.3/src/riplex.egg-info → riplex-0.3.5}/PKG-INFO +3 -1
  3. {riplex-0.3.3 → riplex-0.3.5}/docs/changelog.md +6 -0
  4. {riplex-0.3.3 → riplex-0.3.5}/docs/getting-started/installation.md +7 -0
  5. riplex-0.3.5/docs/troubleshooting.md +78 -0
  6. riplex-0.3.5/issues/orchestrate-dvdcompare-fallback.md +45 -0
  7. riplex-0.3.5/issues/planned-features.md +77 -0
  8. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/config.py +7 -1
  9. {riplex-0.3.3 → riplex-0.3.5/src/riplex.egg-info}/PKG-INFO +3 -1
  10. {riplex-0.3.3 → riplex-0.3.5}/src/riplex.egg-info/SOURCES.txt +4 -3
  11. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_app/screens/welcome.py +4 -1
  12. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_cli/commands/setup.py +9 -4
  13. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_cli/main.py +8 -2
  14. riplex-0.3.3/MONOREPO_PLAN.md +0 -115
  15. riplex-0.3.3/PLANNED_FEATURES.md +0 -416
  16. riplex-0.3.3/plex_naming_rules.md +0 -154
  17. {riplex-0.3.3 → riplex-0.3.5}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  18. {riplex-0.3.3 → riplex-0.3.5}/.github/agents/riplex.agent.md +0 -0
  19. {riplex-0.3.3 → riplex-0.3.5}/.github/copilot-instructions.md +0 -0
  20. {riplex-0.3.3 → riplex-0.3.5}/.github/workflows/publish.yml +0 -0
  21. {riplex-0.3.3 → riplex-0.3.5}/.github/workflows/release.yml +0 -0
  22. {riplex-0.3.3 → riplex-0.3.5}/.gitignore +0 -0
  23. {riplex-0.3.3 → riplex-0.3.5}/README.md +0 -0
  24. {riplex-0.3.3 → riplex-0.3.5}/REFACTOR_PLAN.md +0 -0
  25. {riplex-0.3.3 → riplex-0.3.5}/docs/architecture.md +0 -0
  26. {riplex-0.3.3 → riplex-0.3.5}/docs/getting-started/configuration.md +0 -0
  27. {riplex-0.3.3 → riplex-0.3.5}/docs/guide/lookup.md +0 -0
  28. {riplex-0.3.3 → riplex-0.3.5}/docs/guide/orchestrate.md +0 -0
  29. {riplex-0.3.3 → riplex-0.3.5}/docs/guide/organize.md +0 -0
  30. {riplex-0.3.3 → riplex-0.3.5}/docs/guide/workflow.md +0 -0
  31. {riplex-0.3.3 → riplex-0.3.5}/docs/index.md +0 -0
  32. {riplex-0.3.3 → riplex-0.3.5}/docs/naming-rules.md +0 -0
  33. {riplex-0.3.3 → riplex-0.3.5}/docs/reference/cli.md +0 -0
  34. {riplex-0.3.3 → riplex-0.3.5}/issues/cross-disc-dvdcompare-matching.md +0 -0
  35. {riplex-0.3.3 → riplex-0.3.5}/mkdocs.yml +0 -0
  36. {riplex-0.3.3 → riplex-0.3.5}/pyproject.toml +0 -0
  37. {riplex-0.3.3 → riplex-0.3.5}/setup.cfg +0 -0
  38. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/__init__.py +0 -0
  39. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/cache.py +0 -0
  40. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/dedup.py +0 -0
  41. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/detect.py +0 -0
  42. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/disc/__init__.py +0 -0
  43. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/disc/analysis.py +0 -0
  44. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/disc/makemkv.py +0 -0
  45. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/disc/provider.py +0 -0
  46. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/formatter.py +0 -0
  47. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/lookup.py +0 -0
  48. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/manifest.py +0 -0
  49. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/matcher.py +0 -0
  50. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/metadata/__init__.py +0 -0
  51. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/metadata/planner.py +0 -0
  52. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/metadata/provider.py +0 -0
  53. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/metadata/sources/__init__.py +0 -0
  54. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/metadata/sources/tmdb.py +0 -0
  55. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/models.py +0 -0
  56. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/normalize.py +0 -0
  57. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/organizer.py +0 -0
  58. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/scanner.py +0 -0
  59. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/snapshot.py +0 -0
  60. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/splitter.py +0 -0
  61. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/tagger.py +0 -0
  62. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/title.py +0 -0
  63. {riplex-0.3.3 → riplex-0.3.5}/src/riplex/ui.py +0 -0
  64. {riplex-0.3.3 → riplex-0.3.5}/src/riplex.egg-info/dependency_links.txt +0 -0
  65. {riplex-0.3.3 → riplex-0.3.5}/src/riplex.egg-info/entry_points.txt +0 -0
  66. {riplex-0.3.3 → riplex-0.3.5}/src/riplex.egg-info/requires.txt +0 -0
  67. {riplex-0.3.3 → riplex-0.3.5}/src/riplex.egg-info/top_level.txt +0 -0
  68. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_app/__init__.py +0 -0
  69. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_app/main.py +0 -0
  70. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_app/screens/__init__.py +0 -0
  71. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_app/screens/disc_detection.py +0 -0
  72. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_app/screens/done.py +0 -0
  73. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_app/screens/folder_picker.py +0 -0
  74. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_app/screens/metadata.py +0 -0
  75. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_app/screens/organize_done.py +0 -0
  76. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_app/screens/organize_preview.py +0 -0
  77. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_app/screens/progress.py +0 -0
  78. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_app/screens/release.py +0 -0
  79. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_app/screens/selection.py +0 -0
  80. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_app/updater.py +0 -0
  81. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_cli/__init__.py +0 -0
  82. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_cli/commands/__init__.py +0 -0
  83. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_cli/commands/lookup.py +0 -0
  84. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_cli/commands/orchestrate.py +0 -0
  85. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_cli/commands/organize.py +0 -0
  86. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_cli/commands/rip.py +0 -0
  87. {riplex-0.3.3 → riplex-0.3.5}/src/riplex_cli/formatting.py +0 -0
  88. {riplex-0.3.3 → riplex-0.3.5}/tests/__init__.py +0 -0
  89. {riplex-0.3.3 → riplex-0.3.5}/tests/fixtures/makemkvcon_frozen_planet_ii_d2.txt +0 -0
  90. {riplex-0.3.3 → riplex-0.3.5}/tests/fixtures/makemkvcon_list.txt +0 -0
  91. {riplex-0.3.3 → riplex-0.3.5}/tests/snapshots/Batman Begins.snapshot.json +0 -0
  92. {riplex-0.3.3 → riplex-0.3.5}/tests/snapshots/Blade Runner (Blu-ray 4k).snapshot.json +0 -0
  93. {riplex-0.3.3 → riplex-0.3.5}/tests/snapshots/Blade Runner The Final Cut.snapshot.json +0 -0
  94. {riplex-0.3.3 → riplex-0.3.5}/tests/snapshots/Seven Worlds One Planet.snapshot.json +0 -0
  95. {riplex-0.3.3 → riplex-0.3.5}/tests/snapshots/The Dark Knight Rises.snapshot.json +0 -0
  96. {riplex-0.3.3 → riplex-0.3.5}/tests/snapshots/The Dark Knight.snapshot.json +0 -0
  97. {riplex-0.3.3 → riplex-0.3.5}/tests/snapshots/Waterworld.snapshot.json +0 -0
  98. {riplex-0.3.3 → riplex-0.3.5}/tests/test_cache.py +0 -0
  99. {riplex-0.3.3 → riplex-0.3.5}/tests/test_cli_utils.py +0 -0
  100. {riplex-0.3.3 → riplex-0.3.5}/tests/test_config.py +0 -0
  101. {riplex-0.3.3 → riplex-0.3.5}/tests/test_dedup.py +0 -0
  102. {riplex-0.3.3 → riplex-0.3.5}/tests/test_detect.py +0 -0
  103. {riplex-0.3.3 → riplex-0.3.5}/tests/test_disc_analysis.py +0 -0
  104. {riplex-0.3.3 → riplex-0.3.5}/tests/test_disc_provider.py +0 -0
  105. {riplex-0.3.3 → riplex-0.3.5}/tests/test_formatter.py +0 -0
  106. {riplex-0.3.3 → riplex-0.3.5}/tests/test_makemkv.py +0 -0
  107. {riplex-0.3.3 → riplex-0.3.5}/tests/test_matcher.py +0 -0
  108. {riplex-0.3.3 → riplex-0.3.5}/tests/test_normalize.py +0 -0
  109. {riplex-0.3.3 → riplex-0.3.5}/tests/test_organizer.py +0 -0
  110. {riplex-0.3.3 → riplex-0.3.5}/tests/test_planner.py +0 -0
  111. {riplex-0.3.3 → riplex-0.3.5}/tests/test_rip_guide.py +0 -0
  112. {riplex-0.3.3 → riplex-0.3.5}/tests/test_scanner.py +0 -0
  113. {riplex-0.3.3 → riplex-0.3.5}/tests/test_snapshot.py +0 -0
  114. {riplex-0.3.3 → riplex-0.3.5}/tests/test_splitter.py +0 -0
  115. {riplex-0.3.3 → riplex-0.3.5}/tests/test_tagger.py +0 -0
  116. {riplex-0.3.3 → riplex-0.3.5}/tests/test_ui.py +0 -0
  117. {riplex-0.3.3 → riplex-0.3.5}/tests/test_updater.py +0 -0
riplex-0.3.5/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
3
+ Version: 0.3.5
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
 
@@ -4,6 +4,12 @@ All notable changes to the riplex documentation are recorded here.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/).
6
6
 
7
+ ## 2026-05-03
8
+
9
+ ### Added
10
+
11
+ - New troubleshooting guide (`docs/troubleshooting.md`) covering: makemkvcon not on PATH (Flatpak issue), drive not detected, invalid config file, TMDb API key signup, and dvdcompare lookup failures
12
+
7
13
  ## 2026-05-02
8
14
 
9
15
  ### Changed
@@ -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,78 @@
1
+ # Troubleshooting
2
+
3
+ Common issues and solutions reported by users.
4
+
5
+ ---
6
+
7
+ ## "makemkvcon not on PATH"
8
+
9
+ **Symptom:** `riplex setup` warns that `makemkvcon` is not found, even though MakeMKV is installed.
10
+
11
+ **Cause:** On Linux, installing MakeMKV via Flatpak does not include `makemkvcon` (the command-line tool). The Flatpak only packages the GUI.
12
+
13
+ **Solution:** Install MakeMKV from source using the instructions on the [MakeMKV website](https://www.makemkv.com/forum/viewtopic.php?f=3&t=224). This installs both the GUI and `makemkvcon`.
14
+
15
+ ---
16
+
17
+ ## "Error reading disc" / drive not detected
18
+
19
+ **Symptom:** `riplex orchestrate` or `riplex rip` can't find your optical drive, or reports "no disc found in any drive."
20
+
21
+ **Possible causes:**
22
+
23
+ 1. **MakeMKV itself can't see the drive.** Run `makemkvcon info disc:-1` to check. If MakeMKV doesn't list your drive, the problem is at the OS/driver level, not riplex.
24
+
25
+ 2. **MakeMKV not installed correctly (Linux).** If you see an error like `libmakemkv.so.1: cannot open shared object file`, MakeMKV's libraries aren't linked properly. Reinstalling MakeMKV from source usually fixes this.
26
+
27
+ 3. **External drive not recognized.** Try specifying the drive manually:
28
+ ```
29
+ riplex orchestrate --drive /dev/sr0 # Linux
30
+ riplex orchestrate --drive D: # Windows
31
+ riplex rip --drive 1 # by index
32
+ ```
33
+
34
+ **Solution:** Verify MakeMKV works first (open the GUI or run `makemkvcon info disc:-1`). If it doesn't see the drive, reinstall MakeMKV. If MakeMKV works but riplex doesn't, use the `--drive` flag to specify the drive manually.
35
+
36
+ ---
37
+
38
+ ## Invalid config file (TOML parse error)
39
+
40
+ **Symptom:** Running any riplex command crashes with a `TOMLDecodeError` traceback mentioning your config file.
41
+
42
+ **Cause:** The config file has a syntax error, possibly from hand-editing or a corrupted write.
43
+
44
+ **Solution:** Re-run setup with the `--force` flag to delete the bad config and start fresh:
45
+
46
+ ```
47
+ riplex setup --force
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Getting a TMDb API key
53
+
54
+ **Symptom:** You're not sure what to enter on the TMDb API key request form because you're not a business or app developer.
55
+
56
+ **Solution:** TMDb asks for an app name and URL when you request a key. You can enter "riplex" as the app name and `https://github.com/AnyCredit5518/riplex` as the URL. The rest of the form can be filled with basic info - it doesn't need to be a real business. The key is approved instantly.
57
+
58
+ Sign up and request a key at: https://www.themoviedb.org/settings/api
59
+
60
+ ---
61
+
62
+ ## "dvdcompare lookup failed" / disc not on dvdcompare
63
+
64
+ **Symptom:** `riplex orchestrate` or `riplex organize` exits with an error about dvdcompare failing to find your disc.
65
+
66
+ **Cause:** Not every disc release is listed on dvdcompare.net. Niche or region-specific releases may not have entries.
67
+
68
+ **Workaround:** Use `riplex rip` instead, which handles missing dvdcompare data gracefully by falling back to TMDb runtime matching:
69
+
70
+ ```
71
+ riplex rip "Movie Title"
72
+ riplex rip "Movie Title" --execute
73
+ ```
74
+
75
+ You'll still get duplicate filtering, 4K preference, play-all detection, and TMDb runtime matching. The only thing you lose is automatic title matching and naming, so you'll need to rename and move the file into your Plex library manually after ripping.
76
+
77
+ > [!NOTE]
78
+ > A fix to make `orchestrate` and `organize` handle missing dvdcompare data gracefully is planned.
@@ -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
- return tomllib.load(f)
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
3
+ Version: 0.3.5
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
- MONOREPO_PLAN.md
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
@@ -15,6 +13,7 @@ docs/architecture.md
15
13
  docs/changelog.md
16
14
  docs/index.md
17
15
  docs/naming-rules.md
16
+ docs/troubleshooting.md
18
17
  docs/getting-started/configuration.md
19
18
  docs/getting-started/installation.md
20
19
  docs/guide/lookup.md
@@ -23,6 +22,8 @@ docs/guide/organize.md
23
22
  docs/guide/workflow.md
24
23
  docs/reference/cli.md
25
24
  issues/cross-disc-dvdcompare-matching.md
25
+ issues/orchestrate-dvdcompare-fallback.md
26
+ issues/planned-features.md
26
27
  src/riplex/__init__.py
27
28
  src/riplex/cache.py
28
29
  src/riplex/config.py
@@ -346,8 +346,11 @@ class WelcomeScreen:
346
346
  self.app.page.update()
347
347
  return
348
348
 
349
- self._install_status.value = "Done! Restart the app for changes to take effect."
349
+ self._install_status.value = "Done! Reloading..."
350
350
  self.app.page.update()
351
+ import time
352
+ time.sleep(1.5)
353
+ self.app.navigate("welcome")
351
354
  except Exception as exc:
352
355
  self._install_status.value = f"Install failed: {exc}. Try the manual links above."
353
356
  self.app.page.update()
@@ -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
- existing: dict[str, str] = load_config()
66
- if existing:
67
- print("(Existing config found. Press Enter to keep current values.)\n")
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
 
@@ -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