riplex 0.1.10__tar.gz → 0.2.2__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 (112) hide show
  1. {riplex-0.1.10 → riplex-0.2.2}/.github/workflows/release.yml +2 -0
  2. {riplex-0.1.10/src/riplex.egg-info → riplex-0.2.2}/PKG-INFO +1 -1
  3. {riplex-0.1.10 → riplex-0.2.2/src/riplex.egg-info}/PKG-INFO +1 -1
  4. {riplex-0.1.10 → riplex-0.2.2}/src/riplex.egg-info/SOURCES.txt +1 -0
  5. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_app/main.py +4 -0
  6. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_app/screens/welcome.py +52 -0
  7. riplex-0.2.2/src/riplex_app/updater.py +83 -0
  8. {riplex-0.1.10 → riplex-0.2.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  9. {riplex-0.1.10 → riplex-0.2.2}/.github/agents/riplex.agent.md +0 -0
  10. {riplex-0.1.10 → riplex-0.2.2}/.github/copilot-instructions.md +0 -0
  11. {riplex-0.1.10 → riplex-0.2.2}/.github/workflows/publish.yml +0 -0
  12. {riplex-0.1.10 → riplex-0.2.2}/.gitignore +0 -0
  13. {riplex-0.1.10 → riplex-0.2.2}/MONOREPO_PLAN.md +0 -0
  14. {riplex-0.1.10 → riplex-0.2.2}/PLANNED_FEATURES.md +0 -0
  15. {riplex-0.1.10 → riplex-0.2.2}/README.md +0 -0
  16. {riplex-0.1.10 → riplex-0.2.2}/REFACTOR_PLAN.md +0 -0
  17. {riplex-0.1.10 → riplex-0.2.2}/docs/architecture.md +0 -0
  18. {riplex-0.1.10 → riplex-0.2.2}/docs/changelog.md +0 -0
  19. {riplex-0.1.10 → riplex-0.2.2}/docs/getting-started/configuration.md +0 -0
  20. {riplex-0.1.10 → riplex-0.2.2}/docs/getting-started/installation.md +0 -0
  21. {riplex-0.1.10 → riplex-0.2.2}/docs/guide/lookup.md +0 -0
  22. {riplex-0.1.10 → riplex-0.2.2}/docs/guide/orchestrate.md +0 -0
  23. {riplex-0.1.10 → riplex-0.2.2}/docs/guide/organize.md +0 -0
  24. {riplex-0.1.10 → riplex-0.2.2}/docs/guide/workflow.md +0 -0
  25. {riplex-0.1.10 → riplex-0.2.2}/docs/index.md +0 -0
  26. {riplex-0.1.10 → riplex-0.2.2}/docs/naming-rules.md +0 -0
  27. {riplex-0.1.10 → riplex-0.2.2}/docs/reference/cli.md +0 -0
  28. {riplex-0.1.10 → riplex-0.2.2}/issues/cross-disc-dvdcompare-matching.md +0 -0
  29. {riplex-0.1.10 → riplex-0.2.2}/mkdocs.yml +0 -0
  30. {riplex-0.1.10 → riplex-0.2.2}/plex_naming_rules.md +0 -0
  31. {riplex-0.1.10 → riplex-0.2.2}/pyproject.toml +0 -0
  32. {riplex-0.1.10 → riplex-0.2.2}/setup.cfg +0 -0
  33. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/__init__.py +0 -0
  34. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/cache.py +0 -0
  35. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/config.py +0 -0
  36. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/dedup.py +0 -0
  37. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/detect.py +0 -0
  38. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/disc/__init__.py +0 -0
  39. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/disc/analysis.py +0 -0
  40. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/disc/makemkv.py +0 -0
  41. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/disc/provider.py +0 -0
  42. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/formatter.py +0 -0
  43. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/lookup.py +0 -0
  44. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/manifest.py +0 -0
  45. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/matcher.py +0 -0
  46. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/metadata/__init__.py +0 -0
  47. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/metadata/planner.py +0 -0
  48. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/metadata/provider.py +0 -0
  49. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/metadata/sources/__init__.py +0 -0
  50. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/metadata/sources/tmdb.py +0 -0
  51. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/models.py +0 -0
  52. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/normalize.py +0 -0
  53. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/organizer.py +0 -0
  54. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/scanner.py +0 -0
  55. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/snapshot.py +0 -0
  56. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/splitter.py +0 -0
  57. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/tagger.py +0 -0
  58. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/title.py +0 -0
  59. {riplex-0.1.10 → riplex-0.2.2}/src/riplex/ui.py +0 -0
  60. {riplex-0.1.10 → riplex-0.2.2}/src/riplex.egg-info/dependency_links.txt +0 -0
  61. {riplex-0.1.10 → riplex-0.2.2}/src/riplex.egg-info/entry_points.txt +0 -0
  62. {riplex-0.1.10 → riplex-0.2.2}/src/riplex.egg-info/requires.txt +0 -0
  63. {riplex-0.1.10 → riplex-0.2.2}/src/riplex.egg-info/top_level.txt +0 -0
  64. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_app/__init__.py +0 -0
  65. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_app/screens/__init__.py +0 -0
  66. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_app/screens/disc_detection.py +0 -0
  67. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_app/screens/done.py +0 -0
  68. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_app/screens/folder_picker.py +0 -0
  69. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_app/screens/metadata.py +0 -0
  70. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_app/screens/organize_done.py +0 -0
  71. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_app/screens/organize_preview.py +0 -0
  72. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_app/screens/progress.py +0 -0
  73. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_app/screens/release.py +0 -0
  74. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_app/screens/selection.py +0 -0
  75. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_cli/__init__.py +0 -0
  76. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_cli/commands/__init__.py +0 -0
  77. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_cli/commands/lookup.py +0 -0
  78. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_cli/commands/orchestrate.py +0 -0
  79. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_cli/commands/organize.py +0 -0
  80. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_cli/commands/rip.py +0 -0
  81. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_cli/commands/setup.py +0 -0
  82. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_cli/formatting.py +0 -0
  83. {riplex-0.1.10 → riplex-0.2.2}/src/riplex_cli/main.py +0 -0
  84. {riplex-0.1.10 → riplex-0.2.2}/tests/__init__.py +0 -0
  85. {riplex-0.1.10 → riplex-0.2.2}/tests/fixtures/makemkvcon_frozen_planet_ii_d2.txt +0 -0
  86. {riplex-0.1.10 → riplex-0.2.2}/tests/fixtures/makemkvcon_list.txt +0 -0
  87. {riplex-0.1.10 → riplex-0.2.2}/tests/snapshots/Batman Begins.snapshot.json +0 -0
  88. {riplex-0.1.10 → riplex-0.2.2}/tests/snapshots/Blade Runner (Blu-ray 4k).snapshot.json +0 -0
  89. {riplex-0.1.10 → riplex-0.2.2}/tests/snapshots/Blade Runner The Final Cut.snapshot.json +0 -0
  90. {riplex-0.1.10 → riplex-0.2.2}/tests/snapshots/Seven Worlds One Planet.snapshot.json +0 -0
  91. {riplex-0.1.10 → riplex-0.2.2}/tests/snapshots/The Dark Knight Rises.snapshot.json +0 -0
  92. {riplex-0.1.10 → riplex-0.2.2}/tests/snapshots/The Dark Knight.snapshot.json +0 -0
  93. {riplex-0.1.10 → riplex-0.2.2}/tests/snapshots/Waterworld.snapshot.json +0 -0
  94. {riplex-0.1.10 → riplex-0.2.2}/tests/test_cache.py +0 -0
  95. {riplex-0.1.10 → riplex-0.2.2}/tests/test_cli_utils.py +0 -0
  96. {riplex-0.1.10 → riplex-0.2.2}/tests/test_config.py +0 -0
  97. {riplex-0.1.10 → riplex-0.2.2}/tests/test_dedup.py +0 -0
  98. {riplex-0.1.10 → riplex-0.2.2}/tests/test_detect.py +0 -0
  99. {riplex-0.1.10 → riplex-0.2.2}/tests/test_disc_analysis.py +0 -0
  100. {riplex-0.1.10 → riplex-0.2.2}/tests/test_disc_provider.py +0 -0
  101. {riplex-0.1.10 → riplex-0.2.2}/tests/test_formatter.py +0 -0
  102. {riplex-0.1.10 → riplex-0.2.2}/tests/test_makemkv.py +0 -0
  103. {riplex-0.1.10 → riplex-0.2.2}/tests/test_matcher.py +0 -0
  104. {riplex-0.1.10 → riplex-0.2.2}/tests/test_normalize.py +0 -0
  105. {riplex-0.1.10 → riplex-0.2.2}/tests/test_organizer.py +0 -0
  106. {riplex-0.1.10 → riplex-0.2.2}/tests/test_planner.py +0 -0
  107. {riplex-0.1.10 → riplex-0.2.2}/tests/test_rip_guide.py +0 -0
  108. {riplex-0.1.10 → riplex-0.2.2}/tests/test_scanner.py +0 -0
  109. {riplex-0.1.10 → riplex-0.2.2}/tests/test_snapshot.py +0 -0
  110. {riplex-0.1.10 → riplex-0.2.2}/tests/test_splitter.py +0 -0
  111. {riplex-0.1.10 → riplex-0.2.2}/tests/test_tagger.py +0 -0
  112. {riplex-0.1.10 → riplex-0.2.2}/tests/test_ui.py +0 -0
@@ -51,6 +51,7 @@ jobs:
51
51
  --hidden-import riplex_app.screens.organize_preview ^
52
52
  --hidden-import riplex_app.screens.organize_done ^
53
53
  --collect-all flet ^
54
+ --collect-all flet_desktop ^
54
55
  --collect-data dvdcompare_scraper ^
55
56
  src/riplex_app/main.py
56
57
  shell: cmd
@@ -108,6 +109,7 @@ jobs:
108
109
  --hidden-import riplex_app.screens.organize_preview \
109
110
  --hidden-import riplex_app.screens.organize_done \
110
111
  --collect-all flet \
112
+ --collect-all flet_desktop \
111
113
  --collect-data dvdcompare_scraper \
112
114
  src/riplex_app/main.py
113
115
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: riplex
3
- Version: 0.1.10
3
+ Version: 0.2.2
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: riplex
3
- Version: 0.1.10
3
+ Version: 0.2.2
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
@@ -58,6 +58,7 @@ src/riplex/metadata/sources/__init__.py
58
58
  src/riplex/metadata/sources/tmdb.py
59
59
  src/riplex_app/__init__.py
60
60
  src/riplex_app/main.py
61
+ src/riplex_app/updater.py
61
62
  src/riplex_app/screens/__init__.py
62
63
  src/riplex_app/screens/disc_detection.py
63
64
  src/riplex_app/screens/done.py
@@ -76,6 +76,10 @@ class RiplexApp:
76
76
  self.page.controls.append(screen.build())
77
77
  self.page.update()
78
78
 
79
+ # Kick off background update check on welcome screen
80
+ if screen_name == "welcome":
81
+ screen.check_for_updates()
82
+
79
83
 
80
84
  def main():
81
85
  logging.basicConfig(
@@ -2,11 +2,13 @@
2
2
 
3
3
  import shutil
4
4
  import threading
5
+ import webbrowser
5
6
 
6
7
  import flet as ft
7
8
 
8
9
  from riplex.config import load_config, get_api_key
9
10
  from riplex.disc.makemkv import find_makemkvcon
11
+ from riplex_app.updater import check_for_update, get_current_version, get_download_url
10
12
 
11
13
 
12
14
  class WelcomeScreen:
@@ -148,9 +150,32 @@ class WelcomeScreen:
148
150
  tooltip="Organize existing MKV rips into Plex-compatible folder structure.",
149
151
  )
150
152
 
153
+ # Update banner (hidden until check completes)
154
+ self.update_banner = ft.Container(
155
+ ft.Row(
156
+ [
157
+ ft.Icon(ft.Icons.SYSTEM_UPDATE, color=ft.Colors.BLUE),
158
+ ft.Text("", size=13, ref=None),
159
+ ft.TextButton("Download", on_click=self._open_update),
160
+ ],
161
+ spacing=8,
162
+ ),
163
+ bgcolor=ft.Colors.BLUE_50,
164
+ border_radius=8,
165
+ padding=ft.padding.symmetric(horizontal=12, vertical=8),
166
+ visible=False,
167
+ )
168
+ self._update_info = None
169
+
151
170
  return ft.Column(
152
171
  [
153
172
  ft.Text("riplex", size=32, weight=ft.FontWeight.BOLD),
173
+ ft.Text(
174
+ f"v{get_current_version()}",
175
+ size=12,
176
+ color=ft.Colors.GREY_500,
177
+ ),
178
+ self.update_banner,
154
179
  ft.Text(
155
180
  "Rip physical discs and organize into Plex-compatible libraries.",
156
181
  size=14,
@@ -198,6 +223,33 @@ class WelcomeScreen:
198
223
 
199
224
  threading.Thread(target=_pick, daemon=True).start()
200
225
 
226
+ def check_for_updates(self):
227
+ """Run update check in background thread (call after page is available)."""
228
+ def _check():
229
+ info = check_for_update()
230
+ if info:
231
+ self._update_info = info
232
+ # Update UI from main thread
233
+ import asyncio
234
+
235
+ async def _show():
236
+ banner_row = self.update_banner.content
237
+ banner_row.controls[1] = ft.Text(
238
+ f"Update available: {info['tag']}", size=13
239
+ )
240
+ self.update_banner.visible = True
241
+ self.app.page.update()
242
+
243
+ self.app.page.run_task(_show)
244
+
245
+ threading.Thread(target=_check, daemon=True).start()
246
+
247
+ def _open_update(self, e):
248
+ """Open the download URL in the user's browser."""
249
+ if self._update_info:
250
+ url = get_download_url(self._update_info)
251
+ webbrowser.open(url)
252
+
201
253
  def _save_config(self, e):
202
254
  """Write config from the setup fields."""
203
255
  from riplex.config import save_config
@@ -0,0 +1,83 @@
1
+ """Check for newer releases on GitHub."""
2
+
3
+ import urllib.request
4
+ import json
5
+ import sys
6
+ from importlib.metadata import version, PackageNotFoundError
7
+
8
+ GITHUB_REPO = "AnyCredit5518/riplex"
9
+ RELEASES_URL = f"https://api.github.com/repos/{GITHUB_REPO}/releases/latest"
10
+
11
+
12
+ def get_current_version() -> str:
13
+ """Return the installed package version, or 'dev' if not installed."""
14
+ try:
15
+ return version("riplex")
16
+ except PackageNotFoundError:
17
+ return "dev"
18
+
19
+
20
+ def _parse_version(tag: str) -> tuple:
21
+ """Parse 'v1.2.3' into (1, 2, 3) for comparison."""
22
+ tag = tag.lstrip("v")
23
+ parts = []
24
+ for p in tag.split("."):
25
+ try:
26
+ parts.append(int(p))
27
+ except ValueError:
28
+ break
29
+ return tuple(parts)
30
+
31
+
32
+ def check_for_update() -> dict | None:
33
+ """Check GitHub for a newer release.
34
+
35
+ Returns a dict with 'tag', 'url', and 'assets' if an update is available,
36
+ or None if already up to date (or on error).
37
+ """
38
+ current = get_current_version()
39
+ if current == "dev":
40
+ return None
41
+
42
+ try:
43
+ req = urllib.request.Request(
44
+ RELEASES_URL,
45
+ headers={"Accept": "application/vnd.github+json", "User-Agent": "riplex"},
46
+ )
47
+ with urllib.request.urlopen(req, timeout=5) as resp:
48
+ data = json.loads(resp.read())
49
+ except Exception:
50
+ return None
51
+
52
+ latest_tag = data.get("tag_name", "")
53
+ if not latest_tag:
54
+ return None
55
+
56
+ if _parse_version(latest_tag) > _parse_version(current):
57
+ # Find the right asset for this platform
58
+ assets = {}
59
+ for asset in data.get("assets", []):
60
+ name = asset["name"].lower()
61
+ assets[name] = asset["browser_download_url"]
62
+
63
+ return {
64
+ "tag": latest_tag,
65
+ "url": data.get("html_url", ""),
66
+ "assets": assets,
67
+ }
68
+
69
+ return None
70
+
71
+
72
+ def get_download_url(update_info: dict) -> str:
73
+ """Get the platform-appropriate download URL from update info."""
74
+ if sys.platform == "win32":
75
+ for name, url in update_info["assets"].items():
76
+ if "ui" in name and "windows" in name:
77
+ return url
78
+ elif sys.platform == "darwin":
79
+ for name, url in update_info["assets"].items():
80
+ if "ui" in name and "macos" in name:
81
+ return url
82
+ # Fallback to release page
83
+ return update_info["url"]
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