fix and working Clips downloader/uploader

This commit is contained in:
2026-07-17 23:08:26 -04:00
parent 8be00d16d1
commit 483a771575
5 changed files with 101 additions and 21 deletions
+21
View File
@@ -0,0 +1,21 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python Debugger: Python File",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"args": [
"--file", "my_video.mp4",
"--title", "My Automated Video Title",
"--category", "SCIENCE_AND_TECHNOLOGY",
"--description", "Testing out the Data API!",
"--privacy", "private"
]
}
]
}
+4 -3
View File
@@ -37,7 +37,8 @@ class Database:
clip_by TEXT NOT NULL, clip_by TEXT NOT NULL,
view_count INTEGER NOT NULL, view_count INTEGER NOT NULL,
downloaded INTEGER NOT NULL, downloaded INTEGER NOT NULL,
uploaded_yt INTEGER NOT NULL uploaded_yt INTEGER NOT NULL,
shorts_upload_yt INTEGER NOT NULL
) )
""" """
) )
@@ -71,11 +72,11 @@ class Database:
if self.table == "clips": if self.table == "clips":
id = "slug" id = "slug"
CURSOR.execute( self.CURSOR.execute(
f"UPDATE {self.table} SET uploaded_yt = 1 WHERE {id} = ?", f"UPDATE {self.table} SET uploaded_yt = 1 WHERE {id} = ?",
(record_id,) (record_id,)
) )
CONN.commit() self.CONN.commit()
def mark_as_downloaded(self, record_id: int): def mark_as_downloaded(self, record_id: int):
"""Updates the downloaded status to True (1) for a specific record ID.""" """Updates the downloaded status to True (1) for a specific record ID."""
+10 -8
View File
@@ -24,21 +24,21 @@ def run_linux_command(command: str):
def download_clips(): def download_clips():
"""Loops over undownloaded metadata entries to write files down locally.""" """Loops over undownloaded metadata entries to write files down locally."""
undownloaded_clips = get_undownloaded_clips() undownloaded_clips = DB.get_undownloaded()
print("Downloading Clips...") print("Downloading Clips...")
for slug, record_date, title, game_name, clip_by, views, downloaded in undownloaded_clips: for slug, record_date, title, game_name, clip_by, views, downloaded, uploaded_yt in undownloaded_clips:
print(f"Slug: {slug} | Date: {record_date} | Game: {game_name} | By: {clip_by} | Views: {views} | Title: {title}") print(f"Slug: {slug} | Date: {record_date} | Game: {game_name} | By: {clip_by} | Views: {views} | Title: {title}")
# Uses standard clipdownload directive # Uses standard clipdownload directive
output = run_linux_command(f"TwitchDownloaderCLI clipdownload --id {slug} -o save/clips/{slug}/{title}.mp4") output = run_linux_command(f"TwitchDownloaderCLI clipdownload --id {slug} -o save/clips/{slug}/{slug}.mp4")
if output["success"] is True: if output["success"] is True:
print(f"Slug: {slug} | Was successfully downloaded.") print(f"Slug: {slug} | Was successfully downloaded.")
DB.mark_as_downloaded(slug) DB.mark_as_downloaded(slug)
else: else:
print(f"Slug: {slug} | Download Process failed.") print(f"Slug: {slug} | Download Process failed. {output["stdout"]}. Error: {output["stderr"]}")
print("Finished Downloading Clips...") print("Finished Downloading Clips...")
@@ -46,19 +46,21 @@ def upload_clips():
"""Loops over the downloaded videos entries and uploaded them to youtube.""" """Loops over the downloaded videos entries and uploaded them to youtube."""
print("Uploading Clips...") print("Uploading Clips...")
unpuloaded_clips = get_unuploaded_clips() unpuloaded_clips = DB.get_unuploaded()
categoryId = CategoryId.GAMING
for slug, record_date, title, game_name, clip_by, views, downloaded, uploaded_yt in unpuloaded_clips: for slug, record_date, title, game_name, clip_by, views, downloaded, uploaded_yt in unpuloaded_clips:
print(f"Slug: {slug} | Date: {record_date} | Game: {game_name} | By: {clip_by} | Views: {views} | Title: {title}") print(f"Slug: {slug} | Date: {record_date} | Game: {game_name} | By: {clip_by} | Views: {views} | Title: {title}")
file_path = f"save/clips/{slug}/{slug}.mp4" file_path = f"save/clips/{slug}/{slug}.mp4"
title = title title = title
description = f"Game: {game_name}, Cliped By: {clip_by}, on {record_date}, #Shorts" description = f"Game: {game_name}, Cliped By: {clip_by}, on {record_date}, #Shorts #Clips #Twitch"
categoryId = CategoryId.GAMING categoryId = CategoryId.GAMING
privatcyStatus = 'private' privatcyStatus = 'private'
tags = ['shorts', 'gaming', 'TeamPGP', f'{game_name}', f'{clip_by}'] tags = ['shorts', 'gaming', 'TeamPGP', f'{game_name}', f'{clip_by}', 'twitch_clips', 'clips']
output = uploader.upload_video(file_path, title, description, categoryId, privatcyStatus, tags) output = uploader.upload_video(file_path, title, categoryId, description, privatcyStatus, tags)
if output is True: if output is True:
print(f"Slug: {slug} | Was successfully uploaded.") print(f"Slug: {slug} | Was successfully uploaded.")
+23 -10
View File
@@ -86,10 +86,6 @@ def upload_video(file_path: str, title: str, category: CategoryId, description:
if not credentials: if not credentials:
return False return False
# Format release time to ISO 8601 UTC format (Required by YouTube)
# Example format generated: "2026-07-20T15:00:00Z"
publish_at_iso = release_time.strftime('%Y-%m-%dT%H:%M:%S.000Z')
# Create YouTube API client # Create YouTube API client
youtube = build('youtube', 'v3', credentials=credentials) youtube = build('youtube', 'v3', credentials=credentials)
@@ -98,7 +94,10 @@ def upload_video(file_path: str, title: str, category: CategoryId, description:
'selfDeclaredMadeForKids': False 'selfDeclaredMadeForKids': False
} }
if release_time: if release_time is not None:
if release_time.tzinfo is None:
release_time = release_time.replace(tzinfo=timezone.utc)
# If release_time is passed, YouTube forces privacyStatus to 'private' # If release_time is passed, YouTube forces privacyStatus to 'private'
status_body['privacyStatus'] = 'private' status_body['privacyStatus'] = 'private'
status_body['publishAt'] = release_time.strftime('%Y-%m-%dT%H:%M:%S.000Z') status_body['publishAt'] = release_time.strftime('%Y-%m-%dT%H:%M:%S.000Z')
@@ -119,7 +118,6 @@ def upload_video(file_path: str, title: str, category: CategoryId, description:
'status': status_body 'status': status_body
} }
# Create media file upload # Create media file upload
media = MediaFileUpload( media = MediaFileUpload(
file_path, file_path,
@@ -162,12 +160,27 @@ def upload_video(file_path: str, title: str, category: CategoryId, description:
return False return False
def main(): def main():
# FIXED: Reconstructed arguments to provide all required inputs for upload_video
parser = argparse.ArgumentParser(description='Upload a video to YouTube') parser = argparse.ArgumentParser(description='Upload a video to YouTube')
parser.add_argument('file', help='Path to the video file') parser.add_argument('--file', required=True, help='Path to the video file')
parser.add_argument('description', help='Video description') parser.add_argument('--title', required=True, help='Title of the video')
parser.add_argument('--category', default='PEOPLE_AND_BLOGS', choices=[c.name for c in CategoryId], help='Video category genre')
parser.add_argument('--description', default='', help='Video description text')
parser.add_argument('--privacy', default='private', choices=['public', 'private', 'unlisted'], help='Video privacy settings')
args = parser.parse_args() args = parser.parse_args()
upload_video(args.file, args.description)
# Map the text input choice back to your CategoryId Enum object
chosen_category = CategoryId[args.category]
# FIXED: Properly pass all required positional and optional parameters
upload_video(
file_path=args.file,
title=args.title,
category=chosen_category,
description=args.description,
privacyStatus=args.privacy
)
if __name__ == "__main__": if __name__ == "__main__":
main() main()
+43
View File
@@ -0,0 +1,43 @@
import sys
from moviepy.editor import VideoFileClip
def convert_to_short(input_path, output_path="youtube_short.mp4"):
# Load the video file
clip = VideoFileClip(input_path)
# 1. Check video duration
if clip.duration >= 60:
print(f"❌ Error: Video is {clip.duration:.2f}s long. It must be under 60 seconds.")
clip.close()
return False
print(f"✅ Video length check passed ({clip.duration:.2f}s). Processing...")
# 2. Calculate the target width for a 9:16 aspect ratio based on height
target_height = clip.h
target_width = int(target_height * (9 / 16))
# 3. Crop the center of the video
# x_center centers the crop horizontally
short_clip = clip.crop(width=target_width, height=target_height, x_center=clip.w / 2, y_center=clip.h / 2)
# Optional: Resize to standard YouTube Short resolution (1080x1920)
if short_clip.h != 1920:
short_clip = short_clip.resize(newsize=(1080, 1920))
# 4. Export the final video file
short_clip.write_videofile(
output_path,
codec="libx264",
audio_codec="aac",
fps=clip.fps
)
# Clean up files
clip.close()
short_clip.close()
print(f"🎉 Success! Short saved as: {output_path}")
return True
if __name__ == "__main__":
convert_to_short()