mirror of
https://git.lecygnenoir.info/LecygneNoir/prismedia.git
synced 2025-10-06 10:39:58 +02:00
Merge branch 'feature/logs_improvement' into develop
This commit is contained in:
commit
f8ca4b093a
5 changed files with 202 additions and 81 deletions
32
README.md
32
README.md
|
@ -122,9 +122,8 @@ Use --help to get all available options:
|
|||
|
||||
```
|
||||
Options:
|
||||
-f, --file=STRING Path to the video file to upload in mp4
|
||||
-f, --file=STRING Path to the video file to upload in mp4. This is the only mandatory option.
|
||||
--name=NAME Name of the video to upload. (default to video filename)
|
||||
--debug Trigger some debug information like options used (default: no)
|
||||
-d, --description=STRING Description of the video. (default: default description)
|
||||
-t, --tags=STRING Tags for the video. comma separated.
|
||||
WARN: tags with punctuation (!, ', ", ?, ...)
|
||||
|
@ -160,6 +159,34 @@ Options:
|
|||
-h --help Show this help.
|
||||
--version Show version.
|
||||
|
||||
Logging options
|
||||
-q --quiet Suppress any log except Critical (alias for --log=critical).
|
||||
--log=STRING Log level, between debug, info, warning, error, critical. Ignored if --quiet is set (default to info)
|
||||
-u --url-only Display generated URL after upload directly on stdout, implies --quiet
|
||||
--batch Display generated URL after upload with platform information for easier parsing. Implies --quiet
|
||||
Be careful --batch and --url-only are mutually exclusives.
|
||||
--debug (Deprecated) Alias for --log=debug. Ignored if --log is set
|
||||
|
||||
Strict options:
|
||||
Strict options allow you to force some option to be present when uploading a video. It's useful to be sure you do not
|
||||
forget something when uploading a video, for example if you use multiples NFO. You may force the presence of description,
|
||||
tags, thumbnail, ...
|
||||
All strict option are optionals and are provided only to avoid errors when uploading :-)
|
||||
All strict options can be specified in NFO directly, the only strict option mandatory on cli is --withNFO
|
||||
All strict options are off by default
|
||||
|
||||
--withNFO Prevent the upload without a NFO, either specified via cli or found in the directory
|
||||
--withThumbnail Prevent the upload without a thumbnail
|
||||
--withName Prevent the upload if no name are found
|
||||
--withDescription Prevent the upload without description
|
||||
--withTags Prevent the upload without tags
|
||||
--withPlaylist Prevent the upload if no playlist
|
||||
--withPublishAt Prevent the upload if no schedule
|
||||
--withPlatform Prevent the upload if at least one platform is not specified
|
||||
--withCategory Prevent the upload if no category
|
||||
--withLanguage Prevent upload if no language
|
||||
--withChannel Prevent upload if no channel
|
||||
|
||||
Categories:
|
||||
Category is the type of video you upload. Default is films.
|
||||
Here are available categories from Peertube and Youtube:
|
||||
|
@ -174,6 +201,7 @@ Languages:
|
|||
Here are available languages from Peertube and Youtube:
|
||||
Arabic, English, French, German, Hindi, Italian,
|
||||
Japanese, Korean, Mandarin, Portuguese, Punjabi, Russian, Spanish
|
||||
|
||||
```
|
||||
|
||||
## Enhanced use of NFO
|
||||
|
|
|
@ -5,6 +5,7 @@ import os
|
|||
import mimetypes
|
||||
import json
|
||||
import logging
|
||||
import sys
|
||||
import datetime
|
||||
import pytz
|
||||
from os.path import splitext, basename, abspath
|
||||
|
@ -16,6 +17,7 @@ from oauthlib.oauth2 import LegacyApplicationClient
|
|||
from requests_toolbelt.multipart.encoder import MultipartEncoder
|
||||
|
||||
from . import utils
|
||||
logger = logging.getLogger('Prismedia')
|
||||
|
||||
PEERTUBE_SECRETS_FILE = 'peertube_secret'
|
||||
PEERTUBE_PRIVACY = {
|
||||
|
@ -43,10 +45,10 @@ def get_authenticated_service(secret):
|
|||
)
|
||||
except Exception as e:
|
||||
if hasattr(e, 'message'):
|
||||
logging.error("Peertube: Error: " + str(e.message))
|
||||
logger.critical("Peertube: " + str(e.message))
|
||||
exit(1)
|
||||
else:
|
||||
logging.error("Peertube: Error: " + str(e))
|
||||
logger.critical("Peertube: " + str(e))
|
||||
exit(1)
|
||||
return oauth
|
||||
|
||||
|
@ -63,7 +65,7 @@ def get_channel_by_name(user_info, options):
|
|||
|
||||
def create_channel(oauth, url, options):
|
||||
template = ('Peertube: Channel %s does not exist, creating it.')
|
||||
logging.info(template % (str(options.get('--channel'))))
|
||||
logger.info(template % (str(options.get('--channel'))))
|
||||
channel_name = utils.cleanString(str(options.get('--channel')))
|
||||
# Peertube allows 20 chars max for channel name
|
||||
channel_name = channel_name[:19]
|
||||
|
@ -81,23 +83,23 @@ def create_channel(oauth, url, options):
|
|||
headers=headers)
|
||||
except Exception as e:
|
||||
if hasattr(e, 'message'):
|
||||
logging.error("Error: " + str(e.message))
|
||||
logger.error("Peertube: " + str(e.message))
|
||||
else:
|
||||
logging.error("Error: " + str(e))
|
||||
logger.error("Peertube: " + str(e))
|
||||
if response is not None:
|
||||
if response.status_code == 200:
|
||||
jresponse = response.json()
|
||||
jresponse = jresponse['videoChannel']
|
||||
return jresponse['id']
|
||||
if response.status_code == 409:
|
||||
logging.error('Peertube: Error: It seems there is a conflict with an existing channel named '
|
||||
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.')
|
||||
exit(1)
|
||||
else:
|
||||
logging.error(('Peertube: Creating channel failed with an unexpected response: '
|
||||
logger.critical(('Peertube: Creating channel failed with an unexpected response: '
|
||||
'%s') % response)
|
||||
exit(1)
|
||||
|
||||
|
@ -114,7 +116,7 @@ def get_playlist_by_name(user_playlists, options):
|
|||
|
||||
def create_playlist(oauth, url, options, channel):
|
||||
template = ('Peertube: Playlist %s does not exist, creating it.')
|
||||
logging.info(template % (str(options.get('--playlist'))))
|
||||
logger.info(template % (str(options.get('--playlist'))))
|
||||
# We use files for form-data Content
|
||||
# see https://requests.readthedocs.io/en/latest/user/quickstart/#post-a-multipart-encoded-file
|
||||
# None is used to mute "filename" field
|
||||
|
@ -128,22 +130,22 @@ def create_playlist(oauth, url, options, channel):
|
|||
files=files)
|
||||
except Exception as e:
|
||||
if hasattr(e, 'message'):
|
||||
logging.error("Error: " + str(e.message))
|
||||
logger.error("Peertube: " + str(e.message))
|
||||
else:
|
||||
logging.error("Error: " + str(e))
|
||||
logger.error("Peertube: " + str(e))
|
||||
if response is not None:
|
||||
if response.status_code == 200:
|
||||
jresponse = response.json()
|
||||
jresponse = jresponse['videoPlaylist']
|
||||
return jresponse['id']
|
||||
else:
|
||||
logging.error(('Peertube: Creating the playlist failed with an unexpected response: '
|
||||
logger.critical(('Peertube: Creating the playlist failed with an unexpected response: '
|
||||
'%s') % response)
|
||||
exit(1)
|
||||
|
||||
|
||||
def set_playlist(oauth, url, video_id, playlist_id):
|
||||
logging.info('Peertube: add video to playlist.')
|
||||
logger.info('Peertube: add video to playlist.')
|
||||
data = '{"videoId":"' + str(video_id) + '"}'
|
||||
|
||||
headers = {
|
||||
|
@ -155,14 +157,14 @@ def set_playlist(oauth, url, video_id, playlist_id):
|
|||
headers=headers)
|
||||
except Exception as e:
|
||||
if hasattr(e, 'message'):
|
||||
logging.error("Error: " + str(e.message))
|
||||
logger.error("Peertube: " + str(e.message))
|
||||
else:
|
||||
logging.error("Error: " + str(e))
|
||||
logger.error("Peertube: " + str(e))
|
||||
if response is not None:
|
||||
if response.status_code == 200:
|
||||
logging.info('Peertube: Video is successfully added to the playlist.')
|
||||
logger.info('Peertube: Video is successfully added to the playlist.')
|
||||
else:
|
||||
logging.error(('Peertube: Configuring the playlist failed with an unexpected response: '
|
||||
logger.critical(('Peertube: Configuring the playlist failed with an unexpected response: '
|
||||
'%s') % response)
|
||||
exit(1)
|
||||
|
||||
|
@ -205,8 +207,9 @@ def upload_video(oauth, secret, options):
|
|||
continue
|
||||
# Tag more than 30 chars crashes Peertube, so exit and check tags
|
||||
if len(strtag) >= 30:
|
||||
logging.warning("Peertube: Sorry, Peertube does not support tag with more than 30 characters, please reduce tag: " + strtag)
|
||||
exit(1)
|
||||
logger.error("Peertube: Sorry, Peertube does not support tag with more than 30 characters, please reduce tag: " + strtag)
|
||||
logger.error("Peertube: Meanwhile, this tag will be skipped")
|
||||
continue
|
||||
fields.append(("tags[]", strtag))
|
||||
|
||||
if options.get('--category'):
|
||||
|
@ -256,7 +259,7 @@ def upload_video(oauth, secret, options):
|
|||
if not channel_id and options.get('--channelCreate'):
|
||||
channel_id = create_channel(oauth, url, options)
|
||||
elif not channel_id:
|
||||
logging.warning("Channel `" + options.get('--channel') + "` is unknown, using default channel.")
|
||||
logger.warning("Peertube: Channel `" + options.get('--channel') + "` is unknown, using default channel.")
|
||||
channel_id = get_default_channel(user_info)
|
||||
else:
|
||||
channel_id = get_default_channel(user_info)
|
||||
|
@ -268,10 +271,14 @@ def upload_video(oauth, secret, options):
|
|||
if not playlist_id and options.get('--playlistCreate'):
|
||||
playlist_id = create_playlist(oauth, url, options, channel_id)
|
||||
elif not playlist_id:
|
||||
logging.warning("Playlist `" + options.get('--playlist') + "` does not exist, please set --playlistCreate"
|
||||
logger.critical("Peertube: Playlist `" + options.get('--playlist') + "` does not exist, please set --playlistCreate"
|
||||
" if you want to create it")
|
||||
exit(1)
|
||||
|
||||
logger_stdout = None
|
||||
if options.get('--url-only') or options.get('--batch'):
|
||||
logger_stdout = logging.getLogger('stdoutlogs')
|
||||
|
||||
multipart_data = MultipartEncoder(fields)
|
||||
|
||||
headers = {
|
||||
|
@ -286,14 +293,19 @@ def upload_video(oauth, secret, options):
|
|||
jresponse = jresponse['video']
|
||||
uuid = jresponse['uuid']
|
||||
video_id = str(jresponse['id'])
|
||||
logging.info('Peertube : Video was successfully uploaded.')
|
||||
logger.info('Peertube : Video was successfully uploaded.')
|
||||
template = 'Peertube: Watch it at %s/videos/watch/%s.'
|
||||
logging.info(template % (url, uuid))
|
||||
logger.info(template % (url, uuid))
|
||||
template_stdout = '%s/videos/watch/%s'
|
||||
if options.get('--url-only'):
|
||||
logger_stdout.info(template_stdout % (url, uuid))
|
||||
elif options.get('--batch'):
|
||||
logger_stdout.info("Peertube: " + template_stdout % (url, uuid))
|
||||
# Upload is successful we may set playlist
|
||||
if options.get('--playlist'):
|
||||
set_playlist(oauth, url, video_id, playlist_id)
|
||||
else:
|
||||
logging.error(('Peertube: The upload failed with an unexpected response: '
|
||||
logger.critical(('Peertube: The upload failed with an unexpected response: '
|
||||
'%s') % response)
|
||||
exit(1)
|
||||
|
||||
|
@ -303,16 +315,16 @@ def run(options):
|
|||
try:
|
||||
secret.read(PEERTUBE_SECRETS_FILE)
|
||||
except Exception as e:
|
||||
logging.error("Peertube: Error loading " + str(PEERTUBE_SECRETS_FILE) + ": " + str(e))
|
||||
logger.critical("Peertube: Error loading " + str(PEERTUBE_SECRETS_FILE) + ": " + str(e))
|
||||
exit(1)
|
||||
insecure_transport = secret.get('peertube', 'OAUTHLIB_INSECURE_TRANSPORT')
|
||||
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = insecure_transport
|
||||
oauth = get_authenticated_service(secret)
|
||||
try:
|
||||
logging.info('Peertube: Uploading video...')
|
||||
logger.info('Peertube: Uploading video...')
|
||||
upload_video(oauth, secret, options)
|
||||
except Exception as e:
|
||||
if hasattr(e, 'message'):
|
||||
logging.error("Peertube: Error: " + str(e.message))
|
||||
logger.error("Peertube: " + str(e.message))
|
||||
else:
|
||||
logging.error("Peertube: Error: " + str(e))
|
||||
logger.error("Peertube: " + str(e))
|
||||
|
|
|
@ -11,9 +11,8 @@ Usage:
|
|||
prismedia --version
|
||||
|
||||
Options:
|
||||
-f, --file=STRING Path to the video file to upload in mp4
|
||||
-f, --file=STRING Path to the video file to upload in mp4. This is the only mandatory option.
|
||||
--name=NAME Name of the video to upload. (default to video filename)
|
||||
--debug Trigger some debug information like options used (default: no)
|
||||
-d, --description=STRING Description of the video. (default: default description)
|
||||
-t, --tags=STRING Tags for the video. comma separated.
|
||||
WARN: tags with punctuation (!, ', ", ?, ...)
|
||||
|
@ -49,6 +48,14 @@ Options:
|
|||
-h --help Show this help.
|
||||
--version Show version.
|
||||
|
||||
Logging options
|
||||
-q --quiet Suppress any log except Critical (alias for --log=critical).
|
||||
--log=STRING Log level, between debug, info, warning, error, critical. Ignored if --quiet is set (default to info)
|
||||
-u --url-only Display generated URL after upload directly on stdout, implies --quiet
|
||||
--batch Display generated URL after upload with platform information for easier parsing. Implies --quiet
|
||||
Be careful --batch and --url-only are mutually exclusives.
|
||||
--debug (Deprecated) Alias for --log=debug. Ignored if --log is set
|
||||
|
||||
Strict options:
|
||||
Strict options allow you to force some option to be present when uploading a video. It's useful to be sure you do not
|
||||
forget something when uploading a video, for example if you use multiples NFO. You may force the presence of description,
|
||||
|
@ -92,7 +99,13 @@ if sys.version_info[0] < 3:
|
|||
import os
|
||||
import datetime
|
||||
import logging
|
||||
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)
|
||||
logger = logging.getLogger('Prismedia')
|
||||
logger.setLevel(logging.INFO)
|
||||
ch = logging.StreamHandler()
|
||||
ch.setLevel(logging.INFO)
|
||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s: %(message)s')
|
||||
ch.setFormatter(formatter)
|
||||
logger.addHandler(ch)
|
||||
|
||||
from docopt import docopt
|
||||
|
||||
|
@ -102,9 +115,9 @@ from . import utils
|
|||
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences
|
||||
from schema import Schema, And, Or, Optional, SchemaError, Hook
|
||||
from schema import Schema, And, Or, Optional, SchemaError, Hook, Use
|
||||
except ImportError:
|
||||
logging.error('This program requires that the `schema` data-validation library'
|
||||
logger.critical('This program requires that the `schema` data-validation library'
|
||||
' is installed: \n'
|
||||
'see https://github.com/halst/schema\n')
|
||||
exit(1)
|
||||
|
@ -112,7 +125,7 @@ try:
|
|||
# noinspection PyUnresolvedReferences
|
||||
import magic
|
||||
except ImportError:
|
||||
logging.error('This program requires that the `python-magic` library'
|
||||
logger.critical('This program requires that the `python-magic` library'
|
||||
' is installed, NOT the Python bindings to libmagic API \n'
|
||||
'see https://github.com/ahupp/python-magic\n')
|
||||
exit(1)
|
||||
|
@ -198,19 +211,68 @@ def validateThumbnail(thumbnail):
|
|||
return False
|
||||
|
||||
|
||||
def validateLogLevel(loglevel):
|
||||
numeric_level = getattr(logging, loglevel, None)
|
||||
if not isinstance(numeric_level, int):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _optionnalOrStrict(key, scope, error):
|
||||
option = key.replace('-', '')
|
||||
option = option[0].upper() + option[1:]
|
||||
if scope["--with" + option] is True and scope[key] is None:
|
||||
logging.error("Prismedia: you have required the strict presence of " + key + " but none is found")
|
||||
logger.critical("Prismedia: you have required the strict presence of " + key + " but none is found")
|
||||
exit(1)
|
||||
return True
|
||||
|
||||
|
||||
def configureLogs(options):
|
||||
if options.get('--batch') and options.get('--url-only'):
|
||||
logger.critical("Prismedia: Please use either --batch OR --url-only, not both.")
|
||||
exit(1)
|
||||
# batch and url-only implies quiet
|
||||
if options.get('--batch') or options.get('--url-only'):
|
||||
options['--quiet'] = True
|
||||
|
||||
if options.get('--quiet'):
|
||||
# We need to set both log level in the same time
|
||||
logger.setLevel(50)
|
||||
ch.setLevel(50)
|
||||
elif options.get('--log'):
|
||||
numeric_level = getattr(logging, options["--log"], None)
|
||||
# We need to set both log level in the same time
|
||||
logger.setLevel(numeric_level)
|
||||
ch.setLevel(numeric_level)
|
||||
elif options.get('--debug'):
|
||||
logger.warning("DEPRECATION: --debug is deprecated, please use --log=debug instead")
|
||||
logger.setLevel(10)
|
||||
ch.setLevel(10)
|
||||
|
||||
|
||||
def configureStdoutLogs():
|
||||
logger_stdout = logging.getLogger('stdoutlogs')
|
||||
logger_stdout.setLevel(logging.INFO)
|
||||
ch_stdout = logging.StreamHandler(stream=sys.stdout)
|
||||
ch_stdout.setLevel(logging.INFO)
|
||||
# Default stdout logs is url only
|
||||
formatter_stdout = logging.Formatter('%(message)s')
|
||||
ch_stdout.setFormatter(formatter_stdout)
|
||||
logger_stdout.addHandler(ch_stdout)
|
||||
|
||||
def main():
|
||||
options = docopt(__doc__, version=VERSION)
|
||||
|
||||
strictoptionSchema = Schema({
|
||||
earlyoptionSchema = Schema({
|
||||
Optional('--log'): Or(None, And(
|
||||
str,
|
||||
Use(str.upper),
|
||||
validateLogLevel,
|
||||
error="Log level not recognized")
|
||||
),
|
||||
Optional('--quiet', default=False): bool,
|
||||
Optional('--debug'): bool,
|
||||
Optional('--url-only', default=False): bool,
|
||||
Optional('--batch', default=False): bool,
|
||||
Optional('--withNFO', default=False): bool,
|
||||
Optional('--withThumbnail', default=False): bool,
|
||||
Optional('--withName', default=False): bool,
|
||||
|
@ -222,7 +284,8 @@ def main():
|
|||
Optional('--withCategory', default=False): bool,
|
||||
Optional('--withLanguage', default=False): bool,
|
||||
Optional('--withChannel', default=False): bool,
|
||||
object: object # This allow to return all other options for further use: https://github.com/keleshev/schema#extra-keys
|
||||
# This allow to return all other options for further use: https://github.com/keleshev/schema#extra-keys
|
||||
object: object
|
||||
})
|
||||
|
||||
schema = Schema({
|
||||
|
@ -286,7 +349,6 @@ def main():
|
|||
validatePublish,
|
||||
error="DATE should be the form YYYY-MM-DDThh:mm:ss and has to be in the future")
|
||||
),
|
||||
Optional('--debug'): bool,
|
||||
Optional('--cca'): bool,
|
||||
Optional('--disable-comments'): bool,
|
||||
Optional('--nsfw'): bool,
|
||||
|
@ -299,22 +361,28 @@ def main():
|
|||
Optional('--playlistCreate'): bool,
|
||||
'--help': bool,
|
||||
'--version': bool,
|
||||
object: object # This allow to return all other options for further use: https://github.com/keleshev/schema#extra-keys
|
||||
# This allow to return all other options for further use: https://github.com/keleshev/schema#extra-keys
|
||||
object: object
|
||||
})
|
||||
|
||||
# We need to validate strict options first as withNFO should be validated before NFO parsing
|
||||
# We need to validate early options first as withNFO and logs options should be prioritized
|
||||
try:
|
||||
options = strictoptionSchema.validate(options)
|
||||
options = earlyoptionSchema.validate(options)
|
||||
configureLogs(options)
|
||||
except SchemaError as e:
|
||||
exit(e)
|
||||
logger.critical(e)
|
||||
exit(1)
|
||||
|
||||
if options.get('--url-only') or options.get('--batch'):
|
||||
configureStdoutLogs()
|
||||
|
||||
options = utils.parseNFO(options)
|
||||
|
||||
# Once NFO are loaded, we need to revalidate strict options in case some were in NFO
|
||||
try:
|
||||
options = strictoptionSchema.validate(options)
|
||||
options = earlyoptionSchema.validate(options)
|
||||
except SchemaError as e:
|
||||
exit(e)
|
||||
logger.critical(e)
|
||||
exit(1)
|
||||
|
||||
if not options.get('--thumbnail'):
|
||||
options = utils.searchThumbnail(options)
|
||||
|
@ -322,11 +390,11 @@ def main():
|
|||
try:
|
||||
options = schema.validate(options)
|
||||
except SchemaError as e:
|
||||
exit(e)
|
||||
logger.critical(e)
|
||||
exit(1)
|
||||
|
||||
if options.get('--debug'):
|
||||
print("Python " + sys.version)
|
||||
print(options)
|
||||
logger.debug("Python " + sys.version)
|
||||
logger.debug(options)
|
||||
|
||||
if options.get('--platform') is None or "peertube" in options.get('--platform'):
|
||||
pt_upload.run(options)
|
||||
|
@ -335,6 +403,5 @@ def main():
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import warnings
|
||||
warnings.warn("use 'python -m prismedia', not 'python -m prismedia.upload'", DeprecationWarning)
|
||||
logger.warning("DEPRECATION: use 'python -m prismedia', not 'python -m prismedia.upload'")
|
||||
main()
|
||||
|
|
|
@ -9,6 +9,8 @@ from subprocess import check_call, CalledProcessError, STDOUT
|
|||
import unidecode
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('Prismedia')
|
||||
|
||||
### CATEGORIES ###
|
||||
YOUTUBE_CATEGORY = {
|
||||
"music": 10,
|
||||
|
@ -123,18 +125,25 @@ def searchThumbnail(options):
|
|||
options['--thumbnail'] = video_directory + video_file + ".jpg"
|
||||
elif isfile(video_directory + video_file + ".jpeg"):
|
||||
options['--thumbnail'] = video_directory + video_file + ".jpeg"
|
||||
|
||||
# Display some info after research
|
||||
if not options.get('--thumbnail'):
|
||||
logger.debug("No thumbnail has been found, continuing")
|
||||
else:
|
||||
logger.info("Using " + options.get('--thumbnail') + "as thumbnail")
|
||||
|
||||
return options
|
||||
|
||||
|
||||
# return the nfo as a RawConfigParser object
|
||||
def loadNFO(filename):
|
||||
try:
|
||||
logging.info("Loading " + filename + " as NFO")
|
||||
logger.info("Loading " + filename + " as NFO")
|
||||
nfo = RawConfigParser()
|
||||
nfo.read(filename, encoding='utf-8')
|
||||
return nfo
|
||||
except Exception as e:
|
||||
logging.error("Problem loading NFO file " + filename + ": " + str(e))
|
||||
logger.critical("Problem loading NFO file " + filename + ": " + str(e))
|
||||
exit(1)
|
||||
return False
|
||||
|
||||
|
@ -168,7 +177,7 @@ def parseNFO(options):
|
|||
if isfile(options.get('--nfo')):
|
||||
nfo_cli = loadNFO(options.get('--nfo'))
|
||||
else:
|
||||
logging.error("Given NFO file does not exist, please check your path.")
|
||||
logger.critical("Given NFO file does not exist, please check your path.")
|
||||
exit(1)
|
||||
|
||||
# If there is no NFO and strict option is enabled, then stop there
|
||||
|
@ -178,7 +187,7 @@ def parseNFO(options):
|
|||
not isinstance(nfo_videoname, RawConfigParser) and \
|
||||
not isinstance(nfo_directory, RawConfigParser) and \
|
||||
not isinstance(nfo_txt, RawConfigParser):
|
||||
logging.error("Prismedia: you have required the strict presence of NFO but none is found, please use a NFO.")
|
||||
logger.critical("You have required the strict presence of NFO but none is found, please use a NFO.")
|
||||
exit(1)
|
||||
|
||||
# We need to load NFO in this exact order to keep the priorities
|
||||
|
@ -198,7 +207,7 @@ def parseNFO(options):
|
|||
except NoOptionError:
|
||||
continue
|
||||
except NoSectionError:
|
||||
logging.error("Prismedia: " + nfo + " misses section [video], please check syntax of your NFO.")
|
||||
logger.critical(nfo + " misses section [video], please check syntax of your NFO.")
|
||||
exit(1)
|
||||
return options
|
||||
|
||||
|
|
|
@ -23,9 +23,7 @@ from google_auth_oauthlib.flow import InstalledAppFlow
|
|||
|
||||
|
||||
from . import utils
|
||||
|
||||
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)
|
||||
|
||||
logger = logging.getLogger('Prismedia')
|
||||
|
||||
# Explicitly tell the underlying HTTP transport library not to retry, since
|
||||
# we are handling retry logic ourselves.
|
||||
|
@ -87,7 +85,7 @@ def check_authenticated_scopes():
|
|||
credential_params = json.load(f)
|
||||
# Check if all scopes are present
|
||||
if credential_params["_scopes"] != SCOPES:
|
||||
logging.warning("Youtube: Credentials are obsolete, need to re-authenticate.")
|
||||
logger.warning("Youtube: Credentials are obsolete, need to re-authenticate.")
|
||||
os.remove(CREDENTIALS_PATH)
|
||||
|
||||
|
||||
|
@ -144,8 +142,8 @@ def initialize_upload(youtube, options):
|
|||
if not playlist_id and options.get('--playlistCreate'):
|
||||
playlist_id = create_playlist(youtube, options.get('--playlist'))
|
||||
elif not playlist_id:
|
||||
logging.warning("Youtube: Playlist `" + options.get('--playlist') + "` is unknown.")
|
||||
logging.warning("If you want to create it, set the --playlistCreate option.")
|
||||
logger.warning("Youtube: Playlist `" + options.get('--playlist') + "` is unknown.")
|
||||
logger.warning("Youtube: If you want to create it, set the --playlistCreate option.")
|
||||
playlist_id = ""
|
||||
else:
|
||||
playlist_id = ""
|
||||
|
@ -156,7 +154,7 @@ def initialize_upload(youtube, options):
|
|||
body=body,
|
||||
media_body=MediaFileUpload(path, chunksize=-1, resumable=True)
|
||||
)
|
||||
video_id = resumable_upload(insert_request, 'video', 'insert')
|
||||
video_id = resumable_upload(insert_request, 'video', 'insert', options)
|
||||
|
||||
# If we get a video_id, upload is successful and we are able to set thumbnail
|
||||
if video_id and options.get('--thumbnail'):
|
||||
|
@ -179,8 +177,8 @@ def get_playlist_by_name(youtube, playlist_name):
|
|||
|
||||
|
||||
def create_playlist(youtube, playlist_name):
|
||||
template = ('Youtube: Playlist %s does not exist, creating it.')
|
||||
logging.info(template % (str(playlist_name)))
|
||||
template = 'Youtube: Playlist %s does not exist, creating it.'
|
||||
logger.info(template % (str(playlist_name)))
|
||||
resources = build_resource({'snippet.title': playlist_name,
|
||||
'snippet.description': '',
|
||||
'status.privacyStatus': 'public'})
|
||||
|
@ -244,7 +242,7 @@ def set_thumbnail(youtube, media_file, **kwargs):
|
|||
|
||||
|
||||
def set_playlist(youtube, playlist_id, video_id):
|
||||
logging.info('Youtube: Configuring playlist...')
|
||||
logger.info('Youtube: Configuring playlist...')
|
||||
resource = build_resource({'snippet.playlistId': playlist_id,
|
||||
'snippet.resourceId.kind': 'youtube#video',
|
||||
'snippet.resourceId.videoId': video_id,
|
||||
|
@ -257,37 +255,45 @@ def set_playlist(youtube, playlist_id, video_id):
|
|||
).execute()
|
||||
except Exception as e:
|
||||
if hasattr(e, 'message'):
|
||||
logging.error("Youtube: Error: " + str(e.message))
|
||||
logger.critical("Youtube: " + str(e.message))
|
||||
exit(1)
|
||||
else:
|
||||
logging.error("Youtube: Error: " + str(e))
|
||||
logger.critical("Youtube: " + str(e))
|
||||
exit(1)
|
||||
logging.info('Youtube: Video is correctly added to the playlist.')
|
||||
logger.info('Youtube: Video is correctly added to the playlist.')
|
||||
|
||||
|
||||
# This method implements an exponential backoff strategy to resume a
|
||||
# failed upload.
|
||||
def resumable_upload(request, resource, method):
|
||||
def resumable_upload(request, resource, method, options):
|
||||
response = None
|
||||
error = None
|
||||
retry = 0
|
||||
logger_stdout = None
|
||||
if options.get('--url-only') or options.get('--batch'):
|
||||
logger_stdout = logging.getLogger('stdoutlogs')
|
||||
while response is None:
|
||||
try:
|
||||
template = 'Youtube: Uploading %s...'
|
||||
logging.info(template % resource)
|
||||
logger.info(template % resource)
|
||||
status, response = request.next_chunk()
|
||||
if response is not None:
|
||||
if method == 'insert' and 'id' in response:
|
||||
logging.info('Youtube : Video was successfully uploaded.')
|
||||
logger.info('Youtube : Video was successfully uploaded.')
|
||||
template = 'Youtube: Watch it at https://youtu.be/%s (post-encoding could take some time)'
|
||||
logging.info(template % response['id'])
|
||||
logger.info(template % response['id'])
|
||||
template_stdout = 'https://youtu.be/%s'
|
||||
if options.get('--url-only'):
|
||||
logger_stdout.info(template_stdout % response['id'])
|
||||
elif options.get('--batch'):
|
||||
logger_stdout.info("Youtube: " + template_stdout % response['id'])
|
||||
return response['id']
|
||||
elif method != 'insert' or "id" not in response:
|
||||
logging.info('Youtube: Thumbnail was successfully set.')
|
||||
logger.info('Youtube: Thumbnail was successfully set.')
|
||||
else:
|
||||
template = ('Youtube : The upload failed with an '
|
||||
'unexpected response: %s')
|
||||
logging.error(template % response)
|
||||
logger.critical(template % response)
|
||||
exit(1)
|
||||
except HttpError as e:
|
||||
if e.resp.status in RETRIABLE_STATUS_CODES:
|
||||
|
@ -299,15 +305,14 @@ def resumable_upload(request, resource, method):
|
|||
error = 'Youtube : A retriable error occurred: %s' % e
|
||||
|
||||
if error is not None:
|
||||
logging.warning(error)
|
||||
logger.warning(error)
|
||||
retry += 1
|
||||
if retry > MAX_RETRIES:
|
||||
logging.error('Youtube : No longer attempting to retry.')
|
||||
exit(1)
|
||||
logger.error('Youtube : No longer attempting to retry.')
|
||||
|
||||
max_sleep = 2 ** retry
|
||||
sleep_seconds = random.random() * max_sleep
|
||||
logging.warning('Youtube : Sleeping %f seconds and then retrying...'
|
||||
logger.warning('Youtube : Sleeping %f seconds and then retrying...'
|
||||
% sleep_seconds)
|
||||
time.sleep(sleep_seconds)
|
||||
|
||||
|
@ -317,5 +322,5 @@ def run(options):
|
|||
try:
|
||||
initialize_upload(youtube, options)
|
||||
except HttpError as e:
|
||||
logging.error('Youtube : An HTTP error %d occurred:\n%s' % (e.resp.status,
|
||||
logger.error('Youtube : An HTTP error %d occurred:\n%s' % (e.resp.status,
|
||||
e.content))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue