mirror of
https://github.com/bspeice/elektricity
synced 2025-07-03 23:14:58 -04:00
Get everything nice for heroku
This commit is contained in:
BIN
elektricity/.server.py.swp
Normal file
BIN
elektricity/.server.py.swp
Normal file
Binary file not shown.
0
elektricity/__init__.py
Normal file
0
elektricity/__init__.py
Normal file
30
elektricity/conf_parser.py
Executable file
30
elektricity/conf_parser.py
Executable 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
|
0
elektricity/podcasters/__init__.py
Normal file
0
elektricity/podcasters/__init__.py
Normal file
19
elektricity/podcasters/base.py
Normal file
19
elektricity/podcasters/base.py
Normal 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
|
132
elektricity/podcasters/bassdrive.py
Normal file
132
elektricity/podcasters/bassdrive.py
Normal 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
51
elektricity/server.py
Normal 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())
|
0
elektricity/tests/__init__.py
Normal file
0
elektricity/tests/__init__.py
Normal file
Reference in New Issue
Block a user