mirror of
https://git.lecygnenoir.info/LecygneNoir/prismedia.git
synced 2025-10-03 01:19:15 +02:00
Merge branch 'feature/thumbnail' into develop
This commit is contained in:
commit
dea06c6e8f
6 changed files with 95 additions and 23 deletions
15
README.md
15
README.md
|
@ -69,6 +69,12 @@ Specify description and tags:
|
|||
./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:
|
||||
|
||||
|
@ -111,7 +117,10 @@ Options:
|
|||
--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 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.
|
||||
--version Show version.
|
||||
|
||||
|
@ -145,8 +154,8 @@ Languages:
|
|||
- [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] 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)
|
||||
- [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)
|
||||
|
|
|
@ -47,12 +47,11 @@ def upload_video(oauth, secret, options):
|
|||
user_info = json.loads(oauth.get(url + "/api/v1/users/me").content)
|
||||
return str(user_info["id"])
|
||||
|
||||
def get_videofile(path):
|
||||
def get_file(path):
|
||||
mimetypes.init()
|
||||
return (basename(path), open(abspath(path), 'rb'),
|
||||
mimetypes.types_map[splitext(path)[1]])
|
||||
|
||||
path = options.get('--file')
|
||||
url = secret.get('peertube', 'peertube_url')
|
||||
|
||||
# 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/205
|
||||
fields = [
|
||||
("name", options.get('--name') or splitext(basename(path))[0]),
|
||||
("name", options.get('--name') or splitext(basename(options.get('--file')))[0]),
|
||||
("licence", "1"),
|
||||
("description", options.get('--description') or "default description"),
|
||||
("nsfw", str(int(options.get('--nsfw')) or "0")),
|
||||
("channelId", get_userinfo()),
|
||||
("videofile", get_videofile(path))
|
||||
("videofile", get_file(options.get('--file')))
|
||||
]
|
||||
|
||||
if options.get('--tags'):
|
||||
|
@ -76,7 +75,7 @@ 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("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)
|
||||
# If Mastodon compatibility is enabled, clean tags from special characters
|
||||
if options.get('--mt'):
|
||||
|
@ -105,6 +104,9 @@ def upload_video(oauth, secret, options):
|
|||
else:
|
||||
fields.append(("commentsEnabled", "1"))
|
||||
|
||||
if options.get('--thumbnail'):
|
||||
fields.append(("thumbnailfile", get_file(options.get('--thumbnail'))))
|
||||
|
||||
multipart_data = MultipartEncoder(fields)
|
||||
|
||||
headers = {
|
||||
|
@ -120,13 +122,13 @@ def upload_video(oauth, secret, options):
|
|||
jresponse = jresponse['video']
|
||||
uuid = jresponse['uuid']
|
||||
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))
|
||||
if options.get('--publishAt'):
|
||||
utils.publishAt(str(options.get('--publishAt')), oauth, url, idvideo, secret)
|
||||
else:
|
||||
logging.error(('Peertube : The upload failed with an unexpected response: '
|
||||
logging.error(('Peertube: The upload failed with an unexpected response: '
|
||||
'%s') % response)
|
||||
exit(1)
|
||||
|
||||
|
@ -136,16 +138,16 @@ def run(options):
|
|||
try:
|
||||
secret.read(PEERTUBE_SECRETS_FILE)
|
||||
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)
|
||||
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 file...')
|
||||
logging.info('Peertube: Uploading video...')
|
||||
upload_video(oauth, secret, options)
|
||||
except Exception as e:
|
||||
if hasattr(e, 'message'):
|
||||
logging.error("Error: " + str(e.message))
|
||||
logging.error("Peertube: Error: " + str(e.message))
|
||||
else:
|
||||
logging.error("Error: " + str(e))
|
||||
logging.error("Peertube: Error: " + str(e))
|
||||
|
|
27
lib/utils.py
27
lib/utils.py
|
@ -98,6 +98,32 @@ def getLanguage(language, platform):
|
|||
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
|
||||
def loadNFO(options):
|
||||
video_directory = dirname(options.get('--file')) + "/"
|
||||
|
@ -117,7 +143,6 @@ def loadNFO(options):
|
|||
else:
|
||||
if options.get('--name'):
|
||||
nfo_file = video_directory + options.get('--name') + ".txt"
|
||||
print nfo_file
|
||||
if isfile(nfo_file):
|
||||
try:
|
||||
logging.info("Using " + nfo_file + " as NFO, loading...")
|
||||
|
|
|
@ -20,6 +20,7 @@ from googleapiclient.errors import HttpError
|
|||
from googleapiclient.http import MediaFileUpload
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
|
||||
|
||||
import utils
|
||||
|
||||
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)
|
||||
|
@ -126,25 +127,44 @@ def initialize_upload(youtube, options):
|
|||
body=body,
|
||||
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
|
||||
# failed upload.
|
||||
def resumable_upload(request):
|
||||
def resumable_upload(request, resource, method):
|
||||
response = None
|
||||
error = None
|
||||
retry = 0
|
||||
while response is None:
|
||||
try:
|
||||
logging.info('Youtube : Uploading file...')
|
||||
template = 'Youtube: Uploading %s...'
|
||||
logging.info(template % resource)
|
||||
status, response = request.next_chunk()
|
||||
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'])
|
||||
return response['id']
|
||||
elif method != 'insert' or "id" not in response:
|
||||
logging.info('Youtube: Thumbnail was successfully set.')
|
||||
else:
|
||||
template = ('Youtube : The upload failed with an '
|
||||
'unexpected response: %s')
|
||||
|
|
|
@ -17,6 +17,7 @@ category = Films
|
|||
cca = True
|
||||
privacy = private
|
||||
disable-comments = True
|
||||
thumbnail = /path/to/your/thumbnail.jpg # Set the absolute path to your thumbnail
|
||||
nsfw = True
|
||||
platform = youtube, peertube
|
||||
language = French
|
||||
|
|
|
@ -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 in the future
|
||||
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.
|
||||
--version Show version.
|
||||
|
||||
|
@ -151,6 +154,12 @@ def validatePublish(publish):
|
|||
return False
|
||||
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__':
|
||||
|
||||
|
@ -199,12 +208,18 @@ if __name__ == '__main__':
|
|||
Optional('--cca'): bool,
|
||||
Optional('--disable-comments'): bool,
|
||||
Optional('--nsfw'): bool,
|
||||
Optional('--thumbnail'): Or(None, And(
|
||||
str, validateThumbnail, error='thumbnail is not supported, please use jpg/jpeg'),
|
||||
),
|
||||
'--help': bool,
|
||||
'--version': bool
|
||||
})
|
||||
|
||||
options = utils.parseNFO(options)
|
||||
|
||||
if not options.get('--thumbnail'):
|
||||
options = utils.searchThumbnail(options)
|
||||
|
||||
try:
|
||||
options = schema.validate(options)
|
||||
except SchemaError as e:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue