riplex 0.9.2__tar.gz → 0.9.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.
Files changed (143) hide show
  1. {riplex-0.9.2 → riplex-0.9.4}/.github/workflows/release.yml +19 -7
  2. {riplex-0.9.2/src/riplex.egg-info → riplex-0.9.4}/PKG-INFO +2 -2
  3. {riplex-0.9.2 → riplex-0.9.4}/pyproject.toml +11 -5
  4. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/scanner.py +31 -2
  5. {riplex-0.9.2 → riplex-0.9.4/src/riplex.egg-info}/PKG-INFO +2 -2
  6. {riplex-0.9.2 → riplex-0.9.4}/src/riplex.egg-info/requires.txt +1 -1
  7. riplex-0.9.4/src/riplex.egg-info/scm_version.json +8 -0
  8. {riplex-0.9.2 → riplex-0.9.4}/tests/test_scanner.py +60 -1
  9. riplex-0.9.2/src/riplex.egg-info/scm_version.json +0 -8
  10. {riplex-0.9.2 → riplex-0.9.4}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  11. {riplex-0.9.2 → riplex-0.9.4}/.github/ISSUE_TEMPLATE/crash_report.yml +0 -0
  12. {riplex-0.9.2 → riplex-0.9.4}/.github/agents/riplex.agent.md +0 -0
  13. {riplex-0.9.2 → riplex-0.9.4}/.github/copilot-instructions.md +0 -0
  14. {riplex-0.9.2 → riplex-0.9.4}/.github/workflows/publish.yml +0 -0
  15. {riplex-0.9.2 → riplex-0.9.4}/.gitignore +0 -0
  16. {riplex-0.9.2 → riplex-0.9.4}/.vscode/settings.json +0 -0
  17. {riplex-0.9.2 → riplex-0.9.4}/CONTRIBUTORS.md +0 -0
  18. {riplex-0.9.2 → riplex-0.9.4}/LICENSE +0 -0
  19. {riplex-0.9.2 → riplex-0.9.4}/README.md +0 -0
  20. {riplex-0.9.2 → riplex-0.9.4}/docs/architecture.md +0 -0
  21. {riplex-0.9.2 → riplex-0.9.4}/docs/changelog.md +0 -0
  22. {riplex-0.9.2 → riplex-0.9.4}/docs/cli-guide/lookup.md +0 -0
  23. {riplex-0.9.2 → riplex-0.9.4}/docs/cli-guide/orchestrate.md +0 -0
  24. {riplex-0.9.2 → riplex-0.9.4}/docs/cli-guide/organize.md +0 -0
  25. {riplex-0.9.2 → riplex-0.9.4}/docs/cli-guide/workflow.md +0 -0
  26. {riplex-0.9.2 → riplex-0.9.4}/docs/getting-started/configuration.md +0 -0
  27. {riplex-0.9.2 → riplex-0.9.4}/docs/getting-started/installation.md +0 -0
  28. {riplex-0.9.2 → riplex-0.9.4}/docs/gui-guide/gui-walkthrough.md +0 -0
  29. {riplex-0.9.2 → riplex-0.9.4}/docs/index.md +0 -0
  30. {riplex-0.9.2 → riplex-0.9.4}/docs/naming-rules.md +0 -0
  31. {riplex-0.9.2 → riplex-0.9.4}/docs/reference/cli.md +0 -0
  32. {riplex-0.9.2 → riplex-0.9.4}/docs/troubleshooting.md +0 -0
  33. {riplex-0.9.2 → riplex-0.9.4}/issues/debug-artifacts-consolidation.md +0 -0
  34. {riplex-0.9.2 → riplex-0.9.4}/issues/manual-rip-and-organize-escape-hatch.md +0 -0
  35. {riplex-0.9.2 → riplex-0.9.4}/issues/orchestrate-dvdcompare-fallback.md +0 -0
  36. {riplex-0.9.2 → riplex-0.9.4}/issues/planned-features.md +0 -0
  37. {riplex-0.9.2 → riplex-0.9.4}/mkdocs.yml +0 -0
  38. {riplex-0.9.2 → riplex-0.9.4}/screenshots/0_Rip_Flow_BTTF.gif +0 -0
  39. {riplex-0.9.2 → riplex-0.9.4}/screenshots/1_Welcome_Screen.png +0 -0
  40. {riplex-0.9.2 → riplex-0.9.4}/screenshots/2_Disc_Detection_BTTF.png +0 -0
  41. {riplex-0.9.2 → riplex-0.9.4}/screenshots/3_Metadata_Lookup_BTTF.png +0 -0
  42. {riplex-0.9.2 → riplex-0.9.4}/screenshots/4_Disc_Release_BTTF.png +0 -0
  43. {riplex-0.9.2 → riplex-0.9.4}/screenshots/5_Multi_Disc_Overview_BTTF.png +0 -0
  44. {riplex-0.9.2 → riplex-0.9.4}/screenshots/5_Select_Title_to_RIP_BTTF.png +0 -0
  45. {riplex-0.9.2 → riplex-0.9.4}/setup.cfg +0 -0
  46. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/__init__.py +0 -0
  47. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/cache.py +0 -0
  48. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/config.py +0 -0
  49. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/dedup.py +0 -0
  50. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/detect.py +0 -0
  51. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/disc/__init__.py +0 -0
  52. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/disc/analysis.py +0 -0
  53. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/disc/makemkv.py +0 -0
  54. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/disc/provider.py +0 -0
  55. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/formatter.py +0 -0
  56. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/lookup.py +0 -0
  57. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/manifest.py +0 -0
  58. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/matcher.py +0 -0
  59. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/metadata/__init__.py +0 -0
  60. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/metadata/planner.py +0 -0
  61. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/metadata/provider.py +0 -0
  62. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/metadata/sources/__init__.py +0 -0
  63. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/metadata/sources/tmdb.py +0 -0
  64. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/models.py +0 -0
  65. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/normalize.py +0 -0
  66. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/organizer.py +0 -0
  67. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/snapshot.py +0 -0
  68. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/splitter.py +0 -0
  69. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/tagger.py +0 -0
  70. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/title.py +0 -0
  71. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/ui.py +0 -0
  72. {riplex-0.9.2 → riplex-0.9.4}/src/riplex/updater.py +0 -0
  73. {riplex-0.9.2 → riplex-0.9.4}/src/riplex.egg-info/SOURCES.txt +0 -0
  74. {riplex-0.9.2 → riplex-0.9.4}/src/riplex.egg-info/dependency_links.txt +0 -0
  75. {riplex-0.9.2 → riplex-0.9.4}/src/riplex.egg-info/entry_points.txt +0 -0
  76. {riplex-0.9.2 → riplex-0.9.4}/src/riplex.egg-info/scm_file_list.json +0 -0
  77. {riplex-0.9.2 → riplex-0.9.4}/src/riplex.egg-info/top_level.txt +0 -0
  78. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/__init__.py +0 -0
  79. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/bug_report.py +0 -0
  80. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/crash_dump.py +0 -0
  81. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/keep_awake.py +0 -0
  82. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/main.py +0 -0
  83. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/screens/__init__.py +0 -0
  84. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/screens/disc_detection.py +0 -0
  85. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/screens/disc_overview.py +0 -0
  86. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/screens/disc_swap.py +0 -0
  87. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/screens/done.py +0 -0
  88. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/screens/folder_picker.py +0 -0
  89. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/screens/metadata.py +0 -0
  90. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/screens/orchestrate_done.py +0 -0
  91. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/screens/organize_done.py +0 -0
  92. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/screens/organize_preview.py +0 -0
  93. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/screens/progress.py +0 -0
  94. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/screens/release.py +0 -0
  95. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/screens/selection.py +0 -0
  96. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/screens/update.py +0 -0
  97. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_app/screens/welcome.py +0 -0
  98. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_cli/__init__.py +0 -0
  99. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_cli/commands/__init__.py +0 -0
  100. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_cli/commands/lookup.py +0 -0
  101. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_cli/commands/orchestrate.py +0 -0
  102. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_cli/commands/organize.py +0 -0
  103. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_cli/commands/rip.py +0 -0
  104. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_cli/commands/setup.py +0 -0
  105. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_cli/formatting.py +0 -0
  106. {riplex-0.9.2 → riplex-0.9.4}/src/riplex_cli/main.py +0 -0
  107. {riplex-0.9.2 → riplex-0.9.4}/tests/__init__.py +0 -0
  108. {riplex-0.9.2 → riplex-0.9.4}/tests/fixtures/chernobyl_disc1.json +0 -0
  109. {riplex-0.9.2 → riplex-0.9.4}/tests/fixtures/makemkvcon_frozen_planet_ii_d2.txt +0 -0
  110. {riplex-0.9.2 → riplex-0.9.4}/tests/fixtures/makemkvcon_list.txt +0 -0
  111. {riplex-0.9.2 → riplex-0.9.4}/tests/snapshots/Batman Begins.snapshot.json +0 -0
  112. {riplex-0.9.2 → riplex-0.9.4}/tests/snapshots/Blade Runner (Blu-ray 4k).snapshot.json +0 -0
  113. {riplex-0.9.2 → riplex-0.9.4}/tests/snapshots/Blade Runner The Final Cut.snapshot.json +0 -0
  114. {riplex-0.9.2 → riplex-0.9.4}/tests/snapshots/Seven Worlds One Planet.snapshot.json +0 -0
  115. {riplex-0.9.2 → riplex-0.9.4}/tests/snapshots/The Dark Knight Rises.snapshot.json +0 -0
  116. {riplex-0.9.2 → riplex-0.9.4}/tests/snapshots/The Dark Knight.snapshot.json +0 -0
  117. {riplex-0.9.2 → riplex-0.9.4}/tests/snapshots/Waterworld.snapshot.json +0 -0
  118. {riplex-0.9.2 → riplex-0.9.4}/tests/test_app_main.py +0 -0
  119. {riplex-0.9.2 → riplex-0.9.4}/tests/test_cache.py +0 -0
  120. {riplex-0.9.2 → riplex-0.9.4}/tests/test_cli_utils.py +0 -0
  121. {riplex-0.9.2 → riplex-0.9.4}/tests/test_config.py +0 -0
  122. {riplex-0.9.2 → riplex-0.9.4}/tests/test_dedup.py +0 -0
  123. {riplex-0.9.2 → riplex-0.9.4}/tests/test_detect.py +0 -0
  124. {riplex-0.9.2 → riplex-0.9.4}/tests/test_disc_analysis.py +0 -0
  125. {riplex-0.9.2 → riplex-0.9.4}/tests/test_disc_detection_screen.py +0 -0
  126. {riplex-0.9.2 → riplex-0.9.4}/tests/test_disc_fixtures.py +0 -0
  127. {riplex-0.9.2 → riplex-0.9.4}/tests/test_disc_provider.py +0 -0
  128. {riplex-0.9.2 → riplex-0.9.4}/tests/test_formatter.py +0 -0
  129. {riplex-0.9.2 → riplex-0.9.4}/tests/test_lookup.py +0 -0
  130. {riplex-0.9.2 → riplex-0.9.4}/tests/test_makemkv.py +0 -0
  131. {riplex-0.9.2 → riplex-0.9.4}/tests/test_matcher.py +0 -0
  132. {riplex-0.9.2 → riplex-0.9.4}/tests/test_normalize.py +0 -0
  133. {riplex-0.9.2 → riplex-0.9.4}/tests/test_organizer.py +0 -0
  134. {riplex-0.9.2 → riplex-0.9.4}/tests/test_planner.py +0 -0
  135. {riplex-0.9.2 → riplex-0.9.4}/tests/test_release_screen.py +0 -0
  136. {riplex-0.9.2 → riplex-0.9.4}/tests/test_rip_guide.py +0 -0
  137. {riplex-0.9.2 → riplex-0.9.4}/tests/test_snapshot.py +0 -0
  138. {riplex-0.9.2 → riplex-0.9.4}/tests/test_splitter.py +0 -0
  139. {riplex-0.9.2 → riplex-0.9.4}/tests/test_tagger.py +0 -0
  140. {riplex-0.9.2 → riplex-0.9.4}/tests/test_tmdb_provider.py +0 -0
  141. {riplex-0.9.2 → riplex-0.9.4}/tests/test_ui.py +0 -0
  142. {riplex-0.9.2 → riplex-0.9.4}/tests/test_updater.py +0 -0
  143. {riplex-0.9.2 → riplex-0.9.4}/tests/test_welcome_screen.py +0 -0
