')[1]
@@ -615,18 +763,21 @@ def _atom_feed_to_dict(base_dir: str, domain: str, xml_str: str,
if _valid_feed_date(pub_date_str):
post_filename = ''
votes_status = []
+ podcast_properties = xml_podcast_to_dict(atom_item)
+ if podcast_properties:
+ podcast_properties['linkMimeType'] = link_mime_type
_add_newswire_dict_entry(base_dir, domain,
result, pub_date_str,
title, link,
votes_status, post_filename,
description, moderated,
- mirrored, [], 32, session, debug)
+ mirrored, [], 32, session, debug,
+ podcast_properties)
post_ctr += 1
if post_ctr >= max_posts_per_source:
break
if post_ctr > 0:
- print('Added ' + str(post_ctr) +
- ' atom feed items to newswire')
+ print('Added ' + str(post_ctr) + ' atom feed items to newswire')
return result
@@ -732,7 +883,8 @@ def _json_feed_v1to_dict(base_dir: str, domain: str, xml_str: str,
title, link,
votes_status, post_filename,
description, moderated,
- mirrored, [], 32, session, debug)
+ mirrored, [], 32, session, debug,
+ None)
post_ctr += 1
if post_ctr >= max_posts_per_source:
break
@@ -805,7 +957,8 @@ def _atom_feed_yt_to_dict(base_dir: str, domain: str, xml_str: str,
title, link,
votes_status, post_filename,
description, moderated, mirrored,
- [], 32, session, debug)
+ [], 32, session, debug,
+ None)
post_ctr += 1
if post_ctr >= max_posts_per_source:
break
@@ -829,24 +982,24 @@ def _xml_str_to_dict(base_dir: str, domain: str, xml_str: str,
max_posts_per_source,
max_feed_item_size_kb,
session, debug)
- elif 'rss version="2.0"' in xml_str:
+ if 'rss version="2.0"' in xml_str:
return _xml2str_to_dict(base_dir, domain,
xml_str, moderated, mirrored,
max_posts_per_source, max_feed_item_size_kb,
max_categories_feedItem_size_kb,
session, debug)
- elif '= max_blogs_per_account:
diff --git a/outbox.py b/outbox.py
index ed4f0f1b0..1a8945ba8 100644
--- a/outbox.py
+++ b/outbox.py
@@ -143,7 +143,7 @@ def _person_receive_update_outbox(recent_posts_cache: {},
if 'attachment' not in actor_json:
continue
found = False
- for attach_idx in range(len(actor_json['attachment'])):
+ for attach_idx, _ in enumerate(actor_json['attachment']):
if actor_json['attachment'][attach_idx]['type'] != \
'PropertyValue':
continue
diff --git a/person.py b/person.py
index f0f2406d5..da54a191f 100644
--- a/person.py
+++ b/person.py
@@ -797,7 +797,7 @@ def person_upgrade_actor(base_dir: str, person_json: {},
update_actor = True
else:
# add location if it is missing
- for index in range(len(person_json['hasOccupation'])):
+ for index, _ in enumerate(person_json['hasOccupation']):
oc_item = person_json['hasOccupation'][index]
if oc_item.get('hasOccupation'):
oc_item = oc_item['hasOccupation']
diff --git a/roles.py b/roles.py
index 770f0c1c3..f638d15db 100644
--- a/roles.py
+++ b/roles.py
@@ -163,7 +163,7 @@ def _set_actor_role(actor_json: {}, role_name: str) -> bool:
if not category:
return False
- for index in range(len(actor_json['hasOccupation'])):
+ for index, _ in enumerate(actor_json['hasOccupation']):
occupation_item = actor_json['hasOccupation'][index]
if not isinstance(occupation_item, dict):
continue
diff --git a/speaker.py b/speaker.py
index 5bb896f90..984f30a62 100644
--- a/speaker.py
+++ b/speaker.py
@@ -503,7 +503,7 @@ def _post_to_speaker_json(base_dir: str, http_prefix: str,
follows = fp_foll.readlines()
if len(follows) > 0:
follow_requests_exist = True
- for i in range(len(follows)):
+ for i, _ in enumerate(follows):
follows[i] = follows[i].strip()
follow_requests_list = follows
post_dm = False
diff --git a/tests.py b/tests.py
index c5626da89..f549b8184 100644
--- a/tests.py
+++ b/tests.py
@@ -150,6 +150,8 @@ from linked_data_sig import generate_json_signature
from linked_data_sig import verify_json_signature
from newsdaemon import hashtag_rule_tree
from newsdaemon import hashtag_rule_resolve
+from newswire import get_link_from_rss_item
+from newswire import xml_podcast_to_dict
from newswire import get_newswire_tags
from newswire import parse_feed_date
from newswire import limit_word_lengths
@@ -6354,7 +6356,7 @@ def _test_httpsig_base_new(with_digest: bool, base_dir: str,
def _test_get_actor_from_in_reply_to() -> None:
- print('testGetActorFromInReplyTo')
+ print('test_get_actor_from_in_reply_to')
in_reply_to = \
'https://fosstodon.org/users/bashrc/statuses/107400700612621140'
reply_actor = get_actor_from_in_reply_to(in_reply_to)
@@ -6365,6 +6367,117 @@ def _test_get_actor_from_in_reply_to() -> None:
assert reply_actor is None
+def _test_xml_podcast_dict() -> None:
+ print('test_xml_podcast_dict')
+ xml_str = \
+ '\n' + \
+ '\n' + \
+ '5 \n' + \
+ ' \n' + \
+ '' + \
+ 'Support the show \n' + \
+ ' \n' + \
+ '' + \
+ 'Nowheresville \n' + \
+ 'yes' + \
+ ' \n' + \
+ '' + \
+ 'Rodger Rabbit \n' + \
+ 'Rodger Rabbit' + \
+ ' \n' + \
+ '' + \
+ 'Jessica Rabbit \n' + \
+ '' + \
+ 'Betty Boop \n' + \
+ '' + \
+ 'Bob Hoskins \n' + \
+ '1 \n' + \
+ ' \n' + \
+ ' \n' + \
+ ' \n' + \
+ ' \n' + \
+ ' \n' + \
+ '\n' + \
+ ' \n' + \
+ ' \n' + \
+ ' \n' + \
+ ' '
+ podcast_properties = xml_podcast_to_dict(xml_str)
+ assert podcast_properties
+ # pprint(podcast_properties)
+ assert podcast_properties.get('valueRecipients')
+ assert podcast_properties.get('persons')
+ assert podcast_properties.get('soundbites')
+ assert podcast_properties.get('locations')
+ assert podcast_properties.get('transcripts')
+ assert podcast_properties.get('episode')
+ assert podcast_properties.get('funding')
+ assert int(podcast_properties['episode']) == 5
+ assert podcast_properties['funding']['text'] == "Support the show"
+ assert podcast_properties['funding']['url'] == \
+ "https://whoframed.rodger/donate"
+ assert len(podcast_properties['transcripts']) == 3
+ assert len(podcast_properties['valueRecipients']) == 2
+ assert len(podcast_properties['persons']) == 5
+ assert len(podcast_properties['locations']) == 1
+
+
+def _test_get_link_from_rss_item() -> None:
+ print('test_get_link_from_rssitem')
+ rss_item = \
+ ' ' + \
+ 'https://anchor.fm/creativecommons/episodes/' + \
+ 'Hessel-van-Oorschot-of-Tribe-of-Noise--Free-Music-Archive-e1crvce' + \
+ '' + \
+ 'Wed, 12 Jan 2022 14:28:46 GMT ' + \
+ ' '
+ link, mime_type = get_link_from_rss_item(rss_item)
+ assert link
+ assert link.endswith('.mp3')
+ assert mime_type
+ assert mime_type == 'audio/mpeg'
+
+ rss_item = \
+ ' ' + \
+ 'https://anchor.fm/creativecommons/episodes/' + \
+ 'Hessel-van-Oorschot-of-Tribe-of-Noise--Free-Music-Archive-e1crvce' + \
+ '' + \
+ 'Wed, 12 Jan 2022 14:28:46 GMT '
+ link, mime_type = get_link_from_rss_item(rss_item)
+ assert link
+ assert link.startswith('https://anchor.fm')
+ assert not mime_type
+
+
def run_all_tests():
base_dir = os.getcwd()
print('Running tests...')
@@ -6381,6 +6494,8 @@ def run_all_tests():
'message_json', 'liked_post_json'])
_test_checkbox_names()
_test_functions()
+ _test_get_link_from_rss_item()
+ _test_xml_podcast_dict()
_test_get_actor_from_in_reply_to()
_test_valid_emoji_content()
_test_add_cw_lists(base_dir)
diff --git a/theme.py b/theme.py
index 91e054ee0..25cb40619 100644
--- a/theme.py
+++ b/theme.py
@@ -107,7 +107,7 @@ def _get_theme_files() -> []:
return ('epicyon.css', 'login.css', 'follow.css',
'suspended.css', 'calendar.css', 'blog.css',
'options.css', 'search.css', 'links.css',
- 'welcome.css', 'graph.css')
+ 'welcome.css', 'graph.css', 'podcast.css')
def is_news_theme_name(base_dir: str, theme_name: str) -> bool:
diff --git a/utils.py b/utils.py
index 5d0b7f565..3e1930464 100644
--- a/utils.py
+++ b/utils.py
@@ -559,7 +559,7 @@ def get_followers_list(base_dir: str,
with open(filename, 'r') as foll_file:
lines = foll_file.readlines()
- for i in range(len(lines)):
+ for i, _ in enumerate(lines):
lines[i] = lines[i].strip()
return lines
return []
@@ -2126,7 +2126,7 @@ def _search_virtual_box_posts(base_dir: str, nickname: str, domain: str,
if '+' in search_str:
search_words = search_str.split('+')
- for index in range(len(search_words)):
+ for index, _ in enumerate(search_words):
search_words[index] = search_words[index].strip()
print('SEARCH: ' + str(search_words))
else:
@@ -2178,7 +2178,7 @@ def search_box_posts(base_dir: str, nickname: str, domain: str,
if '+' in search_str:
search_words = search_str.split('+')
- for index in range(len(search_words)):
+ for index, _ in enumerate(search_words):
search_words[index] = search_words[index].strip()
print('SEARCH: ' + str(search_words))
else:
@@ -2811,7 +2811,7 @@ def set_occupation_name(actor_json: {}, name: str) -> bool:
return False
if not isinstance(actor_json['hasOccupation'], list):
return False
- for index in range(len(actor_json['hasOccupation'])):
+ for index, _ in enumerate(actor_json['hasOccupation']):
occupation_item = actor_json['hasOccupation'][index]
if not isinstance(occupation_item, dict):
continue
@@ -2831,7 +2831,7 @@ def set_occupation_skills_list(actor_json: {}, skills_list: []) -> bool:
return False
if not isinstance(actor_json['hasOccupation'], list):
return False
- for index in range(len(actor_json['hasOccupation'])):
+ for index, _ in enumerate(actor_json['hasOccupation']):
occupation_item = actor_json['hasOccupation'][index]
if not isinstance(occupation_item, dict):
continue
diff --git a/webapp_column_right.py b/webapp_column_right.py
index 1940f307d..9548eaaa8 100644
--- a/webapp_column_right.py
+++ b/webapp_column_right.py
@@ -263,6 +263,19 @@ def _html_newswire(base_dir: str, newswire: {}, nickname: str, moderator: bool,
' '
moderated_item = item[5]
+ link_url = url
+
+ # is this a podcast episode?
+ if len(item) > 8:
+ # change the link url to a podcast episode screen
+ podcast_properties = item[8]
+ if podcast_properties:
+ if podcast_properties.get('image'):
+ episode_id = date_str.replace(' ', '__')
+ episode_id = episode_id.replace(':', 'aa')
+ link_url = \
+ '/users/' + nickname + '/?podepisode=' + episode_id
+
html_str += separator_str
if moderated_item and 'vote:' + nickname in item[2]:
total_votes_str = ''
@@ -275,7 +288,7 @@ def _html_newswire(base_dir: str, newswire: {}, nickname: str, moderator: bool,
title = remove_long_words(item[0], 16, []).replace('\n', ' ')
title = limit_repeated_words(title, 6)
html_str += '' + \
- '' + \
'' + \
favicon_link + title + ' ' + total_votes_str
@@ -305,7 +318,7 @@ def _html_newswire(base_dir: str, newswire: {}, nickname: str, moderator: bool,
title = limit_repeated_words(title, 6)
if moderator and moderated_item:
html_str += '
' + \
- '' + \
favicon_link + title + ' ' + total_votes_str
html_str += ' ' + date_shown
@@ -318,7 +331,7 @@ def _html_newswire(base_dir: str, newswire: {}, nickname: str, moderator: bool,
html_str += '
\n'
else:
html_str += '' + \
- '' + \
favicon_link + title + ' ' + total_votes_str
html_str += ' '
diff --git a/webapp_podcast.py b/webapp_podcast.py
new file mode 100644
index 000000000..def903fbb
--- /dev/null
+++ b/webapp_podcast.py
@@ -0,0 +1,219 @@
+__filename__ = "webapp_podcast.py"
+__author__ = "Bob Mottram"
+__license__ = "AGPL3+"
+__version__ = "1.2.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 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_performers(podcast_properties: {}) -> str:
+ """Returns html for performers of a podcast
+ """
+ if not podcast_properties.get('persons'):
+ return ''
+
+ # list of performers
+ 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.get('soundbites'):
+ return ''
+
+ 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: {}) -> 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'
+ 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 += \
+ ' \n' + \
+ ' ' + \
+ translate['Your browser does not support the audio element.'] + \
+ '\n \n'
+
+ podcast_title = \
+ remove_html(html.unescape(urllib.parse.unquote_plus(newswire_item[0])))
+ if podcast_title:
+ podcast_str += \
+ '' + podcast_title + \
+ '
\n'
+ if newswire_item[4]:
+ podcast_description = \
+ html.unescape(urllib.parse.unquote_plus(newswire_item[4]))
+ podcast_description = remove_html(podcast_description)
+ if podcast_description:
+ remove_chars = ('Œ', 'â€', 'ğŸ', '�')
+ for remchar in remove_chars:
+ podcast_description = podcast_description.replace(remchar, '')
+ 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 += \
+ '' + translate['Donate'] + \
+ '
\n'
+
+ podcast_str += _html_podcast_performers(podcast_properties)
+
+ podcast_str += ' \n'
+ podcast_str += '
\n'
+
+ podcast_str += html_footer()
+ return podcast_str
diff --git a/webapp_search.py b/webapp_search.py
index d8f3df883..32b3ec17d 100644
--- a/webapp_search.py
+++ b/webapp_search.py
@@ -941,9 +941,8 @@ def rss_hashtag_search(nickname: str, domain: str, port: int,
domain_full = get_full_domain(domain, port)
max_feed_length = 10
- hashtag_feed = \
- rss2tag_header(hashtag, http_prefix, domain_full)
- for index in range(len(lines)):
+ hashtag_feed = rss2tag_header(hashtag, http_prefix, domain_full)
+ for index, _ in enumerate(lines):
post_id = lines[index].strip('\n').strip('\r')
if ' ' not in post_id:
nickname = get_nickname_from_actor(post_id)