riplex 0.9.7__tar.gz → 0.9.9__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/.github/contributors-overrides.json +45 -0
- riplex-0.9.9/.github/workflows/update-contributors.yml +43 -0
- riplex-0.9.9/CONTRIBUTORS.md +23 -0
- {riplex-0.9.7/src/riplex.egg-info → riplex-0.9.9}/PKG-INFO +2 -2
- {riplex-0.9.7 → riplex-0.9.9}/issues/planned-features.md +28 -12
- {riplex-0.9.7 → riplex-0.9.9}/pyproject.toml +1 -1
- riplex-0.9.9/scripts/__init__.py +1 -0
- riplex-0.9.9/scripts/update_contributors.py +365 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/disc/provider.py +8 -6
- {riplex-0.9.7 → riplex-0.9.9/src/riplex.egg-info}/PKG-INFO +2 -2
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex.egg-info/SOURCES.txt +5 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex.egg-info/requires.txt +1 -1
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex.egg-info/scm_file_list.json +6 -1
- riplex-0.9.9/src/riplex.egg-info/scm_version.json +8 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/screens/progress.py +20 -4
- riplex-0.9.9/tests/test_update_contributors.py +254 -0
- riplex-0.9.7/CONTRIBUTORS.md +0 -14
- riplex-0.9.7/src/riplex.egg-info/scm_version.json +0 -8
- {riplex-0.9.7 → riplex-0.9.9}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/.github/ISSUE_TEMPLATE/crash_report.yml +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/.github/agents/riplex.agent.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/.github/copilot-instructions.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/.github/workflows/publish.yml +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/.github/workflows/release.yml +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/.gitignore +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/.vscode/settings.json +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/LICENSE +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/README.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/docs/architecture.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/docs/changelog.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/docs/cli-guide/lookup.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/docs/cli-guide/orchestrate.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/docs/cli-guide/organize.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/docs/cli-guide/workflow.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/docs/getting-started/configuration.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/docs/getting-started/installation.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/docs/gui-guide/gui-walkthrough.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/docs/index.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/docs/naming-rules.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/docs/reference/cli.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/docs/troubleshooting.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/issues/debug-artifacts-consolidation.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/issues/manual-rip-and-organize-escape-hatch.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/issues/orchestrate-dvdcompare-fallback.md +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/mkdocs.yml +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/screenshots/0_Rip_Flow_BTTF.gif +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/screenshots/1_Welcome_Screen.png +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/screenshots/2_Disc_Detection_BTTF.png +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/screenshots/3_Metadata_Lookup_BTTF.png +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/screenshots/4_Disc_Release_BTTF.png +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/screenshots/5_Multi_Disc_Overview_BTTF.png +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/screenshots/5_Select_Title_to_RIP_BTTF.png +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/setup.cfg +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/__init__.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/cache.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/config.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/dedup.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/detect.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/disc/__init__.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/disc/analysis.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/disc/makemkv.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/formatter.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/lookup.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/manifest.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/matcher.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/metadata/__init__.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/metadata/planner.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/metadata/provider.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/metadata/sources/__init__.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/metadata/sources/tmdb.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/models.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/normalize.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/organizer.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/scanner.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/snapshot.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/splitter.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/tagger.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/title.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/ui.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex/updater.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex.egg-info/dependency_links.txt +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex.egg-info/entry_points.txt +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex.egg-info/top_level.txt +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/__init__.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/bug_report.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/crash_dump.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/keep_awake.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/main.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/screens/__init__.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/screens/disc_detection.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/screens/disc_overview.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/screens/disc_swap.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/screens/done.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/screens/folder_picker.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/screens/metadata.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/screens/orchestrate_done.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/screens/organize_done.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/screens/organize_preview.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/screens/release.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/screens/selection.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/screens/update.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_app/screens/welcome.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_cli/__init__.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_cli/commands/__init__.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_cli/commands/lookup.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_cli/commands/orchestrate.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_cli/commands/organize.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_cli/commands/rip.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_cli/commands/setup.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_cli/formatting.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/src/riplex_cli/main.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/__init__.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/fixtures/chernobyl_disc1.json +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/fixtures/makemkvcon_frozen_planet_ii_d2.txt +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/fixtures/makemkvcon_list.txt +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/snapshots/Batman Begins.snapshot.json +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/snapshots/Blade Runner (Blu-ray 4k).snapshot.json +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/snapshots/Blade Runner The Final Cut.snapshot.json +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/snapshots/Seven Worlds One Planet.snapshot.json +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/snapshots/The Dark Knight Rises.snapshot.json +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/snapshots/The Dark Knight.snapshot.json +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/snapshots/Waterworld.snapshot.json +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_app_main.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_cache.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_cli_utils.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_config.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_dedup.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_detect.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_disc_analysis.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_disc_detection_screen.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_disc_fixtures.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_disc_provider.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_formatter.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_lookup.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_makemkv.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_matcher.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_metadata_screen.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_normalize.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_organizer.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_planner.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_release_screen.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_rip_guide.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_scanner.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_snapshot.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_splitter.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_tagger.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_tmdb_provider.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_ui.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_updater.py +0 -0
- {riplex-0.9.7 → riplex-0.9.9}/tests/test_welcome_screen.py +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"include": [
|
|
3
|
+
{
|
|
4
|
+
"number": 1,
|
|
5
|
+
"reason": "Early unlabeled bug report that led to CLI/config handling fixes."
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"number": 5,
|
|
9
|
+
"reason": "Bug report fixed through the dvdcompare-scraper dependency."
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"number": 6,
|
|
13
|
+
"reason": "Bug report that led to Windows title sanitization fixes."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"number": 7,
|
|
17
|
+
"reason": "Bug report that led to Flet compatibility fixes."
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"number": 8,
|
|
21
|
+
"reason": "Setup report that led to improved prerequisite documentation."
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"number": 9,
|
|
25
|
+
"reason": "Bug report that led to dvdcompare caching and rate-limit mitigations."
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"number": 11,
|
|
29
|
+
"reason": "Setup report that led to a friendlier missing-GUI-extra error."
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"number": 16,
|
|
33
|
+
"reason": "Bug report that led to packaged-app SSL certificate handling improvements."
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"number": 17,
|
|
37
|
+
"reason": "Bug report that led to clearer TMDb API credential handling."
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"number": 20,
|
|
41
|
+
"reason": "Bug report that led to better installed-tool detection after winget installs."
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"exclude": []
|
|
45
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Update Contributors
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
- cron: "0 12 * * 1"
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
pull-requests: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
update-contributors:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
|
|
20
|
+
- uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.12"
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: pip install -e ".[dev]"
|
|
26
|
+
|
|
27
|
+
- name: Run updater tests
|
|
28
|
+
run: pytest tests/test_update_contributors.py -v
|
|
29
|
+
|
|
30
|
+
- name: Update contributors
|
|
31
|
+
env:
|
|
32
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
33
|
+
run: python scripts/update_contributors.py
|
|
34
|
+
|
|
35
|
+
- name: Create pull request
|
|
36
|
+
uses: peter-evans/create-pull-request@v6
|
|
37
|
+
with:
|
|
38
|
+
commit-message: chore: refresh CONTRIBUTORS.md
|
|
39
|
+
title: chore: refresh CONTRIBUTORS.md
|
|
40
|
+
body: |
|
|
41
|
+
Updates the generated Bug Bashers table in CONTRIBUTORS.md from closed issue reports.
|
|
42
|
+
branch: chore/update-contributors
|
|
43
|
+
delete-branch: true
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Contributors
|
|
2
|
+
|
|
3
|
+
Thank you to everyone who helps make riplex better!
|
|
4
|
+
|
|
5
|
+
## Bug Bashers
|
|
6
|
+
|
|
7
|
+
These users have gone out of their way to report bugs, provide logs, and help
|
|
8
|
+
us track down issues. We really appreciate it.
|
|
9
|
+
|
|
10
|
+
> **Rank:** 🧪 Bug Spotter (1+) · 🐛 Bug Hunter (4+) · 🔍 Bug Detective (8+) · 🛡️ Bug Slayer (15+) · 💎 Legend (25+)
|
|
11
|
+
|
|
12
|
+
<!-- BUG_BASHERS:START -->
|
|
13
|
+
| User | Issues |
|
|
14
|
+
|------|--------|
|
|
15
|
+
| 🐛 [@JelloEmperor](https://github.com/JelloEmperor) | [#1](https://github.com/AnyCredit5518/riplex/issues/1), [#5](https://github.com/AnyCredit5518/riplex/issues/5), [#6](https://github.com/AnyCredit5518/riplex/issues/6), [#7](https://github.com/AnyCredit5518/riplex/issues/7), [#12](https://github.com/AnyCredit5518/riplex/issues/12) |
|
|
16
|
+
| 🧪 [@titanzeonprime-lab](https://github.com/titanzeonprime-lab) | [#8](https://github.com/AnyCredit5518/riplex/issues/8), [#11](https://github.com/AnyCredit5518/riplex/issues/11) |
|
|
17
|
+
| 🧪 [@bigswannyd](https://github.com/bigswannyd) | [#16](https://github.com/AnyCredit5518/riplex/issues/16) |
|
|
18
|
+
| 🧪 [@darsovit](https://github.com/darsovit) | [#17](https://github.com/AnyCredit5518/riplex/issues/17) |
|
|
19
|
+
| 🧪 [@LtKaneDagger](https://github.com/LtKaneDagger) | [#20](https://github.com/AnyCredit5518/riplex/issues/20) |
|
|
20
|
+
| 🧪 [@nathanpyoung](https://github.com/nathanpyoung) | [#21](https://github.com/AnyCredit5518/riplex/issues/21) |
|
|
21
|
+
| 🧪 [@nerdguy1338](https://github.com/nerdguy1338) | [#9](https://github.com/AnyCredit5518/riplex/issues/9) |
|
|
22
|
+
| 🧪 [@TallGeekyCool](https://github.com/TallGeekyCool) | [#22](https://github.com/AnyCredit5518/riplex/issues/22) |
|
|
23
|
+
<!-- BUG_BASHERS:END -->
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: riplex
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.9
|
|
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.16
|
|
12
12
|
Requires-Dist: platformdirs>=4.0
|
|
13
13
|
Provides-Extra: dev
|
|
14
14
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
@@ -117,35 +117,51 @@ subtitle tracks differ, we should:
|
|
|
117
117
|
## Automated Contributors List Updater
|
|
118
118
|
|
|
119
119
|
`CONTRIBUTORS.md` lists community members who have helped riplex by reporting
|
|
120
|
-
bugs (and eventually other contribution categories).
|
|
121
|
-
which
|
|
120
|
+
bugs (and eventually other contribution categories). The Bug Bashers table used
|
|
121
|
+
to be hand-edited, which meant it went stale fast and we forgot to credit
|
|
122
|
+
people.
|
|
122
123
|
|
|
123
|
-
###
|
|
124
|
+
### Status
|
|
125
|
+
|
|
126
|
+
Implemented by `scripts/update_contributors.py`,
|
|
127
|
+
`.github/contributors-overrides.json`, and
|
|
128
|
+
`.github/workflows/update-contributors.yml`.
|
|
129
|
+
|
|
130
|
+
The updater owns only the generated Bug Bashers table between
|
|
131
|
+
`<!-- BUG_BASHERS:START -->` and `<!-- BUG_BASHERS:END -->`, while the
|
|
132
|
+
surrounding prose stays hand-editable.
|
|
133
|
+
|
|
134
|
+
### Implemented behavior
|
|
124
135
|
|
|
125
136
|
1. **Script** — `scripts/update_contributors.py` that:
|
|
126
|
-
- Uses the GitHub REST API (via `
|
|
127
|
-
|
|
137
|
+
- Uses the GitHub REST API (via `httpx`) to enumerate closed issues in
|
|
138
|
+
`AnyCredit5518/riplex`.
|
|
128
139
|
- For each issue, checks whether it was referenced by a merged commit
|
|
129
140
|
on the default branch (search commit messages for `#N`, `fixes #N`,
|
|
130
|
-
`closes #N`, etc.)
|
|
141
|
+
`closes #N`, etc.) and counts closed issues labeled `bug` that led to an
|
|
142
|
+
actual fix.
|
|
143
|
+
- Supports manual include/exclude overrides for valid reports that the
|
|
144
|
+
strict commit-reference signal cannot infer, such as early unlabeled
|
|
145
|
+
reports or fixes made in a sibling dependency.
|
|
131
146
|
- Tallies issues per opener, excluding `AnyCredit5518` and any account
|
|
132
147
|
ending in `[bot]`.
|
|
133
|
-
- Maps tally to rank: 🐛 4+, 🔍 8+, 🛡️ 15+, 💎 25+ (matches
|
|
134
|
-
thresholds in `CONTRIBUTORS.md`).
|
|
148
|
+
- Maps tally to rank: 🧪 1+, 🐛 4+, 🔍 8+, 🛡️ 15+, 💎 25+ (matches
|
|
149
|
+
existing thresholds in `CONTRIBUTORS.md`).
|
|
135
150
|
- Renders the "Bug Bashers" table between two HTML comment markers
|
|
136
151
|
(`<!-- BUG_BASHERS:START -->` / `<!-- BUG_BASHERS:END -->`) so the
|
|
137
152
|
surrounding prose stays hand-editable.
|
|
138
153
|
- Sorts by issue count (desc), then username (asc) for stable diffs.
|
|
139
154
|
- Reads token from `GITHUB_TOKEN` env var; falls back to unauthenticated
|
|
140
155
|
requests (lower rate limit) for local runs without a token.
|
|
141
|
-
- Has unit tests covering
|
|
142
|
-
replacement,
|
|
143
|
-
mocked API responses
|
|
156
|
+
- Has unit tests covering rank mapping, exclusion rules, marker
|
|
157
|
+
replacement, overrides, dry-run behavior, issue-reference detection, and
|
|
158
|
+
mocked API responses.
|
|
144
159
|
|
|
145
160
|
2. **Workflow** — `.github/workflows/update-contributors.yml`:
|
|
146
161
|
- Triggers: `schedule: cron '0 12 * * 1'` (every Monday 12:00 UTC) +
|
|
147
162
|
`workflow_dispatch` for on-demand runs.
|
|
148
|
-
- Runs `
|
|
163
|
+
- Runs `pytest tests/test_update_contributors.py -v` and
|
|
164
|
+
`python scripts/update_contributors.py`, then uses
|
|
149
165
|
`peter-evans/create-pull-request` to open a PR titled
|
|
150
166
|
`chore: refresh CONTRIBUTORS.md` if there's a diff.
|
|
151
167
|
- Uses the built-in `GITHUB_TOKEN` — no extra secrets needed.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Repository maintenance scripts."""
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import difflib
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any, Iterable, Sequence
|
|
13
|
+
|
|
14
|
+
import httpx
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
DEFAULT_REPO = "AnyCredit5518/riplex"
|
|
18
|
+
DEFAULT_CONTRIBUTORS_FILE = Path("CONTRIBUTORS.md")
|
|
19
|
+
DEFAULT_OVERRIDES_FILE = Path(".github/contributors-overrides.json")
|
|
20
|
+
DEFAULT_GIT_REF = "origin/main"
|
|
21
|
+
START_MARKER = "<!-- BUG_BASHERS:START -->"
|
|
22
|
+
END_MARKER = "<!-- BUG_BASHERS:END -->"
|
|
23
|
+
OWNER_LOGIN = "AnyCredit5518"
|
|
24
|
+
ISSUE_REF_RE = re.compile(
|
|
25
|
+
r"(?i)(?:fix(?:e[sd])?|close[sd]?|resolve[sd]?|issue|issues)?\s*#(\d+)"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True)
|
|
30
|
+
class Issue:
|
|
31
|
+
number: int
|
|
32
|
+
author: str
|
|
33
|
+
labels: frozenset[str]
|
|
34
|
+
url: str
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def is_bug(self) -> bool:
|
|
38
|
+
return any(label.lower() == "bug" for label in self.labels)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class Overrides:
|
|
43
|
+
include: frozenset[int]
|
|
44
|
+
exclude: frozenset[int]
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def empty(cls) -> "Overrides":
|
|
48
|
+
return cls(include=frozenset(), exclude=frozenset())
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(frozen=True)
|
|
52
|
+
class Contributor:
|
|
53
|
+
username: str
|
|
54
|
+
issue_numbers: tuple[int, ...]
|
|
55
|
+
issue_urls: dict[int, str]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def rank_for_count(count: int) -> str:
|
|
59
|
+
if count >= 25:
|
|
60
|
+
return "💎"
|
|
61
|
+
if count >= 15:
|
|
62
|
+
return "🛡️"
|
|
63
|
+
if count >= 8:
|
|
64
|
+
return "🔍"
|
|
65
|
+
if count >= 4:
|
|
66
|
+
return "🐛"
|
|
67
|
+
return "🧪"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def parse_issue(item: dict[str, Any]) -> Issue | None:
|
|
71
|
+
if "pull_request" in item:
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
user = item.get("user") or {}
|
|
75
|
+
login = user.get("login")
|
|
76
|
+
if not login:
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
labels = item.get("labels") or []
|
|
80
|
+
label_names = frozenset(
|
|
81
|
+
label.get("name", "") if isinstance(label, dict) else str(label)
|
|
82
|
+
for label in labels
|
|
83
|
+
)
|
|
84
|
+
return Issue(
|
|
85
|
+
number=int(item["number"]),
|
|
86
|
+
author=str(login),
|
|
87
|
+
labels=label_names,
|
|
88
|
+
url=str(item.get("html_url") or ""),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def fetch_closed_issues(repo: str, token: str | None = None) -> list[Issue]:
|
|
93
|
+
headers = {
|
|
94
|
+
"Accept": "application/vnd.github+json",
|
|
95
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
96
|
+
}
|
|
97
|
+
if token:
|
|
98
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
99
|
+
|
|
100
|
+
issues: list[Issue] = []
|
|
101
|
+
page = 1
|
|
102
|
+
per_page = 100
|
|
103
|
+
with httpx.Client(headers=headers, timeout=30.0, follow_redirects=True) as client:
|
|
104
|
+
while True:
|
|
105
|
+
response = client.get(
|
|
106
|
+
f"https://api.github.com/repos/{repo}/issues",
|
|
107
|
+
params={"state": "closed", "per_page": per_page, "page": page},
|
|
108
|
+
)
|
|
109
|
+
response.raise_for_status()
|
|
110
|
+
items = response.json()
|
|
111
|
+
if not items:
|
|
112
|
+
break
|
|
113
|
+
|
|
114
|
+
for item in items:
|
|
115
|
+
issue = parse_issue(item)
|
|
116
|
+
if issue is not None:
|
|
117
|
+
issues.append(issue)
|
|
118
|
+
|
|
119
|
+
if len(items) < per_page:
|
|
120
|
+
break
|
|
121
|
+
page += 1
|
|
122
|
+
|
|
123
|
+
return issues
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def extract_issue_references(text: str) -> frozenset[int]:
|
|
127
|
+
return frozenset(int(match.group(1)) for match in ISSUE_REF_RE.finditer(text))
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def git_ref_exists(repo_root: Path, git_ref: str) -> bool:
|
|
131
|
+
result = subprocess.run(
|
|
132
|
+
["git", "rev-parse", "--verify", "--quiet", git_ref],
|
|
133
|
+
cwd=repo_root,
|
|
134
|
+
capture_output=True,
|
|
135
|
+
text=True,
|
|
136
|
+
)
|
|
137
|
+
return result.returncode == 0
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def find_referenced_issues(repo_root: Path, git_ref: str = DEFAULT_GIT_REF) -> frozenset[int]:
|
|
141
|
+
log_ref = git_ref if git_ref_exists(repo_root, git_ref) else "HEAD"
|
|
142
|
+
result = subprocess.run(
|
|
143
|
+
["git", "log", log_ref, "--format=%B"],
|
|
144
|
+
cwd=repo_root,
|
|
145
|
+
check=True,
|
|
146
|
+
capture_output=True,
|
|
147
|
+
text=True,
|
|
148
|
+
encoding="utf-8",
|
|
149
|
+
errors="replace",
|
|
150
|
+
)
|
|
151
|
+
return extract_issue_references(result.stdout)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def load_overrides(path: Path) -> Overrides:
|
|
155
|
+
if not path.exists():
|
|
156
|
+
return Overrides.empty()
|
|
157
|
+
|
|
158
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
159
|
+
return Overrides(
|
|
160
|
+
include=frozenset(_parse_override_numbers(data.get("include", []), "include")),
|
|
161
|
+
exclude=frozenset(_parse_override_numbers(data.get("exclude", []), "exclude")),
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _parse_override_numbers(entries: Any, field_name: str) -> list[int]:
|
|
166
|
+
if not isinstance(entries, list):
|
|
167
|
+
raise ValueError(f"overrides field {field_name!r} must be a list")
|
|
168
|
+
|
|
169
|
+
numbers: list[int] = []
|
|
170
|
+
for entry in entries:
|
|
171
|
+
if isinstance(entry, int):
|
|
172
|
+
numbers.append(entry)
|
|
173
|
+
continue
|
|
174
|
+
if isinstance(entry, dict) and isinstance(entry.get("number"), int):
|
|
175
|
+
numbers.append(entry["number"])
|
|
176
|
+
continue
|
|
177
|
+
raise ValueError(
|
|
178
|
+
f"overrides field {field_name!r} must contain issue numbers or objects with a number"
|
|
179
|
+
)
|
|
180
|
+
return numbers
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def should_exclude_author(username: str, owner_login: str = OWNER_LOGIN) -> bool:
|
|
184
|
+
return username.lower() == owner_login.lower() or username.lower().endswith("[bot]")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def eligible_issues(
|
|
188
|
+
issues: Iterable[Issue], referenced_issue_numbers: set[int] | frozenset[int], overrides: Overrides
|
|
189
|
+
) -> list[Issue]:
|
|
190
|
+
selected: list[Issue] = []
|
|
191
|
+
for issue in issues:
|
|
192
|
+
if issue.number in overrides.exclude:
|
|
193
|
+
continue
|
|
194
|
+
if should_exclude_author(issue.author):
|
|
195
|
+
continue
|
|
196
|
+
|
|
197
|
+
has_strict_signal = issue.is_bug and issue.number in referenced_issue_numbers
|
|
198
|
+
has_manual_signal = issue.number in overrides.include
|
|
199
|
+
if has_strict_signal or has_manual_signal:
|
|
200
|
+
selected.append(issue)
|
|
201
|
+
return selected
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def build_contributors(issues: Iterable[Issue]) -> list[Contributor]:
|
|
205
|
+
grouped: dict[str, dict[int, str]] = {}
|
|
206
|
+
display_names: dict[str, str] = {}
|
|
207
|
+
for issue in issues:
|
|
208
|
+
key = issue.author.lower()
|
|
209
|
+
display_names.setdefault(key, issue.author)
|
|
210
|
+
grouped.setdefault(key, {})[issue.number] = issue.url
|
|
211
|
+
|
|
212
|
+
contributors = [
|
|
213
|
+
Contributor(
|
|
214
|
+
username=display_names[key],
|
|
215
|
+
issue_numbers=tuple(sorted(issue_urls)),
|
|
216
|
+
issue_urls=issue_urls,
|
|
217
|
+
)
|
|
218
|
+
for key, issue_urls in grouped.items()
|
|
219
|
+
]
|
|
220
|
+
return sorted(
|
|
221
|
+
contributors,
|
|
222
|
+
key=lambda contributor: (-len(contributor.issue_numbers), contributor.username.lower()),
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def render_table(contributors: Sequence[Contributor]) -> str:
|
|
227
|
+
lines = ["| User | Issues |", "|------|--------|"]
|
|
228
|
+
for contributor in contributors:
|
|
229
|
+
rank = rank_for_count(len(contributor.issue_numbers))
|
|
230
|
+
user = f"{rank} [@{contributor.username}](https://github.com/{contributor.username})"
|
|
231
|
+
issue_links = ", ".join(
|
|
232
|
+
f"[#{number}]({contributor.issue_urls[number]})"
|
|
233
|
+
for number in contributor.issue_numbers
|
|
234
|
+
)
|
|
235
|
+
lines.append(f"| {user} | {issue_links} |")
|
|
236
|
+
return "\n".join(lines)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def replace_marker_block(content: str, replacement: str) -> str:
|
|
240
|
+
start = content.find(START_MARKER)
|
|
241
|
+
end = content.find(END_MARKER)
|
|
242
|
+
if start == -1 or end == -1 or end < start:
|
|
243
|
+
raise ValueError(
|
|
244
|
+
f"Could not find ordered {START_MARKER!r} and {END_MARKER!r} markers"
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
block_start = start + len(START_MARKER)
|
|
248
|
+
before = content[:block_start]
|
|
249
|
+
after = content[end:]
|
|
250
|
+
return f"{before}\n{replacement.rstrip()}\n{after}"
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def unified_diff(original: str, updated: str, path: Path) -> str:
|
|
254
|
+
return "".join(
|
|
255
|
+
difflib.unified_diff(
|
|
256
|
+
original.splitlines(keepends=True),
|
|
257
|
+
updated.splitlines(keepends=True),
|
|
258
|
+
fromfile=f"a/{path.as_posix()}",
|
|
259
|
+
tofile=f"b/{path.as_posix()}",
|
|
260
|
+
)
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def update_contributors_file(
|
|
265
|
+
contributors_file: Path, table: str, dry_run: bool = False
|
|
266
|
+
) -> tuple[bool, str]:
|
|
267
|
+
original = contributors_file.read_text(encoding="utf-8")
|
|
268
|
+
updated = replace_marker_block(original, table)
|
|
269
|
+
if original == updated:
|
|
270
|
+
return False, ""
|
|
271
|
+
|
|
272
|
+
diff = unified_diff(original, updated, contributors_file)
|
|
273
|
+
if not dry_run:
|
|
274
|
+
contributors_file.write_text(updated, encoding="utf-8")
|
|
275
|
+
return True, diff
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def generate_table(
|
|
279
|
+
repo: str,
|
|
280
|
+
repo_root: Path,
|
|
281
|
+
overrides_file: Path,
|
|
282
|
+
token: str | None,
|
|
283
|
+
git_ref: str = DEFAULT_GIT_REF,
|
|
284
|
+
) -> str:
|
|
285
|
+
issues = fetch_closed_issues(repo, token=token)
|
|
286
|
+
referenced_issue_numbers = find_referenced_issues(repo_root, git_ref=git_ref)
|
|
287
|
+
overrides = load_overrides(overrides_file)
|
|
288
|
+
selected_issues = eligible_issues(issues, referenced_issue_numbers, overrides)
|
|
289
|
+
contributors = build_contributors(selected_issues)
|
|
290
|
+
return render_table(contributors)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
294
|
+
parser = argparse.ArgumentParser(
|
|
295
|
+
description="Update the generated Bug Bashers table in CONTRIBUTORS.md."
|
|
296
|
+
)
|
|
297
|
+
parser.add_argument("--repo", default=DEFAULT_REPO, help="GitHub repo as owner/name.")
|
|
298
|
+
parser.add_argument(
|
|
299
|
+
"--contributors-file",
|
|
300
|
+
type=Path,
|
|
301
|
+
default=DEFAULT_CONTRIBUTORS_FILE,
|
|
302
|
+
help="Path to CONTRIBUTORS.md.",
|
|
303
|
+
)
|
|
304
|
+
parser.add_argument(
|
|
305
|
+
"--overrides-file",
|
|
306
|
+
type=Path,
|
|
307
|
+
default=DEFAULT_OVERRIDES_FILE,
|
|
308
|
+
help="Path to the manual include/exclude override JSON file.",
|
|
309
|
+
)
|
|
310
|
+
parser.add_argument(
|
|
311
|
+
"--repo-root",
|
|
312
|
+
type=Path,
|
|
313
|
+
default=Path("."),
|
|
314
|
+
help="Repository root used for local git history scanning.",
|
|
315
|
+
)
|
|
316
|
+
parser.add_argument(
|
|
317
|
+
"--git-ref",
|
|
318
|
+
default=DEFAULT_GIT_REF,
|
|
319
|
+
help="Git ref to scan for issue references. Falls back to HEAD if missing.",
|
|
320
|
+
)
|
|
321
|
+
parser.add_argument(
|
|
322
|
+
"--token",
|
|
323
|
+
default=None,
|
|
324
|
+
help="GitHub token. Defaults to GITHUB_TOKEN when omitted.",
|
|
325
|
+
)
|
|
326
|
+
parser.add_argument(
|
|
327
|
+
"--dry-run",
|
|
328
|
+
action="store_true",
|
|
329
|
+
help="Print the diff without writing CONTRIBUTORS.md.",
|
|
330
|
+
)
|
|
331
|
+
return parser
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
335
|
+
parser = build_parser()
|
|
336
|
+
args = parser.parse_args(argv)
|
|
337
|
+
|
|
338
|
+
repo_root = args.repo_root.resolve()
|
|
339
|
+
contributors_file = (repo_root / args.contributors_file).resolve()
|
|
340
|
+
overrides_file = (repo_root / args.overrides_file).resolve()
|
|
341
|
+
token = args.token or os.environ.get("GITHUB_TOKEN")
|
|
342
|
+
|
|
343
|
+
if not token:
|
|
344
|
+
print(
|
|
345
|
+
"GITHUB_TOKEN is not set; using unauthenticated GitHub API requests with a lower rate limit.",
|
|
346
|
+
file=sys.stderr,
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
table = generate_table(
|
|
350
|
+
args.repo, repo_root, overrides_file, token=token, git_ref=args.git_ref
|
|
351
|
+
)
|
|
352
|
+
changed, diff = update_contributors_file(
|
|
353
|
+
contributors_file, table=table, dry_run=args.dry_run
|
|
354
|
+
)
|
|
355
|
+
if args.dry_run:
|
|
356
|
+
print(diff or "CONTRIBUTORS.md is already up to date.")
|
|
357
|
+
elif changed:
|
|
358
|
+
print(f"Updated {contributors_file.relative_to(repo_root)}")
|
|
359
|
+
else:
|
|
360
|
+
print("CONTRIBUTORS.md is already up to date.")
|
|
361
|
+
return 0
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
if __name__ == "__main__":
|
|
365
|
+
raise SystemExit(main())
|
|
@@ -355,7 +355,9 @@ async def _throttled_find_film(
|
|
|
355
355
|
try:
|
|
356
356
|
if disc_format:
|
|
357
357
|
return await _find_film_prefer_format(title, disc_format, year)
|
|
358
|
-
return await find_film(
|
|
358
|
+
return await find_film(
|
|
359
|
+
title, disc_format, year=year, resolve_pointers=True
|
|
360
|
+
)
|
|
359
361
|
finally:
|
|
360
362
|
_last_request_at = time.monotonic()
|
|
361
363
|
|
|
@@ -370,7 +372,7 @@ async def _throttled_get_film_by_url(url: str) -> FilmComparison:
|
|
|
370
372
|
log.debug("dvdcompare throttle: waiting %.2fs before request", wait)
|
|
371
373
|
await asyncio.sleep(wait)
|
|
372
374
|
try:
|
|
373
|
-
return await get_film_by_url(url)
|
|
375
|
+
return await get_film_by_url(url, resolve_pointers=True)
|
|
374
376
|
finally:
|
|
375
377
|
_last_request_at = time.monotonic()
|
|
376
378
|
|
|
@@ -403,21 +405,21 @@ async def _find_film_prefer_format(
|
|
|
403
405
|
for sr in results:
|
|
404
406
|
if fmt_matches(sr) and year_matches(sr):
|
|
405
407
|
log.debug("dvdcompare pick (format+year): %s", sr.url)
|
|
406
|
-
return await get_film_by_url(sr.url)
|
|
408
|
+
return await get_film_by_url(sr.url, resolve_pointers=True)
|
|
407
409
|
# Tier 2: format only
|
|
408
410
|
for sr in results:
|
|
409
411
|
if fmt_matches(sr):
|
|
410
412
|
log.debug("dvdcompare pick (format-only, year=%s actual=%s): %s",
|
|
411
413
|
year, sr.year, sr.url)
|
|
412
|
-
return await get_film_by_url(sr.url)
|
|
414
|
+
return await get_film_by_url(sr.url, resolve_pointers=True)
|
|
413
415
|
# Tier 3: year only (no format listed on result)
|
|
414
416
|
for sr in results:
|
|
415
417
|
if year_matches(sr) and not sr.disc_format:
|
|
416
418
|
log.debug("dvdcompare pick (year-only, no result format): %s", sr.url)
|
|
417
|
-
return await get_film_by_url(sr.url)
|
|
419
|
+
return await get_film_by_url(sr.url, resolve_pointers=True)
|
|
418
420
|
# Tier 4: first result
|
|
419
421
|
log.debug("dvdcompare pick (first result fallback): %s", results[0].url)
|
|
420
|
-
return await get_film_by_url(results[0].url)
|
|
422
|
+
return await get_film_by_url(results[0].url, resolve_pointers=True)
|
|
421
423
|
|
|
422
424
|
|
|
423
425
|
def _clean_feature_type(raw: str) -> str:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: riplex
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.9
|
|
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.16
|
|
12
12
|
Requires-Dist: platformdirs>=4.0
|
|
13
13
|
Provides-Extra: dev
|
|
14
14
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
@@ -4,12 +4,14 @@ LICENSE
|
|
|
4
4
|
README.md
|
|
5
5
|
mkdocs.yml
|
|
6
6
|
pyproject.toml
|
|
7
|
+
.github/contributors-overrides.json
|
|
7
8
|
.github/copilot-instructions.md
|
|
8
9
|
.github/ISSUE_TEMPLATE/bug_report.yml
|
|
9
10
|
.github/ISSUE_TEMPLATE/crash_report.yml
|
|
10
11
|
.github/agents/riplex.agent.md
|
|
11
12
|
.github/workflows/publish.yml
|
|
12
13
|
.github/workflows/release.yml
|
|
14
|
+
.github/workflows/update-contributors.yml
|
|
13
15
|
.vscode/settings.json
|
|
14
16
|
docs/architecture.md
|
|
15
17
|
docs/changelog.md
|
|
@@ -35,6 +37,8 @@ screenshots/3_Metadata_Lookup_BTTF.png
|
|
|
35
37
|
screenshots/4_Disc_Release_BTTF.png
|
|
36
38
|
screenshots/5_Multi_Disc_Overview_BTTF.png
|
|
37
39
|
screenshots/5_Select_Title_to_RIP_BTTF.png
|
|
40
|
+
scripts/__init__.py
|
|
41
|
+
scripts/update_contributors.py
|
|
38
42
|
src/riplex/__init__.py
|
|
39
43
|
src/riplex/cache.py
|
|
40
44
|
src/riplex/config.py
|
|
@@ -127,6 +131,7 @@ tests/test_splitter.py
|
|
|
127
131
|
tests/test_tagger.py
|
|
128
132
|
tests/test_tmdb_provider.py
|
|
129
133
|
tests/test_ui.py
|
|
134
|
+
tests/test_update_contributors.py
|
|
130
135
|
tests/test_updater.py
|
|
131
136
|
tests/test_welcome_screen.py
|
|
132
137
|
tests/fixtures/chernobyl_disc1.json
|