@@ -23,7 +23,10 @@ jobs:
23
23
  - name: Install dependencies
24
24
  run: |
25
25
  pip install -e ".[gui]"
26
- pip install flet-desktop pyinstaller
26
+ # Keep flet-desktop in lockstep with the flet pin in pyproject
27
+ # (==0.85.1). The 0.85.3 macOS runtime ships a malformed
28
+ # objective_c.framework that breaks codesign (see issue #21).
29
+ pip install "flet-desktop==0.85.1" pyinstaller
27
30
 
28
31
  - name: Cache Flet desktop runtime
29
32
  run: |
@@ -96,11 +99,18 @@ jobs:
96
99
  - name: Install dependencies
97
100
  run: |
98
101
  pip install -e ".[gui]"
99
- pip install flet-desktop pyinstaller
102
+ # Keep flet-desktop in lockstep with the flet pin in pyproject
103
+ # (==0.85.1). The 0.85.3 macOS runtime ships a malformed
104
+ # objective_c.framework that breaks codesign (see issue #21).
105
+ pip install "flet-desktop==0.85.1" pyinstaller
100
106
 
101
- - name: Cache Flet desktop runtime
107
+ - name: Download Flet desktop runtime archive
102
108
  run: |
103
- python -c "import flet_desktop; flet_desktop.ensure_client_cached()"
109
+ set -e
110
+ FLET_VERSION="$(python -c 'import flet_desktop.version; print(flet_desktop.version.version)')"
111
+ FLET_CLIENT_ARCHIVE="$RUNNER_TEMP/flet-macos.tar.gz"
112
+ curl -L "https://github.com/flet-dev/flet/releases/download/v${FLET_VERSION}/flet-macos.tar.gz" -o "$FLET_CLIENT_ARCHIVE"
113
+ echo "FLET_CLIENT_ARCHIVE=$FLET_CLIENT_ARCHIVE" >> "$GITHUB_ENV"
104
114
 
