Source code for Code.youtube_dl_helper

# -*- coding: utf-8 -*-

# standard imports
import logging
import json
import os
import tempfile

# plex debugging
try:
    import plexhints  # noqa: F401
except ImportError:
    pass
else:  # the code is running outside of Plex
    from plexhints.log_kit import Log  # log kit
    from plexhints.prefs_kit import Prefs  # prefs kit

# imports from Libraries\Shared
from constants import plugin_identifier, plugin_support_data_directory
from typing import Optional
import youtube_dl

# get the plugin logger
plugin_logger = logging.getLogger(plugin_identifier)


[docs]def nsbool(value): # type: (bool) -> str """ Format a boolean value for a Netscape cookie jar file. Parameters ---------- value : py:class:`bool` The boolean value to format. Returns ------- str 'TRUE' or 'FALSE'. """ return 'TRUE' if value else 'FALSE'
[docs]def process_youtube(url): # type: (str) -> Optional[str] """ Process URL using `youtube_dl` Parameters ---------- url : str The URL of the YouTube video. Returns ------- Optional[str] The URL of the audio object. Examples -------- >>> process_youtube(url='https://www.youtube.com/watch?v=dQw4w9WgXcQ') ... """ cookie_jar_file = tempfile.NamedTemporaryFile(dir=plugin_support_data_directory, delete=False) cookie_jar_file.write('# Netscape HTTP Cookie File\n') youtube_dl_params = dict( cookiefile=cookie_jar_file.name, logger=plugin_logger, socket_timeout=10, youtube_include_dash_manifest=False, ) if Prefs['str_youtube_cookies']: try: cookies = json.loads(Prefs['str_youtube_cookies']) for cookie in cookies: include_subdom = cookie['domain'].startswith('.') expiry = int(cookie.get('expiry', 0)) values = [ cookie['domain'], nsbool(include_subdom), cookie['path'], nsbool(cookie['secure']), str(expiry), cookie['name'], cookie['value'] ] cookie_jar_file.write('{}\n'.format('\t'.join(values))) except Exception as e: Log.Exception('Failed to write YouTube cookies to file, will try anyway. Error: {}'.format(e)) cookie_jar_file.flush() cookie_jar_file.close() try: ydl = youtube_dl.YoutubeDL(params=youtube_dl_params) with ydl: try: result = ydl.extract_info( url=url, download=False # We just want to extract the info ) except Exception as exc: if isinstance(exc, youtube_dl.utils.ExtractorError) and exc.expected: Log.Info('YDL returned YT error while downloading {}: {}'.format(url, exc)) else: Log.Exception('YDL returned an unexpected error while downloading {}: {}'.format(url, exc)) return None if 'entries' in result: # Can be a playlist or a list of videos video_data = result['entries'][0] else: # Just a video video_data = result selected = { 'opus': { 'size': 0, 'audio_url': None }, 'mp4a': { 'size': 0, 'audio_url': None }, } if video_data: for fmt in video_data['formats']: # loop through formats, select largest audio size for better quality if 'audio only' in fmt['format']: if 'opus' == fmt['acodec']: temp_codec = 'opus' elif 'mp4a' == fmt['acodec'].split('.')[0]: temp_codec = 'mp4a' else: Log.Debug('Unknown codec: %s' % fmt['acodec']) continue # unknown codec filesize = int(fmt['filesize']) if filesize > selected[temp_codec]['size']: selected[temp_codec]['size'] = filesize selected[temp_codec]['audio_url'] = fmt['url'] audio_url = None if 0 < selected['opus']['size'] > selected['mp4a']['size']: audio_url = selected['opus']['audio_url'] elif 0 < selected['mp4a']['size'] > selected['opus']['size']: audio_url = selected['mp4a']['audio_url'] if audio_url and Prefs['bool_prefer_mp4a_codec']: # mp4a codec is preferred if selected['mp4a']['audio_url']: # mp4a codec is available audio_url = selected['mp4a']['audio_url'] elif selected['opus']['audio_url']: # fallback to opus :( audio_url = selected['opus']['audio_url'] return audio_url # return None or url found finally: try: os.remove(cookie_jar_file.name) except Exception as e: Log.Exception('Failed to delete cookie jar file: {}'.format(e))