From afe6490d20158b03f2ea13d5947a71ac423e102d Mon Sep 17 00:00:00 2001 From: SumGuyV5 Date: Sun, 13 Sep 2026 16:01:40 +0000 Subject: [PATCH] moved youtube scirpts into there own folder --- delete/twitch_down_uploader.py | 8 ++-- delete/twitch_download_clips.py | 8 ++-- package/__init__.py | 2 +- package/build_videos.py | 8 ++-- package/twitch/__init__.py | 6 +-- package/twitch/chat_vod.py | 3 ++ package/twitch/download_thumbnails.py | 4 +- package/twitch/download_videos.py | 7 +-- package/twitch/video_info.py | 10 ++--- package/youtube/__init__.py | 15 +++++++ package/youtube/config.py | 36 +++++++++++++++ .../youtube/find_tags.py | 45 +++++-------------- .../youtube/hashtags.py | 34 ++++++++++++-- youtube_short.py => package/youtube/shorts.py | 1 - uploader.py => package/youtube/uploader.py | 42 +++-------------- 15 files changed, 127 insertions(+), 102 deletions(-) create mode 100644 package/youtube/__init__.py create mode 100644 package/youtube/config.py rename find_youtube_tags.py => package/youtube/find_tags.py (77%) rename youtube_hashtags.py => package/youtube/hashtags.py (76%) rename youtube_short.py => package/youtube/shorts.py (99%) rename uploader.py => package/youtube/uploader.py (82%) diff --git a/delete/twitch_down_uploader.py b/delete/twitch_down_uploader.py index 5ccd47f..8fd9bbb 100755 --- a/delete/twitch_down_uploader.py +++ b/delete/twitch_down_uploader.py @@ -6,10 +6,10 @@ import time from package.database import Database -import uploader -from uploader import CategoryId +import package.youtube.uploader as uploader +from package.youtube.uploader import CategoryId -import youtube_hashtags +import package.youtube.hashtags as hashtags import package.transcribe_video as transcribe_video CHANNEL_NAME = 'teampgp' # Replace with the streamer's username @@ -43,7 +43,7 @@ def top_hashtags(id: str): print(f"Transcribe file not found {file_srt}") return [] - tags = youtube_hashtags.get_top_hashtags(file_srt) + tags = hashtags.get_top_hashtags(file_srt) return tags def download(): diff --git a/delete/twitch_download_clips.py b/delete/twitch_download_clips.py index 96339a7..ec8a0fa 100644 --- a/delete/twitch_download_clips.py +++ b/delete/twitch_download_clips.py @@ -5,8 +5,8 @@ from datetime import datetime from package.database import Database -import uploader -from uploader import CategoryId +import package.youtube.uploader as uploader +from package.youtube.uploader import CategoryId CHANNEL_NAME = 'teampgp' # Replace with the streamer's username DB = Database("clips") @@ -49,9 +49,9 @@ def top_hashtags(slug: str): print(f"Transcribe file not found {file_srt}") return [] - import youtube_hashtags + import package.youtube.hashtags as hashtags - return youtube_hashtags.get_top_hashtags(file_srt) + return hashtags.get_top_hashtags(file_srt) def download_clips(): diff --git a/package/__init__.py b/package/__init__.py index 5979208..11e3d4d 100644 --- a/package/__init__.py +++ b/package/__init__.py @@ -14,7 +14,7 @@ os.makedirs(CACHE_DIR, exist_ok=True) # 3. Expose Database Tools (Added) # Replace 'DatabaseClient' and 'connect_to_db' with your actual class/function names -from .build_videos import main as build_videos +from .build_videos import build_videos from .config import CHANNEL_NAME, DB from .database import Database from .transcribe_video import transcribe_to_srt diff --git a/package/build_videos.py b/package/build_videos.py index 3b17f18..4b8b48e 100644 --- a/package/build_videos.py +++ b/package/build_videos.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -import youtube_short +import package.youtube.shorts as yt_shorts import package.transcribe_video as transcribe_video import package.twitch.chat_vod as chat_vod from package.config import DB as DB @@ -108,16 +108,16 @@ def build_shorts(): print(f"🚀 Processing: Clip to Youtube Short {title}") print("====================================================") - youtube_short.fit_to_9_16_letterbox(target_dir, top_txt, f"Clipped By: {creator_name}.") + yt_shorts.fit_to_9_16_letterbox(target_dir, top_txt, f"Clipped By: {creator_name}.") print("====================================================") print(f"✅ Processing: Clip to Youtube Short Done.") print("====================================================") -def main(): +def build_videos(): build_shorts() build_transcribe() build_chat_video() if __name__ == "__main__": - main() \ No newline at end of file + build_videos() \ No newline at end of file diff --git a/package/twitch/__init__.py b/package/twitch/__init__.py index 93975c7..3bdcb8f 100644 --- a/package/twitch/__init__.py +++ b/package/twitch/__init__.py @@ -2,9 +2,9 @@ # Expose components from individual modules using relative imports from .chat_vod import combine_twitch_vod_and_chat -from .download_thumbnails import main as download_thumbnails -from .download_videos import main as download_videos -from .video_info import main as video_info +from .download_thumbnails import download_thumbnails +from .download_videos import download_videos +from .video_info import video_info # Explicitly define what is exposed when importing the subpackage __all__ = [ diff --git a/package/twitch/chat_vod.py b/package/twitch/chat_vod.py index 752b386..5959306 100644 --- a/package/twitch/chat_vod.py +++ b/package/twitch/chat_vod.py @@ -4,6 +4,9 @@ import cv2 import package.utils as utils def get_video_height(video_path: str) -> int: + """ + Gets the videos height + """ # Open the video file video = cv2.VideoCapture(video_path) diff --git a/package/twitch/download_thumbnails.py b/package/twitch/download_thumbnails.py index 8787f1f..4a8833c 100644 --- a/package/twitch/download_thumbnails.py +++ b/package/twitch/download_thumbnails.py @@ -80,7 +80,7 @@ def save_image(url: str, filename: str, stream: bool = True): except Exception as e: print(f"❌ An error occurred during file writing: {e}") -async def main(): +async def download_thumbnails(): # Initialize connection & automatically authorize the App token await config.get_twitch() twitch = config.TWITCH # Twitch(APP_ID, APP_SECRET) @@ -141,4 +141,4 @@ async def main(): await download_clip_thumbnail(id, thumbnail_url) if __name__ == '__main__': - asyncio.run(main()) + asyncio.run(download_thumbnails()) diff --git a/package/twitch/download_videos.py b/package/twitch/download_videos.py index 7db60ab..4b57cfc 100755 --- a/package/twitch/download_videos.py +++ b/package/twitch/download_videos.py @@ -14,7 +14,7 @@ def write_csv(data, file_name): # Write all rows at once writer.writerows(data) -def download(): +def download_videos(): """Find all undownloaded videos and clips, and download them safely.""" undownloaded = DB.get_undownloaded() @@ -96,8 +96,5 @@ def download(): print("Finished Downloading Pipeline.") -def main(): - download() - if __name__ == "__main__": - main() \ No newline at end of file + download_videos() \ No newline at end of file diff --git a/package/twitch/video_info.py b/package/twitch/video_info.py index 4b9df06..9b2b8e4 100755 --- a/package/twitch/video_info.py +++ b/package/twitch/video_info.py @@ -40,7 +40,7 @@ async def get_game_name_by_id(game_id: str) -> str: pass return "Unknown Game" -async def get_vod_game_name(vod_id: str): +async def get_vod_game_name(vod_id: str) -> tuple: """Asynchronously query Twitch GQL endpoint for VOD game metadata.""" game_id = "0" game_name = "Unknown Game" @@ -83,7 +83,7 @@ async def get_vod_game_name(vod_id: str): return game_id, game_name -async def get_streamer_vods(): +async def get_streamer_vods() -> list: await config.get_twitch() print(f"Starting VOD extraction for {config.USER.display_name}...") @@ -121,7 +121,7 @@ async def get_streamer_vods(): print(f"\nFinished extracting VODs. Total gathered: {len(all_vods)}") return all_vods -async def get_streamer_clips(): +async def get_streamer_clips() -> list: await config.get_twitch() print(f"Starting clip extraction for {config.USER.display_name}...") @@ -151,7 +151,7 @@ async def get_streamer_clips(): print(f"\nFinished extracting clips. Total gathered: {len(all_clips)}") return all_clips -async def main(): +async def video_info(): print("--- Script Started ---") vods_list = await get_streamer_vods() @@ -179,4 +179,4 @@ async def main(): print(f"Recent VOD: '{clips_list[0]['title']}'") if __name__ == "__main__": - asyncio.run(main()) + asyncio.run(video_info()) diff --git a/package/youtube/__init__.py b/package/youtube/__init__.py new file mode 100644 index 0000000..1b8c2e9 --- /dev/null +++ b/package/youtube/__init__.py @@ -0,0 +1,15 @@ +# package/youtube/__init__.py + +# Expose components from individual modules using relative imports +from .find_tags import find_tags +from .hashtags import get_top_nouns +from .shorts import fit_to_9_16_letterbox +from .uploader import upload_video, CategoryId + +# Explicitly define what is exposed when importing the subpackage +__all__ = [ + "find_tags", + "get_top_nouns", + "fit_to_9_16_letterbox", + "upload_video", +] diff --git a/package/youtube/config.py b/package/youtube/config.py new file mode 100644 index 0000000..1c4269b --- /dev/null +++ b/package/youtube/config.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +import json + +from google.oauth2.credentials import Credentials +from google.auth.transport.requests import Request + +CREDENTIALS = None + +def load_credentials() -> Credentials: + """Load credentials from secrets.json""" + try: + with open('secrets.json', 'r') as f: + creds_data = json.load(f) + + credentials = Credentials( + token=creds_data['token'], + refresh_token=creds_data['refresh_token'], + token_uri=creds_data['token_uri'], + client_id=creds_data['client_id'], + client_secret=creds_data['client_secret'], + scopes=creds_data['scopes'] + ) + + # Refresh token if expired + if credentials.expired: + credentials.refresh(Request()) + + return credentials + except FileNotFoundError: + print("Error: secrets.json not found! Please run setup.py first.") + return None + except Exception as e: + print(f"Error loading credentials: {str(e)}") + return None + +CREDENTIALS = load_credentials() \ No newline at end of file diff --git a/find_youtube_tags.py b/package/youtube/find_tags.py similarity index 77% rename from find_youtube_tags.py rename to package/youtube/find_tags.py index 7354330..8fdb315 100755 --- a/find_youtube_tags.py +++ b/package/youtube/find_tags.py @@ -1,37 +1,12 @@ #!/usr/bin/env python3 -import json -from google.oauth2.credentials import Credentials -from google.auth.transport.requests import Request +from typing import Any + from googleapiclient.discovery import build from googleapiclient.errors import HttpError -def load_credentials(): - """Load credentials from secrets.json (Reused from your previous flow)""" - try: - with open('secrets.json', 'r') as f: - creds_data = json.load(f) - - credentials = Credentials( - token=creds_data['token'], - refresh_token=creds_data['refresh_token'], - token_uri=creds_data['token_uri'], - client_id=creds_data['client_id'], - client_secret=creds_data['client_secret'], - scopes=creds_data['scopes'] - ) - - if credentials.expired: - credentials.refresh(Request()) - - return credentials - except FileNotFoundError: - print("Error: secrets.json not found!") - return None - except Exception as e: - print(f"Error loading credentials: {str(e)}") - return None +import package.youtube.config as config -def get_my_uploads_playlist_id(youtube): +def get_my_uploads_playlist_id(youtube: build) -> Any: """Retrieves the system upload playlist ID for the authenticated user's channel.""" try: # mine=True automatically references the authorized account @@ -47,7 +22,7 @@ def get_my_uploads_playlist_id(youtube): print(f"API Error retrieving channel details: {str(e)}") return None -def scan_channel_videos_for_tag(youtube, uploads_playlist_id: str, target_tag: str): +def scan_channel_videos_for_tag(youtube: build, uploads_playlist_id: str, target_tag: str) -> list: """ Loops through all channel video uploads and filters those possessing the target tag. """ @@ -120,12 +95,12 @@ def scan_channel_videos_for_tag(youtube, uploads_playlist_id: str, target_tag: s return matched_videos -def main(): - credentials = load_credentials() - if not credentials: +def find_tags(): + + if not config.CREDENTIALS: return - youtube = build('youtube', 'v3', credentials=credentials) + youtube = build('youtube', 'v3', credentials=config.CREDENTIALS) # 1. Fetch your dynamic uploads playlist pointer uploads_id = get_my_uploads_playlist_id(youtube) @@ -144,4 +119,4 @@ def main(): print(f"{index}. {item['title']} -> https://youtu.be{item['id']}") if __name__ == "__main__": - main() + find_tags() diff --git a/youtube_hashtags.py b/package/youtube/hashtags.py similarity index 76% rename from youtube_hashtags.py rename to package/youtube/hashtags.py index 36cc414..a510d20 100755 --- a/youtube_hashtags.py +++ b/package/youtube/hashtags.py @@ -14,7 +14,16 @@ nltk.download('punkt_tab', quiet=True) nltk.download('averaged_perceptron_tagger', quiet=True) # Required for POS tagging nltk.download('averaged_perceptron_tagger_eng', quiet=True) -def extract_text_from_srt(file_path: str): +def extract_text_from_srt(file_path: str) -> str: + """ + Extracts the text from the srt file. + + Args: + file_path (str): is the path to the str file. + + Returns: + returns (str): returns the cleaned up text. + """ with open(file_path, 'r', encoding='utf-8') as file: content = file.read() # Remove SRT timestamps and sequence numbers @@ -22,7 +31,16 @@ def extract_text_from_srt(file_path: str): clean_text = re.sub(r'\d+', '', clean_text) return clean_text -def extract_text_from_json(file_path: str): +def extract_text_from_json(file_path: str) -> str: + """ + Extracts the text from the json file. + + Args: + file_path (str): is the path to the json file. + + Returns: + returns (str): returns the cleaned up text. + """ with open(file_path, "r", encoding="utf-8") as file: data = json.load(file) @@ -34,7 +52,17 @@ def extract_text_from_json(file_path: str): # Return a single merged string of all chat text return " ".join(messages) -def get_top_nouns(file_path: str, top_n: int = 10): +def get_top_nouns(file_path: str, top_n: int = 10) -> list: + """ + Gets the top number of nouns based on past (top_n) arg. + + Args: + file_path (str): is the path to the text file. + top_n (int): is the number of nouns to return. + + Returns: + returns (list): returns a list of nouns. + """ # 1. Extract raw text if file_path.endswith(".srt"): raw_text = extract_text_from_srt(file_path) diff --git a/youtube_short.py b/package/youtube/shorts.py similarity index 99% rename from youtube_short.py rename to package/youtube/shorts.py index 9f9d957..d690120 100755 --- a/youtube_short.py +++ b/package/youtube/shorts.py @@ -126,7 +126,6 @@ def fit_to_9_16_letterbox(input_path: str, top_text: str = "TOP TEXT", bottom_te if os.path.exists(path): os.unlink(path) - if __name__ == "__main__": import time diff --git a/uploader.py b/package/youtube/uploader.py similarity index 82% rename from uploader.py rename to package/youtube/uploader.py index e6f718e..fd03ab0 100755 --- a/uploader.py +++ b/package/youtube/uploader.py @@ -1,16 +1,15 @@ #!/usr/bin/env python3 import os -import json import argparse from enum import Enum from datetime import datetime -from google.oauth2.credentials import Credentials -from google_auth_oauthlib.flow import InstalledAppFlow -from google.auth.transport.requests import Request + from googleapiclient.discovery import build from googleapiclient.http import MediaFileUpload from googleapiclient.errors import HttpError +import package.youtube.config as config + class CategoryId(Enum): """Official YouTube Category IDs for API Uploads.""" @@ -30,34 +29,7 @@ class CategoryId(Enum): SCIENCE_AND_TECHNOLOGY = "28" NONPROFITS_AND_ACTIVISM = "29" -def load_credentials(): - """Load credentials from secrets.json""" - try: - with open('secrets.json', 'r') as f: - creds_data = json.load(f) - - credentials = Credentials( - token=creds_data['token'], - refresh_token=creds_data['refresh_token'], - token_uri=creds_data['token_uri'], - client_id=creds_data['client_id'], - client_secret=creds_data['client_secret'], - scopes=creds_data['scopes'] - ) - - # Refresh token if expired - if credentials.expired: - credentials.refresh(Request()) - - return credentials - except FileNotFoundError: - print("Error: secrets.json not found! Please run setup.py first.") - return None - except Exception as e: - print(f"Error loading credentials: {str(e)}") - return None - -def upload_video(file_path: str, title: str, category: CategoryId, description: str = "", privacyStatus: str = 'private', tags: list = None, release_time: datetime = None): +def upload_video(file_path: str, title: str, category: CategoryId, description: str = "", privacyStatus: str = 'private', tags: list = None, release_time: datetime = None) -> bool: """ Upload a video to YouTube @@ -82,12 +54,11 @@ def upload_video(file_path: str, title: str, category: CategoryId, description: return False # Load credentials - credentials = load_credentials() - if not credentials: + if not config.CREDENTIALS: return False # Create YouTube API client - youtube = build('youtube', 'v3', credentials=credentials) + youtube = build('youtube', 'v3', credentials=config.CREDENTIALS) # Configure the status object dynamically status_body = { @@ -197,5 +168,6 @@ def main(): tags=parsed_tags, release_time=release_datetime ) + if __name__ == "__main__": main() \ No newline at end of file