mirror of
https://github.com/anatolykopyl/tt-compiler.git
synced 2026-03-26 11:24:28 +00:00
Apiless uploads
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,5 +2,4 @@ node_modules/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
trend/
|
trend/
|
||||||
*.mp4
|
*.mp4
|
||||||
client_secrets.json
|
cookies.json
|
||||||
*-oauth2.json
|
|
||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "YouTubeUploader"]
|
||||||
|
path = YouTubeUploader
|
||||||
|
url = git@github.com:ContentAutomation/YouTubeUploader.git
|
||||||
1
YouTubeUploader
Submodule
1
YouTubeUploader
Submodule
Submodule YouTubeUploader added at 74255c9043
181
upload.py
181
upload.py
@@ -1,181 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
import httplib
|
|
||||||
import httplib2
|
|
||||||
import os
|
|
||||||
import random
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
|
|
||||||
from apiclient.discovery import build
|
|
||||||
from apiclient.errors import HttpError
|
|
||||||
from apiclient.http import MediaFileUpload
|
|
||||||
from oauth2client.client import flow_from_clientsecrets
|
|
||||||
from oauth2client.file import Storage
|
|
||||||
from oauth2client.tools import argparser, run_flow
|
|
||||||
|
|
||||||
|
|
||||||
# Explicitly tell the underlying HTTP transport library not to retry, since
|
|
||||||
# we are handling retry logic ourselves.
|
|
||||||
httplib2.RETRIES = 1
|
|
||||||
|
|
||||||
# Maximum number of times to retry before giving up.
|
|
||||||
MAX_RETRIES = 10
|
|
||||||
|
|
||||||
# Always retry when these exceptions are raised.
|
|
||||||
RETRIABLE_EXCEPTIONS = (httplib2.HttpLib2Error, IOError, httplib.NotConnected,
|
|
||||||
httplib.IncompleteRead, httplib.ImproperConnectionState,
|
|
||||||
httplib.CannotSendRequest, httplib.CannotSendHeader,
|
|
||||||
httplib.ResponseNotReady, httplib.BadStatusLine)
|
|
||||||
|
|
||||||
# Always retry when an apiclient.errors.HttpError with one of these status
|
|
||||||
# codes is raised.
|
|
||||||
RETRIABLE_STATUS_CODES = [500, 502, 503, 504]
|
|
||||||
|
|
||||||
# The CLIENT_SECRETS_FILE variable specifies the name of a file that contains
|
|
||||||
# the OAuth 2.0 information for this application, including its client_id and
|
|
||||||
# client_secret. You can acquire an OAuth 2.0 client ID and client secret from
|
|
||||||
# the Google API Console at
|
|
||||||
# https://console.developers.google.com/.
|
|
||||||
# Please ensure that you have enabled the YouTube Data API for your project.
|
|
||||||
# For more information about using OAuth2 to access the YouTube Data API, see:
|
|
||||||
# https://developers.google.com/youtube/v3/guides/authentication
|
|
||||||
# For more information about the client_secrets.json file format, see:
|
|
||||||
# https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
|
|
||||||
CLIENT_SECRETS_FILE = "client_secrets.json"
|
|
||||||
|
|
||||||
# This OAuth 2.0 access scope allows an application to upload files to the
|
|
||||||
# authenticated user's YouTube channel, but doesn't allow other types of access.
|
|
||||||
YOUTUBE_UPLOAD_SCOPE = "https://www.googleapis.com/auth/youtube.upload"
|
|
||||||
YOUTUBE_API_SERVICE_NAME = "youtube"
|
|
||||||
YOUTUBE_API_VERSION = "v3"
|
|
||||||
|
|
||||||
# This variable defines a message to display if the CLIENT_SECRETS_FILE is
|
|
||||||
# missing.
|
|
||||||
MISSING_CLIENT_SECRETS_MESSAGE = """
|
|
||||||
WARNING: Please configure OAuth 2.0
|
|
||||||
|
|
||||||
To make this sample run you will need to populate the client_secrets.json file
|
|
||||||
found at:
|
|
||||||
|
|
||||||
%s
|
|
||||||
|
|
||||||
with information from the API Console
|
|
||||||
https://console.developers.google.com/
|
|
||||||
|
|
||||||
For more information about the client_secrets.json file format, please visit:
|
|
||||||
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
|
|
||||||
""" % os.path.abspath(os.path.join(os.path.dirname(__file__),
|
|
||||||
CLIENT_SECRETS_FILE))
|
|
||||||
|
|
||||||
VALID_PRIVACY_STATUSES = ("public", "private", "unlisted")
|
|
||||||
|
|
||||||
|
|
||||||
def get_authenticated_service(args):
|
|
||||||
flow = flow_from_clientsecrets(CLIENT_SECRETS_FILE,
|
|
||||||
scope=YOUTUBE_UPLOAD_SCOPE,
|
|
||||||
message=MISSING_CLIENT_SECRETS_MESSAGE)
|
|
||||||
|
|
||||||
storage = Storage("%s-oauth2.json" % sys.argv[0])
|
|
||||||
credentials = storage.get()
|
|
||||||
|
|
||||||
if credentials is None or credentials.invalid:
|
|
||||||
credentials = run_flow(flow, storage, args)
|
|
||||||
|
|
||||||
return build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION,
|
|
||||||
http=credentials.authorize(httplib2.Http()))
|
|
||||||
|
|
||||||
def initialize_upload(youtube, options):
|
|
||||||
tags = None
|
|
||||||
if options.keywords:
|
|
||||||
tags = options.keywords.split(",")
|
|
||||||
|
|
||||||
body=dict(
|
|
||||||
snippet=dict(
|
|
||||||
title=options.title,
|
|
||||||
description=options.description,
|
|
||||||
tags=tags,
|
|
||||||
categoryId=options.category
|
|
||||||
),
|
|
||||||
status=dict(
|
|
||||||
privacyStatus=options.privacyStatus
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Call the API's videos.insert method to create and upload the video.
|
|
||||||
insert_request = youtube.videos().insert(
|
|
||||||
part=",".join(body.keys()),
|
|
||||||
body=body,
|
|
||||||
# The chunksize parameter specifies the size of each chunk of data, in
|
|
||||||
# bytes, that will be uploaded at a time. Set a higher value for
|
|
||||||
# reliable connections as fewer chunks lead to faster uploads. Set a lower
|
|
||||||
# value for better recovery on less reliable connections.
|
|
||||||
#
|
|
||||||
# Setting "chunksize" equal to -1 in the code below means that the entire
|
|
||||||
# file will be uploaded in a single HTTP request. (If the upload fails,
|
|
||||||
# it will still be retried where it left off.) This is usually a best
|
|
||||||
# practice, but if you're using Python older than 2.6 or if you're
|
|
||||||
# running on App Engine, you should set the chunksize to something like
|
|
||||||
# 1024 * 1024 (1 megabyte).
|
|
||||||
media_body=MediaFileUpload(options.file, chunksize=-1, resumable=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
resumable_upload(insert_request)
|
|
||||||
|
|
||||||
# This method implements an exponential backoff strategy to resume a
|
|
||||||
# failed upload.
|
|
||||||
def resumable_upload(insert_request):
|
|
||||||
response = None
|
|
||||||
error = None
|
|
||||||
retry = 0
|
|
||||||
while response is None:
|
|
||||||
try:
|
|
||||||
print "Uploading file..."
|
|
||||||
status, response = insert_request.next_chunk()
|
|
||||||
if response is not None:
|
|
||||||
if 'id' in response:
|
|
||||||
print "Video id '%s' was successfully uploaded." % response['id']
|
|
||||||
else:
|
|
||||||
exit("The upload failed with an unexpected response: %s" % response)
|
|
||||||
except HttpError, e:
|
|
||||||
if e.resp.status in RETRIABLE_STATUS_CODES:
|
|
||||||
error = "A retriable HTTP error %d occurred:\n%s" % (e.resp.status,
|
|
||||||
e.content)
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
except RETRIABLE_EXCEPTIONS, e:
|
|
||||||
error = "A retriable error occurred: %s" % e
|
|
||||||
|
|
||||||
if error is not None:
|
|
||||||
print error
|
|
||||||
retry += 1
|
|
||||||
if retry > MAX_RETRIES:
|
|
||||||
exit("No longer attempting to retry.")
|
|
||||||
|
|
||||||
max_sleep = 2 ** retry
|
|
||||||
sleep_seconds = random.random() * max_sleep
|
|
||||||
print "Sleeping %f seconds and then retrying..." % sleep_seconds
|
|
||||||
time.sleep(sleep_seconds)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
argparser.add_argument("--file", required=True, help="Video file to upload")
|
|
||||||
argparser.add_argument("--title", help="Video title", default="Test Title")
|
|
||||||
argparser.add_argument("--description", help="Video description",
|
|
||||||
default="Test Description")
|
|
||||||
argparser.add_argument("--category", default="22",
|
|
||||||
help="Numeric video category. " +
|
|
||||||
"See https://developers.google.com/youtube/v3/docs/videoCategories/list")
|
|
||||||
argparser.add_argument("--keywords", help="Video keywords, comma separated",
|
|
||||||
default="")
|
|
||||||
argparser.add_argument("--privacyStatus", choices=VALID_PRIVACY_STATUSES,
|
|
||||||
default=VALID_PRIVACY_STATUSES[0], help="Video privacy status.")
|
|
||||||
args = argparser.parse_args()
|
|
||||||
|
|
||||||
if not os.path.exists(args.file):
|
|
||||||
exit("Please specify a valid file using the --file= parameter.")
|
|
||||||
|
|
||||||
youtube = get_authenticated_service(args)
|
|
||||||
try:
|
|
||||||
initialize_upload(youtube, args)
|
|
||||||
except HttpError, e:
|
|
||||||
print "An HTTP error %d occurred:\n%s" % (e.resp.status, e.content)
|
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
python2 upload.py --file output.mp4 --title="TikTok Test" && rm -rf trend && rm *.mp4
|
poetry run python main.py -l ../cookies.json -t "TikTok Compilation" -d "Best of TikTok" -B firefox "$(pwd)/../output.mp4" &&
|
||||||
|
rm -rf trend && rm *.mp4
|
||||||
Reference in New Issue
Block a user