__filename__ = "webapp_podcast.py" __author__ = "Bob Mottram" __license__ = "AGPL3+" __version__ = "1.3.0" __maintainer__ = "Bob Mottram" __email__ = "bob@libreserver.org" __status__ = "Production" __module_group__ = "Web Interface Columns" import os import html import datetime import urllib.parse from shutil import copyfile from utils import get_config_param from utils import remove_html from media import path_is_audio from content import safe_web_text from webapp_utils import get_broken_link_substitute from webapp_utils import html_header_with_external_style from webapp_utils import html_footer from webapp_utils import html_keyboard_navigation from session import get_json def _html_podcast_chapters(link_url: str, session, session_onion, session_i2p, http_prefix: str, domain: str, podcast_properties: {}, translate: {}, debug: bool) -> str: """Returns html for chapters of a podcast """ if not podcast_properties: return '' key = 'chapters' if not podcast_properties.get(key): return '' if not isinstance(podcast_properties[key], dict): return '' if podcast_properties[key].get('url'): chapters_url = podcast_properties[key]['url'] elif podcast_properties[key].get('uri'): chapters_url = podcast_properties[key]['uri'] else: return '' html_str = '' if podcast_properties[key].get('type'): url_type = podcast_properties[key]['type'] curr_session = session if chapters_url.endswith('.onion'): curr_session = session_onion elif chapters_url.endswith('.i2p'): curr_session = session_i2p as_header = { 'Accept': url_type } if 'json' in url_type: chapters_json = \ get_json(None, curr_session, chapters_url, as_header, None, debug, __version__, http_prefix, domain) if not chapters_json: return '' if not chapters_json.get('chapters'): return '' if not isinstance(chapters_json['chapters'], list): return '' chapters_html = '' for chapter in chapters_json['chapters']: if not isinstance(chapter, dict): continue if not chapter.get('title'): continue if not chapter.get('startTime'): continue chapter_title = chapter['title'] chapter_url = '' if chapter.get('url'): chapter_url = chapter['url'] chapter_title = \ '' + \ chapter['title'] + '<\a>' start_sec = chapter['startTime'] skip_url = link_url + '#t=' + str(start_sec) start_time_str = \ '' + \ str(datetime.timedelta(seconds=start_sec)) + \ '' if chapter.get('img'): chapters_html += \ '
  • \n' + \ ' ' + start_time_str + '\n' + \ ' \n' + \ ' ' + chapter_title + '\n' + \ '
  • \n' if chapters_html: html_str = \ '
    \n' + \ ' \n
    \n' return html_str def _html_podcast_transcripts(podcast_properties: {}, translate: {}) -> str: """Returns html for transcripts of a podcast """ if not podcast_properties: return '' key = 'transcripts' if not podcast_properties.get(key): return '' if not isinstance(podcast_properties[key], list): return '' ctr = 1 html_str = '' for transcript in podcast_properties[key]: transcript_url = None if podcast_properties[key].get('url'): transcript_url = podcast_properties[key]['url'] elif podcast_properties[key].get('uri'): transcript_url = podcast_properties[key]['uri'] if not transcript_url: continue if ctr > 1: html_str += '
    ' html_str += '' html_str += translate['Transcript'] if ctr > 1: html_str += ' ' + str(ctr) html_str += '\n' ctr += 1 return html_str def _html_podcast_social_interactions(podcast_properties: {}, translate: {}, nickname: str) -> str: """Returns html for social interactions with a podcast """ if not podcast_properties: return '' key = 'discussion' if not podcast_properties.get(key): key = 'socialInteract' if not podcast_properties.get(key): return '' if not isinstance(podcast_properties[key], dict): return '' if podcast_properties[key].get('uri'): episode_post_url = podcast_properties[key]['uri'] elif podcast_properties[key].get('url'): episode_post_url = podcast_properties[key]['url'] elif podcast_properties[key].get('text'): episode_post_url = podcast_properties[key]['text'] else: return '' actor_str = '' podcast_account_id = None if podcast_properties[key].get('accountId'): podcast_account_id = podcast_properties[key]['accountId'] elif podcast_properties[key].get('podcastAccountUrl'): podcast_account_id = \ podcast_properties[key]['podcastAccountUrl'] if podcast_account_id: actor_handle = podcast_account_id if actor_handle.startswith('@'): actor_handle = actor_handle[1:] actor_str = '?actor=' + actor_handle podcast_str = \ '
    \n' + \ ' 💬 ' + \ translate['Leave a comment'] + '\n' + \ ' \n' + \ ' ' + \ translate['View comments'] + '\n \n' + \ '
    \n' return podcast_str def _html_podcast_performers(podcast_properties: {}) -> str: """Returns html for performers of a podcast """ if not podcast_properties: return '' key = 'persons' if not podcast_properties.get(key): return '' if not isinstance(podcast_properties[key], list): return '' # list of performers podcast_str = '
    \n' podcast_str += '
    \n' podcast_str += '\n' podcast_str += '
    \n' return podcast_str def _html_podcast_soundbites(link_url: str, extension: str, podcast_properties: {}, translate: {}) -> str: """Returns html for podcast soundbites """ if not podcast_properties: return '' if not podcast_properties.get('soundbites'): return '' podcast_str = '
    \n' podcast_str += '
    \n' podcast_str += '\n' podcast_str += '
    \n' return podcast_str def html_podcast_episode(css_cache: {}, translate: {}, base_dir: str, nickname: str, domain: str, newswire_item: [], theme: str, default_timeline: str, text_mode_banner: str, access_keys: {}, session, session_onion, session_i2p, http_prefix: str, debug: bool) -> str: """Returns html for a podcast episode, giebn an item from the newswire """ css_filename = base_dir + '/epicyon-podcast.css' if os.path.isfile(base_dir + '/podcast.css'): css_filename = base_dir + '/podcast.css' if os.path.isfile(base_dir + '/accounts/podcast-background-custom.jpg'): if not os.path.isfile(base_dir + '/accounts/podcast-background.jpg'): copyfile(base_dir + '/accounts/podcast-background.jpg', base_dir + '/accounts/podcast-background.jpg') instance_title = get_config_param(base_dir, 'instanceTitle') podcast_str = \ html_header_with_external_style(css_filename, instance_title, None) podcast_properties = newswire_item[8] image_url = '' image_src = 'src' if podcast_properties.get('images'): if podcast_properties['images'].get('srcset'): image_url = podcast_properties['images']['srcset'] image_src = 'srcset' if not image_url and podcast_properties.get('image'): image_url = podcast_properties['image'] link_url = newswire_item[1] podcast_str += html_keyboard_navigation(text_mode_banner, {}, {}) podcast_str += '

    \n' podcast_str += \ '
    \n' podcast_str += '
    \n' podcast_str += '
    \n' podcast_str += ' \n' podcast_str += '
    \n' podcast_str += '
    \n' podcast_str += '
    \n' audio_extension = None if path_is_audio(link_url): if '.mp3' in link_url: audio_extension = 'mpeg' elif '.opus' in link_url: audio_extension = 'opus' elif '.flac' in link_url: audio_extension = 'flac' else: audio_extension = 'ogg' else: if podcast_properties.get('linkMimeType'): if 'audio' in podcast_properties['linkMimeType']: audio_extension = \ podcast_properties['linkMimeType'].split('/')[1] # show widgets for soundbites if audio_extension: podcast_str += _html_podcast_soundbites(link_url, audio_extension, podcast_properties, translate) # podcast player widget podcast_str += \ ' \n' + \ ' \n \n' elif podcast_properties.get('linkMimeType'): if '/youtube' in podcast_properties['linkMimeType']: url = link_url.replace('/watch?v=', '/embed/') if '&' in url: url = url.split('&')[0] if '?utm_' in url: url = url.split('?utm_')[0] podcast_str += \ ' \n' + \ " \n \n" elif 'video' in podcast_properties['linkMimeType']: video_mime_type = podcast_properties['linkMimeType'] video_msg = 'Your browser does not support the video element.' podcast_str += \ ' \n' + \ '
    \n' + \ ' \n
    \n
    \n' podcast_title = \ remove_html(html.unescape(urllib.parse.unquote_plus(newswire_item[0]))) if podcast_title: podcast_str += \ '

    \n' transcripts = _html_podcast_transcripts(podcast_properties, translate) if transcripts: podcast_str += '

    ' + transcripts + '

    \n' if newswire_item[4]: podcast_description = \ html.unescape(urllib.parse.unquote_plus(newswire_item[4])) podcast_description = safe_web_text(podcast_description) if podcast_description: podcast_str += \ '

    ' + \ podcast_description + '

    \n' # donate button if podcast_properties.get('funding'): if podcast_properties['funding'].get('url'): donate_url = podcast_properties['funding']['url'] podcast_str += \ '

    \n' if podcast_properties['categories']: tags_str = '' for tag in podcast_properties['categories']: tag = tag.replace('#', '') tag_link = '/users/' + nickname + '/tags/' + tag tags_str += \ '#' + \ '' + tag + '' + \ ' ' podcast_str += '

    ' + tags_str.strip() + '

    \n' podcast_str += _html_podcast_performers(podcast_properties) podcast_str += \ _html_podcast_social_interactions(podcast_properties, translate, nickname) podcast_str += \ _html_podcast_chapters(link_url, session, session_onion, session_i2p, http_prefix, domain, podcast_properties, translate, debug) podcast_str += '
    \n' podcast_str += '
    \n' podcast_str += html_footer() return podcast_str