import os os.environ["HF_HUB_DISABLE_TELEMETRY"] = "1" import gradio as gr import uuid, json, requests, subprocess, shutil, random, re from pathlib import Path from googleapiclient.discovery import build from googleapiclient.http import MediaFileUpload from google.oauth2.credentials import Credentials from google.auth.transport.requests import Request # ───────────────────────────────────────── # AYARLAR # ───────────────────────────────────────── PEXELS_API_KEY = os.getenv("PEXELS_API", "") PIXABAY_API_KEY = os.getenv("PIXABAY_API", "") DRIVE_SCOPES = ["https://www.googleapis.com/auth/drive.file"] WORK_DIR = Path("work") WORK_DIR.mkdir(exist_ok=True) REDDIT_SUBS = { "😂 Komedi": ["funny", "Whatcouldgowrong", "instant_regret", "contagious_laughter"], "🔥 Viral": ["nextfuckinglevel", "interestingasfuck", "Damnthatsinteresting", "BeAmazed"], "🐾 Hayvanlar": ["aww", "AnimalsBeingBros", "likeus", "rarepuppers"], "🏆 Spor": ["sports", "MaddenGIFs", "hitmanimals", "holdmybeer"], "🌍 Dünya": ["worldnews", "europe", "CrazyFuckingVideos", "PublicFreakout"], "✨ Hepsi Karışık": ["funny", "nextfuckinglevel", "aww", "interestingasfuck", "BeAmazed"], } ARCHIVE_QUERIES = { "😂 Komedi": ["comedy short film", "funny animation", "slapstick"], "🔥 Viral": ["viral video", "amazing stunt", "incredible moment"], "🐾 Hayvanlar": ["animals nature", "wildlife documentary", "cute animals"], "🏆 Spor": ["sports highlights", "athletic amazing", "competition"], "🌍 Dünya": ["world culture", "travel documentary", "street life"], "✨ Hepsi Karışık": ["funny", "amazing", "nature", "documentary short"], } def uid(): return uuid.uuid4().hex[:8] def cleanup_work(): for f in WORK_DIR.glob("*"): try: f.unlink() except: pass # ───────────────────────────────────────── # REDDIT # ───────────────────────────────────────── def fetch_reddit_videos(category, count=4): subs = REDDIT_SUBS.get(category, REDDIT_SUBS["✨ Hepsi Karışık"]) results = [] headers = {"User-Agent": "VideoBot/1.0"} for sub in random.sample(subs, min(2, len(subs))): try: url = f"https://www.reddit.com/r/{sub}/hot.json?limit=25" r = requests.get(url, headers=headers, timeout=15) posts = r.json()["data"]["children"] for post in posts: d = post["data"] # Sadece video postları if not d.get("is_video"): continue media = d.get("media", {}) if not media: continue reddit_video = media.get("reddit_video", {}) video_url = reddit_video.get("fallback_url", "") if not video_url: continue # DASH url düzelt video_url = video_url.split("?")[0] results.append({ "url": video_url, "title": d.get("title", "Reddit Video")[:60], "source": f"Reddit/r/{sub}", "duration": reddit_video.get("duration", 30) }) if len(results) >= count: return results except Exception as e: print(f"Reddit hata ({sub}): {e}") return results # ───────────────────────────────────────── # ARCHIVE.ORG # ───────────────────────────────────────── def fetch_archive_videos(category, count=3): queries = ARCHIVE_QUERIES.get(category, ARCHIVE_QUERIES["✨ Hepsi Karışık"]) query = random.choice(queries) results = [] try: params = { "q": f"{query} AND mediatype:movies", "fl[]": ["identifier", "title"], "rows": 20, "output": "json", "sort[]": "downloads desc" } r = requests.get("https://archive.org/advancedsearch.php", params=params, timeout=15) items = r.json().get("response", {}).get("docs", []) random.shuffle(items) for item in items[:10]: ident = item.get("identifier", "") if not ident: continue try: meta_r = requests.get( f"https://archive.org/metadata/{ident}", timeout=10 ) files = meta_r.json().get("files", []) # mp4 dosyalarını bul mp4s = [f for f in files if f.get("name", "").endswith(".mp4")] if not mp4s: continue # En küçük mp4'ü seç (hız için) mp4s.sort(key=lambda x: int(x.get("size", 999999999))) chosen = mp4s[0] video_url = f"https://archive.org/download/{ident}/{chosen['name']}" results.append({ "url": video_url, "title": item.get("title", ident)[:60], "source": "Archive.org" }) if len(results) >= count: break except: continue except Exception as e: print(f"Archive hata: {e}") return results # ───────────────────────────────────────── # PEXELS (fallback) # ───────────────────────────────────────── def fetch_pexels(query, count=2): if not PEXELS_API_KEY: return [] try: headers = {"Authorization": PEXELS_API_KEY} params = {"query": query, "per_page": count} r = requests.get("https://api.pexels.com/videos/search", headers=headers, params=params, timeout=15) videos = r.json().get("videos", []) results = [] for v in videos: files = v.get("video_files", []) good = [f for f in files if 400 <= f.get("width", 0) <= 1280] best = sorted(good or files, key=lambda x: x.get("width", 0))[-1] results.append({ "url": best["link"], "title": f"Pexels-{v.get('id','')}", "source": "Pexels" }) return results except Exception as e: print(f"Pexels hata: {e}") return [] # ───────────────────────────────────────── # İNDİR # ───────────────────────────────────────── def download_video(url, source="", timeout=90): out = str(WORK_DIR / f"dl_{uid()}.mp4") try: r = requests.get(url, timeout=timeout, stream=True, headers={"User-Agent": "VideoBot/1.0"}) if r.status_code != 200: return None with open(out, "wb") as f: for chunk in r.iter_content(16384): f.write(chunk) size = os.path.getsize(out) if size > 50_000: # En az 50KB return out os.remove(out) except Exception as e: print(f"İndirme hatası ({source}): {e}") return None # ───────────────────────────────────────── # FFMPEG # ───────────────────────────────────────── def get_duration(path): try: r = subprocess.run( ["ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", path], capture_output=True, text=True, timeout=15 ) return float(json.loads(r.stdout)["format"]["duration"]) except: return 30.0 def cut_clip(path, start, duration): out = str(WORK_DIR / f"cut_{uid()}.mp4") try: subprocess.run([ "ffmpeg", "-y", "-ss", str(start), "-i", path, "-t", str(duration), "-c:v", "libx264", "-preset", "ultrafast", "-c:a", "aac", "-avoid_negative_ts", "make_zero", out ], capture_output=True, timeout=60, check=True) if os.path.exists(out) and os.path.getsize(out) > 1000: return out except Exception as e: print(f"Kesme hatası: {e}") return None def normalize_clip(path): out = str(WORK_DIR / f"norm_{uid()}.mp4") try: subprocess.run([ "ffmpeg", "-y", "-i", path, "-vf", "scale=1280:720:force_original_aspect_ratio=decrease," "pad=1280:720:(ow-iw)/2:(oh-ih)/2,setsar=1", "-c:v", "libx264", "-preset", "ultrafast", "-c:a", "aac", "-ar", "44100", "-ac", "2", "-r", "30", out ], capture_output=True, timeout=60, check=True) if os.path.exists(out) and os.path.getsize(out) > 1000: return out except: pass return path def merge_clips(clips): valid = [c for c in clips if c and os.path.exists(c)] if not valid: return None if len(valid) == 1: out = str(WORK_DIR / f"final_{uid()}.mp4") shutil.copy(valid[0], out) return out list_file = str(WORK_DIR / f"list_{uid()}.txt") out = str(WORK_DIR / f"final_{uid()}.mp4") with open(list_file, "w") as f: for c in valid: f.write(f"file '{os.path.abspath(c)}'\n") try: subprocess.run([ "ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", list_file, "-c:v", "libx264", "-preset", "ultrafast", "-c:a", "aac", out ], capture_output=True, timeout=180, check=True) if os.path.exists(out) and os.path.getsize(out) > 1000: return out except Exception as e: print(f"Birleştirme hatası: {e}") return None def add_watermark(path, text="@shorts"): out = str(WORK_DIR / f"wm_{uid()}.mp4") safe = text.replace("'", "") try: subprocess.run([ "ffmpeg", "-y", "-i", path, "-vf", f"drawtext=text='{safe}':fontsize=36:fontcolor=white@0.7:" "borderw=2:bordercolor=black@0.5:x=20:y=20", "-c:v", "libx264", "-preset", "ultrafast", "-c:a", "copy", out ], capture_output=True, timeout=60, check=True) if os.path.exists(out) and os.path.getsize(out) > 1000: return out except: pass return path # ───────────────────────────────────────── # GOOGLE DRIVE # ───────────────────────────────────────── def upload_to_drive(file_path, filename, token_json, folder_name="Video Fabrikası"): if not token_json or not token_json.strip(): return False, "Drive token girilmedi." try: creds = Credentials.from_authorized_user_info( json.loads(token_json), DRIVE_SCOPES) if not creds.valid and creds.expired and creds.refresh_token: creds.refresh(Request()) service = build("drive", "v3", credentials=creds) q = f"mimeType='application/vnd.google-apps.folder' and name='{folder_name}' and trashed=false" res = service.files().list(q=q, fields="files(id)").execute() folders = res.get("files", []) if folders: folder_id = folders[0]["id"] else: folder = service.files().create( body={"name": folder_name, "mimeType": "application/vnd.google-apps.folder"}, fields="id").execute() folder_id = folder["id"] media = MediaFileUpload(file_path, mimetype="video/mp4", resumable=True) uploaded = service.files().create( body={"name": filename, "parents": [folder_id]}, media_body=media, fields="id,webViewLink" ).execute() return True, f"✅ Drive'a yüklendi!\n🔗 {uploaded.get('webViewLink', uploaded['id'])}" except Exception as e: return False, f"Drive hatası: {e}" # ───────────────────────────────────────── # ANA SÜREÇ # ───────────────────────────────────────── def main_process(category, clip_duration, clip_count, watermark_text, drive_token): logs = "" def log(msg): nonlocal logs logs += msg + "\n" return logs all_clips = [] clip_count = int(clip_count) clip_duration = int(clip_duration) try: log(f"🚀 Başlıyor | Kategori: {category} | {clip_count} klip × {clip_duration}sn") log("─" * 45) yield logs # 1. Kaynak topla log("\n📡 1. Videolar aranıyor...") yield logs sources = [] # Reddit log(" → Reddit taranıyor...") yield logs reddit = fetch_reddit_videos(category, count=clip_count) sources.extend(reddit) log(f" ✅ Reddit: {len(reddit)} video bulundu") yield logs # Archive.org log(" → Archive.org taranıyor...") yield logs archive = fetch_archive_videos(category, count=clip_count) sources.extend(archive) log(f" ✅ Archive.org: {len(archive)} video bulundu") yield logs # Pexels fallback if len(sources) < clip_count and PEXELS_API_KEY: log(" → Pexels'tan tamamlanıyor...") yield logs query = random.choice(ARCHIVE_QUERIES.get(category, ["nature"])) pexels = fetch_pexels(query, count=clip_count) sources.extend(pexels) log(f" ✅ Pexels: {len(pexels)} video eklendi") yield logs if not sources: log("\n💥 Hiç kaynak bulunamadı. İnternet bağlantısını kontrol et.") yield logs return random.shuffle(sources) log(f"\n Toplam {len(sources)} kaynak, {clip_count} tanesi kullanılacak.") yield logs # 2. İndir + Kes log("\n📥 2. İndiriliyor ve kesiliyor...") yield logs used = 0 for i, src in enumerate(sources): if used >= clip_count: break log(f"\n [{used+1}/{clip_count}] {src['source']}") log(f" 📌 {src['title'][:50]}") yield logs path = download_video(src["url"], src["source"]) if not path: log(" ❌ İndirilemedi, sonraki deneniyor...") yield logs continue size_kb = os.path.getsize(path) // 1024 log(f" ✅ İndirildi ({size_kb} KB)") yield logs total = get_duration(path) if total < clip_duration: start = 0 else: # Videonun %20-%70 arasından kes (başı ve sonu genelde boş) max_start = total - clip_duration start = random.uniform(total * 0.1, min(total * 0.6, max_start)) cut = cut_clip(path, int(start), clip_duration) if not cut: log(" ⚠️ Kesilemedi, atlanıyor.") yield logs continue norm = normalize_clip(cut) all_clips.append(norm) used += 1 log(f" 🎬 Klip {used} hazır") yield logs if not all_clips: log("\n💥 Hiç klip oluşturulamadı.") yield logs return # 3. Birleştir log(f"\n🎞️ 3. {len(all_clips)} klip birleştiriliyor...") yield logs final = merge_clips(all_clips) if not final: log("💥 Birleştirme başarısız.") yield logs return size_mb = os.path.getsize(final) // 1024 // 1024 log(f" ✅ Video hazır! ({size_mb} MB, ~{len(all_clips)*clip_duration}sn)") yield logs # 4. Watermark if watermark_text.strip(): log(f"\n✍️ 4. Watermark ekleniyor: {watermark_text}") yield logs final = add_watermark(final, watermark_text.strip()) log(" ✅ Eklendi") yield logs # 5. Drive log("\n☁️ 5. Google Drive'a yükleniyor...") yield logs filename = f"{category.split()[1]}_{uid()}.mp4" success, result = upload_to_drive(final, filename, drive_token) log(f" {result}") yield logs # 6. Temizlik cleanup_work() log("\n" + "─" * 45) log("🎉 TAMAMLANDI!") yield logs except Exception as e: log(f"\n💥 KRİTİK HATA: {e}") cleanup_work() yield logs # ───────────────────────────────────────── # DURUM # ───────────────────────────────────────── def check_status(): lines = ["── SİSTEM DURUMU ──────────────────"] lines.append("✅ Reddit (API key gerekmez)") lines.append("✅ Archive.org (API key gerekmez)") lines.append(f"{'✅' if PEXELS_API_KEY else '⚠️ '} Pexels (isteğe bağlı fallback)") try: subprocess.run(["ffmpeg", "-version"], capture_output=True, check=True) lines.append("✅ FFmpeg") except: lines.append("❌ FFmpeg bulunamadı") lines.append("────────────────────────────────────") lines.append("🚀 Hazır! Reddit + Archive.org her zaman çalışır.") return "\n".join(lines) # ───────────────────────────────────────── # UI # ───────────────────────────────────────── with gr.Blocks( theme=gr.themes.Soft(primary_hue="violet", secondary_hue="purple"), title="🎬 Video Fabrikası" ) as demo: gr.Markdown(""" # 🎬 Video Fabrikası **Reddit + Archive.org → Otomatik kes & birleştir → Drive'a kaydet** """) # Drive token - her zaman üstte with gr.Row(): drive_token_input = gr.Textbox( label="☁️ Google Drive Token", placeholder='{"token": "...", "refresh_token": "..."}', type="password", scale=4 ) with gr.Tab("📊 Durum"): status_box = gr.Textbox(value=check_status(), lines=8, interactive=False, label="Sistem Durumu") gr.Button("🔄 Yenile").click(check_status, outputs=status_box) with gr.Tab("🚀 Video Üret"): with gr.Row(): with gr.Column(scale=2): category_input = gr.Dropdown( choices=list(REDDIT_SUBS.keys()), value="✨ Hepsi Karışık", label="📂 Kategori" ) with gr.Row(): clip_count_input = gr.Slider( 2, 8, value=4, step=1, label="🎞️ Klip sayısı" ) clip_duration_input = gr.Slider( 5, 30, value=10, step=5, label="⏱️ Klip süresi (sn)" ) watermark_input = gr.Textbox( label="✍️ Watermark (boş bırakılabilir)", placeholder="@kanaladi", value="" ) with gr.Column(scale=1): gr.Markdown(""" **💡 Bilgi:** - Reddit & Archive.org tamamen ücretsiz - API key gerekmez - 4 klip × 10sn = ~40sn video - Drive token olmadan çalışır (yükleme atlanır) """) run_btn = gr.Button( "🚀 Tam Otomatik Çalıştır", variant="primary", size="lg" ) log_output = gr.Textbox( label="📋 Canlı Loglar", lines=25, interactive=False ) run_btn.click( main_process, inputs=[category_input, clip_duration_input, clip_count_input, watermark_input, drive_token_input], outputs=log_output ) with gr.Tab("🔑 Drive Token"): gr.Markdown(""" ### Termux'ta token al: **1. Script oluştur:** ``` nano drive_token.py ``` **2. İçine yaz:** ```python from google_auth_oauthlib.flow import InstalledAppFlow SCOPES = ['https://www.googleapis.com/auth/drive.file'] flow = InstalledAppFlow.from_client_secrets_file('sok.json', SCOPES) auth_url, _ = flow.authorization_url( prompt='consent', redirect_uri='urn:ietf:wg:oauth:2.0:oob' ) print(auth_url) code = input("Kodu gir: ") flow.fetch_token(code=code, redirect_uri='urn:ietf:wg:oauth:2.0:oob') print(flow.credentials.to_json()) ``` **3. Çalıştır:** ``` python3 drive_token.py ``` **4.** Çıkan JSON'u üstteki Drive Token kutusuna yapıştır. """) with gr.Tab("ℹ️ Hakkında"): gr.Markdown(""" ### Kaynaklar - **Reddit** — Viral, komedi, hayvan, spor videoları (ücretsiz) - **Archive.org** — Kamu domain videolar (ücretsiz) - **Pexels** — Stok video fallback (isteğe bağlı) ### HF Secrets (İsteğe Bağlı) - `PEXELS_API` — Pexels fallback için """) if __name__ == "__main__": demo.launch(share=False)