rearange files and updateing files

This commit is contained in:
SumGuyV5 committed 2026-09-11 20:08:26 +00:00
1 parent 20acb4bc9f
commit 2de2c461e2
28 files changed
+283 -186

No files matched your search

@@ -1,16 +1,16 @@
#!/usr/bin/env python3
import requests
import os
import linux
import package.linux as linux
import time
from database import Database
from package.database import Database
import uploader
from uploader import CategoryId
import youtube_hashtags
import transcribe_video
import package.transcribe_video as transcribe_video
CHANNEL_NAME = 'teampgp' # Replace with the streamer's username
DB = None
+1 -1
View File
@@ -3,7 +3,7 @@ import requests
import subprocess
from datetime import datetime
from database import Database
from package.database import Database
import uploader
from uploader import CategoryId
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
from datetime import datetime, UTC
import package.database as database
DB = None
def has_no_timezone(dt_string: str) -> bool:
try:
# fromisoformat parses standard formats like YYYY-MM-DD HH:MM:SS
dt = datetime.fromisoformat(dt_string)
# Check if tzinfo is completely missing OR if its offset is undefined
return dt.tzinfo is None or dt.tzinfo.utcoffset(dt) is None
except ValueError:
print(f"Error: '{dt_string}' is not a valid date-time string.")
return False
def fix_time():
chats = DB.get_vods()
#chats = DB.get_clips()
for chat in chats:
(
id,
title,
created_at,
view_count,
duration,
url,
thumbnail_url,
game_id,
game_name,
stream_id,
creator_name,
clip_is,
downloaded,
uploaded_yt,
uploaded_yt_chats,
uploaded_yt_shorts,
) = chat
if "T" in created_at:
date_string = created_at.replace("T", " ")
DB.update_datetime(id, date_string)
if has_no_timezone(created_at):
dt_with_utc = datetime.fromisoformat(created_at).replace(tzinfo=UTC)
date_string = dt_with_utc.isoformat().strftime('%Y-%m-%d %H:%M:%S %z')
DB.update_datetime(id, date_string)
print("====================================================")
print(f"✅ Processing: Times Fix.")
print("====================================================")
def main():
global DB
DB = database.Database()
fix_time()
if __name__ == "__main__":
main()
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
from pathlib import Path
import package.database as database
DB = None
def fix_download():
chats = DB.get_undownloaded()
#chats = DB.get_clips()
for chat in chats:
(
id,
title,
created_at,
view_count,
duration,
url,
thumbnail_url,
game_id,
game_name,
stream_id,
creator_name,
clip_is,
downloaded,
uploaded_yt,
uploaded_yt_chats,
uploaded_yt_shorts,
) = chat
if clip_is:
file_name = Path(f"download/clips/{id}/{id}.mp4")
else:
file_name = Path(f"download/videos/{id}/{id}.mp4")
if file_name.exists():
DB.mark_as_downloaded(id)
print("====================================================")
print(f"✅ Processing: download Fix.")
print("====================================================")
def main():
global DB
DB = database.Database()
fix_download()
if __name__ == "__main__":
main()
+3 -3
View File
@@ -4,8 +4,8 @@ import json
import asyncio
from pathlib import Path
import database
import twitch_video_info
import package.database as database
import package.twitch.video_info as video_info
DB = None
def format_to_hms(total_seconds):
@@ -44,7 +44,7 @@ def read_json(file_path: str):
video_length = format_to_hms(video_length_seconds)
game_id = asyncio.run(twitch_video_info.get_game_id_by_name(video_game))
game_id = asyncio.run(video_info.get_game_id_by_name(video_game))
DB.insert_video_record(
video_id,
File renamed without changes.
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
import database
import package.database as database
from pathlib import Path
DB = None
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
import database
import package.database as database
DB = None
@@ -1,6 +1,6 @@
import os
import database
import package.database as database
DB = None
+8 -8
View File
@@ -1,23 +1,23 @@
#!/usr/bin/env python3
import asyncio
import twitch_video_info
import twitch_download_videos
import twitch_download_thumbnails
import package.twitch.video_info as video_info
import package.twitch.download_videos as download_videos
import package.twitch.download_thumbnails as download_thumbnails
import build_videos
import package.build_videos as build_videos
if __name__ == "__main__":
# Get Twitch Video Info
asyncio.run(twitch_video_info.main())
asyncio.run(video_info())
# Download Twitch Videos
twitch_download_videos.main()
download_videos()
twitch_download_thumbnails.main()
download_thumbnails()
#Build
build_videos.main()
build_videos()
print("====================================================")
print(f"✅ Done.")
+35
View File
@@ -0,0 +1,35 @@
# package/__init__.py
import os
# 1. Package Metadata
__version__ = "1.0.0"
__author__ = "Richard Allen"
# 2. Initialization Logic
print("Initializing package...")
# Ensure a required directory exists when the package loads
CACHE_DIR = os.path.join(os.path.expanduser("~"), ".package_cache")
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 .config import CHANNEL_NAME, DB
from .database import Database
from .transcribe_video import transcribe_to_srt
from .utils import run_command
# 4. Expose Subpackages
from . import twitch
# (Optional) Defines what is exposed during 'from package import *'
__all__ = [
"build_videos",
"CHANNEL_NAME",
"DB",
"Database",
"transcribe_to_srt",
"utils",
"twitch",
]
+4 -8
View File
@@ -1,10 +1,8 @@
#!/usr/bin/env python3
import database
import youtube_short
import transcribe_video
import twitch_chat_vod
DB = None
import package.transcribe_video as transcribe_video
import package.twitch.chat_vod as chat_vod
from package.config import DB as DB
def build_chat_video():
chats = DB.get_unuploaded_chats()
@@ -33,7 +31,7 @@ def build_chat_video():
print(f"🚀 Chat Video: {title}")
print("====================================================")
twitch_chat_vod.combine_twitch_vod_and_chat(f"download/videos/{id}/{id}.mp4", "side-by-side")
chat_vod.combine_twitch_vod_and_chat(f"download/videos/{id}/{id}.mp4", "side-by-side")
print("====================================================")
print(f"✅ Processing: Chat Video Done.")
@@ -117,8 +115,6 @@ def build_shorts():
print("====================================================")
def main():
global DB
DB = database.Database()
build_shorts()
build_transcribe()
build_chat_video()
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env python3
import package.database as database
CHANNEL_NAME = "teampgp"
DB = database.Database()
+7
View File
@@ -80,6 +80,13 @@ class Database:
"""Flags a specific row record to downloaded (0)."""
self.__mark_as(record_id, "downloaded", "0")
def update_datetime(self, record_id: str, created_at: str):
"""Updates the Datetime for timezone fix"""
self.cursor.execute(
f"UPDATE twitch_videos SET created_at = ? WHERE id = ?",
(created_at, record_id))
self.conn.commit()
def __get_unuploaded(self, set_row: str, also: str = "") -> list[Any]:
"""Retrieve all rows that were download but not uploaded"""
self.cursor.execute(f"SELECT {self.columns} FROM twitch_videos WHERE downloaded = 1 AND {set_row} = 0 {also}")
@@ -1,6 +1,5 @@
#!/usr/bin/env python3
import os
import sys
import shutil
import subprocess
from pathlib import Path
+15
View File
@@ -0,0 +1,15 @@
# package/twitch/__init__.py
# 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
# Explicitly define what is exposed when importing the subpackage
__all__ = [
"combine_twitch_vod_and_chat",
"download_thumbnails",
"download_videos",
"video_info",
]
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
import os
import cv2
import linux
import package.utils as utils
def get_video_height(video_path: str) -> int:
# Open the video file
@@ -86,12 +86,14 @@ def combine_twitch_vod_and_chat(vod_path: str, layout: str = "side-by-side", for
chat_cmd = (f"{chat_cmd} --generate-mask ")
if not os.path.exists(temp_chat):
chat_success = linux.run_command(chat_cmd, look_for=["[STATUS]"])
chat_success = utils.run_command(chat_cmd, look_for=["[STATUS]"])
if chat_success:
print("✅ Success: TwitchDownloaderCLI render chat video.")
else:
print("❌ Error: TwitchDownloaderCLI failed to render chat video.")
if os.path.exists(temp_chat):
os.remove(temp_chat)
if os.path.exists(mask_path):
os.remove(mask_path)
return False
@@ -119,7 +121,7 @@ def combine_twitch_vod_and_chat(vod_path: str, layout: str = "side-by-side", for
else:
raise ValueError("Invalid layout choice. Choose 'side-by-side' or 'overlay'.")
ffmpeg_success = linux.run_command(ffmpeg_cmd, progress_prefix="FFmpeg Merge")
ffmpeg_success = utils.run_command(ffmpeg_cmd, progress_prefix="FFmpeg Merge")
# Step 4: Final verification and cleanup
if ffmpeg_success and os.path.exists(temp_with_chat):
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env python3
import json
from twitchAPI.twitch import Twitch
from twitchAPI.helper import first
from package.config import CHANNEL_NAME as CHANNEL_NAME
# 1. Fill in your credentials from the Twitch Developer Console
SECRETS = None
TWITCH = None
USER = None
async def get_twitch():
global SECRETS, TWITCH, USER
if SECRETS is None:
with open('twitch_secrets.json', 'r') as f:
SECRETS = json.load(f)
if TWITCH is None:
TWITCH = await Twitch(SECRETS['client_id'], SECRETS['client_secret'])
if USER is None:
USER = await first(TWITCH.get_users(logins=[CHANNEL_NAME]))
if not USER:
print("User not found.")
@@ -1,30 +1,12 @@
#!/usr/bin/env python3
import os
import json
import asyncio
import requests
from twitchAPI.twitch import Twitch
from twitchAPI.helper import first
import database
# 1. Fill in your credentials from the Twitch Developer Console
SECRETS = None
TWITCH = None
USER = None
CHANNEL_NAME = "teampgp"
async def get_twitch():
global SECRETS, TWITCH, USER
if SECRETS is None:
with open('twitch_secrets.json', 'r') as f:
SECRETS = json.load(f)
if TWITCH is None:
TWITCH = await Twitch(SECRETS['client_id'], SECRETS['client_secret'])
if USER is None:
USER = await first(TWITCH.get_users(logins=[CHANNEL_NAME]))
if not USER:
print("User not found.")
import package.config as config
import package.database as database
async def download_live_thumbnail(twitch_client, streamer_username: str, w: int = 1920, h: int = 1080):
"""Fetches and saves the live stream thumbnail for an active broadcast."""
@@ -100,12 +82,12 @@ def save_image(url: str, filename: str, stream: bool = True):
async def main():
# Initialize connection & automatically authorize the App token
await get_twitch()
twitch = TWITCH # Twitch(APP_ID, APP_SECRET)
await config.get_twitch()
twitch = config.TWITCH # Twitch(APP_ID, APP_SECRET)
# --- OPTION A: Download Live Thumbnail ---
# Target user must be streaming live right now
target_streamer = CHANNEL_NAME
target_streamer = config.CHANNEL_NAME
#await download_live_thumbnail(twitch, target_streamer, 1920, 1080)
# --- OPTION B: Download Past Broadcast VOD Thumbnail ---
@@ -3,11 +3,8 @@ import csv
import subprocess
from datetime import datetime, timedelta, timezone
import linux
from database import Database
CHANNEL_NAME = 'teampgp'
DB = None
import package.utils as utils
from package.config import DB as DB
def write_csv(data, file_name):
# Open file with newline='' to prevent extra blank rows across platforms
@@ -78,8 +75,8 @@ def download():
csv_file = f"{target_dir}/{id}.csv"
# 3. FIXED: Use the live streaming function to prevent Out-Of-Memory crashes
output = linux.run_command(cmd, look_for=["[STATUS]"])
output2 = linux.run_command(f"TwitchDownloaderCLI chatdownload --id {id} -o {target_dir}/{id}_chat.json -E --collision Overwrite --temp-path download/temp", look_for=["[STATUS]"])
output = utils.run_command(cmd, look_for=["[STATUS]"])
output2 = utils.run_command(f"TwitchDownloaderCLI chatdownload --id {id} -o {target_dir}/{id}_chat.json -E --collision Overwrite --temp-path download/temp", look_for=["[STATUS]"])
if output:
# Only write CSV and update database if download actually completed
@@ -100,8 +97,6 @@ def download():
print("Finished Downloading Pipeline.")
def main():
global DB
DB = Database()
download()
if __name__ == "__main__":
@@ -1,25 +1,21 @@
#!/usr/bin/env python3
import asyncio
import json
import httpx # Switched from requests to prevent async loop freezing
import database
from twitchAPI.twitch import Twitch
from twitchAPI.helper import first
from twitchAPI.type import VideoType
SECRETS = None
TWITCH = None
USER = None
import package.twitch.config as config
from package.config import DB as DB
GAME_CACHE = {} # Local cache dictionary to store game_id -> game_name mapping
CHANNEL_NAME = "teampgp"
async def get_game_id_by_name(game_name: str) -> str:
if SECRETS is None:
await get_twitch()
if config.SECRETS is None:
await config.get_twitch()
# Query the 'get_games' endpoint using the names parameter
# The API expects a list, even for a single game name
async for game in TWITCH.get_games(names=[game_name]):
async for game in config.TWITCH.get_games(names=[game_name]):
print(f"Game Name: {game.name}")
print(f"Game ID: {game.id}")
print(f"IGDB ID: {game.igdb_id}")
@@ -28,18 +24,6 @@ async def get_game_id_by_name(game_name: str) -> str:
print(f"No game found with the exact name: '{game_name}'")
return None
async def get_twitch():
global SECRETS, TWITCH, USER
if SECRETS is None:
with open('twitch_secrets.json', 'r') as f:
SECRETS = json.load(f)
if TWITCH is None:
TWITCH = await Twitch(SECRETS['client_id'], SECRETS['client_secret'])
if USER is None:
USER = await first(TWITCH.get_users(logins=[CHANNEL_NAME]))
if not USER:
print("User not found.")
async def get_game_name_by_id(game_id: str) -> str:
"""Helper function to fetch game names and cache them locally."""
if not game_id:
@@ -47,7 +31,7 @@ async def get_game_name_by_id(game_id: str) -> str:
if game_id in GAME_CACHE:
return GAME_CACHE[game_id]
try:
game_generator = TWITCH.get_games(game_ids=[game_id])
game_generator = config.TWITCH.get_games(game_ids=[game_id])
game = await first(game_generator)
if game:
GAME_CACHE[game_id] = game.name
@@ -100,10 +84,10 @@ async def get_vod_game_name(vod_id: str):
return game_id, game_name
async def get_streamer_vods():
await get_twitch()
await config.get_twitch()
print(f"Starting VOD extraction for {USER.display_name}...")
vod_generator = TWITCH.get_videos(user_id=USER.id, first=100, video_type=VideoType.ALL)
print(f"Starting VOD extraction for {config.USER.display_name}...")
vod_generator = config.TWITCH.get_videos(user_id=config.USER.id, first=100, video_type=VideoType.ALL)
all_vods = []
@@ -128,7 +112,7 @@ async def get_streamer_vods():
"game_id": clean_game_id,
"game_name": game_name,
"stream_id": str(v.stream_id) if v.stream_id else "0",
"creator_name": CHANNEL_NAME,
"creator_name": config.CHANNEL_NAME,
"clip_is": False,
}
all_vods.append(vod_data)
@@ -138,10 +122,10 @@ async def get_streamer_vods():
return all_vods
async def get_streamer_clips():
await get_twitch()
await config.get_twitch()
print(f"Starting clip extraction for {USER.display_name}...")
clip_generator = TWITCH.get_clips(broadcaster_id=USER.id, first=100)
print(f"Starting clip extraction for {config.USER.display_name}...")
clip_generator = config.TWITCH.get_clips(broadcaster_id=config.USER.id, first=100)
all_clips = []
async for c in clip_generator:
@@ -170,11 +154,10 @@ async def get_streamer_clips():
async def main():
print("--- Script Started ---")
db = database.Database()
vods_list = await get_streamer_vods()
for v in vods_list:
db.insert_video_record(
DB.insert_video_record(
v['id'], v['title'], v['created_at'], v['view_count'], v['duration'],
v['url'], v['thumbnail_url'], v['game_id'], v['game_name'],
v['stream_id'], v['creator_name'], v['clip_is']
@@ -186,7 +169,7 @@ async def main():
clips_list = await get_streamer_clips()
for v in clips_list:
db.insert_video_record(
DB.insert_video_record(
v['id'], v['title'], v['created_at'], v['view_count'], v['duration'],
v['url'], v['thumbnail_url'], v['game_id'], v['game_name'],
v['stream_id'], v['creator_name'], v['clip_is']
View File
File renamed without changes.
+24 -86
View File
@@ -1,109 +1,47 @@
aiohappyeyeballs==2.7.1
aiohttp==3.14.2
aiohttp==3.14.3
aiosignal==1.4.0
anyio==4.14.2
anyio==4.15.1
attrs==26.1.0
beautifulsoup4==4.15.0
av==18.1.0
certifi==2026.7.22
cffi==2.1.0
charset-normalizer==3.4.9
chat-downloader==0.2.8
click==8.4.2
colorlog==6.12.0
cryptography==49.0.0
cuda-bindings==13.3.1
cuda-pathfinder==1.6.0
cuda-toolkit==13.0.3.0
charset-normalizer==3.5.1
click==8.5.0
ctranslate2==4.8.2
decorator==5.3.1
defusedxml==0.7.1
docstring_parser==0.18.0
enum-tools==0.13.0
filelock==3.32.0
faster-whisper==1.2.1
filelock==3.32.6
flatbuffers==25.12.19
frozenlist==1.8.0
fsspec==2026.6.0
git-filter-repo==2.47.0
google==3.0.0
google-api-core==2.32.0
google-api-python-client==2.198.0
google-auth==2.56.2
google-auth-httplib2==0.4.0
google-auth-oauthlib==1.4.0
googleapis-common-protos==1.75.0
fsspec==2026.7.0
h11==0.16.0
hf-xet==1.6.0
httpcore==1.0.9
httplib2==0.32.0
httpx==0.28.1
idna==3.18
huggingface_hub==1.31.0
idna==3.19
ImageIO==2.37.4
imageio-ffmpeg==0.6.0
isodate==0.7.2
Jinja2==3.1.6
joblib==1.5.3
llvmlite==0.48.0
lxml==6.1.1
MarkupSafe==3.0.3
more-itertools==11.1.0
moviepy==2.2.1
mpmath==1.3.0
multidict==6.7.1
networkx==3.6.1
nltk==3.10.0
numba==0.66.0
numpy==2.4.6
nvidia-cublas==13.1.1.3
nvidia-cuda-cupti==13.0.85
nvidia-cuda-nvrtc==13.0.88
nvidia-cuda-runtime==13.0.96
nvidia-cudnn-cu13==9.20.0.48
nvidia-cufft==12.0.0.61
nvidia-cufile==1.15.1.6
nvidia-curand==10.4.0.35
nvidia-cusolver==12.0.4.66
nvidia-cusparse==12.6.3.3
nvidia-cusparselt-cu13==0.8.1
nvidia-nccl-cu13==2.29.7
nvidia-nvjitlink==13.3.33
nvidia-nvshmem-cu13==3.4.5
nvidia-nvtx==13.0.85
oauthlib==3.3.1
openai-whisper==20250625
outcome==1.3.0.post0
multidict==6.8.0
numpy==2.5.3
onnxruntime==1.30.0
opencv-python==5.0.0.93
packaging==26.3
pillow==11.3.0
pip_system_certs==5.3
proglog==0.1.12
propcache==0.5.2
proto-plus==1.28.1
protobuf==7.35.1
pyasn1==0.6.4
pyasn1_modules==0.4.2
pycountry==26.2.16
pycparser==3.0
pycryptodome==3.23.0
Pygments==2.20.0
pyparsing==3.3.2
PySocks==1.7.1
protobuf==7.36.1
Pygments==2.21.0
python-dateutil==2.9.0.post0
python-dotenv==1.2.2
regex==2026.7.19
python-dotenv==1.2.3
PyYAML==6.0.3
requests==2.34.2
requests-oauthlib==2.0.0
setuptools==83.0.0
six==1.17.0
sniffio==1.3.1
sortedcontainers==2.4.0
soupsieve==2.9.1
streamlink==8.4.0
sympy==1.14.0
tiktoken==0.13.0
torch==2.13.0
tqdm==4.69.0
trio==0.33.0
trio-websocket==0.12.2
triton==3.7.1
tokenizers==0.23.2
tqdm==4.70.1
twitchAPI==4.5.0
typing_extensions==4.16.0
uritemplate==4.2.0
urllib3==2.7.0
websocket-client==1.9.0
wsproto==1.3.2
yarl==1.24.5
+2 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
import os
import json
import linux
import package.utils as utils
import tempfile
from pathlib import Path
from moviepy import VideoFileClip, ColorClip, CompositeVideoClip
@@ -118,7 +118,7 @@ def fit_to_9_16_letterbox(input_path: str, top_text: str = "TOP TEXT", bottom_te
)
try:
linux.run_command(ffmpeg_cmd)
utils.run_command(ffmpeg_cmd)
print(f"🎉 High-speed processing complete! Video saved to: {output_path}")
finally:
# Clean up temporary PNG picture files safely