Skip to content

Commit

Permalink
improve crunchyroll downloader
Browse files Browse the repository at this point in the history
  • Loading branch information
justin025 committed Feb 11, 2025
1 parent 665e1f7 commit 5c19953
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 55 deletions.
14 changes: 7 additions & 7 deletions src/onthespot/api/crunchyroll.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import base64
from hashlib import md5
import re
import json
import os
from uuid import uuid4
import re
import requests
import time
from yt_dlp import YoutubeDL
from uuid import uuid4
from pywidevine.cdm import Cdm
from pywidevine.pssh import PSSH
from pywidevine.device import Device
from yt_dlp import YoutubeDL
from ..constants import WVN_KEY
from ..otsconfig import config
from ..runtimedata import get_logger, account_pool
Expand Down Expand Up @@ -135,7 +135,7 @@ def crunchyroll_get_search_results(token, search_term, _):
params={}
params['q'] = search_term
params['n'] = config.get('max_search_results')
params["locale"] = config.get('preferred_subtitle_language').split(',')[0]
params["locale"] = 'en-US'
#params["type"] = "top_results,series,movie_listing,episode"

search_data = requests.get(f'{BASE_URL}/content/v2/discover/search', headers=headers, params=params).json()
Expand Down Expand Up @@ -183,7 +183,7 @@ def crunchyroll_get_episode_metadata(token, item_id):
headers['Content-Type'] = 'application/json'
headers['User-Agent'] = f'Crunchyroll/{APP_VERSION} Android/13 okhttp/4.12.0'

url = f'{BASE_URL}/content/v2/cms/objects/{item_id.split('/')[0]}?ratings=true&images=true&locale=en-US'
url = f'{BASE_URL}/content/v2/cms/objects/{item_id.split("/")[0]}?ratings=true&images=true&locale=en-US'
episode_data = make_call(url, headers=headers)
# intro and credit timestamps
#https://static.crunchyroll.com/skip-events/production/G4VUQ588P.json
Expand Down Expand Up @@ -239,7 +239,7 @@ def crunchyroll_get_mpd_info(token, episode_id):

params={}
params['queue'] = False
params["locale"] = config.get('preferred_audio_language').split(',')[0]
params["locale"] = 'en-US'

# Ensure you properly delete session otherwise app will lockup
url = f"https://cr-play-service.prd.crunchyrollsvc.com/v1/{episode_id.split('/')[0]}/android/phone/play"
Expand Down Expand Up @@ -301,5 +301,5 @@ def crunchyroll_close_stream(token, episode_id, stream_token):
headers['Content-Type'] = 'application/json'
headers['User-Agent'] = f'Crunchyroll/{APP_VERSION} Android/13 okhttp/4.12.0'

url = f'https://cr-play-service.prd.crunchyrollsvc.com/v1/token/{episode_id.split('/')[0]}/{stream_token}'
url = f'https://cr-play-service.prd.crunchyrollsvc.com/v1/token/{episode_id.split("/")[0]}/{stream_token}'
resp = requests.delete(url, headers=headers)
75 changes: 29 additions & 46 deletions src/onthespot/downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,11 +485,11 @@ def run(self):

# Video
elif item_service == "crunchyroll":
# Get versions and close stream
mpd_url, stream_token, default_audio_locale, headers, versions, subtitle_formats = crunchyroll_get_mpd_info(token, item_id)
decryption_key = crunchyroll_get_decryption_key(token, item_id, mpd_url, stream_token)
crunchyroll_close_stream(token, item_id, stream_token)

ydl_opts = {}
ydl_opts['http_headers'] = headers
ydl_opts['quiet'] = True
ydl_opts['no_warnings'] = True
ydl_opts['allow_unplayable_formats'] = True
Expand All @@ -499,64 +499,47 @@ def run(self):
if self.gui:
ydl_opts['progress_hooks'] = [lambda d: self.yt_dlp_progress_hook(item, d)]

# I would prefer to download video and audio together but yt-dlp
# appends a format string when ext is used together.
# Extract preferred language
encrypted_files = []

if self.gui:
self.progress.emit(item, self.tr("Downloading Video"), 1)
ydl_video_opts = ydl_opts
ydl_opts['outtmpl'] = temp_file_path + '.%(ext)s.%(ext)s'
ydl_video_opts['format'] = (f'(bestvideo[height<={config.get("preferred_video_resolution")}][ext=mp4]/bestvideo)')
with YoutubeDL(ydl_video_opts) as video:
encrypted_files.append({
'path': video.prepare_filename(video.extract_info(mpd_url, download=False)),
'type': 'video',
'decryption_key': decryption_key
})
video.download(mpd_url)

