From 9cc311ca5376bfcbcacf7ac492f5958acfac0682 Mon Sep 17 00:00:00 2001 From: Tom van der Lee Date: Mon, 19 Oct 2015 21:38:00 +0200 Subject: Added commandline options and config file --- youtube_podcaster/__init__.py | 44 ++++++++++++----- youtube_podcaster/__main__.py | 8 ---- youtube_podcaster/config.py | 64 +++++++++++++++++++++++++ youtube_podcaster/podcastfeeder.py | 4 +- youtube_podcaster/podcastupdater.py | 34 +++++++++---- youtube_podcaster/youtube-podcaster.json.sample | 10 ---- youtube_podcaster/youtube/downloader.py | 23 +++++---- 7 files changed, 135 insertions(+), 52 deletions(-) delete mode 100644 youtube_podcaster/__main__.py create mode 100644 youtube_podcaster/config.py delete mode 100644 youtube_podcaster/youtube-podcaster.json.sample (limited to 'youtube_podcaster') 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 @@ #!/usr/bin/env python3 -import json +import argparse -from http.server import ( - HTTPServer, -) +from http.server import HTTPServer -from . import ( - youtube, +from .podcastfeeder import create_feeder +from .config import ( + Config, + ConfigException ) -from .podcastfeeder import ( - create_feeder -) +""" +Start the program +""" def main(): - config = json.load(open("youtube-podcaster.json")) + arg_parser = argparse.ArgumentParser(prog="youtube-podcaster", + description="Converts youtube \ + playlists to RSS-feeds") + arg_parser.add_argument("-c", "--config", + dest="config", + help="Use CONFIG as the config file") + arg_parser.add_argument("-i", "--interface", + dest="interface", + help="The interface the http server will listen on") + arg_parser.add_argument("-p", "--port", + dest="port", + help="The port the http server will listen on") + arg_parser.add_argument("--api-key", + dest="apikey", + help="The YouTube API v3 key") + args = arg_parser.parse_args() try: - PodcastFeeder = create_feeder(config["youtube"], config["podcasts"]) - server = HTTPServer(("", 8888), PodcastFeeder) + config = Config.parse_config(args) + + PodcastFeeder = create_feeder(config) + + server = HTTPServer(config.get_server_address(), PodcastFeeder) server.serve_forever() + except ConfigException as e: + print(e) except KeyboardInterrupt: server.socket.close() 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 @@ -#!/usr/bin/env python3 - -import youtube_podcaster - -if __name__ == "__main__": - youtube_podcaster.main() - -# 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 @@ +import sys +import os +import json + + +class ConfigException(Exception): + def __init__(self, msg): + super(ConfigException, self).__init__("Config exception: %s" % (msg)) + + +class Config: + instance = None + + def parse_config(config_file=None): + if not Config.instance: + Config.instance = Config(config_file) + + return Config.instance + + def __init__(self, args): + if args.config: + config = args.config + else: + config_file = "youtube-podcaster.json" + + if sys.platform == "linux" and not hasattr(sys, "real_prefix"): + config = "/etc/%s" % (config_file) + else: + config = "%s/etc/%s" % (sys.prefix, config_file) + + if not os.path.isfile(config): + raise ConfigException("%s not found" % (config)) + + try: + config = json.load(open(config)) + except json.decoder.JSONDecodeError as e: + raise ConfigException("%s is not valid json: %s" % ( + config, str(e))) + + try: + self.server = config["server"] + self.youtube = config["youtube"] + self.podcasts = config["podcasts"] + except KeyError as e: + raise ConfigException("Missing %s-section in %s" % ( + str(e), config)) + + for arg, value in vars(args).items(): + if not value: + continue + + if arg == "interface": + self.server["interface"] = value + elif arg == "port": + self.server["port"] = value + elif arg == "apikey": + self.youtube["api-key"] = value + + def get_server_address(self): + interface = str(self.server["interface"]) + port = int(self.server["port"]) + return interface, port + +# 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 ( ) -def create_feeder(youtube_config, podcast_config): +def create_feeder(config): class PodcastFeeder(BaseHTTPRequestHandler): def __init__(self, request, client_address, server): - self.updater = PodcastUpdater(youtube_config, podcast_config) + self.updater = PodcastUpdater(config) super(PodcastFeeder, self).__init__(request, client_address, server) 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 import os import time import hashlib +import sys from feedgen.feed import FeedGenerator @@ -11,11 +12,19 @@ from . import ( class PodcastUpdater: - def __init__(self, youtube_config, podcast_config): - self.podcasts = podcast_config - self.youtube = youtube.Youtube(youtube_config["api-key"]) + def __init__(self, config): + self.podcasts = config.podcasts + self.youtube = youtube.Youtube(config.youtube["api-key"]) + + if sys.platform == "linux" and not hasattr(sys, "real_prefix"): + self.data_dir = "/var/lib/youtube-podcaster" + else: + self.data_dir = "%s/var/lib/youtube-podcaster" % (sys.prefix) + + os.makedirs(self.data_dir, 0o755, True) + + self.feeds_file = "%s/feeds.dump" % (self.data_dir) - self.feeds_file = "feeds.save" if os.path.isfile(self.feeds_file): with open(self.feeds_file, "rb") as feeds: self.feeds = pickle.load(feeds) @@ -42,6 +51,8 @@ class PodcastUpdater: def update_podcast(self, channel, playlist): feed_id = hashlib.sha1(bytes("%s %s" % (channel, playlist), "UTF-8")).hexdigest() + feed_file = "%s/%s.xml" % (self.data_dir, feed_id) + yt_channel = self.youtube.get_channel(channel)[0] yt_playlists = self.youtube.get_playlists(yt_channel, 50) @@ -58,17 +69,16 @@ class PodcastUpdater: if feed.last_updated < time.time() - 600: self.populate_feed(feed, feed_id, yt_playlist) + feed.rss_file(feed_file) - feed_file = "%s.xml" % (feed_id) - self.feeds[feed_id].rss_file(feed_file) - - with open(self.feeds_file, "wb") as feed: - pickle.dump(self.feeds, feed) + with open(self.feeds_file, "wb") as feeds: + pickle.dump(self.feeds, feeds) - return "%s.xml" % (feed_id) + return feed_file def add_feed(self, feed_id, yt_playlist): feed = FeedGenerator() + feed.load_extension("podcast") feed.id(feed_id) feed.title(yt_playlist["snippet"]["title"]) @@ -78,7 +88,9 @@ class PodcastUpdater: feed.link(href="https://www.youtube.com/playlist?list=%s" % (yt_playlist["id"])) feed.rss_str(pretty=True) feed.last_updated = 0 + self.feeds[feed_id] = feed + return feed def populate_feed(self, feed, feed_id, yt_playlist, max_results=5): @@ -93,7 +105,9 @@ class PodcastUpdater: break else: url, size, mime = downloader.download(video, video_id, feed_id) + feed_entry = feed.add_entry() + feed_entry.id(video_id) feed_entry.guid(video_id) feed_entry.title(video["snippet"]["title"]) diff --git a/youtube_podcaster/youtube-podcaster.json.sample b/youtube_podcaster/youtube-podcaster.json.sample deleted file mode 100644 index d9d078f..0000000 --- a/youtube_podcaster/youtube-podcaster.json.sample +++ /dev/null @@ -1,10 +0,0 @@ -{ - "youtube": { - "api-key": "youtube-api-v3-key-here" - }, - - "podcasts": [{ - "username": "youtube-username-here", - "playlists": ["playlist-name-here"] - }] -} diff --git a/youtube_podcaster/youtube/downloader.py b/youtube_podcaster/youtube/downloader.py index ca1327b..529902a 100644 --- a/youtube_podcaster/youtube/downloader.py +++ b/youtube_podcaster/youtube/downloader.py @@ -1,41 +1,44 @@ #!/usr/bin/env python3 -import youtube_dl import os +import mimetypes + +import youtube_dl class Downloader: instance = None def get_instance(file_format, location, base_url): - if Downloader.instance: - return Downloader.instance - else: + if not Downloader.instance: Downloader.instance = Downloader(file_format, location, base_url) - return Downloader.instance + return Downloader.instance def __init__(self, file_format, location, base_url): self.file_format = file_format self.location = location self.base_url = base_url + if file_format == "vorbis": + self.extension = "ogg" + def download(self, video, video_id, feed_id): - output = "%s/%s/%s.ogg" % (self.location, feed_id, video_id) + output = "%s/%s/%s.%s" % (self.location, feed_id, video_id, self.extension) options = {"format": "bestaudio/best", "outtmpl": output, "postprocessors": [{ "key": "FFmpegExtractAudio", "preferredcodec": self.file_format }], - "nooverwrites": True - } + "nooverwrites": True} + video_url = "https://www.youtube.com/watch?v=%s" % (video["snippet"]["resourceId"]["videoId"]) youtube_dl.YoutubeDL(options).download([video_url]) - url = "%s/%s/%s.ogg" % (self.base_url, feed_id, video_id) + url = "%s/%s/%s.%s" % (self.base_url, feed_id, video_id, self.extension) size = str(os.path.getsize(output)) - mime = "audio/ogg" + mime = mimetypes.guess_type(output)[0] return (url, size, mime) -- cgit v1.2.3