diff options
Diffstat (limited to 'youtube_podcaster')
| -rw-r--r-- | youtube_podcaster/__init__.py | 44 | ||||
| -rw-r--r-- | youtube_podcaster/__main__.py | 8 | ||||
| -rw-r--r-- | youtube_podcaster/config.py | 64 | ||||
| -rw-r--r-- | youtube_podcaster/podcastfeeder.py | 4 | ||||
| -rw-r--r-- | youtube_podcaster/podcastupdater.py | 34 | ||||
| -rw-r--r-- | youtube_podcaster/youtube-podcaster.json.sample | 10 | ||||
| -rw-r--r-- | youtube_podcaster/youtube/downloader.py | 23 |
7 files changed, 135 insertions, 52 deletions
diff --git a/youtube_podcaster/__init__.py b/youtube_podcaster/__init__.py index 32aee7c..77ab9cc 100644 --- a/youtube_podcaster/__init__.py +++ b/youtube_podcaster/__init__.py | |||
| @@ -1,27 +1,47 @@ | |||
| 1 | #!/usr/bin/env python3 | 1 | #!/usr/bin/env python3 |
| 2 | 2 | ||
| 3 | import json | 3 | import argparse |
| 4 | 4 | ||
| 5 | from http.server import ( | 5 | from http.server import HTTPServer |
| 6 | HTTPServer, | ||
| 7 | ) | ||
| 8 | 6 | ||
| 9 | from . import ( | 7 | from .podcastfeeder import create_feeder |
| 10 | youtube, | 8 | from .config import ( |
| 9 | Config, | ||
| 10 | ConfigException | ||
| 11 | ) | 11 | ) |
| 12 | 12 | ||
| 13 | from .podcastfeeder import ( | 13 | """ |
| 14 | create_feeder | 14 | Start the program |
| 15 | ) | 15 | """ |
| 16 | 16 | ||
| 17 | 17 | ||
| 18 | def main(): | 18 | def main(): |
| 19 | config = json.load(open("youtube-podcaster.json")) | 19 | arg_parser = argparse.ArgumentParser(prog="youtube-podcaster", |
| 20 | description="Converts youtube \ | ||
| 21 | playlists to RSS-feeds") | ||
| 22 | arg_parser.add_argument("-c", "--config", | ||
| 23 | dest="config", | ||
| 24 | help="Use CONFIG as the config file") | ||
| 25 | arg_parser.add_argument("-i", "--interface", | ||
| 26 | dest="interface", | ||
| 27 | help="The interface the http server will listen on") | ||
| 28 | arg_parser.add_argument("-p", "--port", | ||
| 29 | dest="port", | ||
| 30 | help="The port the http server will listen on") | ||
| 31 | arg_parser.add_argument("--api-key", | ||
| 32 | dest="apikey", | ||
| 33 | help="The YouTube API v3 key") | ||
| 34 | args = arg_parser.parse_args() | ||
| 20 | 35 | ||
| 21 | try: | 36 | try: |
| 22 | PodcastFeeder = create_feeder(config["youtube"], config["podcasts"]) | 37 | config = Config.parse_config(args) |
| 23 | server = HTTPServer(("", 8888), PodcastFeeder) | 38 | |
| 39 | PodcastFeeder = create_feeder(config) | ||
| 40 | |||
| 41 | server = HTTPServer(config.get_server_address(), PodcastFeeder) | ||
| 24 | server.serve_forever() | 42 | server.serve_forever() |
| 43 | except ConfigException as e: | ||
| 44 | print(e) | ||
| 25 | except KeyboardInterrupt: | 45 | except KeyboardInterrupt: |
| 26 | server.socket.close() | 46 | server.socket.close() |
| 27 | 47 | ||
diff --git a/youtube_podcaster/__main__.py b/youtube_podcaster/__main__.py deleted file mode 100644 index 941a79a..0000000 --- a/youtube_podcaster/__main__.py +++ /dev/null | |||
| @@ -1,8 +0,0 @@ | |||
| 1 | #!/usr/bin/env python3 | ||
| 2 | |||
| 3 | import youtube_podcaster | ||
| 4 | |||
| 5 | if __name__ == "__main__": | ||
| 6 | youtube_podcaster.main() | ||
| 7 | |||
| 8 | # vim: set ts=8 sw=4 tw=0 et : | ||
diff --git a/youtube_podcaster/config.py b/youtube_podcaster/config.py new file mode 100644 index 0000000..bfce902 --- /dev/null +++ b/youtube_podcaster/config.py | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | import sys | ||
| 2 | import os | ||
| 3 | import json | ||
| 4 | |||
| 5 | |||
| 6 | class ConfigException(Exception): | ||
| 7 | def __init__(self, msg): | ||
| 8 | super(ConfigException, self).__init__("Config exception: %s" % (msg)) | ||
| 9 | |||
| 10 | |||
| 11 | class Config: | ||
| 12 | instance = None | ||
| 13 | |||
| 14 | def parse_config(config_file=None): | ||
| 15 | if not Config.instance: | ||
| 16 | Config.instance = Config(config_file) | ||
| 17 | |||
| 18 | return Config.instance | ||
| 19 | |||
| 20 | def __init__(self, args): | ||
| 21 | if args.config: | ||
| 22 | config = args.config | ||
| 23 | else: | ||
| 24 | config_file = "youtube-podcaster.json" | ||
| 25 | |||
| 26 | if sys.platform == "linux" and not hasattr(sys, "real_prefix"): | ||
| 27 | config = "/etc/%s" % (config_file) | ||
| 28 | else: | ||
| 29 | config = "%s/etc/%s" % (sys.prefix, config_file) | ||
| 30 | |||
| 31 | if not os.path.isfile(config): | ||
| 32 | raise ConfigException("%s not found" % (config)) | ||
| 33 | |||
| 34 | try: | ||
| 35 | config = json.load(open(config)) | ||
| 36 | except json.decoder.JSONDecodeError as e: | ||
| 37 | raise ConfigException("%s is not valid json: %s" % ( | ||
| 38 | config, str(e))) | ||
| 39 | |||
| 40 | try: | ||
| 41 | self.server = config["server"] | ||
| 42 | self.youtube = config["youtube"] | ||
| 43 | self.podcasts = config["podcasts"] | ||
| 44 | except KeyError as e: | ||
| 45 | raise ConfigException("Missing %s-section in %s" % ( | ||
| 46 | str(e), config)) | ||
| 47 | |||
| 48 | for arg, value in vars(args).items(): | ||
| 49 | if not value: | ||
| 50 | continue | ||
| 51 | |||
| 52 | if arg == "interface": | ||
| 53 | self.server["interface"] = value | ||
| 54 | elif arg == "port": | ||
| 55 | self.server["port"] = value | ||
| 56 | elif arg == "apikey": | ||
| 57 | self.youtube["api-key"] = value | ||
| 58 | |||
| 59 | def get_server_address(self): | ||
| 60 | interface = str(self.server["interface"]) | ||
| 61 | port = int(self.server["port"]) | ||
| 62 | return interface, port | ||
| 63 | |||
| 64 | # vim: set ts=8 sw=4 tw=0 et : | ||
diff --git a/youtube_podcaster/podcastfeeder.py b/youtube_podcaster/podcastfeeder.py index 3f752d2..4181d6c 100644 --- a/youtube_podcaster/podcastfeeder.py +++ b/youtube_podcaster/podcastfeeder.py | |||
| @@ -5,10 +5,10 @@ from .podcastupdater import ( | |||
| 5 | ) | 5 | ) |
| 6 | 6 | ||
| 7 | 7 | ||
| 8 | def create_feeder(youtube_config, podcast_config): | 8 | def create_feeder(config): |
| 9 | class PodcastFeeder(BaseHTTPRequestHandler): | 9 | class PodcastFeeder(BaseHTTPRequestHandler): |
| 10 | def __init__(self, request, client_address, server): | 10 | def __init__(self, request, client_address, server): |
| 11 | self.updater = PodcastUpdater(youtube_config, podcast_config) | 11 | self.updater = PodcastUpdater(config) |
| 12 | super(PodcastFeeder, self).__init__(request, client_address, server) | 12 | super(PodcastFeeder, self).__init__(request, client_address, server) |
| 13 | 13 | ||
| 14 | def do_GET(self): | 14 | def do_GET(self): |
diff --git a/youtube_podcaster/podcastupdater.py b/youtube_podcaster/podcastupdater.py index 4a0a017..75b8b10 100644 --- a/youtube_podcaster/podcastupdater.py +++ b/youtube_podcaster/podcastupdater.py | |||
| @@ -2,6 +2,7 @@ import pickle | |||
| 2 | import os | 2 | import os |
| 3 | import time | 3 | import time |
| 4 | import hashlib | 4 | import hashlib |
| 5 | import sys | ||
| 5 | 6 | ||
| 6 | from feedgen.feed import FeedGenerator | 7 | from feedgen.feed import FeedGenerator |
| 7 | 8 | ||
| @@ -11,11 +12,19 @@ from . import ( | |||
| 11 | 12 | ||
| 12 | 13 | ||
| 13 | class PodcastUpdater: | 14 | class PodcastUpdater: |
| 14 | def __init__(self, youtube_config, podcast_config): | 15 | def __init__(self, config): |
| 15 | self.podcasts = podcast_config | 16 | self.podcasts = config.podcasts |
| 16 | self.youtube = youtube.Youtube(youtube_config["api-key"]) | 17 | self.youtube = youtube.Youtube(config.youtube["api-key"]) |
| 18 | |||
| 19 | if sys.platform == "linux" and not hasattr(sys, "real_prefix"): | ||
| 20 | self.data_dir = "/var/lib/youtube-podcaster" | ||
| 21 | else: | ||
| 22 | self.data_dir = "%s/var/lib/youtube-podcaster" % (sys.prefix) | ||
| 23 | |||
| 24 | os.makedirs(self.data_dir, 0o755, True) | ||
| 25 | |||
| 26 | self.feeds_file = "%s/feeds.dump" % (self.data_dir) | ||
| 17 | 27 | ||
| 18 | self.feeds_file = "feeds.save" | ||
| 19 | if os.path.isfile(self.feeds_file): | 28 | if os.path.isfile(self.feeds_file): |
| 20 | with open(self.feeds_file, "rb") as feeds: | 29 | with open(self.feeds_file, "rb") as feeds: |
| 21 | self.feeds = pickle.load(feeds) | 30 | self.feeds = pickle.load(feeds) |
| @@ -42,6 +51,8 @@ class PodcastUpdater: | |||
| 42 | 51 | ||
| 43 | def update_podcast(self, channel, playlist): | 52 | def update_podcast(self, channel, playlist): |
| 44 | feed_id = hashlib.sha1(bytes("%s %s" % (channel, playlist), "UTF-8")).hexdigest() | 53 | feed_id = hashlib.sha1(bytes("%s %s" % (channel, playlist), "UTF-8")).hexdigest() |
| 54 | feed_file = "%s/%s.xml" % (self.data_dir, feed_id) | ||
| 55 | |||
| 45 | yt_channel = self.youtube.get_channel(channel)[0] | 56 | yt_channel = self.youtube.get_channel(channel)[0] |
| 46 | yt_playlists = self.youtube.get_playlists(yt_channel, 50) | 57 | yt_playlists = self.youtube.get_playlists(yt_channel, 50) |
| 47 | 58 | ||
| @@ -58,17 +69,16 @@ class PodcastUpdater: | |||
| 58 | 69 | ||
| 59 | if feed.last_updated < time.time() - 600: | 70 | if feed.last_updated < time.time() - 600: |
| 60 | self.populate_feed(feed, feed_id, yt_playlist) | 71 | self.populate_feed(feed, feed_id, yt_playlist) |
| 72 | feed.rss_file(feed_file) | ||
| 61 | 73 | ||
| 62 | feed_file = "%s.xml" % (feed_id) | 74 | with open(self.feeds_file, "wb") as feeds: |
| 63 | self.feeds[feed_id].rss_file(feed_file) | 75 | pickle.dump(self.feeds, feeds) |
| 64 | |||
