update and test twitch clip downloading

This commit is contained in:
2026-07-20 12:42:52 -04:00
parent 0505d2a581
commit ff15ded0c9
9 changed files with 553 additions and 85 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
/secrets.json /secrets.json
/client_secrets.json /client_secrets.json
/clips_database.db /clips_database.db
__pycache__/uploader.cpython-313.pyc
database.db database.db
__pycache__/database.cpython-313.pyc save
__pycache__
+5 -5
View File
@@ -15,7 +15,7 @@ class Database:
self.CURSOR = self.CONN.cursor() self.CURSOR = self.CONN.cursor()
if self.table == "vods": if self.table == "vods":
self.columns = "id, date, title, gamename, downloaded, uploaded_yt, chat_upload_yt" self.columns = "id, date, title, gamename, downloaded, uploaded_yt, chats_upload_yt"
elif self.table == "clips": elif self.table == "clips":
self.columns = "slug, date, title, gamename, clip_by, view_count, downloaded, uploaded_yt, shorts_upload_yt" self.columns = "slug, date, title, gamename, clip_by, view_count, downloaded, uploaded_yt, shorts_upload_yt"
@@ -53,7 +53,7 @@ class Database:
gamename TEXT NOT NULL, gamename TEXT NOT NULL,
downloaded INTEGER NOT NULL, downloaded INTEGER NOT NULL,
uploaded_yt INTEGER NOT NULL, uploaded_yt INTEGER NOT NULL,
chat_upload_yt INTEGER NOT NULL chats_upload_yt INTEGER NOT NULL
) )
""" """
) )
@@ -80,7 +80,7 @@ class Database:
def mark_as_uploaded_shorts_chats(self, record_id: int): def mark_as_uploaded_shorts_chats(self, record_id: int):
"""Flags a specific row record to uploaded (1).""" """Flags a specific row record to uploaded (1)."""
set_sql = "chat_upload_yt" set_sql = "chats_upload_yt"
if self.table == "clips": if self.table == "clips":
set_sql = "shorts_uploaded_yt" set_sql = "shorts_uploaded_yt"
@@ -94,12 +94,12 @@ class Database:
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."""
self.__mark_as(record_id, "download") self.__mark_as(record_id, "downloaded")
def get_unuploaded_shorts_chats(self): def get_unuploaded_shorts_chats(self):
"""Retrieves all clip rows remaining to be chat uploaded.""" """Retrieves all clip rows remaining to be chat uploaded."""
where_sql = "chat_upload_yt" where_sql = "chats_upload_yt"
if self.table == "clips": if self.table == "clips":
where_sql = "shorts_uploaded_yt" where_sql = "shorts_uploaded_yt"
+97
View File
@@ -0,0 +1,97 @@
apt-listchanges==4.8
attrs==26.1.0
beautifulsoup4==4.14.3
certifi==2026.2.25
cffi==2.0.0
chardet==5.2.0
charset-normalizer==3.4.7
click==8.4.2
colorama==0.4.6
cryptography==49.0.0
cuda-bindings==13.3.1
cuda-pathfinder==1.5.6
cuda-toolkit==13.0.3.0
decorator==5.3.1
defusedxml==0.7.1
docopt==0.6.2
filelock==3.31.0
fsspec==2026.6.0
google-api-core==2.31.0
google-api-python-client==2.198.0
google-auth==2.56.0
google-auth-httplib2==0.4.0
google-auth-oauthlib==1.4.0
googleapis-common-protos==1.75.0
h11==0.16.0
httplib2==0.32.0
idna==3.11
ImageIO==2.37.3
imageio-ffmpeg==0.6.0
Jinja2==3.1.6
joblib==1.5.3
llvmlite==0.48.0
MarkupSafe==3.0.3
more-itertools==11.1.0
moviepy==2.2.1
mpmath==1.3.0
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
packaging==26.2
pillow==11.3.0
pipreqs==0.4.13
proglog==0.1.12
proto-plus==1.28.1
protobuf==7.35.1
pyasn1==0.6.4
pyasn1_modules==0.4.2
pycparser==3.0
pyparsing==3.3.2
PySocks==1.7.1
python-apt==3.0.0
python-debian==1.0.1
python-debianbts==4.1.1
python-dotenv==1.2.2
regex==2026.7.19
reportbug==13.2.0
requests==2.33.1
requests-oauthlib==2.0.0
selenium==4.43.0
setuptools==83.0.0
sniffio==1.3.1
sortedcontainers==2.4.0
soupsieve==2.8.3
sympy==1.14.0
tiktoken==0.13.0
torch==2.13.0
tqdm==4.67.3
trio==0.33.0
trio-websocket==0.12.2
triton==3.7.1
typing_extensions==4.15.0
uritemplate==4.2.0
urllib3==2.6.3
webdriver-manager==4.0.2
websocket-client==1.9.0
wheel==0.46.1
wsproto==1.3.2
yarg==0.1.10
+37 -12
View File
@@ -1,23 +1,45 @@
import os import os
import subprocess
import whisper import whisper
from moviepy.editor import VideoFileClip from moviepy import VideoFileClip
from whisper.utils import get_writer from whisper.utils import get_writer
model = whisper.load_model("base")
def extract_audio(video_path, audio_temp_path): def extract_audio(video_path, audio_temp_path):
# Create a temporary path for a sanitized copy of the video
sanitized_video_path = video_path.replace(".mp4", "_clean.mp4")
print("Sanitizing video metadata for MoviePy parser...")
# -map_chapters -1 removes chapter layouts that break the parser.
# -sn strips text/subtitle streams that crash MoviePy.
# -c copy copies video and audio instantly without quality loss.
cleanup_cmd = [
"ffmpeg", "-y", "-i", video_path,
"-map_chapters", "-1", "-sn",
"-c", "copy", sanitized_video_path
]
# Run the sanitization process silently
subprocess.run(cleanup_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
print("Extracting uncompressed WAV audio...") print("Extracting uncompressed WAV audio...")
video = VideoFileClip(video_path) try:
# Extract as WAV, strictly setting the sample rate to 16000Hz for Whisper # Load the sanitized file instead of the raw Twitch clip
video.audio.write_audiofile( with VideoFileClip(sanitized_video_path) as video:
audio_temp_path, video.audio.write_audiofile(
codec="pcm_s16le", audio_temp_path,
ffmpeg_params=["-ar", "16000", "-ac", "1"] fps=16000,
) codec="pcm_s16le",
video.close() ffmpeg_params=["-ac", "1"]
)
finally:
# Always clean up the temporary sanitized video on Windows 11
if os.path.exists(sanitized_video_path):
os.remove(sanitized_video_path)
def transcribe_to_srt(audio_path, output_directory, output_filename): def transcribe_to_srt(audio_path, output_directory, output_filename):
print("Loading Whisper model...")
model = whisper.load_model("base")
print("Transcribing audio...") print("Transcribing audio...")
result = model.transcribe(audio_path) result = model.transcribe(audio_path)
@@ -26,6 +48,9 @@ def transcribe_to_srt(audio_path, output_directory, output_filename):
srt_writer(result, output_filename, {}) srt_writer(result, output_filename, {})
print(f"SRT subtitle file saved in: {output_directory}") print(f"SRT subtitle file saved in: {output_directory}")
if os.path.exists(audio_path):
os.remove(audio_path)
if __name__ == "__main__": if __name__ == "__main__":
video_path = "my_video.mp4" video_path = "my_video.mp4"
+294
View File
@@ -0,0 +1,294 @@
#!/usr/bin/env python3
import requests
import os
import subprocess
from database import Database
import uploader
from uploader import CategoryId
import youtube_hashtags
import transcribe_video
CHANNEL_NAME = 'teampgp' # Replace with the streamer's username
DB = None
def run_linux_command(command: str):
"""Executes a Linux command, waits for completion, and returns output."""
try:
# shell=True allows running full command strings with pipes/wildcards
# text=True returns strings instead of bytes
result = subprocess.run(
command, shell=True, check=True, capture_output=True, text=True
)
return {"success": True, "stdout": result.stdout, "stderr": result.stderr}
except subprocess.CalledProcessError as e:
# Handles errors if the Linux command returns a non-zero exit code
return {"success": False, "stdout": e.stdout, "stderr": e.stderr}
def transcribe(slug: str):
"""Transcribes the Video File."""
video_file = f"save/{DB.table}/{slug}/{slug}.mp4"
# Check if the video file exists
if not os.path.exists(video_file):
print(f"Error: File not found: {video_file}")
return False
# no need to continue if srt transcribe file already exists
if os.path.exists(f"save/{DB.table}/{slug}/transcribe_{slug}.srt"):
print(f"video already transcribed:")
return True
transcribe_video.extract_audio(video_file, f"save/{DB.table}/{slug}/temp_{slug}_audio.wav")
transcribe_video.transcribe_to_srt(f"save/{DB.table}/{slug}/temp_{slug}_audio.wav", f"save/{DB.table}/{slug}/", f"transcribe_{slug}")
return True
def top_hashtags(slug: str):
""""Hashtags from transcribed SRT file."""
file_srt = f"save/{DB.table}/{slug}/transcribe_{slug}.srt"
# if srt transcribe file not exists
if not os.path.exists(file_srt):
print(f"Transcribe file not found {file_srt}")
return []
tags = youtube_hashtags.get_top_hashtags(file_srt)
return tags
def download():
"""Find all undownload vods and download them."""
undownloaded = DB.get_undownloaded()
print(f"Download {DB.table}...")
if DB.table == "vods":
for record_id, record_date, title, game_name, downloaded, uploaded_yt, chat_upload_yt in undownloaded:
print(f"ID: {record_id} | Date: {record_date} | Game: {game_name} | Title: {title}")
output = run_linux_command(f"TwitchDownloaderCLI videodownload --id {record_id} -o save/vods/{record_id}/{record_id}.mp4 --collision Overwrite")
output2 = run_linux_command(f"TwitchDownloaderCLI chatdownload --id {record_id} -o save/vods/{record_id}/{record_id}_chat.json -E --collision Overwrite")
if output["success"] is True:
print(f"ID: {record_id} | Date: {record_date} | Game: {game_name} | Title: {title} | was successful")
DB.mark_as_downloaded(record_id)
else:
print(f"ID: {record_id} | Download Process failed. {output["stdout"]}. Error: {output["stderr"]}")
elif DB.table == "clips":
for slug, record_date, title, game_name, clip_by, views, downloaded, uploaded_yt, uploaded_shorts_yt in undownloaded:
print(f"Slug: {slug} | Date: {record_date} | Game: {game_name} | By: {clip_by} | Views: {views} | Title: {title}")
# Uses standard clipdownload directive
output = run_linux_command(f"TwitchDownloaderCLI clipdownload --id {slug} -o save/clips/{slug}/{slug}.mp4 --collision Overwrite")
if output["success"] is True:
print(f"Slug: {slug} | Was successfully downloaded.")
DB.mark_as_downloaded(slug)
else:
print(f"Slug: {slug} | Download Process failed. {output["stdout"]}. Error: {output["stderr"]}")
print(f"Finished Downloading {DB.table}...")
def upload():
"""Loops over the downloaded videos entries and uploaded them to youtube."""
print(f"Uploading {DB.table}...")
unuploaded = DB.get_unuploaded()
twitch_datetime = " #Twitch Every Friday and Sunday @7:30 EST https://twitch.tv/teampgp"
file_path = ""
title = ""
description = ""
categoryId = CategoryId.GAMING
privatcyStatus = 'private'
base_tags = ['gaming', 'TeamPGP', 'twitch', 'Level1Techs']
upload_queue = []
if DB.table == "vods":
for record_id, record_date, title, game_name, downloaded, uploaded_yt, chats_upload_yt in unuploaded:
print(f"ID: {record_id} | Date: {record_date} | Game: {game_name} | Title: {title}")
file_path = f"save/vods/{record_id}/{record_id}.mp4"
description = f"Game: {game_name}, on {record_date}, #VODS {twitch_datetime}"
tags = list(base_tags)
tags.extend([f'{game_name}', 'twitch_vods', 'vods'])
tags.extend(top_hashtags(record_id))
upload_queue.append([record_id, file_path, title, categoryId, description, privatcyStatus, tags])
elif DB.table == "clips":
for slug, record_date, title, game_name, clip_by, views, downloaded, uploaded_yt, shorts_uploaded_yt in unuploaded:
print(f"Slug: {slug} | Date: {record_date} | Game: {game_name} | By: {clip_by} | Views: {views} | Title: {title}")
transcribe(slug)
file_path = f"save/clips/{slug}/{slug}.mp4"
description = f"Game: {game_name}, Cliped By: {clip_by}, on {record_date}, #Shorts #Clips {twitch_datetime}"
tags = list(base_tags)
tags.extend([f'{game_name}', 'twitch_clips', 'clips', f'{clip_by}', 'shorts'])
tags.extend(top_hashtags(slug))
upload_queue.append([slug, file_path, title, categoryId, description, privatcyStatus, tags])
for db_id, file_path, title, categoryId, description, privatcyStatus, tags in upload_queue:
output = uploader.upload_video(file_path, title, categoryId, description, privatcyStatus, tags)
if output is True:
print(f"Title: {title} | Was successfully uploaded.")
DB.mark_as_uploaded(db_id)
else:
print(f"Title: {title} | Download Process failed.")
def create_chats():
pass
def create_shorts():
pass
def get_vod_ids_simplified(channel_name: str):
"""Queries Twitch's public endpoint directly for trending clips."""
session = requests.Session()
url = "https://gql.twitch.tv/gql"
# Case-preserved headers to prevent 400 Bad Request errors
session.headers = {
"Client-ID": "kimne78kx3ncx6brgo4mv6wki5h1ko",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Content-Type": "text/plain"
}
vods_query_string = """
query GetChannelVideos($login: String!, $limit: Int!) {
user(login: $login) {
videos(first: $limit, types: [ARCHIVE]) {
edges {
node {
id
title
publishedAt
game {
displayName
}
}
}
}
}
}
"""
clips_query_string = """
query GetChannelClips($login: String!, $limit: Int!) {
user(login: $login) {
clips(first: $limit, criteria: { period: ALL_TIME }) {
edges {
node {
slug
title
createdAt
viewCount
game {
displayName
}
curator {
login
}
}
}
}
}
}
"""
limit = 50
query_string = ""
operation_name = ""
if DB.table == "vods":
query_string = vods_query_string
limit = 50
operation_name = "GetChannelVideos"
elif DB.table == "clips":
query_string = clips_query_string
limit = 40
operation_name = "GetChannelClips"
# A simplified query string that hardcodes the ARCHIVE type filter into the request structure
payload = [{
"operationName": operation_name,
"query": query_string,
"variables": {
"login": channel_name.lower(),
"limit": limit
}
}]
try:
# Prepping ensures Python does not rewrite the Client-ID header case
req = requests.Request('POST', url, json=payload)
prepped = session.prepare_request(req)
response = session.send(prepped)
response.raise_for_status()
data = response.json()
# Pull out the target index array dictionary object
result = data[0] if isinstance(data, list) else data
if "errors" in result:
print(f"Twitch GraphQL Error: {result['errors']}")
return []
user_data = result['data']['user']
if not user_data:
print(f"Channel '{channel_name}' not found.")
return []
edges = None
if DB.table == "vods":
edges = user_data['videos']['edges']
elif DB.table == "clips":
edges = user_data['clips']['edges']
video_ids = []
print(f"--- Latest VODs for {channel_name} ---")
for edge in edges:
node = edge['node']
# Safe extraction in case a VOD has no category set (Just Chatting, Uncategorized, etc.)
game_info = node.get('game')
game_name = game_info.get('displayName') if game_info else "Unknown/No Category"
if DB.table == "vods":
print(f"ID: {node['id']} | Date: {node['publishedAt']} | Game: {game_name} | Title: {node['title']}")
# Pass game_name to your database logic
DB.insert_vods_record(node['id'], node['publishedAt'], node['title'], game_name)
video_ids.append(node['id'])
elif DB.table == "clips":
# Safe extraction in case the curator account was deleted/missing
curator_info = node.get('curator')
clip_by = curator_info.get('login') if curator_info else "Unknown Creator"
print(f"Slug: {node['slug']} | Date: {node['createdAt']} | Game: {game_name} | By: {clip_by} | Views: {node['viewCount']} | Title: {node['title']}")
DB.insert_clips_record(node['slug'], node['createdAt'], node['title'], game_name, clip_by, int(node['viewCount']))
video_ids.append(node['slug'])
return video_ids
except Exception as e:
print(f"An unexpected error occurred: {e}")
if 'response' in locals():
print(f"Server Response Text: {response.text}")
return []
if __name__ == "__main__":
tables = ["vods", "clips"]
for table in tables:
DB = Database(table)
get_vod_ids_simplified(CHANNEL_NAME)
download()
upload()
DB.close_database()
+90 -39
View File
@@ -74,7 +74,7 @@ def download():
print(f"ID: {record_id} | Date: {record_date} | Game: {game_name} | Title: {title} | was successful") print(f"ID: {record_id} | Date: {record_date} | Game: {game_name} | Title: {title} | was successful")
DB.mark_as_downloaded(record_id) DB.mark_as_downloaded(record_id)
else: else:
print(f"ID: {record_id} | Date: {record_date} | Game: {game_name} | Title: {title} | was Failed") print(f"ID: {record_id} | Download Process failed. {output["stdout"]}. Error: {output["stderr"]}")
elif DB.table == "clips": elif DB.table == "clips":
for slug, record_date, title, game_name, clip_by, views, downloaded, uploaded_yt, uploaded_shorts_yt in undownloaded: for slug, record_date, title, game_name, clip_by, views, downloaded, uploaded_yt, uploaded_shorts_yt in undownloaded:
@@ -98,49 +98,56 @@ def upload():
unuploaded = DB.get_unuploaded() unuploaded = DB.get_unuploaded()
twitch_datetime = " #Twitch Every Friday and Sunday @7:30 EST https://twitch.tv/teampgp"
file_path = ""
title = ""
description = ""
categoryId = CategoryId.GAMING
privatcyStatus = 'private'
base_tags = ['gaming', 'TeamPGP', 'twitch', 'Level1Techs', 'twitch']
upload_queue = []
if DB.table == "vods": if DB.table == "vods":
for record_id, record_date, title, game_name, downloaded, uploaded_yt, chat_upload_yt in unuploaded_vods: for record_id, record_date, title, game_name, downloaded, uploaded_yt, chats_upload_yt in unuploaded:
print(f"ID: {record_id} | Date: {record_date} | Game: {game_name} | Title: {title}") print(f"ID: {record_id} | Date: {record_date} | Game: {game_name} | Title: {title}")
file_path = f"save/vods/{record_id}/{title}.mp4" file_path = f"save/vods/{record_id}/{title}.mp4"
title = title description = f"Game: {game_name}, on {record_date}, #VODS {twitch_datetime}"
description = f"Game: {game_name}, on {record_date}, #VODS #Twitch Every Friday and Sunday @7:30 EST https://twitch.tv/teampgp" tags = list(base_tags)
categoryId = CategoryId.GAMING tags.extend([f'{game_name}', 'twitch_vods', 'vods'])
privatcyStatus = 'private' tags.extend(top_hashtags(record_id))
tags = ['gaming', 'TeamPGP', f'{game_name}', 'twitch_vods', 'vods', 'Level1Techs', 'twitch']
output = uploader.upload_video(file_path, title, description, categoryId, privatcyStatus, tags) upload_queue.append([record_id, file_path, title, categoryId, description, privatcyStatus, tags])
if output is True:
print(f"ID: {record_id} | Was successfully uploaded.")
DB.mark_as_uploaded(record_id)
else:
print(f"ID: {record_id} | Upload Process failed.")
elif DB.table == "clips": elif DB.table == "clips":
for slug, record_date, title, game_name, clip_by, views, downloaded, uploaded_yt in unuploaded_clips: for slug, record_date, title, game_name, clip_by, views, downloaded, uploaded_yt in unuploaded:
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}/{title}.mp4" file_path = f"save/clips/{slug}/{title}.mp4"
title = title description = f"Game: {game_name}, Cliped By: {clip_by}, on {record_date}, #Shorts #Clips {twitch_datetime}"
description = f"Game: {game_name}, Cliped By: {clip_by}, on {record_date}, #Shorts #Clips #Twitch Every Friday and Sunday @7:30 EST https://twitch.tv/teampgp" tags = list(base_tags)
categoryId = CategoryId.GAMING tags.extend([f'{game_name}', 'twitch_clips', 'clips', f'{clip_by}', 'shorts'])
privatcyStatus = 'private' tags.extend(top_hashtags(slug))
tags = ['shorts', 'gaming', 'TeamPGP', f'{game_name}', f'{clip_by}', 'twitch_clips', 'clips', 'Level1Techs', 'twitch']
tags.extend(top_hashtags({slug})) upload_queue.append([slug, file_path, title, categoryId, description, privatcyStatus, tags])
#output = uploader.upload_video(file_path, title, categoryId, description, privatcyStatus, tags) for db_id, file_path, title, categoryId, description, privatcyStatus, tags in upload_queue:
output = uploader.upload_video(file_path, title, categoryId, description, privatcyStatus, tags)
if output is True:
print(f"Title: {title} | Was successfully uploaded.")
DB.mark_as_uploaded(db_id)
else:
print(f"Title: {title} | Download Process failed.")
if output is True: def create_chats():
print(f"Slug: {slug} | Was successfully uploaded.") pass
DB.mark_as_uploaded(slug)
else:
print(f"Slug: {slug} | Upload Process failed.")
def create_chat_vods(): def create_shorts():
pass pass
def get_vod_ids_simplified(channel_name: str): def get_vod_ids_simplified(channel_name: str):
"""Queries Twitch's public endpoint directly for trending clips."""
session = requests.Session() session = requests.Session()
url = "https://gql.twitch.tv/gql" url = "https://gql.twitch.tv/gql"
@@ -151,8 +158,7 @@ def get_vod_ids_simplified(channel_name: str):
"Content-Type": "text/plain" "Content-Type": "text/plain"
} }
# A simplified query string that hardcodes the ARCHIVE type filter into the request structure vods_query_string = """
query_string = """
query GetChannelVideos($login: String!, $limit: Int!) { query GetChannelVideos($login: String!, $limit: Int!) {
user(login: $login) { user(login: $login) {
videos(first: $limit, types: [ARCHIVE]) { videos(first: $limit, types: [ARCHIVE]) {
@@ -171,12 +177,44 @@ def get_vod_ids_simplified(channel_name: str):
} }
""" """
clips_query_string = """
query GetChannelClips($login: String!, $limit: Int!) {
user(login: $login) {
clips(first: $limit, criteria: { period: ALL_TIME }) {
edges {
node {
slug
title
createdAt
viewCount
game {
displayName
}
curator {
login
}
}
}
}
}
}
"""
limit = 50
query_string = ""
if DB.table == "vods":
query_string = vods_query_string
limit = 50
elif DB.table == "clips":
query_string = clips_query_string
limit = 40
# A simplified query string that hardcodes the ARCHIVE type filter into the request structure
payload = [{ payload = [{
"operationName": "GetChannelVideos", "operationName": "GetChannelVideos",
"query": query_string, "query": query_string,
"variables": { "variables": {
"login": channel_name.lower(), "login": channel_name.lower(),
"limit": 50 "limit": limit
} }
}] }]
@@ -202,8 +240,12 @@ def get_vod_ids_simplified(channel_name: str):
print(f"Channel '{channel_name}' not found.") print(f"Channel '{channel_name}' not found.")
return [] return []
edges = user_data['videos']['edges'] edges = None
vod_ids = [] if DB.table == "vods":
edges = user_data['videos']['edges']
elif DB.table == "clips":
edges = user_data['clips']['edges']
video_ids = []
print(f"--- Latest VODs for {channel_name} ---") print(f"--- Latest VODs for {channel_name} ---")
for edge in edges: for edge in edges:
@@ -212,15 +254,24 @@ def get_vod_ids_simplified(channel_name: str):
# Safe extraction in case a VOD has no category set (Just Chatting, Uncategorized, etc.) # Safe extraction in case a VOD has no category set (Just Chatting, Uncategorized, etc.)
game_info = node.get('game') game_info = node.get('game')
game_name = game_info.get('displayName') if game_info else "Unknown/No Category" game_name = game_info.get('displayName') if game_info else "Unknown/No Category"
if DB.table == "vods":
print(f"ID: {node['id']} | Date: {node['publishedAt']} | Game: {game_name} | Title: {node['title']}")
print(f"ID: {node['id']} | Date: {node['publishedAt']} | Game: {game_name} | Title: {node['title']}") # Pass game_name to your database logic
DB.insert_vods_record(node['id'], node['publishedAt'], node['title'], game_name)
#insert_record(node['id'], node['publishedAt'], node['title'], game_name)
video_ids.append(node['id'])
elif DB.table == "clips":
# Safe extraction in case the curator account was deleted/missing
curator_info = node.get('curator')
clip_by = curator_info.get('login') if curator_info else "Unknown Creator"
# Pass game_name to your database logic print(f"Slug: {node['slug']} | Date: {node['createdAt']} | Game: {game_name} | By: {clip_by} | Views: {node['viewCount']} | Title: {node['title']}")
DB.insert_vods_record(node['id'], node['publishedAt'], node['title'], game_name)
#insert_record(node['id'], node['publishedAt'], node['title'], game_name)
vod_ids.append(node['id'])
return vod_ids DB.insert_clips_record(node['slug'], node['createdAt'], node['title'], game_name, clip_by, int(node['viewCount']))
video_ids.append(node['slug'])
return video_ids
except Exception as e: except Exception as e:
print(f"An unexpected error occurred: {e}") print(f"An unexpected error occurred: {e}")
@@ -232,4 +283,4 @@ if __name__ == "__main__":
get_vod_ids_simplified(CHANNEL_NAME) get_vod_ids_simplified(CHANNEL_NAME)
download_vods() download_vods()
upload_vods() upload_vods()
#close_database() DB.close_database()
+2 -1
View File
@@ -8,6 +8,7 @@ import nltk
# Download necessary NLTK data # Download necessary NLTK data
nltk.download('punkt', quiet=True) nltk.download('punkt', quiet=True)
nltk.download('stopwords', quiet=True) nltk.download('stopwords', quiet=True)
nltk.download('punkt_tab', quiet=True)
def extract_text_from_srt(file_path): def extract_text_from_srt(file_path):
with open(file_path, 'r', encoding='utf-8') as file: with open(file_path, 'r', encoding='utf-8') as file:
@@ -38,7 +39,7 @@ def get_top_hashtags(srt_file_path, top_n=10):
word_counts = Counter(filtered_words) word_counts = Counter(filtered_words)
top_words = word_counts.most_common(top_n) top_words = word_counts.most_common(top_n)
hashtags = [f"#{word[0]}" for word in top_words] hashtags = [f"{word[0]}" for word in top_words]
return hashtags return hashtags
if __name__ == "__main__": if __name__ == "__main__":
+1 -1
View File
@@ -1,5 +1,5 @@
import sys import sys
from moviepy.editor import VideoFileClip from moviepy import VideoFileClip
def convert_to_short(input_path): def convert_to_short(input_path):