105
115
  - name: Build CLI executable
106
116
  run: |
@@ -117,7 +127,6 @@ jobs:
117
127
 
118
128
  - name: Build GUI app bundle
119
129
  run: |
120
- FLET_CLIENT_CACHE="$(python -c 'import flet_desktop; print(flet_desktop.ensure_client_cached())')"
121
130
  pyinstaller --name riplex-ui --onedir --windowed \
122
131
  --hidden-import riplex_app.screens.welcome \
123
132
  --hidden-import riplex_app.screens.disc_detection \
@@ -138,14 +147,17 @@ jobs:
138
147
  --collect-all flet_desktop \
139
148
  --collect-data certifi \
140
149
  --collect-data dvdcompare_scraper \
141
- --add-data "$FLET_CLIENT_CACHE:flet_client" \
150
+ --add-data "$FLET_CLIENT_ARCHIVE:flet_desktop/app" \
142
151
  src/riplex_app/main.py
143
152
 
144
153
  - name: Package macOS artifacts
145
154
  run: |
146
155
  mkdir -p release
147
156
  cp dist/riplex release/riplex-macos
148
- cd dist && zip -r ../release/riplex-ui-macos.zip riplex-ui.app
157
+ # -y preserves symlinks (including the .framework Versions/Current
158
+ # links). Without it, zip flattens those symlinks into duplicate
159
+ # files and the unpacked .app crashes on launch (issue #21).
160
+ cd dist && zip -r -y ../release/riplex-ui-macos.zip riplex-ui.app
149
161
 
