125 lines
3.6 KiB
Python
125 lines
3.6 KiB
Python
"""
|
|||
|
|
scripts/resolve_youtube_api_official.py
|
||
|
|
Queries the official YouTube Data API v3 to fetch verified channel subscriber
|
||
|
|
counts for remaining unverified cohort artists and writes directly to SQLite.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import os
|
||
|
|
import sys
|
||
|
|
import time
|
||
|
|
from pathlib import Path
|
||
|
|
import pandas as pd
|
||
|
|
import requests
|
||
|
|
from dotenv import load_dotenv
|
||
|
|
|
||
|
|
ROOT_DIR = Path(__file__).resolve().parent.parent
|
||
|
|
if str(ROOT_DIR) not in sys.path:
|
||
|
|
sys.path.insert(0, str(ROOT_DIR))
|
||
|
|
|
||
|
|
load_dotenv(ROOT_DIR / ".env")
|
||
|
|
|
||
|
|
from core.database import get_connection
|
||
|
|
|
||
|
|
API_KEY = os.getenv("YOUTUBE_API_KEY")
|
||
|
|
|
||
|
|
|
||
|
|
def get_channel_id_and_subs(artist_name):
|
||
|
|
"""Searches YouTube API for the top channel match and fetches subscriber count."""
|
||
|
|
search_url = "https://www.googleapis.com/youtube/v3/search"
|
||
|
|
search_params = {
|
||
|
|
"part": "snippet",
|
||
|
|
"q": f"{artist_name} official channel",
|
||
|
|
"type": "channel",
|
||
|
|
"maxResults": 1,
|
||
|
|
"key": API_KEY,
|
||
|
|
}
|
||
|
|
|
||
|
|
try:
|
||
|
|
r = requests.get(search_url, params=search_params, timeout=10)
|
||
|
|
if r.status_code != 200:
|
||
|
|
return None, 0
|
||
|
|
|
||
|
|
items = r.json().get("items", [])
|
||
|
|
if not items:
|
||
|
|
return None, 0
|
||
|
|
|
||
|
|
channel_id = items[0]["id"]["channelId"]
|
||
|
|
|
||
|
|
stats_url = "https://www.googleapis.com/youtube/v3/channels"
|
||
|
|
stats_params = {
|
||
|
|
"part": "statistics",
|
||
|
|
"id": channel_id,
|
||
|
|
"key": API_KEY,
|
||
|
|
}
|
||
|
|
|
||
|
|
r_stats = requests.get(stats_url, params=stats_params, timeout=10)
|
||
|
|
if r_stats.status_code == 200:
|
||
|
|
stat_items = r_stats.json().get("items", [])
|
||
|
|
if stat_items:
|
||
|
|
subs = int(
|
||
|
|
stat_items[0].get("statistics", {}).get("subscriberCount", 0)
|
||
|
|
)
|
||
|
|
return channel_id, subs
|
||
|
|
except Exception:
|
||
|
|
pass
|
||
|
|
|
||
|
|
return None, 0
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
print("=" * 65)
|
||
|
|
print("HIFFI INTELLIGENCE: OFFICIAL YOUTUBE API RESOLUTION")
|
||
|
|
print("=" * 65)
|
||
|
|
|
||
|
|
if not API_KEY:
|
||
|
|
print("[!] ERROR: YOUTUBE_API_KEY is not configured in .env.")
|
||
|
|
return
|
||
|
|
|
||
|
|
df500 = pd.read_csv(ROOT_DIR / "data" / "hiffi_multiplatform_500.csv")
|
||
|
|
cohort_names = [n.strip() for n in df500["name"]]
|
||
|
|
|
||
|
|
conn = get_connection()
|
||
|
|
cur = conn.cursor()
|
||
|
|
|
||
|
|
# Target cohort artists with 0 or low placeholder subscriber counts (< 1000)
|
||
|
|
cur.execute("""
|
||
|
|
SELECT a.artist_name, COALESCE(t.subscribers_count, 0)
|
||
|
|
FROM artists a
|
||
|
|
JOIN telemetry_metrics t ON a.artist_id = t.artist_id
|
||
|
|
WHERE t.subscribers_count IS NULL OR t.subscribers_count < 1000
|
||
|
|
""")
|
||
|
|
rows = cur.fetchall()
|
||
|
|
|
||
|
|
targets = [r[0] for r in rows if r[0] in cohort_names]
|
||
|
|
print(f"Targeting {len(targets)} unresolved cohort artists via official API...\n")
|
||
|
|
|
||
|
|
updated = 0
|
||
|
|
for idx, name in enumerate(targets, 1):
|
||
|
|
channel_id, subs = get_channel_id_and_subs(name)
|
||
|
|
|
||
|
|
if subs > 0:
|
||
|
|
updated += 1
|
||
|
|
cur.execute("""
|
||
|
|
UPDATE telemetry_metrics
|
||
|
|
SET subscribers_count = ?
|
||
|
|
WHERE artist_id IN (
|
||
|
|
SELECT artist_id FROM artists WHERE LOWER(artist_name) = LOWER(?)
|
||
|
|
)
|
||
|
|
""", (subs, name))
|
||
|
|
print(f"[{idx:>3}/{len(targets)}] {name:<25} -> {subs:,} subs (ID: {channel_id})")
|
||
|
|
else:
|
||
|
|
print(f"[{idx:>3}/{len(targets)}] {name:<25} -> No channel resolved")
|
||
|
|
|
||
|
|
# Sleep briefly to respect API rate limits
|
||
|
|
time.sleep(0.1)
|
||
|
|
|
||
|
|
conn.commit()
|
||
|
|
conn.close()
|
||
|
|
|
||
|
|
print("\n" + "=" * 65)
|
||
|
|
print(f"Completed: Successfully verified and updated {updated} / {len(targets)} artists.")
|
||
|
|
print("=" * 65)
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|