Browse Source

Merge branch 'feature/thumbnail' into develop

develop
LecygneNoir 5 years ago
parent
commit
dea06c6e8f
6 changed files with 95 additions and 23 deletions
  1. +12
    -3
      README.md
  2. +14
    -12
      lib/pt_upload.py
  3. +26
    -1
      lib/utils.py
  4. +27
    -7
      lib/yt_upload.py
  5. +1
    -0
      nfo_example.txt
  6. +15
    -0
      prismedia_upload.py

+ 12
- 3
README.md View File

@ -69,6 +69,12 @@ Specify description and tags:
./prismedia_upload.py --file="yourvideo.mp4" -d "My supa description" -t "tag1,tag2,foo" ./prismedia_upload.py --file="yourvideo.mp4" -d "My supa description" -t "tag1,tag2,foo"
``` ```
Provide a thumbnail:
```
./prismedia_upload.py --file="yourvideo.mp4" -d "Video with thumbnail" --thumbnail="/path/to/your/thumbnail.jpg"
```
Use a NFO file to specify your video options: Use a NFO file to specify your video options:
@ -111,7 +117,10 @@ Options:
--publishAt=DATE Publish the video at the given DATE using local server timezone. --publishAt=DATE Publish the video at the given DATE using local server timezone.
DATE should be on the form YYYY-MM-DDThh:mm:ss eg: 2018-03-12T19:00:00 DATE should be on the form YYYY-MM-DDThh:mm:ss eg: 2018-03-12T19:00:00
DATE should be in the future DATE should be in the future
For Peertube, requires the "atd", "curl" and "jq" utilities installed on the system
For Peertube, requires the "atd" and "curl utilities installed on the system
--thumbnail=STRING Path to a file to use as a thumbnail for the video.
Supported types are jpg and jpeg.
By default, prismedia search for an image based on video name followed by .jpg or .jpeg
-h --help Show this help. -h --help Show this help.
--version Show version. --version Show version.
@ -145,8 +154,8 @@ Languages:
- [x] enabling/disabling comment (Peertube only as Youtube API does not support it) - [x] enabling/disabling comment (Peertube only as Youtube API does not support it)
- [x] nsfw (Peertube only as Youtube API does not support it) - [x] nsfw (Peertube only as Youtube API does not support it)
- [x] set default language - [x] set default language
- [ ] thumbnail/preview (YT workflow: upload video, upload thumbnail, add thumbnail to video)
- [ ] multiple lines description (see [issue 4](https://git.lecygnenoir.info/LecygneNoir/prismedia/issues/4))
- [x] thumbnail/preview
- [x] multiple lines description (see [issue 4](https://git.lecygnenoir.info/LecygneNoir/prismedia/issues/4))
- [ ] add videos to playlist (YT & PT workflow: upload video, find playlist id, add video to playlist) - [ ] add videos to playlist (YT & PT workflow: upload video, find playlist id, add video to playlist)
- [x] Use a config file (NFO) file to retrieve videos arguments - [x] Use a config file (NFO) file to retrieve videos arguments
- [x] Allow to choose peertube or youtube upload (to resume failed upload for example) - [x] Allow to choose peertube or youtube upload (to resume failed upload for example)

+ 14
- 12
lib/pt_upload.py View File

@ -47,12 +47,11 @@ def upload_video(oauth, secret, options):
user_info = json.loads(oauth.get(url + "/api/v1/users/me").content) user_info = json.loads(oauth.get(url + "/api/v1/users/me").content)
return str(user_info["id"]) return str(user_info["id"])
def get_videofile(path):
def get_file(path):
mimetypes.init() mimetypes.init()
return (basename(path), open(abspath(path), 'rb'), return (basename(path), open(abspath(path), 'rb'),
mimetypes.types_map[splitext(path)[1]]) mimetypes.types_map[splitext(path)[1]])
path = options.get('--file')
url = secret.get('peertube', 'peertube_url') url = secret.get('peertube', 'peertube_url')
# We need to transform fields into tuple to deal with tags as # We need to transform fields into tuple to deal with tags as
@ -60,12 +59,12 @@ def upload_video(oauth, secret, options):
# https://github.com/requests/toolbelt/issues/190 and # https://github.com/requests/toolbelt/issues/190 and
# https://github.com/requests/toolbelt/issues/205 # https://github.com/requests/toolbelt/issues/205
fields = [ fields = [
("name", options.get('--name') or splitext(basename(path))[0]),
("name", options.get('--name') or splitext(basename(options.get('--file')))[0]),
("licence", "1"), ("licence", "1"),
("description", options.get('--description') or "default description"), ("description", options.get('--description') or "default description"),
("nsfw", str(int(options.get('--nsfw')) or "0")), ("nsfw", str(int(options.get('--nsfw')) or "0")),
("channelId", get_userinfo()), ("channelId", get_userinfo()),
("videofile", get_videofile(path))
("videofile", get_file(options.get('--file')))
] ]
if options.get('--tags'): if options.get('--tags'):
@ -76,7 +75,7 @@ def upload_video(oauth, secret, options):
continue continue
# Tag more than 30 chars crashes Peertube, so exit and check tags # Tag more than 30 chars crashes Peertube, so exit and check tags
if len(strtag) >= 30: if len(strtag) >= 30:
logging.warning("Sorry, Peertube does not support tag with more than 30 characters, please reduce your tag size")
logging.warning("Peertube: Sorry, Peertube does not support tag with more than 30 characters, please reduce your tag size")
exit(1) exit(1)
# If Mastodon compatibility is enabled, clean tags from special characters # If Mastodon compatibility is enabled, clean tags from special characters
if options.get('--mt'): if options.get('--mt'):
@ -105,6 +104,9 @@ def upload_video(oauth, secret, options):
else: else:
fields.append(("commentsEnabled", "1")) fields.append(("commentsEnabled", "1"))
if options.get('--thumbnail'):
fields.append(("thumbnailfile", get_file(options.get('--thumbnail'))))
multipart_data = MultipartEncoder(fields) multipart_data = MultipartEncoder(fields)
headers = { headers = {
@ -120,13 +122,13 @@ def upload_video(oauth, secret, options):
jresponse = jresponse['video'] jresponse = jresponse['video']
uuid = jresponse['uuid'] uuid = jresponse['uuid']
idvideo = str(jresponse['id']) idvideo = str(jresponse['id'])
template = ('Peertube : Video was successfully uploaded.\n'
'Watch it at %s/videos/watch/%s.')
logging.info('Peertube : Video was successfully uploaded.')
template = 'Peertube: Watch it at %s/videos/watch/%s.'
logging.info(template % (url, uuid)) logging.info(template % (url, uuid))
if options.get('--publishAt'): if options.get('--publishAt'):
utils.publishAt(str(options.get('--publishAt')), oauth, url, idvideo, secret) utils.publishAt(str(options.get('--publishAt')), oauth, url, idvideo, secret)
else: else:
logging.error(('Peertube : The upload failed with an unexpected response: '
logging.error(('Peertube: The upload failed with an unexpected response: '
'%s') % response) '%s') % response)
exit(1) exit(1)
@ -136,16 +138,16 @@ def run(options):
try: try:
secret.read(PEERTUBE_SECRETS_FILE) secret.read(PEERTUBE_SECRETS_FILE)
except Exception as e: except Exception as e:
logging.error("Error loading " + str(PEERTUBE_SECRETS_FILE) + ": " + str(e))
logging.error("Peertube: Error loading " + str(PEERTUBE_SECRETS_FILE) + ": " + str(e))
exit(1) exit(1)
insecure_transport = secret.get('peertube', 'OAUTHLIB_INSECURE_TRANSPORT') insecure_transport = secret.get('peertube', 'OAUTHLIB_INSECURE_TRANSPORT')
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = insecure_transport os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = insecure_transport
oauth = get_authenticated_service(secret) oauth = get_authenticated_service(secret)
try: try:
logging.info('Peertube : Uploading file...')
logging.info('Peertube: Uploading video...')
upload_video(oauth, secret, options) upload_video(oauth, secret, options)
except Exception as e: except Exception as e:
if hasattr(e, 'message'): if hasattr(e, 'message'):
logging.error("Error: " + str(e.message))
logging.error("Peertube: Error: " + str(e.message))
else: else:
logging.error("Error: " + str(e))
logging.error("Peertube: Error: " + str(e))

+ 26
- 1
lib/utils.py View File

@ -98,6 +98,32 @@ def getLanguage(language, platform):
return PEERTUBE_LANGUAGE[language.lower()] return PEERTUBE_LANGUAGE[language.lower()]
def remove_empty_kwargs(**kwargs):
good_kwargs = {}
if kwargs is not None:
for key, value in kwargs.iteritems():
if value:
good_kwargs[key] = value
return good_kwargs
def searchThumbnail(options):
video_directory = dirname(options.get('--file')) + "/"
# First, check for thumbnail based on videoname
if options.get('--name'):
if isfile(video_directory + options.get('--name') + ".jpg"):
options['--thumbnail'] = video_directory + options.get('--name') + ".jpg"
elif isfile(video_directory + options.get('--name') + ".jpeg"):
options['--thumbnail'] = video_directory + options.get('--name') + ".jpeg"
# Then, if we still not have thumbnail, check for thumbnail based on videofile name
if not options.get('--thumbnail'):
video_file = splitext(basename(options.get('--file')))[0]
if isfile(video_directory + video_file + ".jpg"):
options['--thumbnail'] = video_directory + video_file + ".jpg"
elif isfile(video_directory + video_file + ".jpeg"):
options['--thumbnail'] = video_directory + video_file + ".jpeg"
return options
# return the nfo as a RawConfigParser object # return the nfo as a RawConfigParser object
def loadNFO(options): def loadNFO(options):
video_directory = dirname(options.get('--file')) + "/" video_directory = dirname(options.get('--file')) + "/"
@ -117,7 +143,6 @@ def loadNFO(options):
else: else:
if options.get('--name'): if options.get('--name'):
nfo_file = video_directory + options.get('--name') + ".txt" nfo_file = video_directory + options.get('--name') + ".txt"
print nfo_file
if isfile(nfo_file): if isfile(nfo_file):
try: try:
logging.info("Using " + nfo_file + " as NFO, loading...") logging.info("Using " + nfo_file + " as NFO, loading...")

+ 27
- 7
lib/yt_upload.py View File

@ -20,6 +20,7 @@ from googleapiclient.errors import HttpError
from googleapiclient.http import MediaFileUpload from googleapiclient.http import MediaFileUpload
from google_auth_oauthlib.flow import InstalledAppFlow from google_auth_oauthlib.flow import InstalledAppFlow
import utils import utils
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO) logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)
@ -126,25 +127,44 @@ def initialize_upload(youtube, options):
body=body, body=body,
media_body=MediaFileUpload(path, chunksize=-1, resumable=True) media_body=MediaFileUpload(path, chunksize=-1, resumable=True)
) )
resumable_upload(insert_request)
video_id = resumable_upload(insert_request, 'video', 'insert')
# If we get a video_id, upload is successful and we are able to set thumbnail
if video_id and options.get('--thumbnail'):
set_thumbnail(youtube, options.get('--thumbnail'), videoId=video_id)
def set_thumbnail(youtube, media_file, **kwargs):
kwargs = utils.remove_empty_kwargs(**kwargs) # See full sample for function
request = youtube.thumbnails().set(
media_body=MediaFileUpload(media_file, chunksize=-1,
resumable=True),
**kwargs
)
# See full sample for function
return resumable_upload(request, 'thumbnail', 'set')
# This method implements an exponential backoff strategy to resume a # This method implements an exponential backoff strategy to resume a
# failed upload. # failed upload.
def resumable_upload(request):
def resumable_upload(request, resource, method):
response = None response = None
error = None error = None
retry = 0 retry = 0
while response is None: while response is None:
try: try:
logging.info('Youtube : Uploading file...')
template = 'Youtube: Uploading %s...'
logging.info(template % resource)
status, response = request.next_chunk() status, response = request.next_chunk()
if response is not None: if response is not None:
if 'id' in response:
template = ('Youtube : Video was successfully '
'uploaded.\n'
'Watch it at https://youtu.be/%s (post-encoding could take some time)')
if method == 'insert' and 'id' in response:
logging.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']) logging.info(template % response['id'])
return response['id']
elif method != 'insert' or "id" not in response:
logging.info('Youtube: Thumbnail was successfully set.')
else: else:
template = ('Youtube : The upload failed with an ' template = ('Youtube : The upload failed with an '
'unexpected response: %s') 'unexpected response: %s')

+ 1
- 0
nfo_example.txt View File

@ -17,6 +17,7 @@ category = Films
cca = True cca = True
privacy = private privacy = private
disable-comments = True disable-comments = True
thumbnail = /path/to/your/thumbnail.jpg # Set the absolute path to your thumbnail
nsfw = True nsfw = True
platform = youtube, peertube platform = youtube, peertube
language = French language = French

+ 15
- 0
prismedia_upload.py View File

@ -34,6 +34,9 @@ Options:
DATE should be on the form YYYY-MM-DDThh:mm:ss eg: 2018-03-12T19:00:00 DATE should be on the form YYYY-MM-DDThh:mm:ss eg: 2018-03-12T19:00:00
DATE should be in the future DATE should be in the future
For Peertube, requires the "atd" and "curl utilities installed on the system For Peertube, requires the "atd" and "curl utilities installed on the system
--thumbnail=STRING Path to a file to use as a thumbnail for the video.
Supported types are jpg and jpeg.
By default, prismedia search for an image based on video name followed by .jpg or .jpeg
-h --help Show this help. -h --help Show this help.
--version Show version. --version Show version.
@ -151,6 +154,12 @@ def validatePublish(publish):
return False return False
return True return True
def validateThumbnail(thumbnail):
supported_types = ['image/jpg', 'image/jpeg']
if magic.from_file(thumbnail, mime=True) in supported_types:
return thumbnail
else:
return False
if __name__ == '__main__': if __name__ == '__main__':
@ -199,12 +208,18 @@ if __name__ == '__main__':
Optional('--cca'): bool, Optional('--cca'): bool,
Optional('--disable-comments'): bool, Optional('--disable-comments'): bool,
Optional('--nsfw'): bool, Optional('--nsfw'): bool,
Optional('--thumbnail'): Or(None, And(
str, validateThumbnail, error='thumbnail is not supported, please use jpg/jpeg'),
),
'--help': bool, '--help': bool,
'--version': bool '--version': bool
}) })
options = utils.parseNFO(options) options = utils.parseNFO(options)
if not options.get('--thumbnail'):
options = utils.searchThumbnail(options)
try: try:
options = schema.validate(options) options = schema.validate(options)
except SchemaError as e: except SchemaError as e:

Loading…
Cancel
Save