Compare commits

..
2 Commits
Author SHA1 Message Date
SumGuyV5 afe6490d20 moved youtube scirpts into there own folder 2026-09-13 16:01:40 +00:00
SumGuyV5 8bf85fea2e update requirements.txt 2026-09-11 21:51:45 +00:00
16 changed files with 150 additions and 102 deletions

No files matched your search

+4 -4
View File
@@ -6,10 +6,10 @@ import time
from package.database import Database from package.database import Database
import uploader import package.youtube.uploader as uploader
from uploader import CategoryId from package.youtube.uploader import CategoryId
import youtube_hashtags import package.youtube.hashtags as hashtags
import package.transcribe_video as transcribe_video import package.transcribe_video as transcribe_video
CHANNEL_NAME = 'teampgp' # Replace with the streamer's username 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}") print(f"Transcribe file not found {file_srt}")
return [] return []
tags = youtube_hashtags.get_top_hashtags(file_srt) tags = hashtags.get_top_hashtags(file_srt)
return tags return tags
def download(): def download():
+4 -4
View File
@@ -5,8 +5,8 @@ from datetime import datetime
from package.database import Database from package.database import Database
import uploader import package.youtube.uploader as uploader
from uploader import CategoryId from package.youtube.uploader import CategoryId
CHANNEL_NAME = 'teampgp' # Replace with the streamer's username CHANNEL_NAME = 'teampgp' # Replace with the streamer's username
DB = Database("clips") DB = Database("clips")
@@ -49,9 +49,9 @@ def top_hashtags(slug: str):
print(f"Transcribe file not found {file_srt}") print(f"Transcribe file not found {file_srt}")
return [] 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(): def download_clips():
+1 -1
View File
@@ -14,7 +14,7 @@ os.makedirs(CACHE_DIR, exist_ok=True)
# 3. Expose Database Tools (Added) # 3. Expose Database Tools (Added)
# Replace 'DatabaseClient' and 'connect_to_db' with your actual class/function names # 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 .config import CHANNEL_NAME, DB
from .database import Database from .database import Database
from .transcribe_video import transcribe_to_srt from .transcribe_video import transcribe_to_srt
+4 -4
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import youtube_short import package.youtube.shorts as yt_shorts
import package.transcribe_video as transcribe_video import package.transcribe_video as transcribe_video
import package.twitch.chat_vod as chat_vod import package.twitch.chat_vod as chat_vod
from package.config import DB as DB from package.config import DB as DB
@@ -108,16 +108,16 @@ def build_shorts():
print(f"🚀 Processing: Clip to Youtube Short {title}") print(f"🚀 Processing: Clip to Youtube Short {title}")
print("====================================================") 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("====================================================")
print(f"✅ Processing: Clip to Youtube Short Done.") print(f"✅ Processing: Clip to Youtube Short Done.")
print("====================================================") print("====================================================")
def main(): def build_videos():
build_shorts() build_shorts()
build_transcribe() build_transcribe()
build_chat_video() build_chat_video()
if __name__ == "__main__": if __name__ == "__main__":
main() build_videos()
+3 -3
View File
@@ -2,9 +2,9 @@
# Expose components from individual modules using relative imports # Expose components from individual modules using relative imports
from .chat_vod import combine_twitch_vod_and_chat from .chat_vod import combine_twitch_vod_and_chat
from .download_thumbnails import main as download_thumbnails from .download_thumbnails import download_thumbnails
from .download_videos import main as download_videos from .download_videos import download_videos
from .video_info import main as video_info from .video_info import video_info
# Explicitly define what is exposed when importing the subpackage # Explicitly define what is exposed when importing the subpackage
__all__ = [ __all__ = [
+3
View File
@@ -4,6 +4,9 @@ import cv2
import package.utils as utils import package.utils as utils
def get_video_height(video_path: str) -> int: def get_video_height(video_path: str) -> int:
"""
Gets the videos height
"""
# Open the video file # Open the video file
video = cv2.VideoCapture(video_path) video = cv2.VideoCapture(video_path)
+2 -2
View File
@@ -80,7 +80,7 @@ def save_image(url: str, filename: str, stream: bool = True):
except Exception as e: except Exception as e:
print(f"❌ An error occurred during file writing: {e}") print(f"❌ An error occurred during file writing: {e}")
async def main(): async def download_thumbnails():
# Initialize connection & automatically authorize the App token # Initialize connection & automatically authorize the App token
await config.get_twitch() await config.get_twitch()
twitch = config.TWITCH # Twitch(APP_ID, APP_SECRET) twitch = config.TWITCH # Twitch(APP_ID, APP_SECRET)
@@ -141,4 +141,4 @@ async def main():
await download_clip_thumbnail(id, thumbnail_url) await download_clip_thumbnail(id, thumbnail_url)
if __name__ == '__main__': if __name__ == '__main__':
asyncio.run(main()) asyncio.run(download_thumbnails())
+2 -5
View File
@@ -14,7 +14,7 @@ def write_csv(data, file_name):
# Write all rows at once # Write all rows at once
writer.writerows(data) writer.writerows(data)
def download(): def download_videos():
"""Find all undownloaded videos and clips, and download them safely.""" """Find all undownloaded videos and clips, and download them safely."""
undownloaded = DB.get_undownloaded() undownloaded = DB.get_undownloaded()
@@ -96,8 +96,5 @@ def download():
print("Finished Downloading Pipeline.") print("Finished Downloading Pipeline.")
def main():
download()
if __name__ == "__main__": if __name__ == "__main__":
main() download_videos()
+5 -5
View File
@@ -40,7 +40,7 @@ async def get_game_name_by_id(game_id: str) -> str:
pass pass
return "Unknown Game" 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.""" """Asynchronously query Twitch GQL endpoint for VOD game metadata."""
game_id = "0" game_id = "0"
game_name = "Unknown Game" game_name = "Unknown Game"
@@ -83,7 +83,7 @@ async def get_vod_game_name(vod_id: str):
return game_id, game_name return game_id, game_name
async def get_streamer_vods(): async def get_streamer_vods() -> list:
await config.get_twitch() await config.get_twitch()
print(f"Starting VOD extraction for {config.USER.display_name}...") 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)}") print(f"\nFinished extracting VODs. Total gathered: {len(all_vods)}")
return all_vods return all_vods
async def get_streamer_clips(): async def get_streamer_clips() -> list:
await config.get_twitch() await config.get_twitch()
print(f"Starting clip extraction for {config.USER.display_name}...") 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)}") print(f"\nFinished extracting clips. Total gathered: {len(all_clips)}")
return all_clips return all_clips
async def main(): async def video_info():
print("--- Script Started ---") print("--- Script Started ---")
vods_list = await get_streamer_vods() vods_list = await get_streamer_vods()
@@ -179,4 +179,4 @@ async def main():
print(f"Recent VOD: '{clips_list[0]['title']}'") print(f"Recent VOD: '{clips_list[0]['title']}'")
if __name__ == "__main__": if __name__ == "__main__":
asyncio.run(main()) asyncio.run(video_info())
+15
View File
@@ -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",
]
+36
View File
@@ -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()
@@ -1,37 +1,12 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import json from typing import Any
from google.oauth2.credentials import Credentials
from google.auth.transport.requests import Request
from googleapiclient.discovery import build from googleapiclient.discovery import build
from googleapiclient.errors import HttpError from googleapiclient.errors import HttpError
def load_credentials(): import package.youtube.config as config
"""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
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.""" """Retrieves the system upload playlist ID for the authenticated user's channel."""
try: try:
# mine=True automatically references the authorized account # 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)}") print(f"API Error retrieving channel details: {str(e)}")
return None 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. 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 return matched_videos
def main(): def find_tags():
credentials = load_credentials()
if not credentials: if not config.CREDENTIALS:
return return
youtube = build('youtube', 'v3', credentials=credentials) youtube = build('youtube', 'v3', credentials=config.CREDENTIALS)
# 1. Fetch your dynamic uploads playlist pointer # 1. Fetch your dynamic uploads playlist pointer
uploads_id = get_my_uploads_playlist_id(youtube) uploads_id = get_my_uploads_playlist_id(youtube)
@@ -144,4 +119,4 @@ def main():
print(f"{index}. {item['title']} -> https://youtu.be{item['id']}") print(f"{index}. {item['title']} -> https://youtu.be{item['id']}")
if __name__ == "__main__": if __name__ == "__main__":
main() find_tags()
@@ -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', quiet=True) # Required for POS tagging
nltk.download('averaged_perceptron_tagger_eng', quiet=True) 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: with open(file_path, 'r', encoding='utf-8') as file:
content = file.read() content = file.read()
# Remove SRT timestamps and sequence numbers # 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) clean_text = re.sub(r'\d+', '', clean_text)
return 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: with open(file_path, "r", encoding="utf-8") as file:
data = json.load(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 a single merged string of all chat text
return " ".join(messages) 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 # 1. Extract raw text
if file_path.endswith(".srt"): if file_path.endswith(".srt"):
raw_text = extract_text_from_srt(file_path) raw_text = extract_text_from_srt(file_path)
@@ -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): if os.path.exists(path):
os.unlink(path) os.unlink(path)
if __name__ == "__main__": if __name__ == "__main__":
import time import time
+7 -35
View File
@@ -1,16 +1,15 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import json
import argparse import argparse
from enum import Enum from enum import Enum
from datetime import datetime 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.discovery import build
from googleapiclient.http import MediaFileUpload from googleapiclient.http import MediaFileUpload
from googleapiclient.errors import HttpError from googleapiclient.errors import HttpError
import package.youtube.config as config
class CategoryId(Enum): class CategoryId(Enum):
"""Official YouTube Category IDs for API Uploads.""" """Official YouTube Category IDs for API Uploads."""
@@ -30,34 +29,7 @@ class CategoryId(Enum):
SCIENCE_AND_TECHNOLOGY = "28" SCIENCE_AND_TECHNOLOGY = "28"
NONPROFITS_AND_ACTIVISM = "29" NONPROFITS_AND_ACTIVISM = "29"
def load_credentials(): def upload_video(file_path: str, title: str, category: CategoryId, description: str = "", privacyStatus: str = 'private', tags: list = None, release_time: datetime = None) -> bool:
"""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):
""" """
Upload a video to YouTube Upload a video to YouTube
@@ -82,12 +54,11 @@ def upload_video(file_path: str, title: str, category: CategoryId, description:
return False return False
# Load credentials # Load credentials
credentials = load_credentials() if not config.CREDENTIALS:
if not credentials:
return False return False
# Create YouTube API client # Create YouTube API client
youtube = build('youtube', 'v3', credentials=credentials) youtube = build('youtube', 'v3', credentials=config.CREDENTIALS)
# Configure the status object dynamically # Configure the status object dynamically
status_body = { status_body = {
@@ -197,5 +168,6 @@ def main():
tags=parsed_tags, tags=parsed_tags,
release_time=release_datetime release_time=release_datetime
) )
if __name__ == "__main__": if __name__ == "__main__":
main() main()
+23
View File
@@ -5,43 +5,66 @@ anyio==4.15.1
attrs==26.1.0 attrs==26.1.0
av==18.1.0 av==18.1.0
certifi==2026.7.22 certifi==2026.7.22
cffi==2.1.1
charset-normalizer==3.5.1 charset-normalizer==3.5.1
click==8.5.0 click==8.5.0
cloudpickle==3.1.2
cryptography==50.0.1
ctranslate2==4.8.2 ctranslate2==4.8.2
decorator==5.3.1 decorator==5.3.1
defusedxml==0.7.1
enum-tools==0.13.0 enum-tools==0.13.0
faster-whisper==1.2.1 faster-whisper==1.2.1
filelock==3.32.6 filelock==3.32.6
flatbuffers==25.12.19 flatbuffers==25.12.19
frozenlist==1.8.0 frozenlist==1.8.0
fsspec==2026.7.0 fsspec==2026.7.0
google-api-core==2.36.0
google-api-python-client==2.200.0
google-auth==2.58.0
google-auth-httplib2==0.4.2
google-auth-oauthlib==1.4.1
googleapis-common-protos==1.75.3
h11==0.16.0 h11==0.16.0
hf-xet==1.6.0 hf-xet==1.6.0
httpcore==1.0.9 httpcore==1.0.9
httplib2==0.32.0
httpx==0.28.1 httpx==0.28.1
huggingface_hub==1.31.0 huggingface_hub==1.31.0
idna==3.19 idna==3.19
ImageIO==2.37.4 ImageIO==2.37.4
imageio-ffmpeg==0.6.0 imageio-ffmpeg==0.6.0
joblib==1.6.0
moviepy==2.2.1 moviepy==2.2.1
multidict==6.8.0 multidict==6.8.0
nltk==3.10.3
numpy==2.5.3 numpy==2.5.3
oauthlib==3.3.1
onnxruntime==1.30.0 onnxruntime==1.30.0
opencv-python==5.0.0.93 opencv-python==5.0.0.93
opentelemetry-api==1.44.0
packaging==26.3 packaging==26.3
pillow==11.3.0 pillow==11.3.0
proglog==0.1.12 proglog==0.1.12
propcache==0.5.2 propcache==0.5.2
proto-plus==1.28.4
protobuf==7.36.1 protobuf==7.36.1
pyasn1==0.6.4
pyasn1_modules==0.4.2
pycparser==3.0
Pygments==2.21.0 Pygments==2.21.0
pyparsing==3.3.2
python-dateutil==2.9.0.post0 python-dateutil==2.9.0.post0
python-dotenv==1.2.3 python-dotenv==1.2.3
PyYAML==6.0.3 PyYAML==6.0.3
regex==2026.9.10
requests==2.34.2 requests==2.34.2
requests-oauthlib==2.0.0
six==1.17.0 six==1.17.0
tokenizers==0.23.2 tokenizers==0.23.2
tqdm==4.70.1 tqdm==4.70.1
twitchAPI==4.5.0 twitchAPI==4.5.0
typing_extensions==4.16.0 typing_extensions==4.16.0
uritemplate==4.2.0
urllib3==2.7.0 urllib3==2.7.0
yarl==1.24.5 yarl==1.24.5