rearange files and updateing files
This commit is contained in:
1 parent
20acb4bc9f
commit
2de2c461e2
28 files changed
+283
-186
No files matched your search
@@ -1,16 +1,16 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import requests
|
import requests
|
||||||
import os
|
import os
|
||||||
import linux
|
import package.linux as linux
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from database import Database
|
from package.database import Database
|
||||||
|
|
||||||
import uploader
|
import uploader
|
||||||
from uploader import CategoryId
|
from uploader import CategoryId
|
||||||
|
|
||||||
import youtube_hashtags
|
import youtube_hashtags
|
||||||
import 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
|
||||||
DB = None
|
DB = None
|
||||||
Executable → Regular
+1
-1
@@ -3,7 +3,7 @@ import requests
|
|||||||
import subprocess
|
import subprocess
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from database import Database
|
from package.database import Database
|
||||||
|
|
||||||
import uploader
|
import uploader
|
||||||
from uploader import CategoryId
|
from uploader import CategoryId
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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()
|
||||||
@@ -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()
|
||||||
@@ -4,8 +4,8 @@ import json
|
|||||||
import asyncio
|
import asyncio
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import database
|
import package.database as database
|
||||||
import twitch_video_info
|
import package.twitch.video_info as video_info
|
||||||
|
|
||||||
DB = None
|
DB = None
|
||||||
def format_to_hms(total_seconds):
|
def format_to_hms(total_seconds):
|
||||||
@@ -44,7 +44,7 @@ def read_json(file_path: str):
|
|||||||
|
|
||||||
video_length = format_to_hms(video_length_seconds)
|
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(
|
DB.insert_video_record(
|
||||||
video_id,
|
video_id,
|
||||||
File renamed without changes.
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import database
|
import package.database as database
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
DB = None
|
DB = None
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import database
|
import package.database as database
|
||||||
|
|
||||||
DB = None
|
DB = None
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
import database
|
import package.database as database
|
||||||
|
|
||||||
DB = None
|
DB = None
|
||||||
|
|
||||||
@@ -1,23 +1,23 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
import twitch_video_info
|
import package.twitch.video_info as video_info
|
||||||
import twitch_download_videos
|
import package.twitch.download_videos as download_videos
|
||||||
import twitch_download_thumbnails
|
import package.twitch.download_thumbnails as download_thumbnails
|
||||||
|
|
||||||
import build_videos
|
import package.build_videos as build_videos
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Get Twitch Video Info
|
# Get Twitch Video Info
|
||||||
asyncio.run(twitch_video_info.main())
|
asyncio.run(video_info())
|
||||||
|
|
||||||
# Download Twitch Videos
|
# Download Twitch Videos
|
||||||
twitch_download_videos.main()
|
download_videos()
|
||||||
|
|
||||||
twitch_download_thumbnails.main()
|
download_thumbnails()
|
||||||
|
|
||||||
#Build
|
#Build
|
||||||
build_videos.main()
|
build_videos()
|
||||||
|
|
||||||
print("====================================================")
|
print("====================================================")
|
||||||
print(f"✅ Done.")
|
print(f"✅ Done.")
|
||||||
|
|||||||
@@ -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",
|
||||||
|
]
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import database
|
|
||||||
import youtube_short
|
import youtube_short
|
||||||
import transcribe_video
|
import package.transcribe_video as transcribe_video
|
||||||
import twitch_chat_vod
|
import package.twitch.chat_vod as chat_vod
|
||||||
|
from package.config import DB as DB
|
||||||
DB = None
|
|
||||||
|
|
||||||
def build_chat_video():
|
def build_chat_video():
|
||||||
chats = DB.get_unuploaded_chats()
|
chats = DB.get_unuploaded_chats()
|
||||||
@@ -33,7 +31,7 @@ def build_chat_video():
|
|||||||
print(f"🚀 Chat Video: {title}")
|
print(f"🚀 Chat Video: {title}")
|
||||||
print("====================================================")
|
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("====================================================")
|
||||||
print(f"✅ Processing: Chat Video Done.")
|
print(f"✅ Processing: Chat Video Done.")
|
||||||
@@ -117,8 +115,6 @@ def build_shorts():
|
|||||||
print("====================================================")
|
print("====================================================")
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
global DB
|
|
||||||
DB = database.Database()
|
|
||||||
build_shorts()
|
build_shorts()
|
||||||
build_transcribe()
|
build_transcribe()
|
||||||
build_chat_video()
|
build_chat_video()
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import package.database as database
|
||||||
|
|
||||||
|
CHANNEL_NAME = "teampgp"
|
||||||
|
|
||||||
|
DB = database.Database()
|
||||||
@@ -80,6 +80,13 @@ class Database:
|
|||||||
"""Flags a specific row record to downloaded (0)."""
|
"""Flags a specific row record to downloaded (0)."""
|
||||||
self.__mark_as(record_id, "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]:
|
def __get_unuploaded(self, set_row: str, also: str = "") -> list[Any]:
|
||||||
"""Retrieve all rows that were download but not uploaded"""
|
"""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}")
|
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
|
#!/usr/bin/env python3
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -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
|
#!/usr/bin/env python3
|
||||||
import os
|
import os
|
||||||
import cv2
|
import cv2
|
||||||
import linux
|
import package.utils as utils
|
||||||
|
|
||||||
def get_video_height(video_path: str) -> int:
|
def get_video_height(video_path: str) -> int:
|
||||||
# Open the video file
|
# 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 ")
|
chat_cmd = (f"{chat_cmd} --generate-mask ")
|
||||||
|
|
||||||
if not os.path.exists(temp_chat):
|
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:
|
if chat_success:
|
||||||
print("✅ Success: TwitchDownloaderCLI render chat video.")
|
print("✅ Success: TwitchDownloaderCLI render chat video.")
|
||||||
else:
|
else:
|
||||||
print("❌ Error: TwitchDownloaderCLI failed to render chat video.")
|
print("❌ Error: TwitchDownloaderCLI failed to render chat video.")
|
||||||
|
if os.path.exists(temp_chat):
|
||||||
os.remove(temp_chat)
|
os.remove(temp_chat)
|
||||||
|
if os.path.exists(mask_path):
|
||||||
os.remove(mask_path)
|
os.remove(mask_path)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -119,7 +121,7 @@ def combine_twitch_vod_and_chat(vod_path: str, layout: str = "side-by-side", for
|
|||||||
else:
|
else:
|
||||||
raise ValueError("Invalid layout choice. Choose 'side-by-side' or 'overlay'.")
|
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
|
# Step 4: Final verification and cleanup
|
||||||
if ffmpeg_success and os.path.exists(temp_with_chat):
|
if ffmpeg_success and os.path.exists(temp_with_chat):
|
||||||
@@ -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 os
|
||||||
import json
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import requests
|
import requests
|
||||||
from twitchAPI.twitch import Twitch
|
|
||||||
from twitchAPI.helper import first
|
from twitchAPI.helper import first
|
||||||
|
|
||||||
import database
|
import package.config as config
|
||||||
|
import package.database as 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.")
|
|
||||||
|
|
||||||
|
|
||||||
async def download_live_thumbnail(twitch_client, streamer_username: str, w: int = 1920, h: int = 1080):
|
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."""
|
"""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():
|
async def main():
|
||||||
# Initialize connection & automatically authorize the App token
|
# Initialize connection & automatically authorize the App token
|
||||||
await get_twitch()
|
await config.get_twitch()
|
||||||
twitch = TWITCH # Twitch(APP_ID, APP_SECRET)
|
twitch = config.TWITCH # Twitch(APP_ID, APP_SECRET)
|
||||||
|
|
||||||
# --- OPTION A: Download Live Thumbnail ---
|
# --- OPTION A: Download Live Thumbnail ---
|
||||||
# Target user must be streaming live right now
|
# 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)
|
#await download_live_thumbnail(twitch, target_streamer, 1920, 1080)
|
||||||
|
|
||||||
# --- OPTION B: Download Past Broadcast VOD Thumbnail ---
|
# --- OPTION B: Download Past Broadcast VOD Thumbnail ---
|
||||||
@@ -3,11 +3,8 @@ import csv
|
|||||||
import subprocess
|
import subprocess
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
import linux
|
import package.utils as utils
|
||||||
from database import Database
|
from package.config import DB as DB
|
||||||
|
|
||||||
CHANNEL_NAME = 'teampgp'
|
|
||||||
DB = None
|
|
||||||
|
|
||||||
def write_csv(data, file_name):
|
def write_csv(data, file_name):
|
||||||
# Open file with newline='' to prevent extra blank rows across platforms
|
# Open file with newline='' to prevent extra blank rows across platforms
|
||||||
@@ -78,8 +75,8 @@ def download():
|
|||||||
csv_file = f"{target_dir}/{id}.csv"
|
csv_file = f"{target_dir}/{id}.csv"
|
||||||
|
|
||||||
# 3. FIXED: Use the live streaming function to prevent Out-Of-Memory crashes
|
# 3. FIXED: Use the live streaming function to prevent Out-Of-Memory crashes
|
||||||
output = linux.run_command(cmd, look_for=["[STATUS]"])
|
output = utils.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]"])
|
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:
|
if output:
|
||||||
# Only write CSV and update database if download actually completed
|
# Only write CSV and update database if download actually completed
|
||||||
@@ -100,8 +97,6 @@ def download():
|
|||||||
print("Finished Downloading Pipeline.")
|
print("Finished Downloading Pipeline.")
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
global DB
|
|
||||||
DB = Database()
|
|
||||||
download()
|
download()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -1,25 +1,21 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
|
||||||
import httpx # Switched from requests to prevent async loop freezing
|
import httpx # Switched from requests to prevent async loop freezing
|
||||||
import database
|
|
||||||
from twitchAPI.twitch import Twitch
|
|
||||||
from twitchAPI.helper import first
|
from twitchAPI.helper import first
|
||||||
from twitchAPI.type import VideoType
|
from twitchAPI.type import VideoType
|
||||||
|
|
||||||
SECRETS = None
|
import package.twitch.config as config
|
||||||
TWITCH = None
|
from package.config import DB as DB
|
||||||
USER = None
|
|
||||||
GAME_CACHE = {} # Local cache dictionary to store game_id -> game_name mapping
|
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:
|
async def get_game_id_by_name(game_name: str) -> str:
|
||||||
if SECRETS is None:
|
if config.SECRETS is None:
|
||||||
await get_twitch()
|
await config.get_twitch()
|
||||||
# Query the 'get_games' endpoint using the names parameter
|
# Query the 'get_games' endpoint using the names parameter
|
||||||
# The API expects a list, even for a single game name
|
# 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 Name: {game.name}")
|
||||||
print(f"Game ID: {game.id}")
|
print(f"Game ID: {game.id}")
|
||||||
print(f"IGDB ID: {game.igdb_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}'")
|
print(f"No game found with the exact name: '{game_name}'")
|
||||||
return None
|
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:
|
async def get_game_name_by_id(game_id: str) -> str:
|
||||||
"""Helper function to fetch game names and cache them locally."""
|
"""Helper function to fetch game names and cache them locally."""
|
||||||
if not game_id:
|
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:
|
if game_id in GAME_CACHE:
|
||||||
return GAME_CACHE[game_id]
|
return GAME_CACHE[game_id]
|
||||||
try:
|
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)
|
game = await first(game_generator)
|
||||||
if game:
|
if game:
|
||||||
GAME_CACHE[game_id] = game.name
|
GAME_CACHE[game_id] = game.name
|
||||||
@@ -100,10 +84,10 @@ 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():
|
||||||
await get_twitch()
|
await config.get_twitch()
|
||||||
|
|
||||||
print(f"Starting VOD extraction for {USER.display_name}...")
|
print(f"Starting VOD extraction for {config.USER.display_name}...")
|
||||||
vod_generator = TWITCH.get_videos(user_id=USER.id, first=100, video_type=VideoType.ALL)
|
vod_generator = config.TWITCH.get_videos(user_id=config.USER.id, first=100, video_type=VideoType.ALL)
|
||||||
|
|
||||||
all_vods = []
|
all_vods = []
|
||||||
|
|
||||||
@@ -128,7 +112,7 @@ async def get_streamer_vods():
|
|||||||
"game_id": clean_game_id,
|
"game_id": clean_game_id,
|
||||||
"game_name": game_name,
|
"game_name": game_name,
|
||||||
"stream_id": str(v.stream_id) if v.stream_id else "0",
|
"stream_id": str(v.stream_id) if v.stream_id else "0",
|
||||||
"creator_name": CHANNEL_NAME,
|
"creator_name": config.CHANNEL_NAME,
|
||||||
"clip_is": False,
|
"clip_is": False,
|
||||||
}
|
}
|
||||||
all_vods.append(vod_data)
|
all_vods.append(vod_data)
|
||||||
@@ -138,10 +122,10 @@ async def get_streamer_vods():
|
|||||||
return all_vods
|
return all_vods
|
||||||
|
|
||||||
async def get_streamer_clips():
|
async def get_streamer_clips():
|
||||||
await get_twitch()
|
await config.get_twitch()
|
||||||
|
|
||||||
print(f"Starting clip extraction for {USER.display_name}...")
|
print(f"Starting clip extraction for {config.USER.display_name}...")
|
||||||
clip_generator = TWITCH.get_clips(broadcaster_id=USER.id, first=100)
|
clip_generator = config.TWITCH.get_clips(broadcaster_id=config.USER.id, first=100)
|
||||||
all_clips = []
|
all_clips = []
|
||||||
|
|
||||||
async for c in clip_generator:
|
async for c in clip_generator:
|
||||||
@@ -170,11 +154,10 @@ async def get_streamer_clips():
|
|||||||
async def main():
|
async def main():
|
||||||
print("--- Script Started ---")
|
print("--- Script Started ---")
|
||||||
|
|
||||||
db = database.Database()
|
|
||||||
vods_list = await get_streamer_vods()
|
vods_list = await get_streamer_vods()
|
||||||
|
|
||||||
for v in vods_list:
|
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['id'], v['title'], v['created_at'], v['view_count'], v['duration'],
|
||||||
v['url'], v['thumbnail_url'], v['game_id'], v['game_name'],
|
v['url'], v['thumbnail_url'], v['game_id'], v['game_name'],
|
||||||
v['stream_id'], v['creator_name'], v['clip_is']
|
v['stream_id'], v['creator_name'], v['clip_is']
|
||||||
@@ -186,7 +169,7 @@ async def main():
|
|||||||
clips_list = await get_streamer_clips()
|
clips_list = await get_streamer_clips()
|
||||||
|
|
||||||
for v in clips_list:
|
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['id'], v['title'], v['created_at'], v['view_count'], v['duration'],
|
||||||
v['url'], v['thumbnail_url'], v['game_id'], v['game_name'],
|
v['url'], v['thumbnail_url'], v['game_id'], v['game_name'],
|
||||||
v['stream_id'], v['creator_name'], v['clip_is']
|
v['stream_id'], v['creator_name'], v['clip_is']
|
||||||
File renamed without changes.
+24
-86
@@ -1,109 +1,47 @@
|
|||||||
aiohappyeyeballs==2.7.1
|
aiohappyeyeballs==2.7.1
|
||||||
aiohttp==3.14.2
|
aiohttp==3.14.3
|
||||||
aiosignal==1.4.0
|
aiosignal==1.4.0
|
||||||
anyio==4.14.2
|
anyio==4.15.1
|
||||||
attrs==26.1.0
|
attrs==26.1.0
|
||||||
beautifulsoup4==4.15.0
|
av==18.1.0
|
||||||
certifi==2026.7.22
|
certifi==2026.7.22
|
||||||
cffi==2.1.0
|
charset-normalizer==3.5.1
|
||||||
charset-normalizer==3.4.9
|
click==8.5.0
|
||||||
chat-downloader==0.2.8
|
ctranslate2==4.8.2
|
||||||
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
|
|
||||||
decorator==5.3.1
|
decorator==5.3.1
|
||||||
defusedxml==0.7.1
|
|
||||||
docstring_parser==0.18.0
|
|
||||||
enum-tools==0.13.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
|
frozenlist==1.8.0
|
||||||
fsspec==2026.6.0
|
fsspec==2026.7.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
|
|
||||||
h11==0.16.0
|
h11==0.16.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
|
||||||
idna==3.18
|
huggingface_hub==1.31.0
|
||||||
|
idna==3.19
|
||||||
ImageIO==2.37.4
|
ImageIO==2.37.4
|
||||||
imageio-ffmpeg==0.6.0
|
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
|
moviepy==2.2.1
|
||||||
mpmath==1.3.0
|
multidict==6.8.0
|
||||||
multidict==6.7.1
|
numpy==2.5.3
|
||||||
networkx==3.6.1
|
onnxruntime==1.30.0
|
||||||
nltk==3.10.0
|
opencv-python==5.0.0.93
|
||||||
numba==0.66.0
|
packaging==26.3
|
||||||
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
|
|
||||||
pillow==11.3.0
|
pillow==11.3.0
|
||||||
pip_system_certs==5.3
|
|
||||||
proglog==0.1.12
|
proglog==0.1.12
|
||||||
propcache==0.5.2
|
propcache==0.5.2
|
||||||
proto-plus==1.28.1
|
protobuf==7.36.1
|
||||||
protobuf==7.35.1
|
Pygments==2.21.0
|
||||||
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
|
|
||||||
python-dateutil==2.9.0.post0
|
python-dateutil==2.9.0.post0
|
||||||
python-dotenv==1.2.2
|
python-dotenv==1.2.3
|
||||||
regex==2026.7.19
|
PyYAML==6.0.3
|
||||||
requests==2.34.2
|
requests==2.34.2
|
||||||
requests-oauthlib==2.0.0
|
|
||||||
setuptools==83.0.0
|
|
||||||
six==1.17.0
|
six==1.17.0
|
||||||
sniffio==1.3.1
|
tokenizers==0.23.2
|
||||||
sortedcontainers==2.4.0
|
tqdm==4.70.1
|
||||||
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
|
|
||||||
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
|
||||||
websocket-client==1.9.0
|
|
||||||
wsproto==1.3.2
|
|
||||||
yarl==1.24.5
|
yarl==1.24.5
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import linux
|
import package.utils as utils
|
||||||
import tempfile
|
import tempfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from moviepy import VideoFileClip, ColorClip, CompositeVideoClip
|
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:
|
try:
|
||||||
linux.run_command(ffmpeg_cmd)
|
utils.run_command(ffmpeg_cmd)
|
||||||
print(f"🎉 High-speed processing complete! Video saved to: {output_path}")
|
print(f"🎉 High-speed processing complete! Video saved to: {output_path}")
|
||||||
finally:
|
finally:
|
||||||
# Clean up temporary PNG picture files safely
|
# Clean up temporary PNG picture files safely
|
||||||
|
|||||||
Reference in new issue
Block a user