freeflix-cli 2.1.0__tar.gz → 2.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/.github/workflows/ci.yml +4 -4
  2. freeflix_cli-2.1.2/.github/workflows/macos.yml +36 -0
  3. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/CHANGELOG.md +26 -1
  4. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/PKG-INFO +1 -1
  5. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/docs/SCORECARD-2.1.0.md +1 -1
  6. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/pyproject.toml +1 -1
  7. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/cli.py +17 -0
  8. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/install_health.py +1 -1
  9. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/player_manager.py +34 -20
  10. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_audit_fixes.py +31 -0
  11. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_core_units.py +18 -0
  12. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_recent_new_episodes.py +1 -0
  13. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_release_config.py +3 -1
  14. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/uv.lock +1 -1
  15. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/.github/workflows/binaries.yml +0 -0
  16. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/.github/workflows/release.yml +0 -0
  17. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/.gitignore +0 -0
  18. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/CONTRIBUTING.md +0 -0
  19. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/LICENSE +0 -0
  20. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/README.md +0 -0
  21. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/SECURITY.md +0 -0
  22. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/assets/logo.png +0 -0
  23. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/config/freeflix_position.lua +0 -0
  24. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/config/input.conf +0 -0
  25. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/config/mpv.conf +0 -0
  26. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/data/selectors.jsonc +0 -0
  27. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/data/source_portal.jsonc +0 -0
  28. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/docs/ARCHITECTURE.md +0 -0
  29. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/docs/AUDIT-FREEFLIX-2.0-2026-11-04.md +0 -0
  30. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/docs/CORRECTIONS-2.1.0.md +0 -0
  31. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/docs/README.md +0 -0
  32. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/docs/RELEASE.md +0 -0
  33. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/docs/TROUBLESHOOTING.md +0 -0
  34. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/docs/USAGE.md +0 -0
  35. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/scripts/build-binary.sh +0 -0
  36. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/scripts/install-mac.sh +0 -0
  37. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/scripts/install.ps1 +0 -0
  38. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/scripts/install.py +0 -0
  39. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/scripts/install.sh +0 -0
  40. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/scripts/wrappers/mpv +0 -0
  41. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/__init__.py +0 -0
  42. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/__main__.py +0 -0
  43. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/anilist.py +0 -0
  44. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/cli_utils.py +0 -0
  45. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/cloudflare.py +0 -0
  46. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/config_loader.py +0 -0
  47. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/defaults.py +0 -0
  48. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/doctor.py +0 -0
  49. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/handlers/__init__.py +0 -0
  50. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/handlers/anilist.py +0 -0
  51. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/handlers/anime_sama.py +0 -0
  52. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/handlers/coflix.py +0 -0
  53. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/handlers/french_manga.py +0 -0
  54. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/handlers/french_stream.py +0 -0
  55. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/handlers/goldenanime.py +0 -0
  56. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/handlers/goldenms.py +0 -0
  57. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/handlers/hianime.py +0 -0
  58. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/handlers/nyaa.py +0 -0
  59. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/handlers/papystreaming.py +0 -0
  60. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/handlers/playback.py +0 -0
  61. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/handlers/yts.py +0 -0
  62. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/health.py +0 -0
  63. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/history_ui.py +0 -0
  64. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/httpcache.py +0 -0
  65. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/i18n.py +0 -0
  66. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/icons.py +0 -0
  67. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/languages.py +0 -0
  68. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/logsetup.py +0 -0
  69. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/main.py +0 -0
  70. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/motion.py +0 -0
  71. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/mpv_ipc.py +0 -0
  72. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/neon.py +0 -0
  73. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/net_config.py +0 -0
  74. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/notifications.py +0 -0
  75. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/platform_android.py +0 -0
  76. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/preview_picker.py +0 -0
  77. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/progress.py +0 -0
  78. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/providers_registry.py +0 -0
  79. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/proxy.py +0 -0
  80. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/recent.py +0 -0
  81. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/__init__.py +0 -0
  82. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/anime_sama.py +0 -0
  83. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/anime_sama_planning.py +0 -0
  84. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/coflix.py +0 -0
  85. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/config.py +0 -0
  86. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/deobfuscate.py +0 -0
  87. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/eztv.py +0 -0
  88. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/french_manga.py +0 -0
  89. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/french_stream.py +0 -0
  90. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/goldenanime.py +0 -0
  91. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/goldenms.py +0 -0
  92. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/nyaa.py +0 -0
  93. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/objects.py +0 -0
  94. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/papystreaming.py +0 -0
  95. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/player.py +0 -0
  96. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/resilient.py +0 -0
  97. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/subtitles.py +0 -0
  98. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/utils.py +0 -0
  99. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/scraping/yts.py +0 -0
  100. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/setup_assistant.py +0 -0
  101. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/splash.py +0 -0
  102. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/subtitles.py +0 -0
  103. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/terminal_image.py +0 -0
  104. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/themes.py +0 -0
  105. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/tracker.py +0 -0
  106. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/ui.py +0 -0
  107. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/src/freeflix_cli/update_checker.py +0 -0
  108. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/conftest.py +0 -0
  109. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_android_setup.py +0 -0
  110. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_anilist_errors.py +0 -0
  111. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_anilist_token_input.py +0 -0
  112. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_anime_sama_planning.py +0 -0
  113. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_batch_download.py +0 -0
  114. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_coflix_parse.py +0 -0
  115. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_cover_cache.py +0 -0
  116. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_decoy_stream.py +0 -0
  117. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_download_fixes.py +0 -0
  118. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_download_integration.py +0 -0
  119. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_download_robustness.py +0 -0
  120. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_episode_parsing.py +0 -0
  121. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_fsvid_decode.py +0 -0
  122. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_health.py +0 -0
  123. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_httpcache.py +0 -0
  124. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_i18n_complete.py +0 -0
  125. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_keyboard.py +0 -0
  126. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_managed_binaries.py +0 -0
  127. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_mouse.py +0 -0
  128. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_mpv_ipc.py +0 -0
  129. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_navigation.py +0 -0
  130. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_nerd_font.py +0 -0
  131. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_nyaa.py +0 -0
  132. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_pack_a_polish.py +0 -0
  133. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_performance.py +0 -0
  134. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_platform_android.py +0 -0
  135. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_player_dash.py +0 -0
  136. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_polish.py +0 -0
  137. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_premium_players.py +0 -0
  138. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_providers_repair.py +0 -0
  139. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_proxy_endpoint.py +0 -0
  140. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_proxy_integration.py +0 -0
  141. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_proxy_resilient.py +0 -0
  142. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_proxy_ssrf.py +0 -0
  143. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_resilient.py +0 -0
  144. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_scraper_resilience.py +0 -0
  145. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_search_parity.py +0 -0
  146. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_sendvid.py +0 -0
  147. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_stream_analysis.py +0 -0
  148. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_stream_integrity.py +0 -0
  149. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_system_deps_cache.py +0 -0
  150. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_terminal_image.py +0 -0
  151. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_update_prompt.py +0 -0
  152. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_v1105_improvements.py +0 -0
  153. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tests/test_v2_aesthetic.py +0 -0
  154. {freeflix_cli-2.1.0 → freeflix_cli-2.1.2}/tools/audit_providers.py +0 -0
