riplex 0.9.9__tar.gz → 1.0.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.
- {riplex-0.9.9 → riplex-1.0.2}/.github/agents/riplex.agent.md +12 -1
- {riplex-0.9.9 → riplex-1.0.2}/.github/copilot-instructions.md +28 -4
- {riplex-0.9.9 → riplex-1.0.2}/.github/workflows/release.yml +10 -0
- {riplex-0.9.9 → riplex-1.0.2}/.github/workflows/update-contributors.yml +2 -2
- {riplex-0.9.9/src/riplex.egg-info → riplex-1.0.2}/PKG-INFO +3 -3
- {riplex-0.9.9 → riplex-1.0.2}/README.md +1 -1
- riplex-1.0.2/RELEASE_NOTES_v1.0.0.md +139 -0
- {riplex-0.9.9 → riplex-1.0.2}/docs/architecture.md +3 -0
- {riplex-0.9.9 → riplex-1.0.2}/docs/changelog.md +92 -0
- {riplex-0.9.9 → riplex-1.0.2}/docs/cli-guide/orchestrate.md +2 -0
- {riplex-0.9.9 → riplex-1.0.2}/docs/cli-guide/organize.md +14 -2
- {riplex-0.9.9 → riplex-1.0.2}/docs/cli-guide/workflow.md +3 -1
- riplex-1.0.2/docs/development/testing.md +182 -0
- {riplex-0.9.9 → riplex-1.0.2}/docs/getting-started/configuration.md +2 -0
- {riplex-0.9.9 → riplex-1.0.2}/docs/getting-started/installation.md +13 -1
- {riplex-0.9.9 → riplex-1.0.2}/docs/gui-guide/gui-walkthrough.md +10 -0
- {riplex-0.9.9 → riplex-1.0.2}/docs/index.md +5 -0
- {riplex-0.9.9 → riplex-1.0.2}/docs/troubleshooting.md +12 -0
- riplex-1.0.2/issues/one-season-at-a-time.md +95 -0
- riplex-1.0.2/issues/organize-preview-match-override.md +87 -0
- {riplex-0.9.9 → riplex-1.0.2}/mkdocs.yml +2 -0
- {riplex-0.9.9 → riplex-1.0.2}/pyproject.toml +4 -1
- riplex-1.0.2/scripts/gen_gui_fixtures.py +393 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/cache.py +51 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/config.py +9 -0
- riplex-1.0.2/src/riplex/disc/analysis.py +1588 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/disc/makemkv.py +4 -1
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/disc/provider.py +251 -21
- riplex-1.0.2/src/riplex/lookup.py +512 -0
- riplex-1.0.2/src/riplex/manifest.py +791 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/matcher.py +106 -0
- riplex-1.0.2/src/riplex/metadata/autosearch.py +130 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/metadata/planner.py +29 -7
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/metadata/provider.py +1 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/metadata/sources/tmdb.py +5 -1
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/models.py +79 -0
- riplex-1.0.2/src/riplex/organize_by_group.py +364 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/organizer.py +199 -7
- riplex-1.0.2/src/riplex/resume.py +337 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/ui.py +140 -4
- riplex-1.0.2/src/riplex/updater.py +421 -0
- {riplex-0.9.9 → riplex-1.0.2/src/riplex.egg-info}/PKG-INFO +3 -3
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex.egg-info/SOURCES.txt +80 -1
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex.egg-info/requires.txt +1 -1
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex.egg-info/scm_file_list.json +189 -110
- riplex-1.0.2/src/riplex.egg-info/scm_version.json +8 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_app/bug_report.py +5 -1
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_app/main.py +153 -1
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_app/screens/disc_detection.py +178 -142
- riplex-1.0.2/src/riplex_app/screens/disc_overview.py +1234 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_app/screens/disc_swap.py +57 -10
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_app/screens/done.py +7 -1
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_app/screens/folder_picker.py +221 -32
- riplex-1.0.2/src/riplex_app/screens/metadata.py +535 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_app/screens/orchestrate_done.py +114 -33
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_app/screens/organize_done.py +7 -2
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_app/screens/organize_preview.py +166 -44
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_app/screens/progress.py +85 -9
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_app/screens/release.py +288 -11
- riplex-1.0.2/src/riplex_app/screens/season_select.py +359 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_app/screens/selection.py +195 -20
- riplex-1.0.2/src/riplex_app/screens/update.py +207 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_app/screens/welcome.py +1 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_cli/commands/lookup.py +18 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_cli/commands/orchestrate.py +185 -76
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_cli/commands/organize.py +274 -65
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_cli/commands/rip.py +59 -3
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_cli/main.py +17 -1
- riplex-1.0.2/tests/conftest.py +36 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/2001-a-space-odyssey-1968.json +278 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/akira-1988.json +1351 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/back-to-the-future-1985.json +713 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/back-to-the-future-part-ii-1989.json +764 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/back-to-the-future-part-iii-1990.json +1268 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/blade-1998.json +120 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/blade-runner-2049-2017.json +160 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/chernobyl-2019.json +370 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/d-day-normandy-1944-2014.json +59 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/final-destination-bloodlines-2025.json +98 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/flight-of-the-butterflies-2012.json +277 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/inception-2010.json +449 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/independence-day-1996.json +481 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/journey-to-space-2015.json +272 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/journey-to-the-south-pacific-2013.json +59 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/jurassic-park-1993.json +567 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/king-kong-2005.json +1384 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/mad-max-fury-road-2015.json +125 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/march-of-the-penguins-2005.json +147 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/national-parks-adventure-2016.json +59 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/oceans-our-blue-planet-2018.json +156 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/psych-season-01.json +825 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/psych-season-03.json +1023 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/psych-season-04.json +971 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/psych-the-movie-2017.json +2850 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/space-station-3d-2002.json +59 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/the-godfather-1972.json +1525 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/the-last-reef-cities-beneath-the-sea-2012.json +350 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/the-matrix-1999.json +944 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/the-matrix-reloaded-2003.json +500 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/the-mummy-1999.json +318 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/the-mummy-returns-2001.json +273 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/the-mummy-tomb-of-the-dragon-emperor-2008.json +59 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/the-patriot-2000.json +148 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/the-thing-1982.json +148 -0
- riplex-1.0.2/tests/fixtures/gui/scenarios/wonders-of-the-arctic-2014.json +219 -0
- riplex-1.0.2/tests/integration/__init__.py +6 -0
- riplex-1.0.2/tests/integration/conftest.py +13 -0
- riplex-1.0.2/tests/integration/test_cli_lookup.py +90 -0
- riplex-1.0.2/tests/integration/test_cli_organize.py +92 -0
- riplex-1.0.2/tests/integration/test_cli_parser.py +59 -0
- riplex-1.0.2/tests/integration/test_fixture_scenarios.py +73 -0
- riplex-1.0.2/tests/integration/test_flow_errors.py +79 -0
- riplex-1.0.2/tests/integration/test_flow_media_types.py +123 -0
- riplex-1.0.2/tests/integration/test_flow_movie_orchestrate.py +53 -0
- riplex-1.0.2/tests/integration/test_flow_organize.py +65 -0
- riplex-1.0.2/tests/integration/test_flow_resume_prefill.py +67 -0
- riplex-1.0.2/tests/integration/test_flow_tv_orchestrate.py +52 -0
- riplex-1.0.2/tests/integration/test_flow_welcome.py +42 -0
- riplex-1.0.2/tests/integration/test_screen_build_smoke.py +130 -0
- riplex-1.0.2/tests/support/__init__.py +16 -0
- riplex-1.0.2/tests/support/cli.py +92 -0
- riplex-1.0.2/tests/support/driver.py +99 -0
- riplex-1.0.2/tests/support/fake_page.py +155 -0
- riplex-1.0.2/tests/support/fixtures.py +417 -0
- riplex-1.0.2/tests/support/provider_mocks.py +301 -0
- riplex-1.0.2/tests/support/sync_runtime.py +93 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_app_main.py +67 -2
- riplex-1.0.2/tests/test_autosearch.py +155 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_cache.py +50 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_cli_utils.py +21 -0
- riplex-1.0.2/tests/test_disc_analysis.py +2093 -0
- riplex-1.0.2/tests/test_disc_detection_screen.py +287 -0
- riplex-1.0.2/tests/test_disc_provider.py +662 -0
- riplex-1.0.2/tests/test_folder_picker.py +75 -0
- riplex-1.0.2/tests/test_lookup.py +468 -0
- riplex-1.0.2/tests/test_lookup_resolve_groups.py +122 -0
- riplex-1.0.2/tests/test_manifest_prefill_ids.py +160 -0
- riplex-1.0.2/tests/test_manifest_session.py +660 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_matcher.py +228 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_metadata_screen.py +3 -2
- riplex-1.0.2/tests/test_organize_by_group.py +370 -0
- riplex-1.0.2/tests/test_organize_cli.py +303 -0
- riplex-1.0.2/tests/test_organize_session_fanout.py +224 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_organizer.py +458 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_planner.py +69 -0
- riplex-1.0.2/tests/test_progress.py +223 -0
- riplex-1.0.2/tests/test_release_screen.py +273 -0
- riplex-1.0.2/tests/test_resume.py +492 -0
- riplex-1.0.2/tests/test_season_select_screen.py +509 -0
- riplex-1.0.2/tests/test_ui.py +374 -0
- riplex-1.0.2/tests/test_updater_selfupdate.py +266 -0
- riplex-0.9.9/src/riplex/disc/analysis.py +0 -673
- riplex-0.9.9/src/riplex/lookup.py +0 -90
- riplex-0.9.9/src/riplex/manifest.py +0 -338
- riplex-0.9.9/src/riplex/updater.py +0 -173
- riplex-0.9.9/src/riplex.egg-info/scm_version.json +0 -8
- riplex-0.9.9/src/riplex_app/screens/disc_overview.py +0 -265
- riplex-0.9.9/src/riplex_app/screens/metadata.py +0 -282
- riplex-0.9.9/src/riplex_app/screens/update.py +0 -127
- riplex-0.9.9/tests/test_disc_analysis.py +0 -602
- riplex-0.9.9/tests/test_disc_detection_screen.py +0 -46
- riplex-0.9.9/tests/test_disc_provider.py +0 -298
- riplex-0.9.9/tests/test_lookup.py +0 -48
- riplex-0.9.9/tests/test_release_screen.py +0 -72
- riplex-0.9.9/tests/test_ui.py +0 -163
- {riplex-0.9.9 → riplex-1.0.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/.github/ISSUE_TEMPLATE/crash_report.yml +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/.github/contributors-overrides.json +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/.github/workflows/publish.yml +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/.gitignore +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/.vscode/settings.json +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/CONTRIBUTORS.md +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/LICENSE +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/docs/cli-guide/lookup.md +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/docs/naming-rules.md +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/docs/reference/cli.md +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/issues/debug-artifacts-consolidation.md +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/issues/manual-rip-and-organize-escape-hatch.md +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/issues/orchestrate-dvdcompare-fallback.md +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/issues/planned-features.md +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/screenshots/0_Rip_Flow_BTTF.gif +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/screenshots/1_Welcome_Screen.png +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/screenshots/2_Disc_Detection_BTTF.png +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/screenshots/3_Metadata_Lookup_BTTF.png +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/screenshots/4_Disc_Release_BTTF.png +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/screenshots/5_Multi_Disc_Overview_BTTF.png +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/screenshots/5_Select_Title_to_RIP_BTTF.png +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/scripts/__init__.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/scripts/update_contributors.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/setup.cfg +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/__init__.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/dedup.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/detect.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/disc/__init__.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/formatter.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/metadata/__init__.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/metadata/sources/__init__.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/normalize.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/scanner.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/snapshot.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/splitter.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/tagger.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex/title.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex.egg-info/dependency_links.txt +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex.egg-info/entry_points.txt +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex.egg-info/top_level.txt +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_app/__init__.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_app/crash_dump.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_app/keep_awake.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_app/screens/__init__.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_cli/__init__.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_cli/commands/__init__.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_cli/commands/setup.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/src/riplex_cli/formatting.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/__init__.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/fixtures/chernobyl_disc1.json +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/fixtures/makemkvcon_frozen_planet_ii_d2.txt +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/fixtures/makemkvcon_list.txt +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/snapshots/Batman Begins.snapshot.json +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/snapshots/Blade Runner (Blu-ray 4k).snapshot.json +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/snapshots/Blade Runner The Final Cut.snapshot.json +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/snapshots/Seven Worlds One Planet.snapshot.json +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/snapshots/The Dark Knight Rises.snapshot.json +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/snapshots/The Dark Knight.snapshot.json +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/snapshots/Waterworld.snapshot.json +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_config.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_dedup.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_detect.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_disc_fixtures.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_formatter.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_makemkv.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_normalize.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_rip_guide.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_scanner.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_snapshot.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_splitter.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_tagger.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_tmdb_provider.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_update_contributors.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_updater.py +0 -0
- {riplex-0.9.9 → riplex-1.0.2}/tests/test_welcome_screen.py +0 -0
|
@@ -151,7 +151,18 @@ Do NOT use `py -m riplex` (errors — riplex is a library package). Do NOT use `
|
|
|
151
151
|
|
|
152
152
|
## Debugging the GUI
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
**When the user reports a bug they can reproduce locally, ALWAYS check the debug log first** — before reading code, before guessing at causes. The log will usually tell you exactly which code path ran and what state it saw. Guessing at fixes without checking the log wastes time and often lands the wrong fix.
|
|
155
|
+
|
|
156
|
+
The GUI writes detailed logs including screen navigation, dvdcompare/TMDb lookups, disc analysis, title classification decisions, group routing, autofill results, and rip progress. Log locations (per `platformdirs`):
|
|
157
|
+
|
|
158
|
+
- **Windows**: `%LOCALAPPDATA%\riplex\riplex\Logs\riplex_app.log`
|
|
159
|
+
(typically `C:\Users\<user>\AppData\Local\riplex\riplex\Logs\riplex_app.log`)
|
|
160
|
+
- **macOS**: `~/Library/Logs/riplex/riplex_app.log`
|
|
161
|
+
- **Linux**: `~/.local/state/riplex/log/riplex_app.log` (or `$XDG_STATE_HOME/riplex/log/`)
|
|
162
|
+
|
|
163
|
+
The log is truncated / rotated on each GUI launch, so grab it before the user restarts the app. Use `Select-String` (PowerShell) or `grep` for keywords tied to the buggy behavior — `Season`, `dvdcompare_film`, `group`, `autofill`, `classify`, etc.
|
|
164
|
+
|
|
165
|
+
Do NOT try to capture terminal output with `2>&1` — Flet's Flutter-Python IPC breaks when stderr is redirected.
|
|
155
166
|
|
|
156
167
|
## Testing
|
|
157
168
|
|
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Copilot Instructions for riplex
|
|
2
2
|
|
|
3
|
+
## Shared flow logic (CLI/GUI parity)
|
|
4
|
+
|
|
5
|
+
The CLI (`src/riplex_cli/`) and the GUI (`src/riplex_app/`) must not carry
|
|
6
|
+
duplicated multi-step orchestration. Any flow-level logic — anything that
|
|
7
|
+
does more than "render this" or "prompt for that" — lives in `src/riplex/`
|
|
8
|
+
as a plain function that takes inputs and returns a dataclass. Both surfaces
|
|
9
|
+
then become thin callers: the CLI does `prompt_*` / `print(...)`, the GUI
|
|
10
|
+
does `ft.Control` / `state[...] = ...`.
|
|
11
|
+
|
|
12
|
+
Reference example: `src/riplex/resume.py` centralizes the resume-from-session
|
|
13
|
+
adapter. `_fetch_dvdcompare_for_resume` in
|
|
14
|
+
`src/riplex_app/screens/disc_detection.py` and the resume short-circuit in
|
|
15
|
+
`src/riplex_cli/commands/orchestrate.py` are both callers of that one
|
|
16
|
+
function; adding a new field to the resumed lookup only touches the
|
|
17
|
+
adapter (and its tests), never both surfaces.
|
|
18
|
+
|
|
19
|
+
When adding a new feature that both surfaces need, default to this
|
|
20
|
+
pattern before writing any UI code.
|
|
21
|
+
|
|
3
22
|
## Documentation changelog
|
|
4
23
|
|
|
5
24
|
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.
|
|
@@ -108,10 +127,15 @@ Run tests with `pytest` (or `python -m pytest`) from the project root with the v
|
|
|
108
127
|
|
|
109
128
|
## Debugging the GUI
|
|
110
129
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
130
|
+
**When the user reports a bug they can reproduce locally, ALWAYS check the debug log first** — before reading code, before guessing. The log usually tells you exactly which code path ran and what state it saw.
|
|
131
|
+
|
|
132
|
+
Log locations (per `platformdirs`):
|
|
133
|
+
|
|
134
|
+
- **Windows**: `%LOCALAPPDATA%\riplex\riplex\Logs\riplex_app.log`
|
|
135
|
+
- **macOS**: `~/Library/Logs/riplex/riplex_app.log`
|
|
136
|
+
- **Linux**: `~/.local/state/riplex/log/riplex_app.log`
|
|
137
|
+
|
|
138
|
+
The log is rotated on each GUI launch, so grab it before the user restarts the app. Do NOT try to capture terminal output with `2>&1` — it breaks Flet's Flutter-Python IPC.
|
|
115
139
|
|
|
116
140
|
## Releases and packaging
|
|
117
141
|
|
|
@@ -192,6 +192,12 @@ jobs:
|
|
|
192
192
|
cp artifacts/riplex-windows/riplex-ui.exe release/riplex-ui-windows.exe
|
|
193
193
|
cp artifacts/riplex-macos/riplex-macos release/
|
|
194
194
|
cp artifacts/riplex-macos/riplex-ui-macos.zip release/
|
|
195
|
+
# Publish SHA-256 checksums so the app can verify a download before
|
|
196
|
+
# applying an in-place update (see riplex.updater.stage_update).
|
|
197
|
+
cd release
|
|
198
|
+
for f in riplex-windows.exe riplex-ui-windows.exe riplex-macos riplex-ui-macos.zip; do
|
|
199
|
+
sha256sum "$f" > "$f.sha256"
|
|
200
|
+
done
|
|
195
201
|
|
|
196
202
|
- name: Create GitHub Release
|
|
197
203
|
uses: softprops/action-gh-release@v2
|
|
@@ -204,6 +210,10 @@ jobs:
|
|
|
204
210
|
body: ${{ steps.tag_notes.outputs.body }}
|
|
205
211
|
files: |
|
|
206
212
|
release/riplex-windows.exe
|
|
213
|
+
release/riplex-windows.exe.sha256
|
|
207
214
|
release/riplex-ui-windows.exe
|
|
215
|
+
release/riplex-ui-windows.exe.sha256
|
|
208
216
|
release/riplex-macos
|
|
217
|
+
release/riplex-macos.sha256
|
|
209
218
|
release/riplex-ui-macos.zip
|
|
219
|
+
release/riplex-ui-macos.zip.sha256
|
|
@@ -35,8 +35,8 @@ jobs:
|
|
|
35
35
|
- name: Create pull request
|
|
36
36
|
uses: peter-evans/create-pull-request@v6
|
|
37
37
|
with:
|
|
38
|
-
commit-message: chore: refresh CONTRIBUTORS.md
|
|
39
|
-
title: chore: refresh CONTRIBUTORS.md
|
|
38
|
+
commit-message: "chore: refresh CONTRIBUTORS.md"
|
|
39
|
+
title: "chore: refresh CONTRIBUTORS.md"
|
|
40
40
|
body: |
|
|
41
41
|
Updates the generated Bug Bashers table in CONTRIBUTORS.md from closed issue reports.
|
|
42
42
|
branch: chore/update-contributors
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: riplex
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.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
|
|
@@ -8,7 +8,7 @@ Description-Content-Type: text/markdown
|
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Requires-Dist: certifi>=2024.2.2
|
|
10
10
|
Requires-Dist: httpx>=0.27
|
|
11
|
-
Requires-Dist: dvdcompare-scraper>=0.1.
|
|
11
|
+
Requires-Dist: dvdcompare-scraper>=0.1.18
|
|
12
12
|
Requires-Dist: platformdirs>=4.0
|
|
13
13
|
Provides-Extra: dev
|
|
14
14
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
@@ -67,7 +67,7 @@ More screenshots:
|
|
|
67
67
|
[disc overview](screenshots/5_Multi_Disc_Overview_BTTF.png).
|
|
68
68
|
|
|
69
69
|
> [!NOTE]
|
|
70
|
-
> riplex
|
|
70
|
+
> riplex handles multi-work box sets and complete multi-season TV series in a single guided session. It detects the separate films (or films plus a show) inside a release, groups the discs that belong to each work, and routes every one to its own Plex destination — you can stop after any disc and resume the set later.
|
|
71
71
|
|
|
72
72
|
## Requirements
|
|
73
73
|
|
|
@@ -47,7 +47,7 @@ More screenshots:
|
|
|
47
47
|
[disc overview](screenshots/5_Multi_Disc_Overview_BTTF.png).
|
|
48
48
|
|
|
49
49
|
> [!NOTE]
|
|
50
|
-
> riplex
|
|
50
|
+
> riplex handles multi-work box sets and complete multi-season TV series in a single guided session. It detects the separate films (or films plus a show) inside a release, groups the discs that belong to each work, and routes every one to its own Plex destination — you can stop after any disc and resume the set later.
|
|
51
51
|
|
|
52
52
|
## Requirements
|
|
53
53
|
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# riplex v1.0.0
|
|
2
|
+
|
|
3
|
+
**The first stable release.** v1.0.0 consolidates riplex's box-set and TV-series
|
|
4
|
+
support, hardens episode matching, adds a comprehensive automated test suite so
|
|
5
|
+
the flows that matter stay working as the project grows, and makes upgrades
|
|
6
|
+
painless with one-click in-place updates on Windows.
|
|
7
|
+
|
|
8
|
+
Until now every disc was treated as a standalone rip, and updating meant
|
|
9
|
+
re-downloading the app each release. This release lets riplex understand a whole
|
|
10
|
+
box set at once — detecting the *works* inside a release, grouping the discs that
|
|
11
|
+
belong to each, and routing every work to its correct Plex destination in a
|
|
12
|
+
single guided session — and then keeps itself up to date automatically.
|
|
13
|
+
|
|
14
|
+
## Why 1.0.0?
|
|
15
|
+
|
|
16
|
+
riplex has been usable for a while; three things make this the right moment to
|
|
17
|
+
call it stable:
|
|
18
|
+
|
|
19
|
+
- **The hard workflows are done.** Multi-work box sets and complete multi-season
|
|
20
|
+
TV series — the cases that used to mean ripping disc-by-disc and sorting by
|
|
21
|
+
hand — now work end to end, in both the desktop app and the CLI.
|
|
22
|
+
- **It's protected against regressions.** A headless integration suite exercises
|
|
23
|
+
the entire GUI wizard and the CLI against real-world scenarios generated from
|
|
24
|
+
archived rips, so the flows you depend on keep working as development continues.
|
|
25
|
+
- **Updates are effortless.** In-place updates mean you install once and never
|
|
26
|
+
have to re-download and re-approve the app for each new version.
|
|
27
|
+
|
|
28
|
+
## Headline features
|
|
29
|
+
|
|
30
|
+
### Multi-work box sets
|
|
31
|
+
|
|
32
|
+
A release that contains several distinct works — several films, or films plus a
|
|
33
|
+
TV series — is detected as *multiple works* instead of one. Each work gets its
|
|
34
|
+
own title selection, its own rip grouping, and its own Plex target, so a mixed
|
|
35
|
+
box set is ripped and filed correctly in one pass:
|
|
36
|
+
|
|
37
|
+
• Disc grouping splits on dvdcompare's per-film hyperlinks (`pointer_fid`) rather than a coarse "is this a film" guess, so a bonus-films disc forms its own group and the main features stay separate.
|
|
38
|
+
• The Select Titles screen shows each work's rip output separately, so you can see exactly which files belong to which movie or show.
|
|
39
|
+
• Full CLI parity: `riplex orchestrate` and `riplex organize` route multi-work releases the same way the GUI does, through the shared flow logic.
|
|
40
|
+
|
|
41
|
+
### Multi-season TV series
|
|
42
|
+
|
|
43
|
+
Complete-series sets that span several seasons are ripped season by season:
|
|
44
|
+
|
|
45
|
+
• A **Season Select** screen (GUI) and season prompt (CLI) assign each disc to its season.
|
|
46
|
+
• TV rips nest automatically under `Season NN/`, and specials land in `Season 00`.
|
|
47
|
+
• Season labels and chips appear throughout the Disc Overview, so a 12-disc set is easy to keep track of.
|
|
48
|
+
|
|
49
|
+
### Guided resume across a whole set
|
|
50
|
+
|
|
51
|
+
Every session writes a `_riplex_session.json` marker capturing the full plan, so
|
|
52
|
+
you can rip a few discs, come back later, and pick up exactly where you left off:
|
|
53
|
+
|
|
54
|
+
• Resume from *any* disc of a multi-work or multi-season set — not just the next in sequence.
|
|
55
|
+
• `riplex orchestrate` (CLI) resumes from the marker with GUI parity, via a shared `resume.py` adapter.
|
|
56
|
+
• `riplex organize` discovers the marker and fans out across every work in the set.
|
|
57
|
+
|
|
58
|
+
### Metadata persisted per rip
|
|
59
|
+
|
|
60
|
+
Rip manifests record the exact TMDb and dvdcompare ids you confirmed at rip time.
|
|
61
|
+
When you organize (or resume) a ripped disc, riplex reuses those ids and skips
|
|
62
|
+
the metadata pickers entirely — no re-searching, no risk of picking a different
|
|
63
|
+
release the second time.
|
|
64
|
+
|
|
65
|
+
### Smarter, more resilient TV episode matching
|
|
66
|
+
|
|
67
|
+
Matching ripped titles to episodes used to be a runtime guessing game that falls
|
|
68
|
+
apart when episodes run within seconds of each other — or when a metadata
|
|
69
|
+
listing is simply wrong. Now:
|
|
70
|
+
|
|
71
|
+
• dvdcompare's episode listing is cross-referenced against the TMDb episode list.
|
|
72
|
+
• When a disc's episode-length titles line up 1:1 with the episode list, they're assigned by **disc position** rather than by runtime, so a single wrong dvdcompare runtime can't orphan an episode or let a same-runtime neighbour steal its slot.
|
|
73
|
+
• Organize honors the rip-time season/episode classification and no longer collides identically-named files across different discs.
|
|
74
|
+
• Over-length titles are labeled **Unmatched content** and stay visible instead of being forced into the wrong slot.
|
|
75
|
+
|
|
76
|
+
### Seamless in-place updates (Windows)
|
|
77
|
+
|
|
78
|
+
When a new version is available, the update screen offers **Update & Restart**:
|
|
79
|
+
riplex downloads the new build, **verifies its SHA-256 checksum**, swaps the
|
|
80
|
+
running `.exe` in place, and relaunches — no re-downloading and no repeated
|
|
81
|
+
SmartScreen approval. It only runs from a writable install folder (otherwise it
|
|
82
|
+
falls back to the browser download), and every release now publishes `.sha256`
|
|
83
|
+
checksums for its assets.
|
|
84
|
+
|
|
85
|
+
> Note: this applies going forward. Upgrading *to* v1.0.0 from an older build
|
|
86
|
+
> still uses the browser download (the old build doesn't know how to self-update);
|
|
87
|
+
> from v1.0.0 onward, updates are one click.
|
|
88
|
+
|
|
89
|
+
### Auto-eject after ripping
|
|
90
|
+
|
|
91
|
+
The disc ejects automatically once a rip finishes, so you can swap discs — or
|
|
92
|
+
just know it's done — without reaching for the drive. On by default; set
|
|
93
|
+
`auto_eject = false` in the config to disable.
|
|
94
|
+
|
|
95
|
+
## Reliability & testing
|
|
96
|
+
|
|
97
|
+
v1.0.0 adds a substantial automated safety net so future changes don't quietly
|
|
98
|
+
break the flows you rely on:
|
|
99
|
+
|
|
100
|
+
• **Headless GUI integration tests** drive the full desktop wizard through mocked disc / TMDb / dvdcompare scenarios, asserting every screen renders and hands off correctly without touching a real drive or the network.
|
|
101
|
+
• **CLI integration tests** run the real argument parser and command dispatch for `organize` and `lookup`.
|
|
102
|
+
• **Real-world fixtures** are generated from archived rips and classified by media type (movie, mini-series, series), so new rips can grow the protected-scenario set over time.
|
|
103
|
+
• Cancelling a rip now returns to the **current** disc's Insert Disc screen (retry / skip / eject) instead of skipping ahead, and doesn't mark the disc as ripped.
|
|
104
|
+
• Duplicate-title extras no longer clobber their episode's destination during organize.
|
|
105
|
+
|
|
106
|
+
## Minor improvements
|
|
107
|
+
|
|
108
|
+
• "Currently loaded" disc dropdown on the Disc Overview for quickly switching context.
|
|
109
|
+
• "Organize into Library" shortcut appears once every disc in the set has been ripped.
|
|
110
|
+
• Hidden-discs banner explains any discs the plan intentionally skips, and the primary-work slot shows its runtime.
|
|
111
|
+
• "View on dvdcompare.net" link shown once a release has been matched.
|
|
112
|
+
• Interactive title-selection editor at the CLI Proceed prompt, so you can adjust picks before the rip starts.
|
|
113
|
+
• Every `orchestrate` disc routes through an Insert Disc scan-confirm step for consistent behavior.
|
|
114
|
+
• Ctrl-C exits cleanly (exit code 130, no traceback, no orphaned `makemkvcon` process).
|
|
115
|
+
|
|
116
|
+
## Bug fixes
|
|
117
|
+
|
|
118
|
+
• Duplicate **Quit** buttons on the rip-complete summary and Insert Disc screens.
|
|
119
|
+
• False "multiple films" alert on Select Titles, with a confirmed movie title and clearer section headers.
|
|
120
|
+
• Ctrl-C at a prompt no longer behaves like pressing Enter.
|
|
121
|
+
• Movie picks filter to just the movie disc(s), on both fresh and resumed sessions.
|
|
122
|
+
• Organize fuzzy-matches dvdcompare episode titles against TMDb, and reads title/season from the rip manifest for season-nested output.
|
|
123
|
+
• Organize no longer crashes after a resumed rip.
|
|
124
|
+
• Organize Rips scan-results footer anchors to the bottom, and its Back button walks up the flow instead of dropping to Welcome.
|
|
125
|
+
• `detect_disc_format` recognizes standard-definition DVDs.
|
|
126
|
+
• Linked-film autofill strips dvdcompare format markers, and the main feature is no longer misclassified as a play-all title.
|
|
127
|
+
• The dvdcompare cache auto-invalidates when the scraper version changes, and auto-lookup no longer picks the wrong franchise.
|
|
128
|
+
|
|
129
|
+
## ⚠️ Breaking change
|
|
130
|
+
|
|
131
|
+
Disc-group ids changed from the old `main_1` / `film_31` scheme to
|
|
132
|
+
`disc_1` / `discs_1_4` / `disc_31`. A session saved by an earlier version will
|
|
133
|
+
not resume — **start a fresh session after upgrading**.
|
|
134
|
+
|
|
135
|
+
## Full changelog
|
|
136
|
+
|
|
137
|
+
See [docs/changelog.md](https://github.com/AnyCredit5518/riplex/blob/v1.0.0/docs/changelog.md)
|
|
138
|
+
for the per-section breakdown, and the auto-generated commit list below for
|
|
139
|
+
every commit since v0.9.8.
|
|
@@ -77,7 +77,10 @@ src/
|
|
|
77
77
|
organize_done.py # Organize results summary
|
|
78
78
|
tests/
|
|
79
79
|
test_*.py # One test file per source module
|
|
80
|
+
integration/ # Headless GUI flow tests (see Development > Testing)
|
|
81
|
+
support/ # Integration harness: fake page, driver, provider mocks
|
|
80
82
|
fixtures/ # makemkvcon output samples for parsing tests
|
|
83
|
+
gui/scenarios/ # Committed GUI scenarios (generated from archived rips)
|
|
81
84
|
snapshots/ # MKV metadata snapshots for offline test replay
|
|
82
85
|
```
|
|
83
86
|
|
|
@@ -4,6 +4,98 @@ 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
|
+
## Unreleased
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **TV episodes with parenthetical titles (or filed under extras) no longer land in `Other/`.** When organizing a TV rip, the destination is now chosen from the authoritative `SxxEyy` classification recorded in each disc's rip manifest at rip time, taking priority over the re-derived dvdcompare match label. Previously an episode whose title contains a parenthetical (e.g. *Shawn and Gus in Drag (Racing)*), or one that dvdcompare files under a disc's extras (e.g. an *(Extended Version)* listing such as *Romeo and Juliet and Juliet* or *Dual Spires*), was misread as an extra and dropped into `Other/` instead of its season folder — even though orchestrate had already classified it correctly (e.g. Psych S5 E01/E05/E12).
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **Durations on the organize preview.** Each matched file now shows its runtime next to the matched episode/target's expected runtime; a divergence of more than a minute is highlighted so an incorrect match is easy to spot before you execute.
|
|
16
|
+
|
|
17
|
+
## v1.0.1 — 2026-07-20
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **Out-of-order episode matching.** When dvdcompare files an “(Extended Version)” of an episode under a disc's *extras*, TMDb enrichment promotes it to an episode but leaves it positioned after the real episode list, so the episode entries arrive out of broadcast order. Positional episode alignment now sorts episodes by their canonical `SxxEyy` number before matching disc titles, so a disc whose titles are in broadcast order maps correctly. Previously the misordered list could orphan a title to a generic “Episode” label and shift the following episodes by one (e.g. Psych S6 D3).
|
|
22
|
+
|
|
23
|
+
## v1.0.0 — 2026-07-18
|
|
24
|
+
|
|
25
|
+
Summary: the first stable release. v1.0.0 brings full multi-work and
|
|
26
|
+
multi-season box-set support, hardens TV episode matching, adds a comprehensive
|
|
27
|
+
headless GUI + CLI integration test suite to guard against regressions, and
|
|
28
|
+
introduces one-click in-place updates on Windows so future upgrades are
|
|
29
|
+
seamless. A box set that bundles several films, or a complete multi-season TV
|
|
30
|
+
series, can be ripped and organized in a single guided session, with each work
|
|
31
|
+
routed to its correct Plex destination.
|
|
32
|
+
|
|
33
|
+
### Highlights
|
|
34
|
+
|
|
35
|
+
- **Multi-work box sets.** A single release containing several distinct films (or films plus a TV series) is now detected as multiple works. Each work gets its own title selection, rip grouping, and Plex target, so a mixed box set no longer has to be ripped one disc at a time and sorted by hand.
|
|
36
|
+
- **Multi-season TV.** Complete-series sets spanning multiple seasons are ripped season by season. A new Season Select screen (GUI) and season prompt (CLI) assign each disc to a season, and rips nest under `Season NN/` automatically.
|
|
37
|
+
- **Guided resume.** A session marker (`_riplex_session.json`) records the whole plan, so you can stop after any disc and resume later — from any disc of the set — without re-picking metadata.
|
|
38
|
+
- **Metadata persisted per rip.** Rip manifests now store the chosen TMDb and dvdcompare ids, so organizing (or resuming) a ripped disc skips the pickers and reuses the exact match you confirmed at rip time.
|
|
39
|
+
- **Smarter TV matching.** dvdcompare episode listings are cross-referenced against the TMDb episode list, and episodes are assigned deterministically from their rip-time classification instead of guessing between near-identical runtimes.
|
|
40
|
+
- **Seamless updates.** On Windows, the app can download, verify, and install its own updates in place and relaunch — no more re-downloading the exe and clearing SmartScreen every release.
|
|
41
|
+
- **Regression-proofed for 1.0.** A headless integration suite drives the full GUI wizard and the CLI through mocked disc/TMDb/dvdcompare scenarios (generated from real archived rips), so the flows that matter stay protected as the project grows.
|
|
42
|
+
|
|
43
|
+
### ⚠️ Breaking
|
|
44
|
+
|
|
45
|
+
- **Disc-group ids changed** from the old `main_1` / `film_31` scheme to `disc_1` / `discs_1_4` / `disc_31`. A session saved by an earlier version will not resume — start a fresh session after upgrading.
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
|
|
49
|
+
- Multi-work release routing: per-work title selection, rip groups, and Plex destinations, with full CLI parity for `organize` and `orchestrate`.
|
|
50
|
+
- Season Select screen (GUI) and season prompt (CLI) for assigning discs to seasons in multi-season sets.
|
|
51
|
+
- Interactive title-selection editor at the CLI Proceed prompt, so picks can be adjusted before the rip starts.
|
|
52
|
+
- Per-group rip output on the Select Titles screen, showing each work's files separately.
|
|
53
|
+
- Multi-work session marker (`_riplex_session.json`) enabling resume from any disc of a set.
|
|
54
|
+
- Rip manifests now record the confirmed TMDb and dvdcompare ids to skip the pickers on organize and resume.
|
|
55
|
+
- Season labels and chips on the Disc Overview, plus a "Currently loaded" disc dropdown.
|
|
56
|
+
- "Organize into Library" shortcut on the Disc Overview once every disc has been ripped.
|
|
57
|
+
- Hidden-discs banner explaining discs the plan intentionally skips.
|
|
58
|
+
- "View on dvdcompare.net" link when a release has already been matched.
|
|
59
|
+
- **Development > Testing guide** (`development/testing.md`) documenting the headless GUI and CLI integration test suites, the `tests/support/` harness, the `gui` fixture, media-type-targeted flows, and how to generate scenario fixtures from archived rips with `scripts/gen_gui_fixtures.py`.
|
|
60
|
+
- **GUI auto-eject.** The disc is ejected automatically after a rip finishes so you can swap discs (or know it's done) without reaching for the drive. On by default; set `auto_eject = false` in the config to disable.
|
|
61
|
+
- **GUI in-place update (Windows).** The update screen now offers **Update & Restart**: riplex downloads the new build, verifies its SHA-256 checksum, swaps the running `.exe` in place, and relaunches — no manual re-download or repeated SmartScreen approval. Requires a writable install folder; falls back to the browser download otherwise. Releases now publish `.sha256` checksums for every asset.
|
|
62
|
+
|
|
63
|
+
### Changed
|
|
64
|
+
|
|
65
|
+
- CLI `orchestrate` now resumes from the session marker (GUI parity) via the shared `resume.py` adapter.
|
|
66
|
+
- Disc grouping splits on dvdcompare hyperlinks (`pointer_fid`) rather than `is_film`, so a bonus-films disc forms its own group.
|
|
67
|
+
- `DiscGroup.kind` removed; group labels and ids simplified.
|
|
68
|
+
- TV rips nest under `Season NN/` and cross-reference dvdcompare features against the TMDb episode list.
|
|
69
|
+
- Ctrl-C now exits cleanly (exit code 130, no traceback, no orphaned `makemkvcon` process).
|
|
70
|
+
- Softer disc-mismatch wording, and the release picker shows the matched dvdcompare film.
|
|
71
|
+
- `_plan_show` keeps Season 0 (Specials); the Organize preview shows a title/season source badge.
|
|
72
|
+
- Every `orchestrate` disc now routes through an Insert Disc scan-confirm step.
|
|
73
|
+
- **Architecture** file tree now lists `tests/integration/`, `tests/support/`, and `tests/fixtures/gui/scenarios/`.
|
|
74
|
+
- Home page and docs index/README refreshed for the 1.0.0 feature set (multi-work box sets, multi-season TV, in-place updates, auto-eject).
|
|
75
|
+
|
|
76
|
+
### Fixed
|
|
77
|
+
|
|
78
|
+
- **TV: a single wrong dvdcompare episode runtime no longer mislabels two episodes.** When a disc's episode-length titles line up 1:1 with the dvdcompare episode list, riplex now assigns them by disc position instead of purely by runtime. Previously a bad listed runtime (e.g. Psych S6 D3 listing "Heeeeere's Lassie" at 43:10 when the disc title is 49:41) orphaned that episode to a generic "Episode" label and let a same-runtime neighbour steal its slot. Positional alignment fixes both; it only applies on an exact 1:1 count match with at most one runtime outlier, and defers to runtime matching on ragged or reordered discs.
|
|
79
|
+
- **GUI: cancelling a rip returns to the current disc, not the next one.** In an orchestrate session, stopping a rip mid-disc took you to the *next* disc's Insert Disc screen. It now returns to the current disc's Insert Disc screen so you can retry, skip, or eject — and the cancelled disc is no longer marked as ripped (no manifest written) or auto-ejected.
|
|
80
|
+
- **GUI: Organize Rips scan-results footer now anchors to the bottom** like every other wizard screen. The results form now scrolls internally while the Back/Next buttons stay pinned at the window edge.
|
|
81
|
+
- **GUI: Organize Rips Back button walks up the flow** instead of dropping to the Welcome screen. On the scan-results view it restores the multi-group picker (when the folder held several seasons or works) or otherwise returns to the folder input; the multi-group picker's Back button now returns to the folder input as well.
|
|
82
|
+
- **Organize: duplicate-title extras no longer clobber their episode's destination.** dvdcompare sometimes lists the same episode name twice (real broadcast episode + a shorter bonus re-edit). The rip-time enrichment already demotes the shorter entry to ``[extra]``, but the organizer was ignoring that tag and fuzzy-routing both files to the same ``s0Xe0Y - Title.mkv`` destination — so the second file silently overwrote the first when organize actually executed. The organizer now respects the ``[extra]`` classification and routes the duplicate to an extras folder instead.
|
|
83
|
+
- Duplicate Quit buttons on the rip-complete summary and Insert Disc screens.
|
|
84
|
+
- False "multiple films" alert on Select Titles, with a confirmed movie title and clearer section headers.
|
|
85
|
+
- Ctrl-C at a prompt no longer behaves like pressing Enter.
|
|
86
|
+
- Movie picks now filter to just the movie disc(s), on both fresh and resumed sessions.
|
|
87
|
+
- The primary-work film slot shows its runtime, and hidden discs are explained.
|
|
88
|
+
- Organize honors the rip-time season/episode classification and no longer collides same-basename files across discs.
|
|
89
|
+
- Organize fuzzy-matches dvdcompare episode titles against TMDb, and reads title/season from the rip manifest for season-nested output.
|
|
90
|
+
- Organize no longer crashes after a resumed rip.
|
|
91
|
+
- TV episodes are assigned sequentially (first-fit); over-length titles are labeled "Unmatched content".
|
|
92
|
+
- The Disc Release screen shows the dvdcompare film title as its heading.
|
|
93
|
+
- `detect_disc_format` recognizes standard-definition DVDs.
|
|
94
|
+
- `riplex organize` discovers the session marker and fans out across all works.
|
|
95
|
+
- Resume works from any disc of a multi-work release, and season chips show on leading discs on resume.
|
|
96
|
+
- Linked-film autofill strips dvdcompare format markers, and the main feature is no longer misclassified as a play-all title.
|
|
97
|
+
- The dvdcompare cache auto-invalidates when the scraper version changes, and auto-lookup no longer picks the wrong franchise.
|
|
98
|
+
|
|
7
99
|
## v0.9.2 — 2026-06-24
|
|
8
100
|
|
|
9
101
|
### Fixed
|
|
@@ -29,8 +29,10 @@ In interactive mode, orchestrate presents prompts at each decision point:
|
|
|
29
29
|
|
|
30
30
|
- **Title confirmation**: auto-detected title shown for confirmation or correction
|
|
31
31
|
- **TMDb disambiguation**: if multiple matches, pick the correct one
|
|
32
|
+
- **Season selection** (TV multi-season shows only): when the show has more than one season on TMDb, pick which season is on the disc. The picked season is used both to narrow the plan and to bias the dvdcompare query (e.g. `Psych: Season 2`) so it lands on the correct per-season film page. Season 0 (Specials) is never shown in the picker but is always retained in the plan so extras on the disc that match a curated TMDb special can still route to `Season 00/`. Mini-series (only one non-special season, e.g. *Planet Earth II*) skip this prompt entirely.
|
|
32
33
|
- **dvdcompare release selection**: choose the regional release (default: America)
|
|
33
34
|
- **Disc selection**: pick which discs to rip (skip standard Blu-ray copies, bonus discs, etc.)
|
|
35
|
+
- **Title selection**: after the per-disc analysis, the `Proceed? [Y/n/e(dit)]` prompt accepts `e` to open an interactive picker that lets you toggle titles by index, range (`3-7`), or list (`1,4,6`); type `all`, `none`, `default` to reset, or `done` / Enter to accept. This is the CLI equivalent of the GUI selection screen.
|
|
34
36
|
- **Disc swap**: after each disc, prompted to insert the next one
|
|
35
37
|
- **Archive**: after organize, prompted to archive the rip folder (if `archive_root` is configured)
|
|
36
38
|
|
|
@@ -34,16 +34,28 @@ riplex organize "path/to/rips/PLANET EARTH II" --type tv --format "Blu-ray 4K"
|
|
|
34
34
|
|
|
35
35
|
Multi-disc rips in separate folders (e.g. "Planet Earth III - Disc 1", "Planet Earth III - Disc 2") are automatically grouped into a single title.
|
|
36
36
|
|
|
37
|
+
## Multi-work releases (TV series + bonus films)
|
|
38
|
+
|
|
39
|
+
Some releases bundle more than one work — a classic example is *Psych: The Complete Series*, with the eight-season show on discs 1-30 and three standalone TV-movies on disc 31. `organize` detects this and splits the release into groups, planning each group against its own TMDb match:
|
|
40
|
+
|
|
41
|
+
- The main-content discs organize into the TV show folder using the top-level match you searched for.
|
|
42
|
+
- Each film disc's slot gets a best-guess TMDb match auto-filled from its dvdcompare title.
|
|
43
|
+
- In interactive mode you'll be prompted per slot to accept the auto-fill, search for a different match, or skip the slot (its files stay unmatched).
|
|
44
|
+
- In non-interactive mode (piped output, CI) auto-fills stand and any unresolved slot is reported as `SKIPPED (no TMDb match assigned)` in the group summary.
|
|
45
|
+
|
|
46
|
+
The dry-run preview prints one line per group so you can verify the routing before running with `--execute`.
|
|
47
|
+
|
|
37
48
|
## Chapter splitting
|
|
38
49
|
|
|
39
50
|
When the scanner detects that a file has chapter markers matching the number of TMDb Season 00 episodes, the tool automatically plans a chapter split instead of a single move. With `--execute`, this uses mkvmerge to split the file by chapters and moves each piece to the correct Season 00 location.
|
|
40
51
|
|
|
41
52
|
## Regional release selection
|
|
42
53
|
|
|
43
|
-
dvdcompare lists multiple regional releases. In interactive mode (the default when running in a terminal), you will be presented with all available releases to choose from:
|
|
54
|
+
dvdcompare lists multiple regional releases. In interactive mode (the default when running in a terminal), you will be presented with all available releases to choose from. The line above the picker shows which dvdcompare film page was matched so you can spot a wrong pick (e.g. a season 1 page when the disc is season 2) before choosing a region:
|
|
44
55
|
|
|
45
56
|
```
|
|
46
|
-
|
|
57
|
+
Matched dvdcompare film: Oppenheimer (2023) [film #12345]
|
|
58
|
+
Select a dvdcompare release for Oppenheimer:
|
|
47
59
|
1. North America (4K Ultra HD) [4 discs] *
|
|
48
60
|
2. United Kingdom (4K Ultra HD) [4 discs]
|
|
49
61
|
3. Germany (4K Ultra HD) [3 discs]
|
|
@@ -83,7 +83,9 @@ This will:
|
|
|
83
83
|
5. Show a disc analysis table with rip recommendations
|
|
84
84
|
6. Prompt for confirmation, then rip the selected titles
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
At the final `Proceed? [Y/n/e(dit)]` prompt, press `e` to open an interactive title picker. The picker shows every title on the disc with `[x]`/`[ ]` marks reflecting the current selection and lets you toggle by index (`3`), comma-separated list (`1,4`), or range (`3-7`). Type `all` or `none` to select or clear everything, `default` to restore the analyzer's recommendation, `done` (or Enter) to accept, or `cancel` to abort the edit. This is the CLI equivalent of the GUI selection screen, so you no longer need to abort and re-run with `--titles 1,3,5` to fix a wrong recommendation.
|
|
87
|
+
|
|
88
|
+
Add `--yes` to skip the final rip confirmation prompt (and the edit picker with it). Use `--auto` to also skip all interactive selection prompts (title, TMDb, release, season, edit). Use `--titles 1,2,3` to override the auto-selection non-interactively, or `--all` to rip everything. Add `--organize` to automatically run the organize step after ripping.
|
|
87
89
|
|
|
88
90
|
Repeat for each disc in the set, swapping discs between runs.
|
|
89
91
|
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Testing
|
|
2
|
+
|
|
3
|
+
riplex uses `pytest`. Run the whole suite from the project root with the venv
|
|
4
|
+
active:
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
pytest
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
The suite has two layers:
|
|
11
|
+
|
|
12
|
+
- **Unit tests** (`tests/test_*.py`) — one file per source module, covering
|
|
13
|
+
parsing, planning, matching, and per-screen handler logic in isolation.
|
|
14
|
+
- **Integration tests** (`tests/integration/`) — headless end-to-end flows that
|
|
15
|
+
drive the GUI wizard and the CLI through mocked scenarios to catch regressions
|
|
16
|
+
in how the pieces fit together.
|
|
17
|
+
|
|
18
|
+
To run only the integration flows (or skip them):
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
pytest -m integration
|
|
22
|
+
pytest -m "not integration"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## GUI integration tests
|
|
26
|
+
|
|
27
|
+
The integration suite builds a real `RiplexApp` on a fake page and clicks
|
|
28
|
+
through the wizard the way a user would — but with every external system
|
|
29
|
+
(makemkv, TMDb, dvdcompare, config, network) replaced by fakes. This means a
|
|
30
|
+
whole flow (welcome → disc detection → metadata → release → disc overview →
|
|
31
|
+
rip → done) runs in milliseconds with no disc, no network, and no crash dialog.
|
|
32
|
+
|
|
33
|
+
The goal is to catch the most common regression class: a screen that throws
|
|
34
|
+
while rendering because an upstream change altered the shape of the state it
|
|
35
|
+
reads.
|
|
36
|
+
|
|
37
|
+
### Harness (`tests/support/`)
|
|
38
|
+
|
|
39
|
+
| Module | Responsibility |
|
|
40
|
+
| ------ | -------------- |
|
|
41
|
+
| `fake_page.py` | `FakePage`, a stand-in for `ft.Page` implementing only the surface the screens touch, plus control-tree walking helpers. |
|
|
42
|
+
| `sync_runtime.py` | Makes background threads and `page.run_task` run synchronously so flows are deterministic (no sleeps, no polling loops). |
|
|
43
|
+
| `fixtures.py` | Loads a scenario JSON and reconstructs the **real** riplex dataclasses (`DiscInfo`, `MetadataSearchResult`, `PlannedDisc`, …). |
|
|
44
|
+
| `provider_mocks.py` | Installs scenario-driven fakes for makemkv, TMDb, dvdcompare, config, and network in one call. |
|
|
45
|
+
| `driver.py` | `WizardDriver` — `click(label)`, `has_text(...)`, `current`, `state`, `crashed()`. |
|
|
46
|
+
|
|
47
|
+
The `gui` pytest fixture (in `tests/conftest.py`) wires it all together:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
def test_movie_flow(gui):
|
|
51
|
+
d = gui("the-matrix-1999") # load a scenario, mock everything
|
|
52
|
+
d.click("Rip Disc") # welcome -> disc detection -> metadata
|
|
53
|
+
d.click("Next") # accept the TMDb match
|
|
54
|
+
assert d.current == "disc_overview"
|
|
55
|
+
assert not d.crashed()
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Failure injection to prove the net works: adding a `raise` to any screen's
|
|
59
|
+
`build()` fails both the relevant flow test and the screen-build smoke matrix.
|
|
60
|
+
|
|
61
|
+
### What is covered
|
|
62
|
+
|
|
63
|
+
- **Screen-build smoke matrix** (`test_screen_build_smoke.py`) — builds *every*
|
|
64
|
+
screen with a fully-populated state for one representative scenario per media
|
|
65
|
+
type (plus a no-dvdcompare edge case).
|
|
66
|
+
- **Media-type flow tests** (`test_flow_media_types.py`) — drive *every* fixture
|
|
67
|
+
of a given media type through the core orchestrate path, so the whole fixture
|
|
68
|
+
corpus is exercised through the real screens.
|
|
69
|
+
- **Named flow tests** — movie orchestrate, TV multi-disc, organize,
|
|
70
|
+
resume/prefill fast paths, and error paths (makemkv unavailable, TMDb
|
|
71
|
+
empty/raises, dvdcompare failure, rip failure).
|
|
72
|
+
- **Fixture integrity** — every committed scenario reconstructs into real
|
|
73
|
+
dataclasses and carries a valid media-type category.
|
|
74
|
+
|
|
75
|
+
### Targeting media types
|
|
76
|
+
|
|
77
|
+
Each scenario is classified into one media-type **category** so a test can run
|
|
78
|
+
against exactly the fixtures it applies to — and automatically pick up new
|
|
79
|
+
fixtures of that type as they're generated:
|
|
80
|
+
|
|
81
|
+
| Category | Meaning |
|
|
82
|
+
| -------- | ------- |
|
|
83
|
+
| `movie` | A theatrical / feature film release. |
|
|
84
|
+
| `tv_miniseries` | A limited, self-contained series (e.g. Chernobyl). |
|
|
85
|
+
| `tv_series` | An ongoing multi-season show — a single-season rip *or* a complete-series set. |
|
|
86
|
+
|
|
87
|
+
A single season of an ongoing show (e.g. a `Season 01` folder) is **series
|
|
88
|
+
content**, so it classifies as `tv_series` and flows through the series tests.
|
|
89
|
+
"Seasonal series" isn't a separate category; use `season_scenarios()` to target
|
|
90
|
+
just the single-season rips.
|
|
91
|
+
|
|
92
|
+
The category is inferred from the archive folder name, dvdcompare release name,
|
|
93
|
+
and TMDb season structure; a scenario can also pin it with a top-level
|
|
94
|
+
`"category"` key. `tests/support/fixtures.py` exposes filter helpers that return
|
|
95
|
+
the matching scenario names for parametrization:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from tests.support.fixtures import movie_scenarios, series_scenarios, season_scenarios
|
|
99
|
+
|
|
100
|
+
@pytest.mark.parametrize("name", series_scenarios()) # every TV series fixture
|
|
101
|
+
def test_series_flow(gui, name):
|
|
102
|
+
d = gui(name)
|
|
103
|
+
...
|
|
104
|
+
|
|
105
|
+
@pytest.mark.parametrize("name", season_scenarios()) # just single-season rips
|
|
106
|
+
def test_seasonal_flow(gui, name):
|
|
107
|
+
...
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Adding a new fixture of a category makes it flow through that category's tests
|
|
111
|
+
on the next run — no test edits required.
|
|
112
|
+
|
|
113
|
+
## CLI integration tests
|
|
114
|
+
|
|
115
|
+
The CLI is exercised end-to-end the same way: `tests/support/cli.py` runs the
|
|
116
|
+
real argparse parser and async command dispatch (`_run`) in-process, so tests
|
|
117
|
+
cover argument parsing, command routing, and command logic together. External
|
|
118
|
+
boundaries (config, TMDb, dvdcompare) are stubbed via monkeypatch.
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from tests.support.cli import install_cli_mocks, run_command
|
|
122
|
+
|
|
123
|
+
def test_lookup(monkeypatch, capsys):
|
|
124
|
+
install_cli_mocks(monkeypatch) # offline config + providers
|
|
125
|
+
... # mock the lookup seam
|
|
126
|
+
code = run_command(["lookup", "The Matrix"])
|
|
127
|
+
assert code == 0
|
|
128
|
+
assert "The Matrix" in capsys.readouterr().out
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Covered: parser/dispatch (help on no command, `--version`, unknown command,
|
|
132
|
+
every subcommand parses), `organize` dry-run against a committed scan snapshot
|
|
133
|
+
plus its guard rails (`--execute` rejected with `--snapshot`, missing snapshot,
|
|
134
|
+
non-directory folder), and `lookup` (rip guide, graceful dvdcompare failure,
|
|
135
|
+
`--json` output). The `organize --snapshot` path replays a real
|
|
136
|
+
`tests/snapshots/*.snapshot.json` so no ffprobe or live files are needed.
|
|
137
|
+
|
|
138
|
+
## Test fixtures from archived rips
|
|
139
|
+
|
|
140
|
+
Integration scenarios live as committed JSON under
|
|
141
|
+
`tests/fixtures/gui/scenarios/`. They share one schema between hand-authored
|
|
142
|
+
edge cases and scenarios generated from real archived rips. The loader reads
|
|
143
|
+
only the committed JSON, so the test suite never needs the archive and CI stays
|
|
144
|
+
hermetic.
|
|
145
|
+
|
|
146
|
+
### Generating scenarios
|
|
147
|
+
|
|
148
|
+
Archived rips carry everything a scenario needs — the disc titles makemkv
|
|
149
|
+
reported, the confirmed TMDb match, and the dvdcompare release breakdown — in
|
|
150
|
+
their debug artifacts:
|
|
151
|
+
|
|
152
|
+
- `<Title (Year)>/_riplex/riplex-rip.snapshot.json` — disc titles, TMDb match,
|
|
153
|
+
dvdcompare disc breakdown.
|
|
154
|
+
- `<Title (Year)>/Disc N/_rip_manifest.json` — richer per-title stream info plus
|
|
155
|
+
the exact dvdcompare release name, disc format, and volume label (when
|
|
156
|
+
present).
|
|
157
|
+
|
|
158
|
+
`scripts/gen_gui_fixtures.py` walks an archive root, normalizes each title
|
|
159
|
+
folder into a scenario JSON, and writes it under `tests/fixtures/gui/scenarios/`:
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
# List archived folders (a * marks ones with a usable snapshot)
|
|
163
|
+
python scripts/gen_gui_fixtures.py --list
|
|
164
|
+
|
|
165
|
+
# Generate all scenarios
|
|
166
|
+
python scripts/gen_gui_fixtures.py
|
|
167
|
+
|
|
168
|
+
# Generate just one, from a custom archive path
|
|
169
|
+
python scripts/gen_gui_fixtures.py --only "Chernobyl (2019)" \
|
|
170
|
+
--archive "/path/to/_archive"
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Missing pieces (a TMDb id, per-episode lists on older snapshots, TMDb season
|
|
174
|
+
structure) are synthesized deterministically and listed under a `synthesized`
|
|
175
|
+
key in the output so consumers know which blocks are inferred rather than
|
|
176
|
+
observed. Each generated scenario also carries a `category` (see
|
|
177
|
+
[Targeting media types](#targeting-media-types)), which you can override by
|
|
178
|
+
hand-editing the field.
|
|
179
|
+
|
|
180
|
+
Re-run the generator and commit the resulting JSON whenever you want to refresh
|
|
181
|
+
or add real-world scenarios. The archive itself is never required to run the
|
|
182
|
+
tests.
|