token = get_account_token(item_service)
headers['Authorization'] = f'Bearer {token}'
ydl_opts['http_headers'] = headers

if self.gui:
self.progress.emit(item, self.tr("Downloading Audio"), 1)
ydl_audio_opts = ydl_opts
ydl_audio_opts['outtmpl'] = temp_file_path + f' - {default_audio_locale}.%(ext)s.%(ext)s'
ydl_audio_opts['format'] = ('(bestaudio[ext=m4a]/bestaudio)')
with YoutubeDL(ydl_audio_opts) as audio:
encrypted_files.append({
'path': audio.prepare_filename(audio.extract_info(mpd_url, download=False)),
'decryption_key': decryption_key,
'type': 'audio',
'language': default_audio_locale
})
audio.download(mpd_url)

token = get_account_token(item_service)
crunchyroll_close_stream(token, item_id, stream_token)

# Extract additional audio languages
subtitle_formats = []
for version in versions:
if version['audio_locale'] == default_audio_locale:
continue
if version['audio_locale'] in config.get('preferred_audio_language').split(',') or config.get('download_all_available_audio'):
token = get_account_token(item_service)
headers['Authorization'] = f'Bearer {token}'
ydl_opts['http_headers'] = headers
ydl_opts['outtmpl'] = temp_file_path + f' - {version['audio_locale']}.%(ext)s.%(ext)s'

try:
mpd_url, stream_token, audio_locale, headers, versions, additional_subtitle_formats = crunchyroll_get_mpd_info(token, version['guid'])
subtitle_formats += additional_subtitle_formats
decryption_key = crunchyroll_get_decryption_key(token, version['guid'], mpd_url, stream_token)
except Exception as e:
logger.error(e)
continue
subtitle_formats += additional_subtitle_formats
decryption_key = crunchyroll_get_decryption_key(token, version['guid'], mpd_url, stream_token)

if self.gui:
self.progress.emit(item, self.tr("Downloading Video"), 1)
ydl_video_opts = ydl_opts
ydl_video_opts['format'] = (f'(bestvideo[height<={config.get("preferred_video_resolution")}][ext=mp4]/bestvideo)')
with YoutubeDL(ydl_video_opts) as video:
encrypted_files.append({
'path': video.prepare_filename(video.extract_info(mpd_url, download=False)),
'type': 'video',
'decryption_key': decryption_key,
'language': version['audio_locale']
})
video.download(mpd_url)

# I would prefer to download video and audio together but yt-dlp
# appends a format string when ext is used together.
token = get_account_token(item_service)
headers['Authorization'] = f'Bearer {token}'
ydl_opts['http_headers'] = headers

if self.gui:
self.progress.emit(item, self.tr("Downloading Audio"), 1)
ydl_audio_opts = ydl_opts
ydl_audio_opts['outtmpl'] = temp_file_path + f' - {version['audio_locale']}.%(ext)s.%(ext)s'
ydl_audio_opts['format'] = ('(bestaudio[ext=m4a]/bestaudio)')
ydl_audio_opts['http_headers'] = headers
with YoutubeDL(ydl_audio_opts) as audio:
encrypted_files.append({
'path': audio.prepare_filename(audio.extract_info(mpd_url, download=False)),
Expand Down
8 changes: 6 additions & 2 deletions src/onthespot/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ def convert_video_format(output_path, output_format, video_files):
command += ['-i', file['path']]
format_map += ['-map', f'{map_index}:{current_type[:1]}']

if file['type'] in ('audio', 'subtitle'):
if file.get('language'):
language = file.get('language')
format_map += [f'-metadata:s:{current_type[:1]}:{i}', f'title={file.get('language')}']
format_map += [f'-metadata:s:{current_type[:1]}:{i}', f'language={file.get('language')[:2]}']
Expand Down Expand Up @@ -661,7 +661,11 @@ def add_to_m3u_file(item, item_metadata):

# Check if the item_path is already in the M3U file
with open(m3u_path, 'r', encoding='utf-8') as m3u_file:
m3u_item_header = f"#EXTINF:{round(int(item_metadata['length'])/1000)}, {EXTINF}"
try:
ext_length = round(int(item_metadata['length'])/1000)
except Exception:
ext_length = '-1'
m3u_item_header = f"#EXTINF:{ext_length}, {EXTINF}"
m3u_contents = m3u_file.readlines()
if m3u_item_header not in [line.strip() for line in m3u_contents]:
with open(m3u_path, 'a', encoding='utf-8') as m3u_file:
Expand Down

0 comments on commit 5c19953

Please sign in to comment.