Get everything nice for heroku

This commit is contained in:
Bradlee Speice
2016-07-18 21:37:14 -04:00
parent 2cdf45cd01
commit e4599c2866
15 changed files with 70 additions and 68 deletions

BIN
elektricity/.server.py.swp Normal file

Binary file not shown.

0
elektricity/__init__.py Normal file
View File

30
elektricity/conf_parser.py Executable file
View File

@ -0,0 +1,30 @@
"""
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
from os.path import expanduser, join
# Needed for import_module call
# noinspection PyUnresolvedReferences
import podcasters
def build_configuration(conf=None) -> Configurator:
if conf is None:
conf = join(expanduser('~'), '.repodrc')
server_conf = Configurator()
with open(conf) as conf_file:
conf_dict = yaml.load(conf_file)
for mountpoint, feed in conf_dict.items():
feed_package = import_module('podcasters.' + 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

View File

View File

@ -0,0 +1,19 @@
"""
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
# noinspection PyUnusedLocal
def view(self, request):
fg = self.build_feed()
response = Response(fg.rss_str(pretty=True))
response.content_type = 'application/rss+xml'
return response

View File

@ -0,0 +1,132 @@
"""
Podcast provider for the Bassdrive Archives
"""
from datetime import datetime
from html.parser import HTMLParser
from urllib.parse import unquote
import requests
from feedgen.feed import FeedGenerator
from pytz import UTC
from podcasters.base import BasePodcast
class BassdriveParser(HTMLParser):
def error(self, message):
return super().error(message)
record_link_text = False
link_url = ''
def __init__(self, *args, **kwargs):
# noinspection PyArgumentList
super().__init__(*args, **kwargs)
self.links = []
def handle_starttag(self, tag, attrs):
"""
If we find an 'a' tag, make sure that we record
the next link we come across
>>> b = BassdriveParser()
>>> b.handle_starttag('a', (('href', 'something.mp3'),))
>>> b.record_link_text
True
>>> b.link_url
'something.mp3'
"""
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 we receive a new link, record it if we're inside an `a` tag
>>> b = BassdriveParser()
>>> not b.get_links()
True
>>> b.handle_data("some_link")
>>> not b.get_links()
True
>>> b.handle_starttag('a', [['href', 'something.mp3']])
>>> b.handle_data("some text")
>>> len(b.get_links()) == 1
True
"""
if self.record_link_text:
self.links.append((data, self.link_url))
self.record_link_text = False
def get_links(self):
# Reverse to sort in descending date order
return self.links
def clear_links(self):
"""
For whatever reason, creating a new parser doesn't
clear out the old links.
>>> import requests
>>> b = BassdriveParser()
>>> b.feed(str(requests.get('http://archives.bassdrivearchive.com/' +\
'1%20-%20Monday/Subfactory%20Show%20-%20DJ%20Spim').content))
>>> len(b.get_links()) > 0
True
>>> b.clear_links()
>>> len(b.get_links()) == 0
True
"""
self.links = []
class BassdriveFeed(BasePodcast):
def __init__(self, *args, **kwargs):
self.url = kwargs['url']
self.logo = kwargs.get('logo', '')
# 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.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'})
fg.logo(self.logo)
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')
# Bassdrive always uses date strings of
# [yyyy.mm.dd] with 0 padding on days and months,
# so that makes our lives easy
date_start = link[0].find('[')
date_str = link[0][date_start:date_start+12]
published = datetime.strptime(date_str, '[%Y.%m.%d]')
fe.pubdate(UTC.localize(published))
fe.guid((link[0]))
return fg

51
elektricity/server.py Normal file
View File

@ -0,0 +1,51 @@
import argparse
import traceback
from os.path import expanduser, join, dirname, realpath
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from conf_parser import build_configuration
# noinspection PyUnresolvedReferences
def start_server(server_conf: dict, configurator: Configurator) -> None:
app = configurator.make_wsgi_app()
port = server_conf['port']
host = server_conf['host']
server = make_server(host, port, app)
server.serve_forever()
if __name__ == '__main__':
# default_rc = join(expanduser('~'), '.repodrc')
default_rc = join(dirname(realpath(__file__)), '../default_conf.yaml')
parser = argparse.ArgumentParser()
parser.add_argument('--verbose', action='store_true',
help='Run server in verbose mode')
parser.add_argument('--port', type=int, default=10000,
help='Port to use when starting the server')
parser.add_argument('--host', type=str, default='0.0.0.0',
help='Host address to start the server')
parser.add_argument('--configuration', type=str, default=default_rc,
help='Configuration file to start the server')
cmd_args = parser.parse_args()
try:
configurator = build_configuration(cmd_args.configuration)
server_conf = {
'host': cmd_args.host,
'port': cmd_args.port
}
start_server(server_conf, configurator)
except FileNotFoundError:
print("Unable to find configuration file. Does {} exist?"
.format(cmd_args.configuration))
if cmd_args.verbose:
print(traceback.format_exc())
except AttributeError:
print("Unable to parse configuration file. Is {} a valid YML file?"
.format(cmd_args.configuration))
if cmd_args.verbose:
print(traceback.format_exc())

View File