mirror of
https://git.lecygnenoir.info/LecygneNoir/prismedia.git
synced 2025-10-03 01:19:15 +02:00
Merge branch 'feature/recording_date' into develop
This commit is contained in:
commit
1a937098d8
6 changed files with 94 additions and 107 deletions
|
@ -1,5 +1,10 @@
|
|||
# Changelog
|
||||
|
||||
## v0.11.0
|
||||
|
||||
## Features
|
||||
- Add the configuration of Original date of Record for Youtube and Peertube (see #50)
|
||||
|
||||
## v0.10.3
|
||||
|
||||
### Fix
|
||||
|
|
94
README.md
94
README.md
|
@ -92,24 +92,27 @@ If you plan a larger usage, please consider creating your own youtube_secret fil
|
|||
Support only mp4 for cross compatibility between Youtube and Peertube.
|
||||
**Note that all options may be specified in a NFO file!** (see [Enhanced NFO](#enhanced-use-of-nfo))
|
||||
|
||||
Upload a video:
|
||||
Here are some demonstration of main usage you would like!
|
||||
|
||||
Upload a video:
|
||||
```
|
||||
prismedia --file="yourvideo.mp4"
|
||||
```
|
||||
|
||||
Specify description and tags:
|
||||
|
||||
```
|
||||
prismedia --file="yourvideo.mp4" -d "My supa description" -t "tag1,tag2,foo"
|
||||
```
|
||||
|
||||
Provide a thumbnail:
|
||||
|
||||
```
|
||||
prismedia --file="yourvideo.mp4" -d "Video with thumbnail" --thumbnail="/path/to/your/thumbnail.jpg"
|
||||
```
|
||||
|
||||
Publish on Peertube only, while using a channel and a playlist, creating them if they does not exist.:
|
||||
```
|
||||
prismedia --file="yourvideo.mp4" --platform=peertube --channel="Cooking recipes" --playlist="Cake recipes" --channelCreate --playlistCreate
|
||||
```
|
||||
|
||||
Use a NFO file to specify your video options:
|
||||
(See [Enhanced NFO](#enhanced-use-of-nfo) for more precise example)
|
||||
|
@ -118,90 +121,9 @@ prismedia --file="yourvideo.mp4" --nfo /path/to/your/nfo.txt
|
|||
```
|
||||
|
||||
|
||||
Use --help to get all available options:
|
||||
|
||||
Take a look at all available options with `--help`!
|
||||
```
|
||||
Options:
|
||||
-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)
|
||||
-d, --description=STRING Description of the video. (default: default description)
|
||||
-t, --tags=STRING Tags for the video. comma separated.
|
||||
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)
|
||||
--cca License should be CreativeCommon Attribution (affects Youtube upload only)
|
||||
-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)
|
||||
--nfo=STRING Configure a specific nfo file to set options for the video.
|
||||
By default Prismedia search a .txt based on the video name and will
|
||||
decode the file as UTF-8 (so make sure your nfo file is UTF-8 encoded)
|
||||
See nfo_example.txt for more details
|
||||
--platform=STRING List of platform(s) to upload to, comma separated.
|
||||
Supported platforms are youtube and peertube (default is both)
|
||||
--language=STRING Specify the default language for video. See below for supported language. (default is English)
|
||||
--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
|
||||
--peertubeAt=DATE
|
||||
--youtubeAt=DATE Override publishAt for the corresponding platform. Allow to create preview on specific platform
|
||||
--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
|
||||
--channel=STRING Set the channel to use for the video (Peertube only)
|
||||
If the channel is not found, spawn an error except if --channelCreate is set.
|
||||
--channelCreate Create the channel if not exists. (Peertube only, default do not create)
|
||||
Only relevant if --channel is set.
|
||||
--playlist=STRING Set the playlist to use for the video.
|
||||
If the playlist is not found, spawn an error except if --playlistCreate is set.
|
||||
--playlistCreate Create the playlist if not exists. (default do not create)
|
||||
Only relevant if --playlist is set.
|
||||
-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:
|
||||
music, films, vehicles,
|
||||
sports, travels, gaming, people,
|
||||
comedy, entertainment, news,
|
||||
how to, education, activism, science & technology,
|
||||
science, technology, animals
|
||||
|
||||
Languages:
|
||||
Language of the video (audio track), choose one. Default is English
|
||||
Here are available languages from Peertube and Youtube:
|
||||
Arabic, English, French, German, Hindi, Italian,
|
||||
Japanese, Korean, Mandarin, Portuguese, Punjabi, Russian, Spanish
|
||||
|
||||
prismedia --help
|
||||
```
|
||||
|
||||
## Enhanced use of NFO
|
||||
|
|
|
@ -63,6 +63,13 @@ def get_channel_by_name(user_info, options):
|
|||
return channel['id']
|
||||
|
||||
|
||||
def convert_peertube_date(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 create_channel(oauth, url, options):
|
||||
template = ('Peertube: Channel %s does not exist, creating it.')
|
||||
logger.info(template % (str(options.get('--channel'))))
|
||||
|
@ -255,16 +262,18 @@ def upload_video(oauth, secret, options):
|
|||
publishAt = options.get('--publishAt')
|
||||
|
||||
if 'publishAt' in locals():
|
||||
publishAt = datetime.datetime.strptime(publishAt, '%Y-%m-%dT%H:%M:%S')
|
||||
tz = get_localzone()
|
||||
tz = pytz.timezone(str(tz))
|
||||
publishAt = tz.localize(publishAt).isoformat()
|
||||
publishAt = convert_peertube_date(publishAt)
|
||||
fields.append(("scheduleUpdate[updateAt]", publishAt))
|
||||
fields.append(("scheduleUpdate[privacy]", str(PEERTUBE_PRIVACY["public"])))
|
||||
fields.append(("privacy", str(PEERTUBE_PRIVACY["private"])))
|
||||
else:
|
||||
fields.append(("privacy", str(PEERTUBE_PRIVACY[privacy or "private"])))
|
||||
|
||||
# Set originalDate except if the user force no originalDate
|
||||
if options.get('--originalDate'):
|
||||
originalDate = convert_peertube_date(options.get('--originalDate'))
|
||||
fields.append(("originallyPublishedAt", originalDate))
|
||||
|
||||
if options.get('--thumbnail'):
|
||||
fields.append(("thumbnailfile", get_file(options.get('--thumbnail'))))
|
||||
fields.append(("previewfile", get_file(options.get('--thumbnail'))))
|
||||
|
|
|
@ -34,6 +34,10 @@ Options:
|
|||
DATE should be in the future
|
||||
--peertubeAt=DATE
|
||||
--youtubeAt=DATE Override publishAt for the corresponding platform. Allow to create preview on specific platform
|
||||
--originalDate=DATE Configure the video as initially recorded at DATE
|
||||
DATE should be on the form YYYY-MM-DDThh:mm:ss eg: 2018-03-12T19:00:00
|
||||
DATE should be in the past
|
||||
--auto-originalDate Automatically use the file modification time as original date
|
||||
--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
|
||||
|
@ -71,6 +75,7 @@ Strict options:
|
|||
--withTags Prevent the upload without tags
|
||||
--withPlaylist Prevent the upload if no playlist
|
||||
--withPublishAt Prevent the upload if no schedule
|
||||
--withOriginalDate Prevent the upload if no original date configured
|
||||
--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
|
||||
|
@ -190,11 +195,11 @@ def validateLanguage(language):
|
|||
return False
|
||||
|
||||
|
||||
def validatePublish(publish):
|
||||
def validatePublishDate(publishDate):
|
||||
# Check date format and if date is future
|
||||
try:
|
||||
now = datetime.datetime.now()
|
||||
publishAt = datetime.datetime.strptime(publish, '%Y-%m-%dT%H:%M:%S')
|
||||
publishAt = datetime.datetime.strptime(publishDate, '%Y-%m-%dT%H:%M:%S')
|
||||
if now >= publishAt:
|
||||
return False
|
||||
except ValueError:
|
||||
|
@ -202,6 +207,18 @@ def validatePublish(publish):
|
|||
return True
|
||||
|
||||
|
||||
def validateOriginalDate(originalDate):
|
||||
# Check date format and if date is past
|
||||
try:
|
||||
now = datetime.datetime.now()
|
||||
originalDate = datetime.datetime.strptime(originalDate, '%Y-%m-%dT%H:%M:%S')
|
||||
if now <= originalDate:
|
||||
return False
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def validateThumbnail(thumbnail):
|
||||
supported_types = ['image/jpg', 'image/jpeg']
|
||||
if os.path.exists(thumbnail) and \
|
||||
|
@ -217,6 +234,7 @@ def validateLogLevel(loglevel):
|
|||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _optionnalOrStrict(key, scope, error):
|
||||
option = key.replace('-', '')
|
||||
option = option[0].upper() + option[1:]
|
||||
|
@ -280,6 +298,7 @@ def main():
|
|||
Optional('--withTags', default=False): bool,
|
||||
Optional('--withPlaylist', default=False): bool,
|
||||
Optional('--withPublishAt', default=False): bool,
|
||||
Optional('--withOriginalDate', default=False): bool,
|
||||
Optional('--withPlatform', default=False): bool,
|
||||
Optional('--withCategory', default=False): bool,
|
||||
Optional('--withLanguage', default=False): bool,
|
||||
|
@ -298,6 +317,7 @@ def main():
|
|||
Hook('--language', handler=_optionnalOrStrict): object,
|
||||
Hook('--platform', handler=_optionnalOrStrict): object,
|
||||
Hook('--publishAt', handler=_optionnalOrStrict): object,
|
||||
Hook('--originalDate', handler=_optionnalOrStrict): object,
|
||||
Hook('--thumbnail', handler=_optionnalOrStrict): object,
|
||||
Hook('--channel', handler=_optionnalOrStrict): object,
|
||||
Hook('--playlist', handler=_optionnalOrStrict): object,
|
||||
|
@ -336,19 +356,25 @@ def main():
|
|||
Optional('--platform'): Or(None, And(str, validatePlatform, error="Sorry, upload platform not supported")),
|
||||
Optional('--publishAt'): Or(None, And(
|
||||
str,
|
||||
validatePublish,
|
||||
error="DATE should be the form YYYY-MM-DDThh:mm:ss and has to be in the future")
|
||||
validatePublishDate,
|
||||
error="Publish Date should be the form YYYY-MM-DDThh:mm:ss and has to be in the future")
|
||||
),
|
||||
Optional('--peertubeAt'): Or(None, And(
|
||||
str,
|
||||
validatePublish,
|
||||
error="DATE should be the form YYYY-MM-DDThh:mm:ss and has to be in the future")
|
||||
validatePublishDate,
|
||||
error="Publish Date should be the form YYYY-MM-DDThh:mm:ss and has to be in the future")
|
||||
),
|
||||
Optional('--youtubeAt'): Or(None, And(
|
||||
str,
|
||||
validatePublish,
|
||||
error="DATE should be the form YYYY-MM-DDThh:mm:ss and has to be in the future")
|
||||
validatePublishDate,
|
||||
error="Publish Date should be the form YYYY-MM-DDThh:mm:ss and has to be in the future")
|
||||
),
|
||||
Optional('--originalDate'): Or(None, And(
|
||||
str,
|
||||
validateOriginalDate,
|
||||
error="Original date should be the form YYYY-MM-DDThh:mm:ss and has to be in the past")
|
||||
),
|
||||
Optional('--auto-originalDate'): bool,
|
||||
Optional('--cca'): bool,
|
||||
Optional('--disable-comments'): bool,
|
||||
Optional('--nsfw'): bool,
|
||||
|
@ -377,6 +403,12 @@ def main():
|
|||
|
||||
options = utils.parseNFO(options)
|
||||
|
||||
# If after loading NFO we still has no original date and --auto-originalDate is enabled,
|
||||
# then we need to search from the file
|
||||
# We need to do that before the strict validation in case --withOriginalDate is enabled
|
||||
if not options.get('--originalDate') and options.get('--auto-originalDate'):
|
||||
options['--originalDate'] = utils.searchOriginalDate(options)
|
||||
|
||||
# Once NFO are loaded, we need to revalidate strict options in case some were in NFO
|
||||
try:
|
||||
options = earlyoptionSchema.validate(options)
|
||||
|
|
|
@ -2,12 +2,11 @@
|
|||
# coding: utf-8
|
||||
|
||||
from configparser import RawConfigParser, NoOptionError, NoSectionError
|
||||
from os.path import dirname, splitext, basename, isfile
|
||||
from os.path import dirname, splitext, basename, isfile, getmtime
|
||||
import re
|
||||
from os import devnull
|
||||
from subprocess import check_call, CalledProcessError, STDOUT
|
||||
import unidecode
|
||||
import logging
|
||||
import datetime
|
||||
|
||||
logger = logging.getLogger('Prismedia')
|
||||
|
||||
|
@ -135,6 +134,11 @@ def searchThumbnail(options):
|
|||
return options
|
||||
|
||||
|
||||
def searchOriginalDate(options):
|
||||
fileModificationDate = getmtime(options.get('--file'))
|
||||
return datetime.datetime.fromtimestamp(fileModificationDate).isoformat()
|
||||
|
||||
|
||||
# return the nfo as a RawConfigParser object
|
||||
def loadNFO(filename):
|
||||
try:
|
||||
|
|
|
@ -89,6 +89,15 @@ def check_authenticated_scopes():
|
|||
os.remove(CREDENTIALS_PATH)
|
||||
|
||||
|
||||
def convert_youtube_date(date):
|
||||
# Youtube needs microsecond and the local timezone from ISO 8601
|
||||
date = date + ".000001"
|
||||
date = datetime.datetime.strptime(date, '%Y-%m-%dT%H:%M:%S.%f')
|
||||
tz = get_localzone()
|
||||
tz = pytz.timezone(str(tz))
|
||||
return tz.localize(date).isoformat()
|
||||
|
||||
|
||||
def initialize_upload(youtube, options):
|
||||
path = options.get('--file')
|
||||
tags = None
|
||||
|
@ -107,6 +116,8 @@ def initialize_upload(youtube, options):
|
|||
if options.get('--cca'):
|
||||
license = "creativeCommon"
|
||||
|
||||
# We set recordingDetails empty because it's easier to add options if it already exists
|
||||
# and if empty, it does not cause problem during upload
|
||||
body = {
|
||||
"snippet": {
|
||||
"title": options.get('--name') or splitext(basename(path))[0],
|
||||
|
@ -119,6 +130,9 @@ def initialize_upload(youtube, options):
|
|||
"status": {
|
||||
"privacyStatus": str(options.get('--privacy') or "private"),
|
||||
"license": str(license or "youtube"),
|
||||
},
|
||||
"recordingDetails": {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,15 +142,16 @@ def initialize_upload(youtube, options):
|
|||
elif options.get('--publishAt'):
|
||||
publishAt = options.get('--publishAt')
|
||||
|
||||
# Check if publishAt variable exists in local variables
|
||||
if 'publishAt' in locals():
|
||||
# Youtube needs microsecond and the local timezone from ISO 8601
|
||||
publishAt = publishAt + ".000001"
|
||||
publishAt = datetime.datetime.strptime(publishAt, '%Y-%m-%dT%H:%M:%S.%f')
|
||||
tz = get_localzone()
|
||||
tz = pytz.timezone(str(tz))
|
||||
publishAt = tz.localize(publishAt).isoformat()
|
||||
publishAt = convert_youtube_date(publishAt)
|
||||
body['status']['publishAt'] = str(publishAt)
|
||||
|
||||
# Set originalDate except if the user force no originalDate
|
||||
if options.get('--originalDate'):
|
||||
originalDate = convert_youtube_date(options.get('--originalDate'))
|
||||
body['recordingDetails']['recordingDate'] = str(originalDate)
|
||||
|
||||
if options.get('--playlist'):
|
||||
playlist_id = get_playlist_by_name(youtube, options.get('--playlist'))
|
||||
if not playlist_id and options.get('--playlistCreate'):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue