diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | README.rst | 1 | ||||
| -rw-r--r-- | docs/index.rst | 27 | ||||
| -rw-r--r-- | setup.cfg | 50 | ||||
| -rw-r--r-- | youtube-podcaster.json.example | 21 | ||||
| -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 |
12 files changed, 171 insertions, 116 deletions
| @@ -44,4 +44,3 @@ MANIFEST | |||
| 44 | *.xml | 44 | *.xml |
| 45 | *.save | 45 | *.save |
| 46 | downloads/ | 46 | downloads/ |
| 47 | youtube-podcaster.json | ||
| @@ -1,3 +1,4 @@ | |||
| 1 | ================= | ||
| 1 | YouTube Podcaster | 2 | YouTube Podcaster |
| 2 | ================= | 3 | ================= |
| 3 | 4 | ||
diff --git a/docs/index.rst b/docs/index.rst index 62a305d..73e63f8 100644 --- a/docs/index.rst +++ b/docs/index.rst | |||
| @@ -1,29 +1,4 @@ | |||
| 1 | ================= | 1 | .. include:: ../README.rst |
| 2 | youtube-podcaster | ||
| 3 | ================= | ||
| 4 | |||
| 5 | This is the documentation of **youtube-podcaster**. | ||
| 6 | |||
| 7 | .. note:: | ||
| 8 | |||
| 9 | This is the main page of your project's `Sphinx <http://sphinx-doc.org/>`_ | ||
| 10 | documentation. It is formatted in `reStructuredText | ||
| 11 | <http://sphinx-doc.org/rest.html>`__. Add additional pages by creating | ||
| 12 | rst-files in ``docs`` and adding them to the `toctree | ||
| 13 | <http://sphinx-doc.org/markup/toctree.html>`_ below. Use then | ||
| 14 | `references <http://sphinx-doc.org/markup/inline.html>`__ in order to link | ||
| 15 | them from this page, e.g. :ref:`authors <authors>` and :ref:`changes`. | ||
| 16 | It is also possible to refer to the documentation of other Python packages | ||
| 17 | with the `Python domain syntax | ||
| 18 | <http://sphinx-doc.org/domains.html#the-python-domain>`__. By default you | ||
| 19 | can reference the documentation of `Sphinx <http://sphinx.pocoo.org>`__, | ||
| 20 | `Python <http://docs.python.org/>`__, `matplotlib | ||
| 21 | <http://matplotlib.sourceforge.net>`__, `NumPy | ||
| 22 | <http://docs.scipy.org/doc/numpy>`__, `Scikit-Learn | ||
| 23 | <http://scikit-learn.org/stable>`__, `Pandas | ||
| 24 | <http://pandas.pydata.org/pandas-docs/stable>`__, `SciPy | ||
| 25 | <http://docs.scipy.org/doc/scipy/reference/>`__. You can add more by | ||
| 26 | extending the ``intersphinx_mapping`` in your Sphinx's ``conf.py``. | ||
| 27 | 2 | ||
| 28 | Contents | 3 | Contents |
| 29 | ======== | 4 | ======== |
| @@ -6,53 +6,35 @@ author-email = t0m.vd.l33@gmail.com | |||
| 6 | license = MIT License | 6 | license = MIT License |
| 7 | home-page = http://github.com/tomvanderlee/youtube-podcaster | 7 | home-page = http://github.com/tomvanderlee/youtube-podcaster |
| 8 | description-file = README.rst | 8 | description-file = README.rst |
| 9 | # Add here all kinds of additional classifiers as defined under | ||
| 10 | # https://pypi.python.org/pypi?%3Aaction=list_classifiers | ||
| 11 | classifiers = Development Status :: 3 - Alpha, | 9 | classifiers = Development Status :: 3 - Alpha, |
| 12 | License :: OSI Approved :: MIT License | 10 | License :: OSI Approved :: MIT License |
| 13 | Programming Language :: Python :: 3 :: Only | 11 | Programming Language :: Python :: 3 :: Only |
| 14 | 12 | ||
| 15 | [entry_points] | 13 | [entry_points] |
| 16 | console_scripts = | 14 | console_scripts = |
| 17 | youtube-podcaster = youtube_podcaster:main | 15 | youtube-podcaster = youtube_podcaster:main |
| 18 | # Add here console scripts like: | ||
| 19 | # console_scripts = | ||
| 20 | # hello_world = youtube_podcaster.module:function | ||
| 21 | # as well as other entry_points. | ||
| 22 | |||
| 23 | 16 | ||
| 24 | [files] | 17 | [files] |
| 25 | # Add here 'data_files', 'packages' or 'namespace_packages'. | 18 | packages = |
| 26 | # Additional data files are defined as key value pairs of source and target: | 19 | youtube_podcaster |
| 27 | packages = | 20 | data_files = |
| 28 | youtube_podcaster | 21 | share/man/man1/ = docs/_build/man/* |
| 29 | # data_files = | 22 | share/youtube-podcaster/ = youtube-podcaster.json.example |
| 30 | # share/youtube_podcaster_docs = docs/* | ||
| 31 | 23 | ||
| 32 | [extras] | 24 | [extras] |
| 33 | # Add here additional requirements for extra features, like: | ||
| 34 | # PDF = | ||
| 35 | # ReportLab>=1.2 | ||
| 36 | # RXP | ||
| 37 | 25 | ||
| 38 | [test] | 26 | [test] |
| 39 | # py.test options when running `python setup.py test` | ||
| 40 | addopts = tests | 27 | addopts = tests |
| 41 | 28 | ||
| 42 | [pytest] | 29 | [pytest] |
| 43 | # Options for py.test: | 30 | addopts = |
| 44 | # Specify command line options as you would do when invoking py.test directly. | 31 | --cov youtube_podcaster --cov-report term-missing |
| 45 | # e.g. --cov-report html (or xml) for html/xml output or --junitxml junit.xml | 32 | --verbose |
| 46 | # in order to write a coverage file that can be read by Jenkins. | ||
| 47 | addopts = | ||
| 48 | --cov youtube_podcaster --cov-report term-missing | ||
| 49 | --verbose | ||
| 50 | 33 | ||
| 51 | [aliases] | 34 | [aliases] |
| 52 | docs = build_sphinx | 35 | docs = build_sphinx |
| 53 | 36 | ||
| 54 | [bdist_wheel] | 37 | [bdist_wheel] |
| 55 | # Use this option if your package is pure-python | ||
| 56 | universal = 1 | 38 | universal = 1 |
| 57 | 39 | ||
| 58 | [build_sphinx] | 40 | [build_sphinx] |
| @@ -60,17 +42,11 @@ source_dir = docs | |||
| 60 | build_dir = docs/_build | 42 | build_dir = docs/_build |
| 61 | 43 | ||
| 62 | [pbr] | 44 | [pbr] |
| 63 | # Let pbr run sphinx-apidoc | ||
| 64 | autodoc_tree_index_modules = True | 45 | autodoc_tree_index_modules = True |
| 65 | # autodoc_tree_excludes = ... | ||
| 66 | # Let pbr itself generate the apidoc | ||
| 67 | # autodoc_index_modules = True | ||
| 68 | # autodoc_exclude_modules = ... | ||
| 69 | # Convert warnings to errors | ||
| 70 | # warnerrors = True | ||
| 71 | 46 | ||
| 72 | [devpi:upload] | 47 | [devpi:upload] |
| 73 | # Options for the devpi: PyPI serer and packaging tool | ||
| 74 | # VCS export must be deactivated since we are using setuptools-scm | ||
| 75 | no-vcs = 1 | 48 | no-vcs = 1 |
| 76 | format = bdist_wheel | 49 | format = bdist_wheel |
| 50 | |||
| 51 | [easy_install] | ||
| 52 | |||
diff --git a/youtube-podcaster.json.example b/youtube-podcaster.json.example new file mode 100644 index 0000000..d9b48af --- /dev/null +++ b/youtube-podcaster.json.example | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | { | ||
| 2 | "server" : { | ||
| 3 | "interface": "0.0.0.0", | ||
| 4 | "port": 8888 | ||
| 5 | }, | ||
| 6 | |||
| 7 | "youtube": { | ||
| 8 | "api-key": "" | ||
| 9 | }, | ||
| 10 | |||
| 11 | "podcasts": [{ | ||
| 12 | "username": "", | ||
| 13 | "playlists": [""] | ||
| 14 | }], | ||
| 15 | |||
| 16 | "downloads": { | ||
| 17 | "format": "vorbis", | ||
| 18 | "path": "/tmp/downloads/", | ||
| 19 | "url": "localhost" | ||
| 20 | } | ||
| 21 | } | ||
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 | |||
| 65 | with open(self.feeds_file, "wb") as feed: | ||
| 66 | pickle.dump(self.feeds, feed) | ||
| 67 | 76 | ||
| 68 | return "%s.xml" % (feed_id) | 77 | return feed_file |
| 69 | 78 | ||
| 70 | def add_feed(self, feed_id, yt_playlist): | 79 | def add_feed(self, feed_id, yt_playlist): |
| 71 | feed = FeedGenerator() | 80 | feed = FeedGenerator() |
| 81 | |||
| 72 | feed.load_extension("podcast") | 82 | feed.load_extension("podcast") |
| 73 | feed.id(feed_id) | 83 | feed.id(feed_id) |
| 74 | feed.title(yt_playlist["snippet"]["title"]) | 84 | feed.title(yt_playlist["snippet"]["title"]) |
| @@ -78,7 +88,9 @@ class PodcastUpdater: | |||
| 78 | feed.link(href="https://www.youtube.com/playlist?list=%s" % (yt_playlist["id"])) | 88 | feed.link(href="https://www.youtube.com/playlist?list=%s" % (yt_playlist["id"])) |
| 79 | feed.rss_str(pretty=True) | 89 | feed.rss_str(pretty=True) |
| 80 | feed.last_updated = 0 | 90 | feed.last_updated = 0 |
| 91 | |||
| 81 | self.feeds[feed_id] = feed | 92 | self.feeds[feed_id] = feed |
| 93 | |||
| 82 | return feed | 94 | return feed |
| 83 | 95 | ||
| 84 | def populate_feed(self, feed, feed_id, yt_playlist, max_results=5): | 96 | def populate_feed(self, feed, feed_id, yt_playlist, max_results=5): |
| @@ -93,7 +105,9 @@ class PodcastUpdater: | |||
| 93 | break | 105 | break |
| 94 | else: | 106 | else: |
| 95 | url, size, mime = downloader.download(video, video_id, feed_id) | 107 | url, size, mime = downloader.download(video, video_id, feed_id) |
| 108 | |||
| 96 | feed_entry = feed.add_entry() | 109 | feed_entry = feed.add_entry() |
| 110 | |||
| 97 | feed_entry.id(video_id) | 111 | feed_entry.id(video_id) |
| 98 | feed_entry.guid(video_id) | 112 | feed_entry.guid(video_id) |
| 99 | feed_entry.title(video["snippet"]["title"]) | 113 | 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 @@ | |||
| 1 | { | ||
| 2 | "youtube": { | ||
| 3 | "api-key": "youtube-api-v3-key-here" | ||
| 4 | }, | ||
| 5 | |||
| 6 | "podcasts": [{ | ||
| 7 | "username": "youtube-username-here", | ||
| 8 | "playlists": ["playlist-name-here"] | ||
| 9 | }] | ||
| 10 | } | ||
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 @@ | |||
| 1 | #!/usr/bin/env python3 | 1 | #!/usr/bin/env python3 |
| 2 | 2 | ||
| 3 | import youtube_dl | ||
| 4 | import os | 3 | import os |
| 4 | import mimetypes | ||
| 5 | |||
| 6 | import youtube_dl | ||
| 5 | 7 | ||
| 6 | 8 | ||
| 7 | class Downloader: | 9 | class Downloader: |
| 8 | instance = None | 10 | instance = None |
| 9 | 11 | ||
| 10 | def get_instance(file_format, location, base_url): | 12 | def get_instance(file_format, location, base_url): |
| 11 | if Downloader.instance: | 13 | if not Downloader.instance: |
| 12 | return Downloader.instance | ||
| 13 | else: | ||
| 14 | Downloader.instance = Downloader(file_format, location, base_url) | 14 | Downloader.instance = Downloader(file_format, location, base_url) |
| 15 | return Downloader.instance | 15 | return Downloader.instance |
| 16 | 16 | ||
| 17 | def __init__(self, file_format, location, base_url): | 17 | def __init__(self, file_format, location, base_url): |
| 18 | self.file_format = file_format | 18 | self.file_format = file_format |
| 19 | self.location = location | 19 | self.location = location |
| 20 | self.base_url = base_url | 20 | self.base_url = base_url |
| 21 | 21 | ||
| 22 | if file_format == "vorbis": | ||
| 23 | self.extension = "ogg" | ||
| 24 | |||
| 22 | def download(self, video, video_id, feed_id): | 25 | def download(self, video, video_id, feed_id): |
| 23 | output = "%s/%s/%s.ogg" % (self.location, feed_id, video_id) | 26 | output = "%s/%s/%s.%s" % (self.location, feed_id, video_id, self.extension) |
| 24 | options = {"format": "bestaudio/best", | 27 | options = {"format": "bestaudio/best", |
| 25 | "outtmpl": output, | 28 | "outtmpl": output, |
| 26 | "postprocessors": [{ | 29 | "postprocessors": [{ |
| 27 | "key": "FFmpegExtractAudio", | 30 | "key": "FFmpegExtractAudio", |
| 28 | "preferredcodec": self.file_format | 31 | "preferredcodec": self.file_format |
| 29 | }], | 32 | }], |
| 30 | "nooverwrites": True | 33 | "nooverwrites": True} |
| 31 | } | 34 | |
| 32 | 35 | ||
| 33 | video_url = "https://www.youtube.com/watch?v=%s" % (video["snippet"]["resourceId"]["videoId"]) | 36 | video_url = "https://www.youtube.com/watch?v=%s" % (video["snippet"]["resourceId"]["videoId"]) |
| 34 | youtube_dl.YoutubeDL(options).download([video_url]) | 37 | youtube_dl.YoutubeDL(options).download([video_url]) |
| 35 | 38 | ||
| 36 | url = "%s/%s/%s.ogg" % (self.base_url, feed_id, video_id) | 39 | url = "%s/%s/%s.%s" % (self.base_url, feed_id, video_id, self.extension) |
| 37 | size = str(os.path.getsize(output)) | 40 | size = str(os.path.getsize(output)) |
| 38 | mime = "audio/ogg" | 41 | mime = mimetypes.guess_type(output)[0] |
| 39 | 42 | ||
| 40 | return (url, size, mime) | 43 | return (url, size, mime) |
| 41 | 44 | ||