@@ -1,7 +1,9 @@
1
1
  name: CI
2
2
 
3
- # Lint + tests on every push and PR, on Linux, Windows AND macOS, so
4
- # regressions (e.g. the Windows arrow-key breakage) are caught before release.
3
+ # Lint + tests on every push and PR, on Linux and Windows. macOS lives in
4
+ # macos.yml: GitHub's macOS runners are frequently unavailable, and having
5
+ # them in this required matrix turned green code into red runs. macOS is still
6
+ # tested on demand / weekly.
5
7
  on:
6
8
  push:
7
9
  branches: [main]
@@ -26,8 +28,6 @@ jobs:
26
28
  os: [ubuntu-22.04, windows-latest]
27
29
  python-version: ['3.9', '3.12']
28
30
  include:
29
- - os: macos-latest
30
- python-version: '3.12'
31
31
  - os: ubuntu-22.04
32
32
  python-version: '3.11'
33
33
  - os: ubuntu-22.04
@@ -0,0 +1,36 @@
1
+ name: macOS
2
+
3
+ # macOS is tested separately: the hosted macos-latest runners are often
4
+ # unavailable (job never acquired), which must not turn every push red.
5
+ # Run it manually before a release, or let the weekly schedule catch drift.
6
+ on:
7
+ workflow_dispatch:
8
+ schedule:
9
+ - cron: '0 4 * * 1' # Monday 04:00 UTC
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ test:
16
+ name: Lint + tests (macos-latest, py3.12)
17
+ runs-on: macos-latest
18
+ timeout-minutes: 30
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - uses: astral-sh/setup-uv@v3
22
+ - uses: actions/setup-python@v5
23
+ with:
24
+ python-version: '3.12'
25
+ - name: Ruff
26
+ run: uvx ruff==0.17.3 check --select E,W,F,B --ignore E501,E402 src/freeflix_cli/
27
+ - name: Import every module
28
+ run: >
29
+ uv run --with . python -c "import pkgutil,importlib,freeflix_cli;
30
+ [importlib.import_module(m.name) for m in
31
+ pkgutil.walk_packages(freeflix_cli.__path__,'freeflix_cli.')];
32
+ print('all modules import OK')"
33
+ - name: Pytest
34
+ run: >
35
+ uv run --with . --with pytest --with pytest-cov --with pytest-timeout
36
+ -m pytest -q --timeout=120
@@ -1,5 +1,30 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.1.2
4
+
5
+ - **Téléchargements Vidzy / fsvid / uqload réparés** : l'URL passée à yt-dlp
6
+ traverse toujours le proxy local curl_cffi (fingerprint navigateur), mais
7
+ elle porte maintenant le **jeton de capacité `?k=`** et le **jeton
8
+ d'en-têtes `?h=`** ajoutés en 2.1. Sans eux, le proxy répondait 403 : le
9
+ streaming fonctionnait, le téléchargement échouait.
10
+ - Vérifié en réel sur FROM S1E1 : fsvid et vidzy résolus, yt-dlp natif a
11
+ téléchargé 34/316 fragments via le proxy sans erreur.
12
+ - Test du flux « Nouveautés » isolé des séries réellement suivies (plus de
13
+ dépendance à l'état local de la machine).
14
+ - **CI macOS séparée** : les runners macOS GitHub sont souvent indisponibles
15
+ (« job was not acquired »), ce qui rendait la CI rouge sans bug de code.
16
+ macOS passe dans `macos.yml` (manuel + hebdomadaire) ; la CI de push reste
17
+ Linux + Windows.
18
+ - 588 tests au vert, ruff OK. Aucun changement d'interface : le code écran
19
+ reste celui de la 2.1.1.
20
+
21
+ ## 2.1.1
22
+
23
+ - **Binaire Windows réparé** : `--doctor` (et le reste de l'interface) force
24
+ UTF-8 sur stdout/stderr quand la console est en cp1252 ; le smoke test du
25
+ binaire autonome passe à nouveau.
26
+ - 586 tests au vert, ruff OK.
27
+
3
28
  ## 2.1.0 — Fiabilité, sécurité et cohérence
4
29
 
5
30
  Version de consolidation : l'audit complet de la 2.0.0 a été appliqué de bout
@@ -124,7 +149,7 @@ en bout, sans changement de fonctionnalité visible.
124
149
  - Tests dédiés `cli.py`, `doctor.py`, `providers_registry`, `config_loader`,
125
150
  `install_health`, thèmes/UI, métadonnées de reprise, noms Windows, garde-fous
126
151
  de release et IPC Windows.
127
- - 585 tests au vert, ruff OK.
152
+ - 586 tests au vert, ruff OK.
128
153
 
129
154
  ## 2.0.0 — Cinéma Noir
130
155
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: freeflix-cli
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Watch movies, series, and anime from your terminal — multi-language, multi-source, no browser required. A maintained fork of autoflix-cli.
5
5
  Project-URL: Homepage, https://github.com/freedy237/freeflix-cli
6
6
  Project-URL: Repository, https://github.com/freedy237/freeflix-cli.git
@@ -12,7 +12,7 @@ ouvert ; il ne s'agit pas d'une note « commerciale ».
12
12
  | **Robustesse réseau** | 7,5 | **8,5** | placeholder <90 s y compris MP4 direct, plafond 5 min sans octet, budget analyse 55/60 s, cache qualité 5 min, TLS DoH vérifié, SSRF + jeton proxy, reprise Range | pas de budget adaptatif par type de lecteur |
13
13
  | **Téléchargements** | 6,5 | **9** | reprise séquentielle réparée, staging + vérification + `os.replace`, `.part` atomique, annulation propagée + kill après 5 s, métadonnées sans cookies en 0600, noms Windows réservés, balayage des sous-titres temp | vérification taille seule si ffprobe absent |
14
14
  | **Multi-OS** | 5,5 | **8,5** | CI Linux/Windows/macOS × 3.9→3.13, build wheel + smoke install, syntaxe PowerShell vérifiée, mpv arm64 macOS/Windows, IPC Windows non bloquant, installateurs tolérants, chemins mpv alignés | pas de validation physique Windows/macOS dans cette session |
15
- | **Tests / CI** | 6 | **9** | **585 tests** verts, couverture **~45 %** (plancher 40), `pytest-timeout=120`, ruff épinglé, suite offline, 47 tests de non-régression/unitaires ajoutés, matrice 5 environnements | quelques assertions historiques faibles, pas de tests de rendu visuel |
15
+ | **Tests / CI** | 6 | **9** | **586 tests** verts, couverture **~45 %** (plancher 40), `pytest-timeout=120`, ruff épinglé, suite offline, 47 tests de non-régression/unitaires ajoutés, matrice 5 environnements | quelques assertions historiques faibles, pas de tests de rendu visuel |
16
16
  | **Release / packaging** | 5 | **8,5** | release gardée par tests + version==tag + `twine check`, `SHA256SUMS`, binaires déclenchés explicitement (`workflow_dispatch`), `--add-data`, smoke `--doctor`, tests de config de release | chaîne binaires à exécuter au prochain tag réel |
17
17
  | **Documentation** | 4 | **9** | README 2.1 (10 sources, planning, souris, dev), index `docs/`, USAGE, ARCHITECTURE, TROUBLESHOOTING, RELEASE, SECURITY, CONTRIBUTING, CHANGELOG détaillé | pas de captures d'écran/GIF |
18
18
  | **Sécurité** | 7 | **9** | jeton de capacité proxy (SSRF local), SSRF DNS/IP, TLS DoH, cookies retirés des métadonnées, URLs signées masquées, toasts PowerShell sans interpolation, PATH registre, checksums d'artefacts | `enc-dec.app` tiers pour GoldenMS, `curl \| sh` de `uv` dans un installeur |
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "freeflix-cli"
7
- version = "2.1.0"
7
+ version = "2.1.2"
8
8
  description = "Watch movies, series, and anime from your terminal — multi-language, multi-source, no browser required. A maintained fork of autoflix-cli."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9.2"
@@ -9,6 +9,22 @@ a few tens of milliseconds instead of paying the whole import tax.
9
9
  import sys
10
10
 
11
11
 
12
+ def _utf8_console() -> None:
13
+ """Force UTF-8 on stdout/stderr when the console defaults to cp1252.
14
+
15
+ The Windows standalone binary inherits a cp1252 console; --doctor (box
16
+ drawing, arrows) then crashed with UnicodeEncodeError before printing
17
+ anything. TUI output uses the same code path.
18
+ """
19
+ for stream in (sys.stdout, sys.stderr):
20
+ try:
21
+ enc = (getattr(stream, "encoding", "") or "").lower().replace("-", "")
22
+ if enc not in ("utf8", "utf8mb4"):
23
+ stream.reconfigure(encoding="utf-8", errors="replace")
24
+ except Exception:
25
+ pass
26
+
27
+
12
28
  def _version() -> str:
13
29
  try:
14
30
  import importlib.metadata as _im
@@ -37,6 +53,7 @@ def print_help():
37
53
 
38
54
  def main():
39
55
  """Entry point: fast flags first, then the real application."""
56
+ _utf8_console()
40
57
  args = set(sys.argv[1:])
41
58
  if args & {"--version", "-V"}:
42
59
  print(_version())
@@ -13,7 +13,7 @@ import os
13
13
  import sys
14
14
 
15
15
  # Version BAKED INTO THE CODE — bump together with pyproject.toml.
16
- CODE_VERSION = "2.1.0"
16
+ CODE_VERSION = "2.1.2"
17
17
 
18
18
 
19
19
  def metadata_version() -> str | None:
@@ -1898,6 +1898,38 @@ def _purge_download_temp(temp_dir: str, keep_incomplete: bool = True) -> None:
1898
1898
  pass
1899
1899
 
1900
1900
 
1901
+ def _proxy_download_url(stream_url: str, headers: dict, player_config: dict,
1902
+ is_mp4: bool) -> str:
1903
+ """Route a fingerprint-gated HLS download through the local proxy.
1904
+
1905
+ Vidzy/fsvid/uqload reject yt-dlp's TLS fingerprint (or serve it a 30 s ad)
1906
+ even though the same URL streams fine through curl_cffi. The proxy URL must
1907
+ carry the per-process capability token AND the opaque header handle: a bare
1908
+ ``?url=`` is rejected with 403 since 2.1.
1909
+ """
1910
+ try:
1911
+ from urllib.parse import quote, urlparse
1912
+ from . import proxy
1913
+
1914
+ host = (urlparse(stream_url).hostname or "").lower()
1915
+ needs_proxy = (
1916
+ (player_config or {}).get("type") in ("fsvid", "uqload")
1917
+ or any(h in host for h in ("vidzy", "fsvid", "uqload"))
1918
+ )
1919
+ if not (".m3u8" in stream_url.lower() and needs_proxy):
1920
+ return stream_url
1921
+ proxy.ensure_started()
1922
+ if not proxy.PROXY_URL:
1923
+ return stream_url
1924
+ endpoint = _proxy_endpoint(stream_url, player_config or {}, bool(is_mp4))
1925
+ token = proxy._headers_token(headers or {})
1926
+ handle = f"&h={token}" if token else ""
1927
+ return (f"{proxy.PROXY_URL}/{endpoint}?url={quote(stream_url)}"
1928
+ f"{handle}&k={proxy.PROXY_TOKEN}")
1929
+ except Exception:
1930
+ return stream_url
1931
+
1932
+
1901
1933
  def _download_headers(headers: dict, referer: str = "",
1902
1934
  user_agent: str = "") -> dict:
1903
1935
  """Sanitised headers for yt-dlp/aria2c (cookies, signed-CDN tokens…)."""
@@ -2920,26 +2952,8 @@ def play_video(
2920
2952
  # our curl_cffi proxy. Route the download through the SAME local
2921
2953
  # proxy endpoint, so yt-dlp talks to localhost while the proxy
2922
2954
  # fetches with the browser fingerprint and real headers/cookies.
2923
- dl_url = stream_url
2924
- try:
2925
- from urllib.parse import urlparse, quote
2926
- dl_host = (urlparse(stream_url).hostname or "").lower()
2927
- if ".m3u8" in stream_url.lower() and (
2928
- player_config.get("type") in ("fsvid", "uqload")
2929
- or any(h in dl_host for h in ("vidzy", "fsvid", "uqload"))
2930
- ):
2931
- proxy.ensure_started()
2932
- if proxy.PROXY_URL:
2933
- endpoint = _proxy_endpoint(
2934
- stream_url, player_config, bool(is_mp4)
2935
- )
2936
- dl_url = (
2937
- f"{proxy.PROXY_URL}/{endpoint}"
2938
- f"?url={quote(stream_url)}"
2939
- f"&headers={quote(json.dumps(headers))}"
2940
- )
2941
- except Exception:
2942
- dl_url = stream_url
2955
+ dl_url = _proxy_download_url(
2956
+ stream_url, headers, player_config, bool(is_mp4))
2943
2957
  success = _download_stream(
2944
2958
  stream_url=dl_url,
2945
2959
  referer=referer,
@@ -221,3 +221,34 @@ def test_ffprobe_quality_flags_short_direct_mp4(monkeypatch):
221
221
  q = pm._ffprobe_quality("https://cdn/ad.mp4", {})
222
222
  assert q["height"] == 720 and q["duration"] == 30.0
223
223
  assert pm.is_likely_ad_placeholder(q) is True
224
+
225
+
226
+ def test_proxy_download_url_carries_token_and_hides_cookies(monkeypatch):
227
+ """Vidzy/fsvid downloads must use the local proxy WITH ?k= and ?h=.
228
+
229
+ Regression found after 2.1: the download URL was built without the
230
+ capability token, so the proxy answered 403 while streaming worked.
231
+ """
232
+ from urllib.parse import parse_qs, urlparse
233
+
234
+ from freeflix_cli import player_manager as pm
235
+ from freeflix_cli import proxy
236
+
237
+ monkeypatch.setattr(proxy, "PROXY_URL", "http://127.0.0.1:9999")
238
+ monkeypatch.setattr(proxy, "PROXY_TOKEN", "tok-123")
239
+ monkeypatch.setattr(proxy, "ensure_started", lambda: None)
240
+ headers = {"Referer": "https://vidzy.cc/", "Cookie": "vz_ok=secret"}
241
+ url = pm._proxy_download_url(
242
+ "https://cdn.vidzy.cc/master.m3u8?t=1", headers, {"type": "fsvid"}, False)
243
+ q = parse_qs(urlparse(url).query)
244
+ assert q["k"] == ["tok-123"]
245
+ assert "h" in q and "headers" not in q
246
+ assert "vz_ok" not in url and "secret" not in url
247
+ assert url.startswith("http://127.0.0.1:9999/")
248
+
249
+
250
+ def test_proxy_download_url_leaves_plain_hosts_alone():
251
+ from freeflix_cli import player_manager as pm
252
+
253
+ plain = "https://cdn.example/master.m3u8"
254
+ assert pm._proxy_download_url(plain, {}, {}, False) == plain
@@ -28,6 +28,24 @@ def test_cli_version_prints_a_version(monkeypatch, capsys):
28
28
  assert out and out[0].isdigit()
29
29
 
30
30
 
31
+ def test_utf8_console_reconfigures_cp1252(monkeypatch):
32
+ class _Fake:
33
+ encoding = "cp1252"
34
+
35
+ def __init__(self):
36
+ self.kw = None
37
+
38
+ def reconfigure(self, **kw):
39
+ self.kw = kw
40
+
41
+ out, err = _Fake(), _Fake()
42
+ monkeypatch.setattr(cli.sys, "stdout", out)
43
+ monkeypatch.setattr(cli.sys, "stderr", err)
44
+ cli._utf8_console()
45
+ assert out.kw == {"encoding": "utf-8", "errors": "replace"}
46
+ assert err.kw == {"encoding": "utf-8", "errors": "replace"}
47
+
48
+
31
49
  def test_cli_help_mentions_doctor(monkeypatch, capsys):
32
50
  monkeypatch.setattr("sys.argv", ["freeflix", "--help"])
33
51
  assert cli.main() == 0
@@ -77,6 +77,7 @@ def test_other_providers_are_ignored(monkeypatch):
77
77
  "season_title": "S1", "episode_title": "E1",
78
78
  "series_url": "https://x", "season_url": "https://x"}]
79
79
  monkeypatch.setattr(tracker, "get_history", lambda: hist)
80
+ monkeypatch.setattr(tracker, "get_followed_series", lambda: [])
80
81
  monkeypatch.setattr(tracker, "get_seen_releases", lambda: set())
81
82
  recent._state["items"] = []
82
83
  recent._state["ready"] = False
@@ -47,8 +47,10 @@ def test_ci_has_timeout_coverage_and_installer_checks():
47
47
  assert "--timeout=120" in yml
48
48
  assert "--cov-fail-under=40" in yml
49
49
  assert "PowerShell syntax" in yml
50
- assert "macos-latest" in yml
51
50
  assert "ruff==0.17.3" in yml
51
+ # macOS is a separate, non-blocking workflow (runners are often unavailable).
52
+ macos = _read(".github/workflows/macos.yml")
53
+ assert "macos-latest" in macos and "workflow_dispatch:" in macos
52
54
 
53
55
 
54
56
  def test_installer_scripts_are_tolerant_and_verified():
@@ -238,7 +238,7 @@ wheels = [
238
238
 
239
239
  [[package]]
240
240
  name = "freeflix-cli"
241
- version = "2.1.0"
241
+ version = "2.1.2"
242
242
  source = { editable = "." }
243
243
  dependencies = [
244
244
  { name = "beautifulsoup4" },
File without changes
File without changes
File without changes
File without changes
File without changes