Skip to content

Commit cae0832

Browse files
committed
Fix issue #408
1 parent dc35a9e commit cae0832

File tree

6 files changed

+374
-113
lines changed

6 files changed

+374
-113
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ jobs:
112112
--hidden-import=qbittorrentapi --hidden-import=qbittorrent \
113113
--hidden-import=bs4 --hidden-import=httpx --hidden-import=rich --hidden-import=tqdm \
114114
--hidden-import=m3u8 --hidden-import=psutil --hidden-import=unidecode \
115+
--hidden-import=python-dotenv --hidden-import=dotenv \
115116
--hidden-import=jsbeautifier --hidden-import=jsbeautifier.core \
116117
--hidden-import=jsbeautifier.javascript --hidden-import=jsbeautifier.javascript.beautifier \
117118
--hidden-import=jsbeautifier.unpackers --hidden-import=jsbeautifier.unpackers.packer \

StreamingCommunity/Api/Site/crunchyroll/film.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,13 @@ def download_film(select_title: MediaItem) -> str:
5454

5555
# Generate mpd and license URLs
5656
url_id = select_title.get('url').split('/')[-1]
57-
mpd_url, mpd_headers, mpd_list_sub = get_playback_session(client, url_id)
57+
mpd_url, mpd_headers, mpd_list_sub, token, audio_locale = get_playback_session(client, url_id)
58+
59+
# Parse playback token from mpd_url
5860
parsed_url = urlparse(mpd_url)
5961
query_params = parse_qs(parsed_url.query)
6062

61-
# Download the episode
63+
# Download the film
6264
dash_process = DASH_Downloader(
6365
license_url='https://www.crunchyroll.com/license/v1/license/widevine',
6466
mpd_url=mpd_url,
@@ -86,9 +88,10 @@ def download_film(select_title: MediaItem) -> str:
8688
except Exception:
8789
pass
8890

89-
# Delete stream after download
90-
token = query_params['playbackGuid'][0]
91-
if token:
92-
client.delete_active_stream(url_id, token)
91+
# Delete stream after download to avoid TOO_MANY_ACTIVE_STREAMS
92+
playback_token = token or query_params.get('playbackGuid', [None])[0]
93+
if playback_token:
94+
client.delete_active_stream(url_id, playback_token)
95+
console.print("[dim]✓ Playback session closed[/dim]")
9396

9497
return status['path'], status['stopped']

StreamingCommunity/Api/Site/crunchyroll/series.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
extension_output = config_manager.get("M3U8_CONVERSION", "extension")
4242

4343

44-
def download_video(index_season_selected: int, index_episode_selected: int, scrape_serie: GetSerieInfo) -> Tuple[str,bool]:
44+
def download_video(index_season_selected: int, index_episode_selected: int, scrape_serie: GetSerieInfo) -> Tuple[str, bool]:
4545
"""
4646
Downloads a specific episode from a specified season.
4747
@@ -59,7 +59,6 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
5959

6060
# Get episode information
6161
obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
62-
6362
console.print(f"\n[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{scrape_serie.series_name}[/cyan] \\ [bold magenta]{obj_episode.get('name')}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
6463

6564
# Define filename and path for the downloaded video
@@ -68,8 +67,9 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
6867

6968
# Generate mpd and license URLs
7069
url_id = obj_episode.get('url').split('/')[-1]
71-
72-
mpd_url, mpd_headers, mpd_list_sub = get_playback_session(client, url_id)
70+
71+
# Get playback session with token for cleanup
72+
mpd_url, mpd_headers, mpd_list_sub, token, audio_locale = get_playback_session(client, url_id)
7373
parsed_url = urlparse(mpd_url)
7474
query_params = parse_qs(parsed_url.query)
7575

@@ -101,9 +101,11 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
101101
except Exception:
102102
pass
103103

104-
# Delete episode stream
105-
token = query_params['playbackGuid'][0]
106-
client.delete_active_stream(url_id, token)
104+
# Delete episode stream to avoid TOO_MANY_ACTIVE_STREAMS
105+
playback_token = token or query_params.get('playbackGuid', [None])[0]
106+
if playback_token:
107+
client.delete_active_stream(url_id, playback_token)
108+
console.print("[dim]✓ Playback session closed[/dim]")
107109

108110
return status['path'], status['stopped']
109111

StreamingCommunity/Api/Site/crunchyroll/site.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
# 16.03.25
22

3-
43
# External libraries
54
from rich.console import Console
65

76

87
# Internal utilities
98
from StreamingCommunity.Util.config_json import config_manager
10-
from StreamingCommunity.Util.http_client import create_client_curl
119
from StreamingCommunity.Util.table import TVShowManager
1210

1311

@@ -36,19 +34,16 @@ def title_search(query: str) -> int:
3634
media_search_manager.clear()
3735
table_show_manager.clear()
3836

39-
# Check if device_id or etp_rt is present
4037
config = config_manager.get_dict("SITE_LOGIN", "crunchyroll")
4138
if not config.get('device_id') or not config.get('etp_rt'):
4239
console.print("[bold red] device_id or etp_rt is missing or empty in config.json.[/bold red]")
4340
raise Exception("device_id or etp_rt is missing or empty in config.json.")
4441

45-
# Initialize Crunchyroll client
4642
client = CrunchyrollClient()
4743
if not client.start():
4844
console.print("[bold red] Failed to authenticate with Crunchyroll.[/bold red]")
4945
raise Exception("Failed to authenticate with Crunchyroll.")
5046

51-
# Build new Crunchyroll API search URL
5247
api_url = "https://www.crunchyroll.com/content/v2/discover/search"
5348

5449
params = {
@@ -60,12 +55,10 @@ def title_search(query: str) -> int:
6055
"locale": "it-IT"
6156
}
6257

63-
headers = client._get_headers()
64-
6558
console.print(f"[cyan]Search url: [yellow]{api_url}")
6659

6760
try:
68-
response = create_client_curl(headers=headers).get(api_url, params=params)
61+
response = client._request_with_retry('GET', api_url, params=params)
6962
response.raise_for_status()
7063

7164
except Exception as e:
@@ -88,18 +81,20 @@ def title_search(query: str) -> int:
8881
elif item.get("type") == "series":
8982
meta = item.get("series_metadata", {})
9083

84+
# Heuristic: single episode series might be films
9185
if meta.get("episode_count") == 1 and meta.get("season_count", 1) == 1 and meta.get("series_launch_year"):
92-
tipo = "film" if "film" in item.get("description", "").lower() or "movie" in item.get("description", "").lower() else "tv"
86+
description = item.get("description", "").lower()
87+
if "film" in description or "movie" in description:
88+
tipo = "film"
89+
else:
90+
tipo = "tv"
9391
else:
9492
tipo = "tv"
95-
9693
else:
9794
continue
9895

9996
url = ""
100-
if tipo == "tv":
101-
url = f"https://www.crunchyroll.com/series/{item.get('id')}"
102-
elif tipo == "film":
97+
if tipo in ("tv", "film"):
10398
url = f"https://www.crunchyroll.com/series/{item.get('id')}"
10499
else:
105100
continue

0 commit comments

Comments
 (0)