freeflix-cli 2.4.0__tar.gz → 2.5.0__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.
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/CHANGELOG.md +181 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/PKG-INFO +12 -3
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/README.md +11 -2
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/docs/TROUBLESHOOTING.md +61 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/pyproject.toml +1 -1
- freeflix_cli-2.5.0/scripts/install-mac.sh +209 -0
- freeflix_cli-2.5.0/scripts/install.ps1 +265 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/scripts/install.py +90 -61
- freeflix_cli-2.5.0/scripts/install.sh +421 -0
- freeflix_cli-2.5.0/scripts/wrappers/mpv +39 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/cli_utils.py +271 -130
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/defaults.py +19 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/doctor.py +123 -41
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/handlers/anime_sama.py +4 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/handlers/goldenanime.py +16 -1
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/handlers/match.py +98 -14
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/handlers/playback.py +140 -10
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/i18n.py +41 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/install_health.py +41 -10
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/main.py +33 -19
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/player_manager.py +444 -56
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/preview_picker.py +99 -48
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/proxy.py +39 -9
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/recent.py +156 -72
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/anime_sama.py +329 -1
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/goldenanime.py +110 -35
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/objects.py +4 -1
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/player.py +166 -8
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/resilient.py +11 -6
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/utils.py +62 -6
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/yacine_match.py +47 -14
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/setup_assistant.py +668 -154
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/yacine_live_proxy.py +49 -1
- freeflix_cli-2.5.0/tests/test_anime_sama_fr.py +142 -0
- freeflix_cli-2.5.0/tests/test_anime_sama_lien.py +260 -0
- freeflix_cli-2.5.0/tests/test_hls_preflight.py +104 -0
- freeflix_cli-2.5.0/tests/test_install_flow.py +748 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_providers_repair.py +77 -0
- freeflix_cli-2.5.0/tests/test_recent_releases.py +134 -0
- freeflix_cli-2.5.0/tests/test_release_config.py +163 -0
- freeflix_cli-2.5.0/tests/test_ui_exit.py +161 -0
- freeflix_cli-2.5.0/tests/test_ui_fluidity.py +197 -0
- freeflix_cli-2.5.0/tests/test_voe_mirrors.py +76 -0
- freeflix_cli-2.5.0/tests/test_windows_playback.py +505 -0
- freeflix_cli-2.5.0/tests/test_windows_players.py +74 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_yacine_match.py +195 -1
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/uv.lock +1 -1
- freeflix_cli-2.4.0/scripts/install-mac.sh +0 -124
- freeflix_cli-2.4.0/scripts/install.ps1 +0 -157
- freeflix_cli-2.4.0/scripts/install.sh +0 -261
- freeflix_cli-2.4.0/scripts/wrappers/mpv +0 -14
- freeflix_cli-2.4.0/tests/test_release_config.py +0 -80
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/.github/workflows/binaries.yml +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/.github/workflows/ci.yml +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/.github/workflows/macos.yml +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/.github/workflows/release.yml +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/.gitignore +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/CONTRIBUTING.md +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/LICENSE +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/SECURITY.md +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/assets/logo.png +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/config/freeflix_position.lua +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/config/input.conf +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/config/mpv.conf +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/data/selectors.jsonc +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/data/source_portal.jsonc +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/docs/ARCHITECTURE.md +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/docs/AUDIT-FREEFLIX-2.0-2026-11-04.md +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/docs/CORRECTIONS-2.1.0.md +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/docs/README.md +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/docs/RELEASE.md +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/docs/SCORECARD-2.1.0.md +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/docs/USAGE.md +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/scripts/build-binary.sh +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/__init__.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/__main__.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/anilist.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/cli.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/cloudflare.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/config_loader.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/data/channel_logos.json +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/data/clubs.json +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/handlers/__init__.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/handlers/anilist.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/handlers/coflix.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/handlers/french_manga.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/handlers/french_stream.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/handlers/goldenms.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/handlers/hianime.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/handlers/nyaa.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/handlers/papystreaming.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/handlers/yts.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/health.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/history_ui.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/httpcache.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/icons.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/languages.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/logsetup.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/motion.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/mpv_ipc.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/neon.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/net_config.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/notifications.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/platform_android.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/progress.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/providers_registry.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/__init__.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/anime_sama_planning.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/coflix.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/config.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/deobfuscate.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/eztv.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/french_manga.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/french_stream.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/goldenms.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/nyaa.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/papystreaming.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/subtitles.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/scraping/yts.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/splash.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/subtitles.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/terminal_image.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/themes.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/tracker.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/ui.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/src/freeflix_cli/update_checker.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/conftest.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_android_setup.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_anilist_errors.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_anilist_token_input.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_anime_sama_planning.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_audit_fixes.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_batch_download.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_coflix_parse.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_core_units.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_cover_cache.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_decoy_stream.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_download_fixes.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_download_integration.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_download_robustness.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_episode_parsing.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_fsvid_decode.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_health.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_httpcache.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_i18n_complete.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_keyboard.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_managed_binaries.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_mouse.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_mpv_ipc.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_navigation.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_nerd_font.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_nyaa.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_pack_a_polish.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_performance.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_platform_android.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_player_dash.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_polish.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_premium_players.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_proxy_endpoint.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_proxy_integration.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_proxy_resilient.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_proxy_ssrf.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_recent_new_episodes.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_resilient.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_scraper_resilience.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_search_parity.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_sendvid.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_stream_analysis.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_stream_integrity.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_system_deps_cache.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_terminal_image.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_update_prompt.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_v1105_improvements.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tests/test_v2_aesthetic.py +0 -0
- {freeflix_cli-2.4.0 → freeflix_cli-2.5.0}/tools/audit_providers.py +0 -0
|
@@ -1,5 +1,186 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.5.0
|
|
4
|
+
|
|
5
|
+
- **Anime-Sama : les lecteurs jouent enfin (nouveau format 12/11/2026)**. Les
|
|
6
|
+
tableaux d'épisodes ne contiennent plus d'URL mais `"lien:<id>"` ; le vrai
|
|
7
|
+
lien vient de `/api/lien.php?j=<jeton>&l=<id>`, qui répond
|
|
8
|
+
**`403 {"erreur":"origine"}`** dès que la requête paraît cross-site (le
|
|
9
|
+
fingerprint navigateur envoie `Sec-Fetch-Site: none`). C'était la cause du
|
|
10
|
+
`anime-sama::signed` affiché puis de « Impossible de résoudre l'URL du
|
|
11
|
+
flux ». FreeFlix signe maintenant en `same-origin`, avec rafraîchissement du
|
|
12
|
+
jeton expiré.
|
|
13
|
+
- **Tous les lecteurs visibles sont réellement lisibles** : les lignes
|
|
14
|
+
indirectes sont résolues **en parallèle avant le menu** (et non plus une par
|
|
15
|
+
une au clic), donc les lecteurs morts (uqload bloqué par sa page « premium »,
|
|
16
|
+
ids sendvid supprimés) disparaissent au lieu de faire échouer la lecture.
|
|
17
|
+
L'analyse de qualité (résolutions + bitrate) tourne ensuite normalement sur
|
|
18
|
+
ces URL réelles : `1080p ~4.9 · 480p ~0.5 Mbps`, `720p ~1.1 Mbps` mesurés.
|
|
19
|
+
- **Déduplication corrigée** : toutes les lignes indirectes partagent la même
|
|
20
|
+
URL factice, ce qui réduisait un épisode à un seul « Lecteur 1 ». L'identité
|
|
21
|
+
du slot (épisode/lecteur/id) est maintenant la clé.
|
|
22
|
+
- **Saisons 9× plus rapides** : un seul téléchargement de page (les langues
|
|
23
|
+
déclarées par le site) au lieu de sonder neuf `episodes.js` en 404.
|
|
24
|
+
- **« Nouveautés » visible dès l'ouverture** : le flux coûtait ~30 s de réseau
|
|
25
|
+
alors que l'écran n'attendait que 8 s — d'où la liste vide jusqu'à un détour
|
|
26
|
+
par le Planning. Il est désormais persisté sur disque (affichage immédiat au
|
|
27
|
+
lancement), rafraîchi en parallèle (4 séries à la fois) et l'attente est
|
|
28
|
+
portée à 45 s.
|
|
29
|
+
- **Match : message court** quand la journée est vide —
|
|
30
|
+
« Aucun match disponible aujourd'hui. » au lieu d'un mur de diagnostics.
|
|
31
|
+
- **Navigation fluide dans la recherche et le planning** : les touches d'une
|
|
32
|
+
même rafale (auto-repeat, frappes pendant le rendu d'une affiche) étaient
|
|
33
|
+
**silencieusement jetées** — pire, une touche Entrée coincée dans le même
|
|
34
|
+
lot était avalée et l'écran semblait bloqué. Décodage un-événement-par-lot
|
|
35
|
+
avec rejeu du reste, et l'affiche sixel n'est plus repeinte (~128 ms, 0,5 Mo
|
|
36
|
+
par appui) qu'une fois la navigation stabilisée. Mesures : 10 flèches
|
|
37
|
+
rapides = 10/10 appliquées (avant 1/10), survol souris 120 rapports = retour
|
|
38
|
+
en 10 ms (avant > 30 s).
|
|
39
|
+
- **Sortie propre** : Rich écrivait `?1049l` trois fois, et Konsole rejouait
|
|
40
|
+
son écran sauvegardé à chaque fois — d'où la ligne « Que veux-tu faire ?
|
|
41
|
+
Quitter » ré-affichée sous le prompt du shell. Une seule sortie d'écran
|
|
42
|
+
alterné, purge des rapports souris en vol, et Ctrl-C affiche « Au revoir ! »
|
|
43
|
+
au lieu d'un traceback.
|
|
44
|
+
- **Windows : lecture réparée**. Le mpv/ffmpeg téléchargé par le setup était
|
|
45
|
+
installé dans un dossier hors PATH que la résolution ignorait (et
|
|
46
|
+
`_resolve_tool` cherchait `ffmpeg` sans `.exe`) → « mpv is not installed »
|
|
47
|
+
alors qu'il était là. Aussi : `select()` sur un pipe (crash
|
|
48
|
+
`WinError 10038` du préflight Match) remplacé par un lecteur portable,
|
|
49
|
+
`-nostdin` + `CREATE_NO_WINDOW` sur les pipes ffmpeg, port du proxy publié
|
|
50
|
+
avant le bind réel (course → « connection refused »), en-têtes du mode
|
|
51
|
+
direct mpv joints par des virgules, VLC Windows routé par le proxy HLS.
|
|
52
|
+
- **Installation idempotente** : les « fausses demandes » à chaque lancement
|
|
53
|
+
venaient d'un setup dont le succès dépendait du run courant (un shader
|
|
54
|
+
manquant remettait `setup_done=False` pour toujours). Le succès est dérivé
|
|
55
|
+
des fichiers réellement sur disque, une réparation silencieuse ne
|
|
56
|
+
télécharge que le manquant et ne pose plus de question, les shaders sont
|
|
57
|
+
validés par leur contenu (plus de re-téléchargement quand Anime4K change en
|
|
58
|
+
amont), la Nerd Font n'est plus retéléchargée, le wrapper PRIME et
|
|
59
|
+
FlareSolverr ne sont plus réécrits/recréés. Scripts : pré-vérification des
|
|
60
|
+
paquets (2ᵉ run : 0 install, 0 téléchargement, config utilisateur
|
|
61
|
+
conservée), `uv tool upgrade` au lieu de `--force`, mode non-TTY sans
|
|
62
|
+
abandon, PowerShell 5.1 sans `NativeCommandError`, PATH du bon shell.
|
|
63
|
+
- **`--doctor` honnête** : un binaire système n'est plus annoncé « managed
|
|
64
|
+
MISSING », FlareSolverr non lancé = « NOT RUNNING (optional) », versions
|
|
65
|
+
minimales comparées, commande de réinstallation adaptée à uv/pipx/pip.
|
|
66
|
+
- 752 tests au vert, ruff et shellcheck OK.
|
|
67
|
+
|
|
68
|
+
## 2.4.4
|
|
69
|
+
|
|
70
|
+
- **animes-sama.fr (avec un « s ») réparé sur trois points**. Ce miroir a
|
|
71
|
+
divergé de `anime-sama.to` et cassait FreeFlix silencieusement :
|
|
72
|
+
1. la recherche utilise une **troisième mise en page** (`div.card-title`,
|
|
73
|
+
sans titre `h2`/`h3`) → **0 résultat** pour toute requête ;
|
|
74
|
+
2. `<langue>/episodes.js` répond **404** : les épisodes sont désormais
|
|
75
|
+
**inline** dans la page de saison → « Aucun épisode trouvé » ;
|
|
76
|
+
3. ces tableaux ne contiennent plus d'URL mais des **drapeaux `"1"`/`""`** :
|
|
77
|
+
le vrai lien est fabriqué à la demande par l'API signée
|
|
78
|
+
`/api/player/sign` puis `/api/player/resolve`. FreeFlix sait maintenant
|
|
79
|
+
signer et résoudre ces liens (même session = même cookie CSRF, sinon
|
|
80
|
+
`419`), donc ces lecteurs jouent au lieu d'être masqués.
|
|
81
|
+
- **Vérifié sur Avatar, le dernier maître de l'air** (saisons 1, 3, 4, film,
|
|
82
|
+
VF et VOSTFR) : 2 à 3 lecteurs par épisode, chaîne complète
|
|
83
|
+
sign → resolve → `PLAYABLE`.
|
|
84
|
+
- **`oneupload.to` masqué** : le domaine répond `Connection refused` depuis
|
|
85
|
+
six pays testés (HK, ID, IR, TR…) et depuis ici — il est mort. C'était le
|
|
86
|
+
« Lecteur 1 » de nombreuses séries.
|
|
87
|
+
- **Anime-Sama : les lecteurs Voe rotatifs s'affichent enfin**. Le site sert
|
|
88
|
+
son lecteur Voe sous des domaines jetables qui changent tous les jours
|
|
89
|
+
(`claireguessthousand`, `derricknationtop`, `ashleyhardat`,
|
|
90
|
+
`shanelocaldog`…). Comme la liste était figée dans le code, ces lignes
|
|
91
|
+
étaient **masquées comme « non supportées »** alors que l'épisode se lit.
|
|
92
|
+
La détection reconnaît maintenant la forme de l'URL (`/e/<id>`) puis
|
|
93
|
+
vérifie en direct (avec cache) que la page parle bien Voe, ce qui couvre
|
|
94
|
+
les prochains domaines sans nouvelle version.
|
|
95
|
+
- **Contrôle complet des lecteurs Anime-Sama** (7 séries, 2026-11-11) :
|
|
96
|
+
`ansembed`, `smoothpre` et les miroirs Voe → **lisibles** ;
|
|
97
|
+
`uqload` réclame désormais de désactiver l'adblocker (bloqué côté site),
|
|
98
|
+
`sibnet` répond 403 sur toute la plateforme, `sendvid` est supprimé (404),
|
|
99
|
+
`vk.com` et `oneupload` ne sont pas des lecteurs supportés.
|
|
100
|
+
- 6 tests dédiés (`tests/test_voe_mirrors.py`).
|
|
101
|
+
- **Fin du `avformat_open_input() failed` incompréhensible**. Un lien CDN
|
|
102
|
+
signé peut expirer entre la résolution et la lecture : la playlist répond
|
|
103
|
+
encore `200` avec tous les segments, mais **chaque segment revient en 403**.
|
|
104
|
+
mpv s'arrêtait sur cette erreur ffmpeg brute, VLC affichait une fenêtre
|
|
105
|
+
noire — impossible de distinguer « lien expiré » de « installation cassée ».
|
|
106
|
+
Un préflight HLS vérifie maintenant la playlist **et le premier segment**
|
|
107
|
+
avant d'ouvrir le lecteur :
|
|
108
|
+
« Ce lien de lecture a expiré (le CDN refuse les segments). »
|
|
109
|
+
+ « Rouvre l'épisode ou choisis une autre source pour obtenir un lien neuf. »
|
|
110
|
+
- **Relance automatique de mpv** : si le lecteur se ferme en moins de 3 s
|
|
111
|
+
(échec du démultiplexeur), FreeFlix re-sonde le flux et retente une fois au
|
|
112
|
+
lieu d'abandonner.
|
|
113
|
+
- Le préflight est volontairement **non bloquant** : une erreur réseau, une
|
|
114
|
+
playlist multipiste, un direct sans segment ou un `.mp4` ne refusent jamais
|
|
115
|
+
une lecture. Un flux qui marche n'est pas bloqué par la sonde.
|
|
116
|
+
- 7 tests dédiés (`tests/test_hls_preflight.py`) : expiré, 410, hors ligne,
|
|
117
|
+
flux valide, master playlist, panne réseau, fichier direct.
|
|
118
|
+
- 657 tests au vert, ruff OK.
|
|
119
|
+
|
|
120
|
+
## 2.4.3
|
|
121
|
+
|
|
122
|
+
- **Installation initiale réparée** : le pin SHA-256 de `mpv.conf` n'avait pas
|
|
123
|
+
suivi la suppression du chargement Anime4K, donc un premier lancement
|
|
124
|
+
téléchargeait le bon fichier puis le refusait (« somme de contrôle
|
|
125
|
+
INCORRECTE »). Le pin est corrigé, le message distingue désormais un fichier
|
|
126
|
+
modifié en amont d'un binaire altéré, et un test CI compare les pins aux
|
|
127
|
+
fichiers `config/` pour empêcher la dérive. Un pin reste valide que le
|
|
128
|
+
fichier arrive en LF (HTTPS) ou en CRLF (copie Windows).
|
|
129
|
+
- **Un `mpv.conf` déjà présent n'est plus écrasé** par le setup (Linux et
|
|
130
|
+
macOS) : un réglage personnel (`hwdec=vaapi-copy`, cache, HDR…) survit
|
|
131
|
+
désormais à un relancement du premier lancement. Seuls les fichiers
|
|
132
|
+
réellement absents sont téléchargés.
|
|
133
|
+
- **mpv sous Windows : vrai binaire CLI** : l'installateur pose
|
|
134
|
+
`shinchiro.mpv` (puis `mpv-player.mpv-CI.MSVC`, `mpv.net` en dernier
|
|
135
|
+
recours) au lieu de `mpv.net` seul. `mpv.net` ne fournit que `mpvnet.exe`,
|
|
136
|
+
une interface graphique qui n'accepte pas toujours la lecture sur stdin,
|
|
137
|
+
l'IPC ou `--config-dir` — d'où les échecs de lecture. La découverte de mpv
|
|
138
|
+
cherche aussi les dossiers winget versionnés, Scoop et Chocolatey, et le
|
|
139
|
+
dossier `WinGet\Links` est ajouté au PATH après installation.
|
|
140
|
+
- **Faux positif antivirus documenté** : Defender signale parfois
|
|
141
|
+
l'installateur PowerShell ou le binaire PyInstaller comme
|
|
142
|
+
`Trojan:Win32/Commando.*!ml` / `Wacatac.*!ml` (heuristique `!ml`, pas une
|
|
143
|
+
signature). Le README remplace le `iwr | iex` par un téléchargement +
|
|
144
|
+
`Unblock-File`, et `docs/TROUBLESHOOTING.md` explique comment vérifier le
|
|
145
|
+
SHA-256, restaurer le fichier et signaler le faux positif.
|
|
146
|
+
- **Match : erreur YacineTV clarifiée**. Quand l'API répond `200` avec un
|
|
147
|
+
catalogue vide, FreeFlix l'annonce comme tel (« côté serveur, 0 rencontre »)
|
|
148
|
+
au lieu d'afficher l'erreur `HTTP 436` du host de secours, qui faisait croire
|
|
149
|
+
à un problème de connexion. Le cache précédent reste utilisé si disponible.
|
|
150
|
+
- 635 tests au vert, ruff OK.
|
|
151
|
+
|
|
152
|
+
## 2.4.2
|
|
153
|
+
|
|
154
|
+
- **Match ne reste plus bloqué sur un écran vide** : si l'API répond `[]`
|
|
155
|
+
(entre deux journées, ou incident côté serveur), FreeFlix garde le dernier
|
|
156
|
+
catalogue connu et affiche un message clair (« Le catalogue est vide entre
|
|
157
|
+
deux journées ; réessaie plus tard. ») au lieu d'une liste vide.
|
|
158
|
+
- En cas d'erreur réseau, message explicite + pause pour pouvoir lire la
|
|
159
|
+
raison, puis retour au menu.
|
|
160
|
+
- 625 tests au vert, ruff OK.
|
|
161
|
+
|
|
162
|
+
## 2.4.1
|
|
163
|
+
|
|
164
|
+
- **Match : une seule ligne par affiche**. Le catalogue répétait un même
|
|
165
|
+
match sur plusieurs créneaux (un « Twente - Ajax » en direct alors qu'un
|
|
166
|
+
autre du même jour était déjà terminé). Une entrée live/à venir/terminée
|
|
167
|
+
par match, la plus utile gagne.
|
|
168
|
+
- **Bascule automatique de source** : un match en direct expose désormais
|
|
169
|
+
HD, SD, Low et des flux **Multi**. Si la qualité choisie est refusée par
|
|
170
|
+
le CDN, FreeFlix essaie les autres sources au lieu de s'arrêter.
|
|
171
|
+
- **Contrôle avant lecture fiable** : le préflight passe par **ffmpeg en
|
|
172
|
+
direct** (comme la lecture) et lit sa sortie en continu. Le proxy local
|
|
173
|
+
déclenchait des 404 anti-hotlink et un live qui joue « sans fin » était
|
|
174
|
+
pris pour une panne — les deux sont corrigés. Les erreurs sont
|
|
175
|
+
distinguées : `403` (serveur qui refuse) vs `404/invalid data` (flux sans
|
|
176
|
+
données).
|
|
177
|
+
- **Multi en tête** : les flux « Multi » passent avant HD/SD/Low (source la
|
|
178
|
+
plus fiable en direct) ; tri stable, ordre du catalogue conservé à
|
|
179
|
+
qualité égale.
|
|
180
|
+
- Proxy HLS : rafraîchissement automatique de la playlist quand un segment
|
|
181
|
+
devient introuvable.
|
|
182
|
+
- 618 tests au vert, ruff OK.
|
|
183
|
+
|
|
3
184
|
## 2.4.0
|
|
4
185
|
|
|
5
186
|
- **Match regroupé par statut** : sections `En direct (n)`, `À venir (n)`,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: freeflix-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.0
|
|
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
|
|
@@ -143,12 +143,21 @@ No git, no package manager, no sudo needed. Just Python (or PowerShell on Window
|
|
|
143
143
|
# Linux / macOS
|
|
144
144
|
curl -fsSL https://raw.githubusercontent.com/freedy237/freeflix-cli/main/scripts/install.py | python3
|
|
145
145
|
|
|
146
|
-
# Windows (PowerShell)
|
|
147
|
-
|
|
146
|
+
# Windows (PowerShell) — download, inspect, then run
|
|
147
|
+
irm https://raw.githubusercontent.com/freedy237/freeflix-cli/main/scripts/install.ps1 -OutFile install.ps1
|
|
148
|
+
Unblock-File .\install.ps1
|
|
149
|
+
powershell -ExecutionPolicy Bypass -File .\install.ps1
|
|
148
150
|
```
|
|
149
151
|
|
|
150
152
|
This installs `uv` if missing, then `freeflix-cli`.
|
|
151
153
|
|
|
154
|
+
> **Antivirus on Windows:** Defender's ML engine sometimes flags the installer
|
|
155
|
+
> or the standalone `.exe` as `Trojan:Win32/Commando.*!ml` or
|
|
156
|
+
> `.../Wacatac.*!ml`. The `!ml` suffix means a heuristic guess, not a virus
|
|
157
|
+
> signature — it fires on unsigned installers that download and run things.
|
|
158
|
+
> Verify the SHA-256 published with the release, then restore/add an exclusion.
|
|
159
|
+
> Details: [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md#windows--antivirus-faux-positif).
|
|
160
|
+
|
|
152
161
|
### From PyPI (if you already have the system deps)
|
|
153
162
|
|
|
154
163
|
If `mpv`, `yt-dlp`, `ffmpeg`, `aria2` and `libnotify` are installed :
|
|
@@ -111,12 +111,21 @@ No git, no package manager, no sudo needed. Just Python (or PowerShell on Window
|
|
|
111
111
|
# Linux / macOS
|
|
112
112
|
curl -fsSL https://raw.githubusercontent.com/freedy237/freeflix-cli/main/scripts/install.py | python3
|
|
113
113
|
|
|
114
|
-
# Windows (PowerShell)
|
|
115
|
-
|
|
114
|
+
# Windows (PowerShell) — download, inspect, then run
|
|
115
|
+
irm https://raw.githubusercontent.com/freedy237/freeflix-cli/main/scripts/install.ps1 -OutFile install.ps1
|
|
116
|
+
Unblock-File .\install.ps1
|
|
117
|
+
powershell -ExecutionPolicy Bypass -File .\install.ps1
|
|
116
118
|
```
|
|
117
119
|
|
|
118
120
|
This installs `uv` if missing, then `freeflix-cli`.
|
|
119
121
|
|
|
122
|
+
> **Antivirus on Windows:** Defender's ML engine sometimes flags the installer
|
|
123
|
+
> or the standalone `.exe` as `Trojan:Win32/Commando.*!ml` or
|
|
124
|
+
> `.../Wacatac.*!ml`. The `!ml` suffix means a heuristic guess, not a virus
|
|
125
|
+
> signature — it fires on unsigned installers that download and run things.
|
|
126
|
+
> Verify the SHA-256 published with the release, then restore/add an exclusion.
|
|
127
|
+
> Details: [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md#windows--antivirus-faux-positif).
|
|
128
|
+
|
|
120
129
|
### From PyPI (if you already have the system deps)
|
|
121
130
|
|
|
122
131
|
If `mpv`, `yt-dlp`, `ffmpeg`, `aria2` and `libnotify` are installed :
|
|
@@ -88,6 +88,67 @@ fois) ; annule avec `Échap Échap`.
|
|
|
88
88
|
- Police Nerd : l'installateur la pose et configure Windows Terminal ; ferme
|
|
89
89
|
**toutes** les fenêtres pour que la police s'applique.
|
|
90
90
|
|
|
91
|
+
## Windows : antivirus (faux positif)
|
|
92
|
+
|
|
93
|
+
Defender (ou un antivirus tiers) peut signaler `scripts/install.ps1` ou
|
|
94
|
+
`freeflix-windows-x86_64.exe` comme :
|
|
95
|
+
|
|
96
|
+
- `Trojan:Win32/Commando.A!ml` / `Commando.C!ml`
|
|
97
|
+
- `Trojan:Script/Wacatac.*!ml`
|
|
98
|
+
- `Program:Win32/Wacapew.*!ml`
|
|
99
|
+
|
|
100
|
+
Le suffixe `!ml` = détection **heuristique** (machine learning), pas une
|
|
101
|
+
signature. Un installeur PowerShell non signé qui télécharge `uv`, écrit dans
|
|
102
|
+
le PATH et installe des paquets déclenche régulièrement ces faux positifs ;
|
|
103
|
+
c'est aussi le cas classique des binaires PyInstaller.
|
|
104
|
+
|
|
105
|
+
**Vérifier avant de faire confiance :**
|
|
106
|
+
|
|
107
|
+
1. Le script est public et lisible en entier :
|
|
108
|
+
<https://github.com/freedy237/freeflix-cli/blob/main/scripts/install.ps1>
|
|
109
|
+
2. Compare le SHA-256 du binaire téléchargé avec `SHA256SUMS` de la release :
|
|
110
|
+
`Get-FileHash .\freeflix-windows-x86_64.exe -Algorithm SHA256`
|
|
111
|
+
3. Envoie le fichier sur <https://www.virustotal.com> : quelques détections
|
|
112
|
+
heuristiques parmi 70 moteurs = faux positif probable.
|
|
113
|
+
|
|
114
|
+
**Débloquer :**
|
|
115
|
+
|
|
116
|
+
```powershell
|
|
117
|
+
# Fichier téléchargé depuis Internet : retire la marque "Mark of the Web"
|
|
118
|
+
Unblock-File .\install.ps1
|
|
119
|
+
Unblock-File .\freeflix-windows-x86_64.exe
|
|
120
|
+
|
|
121
|
+
# Restaure depuis la quarantaine (Sécurité Windows → Protection contre les
|
|
122
|
+
# virus → Historique des menaces → Autoriser sur l'appareil), puis ajoute une
|
|
123
|
+
# exclusion sur le dossier de l'app si nécessaire :
|
|
124
|
+
Add-MpPreference -ExclusionPath "$env:LOCALAPPDATA\Microsoft\WinGet\Packages"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Signale le faux positif à Microsoft :
|
|
128
|
+
<https://www.microsoft.com/en-us/wdsi/filesubmission>
|
|
129
|
+
|
|
130
|
+
## Windows : mpv introuvable ou ne démarre pas
|
|
131
|
+
|
|
132
|
+
Depuis 2.4.3, l'installateur pose un **vrai mpv CLI** (`shinchiro.mpv`, puis
|
|
133
|
+
`mpv-player.mpv-CI.MSVC` en secours) au lieu de `mpv.net`. `mpv.net` ne fournit
|
|
134
|
+
que `mpvnet.exe`, une interface graphique qui n'accepte pas toujours les
|
|
135
|
+
options utilisées par FreeFlix (lecture sur stdin, IPC, `--config-dir`) —
|
|
136
|
+
d'où les échecs de lecture.
|
|
137
|
+
|
|
138
|
+
```powershell
|
|
139
|
+
# Vérifier ce que FreeFlix trouve
|
|
140
|
+
freeflix --doctor
|
|
141
|
+
winget install --id shinchiro.mpv -e
|
|
142
|
+
# ou, si le paquet n'existe plus :
|
|
143
|
+
winget search mpv
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Après installation, ouvre un **nouveau** terminal (le lien winget est dans
|
|
147
|
+
`%LOCALAPPDATA%\Microsoft\WinGet\Links`). Si seul `mpvnet.exe` est présent,
|
|
148
|
+
FreeFlix l'utilise en dernier recours mais peut refuser certains flux : dans ce
|
|
149
|
+
cas installe VLC (`winget install --id VideoLAN.VLC -e`) et choisis VLC comme
|
|
150
|
+
lecteur des matchs.
|
|
151
|
+
|
|
91
152
|
## macOS
|
|
92
153
|
|
|
93
154
|
- `install-mac.sh` écrit dans `~/.config/freeflix/mpv` (dossier dédié).
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "freeflix-cli"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.5.0"
|
|
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"
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# FreeFlix CLI — macOS installer (Homebrew based)
|
|
3
|
+
#
|
|
4
|
+
# Re-runnable by design : already-installed brew packages are skipped, an
|
|
5
|
+
# existing tuned mpv.conf is never overwritten, shaders are only downloaded
|
|
6
|
+
# when missing, and prompts are skipped when stdin is not a terminal.
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
BLUE="\033[1;34m"; GREEN="\033[1;32m"; YELLOW="\033[1;33m"; RED="\033[1;31m"; NC="\033[0m"
|
|
11
|
+
log() { echo -e "${BLUE}»${NC} $*"; }
|
|
12
|
+
ok() { echo -e "${GREEN}✓${NC} $*"; }
|
|
13
|
+
warn() { echo -e "${YELLOW}!${NC} $*"; }
|
|
14
|
+
err() { echo -e "${RED}✗${NC} $*" >&2; }
|
|
15
|
+
|
|
16
|
+
# Take the default instead of hanging/aborting when piped (no TTY).
|
|
17
|
+
ask() {
|
|
18
|
+
local prompt="$1" default="$2" ans
|
|
19
|
+
if [[ ! -t 0 ]]; then
|
|
20
|
+
[[ "$default" == "y" ]]
|
|
21
|
+
return
|
|
22
|
+
fi
|
|
23
|
+
read -rp "$prompt" ans
|
|
24
|
+
ans="${ans:-$default}"
|
|
25
|
+
[[ "$ans" =~ ^[Yy] ]]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
29
|
+
|
|
30
|
+
# ─── 1. Ensure Homebrew ──────────────────────────────────────────────
|
|
31
|
+
if ! command -v brew >/dev/null 2>&1; then
|
|
32
|
+
log "Homebrew not found, installing it…"
|
|
33
|
+
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
|
34
|
+
# Add brew to PATH for this session (Apple Silicon vs Intel default paths)
|
|
35
|
+
if [[ -x /opt/homebrew/bin/brew ]]; then
|
|
36
|
+
eval "$(/opt/homebrew/bin/brew shellenv)"
|
|
37
|
+
elif [[ -x /usr/local/bin/brew ]]; then
|
|
38
|
+
eval "$(/usr/local/bin/brew shellenv)"
|
|
39
|
+
fi
|
|
40
|
+
fi
|
|
41
|
+
ok "Homebrew available"
|
|
42
|
+
|
|
43
|
+
# ─── 2. Install system packages (only the missing ones) ──────────────
|
|
44
|
+
# `brew list --versions` is a cheap local check ; calling `brew install`
|
|
45
|
+
# again for present packages re-ran the whole dependency resolution and
|
|
46
|
+
# printed upgrade noise on every re-run.
|
|
47
|
+
brew_installed() {
|
|
48
|
+
brew list --versions "$1" >/dev/null 2>&1 \
|
|
49
|
+
|| brew list --cask --versions "$1" >/dev/null 2>&1
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
log "Installing system deps via brew (missing only)…"
|
|
53
|
+
for pkg in python yt-dlp ffmpeg aria2 mpv chafa; do
|
|
54
|
+
if brew_installed "$pkg"; then
|
|
55
|
+
continue
|
|
56
|
+
fi
|
|
57
|
+
brew install --quiet "$pkg" || warn "Could not install $pkg — continuing."
|
|
58
|
+
done
|
|
59
|
+
if ! brew_installed vlc; then
|
|
60
|
+
brew install --quiet --cask vlc 2>/dev/null \
|
|
61
|
+
|| brew install --quiet vlc 2>/dev/null \
|
|
62
|
+
|| warn "VLC unavailable — mpv alone is enough to watch."
|
|
63
|
+
fi
|
|
64
|
+
ok "System packages ready"
|
|
65
|
+
|
|
66
|
+
# ─── 2b. Nerd Font (crisp TUI icons) — idempotent ────────────────────
|
|
67
|
+
if brew_installed font-caskaydia-cove-nerd-font; then
|
|
68
|
+
ok "Nerd Font already installed"
|
|
69
|
+
elif brew install --quiet --cask font-caskaydia-cove-nerd-font 2>/dev/null; then
|
|
70
|
+
ok "Nerd Font installed (set your terminal font to 'CaskaydiaCove Nerd Font')"
|
|
71
|
+
else
|
|
72
|
+
warn "Nerd Font cask unavailable — emoji icons still work out of the box."
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# ─── 3. Install freeflix-cli (skip when already at this version) ─────
|
|
76
|
+
install_uv_if_missing() {
|
|
77
|
+
if command -v uv >/dev/null 2>&1; then
|
|
78
|
+
return 0
|
|
79
|
+
fi
|
|
80
|
+
log "uv not found — installing …"
|
|
81
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
82
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
83
|
+
if ! command -v uv >/dev/null 2>&1; then
|
|
84
|
+
err "Could not install uv automatically."
|
|
85
|
+
err "Visit https://docs.astral.sh/uv/#getting-started"
|
|
86
|
+
exit 1
|
|
87
|
+
fi
|
|
88
|
+
ok "uv installed"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
project_version() {
|
|
92
|
+
sed -n 's/^version = "\(.*\)"/\1/p' "$PROJECT_ROOT/pyproject.toml" | head -n 1
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
install_uv_if_missing
|
|
96
|
+
want="$(project_version)"
|
|
97
|
+
have="$(uv tool list 2>/dev/null | awk '/^freeflix-cli /{print $2; exit}' | sed 's/^v//')"
|
|
98
|
+
if [[ -n "$have" && -n "$want" && "$have" == "$want" \
|
|
99
|
+
&& "${FREEFLIX_FORCE_REINSTALL:-0}" != "1" ]]; then
|
|
100
|
+
ok "freeflix-cli ${have} already installed (no reinstall)"
|
|
101
|
+
else
|
|
102
|
+
log "Installing freeflix-cli${want:+ ${want}} via uv…"
|
|
103
|
+
uv tool install --force "$PROJECT_ROOT"
|
|
104
|
+
ok "freeflix command installed"
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
ensure_permanent_path() {
|
|
108
|
+
local bin_dir="$HOME/.local/bin"
|
|
109
|
+
mkdir -p "$bin_dir"
|
|
110
|
+
local rc
|
|
111
|
+
case "${SHELL:-}" in
|
|
112
|
+
*zsh*) rc="$HOME/.zshrc" ;;
|
|
113
|
+
*bash*) rc="$HOME/.bashrc" ;;
|
|
114
|
+
*) rc="$HOME/.profile" ;;
|
|
115
|
+
esac
|
|
116
|
+
if ! grep -qs "$bin_dir" "$rc" 2>/dev/null; then
|
|
117
|
+
{
|
|
118
|
+
echo ""
|
|
119
|
+
echo "# Added by FreeFlix CLI"
|
|
120
|
+
echo "export PATH=\"$bin_dir:\$PATH\""
|
|
121
|
+
} >> "$rc"
|
|
122
|
+
ok "$bin_dir added to $rc (source it or open a new terminal)"
|
|
123
|
+
else
|
|
124
|
+
ok "$bin_dir already in $rc"
|
|
125
|
+
fi
|
|
126
|
+
}
|
|
127
|
+
ensure_permanent_path
|
|
128
|
+
|
|
129
|
+
# ─── 4. Optional : mpv config + Anime4K (never overwrites) ──────────
|
|
130
|
+
MPV_CFG="$HOME/.config/freeflix/mpv"
|
|
131
|
+
|
|
132
|
+
install_file_once() {
|
|
133
|
+
local src="$1" dest="$2"
|
|
134
|
+
if [[ -s "$dest" ]]; then
|
|
135
|
+
return 0
|
|
136
|
+
fi
|
|
137
|
+
cp "$src" "$dest"
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
download_shader_once() {
|
|
141
|
+
local dest="$1" url="$2"
|
|
142
|
+
if [[ -s "$dest" ]]; then
|
|
143
|
+
return 0
|
|
144
|
+
fi
|
|
145
|
+
if curl -fsSL -o "$dest.part" "$url" && [[ -s "$dest.part" ]]; then
|
|
146
|
+
mv "$dest.part" "$dest"
|
|
147
|
+
else
|
|
148
|
+
rm -f "$dest.part"
|
|
149
|
+
warn "Could not download $(basename "$dest") — anime upscaling may be limited."
|
|
150
|
+
fi
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if [[ -s "$MPV_CFG/mpv.conf" && -s "$MPV_CFG/input.conf" \
|
|
154
|
+
&& -s "$MPV_CFG/scripts/freeflix_position.lua" ]]; then
|
|
155
|
+
ok "mpv config already installed (kept)"
|
|
156
|
+
elif ask "Install default mpv config (Anime4K toggle, anti-crash) ? [Y/n] " y; then
|
|
157
|
+
# FreeFlix launches mpv with --config-dir ~/.config/freeflix/mpv: writing
|
|
158
|
+
# the global ~/.config/mpv made Anime4K invisible to the app.
|
|
159
|
+
mkdir -p "$MPV_CFG/scripts" "$MPV_CFG/shaders"
|
|
160
|
+
install_file_once "$PROJECT_ROOT/config/mpv.conf" "$MPV_CFG/mpv.conf"
|
|
161
|
+
install_file_once "$PROJECT_ROOT/config/input.conf" "$MPV_CFG/input.conf"
|
|
162
|
+
install_file_once "$PROJECT_ROOT/config/freeflix_position.lua" \
|
|
163
|
+
"$MPV_CFG/scripts/freeflix_position.lua"
|
|
164
|
+
ok "mpv config installed (CTRL+1 toggles Anime4K)"
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
if [[ -d "$MPV_CFG/shaders" ]]; then
|
|
168
|
+
BASE="https://raw.githubusercontent.com/bloc97/Anime4K/master/glsl"
|
|
169
|
+
download_shader_once "$MPV_CFG/shaders/Anime4K_Clamp_Highlights.glsl" \
|
|
170
|
+
"$BASE/Restore/Anime4K_Clamp_Highlights.glsl"
|
|
171
|
+
# mpv.conf loads the S variant by default; CTRL+1 switches to VL.
|
|
172
|
+
download_shader_once "$MPV_CFG/shaders/Anime4K_Restore_CNN_S.glsl" \
|
|
173
|
+
"$BASE/Restore/Anime4K_Restore_CNN_S.glsl"
|
|
174
|
+
download_shader_once "$MPV_CFG/shaders/Anime4K_Upscale_CNN_x2_S.glsl" \
|
|
175
|
+
"$BASE/Upscale/Anime4K_Upscale_CNN_x2_S.glsl"
|
|
176
|
+
download_shader_once "$MPV_CFG/shaders/Anime4K_Restore_CNN_VL.glsl" \
|
|
177
|
+
"$BASE/Restore/Anime4K_Restore_CNN_VL.glsl"
|
|
178
|
+
download_shader_once "$MPV_CFG/shaders/Anime4K_Upscale_CNN_x2_VL.glsl" \
|
|
179
|
+
"$BASE/Upscale/Anime4K_Upscale_CNN_x2_VL.glsl"
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
# ─── 5. Optional : FlareSolverr (auto-solve Cloudflare) via Docker ───
|
|
183
|
+
flaresolverr_running() {
|
|
184
|
+
curl -fsS --max-time 2 http://127.0.0.1:8191/health >/dev/null 2>&1 \
|
|
185
|
+
|| curl -fsS --max-time 2 http://127.0.0.1:8191/ >/dev/null 2>&1
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if flaresolverr_running; then
|
|
189
|
+
ok "FlareSolverr already running on :8191"
|
|
190
|
+
elif ask "Set up FlareSolverr to auto-solve Cloudflare (needs Docker) ? [y/N] " n; then
|
|
191
|
+
if command -v docker >/dev/null 2>&1; then
|
|
192
|
+
if ! docker start flaresolverr >/dev/null 2>&1; then
|
|
193
|
+
if docker container inspect flaresolverr >/dev/null 2>&1; then
|
|
194
|
+
warn "FlareSolverr container exists but could not be started."
|
|
195
|
+
elif ! docker run -d --name flaresolverr --restart unless-stopped \
|
|
196
|
+
-p 8191:8191 ghcr.io/flaresolverr/flaresolverr:latest; then
|
|
197
|
+
warn "Could not start FlareSolverr — start it manually later."
|
|
198
|
+
fi
|
|
199
|
+
fi
|
|
200
|
+
ok "FlareSolverr requested (give it a moment to be ready)."
|
|
201
|
+
else
|
|
202
|
+
warn "Docker not found. Install Docker, then run the flaresolverr container."
|
|
203
|
+
fi
|
|
204
|
+
fi
|
|
205
|
+
|
|
206
|
+
echo
|
|
207
|
+
ok "Installation complete. Run : ${GREEN}freeflix${NC}"
|
|
208
|
+
log "Icons: set your terminal font to 'CaskaydiaCove Nerd Font', then in"
|
|
209
|
+
log "FreeFlix go to Settings > Icon Style > nerd (emoji is the default)."
|