Skip to content

Commit

Permalink
improve embedded metadata for crunchyroll
Browse files Browse the repository at this point in the history
  • Loading branch information
justin025 committed Feb 14, 2025
1 parent 5f09448 commit 0e1f178
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 41 deletions.
34 changes: 23 additions & 11 deletions src/onthespot/api/crunchyroll.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from ..constants import WVN_KEY
from ..otsconfig import config
from ..runtimedata import get_logger, account_pool
from ..utils import make_call
from ..utils import make_call, conv_list_format

logger = get_logger("api.crunchyroll")
PUBLIC_TOKEN = "dC1rZGdwMmg4YzNqdWI4Zm4wZnE6eWZMRGZNZnJZdktYaDRKWFMxTEVJMmNDcXUxdjVXYW4="
Expand Down Expand Up @@ -183,28 +183,40 @@ 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'
episode_data = make_call(url, headers=headers)
episode_data = make_call(f'{BASE_URL}/content/v2/cms/objects/{item_id.split("/")[0]}?ratings=true&images=true&locale=en-US', headers=headers)
info_dict = episode_data['data'][0]
# Doesn't seem to work with android bearer.
#genre_data = make_call(f'{BASE_URL}/content/v2/discover/categories?guid={item_id.split("/")[0]}&locale=en-US', headers=headers)
# Headers not required, 403 means data does not exist
copyright_data = make_call(f'https://static.crunchyroll.com/copyright/{item_id.split("/")[0]}.json')
# intro and credit timestamps (done in downloader step)
#https://static.crunchyroll.com/skip-events/production/G4VUQ588P.json
# genre (dont think mkv supports this)
#https://www.crunchyroll.com/content/v2/discover/categories?guid=G1XHJV0XM&locale=en-US
# copyright (dont think mkv supports this)
#https://static.crunchyroll.com/copyright/G1XHJV0XM.json
# I believe this url gives you the difference in time between different audio formats or skips, if applicable else 403. Not entirely sure.
#https://static.crunchyroll.com/datalab-intro-v2/G14U4XX4N.json

info_dict = episode_data['data'][0]
#genres = []
#for genre in genre_data['data']:
# genres.append(genre.get('localization', {}).get('title'))

copyright_string = ''
if copyright_data:
copyright_string = copyright_data.get('long_copyright')

info = {}
info['title'] = info_dict.get('title')
info['description'] = info_dict.get('description')
info['image_url'] = info_dict.get('images', {}).get('thumbnail', [])[0][-1].get('source')
info['show_name'] = info_dict.get('episode_metadata').get('series_title')
info['season_number'] = info_dict.get('episode_metadata').get('season_number')
info['episode_number'] = info_dict.get('episode_metadata').get('episode_number')
info['season_number'] = info_dict.get('episode_metadata', {}).get('season_number')
info['episode_number'] = info_dict.get('episode_metadata', {}).get('episode_number')
info['item_url'] = f"https://www.crunchyroll.com/watch/{item_id}"
#info['genre'] = conv_list_format(genres)
info['copyright'] = copyright_string
info['versions'] = info_dict.get('episode_metadata', {}).get('versions', {})
# Not accurate
#info['release_year'] = info_dict.get('release_year') if info_dict.get('release_year') else info_dict.get('upload_date')[:4]
info['item_id'] = item_id.split('/')[0]
info['explicit'] = True if int(info_dict.get('episode_metadata').get('extended_maturity_rating').get('rating')) >= 17 else False
info['explicit'] = True if int(info_dict.get('episode_metadata', {}).get('extended_maturity_rating', {}).get('rating')) >= 17 else False
info['is_playable'] = True

return info
Expand Down
47 changes: 23 additions & 24 deletions src/onthespot/downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,10 +485,6 @@ 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)
crunchyroll_close_stream(token, item_id, stream_token)

ydl_opts = {}
ydl_opts['quiet'] = True
ydl_opts['no_warnings'] = True
Expand All @@ -503,13 +499,8 @@ def run(self):
encrypted_files = []
video_files = []
subtitle_formats = []
for version in versions:
for version in item_metadata['versions']:
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
Expand All @@ -518,6 +509,11 @@ def run(self):
logger.error(e)
continue

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'

