mirror of
https://git.lecygnenoir.info/LecygneNoir/prismedia.git
synced 2025-10-03 09:29:16 +02:00
Add the publishAt option to allow planned publication for your videos. See README for prerequisites
This commit is contained in:
parent
8968438af2
commit
d2c6154738
5 changed files with 95 additions and 4 deletions
10
README.md
10
README.md
|
@ -12,6 +12,11 @@ Search in your package manager, otherwise use ``pip install --upgrade``
|
||||||
- schema
|
- schema
|
||||||
- python-magic
|
- python-magic
|
||||||
- requests-toolbelt
|
- requests-toolbelt
|
||||||
|
- tzlocal
|
||||||
|
|
||||||
|
For Peertube and if you want to use the publishAt option, you also need some utilities on you local system
|
||||||
|
- [atd](https://linux.die.net/man/8/atd) daemon
|
||||||
|
- [curl](https://linux.die.net/man/1/curl)
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
@ -102,6 +107,10 @@ Options:
|
||||||
--platform=STRING List of platform(s) to upload to, comma separated.
|
--platform=STRING List of platform(s) to upload to, comma separated.
|
||||||
Supported platforms are youtube and peertube (default is both)
|
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)
|
--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
|
||||||
|
For Peertube, requires the "atd" and "curl utilities installed on the system
|
||||||
-h --help Show this help.
|
-h --help Show this help.
|
||||||
--version Show version.
|
--version Show version.
|
||||||
|
|
||||||
|
@ -138,6 +147,7 @@ Languages:
|
||||||
- ~~thumbnail/preview~~ Canceled, waiting for Youtube's API support
|
- ~~thumbnail/preview~~ Canceled, waiting for Youtube's API support
|
||||||
- [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)
|
||||||
|
- [x] Add publishAt option to plan your videos (need the [atd](https://linux.die.net/man/8/atd) daemon and [curl](https://linux.die.net/man/1/curl))
|
||||||
- [ ] Record and forget: put the video in a directory, and the script uploads it for you
|
- [ ] Record and forget: put the video in a directory, and the script uploads it for you
|
||||||
- [ ] Usable on Desktop (Linux and/or Windows and/or MacOS)
|
- [ ] Usable on Desktop (Linux and/or Windows and/or MacOS)
|
||||||
- [ ] Graphical User Interface
|
- [ ] Graphical User Interface
|
||||||
|
|
|
@ -36,6 +36,7 @@ def get_authenticated_service(secret):
|
||||||
client_id=str(secret.get('peertube', 'client_id')),
|
client_id=str(secret.get('peertube', 'client_id')),
|
||||||
client_secret=str(secret.get('peertube', 'client_secret'))
|
client_secret=str(secret.get('peertube', 'client_secret'))
|
||||||
)
|
)
|
||||||
|
|
||||||
return oauth
|
return oauth
|
||||||
|
|
||||||
|
|
||||||
|
@ -113,9 +114,10 @@ def upload_video(oauth, secret, options):
|
||||||
headers=headers)
|
headers=headers)
|
||||||
if response is not None:
|
if response is not None:
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
uuid = response.json()
|
jresponse = response.json()
|
||||||
uuid = uuid['video']
|
jresponse = jresponse['video']
|
||||||
uuid = uuid['uuid']
|
uuid = jresponse['uuid']
|
||||||
|
idvideo = str(jresponse['id'])
|
||||||
template = ('Peertube : Video was successfully uploaded.\n'
|
template = ('Peertube : Video was successfully uploaded.\n'
|
||||||
'Watch it at %s/videos/watch/%s.')
|
'Watch it at %s/videos/watch/%s.')
|
||||||
print(template % (url, uuid))
|
print(template % (url, uuid))
|
||||||
|
@ -123,6 +125,9 @@ def upload_video(oauth, secret, options):
|
||||||
exit(('Peertube : The upload failed with an unexpected response: '
|
exit(('Peertube : The upload failed with an unexpected response: '
|
||||||
'%s') % response)
|
'%s') % response)
|
||||||
|
|
||||||
|
if options.get('--publishAt'):
|
||||||
|
utils.publishAt(str(options.get('--publishAt')), oauth, url, idvideo)
|
||||||
|
|
||||||
|
|
||||||
def run(options):
|
def run(options):
|
||||||
secret = RawConfigParser()
|
secret = RawConfigParser()
|
||||||
|
|
42
lib/utils.py
42
lib/utils.py
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
from ConfigParser import RawConfigParser, NoOptionError, NoSectionError
|
from ConfigParser import RawConfigParser, NoOptionError, NoSectionError
|
||||||
from os.path import dirname, splitext, basename, isfile
|
from os.path import dirname, splitext, basename, isfile
|
||||||
|
from os import devnull
|
||||||
|
from subprocess import check_call, CalledProcessError, STDOUT
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
### CATEGORIES ###
|
### CATEGORIES ###
|
||||||
|
@ -161,6 +163,46 @@ def upcaseFirstLetter(s):
|
||||||
return s[0].upper() + s[1:]
|
return s[0].upper() + s[1:]
|
||||||
|
|
||||||
|
|
||||||
|
def publishAt(publishAt, oauth, url, idvideo):
|
||||||
|
try:
|
||||||
|
FNULL = open(devnull, 'w')
|
||||||
|
check_call(["at", "-V"], stdout=FNULL, stderr=STDOUT)
|
||||||
|
except CalledProcessError:
|
||||||
|
exit("You need to install the atd daemon to use the publishAt option.")
|
||||||
|
try:
|
||||||
|
FNULL = open(devnull, 'w')
|
||||||
|
check_call(["curl", "-V"], stdout=FNULL, stderr=STDOUT)
|
||||||
|
except CalledProcessError:
|
||||||
|
exit("You need to install the curl command line to use the publishAt option.")
|
||||||
|
time = publishAt.split("T")
|
||||||
|
# Remove leading seconds that atd does not manage
|
||||||
|
if time[1].count(":") == 2:
|
||||||
|
time[1] = time[1][:-3]
|
||||||
|
|
||||||
|
atTime = time[1] + " " + time[0]
|
||||||
|
token=str(oauth.__dict__['_client'].__dict__['access_token'])
|
||||||
|
atFile = "/tmp/peertube_" + idvideo + "_" + publishAt + ".at"
|
||||||
|
try:
|
||||||
|
file = open(atFile,"w")
|
||||||
|
file.write("curl '" + url + "/api/v1/videos/" + idvideo + "' -X PUT -H 'Authorization: Bearer " + token + "' -H 'Content-Type: multipart/form-data' -F 'privacy=1'")
|
||||||
|
file.write(" ") # atd needs an empty line at the end of the file to load...
|
||||||
|
file.close()
|
||||||
|
except Exception as e:
|
||||||
|
if hasattr(e, 'message'):
|
||||||
|
print("Error: " + str(e.message))
|
||||||
|
else:
|
||||||
|
print("Error: " + str(e))
|
||||||
|
|
||||||
|
try:
|
||||||
|
FNULL = open(devnull, 'w')
|
||||||
|
check_call(["at", "-M", "-f", atFile, atTime], stdout=FNULL, stderr=STDOUT)
|
||||||
|
except Exception as e:
|
||||||
|
if hasattr(e, 'message'):
|
||||||
|
print("Error: " + str(e.message))
|
||||||
|
else:
|
||||||
|
print("Error: " + str(e))
|
||||||
|
|
||||||
|
|
||||||
def mastodonTag(tag):
|
def mastodonTag(tag):
|
||||||
tags = tag.split(' ')
|
tags = tag.split(' ')
|
||||||
mtag = ''
|
mtag = ''
|
||||||
|
|
|
@ -10,6 +10,9 @@ import copy
|
||||||
import json
|
import json
|
||||||
from os.path import splitext, basename, exists
|
from os.path import splitext, basename, exists
|
||||||
import google.oauth2.credentials
|
import google.oauth2.credentials
|
||||||
|
import datetime
|
||||||
|
import pytz
|
||||||
|
from tzlocal import get_localzone
|
||||||
|
|
||||||
from googleapiclient.discovery import build
|
from googleapiclient.discovery import build
|
||||||
from googleapiclient.errors import HttpError
|
from googleapiclient.errors import HttpError
|
||||||
|
@ -100,10 +103,19 @@ def initialize_upload(youtube, options):
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"privacyStatus": str(options.get('--privacy') or "private"),
|
"privacyStatus": str(options.get('--privacy') or "private"),
|
||||||
"license": str(license or "youtube")
|
"license": str(license or "youtube"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if options.get('--publishAt'):
|
||||||
|
# Youtube needs microsecond and the local timezone from ISO 8601
|
||||||
|
publishAt = options.get('--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()
|
||||||
|
body['status']['publishAt'] = str(publishAt)
|
||||||
|
|
||||||
# Call the API's videos.insert method to create and upload the video.
|
# Call the API's videos.insert method to create and upload the video.
|
||||||
insert_request = youtube.videos().insert(
|
insert_request = youtube.videos().insert(
|
||||||
part=','.join(body.keys()),
|
part=','.join(body.keys()),
|
||||||
|
|
|
@ -30,6 +30,10 @@ Options:
|
||||||
--platform=STRING List of platform(s) to upload to, comma separated.
|
--platform=STRING List of platform(s) to upload to, comma separated.
|
||||||
Supported platforms are youtube and peertube (default is both)
|
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)
|
--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
|
||||||
|
For Peertube, requires the "atd" and "curl utilities installed on the system
|
||||||
-h --help Show this help.
|
-h --help Show this help.
|
||||||
--version Show version.
|
--version Show version.
|
||||||
|
|
||||||
|
@ -51,6 +55,7 @@ Languages:
|
||||||
"""
|
"""
|
||||||
from os.path import dirname, realpath
|
from os.path import dirname, realpath
|
||||||
import sys
|
import sys
|
||||||
|
import datetime
|
||||||
|
|
||||||
from docopt import docopt
|
from docopt import docopt
|
||||||
|
|
||||||
|
@ -131,6 +136,18 @@ def validateLanguage(language):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def validatePublish(publish):
|
||||||
|
# Check date format and if date is future
|
||||||
|
try:
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
publishAt = datetime.datetime.strptime(publish, '%Y-%m-%dT%H:%M:%S')
|
||||||
|
if now >= publishAt:
|
||||||
|
return False
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
options = docopt(__doc__, version=VERSION)
|
options = docopt(__doc__, version=VERSION)
|
||||||
|
@ -170,6 +187,11 @@ if __name__ == '__main__':
|
||||||
),
|
),
|
||||||
Optional('--nfo'): Or(None, str),
|
Optional('--nfo'): Or(None, str),
|
||||||
Optional('--platform'): Or(None, And(str, validatePlatform, error="Sorry, upload platform not supported")),
|
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")
|
||||||
|
),
|
||||||
Optional('--cca'): bool,
|
Optional('--cca'): bool,
|
||||||
Optional('--disable-comments'): bool,
|
Optional('--disable-comments'): bool,
|
||||||
Optional('--nsfw'): bool,
|
Optional('--nsfw'): bool,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue