Oke bro, ini dia. Gua bikin versi producer_no_sub.py.
import requests
import os
import subprocess
import re
import time
import shutil
import urllib3
import threading
import queue
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# ================= KONFIGURASI =================
API_CODE = "TOKENMU"
INPUT_FILE = "drama_ids.txt"
OUTPUT_FOLDER = "merged"
# Pilih bahasa (in, en, dll)
LANG = "en"
# Intro
INTRO_VERTICAL = "intro916.mp4"
INTRO_HORIZONTAL = "intro169.mp4"
# Jumlah thread download paralel (Jangan terlalu banyak biar ga di-block)
DOWNLOAD_THREADS = 2
# ===============================================
download_queue = queue.Queue()
episode_paths = {} # Untuk nyimpen path hasil download
def log(msg):
print(f"[{time.strftime('%H:%M:%S')}] {msg}")
def clean_title(name):
name = re.sub(r'\$Sulih suara\$', 'IndoDubbing', name, flags=re.IGNORECASE)
name = name.replace("(", "").replace(")", "")
name = re.sub(r'[\\/*?:"<>|]', "", name)
return " ".join(name.split())
def get_info(drama_id):
try:
# Pake variabel LANG
r = requests.get(f"https://netshort.dramabos.online/api/drama/{drama_id}?lang={LANG}", headers={'User-Agent': 'Mozilla'}, verify=False, timeout=10)
if r.status_code == 200:
d = r.json()['data']
return d.get('shortPlayName'), d.get('totalEpisode')
except: pass
return None, 0
def download_file(url, filename):
try:
with requests.get(url, headers={'User-Agent': 'Mozilla'}, stream=True, verify=False) as r:
with open(filename, 'wb') as f:
for c in r.iter_content(8192): f.write(c)
return True
except: return False
def get_resolution(path):
try:
cmd = ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=width,height", "-of", "csv=s=x:p=0", path]
res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
w, h = map(int, res.stdout.decode().strip().split('x'))
return w, h
except: return 0, 0
def remove_id_from_file(drama_id):
try:
with open(INPUT_FILE, "r") as f: lines = f.readlines()
new_lines = [line for line in lines if line.strip() != drama_id]
with open(INPUT_FILE, "w") as f: f.writelines(new_lines)
except: pass
# --- WORKER DOWNLOAD ---
def worker_download(drama_id, folder_proc):
while True:
ep = download_queue.get()
if ep is None:
break
try:
final_out = os.path.join(folder_proc, f"{ep}.mp4")
# Skip kalau sudah ada
if os.path.exists(final_out):
download_queue.task_done()
continue
log(f" [DL] Ep {ep}...")
watch_url = f"https://netshort.dramabos.online/api/watch/{drama_id}/{ep}?lang={LANG}&code={API_CODE}"
res = requests.get(watch_url, headers={'User-Agent': 'Mozilla'}, verify=False, timeout=15)
data = res.json()
if data.get('success'):
v_url = data['data']['videoUrl']
# Langsung download ke folder processed (tanpa hardsub)
if download_file(v_url, final_out):
pass # Sukses
else:
log(f" [GAGAL] Download Ep {ep}")
else:
log(f" [GAGAL] API Ep {ep}")
except Exception as e:
log(f" Error Ep {ep}: {e}")
download_queue.task_done()
def process(id):
final_path = os.path.join(OUTPUT_FOLDER, f"{id}.mp4")
if os.path.exists(final_path):
log(f"ID {id} sudah ada, skip.");
remove_id_from_file(id)
return
title, total = get_info(id)
if not title:
log(f"ID {id} Gagal ambil info."); return
log(f"Memproses ID {id} - {title} ({total} Ep) [Mode: No Sub]")
folder_proc = f"temp_proc_{id}"
os.makedirs(folder_proc, exist_ok=True)
# Isi Queue
for ep in range(1, total + 1):
download_queue.put(ep)
# Jalankan Thread Download
threads = []
for _ in range(DOWNLOAD_THREADS):
t = threading.Thread(target=worker_download, args=(id, folder_proc))
t.start()
threads.append(t)
# Tunggu semua selesai
download_queue.join()
# Stop thread
for _ in range(DOWNLOAD_THREADS): download_queue.put(None)
for t in threads: t.join()
log(f" Download selesai. Merging...")
# --- MERGE ---
files = []
if os.path.exists(folder_proc):
files = [os.path.join(folder_proc, f"{i}.mp4") for i in range(1, total+1) if os.path.exists(os.path.join(folder_proc, f"{i}.mp4"))]
if files:
# Deteksi Intro
intro = None
ep1_path = os.path.join(folder_proc, "1.mp4")
if os.path.exists(ep1_path):
w, h = get_resolution(ep1_path)
if h > w: intro = INTRO_VERTICAL if os.path.exists(INTRO_VERTICAL) else None
else: intro = INTRO_HORIZONTAL if os.path.exists(INTRO_HORIZONTAL) else None
if intro: files.insert(0, intro)
list_file = "list.txt"
with open(list_file, "w") as f:
for x in files: f.write(f"file '{x}'\n")
temp_merge = f"{id}_temp.mp4"
cmd = ["ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", list_file, "-c", "copy", temp_merge]
subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if os.path.exists(temp_merge):
shutil.move(temp_merge, final_path)
with open(final_path + ".info", "w") as f: f.write(clean_title(title))
log(f"FINISHED ID {id}.")
remove_id_from_file(id)
if os.path.exists(list_file): os.remove(list_file)
if os.path.exists(folder_proc): shutil.rmtree(folder_proc)
def main():
if not os.path.exists(INPUT_FILE):
print(f"ERROR: File {INPUT_FILE} tidak ditemukan!")
return
if not os.path.exists(OUTPUT_FOLDER): os.makedirs(OUTPUT_FOLDER)
with open(INPUT_FILE) as f: ids = [x.strip() for x in f if x.strip()]
log(f"Total {len(ids)} drama antrian.")
for id in ids: process(id)
log("Semua proses selesai.")
if __name__ == "__main__":
main()
Cara Pakai:
Script ini akan jauh lebih ringan dan cepat karena langsung download -> merge (tanpa proses burn subtitle).
import os
import time
import requests
import boto3
from botocore.client import Config
import urllib3
import csv
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# ================= KONFIGURASI =================
MERGED_FOLDER = "merged"
CSV_FILE = "database.csv"
# R2 Config
R2_ACCOUNT_ID = "" # Isi disini
R2_ACCESS_KEY = "" # Isi disini
R2_SECRET_KEY = "" # Isi disini
R2_BUCKET = "netshort-dramas"
R2_PUBLIC_URL = "https://pub-xxxdff9bbed46d6a8f79059b0c42944.r2.dev"
# ===============================================
def log(msg): print(f"[Consumer] {msg}")
def upload_r2(path, name):
try:
s3 = boto3.client('s3', endpoint_url=f"https://{R2_ACCOUNT_ID}.r2.cloudflarestorage.com",
aws_access_key_id=R2_ACCESS_KEY, aws_secret_access_key=R2_SECRET_KEY,
config=Config(signature_version='s3v4'), region_name='auto')
s3.upload_file(path, R2_BUCKET, name)
return f"{R2_PUBLIC_URL}/{name}"
except Exception as e:
log(f"R2 Error: {e}")
return None
def main():
log("Consumer started. Watching folder 'merged/'...")
# Init CSV
if not os.path.exists(CSV_FILE):
with open(CSV_FILE, 'w', newline='') as f:
w = csv.writer(f)
w.writerow(['drama_id', 'title', 'r2_url'])
while True:
try:
files = [x for x in os.listdir(MERGED_FOLDER) if x.endswith('.mp4')]
for file in files:
filepath = os.path.join(MERGED_FOLDER, file)
drama_id = file.replace('.mp4', '')
info_file = filepath + ".info"
# Ambil judul
title = drama_id
if os.path.exists(info_file):
with open(info_file, 'r') as f: title = f.read().strip()
log(f"Processing {file} ({title})...")
# 1. Upload R2
r2_url = upload_r2(filepath, file)
if r2_url:
log(f" R2 OK: {r2_url}")
# 2. Write CSV
with open(CSV_FILE, 'a', newline='') as f:
w = csv.writer(f)
w.writerow([drama_id, title, r2_url])
# 3. Cleanup
log(f" Cleaning up...")
os.remove(filepath)
if os.path.exists(info_file): os.remove(info_file)
log(f" DONE for {drama_id}.")
else:
log(f" R2 Failed. File kept for retry.")
time.sleep(5)
except KeyboardInterrupt:
log("Stopped.")
break
except Exception as e:
log(f"Error: {e}")
time.sleep(10)
if __name__ == "__main__":
main()
Sekarang prosesnya bakal jauh lebih cepet karena cuma nunggu 1 upload (R2) doang. Kalau lu mau upload manual ke tempat lain nanti, tinggal ambil link R2 dari file database.csv.