mirror of
https://git.lecygnenoir.info/LecygneNoir/prismedia.git
synced 2025-10-03 01:19:15 +02:00
Merge pull request 'hearthbeat (keepalive ?)' (#54) from Zykino/prismedia:hearthbeat into develop
Reviewed-on: https://git.lecygnenoir.info/LecygneNoir/prismedia/pulls/54 Thanks a lot for the idea and the feature!
This commit is contained in:
commit
ca733e0dc3
4 changed files with 87 additions and 48 deletions
63
README.md
63
README.md
|
@ -23,23 +23,20 @@ Scripting your way to upload videos to peertube and youtube. Works with Python 3
|
|||
|
||||
### From pip
|
||||
|
||||
Simply install with
|
||||
|
||||
```bash
|
||||
Simply install with
|
||||
```sh
|
||||
pip install prismedia
|
||||
```
|
||||
|
||||
Upgrade with
|
||||
|
||||
```bash
|
||||
Upgrade with
|
||||
```sh
|
||||
pip install --upgrade prismedia
|
||||
```
|
||||
|
||||
### From source
|
||||
|
||||
Get the source:
|
||||
|
||||
```bash
|
||||
Get the source:
|
||||
```sh
|
||||
git clone https://git.lecygnenoir.info/LecygneNoir/prismedia.git prismedia
|
||||
```
|
||||
|
||||
|
@ -49,11 +46,10 @@ You may use pip to install requirements: `pip install -r requirements.txt` if yo
|
|||
Otherwise, you can use [poetry](https://python-poetry.org), which create a virtualenv for the project directly
|
||||
(Or use the existing virtualenv if one is activated)
|
||||
|
||||
```
|
||||
```sh
|
||||
poetry install
|
||||
```
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
Generate sample files with `python -m prismedia.genconfig`.
|
||||
|
@ -72,7 +68,7 @@ Youtube uses combination of oauth and API access to identify.
|
|||
The first time you connect, prismedia will open your browser to ask you to authenticate to
|
||||
Youtube and allow the app to use your Youtube channel.
|
||||
**It is here you choose which channel you will upload to**.
|
||||
Once authenticated, the token is stored inside the file ``.youtube_credentials.json``.
|
||||
Once authenticated, the token is stored inside the file `.youtube_credentials.json`.
|
||||
Prismedia will try to use this file at each launch, and re-ask for authentication if it does not exist.
|
||||
|
||||
**Oauth**:
|
||||
|
@ -95,35 +91,42 @@ Support only mp4 for cross compatibility between Youtube and Peertube.
|
|||
Here are some demonstration of main usage you would like!
|
||||
|
||||
Upload a video:
|
||||
```
|
||||
prismedia --file="yourvideo.mp4"
|
||||
```sh
|
||||
python -m prismedia --file="yourvideo.mp4"
|
||||
```
|
||||
|
||||
Specify description and tags:
|
||||
```
|
||||
prismedia --file="yourvideo.mp4" -d "My supa description" -t "tag1,tag2,foo"
|
||||
```sh
|
||||
python -m 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"
|
||||
```sh
|
||||
python -m 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
|
||||
```sh
|
||||
python -m 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)
|
||||
```
|
||||
prismedia --file="yourvideo.mp4" --nfo /path/to/your/nfo.txt
|
||||
```sh
|
||||
python -m prismedia --file="yourvideo.mp4" --nfo /path/to/your/nfo.txt
|
||||
```
|
||||
|
||||
Use some credits to show some activity for you apikey so the platform know it is used and would not put your quota to 0 (only Youtube currently).
|
||||
|
||||
To prevent Youtube from inactivating your apikey after 90days of inactivity it is recommended to launch this command automatically from a script around once a month. It will mwke a call to use a few credits from your daily quota.
|
||||
On Linux and MacOS, you can use cron, on Windows the "Task Scheduler".
|
||||
```sh
|
||||
python -m prismedia --hearthbeat
|
||||
```
|
||||
|
||||
Take a look at all available options with `--help`!
|
||||
```
|
||||
prismedia --help
|
||||
```sh
|
||||
python -m prismedia --help
|
||||
```
|
||||
|
||||
## Enhanced use of NFO
|
||||
|
@ -136,7 +139,7 @@ Basically, Prismedia will now load options in this order, using the last value f
|
|||
`nfo.txt < directory_name.txt < video_name.txt < command line NFO < command line argument`
|
||||
|
||||
You'll find a complete set of samples in the [prismedia/samples](prismedia/samples) directory so let's take it as an example:
|
||||
```
|
||||
```sh
|
||||
$ tree Recipes/
|
||||
Recipes/
|
||||
├── cli_nfo.txt
|
||||
|
@ -149,9 +152,9 @@ Recipes/
|
|||
└── yourvideo2.txt
|
||||
```
|
||||
|
||||
By using
|
||||
```
|
||||
prismedia --file=/path/to/Recipes/yourvideo1.mp4 --nfo=/path/to/Recipes/cli_nfo.txt --cca
|
||||
By using
|
||||
```sh
|
||||
python -m prismedia --file=/path/to/Recipes/yourvideo1.mp4 --nfo=/path/to/Recipes/cli_nfo.txt --cca
|
||||
```
|
||||
|
||||
Prismedia will:
|
||||
|
@ -186,7 +189,7 @@ Available strict options:
|
|||
- --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
|
||||
- --withChannel Prevent upload if no channel
|
||||
|
||||
## Features
|
||||
|
||||
|
@ -224,4 +227,4 @@ Available strict options:
|
|||
Inspired by [peeror](https://git.rigelk.eu/rigelk/peeror) and [youtube-upload](https://github.com/tokland/youtube-upload)
|
||||
|
||||
## Contributors
|
||||
Thanks to: @Zykino, @meewan, @rigelk 😘
|
||||
Thanks to: @Zykino, @meewan, @rigelk 😘
|
||||
|
|
|
@ -373,7 +373,7 @@ def create_callback(encoder, progress_type):
|
|||
else:
|
||||
# Print a blank line to not (partly) override the progress bar
|
||||
print()
|
||||
logger.info("Peertube : Upload finish, Processing…")
|
||||
logger.info("Peertube: Upload finish, Processing…")
|
||||
|
||||
return callback
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ prismedia - tool to upload videos to Peertube and Youtube
|
|||
Usage:
|
||||
prismedia --file=<FILE> [options]
|
||||
prismedia -f <FILE> --tags=STRING [options]
|
||||
prismedia --hearthbeat
|
||||
prismedia -h | --help
|
||||
prismedia --version
|
||||
|
||||
|
@ -50,6 +51,9 @@ Options:
|
|||
--playlistCreate Create the playlist if not exists. (default do not create)
|
||||
Only relevant if --playlist is set.
|
||||
--progress=STRING Set the progress bar view, one of percentage, bigFile (MB), accurate (KB).
|
||||
|
||||
--hearthbeat Use some credits to show some activity for you apikey so the platform know it is used and would not put your quota to 0 (only Youtube currently)
|
||||
|
||||
-h --help Show this help.
|
||||
--version Show version.
|
||||
|
||||
|
@ -396,11 +400,17 @@ def main():
|
|||
Optional('--playlist'): Or(None, str),
|
||||
Optional('--playlistCreate'): bool,
|
||||
Optional('--progress'): Or(None, And(str, validateProgress, error="Sorry, progress visualisation not supported")),
|
||||
'--hearthbeat': bool,
|
||||
'--help': bool,
|
||||
'--version': bool,
|
||||
# This allow to return all other options for further use: https://github.com/keleshev/schema#extra-keys
|
||||
object: object
|
||||
})
|
||||
|
||||
if options.get('--hearthbeat'):
|
||||
yt_upload.hearthbeat()
|
||||
exit(0)
|
||||
|
||||
# We need to validate early options first as withNFO and logs options should be prioritized
|
||||
try:
|
||||
options = earlyoptionSchema.validate(options)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
# From Youtube samples : https://raw.githubusercontent.com/youtube/api-samples/master/python/upload_video.py # noqa
|
||||
# From Youtube samples: https://raw.githubusercontent.com/youtube/api-samples/master/python/upload_video.py # noqa
|
||||
|
||||
import http.client
|
||||
import httplib2
|
||||
|
@ -60,6 +60,7 @@ def get_authenticated_service():
|
|||
check_authenticated_scopes()
|
||||
flow = InstalledAppFlow.from_client_secrets_file(
|
||||
CLIENT_SECRETS_FILE, SCOPES)
|
||||
|
||||
if exists(CREDENTIALS_PATH):
|
||||
with open(CREDENTIALS_PATH, 'r') as f:
|
||||
credential_params = json.load(f)
|
||||
|
@ -76,7 +77,7 @@ def get_authenticated_service():
|
|||
p = copy.deepcopy(vars(credentials))
|
||||
del p["expiry"]
|
||||
json.dump(p, f)
|
||||
return build(API_SERVICE_NAME, API_VERSION, credentials=credentials, cache_discovery=False)
|
||||
return build(API_SERVICE_NAME, API_VERSION, credentials=credentials, cache_discovery=False)
|
||||
|
||||
|
||||
def check_authenticated_scopes():
|
||||
|
@ -181,14 +182,24 @@ def initialize_upload(youtube, options):
|
|||
|
||||
|
||||
def get_playlist_by_name(youtube, playlist_name):
|
||||
response = youtube.playlists().list(
|
||||
part='snippet,id',
|
||||
mine=True,
|
||||
maxResults=50
|
||||
).execute()
|
||||
for playlist in response["items"]:
|
||||
if playlist["snippet"]['title'] == playlist_name:
|
||||
return playlist['id']
|
||||
pageToken = ""
|
||||
while pageToken != None:
|
||||
response = youtube.playlists().list(
|
||||
part='snippet,id',
|
||||
mine=True,
|
||||
maxResults=50,
|
||||
pageToken=pageToken
|
||||
).execute()
|
||||
|
||||
for playlist in response["items"]:
|
||||
if playlist["snippet"]["title"] == playlist_name:
|
||||
return playlist["id"]
|
||||
|
||||
# Ask next page if there are any
|
||||
if "nextPageToken" in response:
|
||||
pageToken = response["nextPageToken"]
|
||||
else:
|
||||
pageToken = None
|
||||
|
||||
|
||||
def create_playlist(youtube, playlist_name):
|
||||
|
@ -293,7 +304,7 @@ def resumable_upload(request, resource, method, options):
|
|||
status, response = request.next_chunk()
|
||||
if response is not None:
|
||||
if method == 'insert' and 'id' in response:
|
||||
logger.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)'
|
||||
logger.info(template % response['id'])
|
||||
template_stdout = 'https://youtu.be/%s'
|
||||
|
@ -305,36 +316,51 @@ def resumable_upload(request, resource, method, options):
|
|||
elif method != 'insert' or "id" not in response:
|
||||
logger.info('Youtube: Thumbnail was successfully set.')
|
||||
else:
|
||||
template = ('Youtube : The upload failed with an '
|
||||
template = ('Youtube: The upload failed with an '
|
||||
'unexpected response: %s')
|
||||
logger.critical(template % response)
|
||||
exit(1)
|
||||
except HttpError as e:
|
||||
if e.resp.status in RETRIABLE_STATUS_CODES:
|
||||
template = 'Youtube : A retriable HTTP error %d occurred:\n%s'
|
||||
template = 'Youtube: A retriable HTTP error %d occurred:\n%s'
|
||||
error = template % (e.resp.status, e.content)
|
||||
else:
|
||||
raise
|
||||
except RETRIABLE_EXCEPTIONS as e:
|
||||
error = 'Youtube : A retriable error occurred: %s' % e
|
||||
error = 'Youtube: A retriable error occurred: %s' % e
|
||||
|
||||
if error is not None:
|
||||
logger.warning(error)
|
||||
retry += 1
|
||||
if retry > MAX_RETRIES:
|
||||
logger.error('Youtube : No longer attempting to retry.')
|
||||
logger.error('Youtube: No longer attempting to retry.')
|
||||
|
||||
max_sleep = 2 ** retry
|
||||
sleep_seconds = random.random() * max_sleep
|
||||
logger.warning('Youtube : Sleeping %f seconds and then retrying...'
|
||||
logger.warning('Youtube: Sleeping %f seconds and then retrying...'
|
||||
% sleep_seconds)
|
||||
time.sleep(sleep_seconds)
|
||||
|
||||
|
||||
def hearthbeat():
|
||||
"""Use the minimums credits possibles of the API so google does not readuce to 0 the allowed credits.
|
||||
This apparently happens after 90 days without any usage of credits.
|
||||
For more info see the official documentations:
|
||||
- General informations about quotas: https://developers.google.com/youtube/v3/getting-started#quota
|
||||
- Quota costs for API requests: https://developers.google.com/youtube/v3/determine_quota_cost
|
||||
- ToS (Americas) #Usage and Quotas: https://developers.google.com/youtube/terms/api-services-terms-of-service#usage-and-quotas"""
|
||||
youtube = get_authenticated_service()
|
||||
try:
|
||||
get_playlist_by_name(youtube, "Foo")
|
||||
except HttpError as e:
|
||||
logger.error('Youtube: An HTTP error %d occurred on hearthbeat:\n%s' %
|
||||
(e.resp.status, e.content))
|
||||
|
||||
|
||||
def run(options):
|
||||
youtube = get_authenticated_service()
|
||||
try:
|
||||
initialize_upload(youtube, options)
|
||||
except HttpError as e:
|
||||
logger.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