if self.gui:
self.progress.emit(item, self.tr("Downloading Video"), 1)
ydl_video_opts = ydl_opts
Expand Down Expand Up @@ -556,19 +552,22 @@ def run(self):
if not config.get('raw_media_download'):
if self.gui:
self.progress.emit(item, self.tr("Downloading Chapters"), 1)
chapter_data = requests.get(f'https://static.crunchyroll.com/skip-events/production/{version['guid']}.json').json()
chapter_file = temp_file_path + f' - {version['audio_locale']}.txt'
with open(chapter_file, 'w', encoding='utf-8') as file:
file.write(';FFMETADATA1\n')
for entry in ['intro', 'credits']:
if chapter_data.get(entry):
file.write(f'[CHAPTER]\nTIMEBASE=1/1\nSTART={chapter_data[entry].get('start')}\nEND={chapter_data[entry].get('end')}\ntitle={entry.title()}\nlanguage={version['audio_locale']}\n')
video_files.append({
'path': chapter_file,
'type': 'chapter',
'format': 'txt',
'language': version['audio_locale']
})
if not os.path.exists(chapter_file):
resp = requests.get(f'https://static.crunchyroll.com/skip-events/production/{version['guid']}.json')
if resp.status_code == 200:
chapter_data = resp.json()
with open(chapter_file, 'w', encoding='utf-8') as file:
file.write(';FFMETADATA1\n')
for entry in ['intro', 'credits']:
if chapter_data.get(entry):
file.write(f'[CHAPTER]\nTIMEBASE=1/1\nSTART={chapter_data[entry].get('start')}\nEND={chapter_data[entry].get('end')}\ntitle={entry.title()}\nlanguage={version['audio_locale']}\n')
video_files.append({
'path': chapter_file,
'type': 'chapter',
'format': 'txt',
'language': version['audio_locale']
})

if self.gui:
self.progress.emit(item, self.tr("Decrypting"), 99)
Expand Down Expand Up @@ -722,15 +721,15 @@ def run(self):
os.rename(file['path'], final_path)
file['path'] = final_path

if not config.get("raw_media_format"):
if not config.get("raw_media_download"):
item['item_status'] = 'Converting'
if self.gui:
self.progress.emit(item, self.tr("Converting"), 99)
if item_type == "episode":
output_format = config.get("show_file_format")
elif item_type == "movie":
output_format = config.get("movie_file_format")
convert_video_format(file_path, output_format, video_files, item_metadata)
convert_video_format(item, file_path, output_format, video_files, item_metadata)
item['file_path'] = file_path + '.' + output_format
else:
item['file_path'] = file_path + '.mp4'
Expand Down
22 changes: 16 additions & 6 deletions src/onthespot/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ def convert_audio_format(filename, bitrate, default_format):
os.remove(temp_name)


def convert_video_format(output_path, output_format, video_files, item_metadata):
def convert_video_format(item, output_path, output_format, video_files, item_metadata):
target_path = os.path.abspath(output_path)
file_name = os.path.basename(target_path)
filetype = os.path.splitext(file_name)[1]
Expand Down Expand Up @@ -268,19 +268,31 @@ def convert_video_format(output_path, output_format, video_files, item_metadata)

i += 1

format_map += [f'-metadata', f'title={item_metadata['title']}']
format_map += [f'-metadata', f'title={item_metadata.get('title')}']
#format_map += [f'-metadata', f'genre={item_metadata.get('genre')}']
format_map += [f'-metadata', f'copyright={item_metadata.get('copyright')}']
format_map += [f'-metadata', f'description={item_metadata.get('description')}']
#format_map += [f'-metadata', f'year={item_metadata.get('release_year')}']
# TV Show Specific Tags
if item['item_type'] == 'episode':
format_map += [f'-metadata', f'show={item_metadata.get('show_name')}']
format_map += [f'-metadata', f'episode_id={item_metadata.get('episode_number')}']
format_map += [f'-metadata', f'tvsn={item_metadata.get('season_number')}']

command += format_map

# Set log level based on environment variable
if int(os.environ.get('SHOW_FFMPEG_OUTPUT', 0)) == 0:
command += ['-loglevel', 'error', '-hide_banner', '-nostats']

command += ['-c', 'copy']

# Add user defined parameters
for param in config.get('ffmpeg_args'):
command.append(param)

command += ['-c', 'copy']
if output_format == 'mp4':
command += ['-c:s', 'mov_text']

# Add output parameter at last
command += [temp_file_path]
logger.debug(
Expand All @@ -294,8 +306,6 @@ def convert_video_format(output_path, output_format, video_files, item_metadata)

for file in video_files:
if os.path.exists(file['path']):
if file['format'] in ('srt', 'ass', 'vtt') and output_format != "mkv":
continue
os.remove(file['path'])

os.rename(temp_file_path, output_path + '.' + output_format)
Expand Down

0 comments on commit 0e1f178

Please sign in to comment.