150
162
  - uses: actions/upload-artifact@v4
151
163
  with:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: riplex
3
- Version: 0.9.2
3
+ Version: 0.9.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
@@ -15,7 +15,7 @@ Requires-Dist: pytest>=8.0; extra == "dev"
15
15
  Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
16
16
  Requires-Dist: respx>=0.21; extra == "dev"
17
17
  Provides-Extra: gui
18
- Requires-Dist: flet<0.86,>=0.84; extra == "gui"
18
+ Requires-Dist: flet==0.85.1; extra == "gui"
19
19
  Dynamic: license-file
20
20
 
21
21
  # riplex
@@ -23,13 +23,19 @@ dev = [
23
23
  "respx>=0.21",
24
24
  ]
25
25
  gui = [
26
- # Pin to a tested Flet range. We've verified 0.84 and 0.85 both work
26
+ # Pin Flet to an exact version. We've verified 0.84 and 0.85 both work
27
27
  # with the GUI's current API usage (``ft.Border.all``,
28
28
  # ``page.show_dialog``). An open upper bound risked picking up
29
- # breaking renames silently (see issue #12); cap at the next minor
30
- # bump so a future Flet release can't break ``pip install`` for
31
- # everyone the moment it lands on PyPI.
32
- "flet>=0.84,<0.86",
29
+ # breaking renames silently (see issue #12).
30
+ #
31
+ # We pin to ``==0.85.1`` specifically because the prebuilt macOS desktop
32
+ # runtime that ships with 0.85.3 (``flet-desktop-full-0.85.3``, downloaded
33
+ # from Flet's CDN at build time) has a malformed nested
34
+ # ``objective_c.framework`` whose code signature newer macOS ``codesign``
35
+ # rejects. That broke the PyInstaller macOS build (release workflow) and
36
+ # produced a GUI bundle that crashes on launch (issue #21). Keep
37
+ # ``flet`` and ``flet-desktop`` in lockstep at this version.
38
+ "flet==0.85.1",
33
39
  # tkinter is also required for the folder picker buttons but cannot be
34
40
  # installed via pip — it is part of Python's standard library and must be
35
41
  # installed at the OS level:
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
  import logging
7
+ import os
7
8
  import platform
8
9
  import shutil
9
10
  import subprocess
@@ -30,16 +31,44 @@ _FFPROBE_SEARCH_PATHS = [
30
31
  ]
31
32
 
32
33
 
34
+ def _windows_ffprobe_candidates() -> list[Path]:
35
+ """Return likely ``ffprobe.exe`` locations from winget and manual installs.
36
+
37
+ riplex installs FFmpeg via ``winget install Gyan.FFmpeg``, which is a
38
+ portable package: winget drops a shim under ``WinGet\\Links`` and unpacks
39
+ the payload under ``WinGet\\Packages\\Gyan.FFmpeg*\\...\\bin``. Neither is on
40
+ PATH in the current process right after install, so check them directly.
41
+ """
42
+ candidates: list[Path] = []
43
+ local_appdata = os.environ.get("LOCALAPPDATA")
44
+ if local_appdata:
45
+ winget = Path(local_appdata) / "Microsoft" / "WinGet"
46
+ candidates.append(winget / "Links" / "ffprobe.exe")
47
+ packages = winget / "Packages"
48
+ if packages.is_dir():
49
+ candidates.extend(packages.glob("Gyan.FFmpeg*/**/bin/ffprobe.exe"))
50
+ # Common manual install spots
51
+ candidates.append(Path(r"C:\ffmpeg\bin\ffprobe.exe"))
52
+ candidates.append(Path(r"C:\Program Files\ffmpeg\bin\ffprobe.exe"))
53
+ return candidates
54
+
55
+
33
56
  def find_ffprobe() -> str | None:
34
57
  """Locate the ffprobe executable.
35
58
 
36
59
  Checks PATH first, then ``~/.riplex/bin/``, ``/usr/local/bin/``,
37
- and ``/opt/homebrew/bin/``. Returns the path string or *None*.
60
+ and ``/opt/homebrew/bin/``. On Windows, also checks the winget
61
+ portable install locations (the ``WinGet\\Links`` shim and the
62
+ ``WinGet\\Packages\\Gyan.FFmpeg*`` payload) and common manual install
63
+ folders. Returns the path string or *None*.
38
64
  """
39
65
  path = shutil.which("ffprobe")
40
66
  if path:
41
67
  return path
42
- for candidate in _FFPROBE_SEARCH_PATHS:
68
+ candidates = list(_FFPROBE_SEARCH_PATHS)
69
+ if platform.system() == "Windows":
70
+ candidates.extend(_windows_ffprobe_candidates())
71
+ for candidate in candidates:
43
72
  if candidate.is_file():
44
73
  return str(candidate)
45
74
  return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: riplex
3
- Version: 0.9.2
3
+ Version: 0.9.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
@@ -15,7 +15,7 @@ Requires-Dist: pytest>=8.0; extra == "dev"
15
15
  Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
16
16
  Requires-Dist: respx>=0.21; extra == "dev"
17
17
  Provides-Extra: gui
18
- Requires-Dist: flet<0.86,>=0.84; extra == "gui"
18
+ Requires-Dist: flet==0.85.1; extra == "gui"
19
19
  Dynamic: license-file
20
20
 
21
21
  # riplex
@@ -9,4 +9,4 @@ pytest-asyncio>=0.23
9
9
  respx>=0.21
10
10
 
11
11
  [gui]
12
- flet<0.86,>=0.84
12
+ flet==0.85.1
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.9.4",
3
+ "distance": 0,
4
+ "node": "g2e958718f2247e29a80b9323f72e3feab086e258",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-06-25"
8
+ }
@@ -6,7 +6,7 @@ from unittest.mock import patch
6
6
  import pytest
