Skip to content

Commit 6f3b6fc

Browse files
committed
crunchyroll v2
1 parent 622ab17 commit 6f3b6fc

File tree

8 files changed

+327
-626
lines changed

8 files changed

+327
-626
lines changed

StreamingCommunity/Api/Service/crunchyroll/film.py

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from StreamingCommunity.Lib.DASH.downloader import DASH_Downloader
1515

1616

17-
# Logi
17+
# Logic
1818
from .util.get_license import get_playback_session, CrunchyrollClient
1919

2020

@@ -25,7 +25,7 @@
2525

2626
def download_film(select_title: MediaItem) -> str:
2727
"""
28-
Downloads a film using the provided film ID, title name, and domain.
28+
Downloads a film.
2929
3030
Parameters:
3131
- select_title (MediaItem): The selected media item.
@@ -38,28 +38,21 @@ def download_film(select_title: MediaItem) -> str:
3838

3939
# Initialize Crunchyroll client
4040
client = CrunchyrollClient()
41-
if not client.start():
42-
console.print("[red]Failed to authenticate with Crunchyroll.")
43-
return None, True
4441

45-
# Define filename and path for the downloaded video
42+
# Define filename and path
4643
mp4_name = f"{os_manager.get_sanitize_file(select_title.name, select_title.date)}.{extension_output}"
4744
mp4_path = os.path.join(site_constants.MOVIE_FOLDER, mp4_name.replace(f".{extension_output}", ""))
4845

49-
# Get playback session
46+
# Extract media ID
5047
url_id = select_title.get('url').split('/')[-1]
51-
playback_result = get_playback_session(client, url_id)
52-
53-
# Check if access was denied (403)
54-
if playback_result is None:
55-
console.print("[red]✗ Access denied: This content requires a premium subscription")
56-
return None, False
5748

58-
mpd_url, mpd_headers, mpd_list_sub, token, _ = playback_result
49+
# Get playback session
50+
mpd_url, mpd_headers, mpd_list_sub, token, audio_locale = get_playback_session(client, url_id, None)
5951

60-
# Parse playback token from mpd_url
52+
# Parse playback token from URL
6153
parsed_url = urlparse(mpd_url)
6254
query_params = parse_qs(parsed_url.query)
55+
playback_guid = query_params.get('playbackGuid', [token])[0] if query_params.get('playbackGuid') else token
6356

6457
# Download the film
6558
dash_process = DASH_Downloader(
@@ -74,7 +67,7 @@ def download_film(select_title: MediaItem) -> str:
7467
license_headers = mpd_headers.copy()
7568
license_headers.update({
7669
"x-cr-content-id": url_id,
77-
"x-cr-video-token": query_params['playbackGuid'][0],
70+
"x-cr-video-token": playback_guid,
7871
})
7972

8073
if dash_process.download_and_decrypt(custom_headers=license_headers):
@@ -88,11 +81,5 @@ def download_film(select_title: MediaItem) -> str:
8881
os.remove(status['path'])
8982
except Exception:
9083
pass
91-
92-
# Delete stream after download to avoid TOO_MANY_ACTIVE_STREAMS
93-
playback_token = token or query_params.get('playbackGuid', [None])[0]
94-
if playback_token:
95-
client.delete_active_stream(url_id, playback_token)
96-
console.print("[dim]Playback session closed")
97-
84+
9885
return status['path'], status['stopped']

StreamingCommunity/Api/Service/crunchyroll/series.py

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# 16.03.25
22

33
import os
4+
import time
45
from urllib.parse import urlparse, parse_qs
56
from typing import Tuple
67

@@ -59,18 +60,17 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
5960
mp4_name = f"{map_episode_title(scrape_serie.series_name, index_season_selected, index_episode_selected, obj_episode.get('name'))}.{extension_output}"
6061
mp4_path = os_manager.get_sanitize_path(os.path.join(site_constants.SERIES_FOLDER, scrape_serie.series_name, f"S{index_season_selected}"))
6162

62-
# Get playback session
63+
# Get media ID and main_guid for complete subtitles
6364
url_id = obj_episode.get('url').split('/')[-1]
64-
playback_result = get_playback_session(client, url_id)
65+
main_guid = obj_episode.get('main_guid')
6566

66-
# Check if access was denied (403)
67-
if playback_result is None:
68-
console.print("[red]✗ Access denied: This episode requires a premium subscription")
69-
return None, False
67+
# Get playback session
68+
mpd_url, mpd_headers, mpd_list_sub, token, audio_locale = get_playback_session(client, url_id, main_guid)
7069

71-
mpd_url, mpd_headers, mpd_list_sub, token, _ = playback_result
70+
# Parse playback token from URL
7271
parsed_url = urlparse(mpd_url)
7372
query_params = parse_qs(parsed_url.query)
73+
playback_guid = query_params.get('playbackGuid', [token])[0] if query_params.get('playbackGuid') else token
7474

7575
# Download the episode
7676
dash_process = DASH_Downloader(
@@ -85,7 +85,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
8585
license_headers = mpd_headers.copy()
8686
license_headers.update({
8787
"x-cr-content-id": url_id,
88-
"x-cr-video-token": query_params['playbackGuid'][0],
88+
"x-cr-video-token": playback_guid,
8989
})
9090

9191
if dash_process.download_and_decrypt(custom_headers=license_headers):
@@ -100,14 +100,11 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
100100
except Exception:
101101
pass
102102

103-
# Delete episode stream to avoid TOO_MANY_ACTIVE_STREAMS
104-
playback_token = token or query_params.get('playbackGuid', [None])[0]
105-
if playback_token:
106-
client.delete_active_stream(url_id, playback_token)
107-
console.print("[dim]Playback session closed")
108-
103+
# Small delay between episodes to avoid rate limiting
104+
time.sleep(1)
109105
return status['path'], status['stopped']
110106

107+
111108
def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, download_all: bool = False, episode_selection: str = None) -> None:
112109
"""
113110
Handle downloading episodes for a specific season.
@@ -116,7 +113,7 @@ def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, dow
116113
- index_season_selected (int): Season number
117114
- scrape_serie (GetSerieInfo): Scraper object with series information
118115
- download_all (bool): Whether to download all episodes
119-
- episode_selection (str, optional): Pre-defined episode selection that bypasses manual input
116+
- episode_selection (str, optional): Pre-defined episode selection
120117
"""
121118
# Get episodes for the selected season
122119
episodes = scrape_serie.getEpisodeSeasons(index_season_selected)
@@ -154,19 +151,20 @@ def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, dow
154151
if stopped:
155152
break
156153

154+
157155
def download_series(select_season: MediaItem, season_selection: str = None, episode_selection: str = None) -> None:
158156
"""
159157
Handle downloading a complete series.
160158
161159
Parameters:
162160
- select_season (MediaItem): Series metadata from search
163-
- season_selection (str, optional): Pre-defined season selection that bypasses manual input
164-
- episode_selection (str, optional): Pre-defined episode selection that bypasses manual input
161+
- season_selection (str, optional): Pre-defined season selection
162+
- episode_selection (str, optional): Pre-defined episode selection
165163
"""
166164
scrape_serie = GetSerieInfo(select_season.url.split("/")[-1])
167165
seasons_count = scrape_serie.getNumberSeason()
168166

169-
# If season_selection is provided, use it instead of asking for input
167+
# If season_selection is provided, use it
170168
if season_selection is None:
171169
index_season_selected = display_seasons_list(scrape_serie.seasons_manager)
172170
else:

StreamingCommunity/Api/Service/crunchyroll/site.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def title_search(query: str) -> int:
5656
console.print(f"[cyan]Search url: [yellow]{api_url}")
5757

5858
try:
59-
response = client._request_with_retry('GET', api_url, params=params)
59+
response = client.request('GET', api_url, params=params)
6060
response.raise_for_status()
6161

6262
except Exception as e:

0 commit comments

Comments
 (0)