diff --git a/prismedia/cli.py b/prismedia/cli.py index 368d473..a522c3e 100644 --- a/prismedia/cli.py +++ b/prismedia/cli.py @@ -4,13 +4,14 @@ import video as vid from yapsy.PluginManager import PluginManagerSingleton -def helperFunctionnality(options): + +def helperFunctionality(options): pluginManager = PluginManagerSingleton.get() - optionName = "--hearthbeat" + optionName = "--heartbeat" if options.get(optionName): for plugin in pluginManager.getPluginsOfCategory(pi.PluginTypes.PLATFORM): - plugin.plugin_object.hearthbeat() + plugin.plugin_object.heartbeat() return False else: options.pop(optionName) @@ -46,6 +47,6 @@ def parseOptions(options): autoOriginalDate = utils.getOption(options, "--auto-original-date", False) if autoOriginalDate: # TODO: Implement - raise NotImplementedError("--auto-original-date functionnality is not yet implemented.") + raise NotImplementedError("--auto-original-date functionality is not yet implemented.") return video diff --git a/prismedia/core.py b/prismedia/core.py index ad275db..272eac9 100644 --- a/prismedia/core.py +++ b/prismedia/core.py @@ -6,11 +6,11 @@ # TODO: change `youtube-at` and `peertube-at` that are not easely expendable as options in my opinion # TODO: remove `--url-only` and `--batch` """ -prismedia - tool to upload videos to different platforms (historicaly Peertube and Youtube) +prismedia - tool to upload videos to different platforms (historically Peertube and Youtube) Usage: prismedia [options] --file= | [ [...]] - prismedia --hearthbeat + prismedia --heartbeat prismedia -h | --help | -V | --version Options: @@ -22,7 +22,7 @@ Options: WARN: tags with punctuation (!, ', ", ?, ...) are not supported by Mastodon to be published from Peertube -c, --category=STRING Category for the videos, see below. (default: Films) - --licence=STRING Creative Common licence tag (for exemple: CC-BY-SA) (default: proprietary) + --licence=STRING Creative Common licence tag (for example: CC-BY-SA) (default: proprietary) -p, --privacy=STRING Choose between public, unlisted or private. (default: private) --disable-comments Disable comments (Peertube only as YT API does not support) (default: comments are enabled) --nsfw Set the video as No Safe For Work (Peertube only as YT API does not support) (default: video is safe) @@ -52,7 +52,7 @@ Options: Only relevant if --playlist is set. --progress=STRING Set the progress bar view, one of percentage, bigFile, accurate. [default: percentage] - --hearthbeat Use some credits to show some activity for you apikey so the platform know it is used and would not inactivate your keys. + --heartbeat Use some credits to show some activity for you apikey so the platform know it is used and would not inactivate your keys. -h, --help Show this help. Note that calling `help` without the `--` calls a plugin showing a different help for the plugins. -V, --version Show the version. @@ -118,6 +118,7 @@ import logging VERSION = "prismedia v1.0.0-plugins-alpha" + def loadPlugins(basePluginsPath): pluginManager = PluginManagerSingleton.get() pluginManager.setPluginPlaces(basePluginsPath) @@ -145,8 +146,8 @@ def main(): # TODO: add the arguments’s verification (copy/adapt the Schema table) options = docopt(__doc__, version=VERSION) - # Helper functionnalities help the user but do not upload anything - if not cli.helperFunctionnality(options): + # Helper functionalities help the user but do not upload anything + if not cli.helperFunctionality(options): exit(os.EX_OK) video = cli.parseOptions(options) @@ -160,15 +161,15 @@ def main(): logger.critical(utils.get_exception_string(e)) exit(os.EX_CONFIG) - list = utils.getOption(options, "--platform", []) - if list: - platforms = pluginManager.getPluginsOf(categories=pi.PluginTypes.PLATFORM, name=[list.split(",")]) + listPlatforms = utils.getOption(options, "--platform", []) + if listPlatforms: + platforms = pluginManager.getPluginsOf(categories=pi.PluginTypes.PLATFORM, name=[listPlatforms.split(",")]) else: platforms = pluginManager.getPluginsOfCategory(pi.PluginTypes.PLATFORM) - list = utils.getOption(options, "--consumer", None) - if list: - consumers = pluginManager.getPluginsOf(categories=pi.PluginTypes.CONSUMER, name=[list.split(",")]) + listConsumers = utils.getOption(options, "--consumer", None) + if listConsumers: + consumers = pluginManager.getPluginsOf(categories=pi.PluginTypes.CONSUMER, name=[listConsumers.split(",")]) else: consumers = pluginManager.getPluginsOfCategory(pi.PluginTypes.CONSUMER) @@ -178,14 +179,14 @@ def main(): for plugin in platforms: # TODO: Check this is needed or not: in case of no plugin or wrong name maybe the list is empty instead of there being a None value if plugin is None: - # TODO: log instead to error ? critical ? + # TODO: log instead to error? critical? print("No plugin installed name `" + plugin.name + "`.") exit(os.EX_USAGE) try: video.platform[plugin.name] = vid.Platform() if not plugin.plugin_object.prepare_options(video, options): - # A plugin found ill formed options, it should have logged the precises infos + # A plugin found ill formed options, it should have logged the precises info print(plugin.name + " found a malformed option.") exit(os.EX_CONFIG) except Exception as e: @@ -195,13 +196,13 @@ def main(): for plugin in consumers: # TODO: Check this is needed or not: in case of no plugin or wrong name maybe the list is empty instead of there being a None value if plugin is None: - # TODO: log instead to error ? critical ? + # TODO: log instead to error? critical? print("No plugin installed name `" + plugin.name + "`.") exit(os.EX_USAGE) try: if not plugin.plugin_object.prepare_options(video, options): - # A plugin found ill formed options, it should have logged the precises infos + # A plugin found ill formed options, it should have logged the precises info print(plugin.name + " found a malformed option.") exit(os.EX_CONFIG) except Exception as e: @@ -209,7 +210,7 @@ def main(): exit(os.EX_CONFIG) if video.path == "": - # TODO: log instead to error ? critical ? + # TODO: log instead to error? critical? print("No valid path to a video file has been provided.") exit(os.EX_USAGE) diff --git a/prismedia/pluginInterfaces.py b/prismedia/pluginInterfaces.py index a244ff7..51189b8 100644 --- a/prismedia/pluginInterfaces.py +++ b/prismedia/pluginInterfaces.py @@ -1,6 +1,7 @@ from enum import Enum from yapsy.IPlugin import IPlugin + class PluginTypes(Enum): """Plugin Types possibles to instantiate in this program.""" ALL = "All" @@ -8,6 +9,7 @@ class PluginTypes(Enum): PLATFORM = "Platform" CONSUMER = "Consumer" + class IPrismediaBasePlugin(IPlugin): """ Base for prismedia’s plugin. @@ -41,11 +43,11 @@ class IPlatformPlugin(IPrismediaBasePlugin): Interface for the Platform plugin category. """ - def hearthbeat(self): + def heartbeat(self): """ If needed for your platform, use a bit of the api so the platform is aware the keys are still in use. """ - raise NotImplementedError("`hearthbeat` must be reimplemented by %s" % self) + raise NotImplementedError("`heartbeat` must be reimplemented by %s" % self) def upload(self, video, options): """ diff --git a/prismedia/plugins/interfaces/help.py b/prismedia/plugins/interfaces/help.py index ed8a559..c10d2c6 100644 --- a/prismedia/plugins/interfaces/help.py +++ b/prismedia/plugins/interfaces/help.py @@ -2,10 +2,11 @@ import pluginInterfaces as pi from yapsy.PluginManager import PluginManagerSingleton + class Help(pi.IInterfacePlugin): """ - The help plugin print the usage informations of prismedia’s plugins. - Use it by simply caling `prismedia help `. + The help plugin print the usage information of prismedia’s plugins. + Use it by simply calling `prismedia help `. For example `prismedia help help` bring this help. """ @@ -20,7 +21,7 @@ class Help(pi.IInterfacePlugin): for p in parameters: plugin = pluginManager.getPluginByName(p, pi.PluginTypes.ALL) if plugin is None: - # TODO: log instead to warning ? error ? + # TODO: log instead to warning? error? print("No plugin was found with name:", p) continue @@ -42,7 +43,7 @@ class Help(pi.IInterfacePlugin): for plugin in pluginManager.getPluginsOfCategory(pi.PluginTypes.CONSUMER): print("\t" + plugin.name) - # Print a line breack between each plugin help. + # Print a line break between each plugin help. print() return False diff --git a/prismedia/plugins/platforms/peertube.py b/prismedia/plugins/platforms/peertube.py index 7d86ba6..fa56650 100644 --- a/prismedia/plugins/platforms/peertube.py +++ b/prismedia/plugins/platforms/peertube.py @@ -12,7 +12,7 @@ import logging import sys import datetime import pytz -from os.path import splitext, basename, abspath # TODO: remove me, we already import `os` or at least choose one +from os.path import splitext, basename, abspath # TODO: remove me, we already import `os` or at least choose one from tzlocal import get_localzone from configparser import RawConfigParser @@ -23,10 +23,11 @@ from clint.textui.progress import Bar as ProgressBar logger = logging.getLogger('Prismedia') + class Peertube(pi.IPlatformPlugin): """ Plugin to upload to the Peertube platform. - The connetions files should be set as # TODO: EXPLAIN HOW TO SETUP THE SECRET FILES + The connections files should be set as # TODO: EXPLAIN HOW TO SETUP THE SECRET FILES - `publish-at-peertube=DATE`: overrides the default `publish-at=DATE` for this platform. # TODO: Maybe we will use a [] section on the config fire, explain that. """ SECRETS_FILE = 'peertube_secret' @@ -74,7 +75,7 @@ class Peertube(pi.IPlatformPlugin): def __init__(self): self.channelCreate = False - self.name = "peertube" # TODO: find if it is possible to get the plugin’s name from inside the plugin + self.name = "peertube" # TODO: find if it is possible to get the plugin’s name from inside the plugin self.oauth = {} self.secret = {} @@ -105,24 +106,20 @@ class Peertube(pi.IPlatformPlugin): client_secret=str(self.secret.get('peertube', 'client_secret')) ) - def convert_peertube_date(self, date): date = datetime.datetime.strptime(date, '%Y-%m-%dT%H:%M:%S') tz = get_localzone() tz = pytz.timezone(str(tz)) return tz.localize(date).isoformat() - def get_default_channel(self, user_info): return user_info['videoChannels'][0]['id'] - def get_channel_by_name(self, user_info, video): for channel in user_info["videoChannels"]: if channel['displayName'] == video.platform[self.name].channel: return channel['id'] - def create_channel(self, instance_url, video): template = ('Peertube: Channel %s does not exist, creating it.') logger.info(template % (video.platform[self.name].channel)) @@ -139,8 +136,8 @@ class Peertube(pi.IPlatformPlugin): } try: response = self.oauth.post(instance_url + "/api/v1/video-channels/", - data=data.encode('utf-8'), - headers=headers) + data=data.encode('utf-8'), + headers=headers) except Exception as e: logger.error("Peertube: " + utils.get_exception_string(e)) @@ -151,25 +148,24 @@ class Peertube(pi.IPlatformPlugin): return jresponse['id'] if response.status_code == 409: logger.critical('Peertube: It seems there is a conflict with an existing channel named ' - + channel_name + '.' - ' Please beware Peertube internal name is compiled from 20 firsts characters of channel name.' - ' Also note that channel name are not case sensitive (no uppercase nor accent)' - ' Please check your channel name and retry.') + + channel_name + '.' + ' Please beware Peertube internal name is compiled from 20 firsts characters of channel name.' + ' Also note that channel name are not case sensitive (no uppercase nor accent)' + ' Please check your channel name and retry.') exit(1) else: logger.critical(('Peertube: Creating channel failed with an unexpected response: ' - '%s') % response) + '%s') % response) exit(1) - - def get_default_playlist(user_info): + def get_default_playlist(self, user_info): return user_info['videoChannels'][0]['id'] - - def get_playlist_by_name(instance_url, username, video): + def get_playlist_by_name(self, instance_url, username, video): start = 0 user_playlists = json.loads(self.oauth.get( - instance_url + "/api/v1/accounts/"+username+"/video-playlists?start="+str(start)+"&count=100").content) + instance_url + "/api/v1/accounts/" + username + "/video-playlists?start=" + str( + start) + "&count=100").content) total = user_playlists["total"] data = user_playlists["data"] # We need to iterate on pagination as peertube returns max 100 playlists (see #41) @@ -179,11 +175,11 @@ class Peertube(pi.IPlatformPlugin): return playlist['id'] start = start + 100 user_playlists = json.loads(self.oauth.get( - instance_url + "/api/v1/accounts/"+username+"/video-playlists?start="+str(start)+"&count=100").content) + instance_url + "/api/v1/accounts/" + username + "/video-playlists?start=" + str( + start) + "&count=100").content) data = user_playlists["data"] - - def create_playlist(instance_url, video, channel): + def create_playlist(self, instance_url, video, channel): template = ('Peertube: Playlist %s does not exist, creating it.') logger.info(template % (str(video.playlistName))) # We use files for form-data Content @@ -197,7 +193,7 @@ class Peertube(pi.IPlatformPlugin): try: response = self.oauth.post(instance_url + "/api/v1/video-playlists/", - files=files) + files=files) except Exception as e: logger.error("Peertube: " + utils.get_exception_string(e)) @@ -208,11 +204,10 @@ class Peertube(pi.IPlatformPlugin): return jresponse['id'] else: logger.critical(('Peertube: Creating the playlist failed with an unexpected response: ' - '%s') % response) + '%s') % response) exit(1) - - def set_playlist(instance_url, video_id, playlist_id): + def set_playlist(self, instance_url, video_id, playlist_id): logger.info('Peertube: add video to playlist.') data = '{"videoId":"' + str(video_id) + '"}' @@ -221,9 +216,9 @@ class Peertube(pi.IPlatformPlugin): } try: - response = self.oauth.post(instance_url + "/api/v1/video-playlists/"+str(playlist_id)+"/videos", - data=data, - headers=headers) + response = self.oauth.post(instance_url + "/api/v1/video-playlists/" + str(playlist_id) + "/videos", + data=data, + headers=headers) except Exception as e: logger.error("Peertube: " + utils.get_exception_string(e)) @@ -232,19 +227,18 @@ class Peertube(pi.IPlatformPlugin): logger.info('Peertube: Video is successfully added to the playlist.') else: logger.critical(('Peertube: Configuring the playlist failed with an unexpected response: ' - '%s') % response) + '%s') % response) exit(1) - def upload_video(self, video, options): - def get_userinfo(instance_url): - return json.loads(self.oauth.get(instance_url + "/api/v1/users/me").content) + def get_userinfo(base_url): + return json.loads(self.oauth.get(base_url + "/api/v1/users/me").content) - def get_file(path): + def get_file(video_path): mimetypes.init() - return (basename(path), open(abspath(path), 'rb'), - mimetypes.types_map[splitext(path)[1]]) + return (basename(video_path), open(abspath(video_path), 'rb'), + mimetypes.types_map[splitext(video_path)[1]]) path = video.path instance_url = str(self.secret.get('peertube', 'peertube_url')).rstrip('/') @@ -257,7 +251,7 @@ class Peertube(pi.IPlatformPlugin): # https://github.com/requests/toolbelt/issues/205 fields = [ ("name", video.name), - ("licence", "1"), # TODO: get licence from video object + ("licence", "1"), # TODO: get licence from video object ("description", video.description), ("category", str(self.CATEGORY[video.category])), ("language", str(self.LANGUAGE[video.language])), @@ -274,7 +268,8 @@ class Peertube(pi.IPlatformPlugin): continue # Tag more than 30 chars crashes Peertube, so skip tags if len(strtag) >= 30: - logger.warning("Peertube: Sorry, Peertube does not support tag with more than 30 characters, please reduce tag: " + strtag) + logger.warning( + "Peertube: Sorry, Peertube does not support tag with more than 30 characters, please reduce tag: " + strtag) logger.warning("Peertube: Meanwhile, this tag will be skipped") continue # Peertube supports only 5 tags at the moment @@ -306,12 +301,13 @@ class Peertube(pi.IPlatformPlugin): fields.append(("thumbnailfile", get_file(video.thumbnail))) fields.append(("previewfile", get_file(video.thumbnail))) - if hasattr(video.platform[self.name], "channel"): # TODO: Should always be present + if hasattr(video.platform[self.name], "channel"): # TODO: Should always be present channel_id = self.get_channel_by_name(user_info, video) if not channel_id and self.channelCreate: channel_id = self.create_channel(instance_url, video) elif not channel_id: - logger.warning("Peertube: Channel `" + video.platform[self.name].channel + "` is unknown, using default channel.") # TODO: debate if we should have the same message and behavior than playlist : "does not exist, please set --channelCreate" + logger.warning("Peertube: Channel `" + video.platform[ + self.name].channel + "` is unknown, using default channel.") # TODO: debate if we should have the same message and behavior than playlist: "does not exist, please set --channelCreate" channel_id = self.get_default_channel(user_info) else: channel_id = self.get_default_channel(user_info) @@ -323,8 +319,9 @@ class Peertube(pi.IPlatformPlugin): if not playlist_id and video.playlistCreate: playlist_id = create_playlist(instance_url, video, channel_id) elif not playlist_id: - logger.critical("Peertube: Playlist `" + video.playlistName + "` does not exist, please set --playlistCreate" - " if you want to create it") + logger.critical( + "Peertube: Playlist `" + video.playlistName + "` does not exist, please set --playlistCreate" + " if you want to create it") exit(1) encoder = MultipartEncoder(fields) @@ -338,8 +335,8 @@ class Peertube(pi.IPlatformPlugin): 'Content-Type': multipart_data.content_type } response = self.oauth.post(instance_url + "/api/v1/videos/upload", - data=multipart_data, - headers=headers) + data=multipart_data, + headers=headers) if response is not None: if response.status_code == 200: @@ -357,10 +354,9 @@ class Peertube(pi.IPlatformPlugin): set_playlist(instance_url, video_id, playlist_id) else: logger.critical(('Peertube: The upload failed with an unexpected response: ' - '%s') % response) + '%s') % response) exit(1) - # upload_finished = False # def create_callback(encoder, progress_type): # upload_size_MB = encoder.len * (1 / (1024 * 1024)) @@ -396,16 +392,14 @@ class Peertube(pi.IPlatformPlugin): # # return callback - - def hearthbeat(self): + def heartbeat(self): """ If needed for your platform, use a bit of the api so the platform is aware the keys are still in use. """ - print("Hearthbeat for peertube (nothing to do)") + print("heartbeat for peertube (nothing to do)") pass - - def upload(self, video, options): # def run(options): + def upload(self, video, options): logger.info('Peertube: Uploading video...') self.upload_video(video, options) diff --git a/prismedia/plugins/platforms/youtube.py b/prismedia/plugins/platforms/youtube.py index 04ff19b..b060eae 100644 --- a/prismedia/plugins/platforms/youtube.py +++ b/prismedia/plugins/platforms/youtube.py @@ -373,7 +373,7 @@ def resumable_upload(request, resource, method, options): time.sleep(sleep_seconds) -def hearthbeat(): +def heartbeat(): """Use the minimums credits possibles of the API so google does not readuce to 0 the allowed credits. This apparently happens after 90 days without any usage of credits. For more info see the official documentations: @@ -384,7 +384,7 @@ def hearthbeat(): try: get_playlist_by_name(youtube, "Foo") except HttpError as e: - logger.error('Youtube: An HTTP error %d occurred on hearthbeat:\n%s' % + logger.error('Youtube: An HTTP error %d occurred on heartbeat:\n%s' % (e.resp.status, e.content)) diff --git a/prismedia/upload.py b/prismedia/upload.py index fa7f923..fdf0416 100755 --- a/prismedia/upload.py +++ b/prismedia/upload.py @@ -7,7 +7,7 @@ prismedia - tool to upload videos to Peertube and Youtube Usage: prismedia --file= [options] prismedia -f --tags=STRING [options] - prismedia --hearthbeat + prismedia --heartbeat prismedia -h | --help prismedia --version @@ -52,7 +52,7 @@ Options: Only relevant if --playlist is set. --progress=STRING Set the progress bar view, one of percentage, bigFile (MB), accurate (KB). - --hearthbeat Use some credits to show some activity for you apikey so the platform know it is used and would not put your quota to 0 (only Youtube currently) + --heartbeat Use some credits to show some activity for you apikey so the platform know it is used and would not put your quota to 0 (only Youtube currently) -h --help Show this help. --version Show version. @@ -109,7 +109,6 @@ if sys.version_info[0] < 3: import os import datetime import logging -logger = logging.getLogger('Prismedia') from docopt import docopt @@ -117,6 +116,8 @@ from . import yt_upload from . import pt_upload from . import utils +logger = logging.getLogger('Prismedia') + try: # noinspection PyUnresolvedReferences from schema import Schema, And, Or, Optional, SchemaError, Hook, Use @@ -289,15 +290,15 @@ def main(): Optional('--playlist'): Or(None, str), Optional('--playlistCreate'): bool, Optional('--progress'): Or(None, And(str, validateProgress, error="Sorry, progress visualisation not supported")), - '--hearthbeat': bool, + '--heartbeat': bool, '--help': bool, '--version': bool, # This allow to return all other options for further use: https://github.com/keleshev/schema#extra-keys object: object }) - if options.get('--hearthbeat'): - yt_upload.hearthbeat() + if options.get('--heartbeat'): + yt_upload.heartbeat() exit(0) # We need to validate early options first as withNFO and logs options should be prioritized diff --git a/prismedia/video.py b/prismedia/video.py index ce26917..b60de92 100644 --- a/prismedia/video.py +++ b/prismedia/video.py @@ -1,5 +1,6 @@ from os.path import dirname, splitext, basename, isfile, normpath, expanduser + class Platform(object): """ Store data representing a Platform. @@ -13,6 +14,7 @@ class Platform(object): def __repr__(self): return str(self.__dict__) + # TODO: Add container for `with-*` and a `isValid` method to check that all `with-*` options are present # TODO: We need some list (using enum?) for the commons licences, language, privacy, categories options class Video(object): @@ -59,13 +61,13 @@ class Video(object): elif isfile(path): self._path = path else: - # TODO: log instead to debug ? info ? + # TODO: log instead to debug? info? print("The path `" + value + "` does not point to a video") self._path = "" @property def thumbnail(self): - if not self._thumbnail is None: + if self._thumbnail is not None: return self._thumbnail else: result = None @@ -89,7 +91,7 @@ class Video(object): elif isfile(video_directory + video_file + ".jpeg"): result = video_directory + video_file + ".jpeg" - # TODO: move to caller. Logging the output is its resporsability + # TODO: move to caller. Logging the output is its responsibility # Display some info after research # if not result: # logger.debug("No thumbnail has been found, continuing") @@ -104,7 +106,7 @@ class Video(object): @property def name(self): - if not self._name is None: + if self._name is not None: return self._name else: return splitext(basename(self.path))[0]