From 622b90aeaa2c105b4045321837e6b51f560bc5e2 Mon Sep 17 00:00:00 2001 From: Bradlee Speice Date: Fri, 6 May 2016 23:20:59 -0400 Subject: [PATCH] Initial repod server commit with basic Bassdrive support --- .gitignore | 6 +++ .idea/.name | 1 + .idea/compiler.xml | 22 ++++++++ .idea/copyright/profiles_settings.xml | 3 ++ .idea/encodings.xml | 6 +++ .idea/misc.xml | 35 +++++++++++++ .idea/modules.xml | 8 +++ Requirements | 4 ++ example_conf.yaml | 11 ++++ repod.iml | 9 ++++ repod/__init__.py | 0 repod/conf_parser.py | 29 +++++++++++ repod/example_conf.yaml | 11 ++++ repod/modules/__init__.py | 0 repod/modules/bassdrive.py | 74 +++++++++++++++++++++++++++ repod/podcast.py | 16 ++++++ repod/server.py | 10 ++++ 17 files changed, 245 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.name create mode 100644 .idea/compiler.xml create mode 100644 .idea/copyright/profiles_settings.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 Requirements create mode 100644 example_conf.yaml create mode 100644 repod.iml create mode 100644 repod/__init__.py create mode 100644 repod/conf_parser.py create mode 100644 repod/example_conf.yaml create mode 100644 repod/modules/__init__.py create mode 100644 repod/modules/bassdrive.py create mode 100644 repod/podcast.py create mode 100644 repod/server.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2fb87f8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.pyc +bin/ +lib/ +share/ +include/ +.idea/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..d22af05 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +repod \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..96cc43e --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..c7d1c5a --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..c777e63 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + Python 3.5.1 (C:\Users\Bradlee Speice\Anaconda3\python.exe) + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..f9ed142 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Requirements b/Requirements new file mode 100644 index 0000000..5477442 --- /dev/null +++ b/Requirements @@ -0,0 +1,4 @@ +pyramid>=1.6.1 +feedgen>=0.3.2 +PyYAML>=3.11 +requests>=2.9.1 diff --git a/example_conf.yaml b/example_conf.yaml new file mode 100644 index 0000000..fe79036 --- /dev/null +++ b/example_conf.yaml @@ -0,0 +1,11 @@ +# Format: (all args are passed to __init__ as kwargs +# +# : +# class: +# args: +# key: value +subfactory-show: + package: bassdrive + class: BassdriveFeed + args: + url: http://archives.bassdrivearchive.com/1%20-%20Monday/Subfactory%20Show%20-%20DJ%20Spim/ diff --git a/repod.iml b/repod.iml new file mode 100644 index 0000000..ad3c0a3 --- /dev/null +++ b/repod.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/repod/__init__.py b/repod/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/repod/conf_parser.py b/repod/conf_parser.py new file mode 100644 index 0000000..32d7102 --- /dev/null +++ b/repod/conf_parser.py @@ -0,0 +1,29 @@ +""" +Given a configuration file, set up everything needed to kick +off the server. +""" +from importlib import import_module +import yaml +from pyramid.config import Configurator + +# Needed for import_module call +# noinspection PyUnresolvedReferences +import modules + +# Hardcoded, need to look up from users $HOME later +file_loc = 'example_conf.yaml' + + +def build_configuration(conf=file_loc) -> Configurator: + with open(conf) as conf_file: + conf_dict = yaml.load(conf_file) + server_conf = Configurator() + for mountpoint, feed in conf_dict.items(): + feed_package = import_module('modules.' + feed['package']) + feed_class = getattr(feed_package, feed['class']) + feed_instance = feed_class(**feed['args']) + + server_conf.add_route(mountpoint, '/' + mountpoint + '/') + server_conf.add_view(feed_instance.view, route_name=mountpoint) + + return server_conf diff --git a/repod/example_conf.yaml b/repod/example_conf.yaml new file mode 100644 index 0000000..fe79036 --- /dev/null +++ b/repod/example_conf.yaml @@ -0,0 +1,11 @@ +# Format: (all args are passed to __init__ as kwargs +# +# : +# class: +# args: +# key: value +subfactory-show: + package: bassdrive + class: BassdriveFeed + args: + url: http://archives.bassdrivearchive.com/1%20-%20Monday/Subfactory%20Show%20-%20DJ%20Spim/ diff --git a/repod/modules/__init__.py b/repod/modules/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/repod/modules/bassdrive.py b/repod/modules/bassdrive.py new file mode 100644 index 0000000..5c1f027 --- /dev/null +++ b/repod/modules/bassdrive.py @@ -0,0 +1,74 @@ +""" +Podcast provider for the Bassdrive Archives +""" +from html.parser import HTMLParser +from urllib.parse import unquote + +import requests +from feedgen.feed import FeedGenerator + +from podcast import BasePodcast + + +class BassdriveParser(HTMLParser): + links = [] + record_link_text = False + link_url = '' + + def handle_starttag(self, tag, attrs): + href = '' + for attr, val in attrs: + if attr == 'href': + href = val + + if tag == 'a' and href.find('mp3') != -1: + self.record_link_text = True + self.link_url = href + + def handle_data(self, data): + if self.record_link_text: + print(self.link_url) + self.links.append((data, self.link_url)) + self.record_link_text = False + + def get_links(self): + # Reverse to sort in descending date order + self.links.reverse() + return self.links + + +class BassdriveFeed(BasePodcast): + def __init__(self, *args, **kwargs): + print(kwargs) + self.url = kwargs['url'] + # Get the title and DJ while handling trailing slash + url_pretty = unquote(self.url) + elems = filter(lambda x: x, url_pretty.split('/')) + self.title, self.dj = list(elems)[-1].split(' - ') + + def build_feed(self): + "Build the feed given our existing URL" + # Get all the episodes + page_content = str(requests.get(self.url).content) + parser = BassdriveParser() + parser.feed(page_content) + links = parser.get_links() + + # And turn them into something usable + fg = FeedGenerator() + fg.load_extension('podcast') + fg.id(self.url) + fg.title(self.title) + fg.description(self.title) + fg.author({'name': self.dj}) + fg.language('en') + fg.link({'href': self.url, 'rel': 'alternate'}) + + for link in links: + fe = fg.add_entry() + fe.author({'name': self.dj}) + fe.title(link[0]) + fe.description(link[0]) + fe.enclosure(self.url + link[1], 0, 'audio/mpeg') + + return fg diff --git a/repod/podcast.py b/repod/podcast.py new file mode 100644 index 0000000..0637d76 --- /dev/null +++ b/repod/podcast.py @@ -0,0 +1,16 @@ +""" +Base skeleton for what needs to be implemented by a podcast provider +""" +from feedgen.feed import FeedGenerator +from pyramid.response import Response + +class BasePodcast(): + def build_feed(self) -> FeedGenerator: + "Return a list of all episodes, in descending date order" + pass + + def view(self, request): + fg = self.build_feed() + response = Response(fg.rss_str(pretty=True)) + response.content_type = 'application/rss+xml' + return response diff --git a/repod/server.py b/repod/server.py new file mode 100644 index 0000000..baa2c74 --- /dev/null +++ b/repod/server.py @@ -0,0 +1,10 @@ +from wsgiref.simple_server import make_server +from conf_parser import build_configuration + +def start_server(): + app = build_configuration().make_wsgi_app() + server = make_server('0.0.0.0', 8080, app) + server.serve_forever() + +if __name__ == '__main__': + start_server()