mirror of https://gitlab.com/bashrc2/epicyon
				
				
				
			
		
			
				
	
	
		
			295 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			295 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
__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 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
 | 
						|
 | 
						|
 | 
						|
def _html_podcast_social_interactions(podcast_properties: {},
 | 
						|
                                      translate: {},
 | 
						|
                                      nickname: str) -> str:
 | 
						|
    """Returns html for social interactions with a podcast
 | 
						|
    """
 | 
						|
    if not podcast_properties:
 | 
						|
        return ''
 | 
						|
    if not podcast_properties.get('socialInteract'):
 | 
						|
        return ''
 | 
						|
    if not podcast_properties['socialInteract'].get('text'):
 | 
						|
        return ''
 | 
						|
    episode_post_url = podcast_properties['socialInteract']['text']
 | 
						|
    actor_str = ''
 | 
						|
    podcast_account_id = None
 | 
						|
    if podcast_properties['socialInteract'].get('accountId'):
 | 
						|
        podcast_account_id = podcast_properties['socialInteract']['accountId']
 | 
						|
    elif podcast_properties['socialInteract'].get('podcastAccountUrl'):
 | 
						|
        podcast_account_id = \
 | 
						|
            podcast_properties['socialInteract']['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 = \
 | 
						|
        '<center>\n' + \
 | 
						|
        '  <a href="/users/' + nickname + \
 | 
						|
        '?replyto=' + episode_post_url + actor_str + '" target="_blank" ' + \
 | 
						|
        'rel="nofollow noopener noreferrer">💬 ' + \
 | 
						|
        translate['Leave a comment'] + '</a>\n' + \
 | 
						|
        '  <a href="' + episode_post_url + '" target="_blank" ' + \
 | 
						|
        'rel="nofollow noopener noreferrer">' + \
 | 
						|
        translate['View comments'] + '</a>\n' + \
 | 
						|
        '</center>\n'
 | 
						|
    return podcast_str
 | 
						|
 | 
						|
 | 
						|
def _html_podcast_performers(podcast_properties: {}) -> str:
 | 
						|
    """Returns html for performers of a podcast
 | 
						|
    """
 | 
						|
    if not podcast_properties:
 | 
						|
        return ''
 | 
						|
    if not podcast_properties.get('persons'):
 | 
						|
        return ''
 | 
						|
 | 
						|
    # list of performers
 | 
						|
    podcast_str = '<div class="performers">\n'
 | 
						|
    podcast_str += '  <center>\n'
 | 
						|
    podcast_str += '<ul>\n'
 | 
						|
    for performer in podcast_properties['persons']:
 | 
						|
        if not performer.get('text'):
 | 
						|
            continue
 | 
						|
        performer_name = performer['text']
 | 
						|
        performer_title = performer_name
 | 
						|
 | 
						|
        if performer.get('role'):
 | 
						|
            performer_title += ' (' + performer['role'] + ')'
 | 
						|
        if performer.get('group'):
 | 
						|
            performer_title += ', <i>' + performer['group'] + '</i>'
 | 
						|
        performer_title = remove_html(performer_title)
 | 
						|
 | 
						|
        performer_url = ''
 | 
						|
        if performer.get('href'):
 | 
						|
            performer_url = performer['href']
 | 
						|
 | 
						|
        performer_img = ''
 | 
						|
        if performer.get('img'):
 | 
						|
            performer_img = performer['img']
 | 
						|
 | 
						|
        podcast_str += '  <li>\n'
 | 
						|
        podcast_str += '    <figure>\n'
 | 
						|
        podcast_str += '      <a href="' + performer_url + '">\n'
 | 
						|
        podcast_str += \
 | 
						|
            '        <img loading="lazy" decoding="async" ' + \
 | 
						|
            'src="' + performer_img + '" alt="" />\n'
 | 
						|
        podcast_str += \
 | 
						|
            '      <figcaption>' + performer_title + '</figcaption>\n'
 | 
						|
        podcast_str += '      </a>\n'
 | 
						|
        podcast_str += '    </figure>\n'
 | 
						|
        podcast_str += '  </li>\n'
 | 
						|
 | 
						|
    podcast_str += '</ul>\n'
 | 
						|
    podcast_str += '</div>\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 = '<div class="performers">\n'
 | 
						|
    podcast_str += '  <center>\n'
 | 
						|
    podcast_str += '<ul>\n'
 | 
						|
    ctr = 1
 | 
						|
    for performer in podcast_properties['soundbites']:
 | 
						|
        if not performer.get('startTime'):
 | 
						|
            continue
 | 
						|
        if not performer['startTime'].isdigit():
 | 
						|
            continue
 | 
						|
        if not performer.get('duration'):
 | 
						|
            continue
 | 
						|
        if not performer['duration'].isdigit():
 | 
						|
            continue
 | 
						|
        end_time = str(float(performer['startTime']) +
 | 
						|
                       float(performer['duration']))
 | 
						|
 | 
						|
        podcast_str += '  <li>\n'
 | 
						|
        preview_url = \
 | 
						|
            link_url + '#t=' + performer['startTime'] + ',' + end_time
 | 
						|
        soundbite_title = translate['Preview']
 | 
						|
        if ctr > 0:
 | 
						|
            soundbite_title += ' ' + str(ctr)
 | 
						|
        podcast_str += \
 | 
						|
            '    <audio controls>\n' + \
 | 
						|
            '    <p>' + soundbite_title + '</p>\n' + \
 | 
						|
            '    <source src="' + preview_url + '" type="audio/' + \
 | 
						|
            extension.replace('.', '') + '">' + \
 | 
						|
            translate['Your browser does not support the audio element.'] + \
 | 
						|
            '</audio>\n'
 | 
						|
        podcast_str += '  </li>\n'
 | 
						|
        ctr += 1
 | 
						|
 | 
						|
    podcast_str += '</ul>\n'
 | 
						|
    podcast_str += '</div>\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: {}) -> 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 += '<br><br>\n'
 | 
						|
    podcast_str += '<div class="options">\n'
 | 
						|
    podcast_str += '  <div class="optionsAvatar">\n'
 | 
						|
    podcast_str += '  <center>\n'
 | 
						|
    podcast_str += '  <a href="' + link_url + '">\n'
 | 
						|
    if image_src == 'srcset':
 | 
						|
        podcast_str += '  <img loading="lazy" decoding="async" ' + \
 | 
						|
            'srcset="' + image_url + \
 | 
						|
            '" alt="" ' + get_broken_link_substitute() + '/></a>\n'
 | 
						|
    else:
 | 
						|
        podcast_str += '  <img loading="lazy" decoding="async" ' + \
 | 
						|
            'src="' + image_url + \
 | 
						|
            '" alt="" ' + get_broken_link_substitute() + '/></a>\n'
 | 
						|
    podcast_str += '  </center>\n'
 | 
						|
    podcast_str += '  </div>\n'
 | 
						|
 | 
						|
    podcast_str += '  <center>\n'
 | 
						|
    audio_extension = None
 | 
						|
    if path_is_audio(link_url):
 | 
						|
        if '.mp3' in link_url:
 | 
						|
            audio_extension = 'mpeg'
 | 
						|
        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 += \
 | 
						|
            '  <audio controls>\n' + \
 | 
						|
            '    <source src="' + link_url + '" type="audio/' + \
 | 
						|
            audio_extension.replace('.', '') + '">' + \
 | 
						|
            translate['Your browser does not support the audio element.'] + \
 | 
						|
            '\n  </audio>\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 += \
 | 
						|
                "  <iframe loading=\"lazy\" decoding=\"async\" src=\"" + \
 | 
						|
                url + "\" width=\"400\" height=\"300\" " + \
 | 
						|
                "frameborder=\"0\" allow=\"fullscreen\" " + \
 | 
						|
                "allowfullscreen>\n  </iframe>\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 += \
 | 
						|
                '  <figure id="videoContainer" ' + \
 | 
						|
                'data-fullscreen="false">\n' + \
 | 
						|
                '    <video id="video" controls preload="metadata">\n' + \
 | 
						|
                '<source src="' + link_url + '" ' + \
 | 
						|
                'type="' + video_mime_type + '">' + \
 | 
						|
                translate[video_msg] + '</video>\n  </figure>\n'
 | 
						|
 | 
						|
    podcast_title = \
 | 
						|
        remove_html(html.unescape(urllib.parse.unquote_plus(newswire_item[0])))
 | 
						|
    if podcast_title:
 | 
						|
        podcast_str += \
 | 
						|
            '<p><label class="podcast-title">' + podcast_title + \
 | 
						|
            '</label></p>\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 += '<p>' + podcast_description + '</p>\n'
 | 
						|
 | 
						|
    # donate button
 | 
						|
    if podcast_properties.get('funding'):
 | 
						|
        if podcast_properties['funding'].get('url'):
 | 
						|
            donate_url = podcast_properties['funding']['url']
 | 
						|
            podcast_str += \
 | 
						|
                '<p><a href="' + donate_url + \
 | 
						|
                '"><button class="donateButton">' + translate['Donate'] + \
 | 
						|
                '</button></a></p>\n'
 | 
						|
 | 
						|
    if podcast_properties['categories']:
 | 
						|
        podcast_str += '<p>'
 | 
						|
        tags_str = ''
 | 
						|
        for tag in podcast_properties['categories']:
 | 
						|
            tag_link = '/users/' + nickname + '/tags/' + tag.replace('#', '')
 | 
						|
            tags_str += '<a href="' + tag_link + '">' + tag + '</a> '
 | 
						|
        podcast_str += tags_str.strip() + '</p>\n'
 | 
						|
 | 
						|
    podcast_str += _html_podcast_performers(podcast_properties)
 | 
						|
    podcast_str += \
 | 
						|
        _html_podcast_social_interactions(podcast_properties, translate,
 | 
						|
                                          nickname)
 | 
						|
 | 
						|
    podcast_str += '  </center>\n'
 | 
						|
    podcast_str += '</div>\n'
 | 
						|
 | 
						|
    podcast_str += html_footer()
 | 
						|
    return podcast_str
 |