Skip to content

Commit d874f9c

Browse files
committed
Fix #407 2 / 2
1 parent 15cab24 commit d874f9c

File tree

14 files changed

+498
-290
lines changed

14 files changed

+498
-290
lines changed

.github/.site/login.md

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,14 @@
22

33
Follow the instructions below to obtain the required keys for each streaming service and add them to your `config.json`.
44

5-
## Crunchyroll: Get `etp_rt` and `x_cr_tab_id`
5+
## Crunchyroll: Get `etp_rt` and `device_id`
66

77
1. **Log in** to [Crunchyroll](https://www.crunchyroll.com/).
88

99
2. **Open Developer Tools** (<kbd>F12</kbd>).
1010

11-
3. **Get `etp_rt`:**
11+
3. **Get `etp_rt` and `device_id`:**
1212
- Go to the **Application** tab.
13-
- Find the `etp_rt` cookie under **Cookies** for the site.
14-
- **Copy** its value for `config.json`.
15-
- ![etp_rt location](./img/crunchyroll_etp_rt.png)
16-
17-
4. **Get `x_cr_tab_id`:**
18-
- Start playing any video.
19-
- Go to the **Network** tab.
20-
- Filter by **XHR** requests.
21-
- Select a request and find the `x-cr-tab-id` header.
22-
- **Copy** its value for `config.json`.
23-
- ![x_cr_tab_id location](./img/crunchyroll_x_cr_tab_id.png)
24-
25-
</TabItem>
26-
</Tabs>
13+
- Find the `etp_rt` and `device_id` cookies under **Cookies** for the site (you can use the filter/search field and search for `etp_rt` or `device_id`).
14+
- **Copy** their values for `config.json`.
15+
- ![etp_rt location](./img/crunchyroll_etp_rt.png)

StreamingCommunity/Api/Site/crunchyroll/film.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
# Player
2323
from StreamingCommunity import DASH_Downloader
24-
from .util.get_license import get_playback_session, get_auth_token, generate_device_id
24+
from .util.get_license import get_playback_session, CrunchyrollClient
2525

2626

2727
# Variable
@@ -42,14 +42,19 @@ def download_film(select_title: MediaItem) -> str:
4242
start_message()
4343
console.print(f"\n[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{select_title.name}[/cyan] \n")
4444

45+
# Initialize Crunchyroll client
46+
client = CrunchyrollClient()
47+
if not client.start():
48+
console.print("[bold red]Failed to authenticate with Crunchyroll.[/bold red]")
49+
return None, True
50+
4551
# Define filename and path for the downloaded video
4652
mp4_name = os_manager.get_sanitize_file(select_title.name) + ".mp4"
4753
mp4_path = os.path.join(site_constant.MOVIE_FOLDER, mp4_name.replace(".mp4", ""))
4854

4955
# Generate mpd and license URLs
5056
url_id = select_title.get('url').split('/')[-1]
51-
device_id = generate_device_id()
52-
mpd_url, mpd_headers, mpd_list_sub = get_playback_session(get_auth_token(device_id), device_id, url_id)
57+
mpd_url, mpd_headers, mpd_list_sub = get_playback_session(client, url_id)
5358
parsed_url = urlparse(mpd_url)
5459
query_params = parse_qs(parsed_url.query)
5560

@@ -82,4 +87,9 @@ def download_film(select_title: MediaItem) -> str:
8287
except Exception:
8388
pass
8489

90+
# Delete stream after download
91+
token = query_params['playbackGuid'][0]
92+
if token:
93+
client.delete_active_stream(url_id, token)
94+
8595
return status['path'], status['stopped']

StreamingCommunity/Api/Site/crunchyroll/series.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717

1818
# Logic class
19-
from .util.ScrapeSerie import GetSerieInfo, delete_stream_episode
19+
from .util.ScrapeSerie import GetSerieInfo
2020
from StreamingCommunity.Api.Template.Util import (
2121
manage_selection,
2222
map_episode_title,
@@ -30,7 +30,7 @@
3030

3131
# Player
3232
from StreamingCommunity import DASH_Downloader
33-
from .util.get_license import get_playback_session, get_auth_token, generate_device_id
33+
from .util.get_license import get_playback_session
3434

3535

3636
# Variable
@@ -52,6 +52,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
5252
- bool: Whether download was stopped
5353
"""
5454
start_message()
55+
client = scrape_serie.client
5556

5657
# Get episode information
5758
obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
@@ -64,10 +65,8 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
6465

6566
# Generate mpd and license URLs
6667
url_id = obj_episode.get('url').split('/')[-1]
67-
device_id = generate_device_id()
68-
token_mpd = get_auth_token(device_id)
6968

70-
mpd_url, mpd_headers, mpd_list_sub = get_playback_session(token_mpd, device_id, url_id)
69+
mpd_url, mpd_headers, mpd_list_sub = get_playback_session(client, url_id)
7170
parsed_url = urlparse(mpd_url)
7271
query_params = parse_qs(parsed_url.query)
7372

@@ -101,7 +100,9 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
101100
pass
102101

103102
# Delete episode stream
104-
delete_stream_episode(url_id, query_params['playbackGuid'][0], mpd_headers)
103+
token = query_params['playbackGuid'][0]
104+
if token:
105+
client.delete_active_stream(url_id, token)
105106

106107
return status['path'], status['stopped']
107108

StreamingCommunity/Api/Site/crunchyroll/site.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,13 @@
1010

1111
# Internal utilities
1212
from StreamingCommunity.Util.config_json import config_manager
13-
from StreamingCommunity.Util.headers import get_headers
1413
from StreamingCommunity.Util.table import TVShowManager
1514

1615

1716
# Logic class
1817
from StreamingCommunity.Api.Template.config_loader import site_constant
1918
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
20-
from .util.get_license import get_auth_token, generate_device_id
19+
from .util.get_license import CrunchyrollClient
2120

2221

2322
# Variable
@@ -40,9 +39,16 @@ def title_search(query: str) -> int:
4039
media_search_manager.clear()
4140
table_show_manager.clear()
4241

43-
# Check if x_cr_tab_id or etp_rt is present
44-
if config_manager.get_dict("SITE_LOGIN", "crunchyroll")['x_cr_tab_id'] is None or config_manager.get_dict("SITE_LOGIN", "crunchyroll")['x_cr_tab_id'] == "" or config_manager.get_dict("SITE_LOGIN", "crunchyroll")['etp_rt'] is None or config_manager.get_dict("SITE_LOGIN", "crunchyroll")['etp_rt'] == "":
45-
console.print("[bold red] x_cr_tab_id or etp_rt is missing or empty.[/bold red]")
42+
# Check if device_id or etp_rt is present
43+
config = config_manager.get_dict("SITE_LOGIN", "crunchyroll")
44+
if not config.get('device_id') or not config.get('etp_rt'):
45+
console.print("[bold red] device_id or etp_rt is missing or empty in config.json.[/bold red]")
46+
sys.exit(0)
47+
48+
# Initialize Crunchyroll client
49+
client = CrunchyrollClient()
50+
if not client.start():
51+
console.print("[bold red] Failed to authenticate with Crunchyroll.[/bold red]")
4652
sys.exit(0)
4753

4854
# Build new Crunchyroll API search URL
@@ -57,8 +63,7 @@ def title_search(query: str) -> int:
5763
"locale": "it-IT"
5864
}
5965

60-
headers = get_headers()
61-
headers['authorization'] = f"Bearer {get_auth_token(generate_device_id()).access_token}"
66+
headers = client._get_headers()
6267

6368
console.print(f"[cyan]Search url: [yellow]{api_url}")
6469

StreamingCommunity/Api/Site/crunchyroll/util/ScrapeSerie.py

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@
88

99

1010
# Internal utilities
11-
from StreamingCommunity.Util.headers import get_headers
1211
from StreamingCommunity.Util.config_json import config_manager
1312
from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager
14-
from .get_license import get_auth_token, generate_device_id
13+
from .get_license import CrunchyrollClient
1514

1615

1716
# Variable
@@ -45,26 +44,6 @@ def get_season_episodes(season_id, headers, params):
4544
)
4645
return response
4746

48-
def delete_stream_episode(episode_id, stream_id, headers):
49-
"""
50-
Deletes a specific stream episode by episode ID and stream ID.
51-
"""
52-
url = f'https://www.crunchyroll.com/playback/v1/token/{episode_id}/{stream_id}'
53-
headers = get_headers()
54-
55-
response = requests.delete(
56-
url,
57-
headers=headers,
58-
impersonate="chrome110"
59-
)
60-
61-
if response.status_code == 204:
62-
return True
63-
64-
else:
65-
logging.error(f"Failed to delete stream episode: {response.status_code} - {response.text}")
66-
return False
67-
6847

6948
class GetSerieInfo:
7049
def __init__(self, series_id):
@@ -76,8 +55,13 @@ def __init__(self, series_id):
7655
"""
7756
self.series_id = series_id
7857
self.seasons_manager = SeasonManager()
79-
self.headers = get_headers()
80-
self.headers['authorization'] = f"Bearer {get_auth_token(generate_device_id()).access_token}"
58+
59+
# Initialize Crunchyroll client
60+
self.client = CrunchyrollClient()
61+
if not self.client.start():
62+
raise Exception("Failed to authenticate with Crunchyroll")
63+
64+
self.headers = self.client._get_headers()
8165
self.params = {
8266
'force_locale': '',
8367
'preferred_audio_language': 'it-IT',
@@ -186,7 +170,6 @@ def _get_episode_audio_locales_and_urls(self, episode_id):
186170
if locale and guid:
187171
audio_locales.append(locale)
188172
urls_by_locale[locale] = f"https://www.crunchyroll.com/it/watch/{guid}"
189-
#print(f"Locale: {locale}, URL: {urls_by_locale[locale]}")
190173

191174
return audio_locales, urls_by_locale
192175

0 commit comments

Comments
 (0)