riplex 0.3.6__tar.gz → 0.4.0__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 (116) hide show
  1. riplex-0.4.0/.github/copilot-instructions.md +69 -0
  2. {riplex-0.3.6 → riplex-0.4.0}/.github/workflows/release.yml +20 -8
  3. riplex-0.4.0/.vscode/settings.json +4 -0
  4. {riplex-0.3.6/src/riplex.egg-info → riplex-0.4.0}/PKG-INFO +2 -2
  5. {riplex-0.3.6 → riplex-0.4.0}/README.md +1 -1
  6. {riplex-0.3.6 → riplex-0.4.0}/docs/changelog.md +14 -0
  7. {riplex-0.3.6 → riplex-0.4.0}/docs/getting-started/installation.md +41 -10
  8. {riplex-0.3.6 → riplex-0.4.0}/pyproject.toml +6 -0
  9. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/disc/makemkv.py +6 -3
  10. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/scanner.py +30 -9
  11. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/splitter.py +8 -1
  12. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/tagger.py +2 -0
  13. {riplex-0.3.6 → riplex-0.4.0/src/riplex.egg-info}/PKG-INFO +2 -2
  14. {riplex-0.3.6 → riplex-0.4.0}/src/riplex.egg-info/SOURCES.txt +1 -0
  15. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_app/screens/folder_picker.py +8 -2
  16. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_app/screens/welcome.py +212 -8
  17. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_app/updater.py +6 -0
  18. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_cli/commands/setup.py +12 -6
  19. {riplex-0.3.6 → riplex-0.4.0}/tests/test_scanner.py +8 -4
  20. {riplex-0.3.6 → riplex-0.4.0}/tests/test_splitter.py +6 -3
  21. {riplex-0.3.6 → riplex-0.4.0}/tests/test_tagger.py +4 -2
  22. {riplex-0.3.6 → riplex-0.4.0}/tests/test_updater.py +21 -3
  23. riplex-0.3.6/.github/copilot-instructions.md +0 -38
  24. {riplex-0.3.6 → riplex-0.4.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  25. {riplex-0.3.6 → riplex-0.4.0}/.github/agents/riplex.agent.md +0 -0
  26. {riplex-0.3.6 → riplex-0.4.0}/.github/workflows/publish.yml +0 -0
  27. {riplex-0.3.6 → riplex-0.4.0}/.gitignore +0 -0
  28. {riplex-0.3.6 → riplex-0.4.0}/LICENSE +0 -0
  29. {riplex-0.3.6 → riplex-0.4.0}/REFACTOR_PLAN.md +0 -0
  30. {riplex-0.3.6 → riplex-0.4.0}/docs/architecture.md +0 -0
  31. {riplex-0.3.6 → riplex-0.4.0}/docs/getting-started/configuration.md +0 -0
  32. {riplex-0.3.6 → riplex-0.4.0}/docs/guide/lookup.md +0 -0
  33. {riplex-0.3.6 → riplex-0.4.0}/docs/guide/orchestrate.md +0 -0
  34. {riplex-0.3.6 → riplex-0.4.0}/docs/guide/organize.md +0 -0
  35. {riplex-0.3.6 → riplex-0.4.0}/docs/guide/workflow.md +0 -0
  36. {riplex-0.3.6 → riplex-0.4.0}/docs/index.md +0 -0
  37. {riplex-0.3.6 → riplex-0.4.0}/docs/naming-rules.md +0 -0
  38. {riplex-0.3.6 → riplex-0.4.0}/docs/reference/cli.md +0 -0
  39. {riplex-0.3.6 → riplex-0.4.0}/docs/troubleshooting.md +0 -0
  40. {riplex-0.3.6 → riplex-0.4.0}/issues/cross-disc-dvdcompare-matching.md +0 -0
  41. {riplex-0.3.6 → riplex-0.4.0}/issues/orchestrate-dvdcompare-fallback.md +0 -0
  42. {riplex-0.3.6 → riplex-0.4.0}/issues/planned-features.md +0 -0
  43. {riplex-0.3.6 → riplex-0.4.0}/mkdocs.yml +0 -0
  44. {riplex-0.3.6 → riplex-0.4.0}/setup.cfg +0 -0
  45. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/__init__.py +0 -0
  46. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/cache.py +0 -0
  47. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/config.py +0 -0
  48. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/dedup.py +0 -0
  49. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/detect.py +0 -0
  50. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/disc/__init__.py +0 -0
  51. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/disc/analysis.py +0 -0
  52. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/disc/provider.py +0 -0
  53. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/formatter.py +0 -0
  54. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/lookup.py +0 -0
  55. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/manifest.py +0 -0
  56. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/matcher.py +0 -0
  57. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/metadata/__init__.py +0 -0
  58. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/metadata/planner.py +0 -0
  59. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/metadata/provider.py +0 -0
  60. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/metadata/sources/__init__.py +0 -0
  61. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/metadata/sources/tmdb.py +0 -0
  62. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/models.py +0 -0
  63. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/normalize.py +0 -0
  64. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/organizer.py +0 -0
  65. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/snapshot.py +0 -0
  66. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/title.py +0 -0
  67. {riplex-0.3.6 → riplex-0.4.0}/src/riplex/ui.py +0 -0
  68. {riplex-0.3.6 → riplex-0.4.0}/src/riplex.egg-info/dependency_links.txt +0 -0
  69. {riplex-0.3.6 → riplex-0.4.0}/src/riplex.egg-info/entry_points.txt +0 -0
  70. {riplex-0.3.6 → riplex-0.4.0}/src/riplex.egg-info/requires.txt +0 -0
  71. {riplex-0.3.6 → riplex-0.4.0}/src/riplex.egg-info/top_level.txt +0 -0
  72. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_app/__init__.py +0 -0
  73. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_app/main.py +0 -0
  74. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_app/screens/__init__.py +0 -0
  75. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_app/screens/disc_detection.py +0 -0
  76. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_app/screens/done.py +0 -0
  77. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_app/screens/metadata.py +0 -0
  78. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_app/screens/organize_done.py +0 -0
  79. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_app/screens/organize_preview.py +0 -0
  80. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_app/screens/progress.py +0 -0
  81. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_app/screens/release.py +0 -0
  82. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_app/screens/selection.py +0 -0
  83. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_cli/__init__.py +0 -0
  84. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_cli/commands/__init__.py +0 -0
  85. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_cli/commands/lookup.py +0 -0
  86. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_cli/commands/orchestrate.py +0 -0
  87. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_cli/commands/organize.py +0 -0
  88. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_cli/commands/rip.py +0 -0
  89. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_cli/formatting.py +0 -0
  90. {riplex-0.3.6 → riplex-0.4.0}/src/riplex_cli/main.py +0 -0
  91. {riplex-0.3.6 → riplex-0.4.0}/tests/__init__.py +0 -0
  92. {riplex-0.3.6 → riplex-0.4.0}/tests/fixtures/makemkvcon_frozen_planet_ii_d2.txt +0 -0
  93. {riplex-0.3.6 → riplex-0.4.0}/tests/fixtures/makemkvcon_list.txt +0 -0
  94. {riplex-0.3.6 → riplex-0.4.0}/tests/snapshots/Batman Begins.snapshot.json +0 -0
  95. {riplex-0.3.6 → riplex-0.4.0}/tests/snapshots/Blade Runner (Blu-ray 4k).snapshot.json +0 -0
  96. {riplex-0.3.6 → riplex-0.4.0}/tests/snapshots/Blade Runner The Final Cut.snapshot.json +0 -0
  97. {riplex-0.3.6 → riplex-0.4.0}/tests/snapshots/Seven Worlds One Planet.snapshot.json +0 -0
  98. {riplex-0.3.6 → riplex-0.4.0}/tests/snapshots/The Dark Knight Rises.snapshot.json +0 -0
  99. {riplex-0.3.6 → riplex-0.4.0}/tests/snapshots/The Dark Knight.snapshot.json +0 -0
  100. {riplex-0.3.6 → riplex-0.4.0}/tests/snapshots/Waterworld.snapshot.json +0 -0
  101. {riplex-0.3.6 → riplex-0.4.0}/tests/test_cache.py +0 -0
  102. {riplex-0.3.6 → riplex-0.4.0}/tests/test_cli_utils.py +0 -0
  103. {riplex-0.3.6 → riplex-0.4.0}/tests/test_config.py +0 -0
  104. {riplex-0.3.6 → riplex-0.4.0}/tests/test_dedup.py +0 -0
  105. {riplex-0.3.6 → riplex-0.4.0}/tests/test_detect.py +0 -0
  106. {riplex-0.3.6 → riplex-0.4.0}/tests/test_disc_analysis.py +0 -0
  107. {riplex-0.3.6 → riplex-0.4.0}/tests/test_disc_provider.py +0 -0
  108. {riplex-0.3.6 → riplex-0.4.0}/tests/test_formatter.py +0 -0
  109. {riplex-0.3.6 → riplex-0.4.0}/tests/test_makemkv.py +0 -0
  110. {riplex-0.3.6 → riplex-0.4.0}/tests/test_matcher.py +0 -0
  111. {riplex-0.3.6 → riplex-0.4.0}/tests/test_normalize.py +0 -0
  112. {riplex-0.3.6 → riplex-0.4.0}/tests/test_organizer.py +0 -0
  113. {riplex-0.3.6 → riplex-0.4.0}/tests/test_planner.py +0 -0
  114. {riplex-0.3.6 → riplex-0.4.0}/tests/test_rip_guide.py +0 -0
  115. {riplex-0.3.6 → riplex-0.4.0}/tests/test_snapshot.py +0 -0
  116. {riplex-0.3.6 → riplex-0.4.0}/tests/test_ui.py +0 -0
@@ -0,0 +1,69 @@
1
+ # Copilot Instructions for riplex
2
+
3
+ ## Documentation changelog
4
+
5
+ When any file under `docs/` is added, modified, or removed, update `docs/changelog.md` with a dated entry describing the change. Follow the [Keep a Changelog](https://keepachangelog.com/) format with sections like Added, Changed, Removed, or Fixed under a date heading.
6
+
7
+ ## Installing from source
8
+
9
+ First-time setup — create a virtualenv and install in editable mode:
10
+ ```
11
+ python3.12 -m venv .venv
12
+ source .venv/bin/activate # macOS/Linux
13
+ .venv\Scripts\activate # Windows
14
+ pip install -e ".[dev,gui]"
15
+ ```
16
+
17
+ The `.vscode/settings.json` in this repo points VS Code at `.venv` automatically,
18
+ so the integrated terminal activates it on open. In any external terminal, run
19
+ `source .venv/bin/activate` first.
20
+
21
+ If you already have a venv active, just install:
22
+ ```
23
+ pip install -e ".[dev]"
24
+ ```
25
+
26
+ For the GUI, also include the gui extra:
27
+ ```
28
+ pip install -e ".[dev,gui]"
29
+ ```
30
+
31
+ ### macOS extras (Homebrew Python only)
32
+
33
+ If you installed Python via Homebrew, two additional one-time steps are needed:
34
+
35
+ **1. SSL certificates** — Flet downloads its desktop runtime on first launch and will
36
+ fail with an SSL error without this fix:
37
+ ```
38
+ CERT=$(python3.12 -c "import certifi; print(certifi.where())")
39
+ echo "export SSL_CERT_FILE=\"$CERT\"" >> .venv/bin/activate
40
+ echo "export REQUESTS_CA_BUNDLE=\"$CERT\"" >> .venv/bin/activate
41
+ source .venv/bin/activate
42
+ ```
43
+
44
+ **2. Folder picker (tkinter)** — the browse buttons in the GUI require tkinter,
45
+ which Homebrew ships as a separate package:
46
+ ```
47
+ brew install python-tk@3.12
48
+ ```
49
+ Without this, the browse buttons show a hint telling the user to type the path
50
+ manually instead of crashing silently.
51
+
52
+ ## Running
53
+
54
+ After installing from source, use the installed entry points:
55
+ ```
56
+ riplex rip # CLI dry-run
57
+ riplex rip --execute # CLI actual rip
58
+ riplex-ui # Launch the Flet GUI
59
+ ```
60
+
61
+ Do NOT use `python -m riplex` (that errors — riplex is a library package, not runnable). Do NOT use `python -m riplex_cli.main` when the entry point works.
62
+
63
+ ## Dry-run default
64
+
65
+ All destructive commands (`rip`, `organize`, `orchestrate`) are dry-run by default. There is no `--dry-run` flag. Use `--execute` to actually perform the operation.
66
+
67
+ ## Testing
68
+
69
+ Run tests with `pytest` (or `python -m pytest`) from the project root with the venv active. All tests must pass before committing.
@@ -69,7 +69,15 @@ jobs:
69
69
  path: release/
70
70
 
71
71
  build-macos:
72
- runs-on: macos-latest
72
+ strategy:
73
+ fail-fast: false
74
+ matrix:
75
+ include:
76
+ - runner: macos-13
77
+ arch: x86_64
78
+ - runner: macos-14
79
+ arch: arm64
80
+ runs-on: ${{ matrix.runner }}
73
81
  steps:
74
82
  - uses: actions/checkout@v4
75
83
  with:
@@ -116,12 +124,12 @@ jobs:
116
124
  - name: Package macOS artifacts
117
125
  run: |
118
126
  mkdir -p release
119
- cp dist/riplex release/
120
- cd dist && zip -r ../release/riplex-ui-macos.zip riplex-ui.app
127
+ cp dist/riplex release/riplex-${{ matrix.arch }}
128
+ cd dist && zip -r ../release/riplex-ui-macos-${{ matrix.arch }}.zip riplex-ui.app
121
129
 
122
130
  - uses: actions/upload-artifact@v4
123
131
  with:
124
- name: riplex-macos
132
+ name: riplex-macos-${{ matrix.arch }}
125
133
  path: release/
126
134
 
127
135
  release:
@@ -138,8 +146,10 @@ jobs:
138
146
  mkdir release
139
147
  cp artifacts/riplex-windows/riplex.exe release/riplex-windows.exe
140
148
  cp artifacts/riplex-windows/riplex-ui.exe release/riplex-ui-windows.exe
141
- cp artifacts/riplex-macos/riplex release/riplex-macos
142
- cp artifacts/riplex-macos/riplex-ui-macos.zip release/
149
+ cp artifacts/riplex-macos-x86_64/riplex-x86_64 release/riplex-macos-x86_64
150
+ cp artifacts/riplex-macos-x86_64/riplex-ui-macos-x86_64.zip release/
151
+ cp artifacts/riplex-macos-arm64/riplex-arm64 release/riplex-macos-arm64
152
+ cp artifacts/riplex-macos-arm64/riplex-ui-macos-arm64.zip release/
143
153
 
144
154
  - name: Create GitHub Release
145
155
  uses: softprops/action-gh-release@v2
@@ -148,5 +158,7 @@ jobs:
148
158
  files: |
149
159
  release/riplex-windows.exe
150
160
  release/riplex-ui-windows.exe
151
- release/riplex-macos
152
- release/riplex-ui-macos.zip
161
+ release/riplex-macos-x86_64
162
+ release/riplex-ui-macos-x86_64.zip
163
+ release/riplex-macos-arm64
164
+ release/riplex-ui-macos-arm64.zip
@@ -0,0 +1,4 @@
1
+ {
2
+ "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
3
+ "python.terminal.activateEnvironment": true
4
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: riplex
3
- Version: 0.3.6
3
+ Version: 0.4.0
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
@@ -26,7 +26,7 @@ Automates the tedious manual work around MakeMKV: figuring out what to rip, whic
26
26
  If you'd rather use a simple graphical interface instead of the command line, download the pre-built app from the [Releases page](https://github.com/AnyCredit5518/riplex/releases/latest):
27
27
 
28
28
  - **Windows**: Download `riplex-ui-windows.exe` and double-click to run
29
- - **macOS**: Download `riplex-ui-macos.zip`, unzip, and open `riplex-ui.app`
29
+ - **macOS**: Download `riplex-ui-macos-arm64.zip` (Apple Silicon) or `riplex-ui-macos-x86_64.zip` (Intel), unzip, and open `riplex-ui.app`
30
30
 
31
31
  No Python install required. The app walks you through setup and provides buttons for all workflows.
32
32
 
@@ -7,7 +7,7 @@ Automates the tedious manual work around MakeMKV: figuring out what to rip, whic
7
7
  If you'd rather use a simple graphical interface instead of the command line, download the pre-built app from the [Releases page](https://github.com/AnyCredit5518/riplex/releases/latest):
8
8
 
9
9
  - **Windows**: Download `riplex-ui-windows.exe` and double-click to run
10
- - **macOS**: Download `riplex-ui-macos.zip`, unzip, and open `riplex-ui.app`
10
+ - **macOS**: Download `riplex-ui-macos-arm64.zip` (Apple Silicon) or `riplex-ui-macos-x86_64.zip` (Intel), unzip, and open `riplex-ui.app`
11
11
 
12
12
  No Python install required. The app walks you through setup and provides buttons for all workflows.
13
13
 
@@ -6,9 +6,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
6
6
 
7
7
  ## 2026-05-03
8
8
 
9
+ ### Changed
10
+
11
+ - Installation guide: macOS pre-built executables now ship as separate `arm64` (Apple Silicon) and `x86_64` (Intel) builds; added instructions to pick the right one and remove the Gatekeeper quarantine flag.
12
+ - Installation guide: "Installing from source" section now includes venv setup steps and a macOS SSL fix for Homebrew Python users (`SSL_CERT_FILE` via certifi).
13
+ - Installation guide: added macOS tkinter section for folder picker support.
14
+
9
15
  ### Added
10
16
 
11
17
  - 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
18
+ - `find_ffprobe()` helper: all ffprobe consumers now check `~/.riplex/bin/`, `/usr/local/bin/`, and `/opt/homebrew/bin/` in addition to PATH.
19
+ - macOS auto-download: "Install Missing Tools" on macOS < 14 auto-downloads ffprobe from evermeet.cx to `~/.riplex/bin/`; opens download pages for MakeMKV and MKVToolNix.
20
+ - macOS .app bundle detection: `find_makemkvcon()` checks `/Applications/MakeMKV.app/`; `find_mkvmerge()` and `find_mkvpropedit()` check `/Applications/MKVToolNix.app/`.
21
+ - Dual-arch macOS CI builds (`macos-13`/x86_64 and `macos-14`/arm64) in release workflow.
22
+ - Arch-aware macOS update checker in GUI updater.
23
+ - Install progress bar and streaming output for Homebrew installs on macOS 14+.
24
+ - Graceful tkinter fallback in folder picker and welcome screen browse buttons.
25
+ - Linux apt support in GUI tool installer.
12
26
 
13
27
  ## 2026-05-02
14
28
 
@@ -14,7 +14,8 @@ Download the latest release for your platform from the [GitHub Releases page](ht
14
14
  | Platform | CLI | GUI |
15
15
  |---|---|---|
16
16
  | Windows | `riplex-windows.exe` | `riplex-ui-windows.exe` |
17
- | macOS | `riplex-macos` | `riplex-ui-macos.zip` |
17
+ | macOS (Apple Silicon) | `riplex-macos-arm64` | `riplex-ui-macos-arm64.zip` |
18
+ | macOS (Intel) | `riplex-macos-x86_64` | `riplex-ui-macos-x86_64.zip` |
18
19
 
19
20
  ### Windows
20
21
 
@@ -24,10 +25,18 @@ Download the latest release for your platform from the [GitHub Releases page](ht
24
25
 
25
26
  ### macOS
26
27
 
27
- 1. Download `riplex-macos` and/or `riplex-ui-macos.zip`
28
- 2. Make the CLI executable: `chmod +x riplex-macos`
29
- 3. For the GUI, unzip `riplex-ui-macos.zip` and move `riplex-ui.app` to `/Applications/`
30
- 4. Run `./riplex-macos setup` to configure
28
+ 1. Pick the build matching your Mac:
29
+ - Apple Silicon (M1/M2/M3/...): `riplex-macos-arm64` and `riplex-ui-macos-arm64.zip`
30
+ - Intel: `riplex-macos-x86_64` and `riplex-ui-macos-x86_64.zip`
31
+
32
+ Not sure which you have? Run `sysctl -n machdep.cpu.brand_string` in Terminal.
33
+ "Apple ..." means Apple Silicon; "Intel ..." means Intel.
34
+ 2. Make the CLI executable: `chmod +x riplex-macos-*`
35
+ 3. For the GUI, unzip the `.zip` and move `riplex-ui.app` to `/Applications/`.
36
+ The first time you launch it, macOS may block the unsigned app — remove the
37
+ quarantine flag with `xattr -dr com.apple.quarantine /Applications/riplex-ui.app`
38
+ and try again.
39
+ 4. Run `./riplex-macos-arm64 setup` (or the `x86_64` variant) to configure.
31
40
 
32
41
  ## Option B: Install via pip
33
42
 
@@ -103,21 +112,43 @@ If you want to contribute or run the latest unreleased code:
103
112
  ```bash
104
113
  git clone https://github.com/AnyCredit5518/riplex.git
105
114
  cd riplex
106
- pip install -e ".[dev]"
115
+ python3.12 -m venv .venv
116
+ source .venv/bin/activate # macOS/Linux
117
+ pip install -e ".[dev,gui]"
107
118
  ```
108
119
 
109
- To also install the Flet-based GUI:
120
+ The repo's `.vscode/settings.json` points VS Code at `.venv` automatically, so
121
+ the integrated terminal activates it on open. In any external terminal, run
122
+ `source .venv/bin/activate` first.
123
+
124
+ Then launch the GUI with:
110
125
 
111
126
  ```bash
112
- pip install -e ".[dev,gui]"
127
+ riplex-ui
113
128
  ```
114
129
 
115
- Then launch it with:
130
+ ### macOS SSL fix (Homebrew Python only)
131
+
132
+ If you installed Python via Homebrew and `riplex-ui` crashes on first launch with
133
+ an SSL certificate error, run this one-time fix:
116
134
 
117
135
  ```bash
118
- riplex-ui
136
+ CERT=$(python3.12 -c "import certifi; print(certifi.where())")
137
+ echo "export SSL_CERT_FILE=\"$CERT\"" >> .venv/bin/activate
138
+ echo "export REQUESTS_CA_BUNDLE=\"$CERT\"" >> .venv/bin/activate
139
+ source .venv/bin/activate
119
140
  ```
120
141
 
142
+ ### macOS folder picker (Homebrew Python only)
143
+
144
+ The browse buttons in the GUI use tkinter, which Homebrew ships separately:
145
+
146
+ ```bash
147
+ brew install python-tk@3.12
148
+ ```
149
+
150
+ Without this, clicking a browse button shows a hint to type the path manually instead.
151
+
121
152
  ## External tools
122
153
 
123
154
  riplex uses these tools under the hood. The setup wizard handles installation, but if you prefer to install manually:
@@ -23,6 +23,12 @@ dev = [
23
23
  ]
24
24
  gui = [
25
25
  "flet>=0.84",
26
+ # tkinter is also required for the folder picker buttons but cannot be
27
+ # installed via pip — it is part of Python's standard library and must be
28
+ # installed at the OS level:
29
+ # macOS (Homebrew Python): brew install python-tk@3.12
30
+ # Linux (Debian/Ubuntu): sudo apt install python3-tk
31
+ # Windows: included in the standard python.org installer
26
32
  ]
27
33
 
28
34
  [project.scripts]
@@ -19,8 +19,11 @@ _SUBPROCESS_FLAGS: dict = (
19
19
 
20
20
  log = logging.getLogger(__name__)
21
21
 
22
- # Default search paths for makemkvcon on Windows
22
+ # Common install paths where makemkvcon lives outside of PATH
23
23
  _MAKEMKVCON_SEARCH_PATHS = [
24
+ # macOS .app bundle
25
+ Path("/Applications/MakeMKV.app/Contents/MacOS/makemkvcon"),
26
+ # Windows
24
27
  Path(r"C:\Program Files\MakeMKV\makemkvcon64.exe"),
25
28
  Path(r"C:\Program Files (x86)\MakeMKV\makemkvcon64.exe"),
26
29
  Path(r"C:\Program Files\MakeMKV\makemkvcon.exe"),
@@ -649,9 +652,9 @@ def probe_chapter_durations(mkv_path: str | Path) -> list[int]:
649
652
  Returns a list of chapter durations in seconds. Returns empty list
650
653
  if ffprobe is unavailable or the file has no chapters.
651
654
  """
652
- import shutil
655
+ from riplex.scanner import find_ffprobe
653
656
 
654
- ffprobe = shutil.which("ffprobe")
657
+ ffprobe = find_ffprobe()
655
658
  if not ffprobe:
656
659
  log.debug("ffprobe not found, skipping chapter duration extraction")
657
660
  return []
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import json
6
6
  import logging
7
7
  import platform
8
+ import shutil
8
9
  import subprocess
9
10
  from pathlib import Path
10
11
  from typing import Callable
@@ -21,6 +22,28 @@ _SUBPROCESS_FLAGS: dict = (
21
22
  else {}
22
23
  )
23
24
 
25
+ # Common install locations for ffprobe outside of PATH
26
+ _FFPROBE_SEARCH_PATHS = [
27
+ Path.home() / ".riplex" / "bin" / "ffprobe",
28
+ Path("/usr/local/bin/ffprobe"),
29
+ Path("/opt/homebrew/bin/ffprobe"),
30
+ ]
31
+
32
+
33
+ def find_ffprobe() -> str | None:
34
+ """Locate the ffprobe executable.
35
+
36
+ Checks PATH first, then ``~/.riplex/bin/``, ``/usr/local/bin/``,
37
+ and ``/opt/homebrew/bin/``. Returns the path string or *None*.
38
+ """
39
+ path = shutil.which("ffprobe")
40
+ if path:
41
+ return path
42
+ for candidate in _FFPROBE_SEARCH_PATHS:
43
+ if candidate.is_file():
44
+ return str(candidate)
45
+ return None
46
+
24
47
 
25
48
  def _probe_file(path: Path) -> ScannedFile:
26
49
  """Extract metadata from a single MKV file using ffprobe.
@@ -37,10 +60,14 @@ def _probe_file(path: Path) -> ScannedFile:
37
60
  except OSError:
38
61
  size_bytes = 0
39
62
 
63
+ ffprobe = find_ffprobe()
64
+ if not ffprobe:
65
+ return ScannedFile(name=name, path=abs_path, size_bytes=size_bytes)
66
+
40
67
  try:
41
68
  result = subprocess.run(
42
69
  [
43
- "ffprobe",
70
+ ffprobe,
44
71
  "-v", "quiet",
45
72
  "-print_format", "json",
46
73
  "-show_entries",
@@ -163,14 +190,8 @@ def scan_folder(
163
190
  raise FileNotFoundError(f"Not a directory: {folder}")
164
191
 
165
192
  # Check for ffprobe availability
166
- try:
167
- subprocess.run(
168
- ["ffprobe", "-version"],
169
- capture_output=True,
170
- timeout=5,
171
- **_SUBPROCESS_FLAGS,
172
- )
173
- except FileNotFoundError:
193
+ ffprobe = find_ffprobe()
194
+ if not ffprobe:
174
195
  raise RuntimeError(
175
196
  "ffprobe not found. Install FFmpeg to enable MKV duration scanning."
176
197
  )
@@ -41,6 +41,7 @@ def find_mkvmerge() -> str | None:
41
41
  if path:
42
42
  return path
43
43
  for candidate in [
44
+ "/Applications/MKVToolNix.app/Contents/MacOS/mkvmerge",
44
45
  r"C:\Program Files\MKVToolNix\mkvmerge.exe",
45
46
  r"C:\Program Files (x86)\MKVToolNix\mkvmerge.exe",
46
47
  ]:
@@ -51,9 +52,15 @@ def find_mkvmerge() -> str | None:
51
52
 
52
53
  def get_chapters(file_path: str) -> list[Chapter]:
53
54
  """Extract chapter information from an MKV file using ffprobe."""
55
+ from riplex.scanner import find_ffprobe
56
+
57
+ ffprobe = find_ffprobe()
58
+ if not ffprobe:
59
+ return []
60
+
54
61
  result = subprocess.run(
55
62
  [
56
- "ffprobe", "-v", "quiet",
63
+ ffprobe, "-v", "quiet",
57
64
  "-print_format", "json",
58
65
  "-show_chapters",
59
66
  file_path,
@@ -36,6 +36,7 @@ def find_mkvpropedit() -> str | None:
36
36
  if path:
37
37
  return path
38
38
  for candidate in [
39
+ "/Applications/MKVToolNix.app/Contents/MacOS/mkvpropedit",
39
40
  r"C:\Program Files\MKVToolNix\mkvpropedit.exe",
40
41
  r"C:\Program Files (x86)\MKVToolNix\mkvpropedit.exe",
41
42
  ]:
@@ -113,6 +114,7 @@ def read_organized_tag(file_path: str) -> str | None:
113
114
  mkvmerge = shutil.which("mkvmerge")
114
115
  if mkvmerge is None:
115
116
  for candidate in [
117
+ "/Applications/MKVToolNix.app/Contents/MacOS/mkvmerge",
116
118
  r"C:\Program Files\MKVToolNix\mkvmerge.exe",
117
119
  r"C:\Program Files (x86)\MKVToolNix\mkvmerge.exe",
118
120
  ]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: riplex
3
- Version: 0.3.6
3
+ Version: 0.4.0
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
@@ -26,7 +26,7 @@ Automates the tedious manual work around MakeMKV: figuring out what to rip, whic
26
26
  If you'd rather use a simple graphical interface instead of the command line, download the pre-built app from the [Releases page](https://github.com/AnyCredit5518/riplex/releases/latest):
27
27
 
28
28
  - **Windows**: Download `riplex-ui-windows.exe` and double-click to run
29
- - **macOS**: Download `riplex-ui-macos.zip`, unzip, and open `riplex-ui.app`
29
+ - **macOS**: Download `riplex-ui-macos-arm64.zip` (Apple Silicon) or `riplex-ui-macos-x86_64.zip` (Intel), unzip, and open `riplex-ui.app`
30
30
 
31
31
  No Python install required. The app walks you through setup and provides buttons for all workflows.
32
32
 
@@ -9,6 +9,7 @@ pyproject.toml
9
9
  .github/agents/riplex.agent.md
10
10
  .github/workflows/publish.yml
11
11
  .github/workflows/release.yml
12
+ .vscode/settings.json
12
13
  docs/architecture.md
13
14
  docs/changelog.md
14
15
  docs/index.md
@@ -85,8 +85,14 @@ class FolderPickerScreen:
85
85
  log.debug("_browse clicked")
86
86
 
87
87
  def _pick():
88
- import tkinter as tk
89
- from tkinter import filedialog
88
+ try:
89
+ import tkinter as tk
90
+ from tkinter import filedialog
91
+ except ModuleNotFoundError:
92
+ log.warning("tkinter not available; user must type path manually")
93
+ self.folder_field.hint_text = "Type the path manually (brew install python-tk@3.12 to enable folder picker)"
94
+ self.app.page.update()
95
+ return
90
96
 
91
97
  try:
92
98
  root = tk.Tk()