7
7
 
8
8
  from riplex.models import ScannedFile
9
- from riplex.scanner import _probe_file, scan_folder
9
+ from riplex.scanner import _probe_file, find_ffprobe, scan_folder
10
10
 
11
11
 
12
12
  @pytest.fixture
@@ -103,3 +103,62 @@ class TestScanFolder:
103
103
  for disc in discs:
104
104
  for f in disc.files:
105
105
  assert Path(f.path).is_absolute()
106
+
107
+
108
+ class TestFindFfprobe:
109
+ def test_returns_path_when_on_path(self):
110
+ with patch("riplex.scanner.shutil.which", return_value="/usr/bin/ffprobe"):
111
+ assert find_ffprobe() == "/usr/bin/ffprobe"
112
+
113
+ def test_returns_none_when_not_found(self, monkeypatch):
114
+ monkeypatch.setattr("riplex.scanner.shutil.which", lambda _: None)
115
+ monkeypatch.setattr("riplex.scanner.platform.system", lambda: "Linux")
116
+ monkeypatch.setattr("riplex.scanner._FFPROBE_SEARCH_PATHS", [])
117
+ assert find_ffprobe() is None
118
+
119
+ def test_finds_winget_links_shim_on_windows(self, tmp_path, monkeypatch):
120
+ local_appdata = tmp_path / "AppData" / "Local"
121
+ links = local_appdata / "Microsoft" / "WinGet" / "Links"
122
+ links.mkdir(parents=True)
123
+ shim = links / "ffprobe.exe"
124
+ shim.write_text("", encoding="utf-8")
125
+
126
+ monkeypatch.setattr("riplex.scanner.shutil.which", lambda _: None)
127
+ monkeypatch.setattr("riplex.scanner.platform.system", lambda: "Windows")
128
+ monkeypatch.setattr("riplex.scanner._FFPROBE_SEARCH_PATHS", [])
129
+ monkeypatch.setenv("LOCALAPPDATA", str(local_appdata))
130
+
131
+ assert find_ffprobe() == str(shim)
132
+
133
+ def test_finds_winget_package_payload_on_windows(self, tmp_path, monkeypatch):
134
+ local_appdata = tmp_path / "AppData" / "Local"
135
+ bin_dir = (
136
+ local_appdata / "Microsoft" / "WinGet" / "Packages"
137
+ / "Gyan.FFmpeg_Microsoft.Winget.Source_abc"
138
+ / "ffmpeg-7.0-full_build" / "bin"
139
+ )
140
+ bin_dir.mkdir(parents=True)
141
+ exe = bin_dir / "ffprobe.exe"
142
+ exe.write_text("", encoding="utf-8")
143
+
144
+ monkeypatch.setattr("riplex.scanner.shutil.which", lambda _: None)
145
+ monkeypatch.setattr("riplex.scanner.platform.system", lambda: "Windows")
146
+ monkeypatch.setattr("riplex.scanner._FFPROBE_SEARCH_PATHS", [])
147
+ monkeypatch.setenv("LOCALAPPDATA", str(local_appdata))
148
+
149
+ assert find_ffprobe() == str(exe)
150
+
151
+ def test_windows_candidates_skipped_on_non_windows(self, monkeypatch):
152
+ monkeypatch.setattr("riplex.scanner.shutil.which", lambda _: None)
153
+ monkeypatch.setattr("riplex.scanner.platform.system", lambda: "Linux")
154
+ monkeypatch.setattr("riplex.scanner._FFPROBE_SEARCH_PATHS", [])
155
+ called = {"hit": False}
156
+
157
+ def _should_not_run():
158
+ called["hit"] = True
159
+ return []
160
+
161
+ monkeypatch.setattr("riplex.scanner._windows_ffprobe_candidates", _should_not_run)
162
+ assert find_ffprobe() is None
163
+ assert called["hit"] is False
164
+
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "0.9.2",
3
- "distance": 0,
4
- "node": "g775f04026aebf5255891652cd7e5dec5dce26000",
5
- "dirty": false,
6
- "branch": "HEAD",
7
- "node_date": "2026-06-24"
8
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes