From a7fe15b99d64904855ba2bd3649b1a3642824a75 Mon Sep 17 00:00:00 2001 From: Tom van der Lee Date: Sun, 11 Oct 2015 17:54:06 +0200 Subject: Can actually be installed with setup.py now --- .gitignore | 6 ++ setup.cfg | 2 + youtube_podcaster/__init__.py | 28 ++++++ youtube_podcaster/__main__.py | 8 ++ youtube_podcaster/podcastfeeder.py | 43 +++++++++ youtube_podcaster/podcastupdater.py | 106 +++++++++++++++++++++++ youtube_podcaster/youtube-podcaster | 159 ---------------------------------- youtube_podcaster/youtube/__init__.py | 4 +- 8 files changed, 195 insertions(+), 161 deletions(-) create mode 100644 youtube_podcaster/__init__.py create mode 100644 youtube_podcaster/__main__.py create mode 100644 youtube_podcaster/podcastfeeder.py create mode 100644 youtube_podcaster/podcastupdater.py delete mode 100755 youtube_podcaster/youtube-podcaster diff --git a/.gitignore b/.gitignore index f41c0ad..55cf740 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,9 @@ docs/api/* docs/_build/* cover/* MANIFEST + +# Outputted files by youtube-podcaster +*.xml +*.save +downloads/ +youtube-podcaster.json diff --git a/setup.cfg b/setup.cfg index 1d3b7c5..f1b8f8c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -13,6 +13,8 @@ classifiers = Development Status :: 3 - Alpha, Programming Language :: Python :: 3 :: Only [entry_points] +console_scripts = + youtube-podcaster = youtube_podcaster:main # Add here console scripts like: # console_scripts = # hello_world = youtube_podcaster.module:function diff --git a/youtube_podcaster/__init__.py b/youtube_podcaster/__init__.py new file mode 100644 index 0000000..32aee7c --- /dev/null +++ b/youtube_podcaster/__init__.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 + +import json + +from http.server import ( + HTTPServer, +) + +from . import ( + youtube, +) + +from .podcastfeeder import ( + create_feeder +) + + +def main(): + config = json.load(open("youtube-podcaster.json")) + + try: + PodcastFeeder = create_feeder(config["youtube"], config["podcasts"]) + server = HTTPServer(("", 8888), PodcastFeeder) + server.serve_forever() + except KeyboardInterrupt: + server.socket.close() + +# vim: set ts=8 sw=4 tw=0 et : diff --git a/youtube_podcaster/__main__.py b/youtube_podcaster/__main__.py new file mode 100644 index 0000000..941a79a --- /dev/null +++ b/youtube_podcaster/__main__.py @@ -0,0 +1,8 @@ +#!/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/podcastfeeder.py b/youtube_podcaster/podcastfeeder.py new file mode 100644 index 0000000..3f752d2 --- /dev/null +++ b/youtube_podcaster/podcastfeeder.py @@ -0,0 +1,43 @@ +from http.server import BaseHTTPRequestHandler + +from .podcastupdater import ( + PodcastUpdater +) + + +def create_feeder(youtube_config, podcast_config): + class PodcastFeeder(BaseHTTPRequestHandler): + def __init__(self, request, client_address, server): + self.updater = PodcastUpdater(youtube_config, podcast_config) + super(PodcastFeeder, self).__init__(request, client_address, server) + + def do_GET(self): + path = self.path.split('/') + + if len(path) == 3: + channel = path[1] + playlist = path[2] + else: + return self.return_error(404) + + xml = self.updater.get_xml(channel, playlist) + + if not xml: + return self.return_error(404) + else: + self.send_response(200) + self.send_header("Content-type", "text/xml") + self.end_headers() + self.wfile.write(bytes(xml, 'UTF-8')) + + def return_error(self, code): + self.send_response(code) + self.send_header("Content-type", "text/html") + self.end_headers() + + reponse = "Error: %s" % (code) + self.wfile.write(bytes(reponse, 'UTF-8')) + + return PodcastFeeder + +# vim: set ts=8 sw=4 tw=0 et : diff --git a/youtube_podcaster/podcastupdater.py b/youtube_podcaster/podcastupdater.py new file mode 100644 index 0000000..4a0a017 --- /dev/null +++ b/youtube_podcaster/podcastupdater.py @@ -0,0 +1,106 @@ +import pickle +import os +import time +import hashlib + +from feedgen.feed import FeedGenerator + +from . import ( + youtube, +) + + +class PodcastUpdater: + def __init__(self, youtube_config, podcast_config): + self.podcasts = podcast_config + self.youtube = youtube.Youtube(youtube_config["api-key"]) + + 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) + else: + self.feeds = {} + + def get_xml(self, channel, playlist): + channel = channel.replace('_', ' ') + playlist = playlist.replace('_', ' ') + + for podcast in self.podcasts: + if podcast["username"] == channel: + break + else: + return None + + if playlist not in podcast["playlists"]: + return None + + xml = self.update_podcast(channel, playlist) + + if xml: + return open(xml).read() + + def update_podcast(self, channel, playlist): + feed_id = hashlib.sha1(bytes("%s %s" % (channel, playlist), "UTF-8")).hexdigest() + yt_channel = self.youtube.get_channel(channel)[0] + yt_playlists = self.youtube.get_playlists(yt_channel, 50) + + for yt_playlist in yt_playlists: + if yt_playlist["snippet"]["title"] == playlist: + break + else: + return None + + if feed_id in self.feeds: + feed = self.feeds[feed_id] + else: + feed = self.add_feed(feed_id, yt_playlist) + + if feed.last_updated < time.time() - 600: + self.populate_feed(feed, feed_id, yt_playlist) + + 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) + + return "%s.xml" % (feed_id) + + def add_feed(self, feed_id, yt_playlist): + feed = FeedGenerator() + feed.load_extension("podcast") + feed.id(feed_id) + feed.title(yt_playlist["snippet"]["title"]) + feed.author({"name": yt_playlist["snippet"]["channelTitle"]}) + feed.description(yt_playlist["snippet"]["description"]) + feed.logo(yt_playlist["snippet"]["thumbnails"]["standard"]["url"]) + 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): + videos = self.youtube.get_playlist_items(yt_playlist, max_results) + downloader = youtube.Downloader.get_instance("vorbis", "downloads", "192.168.178.100") + + entries = feed.entry() + for video in videos: + video_id = hashlib.sha1(bytes(video["id"], "UTF-8")).hexdigest() + for entry in entries: + if entry.id() == video_id: + 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"]) + feed_entry.description(video["snippet"]["description"]) + feed_entry.published(video["snippet"]["publishedAt"]) + feed_entry.enclosure(url, size, mime) + + feed.last_updated = time.time() + +# vim: set ts=8 sw=4 tw=0 et : diff --git a/youtube_podcaster/youtube-podcaster b/youtube_podcaster/youtube-podcaster deleted file mode 100755 index bb2db26..0000000 --- a/youtube_podcaster/youtube-podcaster +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env python3 -import time -import os -import pickle -import json - -from http.server import HTTPServer, BaseHTTPRequestHandler -from hashlib import sha1 - -from feedgen.feed import FeedGenerator - -import youtube - - -class PodcastUpdater: - instance = None - - def get_instance(): - if PodcastUpdater.instance: - return PodcastUpdater.instance - else: - PodcastUpdater.instance = PodcastUpdater() - return PodcastUpdater.instance - - def __init__(self): - self.podcasts = config["podcasts"] - - 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) - else: - self.feeds = {} - - def get_xml(self, channel, playlist): - channel = channel.replace('_', ' ') - playlist = playlist.replace('_', ' ') - - for podcast in self.podcasts: - if podcast["username"] == channel: - break - else: - return None - - if playlist not in podcast["playlists"]: - return None - - xml = self.update_podcast(channel, playlist) - - if xml: - return open(xml).read() - - def update_podcast(self, channel, playlist): - feed_id = sha1(bytes("%s %s" % (channel, playlist), "UTF-8")).hexdigest() - yt_channel = yt.get_channel(channel)[0] - yt_playlists = yt.get_playlists(yt_channel, 50) - - for yt_playlist in yt_playlists: - if yt_playlist["snippet"]["title"] == playlist: - break - else: - return None - - if feed_id in self.feeds: - feed = self.feeds[feed_id] - else: - feed = self.add_feed(feed_id, yt_playlist) - - if feed.last_updated < time.time() - 600: - self.populate_feed(feed, feed_id, yt_playlist) - - 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) - - return "%s.xml" % (feed_id) - - def add_feed(self, feed_id, yt_playlist): - feed = FeedGenerator() - feed.load_extension("podcast") - feed.id(feed_id) - feed.title(yt_playlist["snippet"]["title"]) - feed.author({"name": yt_playlist["snippet"]["channelTitle"]}) - feed.description(yt_playlist["snippet"]["description"]) - feed.logo(yt_playlist["snippet"]["thumbnails"]["standard"]["url"]) - 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): - videos = yt.get_playlist_items(yt_playlist, max_results) - downloader = youtube.Downloader.get_instance("vorbis", "downloads", "192.168.178.100") - - entries = feed.entry() - for video in videos: - video_id = sha1(bytes(video["id"], "UTF-8")).hexdigest() - for entry in entries: - if entry.id() == video_id: - 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"]) - feed_entry.description(video["snippet"]["description"]) - feed_entry.published(video["snippet"]["publishedAt"]) - feed_entry.enclosure(url, size, mime) - - feed.last_updated = time.time() - - -class PodcastFeeder(BaseHTTPRequestHandler): - def do_GET(self): - updater = PodcastUpdater.get_instance() - path = self.path.split('/') - - if len(path) == 3: - channel = path[1] - playlist = path[2] - else: - return self.return_error(404) - - xml = updater.get_xml(channel, playlist) - - if not xml: - return self.return_error(404) - else: - self.send_response(200) - self.send_header("Content-type", "text/xml") - self.end_headers() - self.wfile.write(bytes(xml, 'UTF-8')) - - def return_error(self, code): - self.send_response(code) - self.send_header("Content-type", "text/html") - self.end_headers() - - reponse = "Error: %s" % (code) - self.wfile.write(bytes(reponse, 'UTF-8')) - - -def main(): - try: - server = HTTPServer(("", 8888), PodcastFeeder) - server.serve_forever() - except KeyboardInterrupt: - server.socket.close() - -if __name__ == "__main__": - config = json.load(open("youtube-podcaster.json")) - yt = youtube.Youtube(config["youtube"]["api-key"]) - main() - -# vim: set ts=8 sw=4 tw=0 et : diff --git a/youtube_podcaster/youtube/__init__.py b/youtube_podcaster/youtube/__init__.py index d26ae61..e84ecc6 100644 --- a/youtube_podcaster/youtube/__init__.py +++ b/youtube_podcaster/youtube/__init__.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -from youtube.youtube import Youtube -from youtube.downloader import Downloader +from .youtube import Youtube +from .downloader import Downloader # vim: set ts=8 sw=4 tw=0 et : -- cgit v1.2.3