- commit
- c8a04d20618ef09522df7743c696cee216975a0d
- parent
- 476189072ab3657f035c71d3c501488ba3c06112
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2024-02-13 18:15
refactor xml rendering
Diffstat
| M | audiothek.py | 137 | +++++++++++++++++++++++++++++++++---------------------------- |
1 files changed, 75 insertions, 62 deletions
diff --git a/audiothek.py b/audiothek.py
@@ -1,6 +1,7 @@ 1 1 import argparse 2 2 import json 3 3 from xml.sax.saxutils import escape -1 4 from xml.sax.saxutils import quoteattr 4 5 5 6 import aiohttp 6 7 from aiohttp import web @@ -32,43 +33,77 @@ query ProgramSetEpisodesQuery($id:ID!, $offset:Int!, $count:Int!) { 32 33 } 33 34 """.replace(' ', '').replace('\n', ' ').strip() 34 3535 -1 FEED = """<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">36 -1 <channel>37 -1 <title>{title}</title>38 -1 <link>{url}</link>39 -1 <image>40 -1 <url>{img_url}</url>41 -1 <width>{img_size}</width>42 -1 <height>{img_size}</height>43 -1 </image>44 -145 -1 {entries}46 -1 </channel>47 -1 </rss>"""48 -149 -1 LINK = '<atom:link href="{href}" rel="{rel}" type="application/rss+xml"/>'50 -151 -1 ENTRY = """ <item>52 -1 <title>{title}</title>53 -1 <link>{url}</link>54 -1 <guid>{url}</guid>55 -1 <enclosure url="{downloadUrl}" type="audio/mpeg"/>56 -1 <pubDate>{updated}</pubDate>57 -1 <description>{summary}</description>58 -1 <itunes:duration>{duration}</itunes:duration>59 -1 </item>"""60 -161 -162 -1 def escape_data(data):63 -1 return {64 -1 key: escape(value) for key, value in data.items()65 -1 }66 -167 3668 -1 def render_feed(show, episodes, links):69 -1 entries = [ENTRY.format(**escape_data(ep)) for ep in reversed(episodes)]70 -1 entries += [LINK.format(**escape_data(link)) for link in links]71 -1 return FEED.format(entries='\n'.join(entries), **escape_data(show))-1 37 def el(tag, content=None, attrs={}): -1 38 a = ''.join(f' {k}={quoteattr(v)}' for k, v in attrs.items()) -1 39 if content is None: -1 40 return f'<{tag}{a} />' -1 41 else: -1 42 return f'<{tag}{a}>{escape(content)}</{tag}>' -1 43 -1 44 -1 45 def indent(lines): -1 46 return [f'\t{line}' for line in lines] -1 47 -1 48 -1 49 def render_items(items): -1 50 lines = [] -1 51 for item in reversed(items): -1 52 lines += [ -1 53 '<item>', -1 54 *indent([ -1 55 el('title', item['title']), -1 56 el('link', 'https://www.ardaudiothek.de' + item['path']), -1 57 el('guid', 'https://www.ardaudiothek.de' + item['path']), -1 58 el('enclosure', attrs={ -1 59 'url': item['audios'][0]['url'], -1 60 'type': 'audio/mpeg', -1 61 }), -1 62 el('pubDate', item['publishDate']), -1 63 el('description', item['summary']), -1 64 el('itunes:duration', str(item['duration'])), -1 65 ]), -1 66 '</item>', -1 67 ] -1 68 return lines -1 69 -1 70 -1 71 def render_feed(show, links): -1 72 feed = [ -1 73 '<?xml version="1.0" encoding="UTF-8"?>', -1 74 ( -1 75 '<rss version="2.0" ' -1 76 'xmlns:atom="http://www.w3.org/2005/Atom" ' -1 77 'xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">' -1 78 ), -1 79 *indent([ -1 80 '<channel>', -1 81 *indent([ -1 82 el('title', show['title']), -1 83 el('link', 'https://www.ardaudiothek.de' + show['path']), -1 84 '<image>', -1 85 *indent([ -1 86 el('url', show['image']['url1X1'].replace('{width}', '64')), -1 87 el('width', '64'), -1 88 el('height', '64'), -1 89 ]), -1 90 '</image>', -1 91 *render_items(show['items']['nodes']), -1 92 *[ -1 93 el('atom:link', attrs={ -1 94 'href': href, -1 95 'rel': rel, -1 96 'type': 'application/rss+xml', -1 97 }) -1 98 for rel, href in links.items() -1 99 ], -1 100 ]), -1 101 '</channel>', -1 102 ]), -1 103 '</rss>', -1 104 ] -1 105 -1 106 return ''.join(f'{line}\n' for line in feed) 72 107 73 108 74 109 async def feed(request): @@ -96,35 +131,13 @@ async def feed(request): 96 131 if show is None: 97 132 raise web.HTTPNotFound 98 13399 -1 show_data = {100 -1 'title': show['title'],101 -1 'url': 'https://www.ardaudiothek.de' + show['path'],102 -1 'img_url': show['image']['url1X1'].replace('{width}', '64'),103 -1 'img_size': '64',104 -1 }105 -1106 -1 episode_data = [{107 -1 'title': node['title'],108 -1 'url': 'https://www.ardaudiothek.de' + node['path'],109 -1 'summary': node['summary'],110 -1 'updated': node['publishDate'],111 -1 'downloadUrl': node['audios'][0]['url'], # FIXME: more than one112 -1 'duration': str(node['duration']),113 -1 } for node in show['items']['nodes']]114 -1115 -1 links = []-1 134 links = {} 116 135 if ORIGIN:117 -1 links.append({118 -1 'href': f'{ORIGIN}{request.url.path_qs}',119 -1 'rel': 'self',120 -1 })-1 136 links['self'] = f'{ORIGIN}{request.url.path_qs}' 121 137 if show['items']['pageInfo']['hasNextPage']:122 -1 links.append({123 -1 'href': f'{ORIGIN}{request.url.path}?offset={offset + COUNT}',124 -1 'rel': 'next',125 -1 })-1 138 links['next'] = f'{ORIGIN}{request.url.path}?offset={offset + COUNT}' 126 139127 -1 body = render_feed(show_data, episode_data, links)-1 140 body = render_feed(show, links) 128 141 129 142 return web.Response( 130 143 body=body.encode('utf8'),