Files
offline-db/main.py
google-labs-jules[bot] af541b0bf8 refactor: Centralize Jikan API client
Created a dedicated Jikan API client to handle all requests, rate limiting, and error handling.

Refactored `main.py` and `blueprints/api.py` to use the new client, removing redundant code and improving maintainability.
2026-01-15 17:07:33 +00:00

825 lines
30 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Anime Offline Veritabanı Yöneticisi
Kullanım:
python main.py --init # Veritabanını başlat
python main.py --guncelle "Naruto" # İsme göre güncelle
python main.py --mal_id 20 # MAL ID'ye göre güncelle
python main.py --toplu # anime_ids.json'dan toplu güncelle
python main.py --toplu --limit 100 # İlk 100 anime'yi güncelle
python main.py --videolar 20 # MAL ID için video linklerini göster
"""
import argparse
import json
import os
import sys
import re
import requests
import threading
from pathlib import Path
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor, as_completed
from difflib import SequenceMatcher
from typing import Dict, Any, cast, Optional, List, Set, Union
# Proje modülleri
from config import (
JIKAN_API_BASE, JIKAN_RATE_LIMIT, COVER_DIR,
UPDATED_IDS_FILE, ADAPTERS, MAX_WORKERS, HTTP_TIMEOUT
)
import db
from jikan_client import jikan
# Adaptörler
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from adapters import animecix, animely, anizle, tranime, turkanime
# ─────────────────────────────────────────────────────────────────────────────
# YARDIMCI FONKSİYONLAR
# ─────────────────────────────────────────────────────────────────────────────
def load_anime_ids() -> dict:
"""anime_ids.json dosyasını yükle."""
try:
with open("anime_ids.json", "r", encoding="utf-8") as f:
return json.load(f)
except FileNotFoundError:
print("[HATA] anime_ids.json bulunamadı!")
return {}
except json.JSONDecodeError:
print("[HATA] anime_ids.json geçersiz JSON!")
return {}
def load_updated_ids() -> set:
"""Güncellenen MAL ID'lerini yükle."""
try:
with open(UPDATED_IDS_FILE, "r", encoding="utf-8") as f:
return set(json.load(f))
except (FileNotFoundError, json.JSONDecodeError):
return set()
def save_updated_ids(updated_ids: set):
"""Güncellenen MAL ID'lerini kaydet."""
with open(UPDATED_IDS_FILE, "w", encoding="utf-8") as f:
json.dump(list(updated_ids), f)
def download_cover(url: str, mal_id: int) -> str:
"""Cover resmini indir ve yerel yolu döndür."""
if not url:
return ""
Path(COVER_DIR).mkdir(parents=True, exist_ok=True)
ext = url.split(".")[-1].split("?")[0]
if ext not in ["jpg", "jpeg", "png", "webp"]:
ext = "jpg"
local_path = f"{COVER_DIR}/{mal_id}.{ext}"
try:
response = requests.get(url, timeout=HTTP_TIMEOUT)
response.raise_for_status()
with open(local_path, "wb") as f:
f.write(response.content)
return local_path
except Exception as e:
print(f"[HATA] Cover indirilemedi ({mal_id}): {e}")
return ""
def similarity_score(query: str, text: str) -> float:
"""İki metin arasındaki benzerlik skorunu hesapla."""
if not text:
return 0.0
query_lower = query.lower()
text_lower = text.lower()
if query_lower == text_lower:
return 1.0
if query_lower in text_lower:
return 0.9
return SequenceMatcher(None, query_lower, text_lower).ratio()
from typing import Optional, Dict, Any
def parse_jikan_data(jikan_data: dict, extra_ids: Optional[Dict[str, Any]] = None) -> dict:
"""Jikan verisini veritabanı formatına dönüştür."""
if not jikan_data:
return {}
# Yayın tarihleri
aired = jikan_data.get("aired", {}) or {}
aired_from = aired.get("from")
aired_to = aired.get("to")
if aired_from:
aired_from = aired_from[:10] # YYYY-MM-DD
if aired_to:
aired_to = aired_to[:10]
# Broadcast
broadcast_data = jikan_data.get("broadcast", {}) or {}
broadcast = broadcast_data.get("string", "")
# Trailer
trailer_data = jikan_data.get("trailer", {}) or {}
trailer_url = trailer_data.get("embed_url", "")
# Cover
images = jikan_data.get("images", {}) or {}
jpg_images = images.get("jpg", {}) or {}
cover_url = jpg_images.get("large_image_url") or jpg_images.get("image_url", "")
anime_data = {
"mal_id": jikan_data.get("mal_id"),
"title": jikan_data.get("title", ""),
"title_english": jikan_data.get("title_english"),
"title_japanese": jikan_data.get("title_japanese"),
"type": jikan_data.get("type"),
"source": jikan_data.get("source"),
"episodes": jikan_data.get("episodes") or 0,
"status": jikan_data.get("status"),
"airing": jikan_data.get("airing", False),
"aired_from": aired_from,
"aired_to": aired_to,
"duration": jikan_data.get("duration"),
"rating": jikan_data.get("rating"),
"score": jikan_data.get("score"),
"scored_by": jikan_data.get("scored_by"),
"rank": jikan_data.get("rank"),
"popularity": jikan_data.get("popularity"),
"members": jikan_data.get("members"),
"favorites": jikan_data.get("favorites"),
"synopsis": jikan_data.get("synopsis"),
"background": jikan_data.get("background"),
"season": jikan_data.get("season"),
"year": jikan_data.get("year"),
"broadcast": broadcast,
"cover_url": cover_url,
"trailer_url": trailer_url,
}
# Extra ID'ler (anime_ids.json'dan)
if extra_ids:
anime_data["anidb_id"] = extra_ids.get("anidb_id")
anime_data["anilist_id"] = extra_ids.get("anilist_id")
anime_data["tvdb_id"] = extra_ids.get("tvdb_id")
anime_data["imdb_id"] = extra_ids.get("imdb_id")
return anime_data
def get_jikan_related_data(jikan_data: dict) -> dict:
"""Jikan verisinden ilişkili verileri çıkar (türler, temalar, stüdyolar, vb.)."""
return {
"titles": jikan_data.get("titles", []),
"genres": jikan_data.get("genres", []),
"themes": jikan_data.get("themes", []),
"studios": jikan_data.get("studios", []),
"producers": jikan_data.get("producers", []),
"licensors": jikan_data.get("licensors", []),
}
# ─────────────────────────────────────────────────────────────────────────────
# ADAPTÖR İŞLEMLERİ
# ─────────────────────────────────────────────────────────────────────────────
def search_in_adapters(title: Optional[str], title_english: Optional[str] = None, title_japanese: Optional[str] = None) -> dict:
"""
Tüm adaptörlerde anime ara.
Returns: {adapter_name: (source_id, source_slug, source_title)}
"""
results = {}
search_terms = [t for t in [title, title_english, title_japanese] if t]
if not search_terms:
return results
# AnimeCiX
if ADAPTERS.get("animecix"):
try:
for term in search_terms:
cix_results = animecix.search_animecix(term)
if cix_results:
best_match = max(cix_results, key=lambda x: similarity_score(term, x[1]))
if similarity_score(term, best_match[1]) > 0.5:
results["animecix"] = (best_match[0], best_match[0], best_match[1])
break
except Exception as e:
print(f"[AnimeCiX] Arama hatası: {e}")
# Animely - DEVRE DIŞI
# if ADAPTERS.get("animely"):
# try:
# for term in search_terms:
# try:
# aly_results = animely.search_animely(term, limit=10)
# if aly_results:
# best_match = max(aly_results, key=lambda x: similarity_score(term, x[1]))
# if similarity_score(term, best_match[1]) > 0.5:
# results["animely"] = (None, best_match[0], best_match[1])
# break
# except Exception:
# break
# except Exception as e:
# print(f"[Animely] Arama hatası: {e}")
# Anizle - hata toleranslı
if ADAPTERS.get("anizle"):
try:
for term in search_terms:
try:
anz_results = anizle.search_anizle(term, limit=10, timeout=10)
if anz_results:
best_match = max(anz_results, key=lambda x: similarity_score(term, x[1]))
if similarity_score(term, best_match[1]) > 0.5:
results["anizle"] = (None, best_match[0], best_match[1])
break
except Exception:
break
except Exception as e:
print(f"[Anizle] Arama hatası: {e}")
# TRAnime - hata toleranslı
if ADAPTERS.get("tranime"):
try:
for term in search_terms:
try:
tr_results = tranime.search_tranime(term, limit=10)
if tr_results:
best_match = max(tr_results, key=lambda x: similarity_score(term, x[1]))
if similarity_score(term, best_match[1]) > 0.5:
results["tranime"] = (None, best_match[0], best_match[1])
break
except Exception:
break
except Exception as e:
print(f"[TRAnime] Arama hatası: {e}")
# TurkAnime - hata toleranslı
if ADAPTERS.get("turkanime"):
try:
for term in search_terms:
try:
ta_results = turkanime.search_anime(term)
if ta_results:
best_match = max(ta_results, key=lambda x: similarity_score(term, x[2]))
if similarity_score(term, best_match[2]) > 0.5:
results["turkanime"] = (best_match[0], best_match[1], best_match[2])
break
except Exception:
break
except Exception as e:
print(f"[TurkAnime] Arama hatası: {e}")
return results
def fetch_episodes_from_animecix(source_id: str) -> list:
"""AnimeCiX'den bölümleri ve video linklerini çek."""
try:
anime = animecix.CixAnime(id=source_id, title="")
episodes = anime.episodes
result = []
for i, ep in enumerate(episodes):
ep_num = i + 1
# Bölüm numarasını isminden çıkarmaya çalış
match = re.search(r'(\d+)\.?\s*[Bb]ölüm', ep.title)
if match:
ep_num = int(match.group(1))
# Video linklerini çek
videos = []
if ep.url:
try:
streams = animecix._video_streams(ep.url)
for stream in streams:
videos.append({
"url": stream.get("url", ""),
"quality": stream.get("label", "default"),
"fansub": "AnimeCiX"
})
except Exception as ve:
print(f"[AnimeCiX] Video çekme hatası ({ep_num}): {ve}")
result.append({
"number": ep_num,
"title": ep.title,
"url": ep.url,
"videos": videos
})
return result
except Exception as e:
print(f"[AnimeCiX] Bölüm çekme hatası: {e}")
return []
def fetch_episodes_from_animely(slug: str) -> list:
"""Animely'den bölümleri ve video linklerini çek."""
try:
episodes = animely.get_anime_episodes(slug)
result = []
for ep in episodes:
videos = []
streams = ep.get_streams()
for stream in streams:
videos.append({
"url": stream.url,
"quality": stream.quality,
"fansub": stream.fansub
})
result.append({
"number": ep.episode_number,
"title": ep.name,
"videos": videos
})
return result
except Exception as e:
print(f"[Animely] Bölüm çekme hatası: {e}")
return []
def fetch_episodes_from_anizle(slug: str) -> list:
"""Anizle'den bölümleri ve video linklerini çek."""
try:
episodes = anizle.get_anime_episodes(slug)
result = []
for ep_slug, ep_title in episodes:
# Bölüm numarasını slug'dan çıkar
match = re.search(r'-(\d+)-bolum', ep_slug)
ep_num = int(match.group(1)) if match else 0
# Video linklerini çek
videos = []
try:
streams = anizle.get_episode_streams(ep_slug)
for stream in streams:
# stream formatı: {"url": "...", "label": "...", "type": "..."}
url = stream.get("url") or stream.get("videoUrl", "")
if url:
# Fansub bilgisini label'dan çıkar
label = stream.get("label", "")
fansub = "Anizle"
quality = "default"
if " - " in label:
parts = label.split(" - ", 1)
fansub = parts[0]
quality = parts[1] if len(parts) > 1 else "default"
else:
quality = label or "default"
videos.append({
"url": url,
"quality": quality,
"fansub": fansub
})
except Exception as ve:
print(f"[Anizle] Video çekme hatası ({ep_num}): {ve}")
result.append({
"number": ep_num,
"title": ep_title,
"slug": ep_slug,
"videos": videos
})
return result
except Exception as e:
print(f"[Anizle] Bölüm çekme hatası: {e}")
return []
def fetch_episodes_from_tranime(slug: str) -> list:
"""TRAnime'den bölümleri ve video linklerini çek."""
try:
episodes = tranime.get_anime_episodes(slug)
result = []
for ep in episodes:
videos = []
# Bölüm detaylarını al (fansub listesi için)
try:
ep_details = tranime.get_episode_details(ep.slug)
if ep_details:
for fansub_id, fansub_name in ep_details.fansubs:
sources = ep_details.get_sources(fansub_id)
for source in sources:
iframe = source.get_iframe()
if iframe:
videos.append({
"url": iframe,
"quality": source.name,
"fansub": fansub_name
})
except:
pass
result.append({
"number": ep.episode_number,
"title": ep.title,
"slug": ep.slug,
"videos": videos
})
return result
except Exception as e:
print(f"[TRAnime] Bölüm çekme hatası: {e}")
return []
# ─────────────────────────────────────────────────────────────────────────────
# ANA GÜNCELLEME FONKSİYONLARI
# ─────────────────────────────────────────────────────────────────────────────
def update_anime_by_mal_id(mal_id: int, extra_ids: Optional[Dict[str, Any]] = None, skip_videos: bool = True) -> bool:
"""
MAL ID'ye göre anime'yi güncelle.
1. Jikan API'den anime bilgilerini çek
2. Cover resmini indir
3. Veritabanına kaydet
4. İlişkili verileri (tür, tema, stüdyo) kaydet
5. Jikan API'den bölüm listesini çek ve kaydet
6. Adaptörlerde ara ve eşleştir (video çekmeden)
NOT: Video linkleri çekilmez. Video linkleri için api.py'deki
/api/sync/videos endpointini kullanın.
"""
print(f"\n[{mal_id}] Güncelleniyor...")
# 1. Jikan API'den bilgileri çek
jikan_data = jikan.get_anime(mal_id)
if not jikan_data:
print(f"[{mal_id}] Anime bulunamadı!")
return False
# 2. Veriyi parse et
anime_data = parse_jikan_data(jikan_data, extra_ids or {})
related_data = get_jikan_related_data(jikan_data)
# 3. Cover indir
cover_url = anime_data.get("cover_url")
if cover_url:
local_cover = download_cover(cover_url, mal_id)
anime_data["cover_local"] = local_cover
print(f"[{mal_id}] Cover indirildi: {local_cover}")
# 4. Veritabanına kaydet
anime_id = db.insert_or_update_anime(anime_data)
if not anime_id:
print(f"[{mal_id}] Veritabanı kayıt hatası!")
return False
print(f"[{mal_id}] {anime_data.get('title')} - DB ID: {anime_id}")
# 5. İlişkili verileri kaydet
# Başlıklar
if related_data.get("titles"):
db.insert_anime_titles(anime_id, related_data["titles"])
# Türler
for genre in related_data.get("genres", []):
genre_id = db.insert_or_get_genre(genre.get("mal_id"), genre.get("name"))
if genre_id:
db.link_anime_genre(anime_id, genre_id)
# Temalar
for theme in related_data.get("themes", []):
theme_id = db.insert_or_get_theme(theme.get("mal_id"), theme.get("name"))
if theme_id:
db.link_anime_theme(anime_id, theme_id)
# Stüdyolar
for studio in related_data.get("studios", []):
studio_id = db.insert_or_get_studio(studio.get("mal_id"), studio.get("name"))
if studio_id:
db.link_anime_studio(anime_id, studio_id)
# Yapımcılar
for producer in related_data.get("producers", []):
producer_id = db.insert_or_get_producer(producer.get("mal_id"), producer.get("name"))
if producer_id:
db.link_anime_producer(anime_id, producer_id, "producer")
# Lisansörler
for licensor in related_data.get("licensors", []):
licensor_id = db.insert_or_get_producer(licensor.get("mal_id"), licensor.get("name"))
if licensor_id:
db.link_anime_producer(anime_id, licensor_id, "licensor")
# 6. Jikan API'den bölüm listesini çek ve kaydet
# NOT: Devam eden seriler için episodes=null olabilir, yine de bölümleri çekmeye çalış
total_episodes = jikan_data.get("episodes") or 0
print(f"[{mal_id}] Bölümler çekiliyor...")
episodes = jikan.get_anime_episodes(mal_id)
if episodes:
for ep in episodes:
ep_num = ep.get("mal_id") # Jikan'da episode number "mal_id" olarak geliyor
ep_title = ep.get("title") or ep.get("title_japanese") or f"{ep_num}. Bölüm"
if ep_num:
db.insert_or_update_episode(anime_id, ep_num, ep_title)
print(f"[{mal_id}] ✓ {len(episodes)} bölüm kaydedildi")
elif total_episodes > 0:
# Episode API'den veri gelmezse ve bölüm sayısı biliniyorsa oluştur
print(f"[{mal_id}] Bölüm detayı yok, {total_episodes} bölüm numarayla oluşturuluyor...")
for ep_num in range(1, total_episodes + 1):
db.insert_or_update_episode(anime_id, ep_num, f"{ep_num}. Bölüm")
print(f"[{mal_id}] ✓ {total_episodes} bölüm oluşturuldu")
else:
print(f"[{mal_id}] Henüz bölüm bilgisi yok (devam eden/yeni seri)")
# 7. Adaptörlerde ara ve sadece eşleşmeyi kaydet (video çekme)
print(f"[{mal_id}] Adaptörlerde aranıyor...")
adapter_matches = search_in_adapters(
anime_data.get("title"),
anime_data.get("title_english"),
anime_data.get("title_japanese")
)
# 8. Her adaptör için sadece eşleşmeyi kaydet
for adapter_name, (source_anime_id, source_slug, source_title) in adapter_matches.items():
print(f"[{mal_id}] {adapter_name}: {source_title}")
source_id = db.get_source_id(adapter_name)
if not source_id:
continue
# Anime-kaynak eşleşmesini kaydet
db.insert_or_update_anime_source(anime_id, source_id, source_anime_id, source_slug, source_title)
print(f"[{mal_id}] ✓ Anime bilgileri, cover ve bölümler kaydedildi (video linkleri çekilmedi)")
return True
def update_anime_by_title(title: str) -> bool:
"""İsme göre anime güncelle."""
print(f"\nAranıyor: {title}")
# Önce anime_ids.json'da MAL ID bul
anime_ids = load_anime_ids()
# Jikan'da ara
try:
results = jikan.search_anime(title)
if not results:
print(f"'{title}' bulunamadı!")
return False
# İlk sonucu al veya kullanıcıya sor
if len(results) == 1:
selected = results[0]
else:
print("\nBulunan sonuçlar:")
for i, anime in enumerate(results):
print(f" {i+1}. [{anime.get('mal_id')}] {anime.get('title')}")
choice = input("\nSeçiminiz (1-5, varsayılan 1): ").strip()
idx = int(choice) - 1 if choice.isdigit() else 0
selected = results[max(0, min(idx, len(results)-1))]
mal_id = selected.get("mal_id")
print(f"\nSeçilen: [{mal_id}] {selected.get('title')}")
# anime_ids.json'dan extra ID'leri bul
extra_ids = None
for anidb_id, ids in anime_ids.items():
if ids.get("mal_id") == mal_id:
extra_ids = {
"anidb_id": int(anidb_id),
"anilist_id": ids.get("anilist_id"),
"tvdb_id": ids.get("tvdb_id"),
"imdb_id": ids.get("imdb_id")
}
break
return update_anime_by_mal_id(mal_id, extra_ids)
except Exception as e:
print(f"Arama hatası: {e}")
return False
def bulk_update(limit: Optional[int] = None, skip_updated: bool = True, parallel: int = 10):
"""
anime_ids.json'dan toplu güncelleme (PARALEL).
Args:
limit: Maksimum güncellenecek anime sayısı
skip_updated: Daha önce güncellenenler atlanır
parallel: Paralel işlem sayısı (varsayılan: 10, max: 50)
"""
anime_ids = load_anime_ids()
updated_ids = load_updated_ids() if skip_updated else set()
print(f"Toplam anime: {len(anime_ids)}")
print(f"Daha önce güncellenen: {len(updated_ids)}")
# MAL ID'leri topla (henüz güncellenmemişler)
to_update = []
for anidb_id, ids in anime_ids.items():
mal_id = ids.get("mal_id")
if mal_id and mal_id not in updated_ids:
to_update.append((mal_id, {
"anidb_id": int(anidb_id),
"anilist_id": ids.get("anilist_id"),
"tvdb_id": ids.get("tvdb_id"),
"imdb_id": ids.get("imdb_id")
}))
if limit:
to_update = to_update[:int(limit)]
print(f"Güncellenecek: {len(to_update)}")
print(f"Paralel işlem: {min(parallel, 50)}")
success_count = 0
fail_count = 0
processed = 0
lock = threading.Lock()
def update_single(item):
nonlocal success_count, fail_count, processed, updated_ids
mal_id, extra_ids = item
try:
result = update_anime_by_mal_id(mal_id, extra_ids)
with lock:
processed += 1
if result:
updated_ids.add(mal_id)
success_count += 1
else:
fail_count += 1
# Her 10 anime'de bir kaydet
if processed % 10 == 0:
save_updated_ids(updated_ids)
print(f"\n[İlerleme] {processed}/{len(to_update)} - Başarılı: {success_count}, Hatalı: {fail_count}")
return result
except Exception as e:
with lock:
processed += 1
fail_count += 1
print(f"\n[HATA] {mal_id}: {e}")
return False
# Paralel güncelleme
try:
with ThreadPoolExecutor(max_workers=min(parallel, 50)) as executor:
futures = [executor.submit(update_single, item) for item in to_update]
for future in as_completed(futures):
try:
future.result()
except Exception as e:
print(f"[HATA] Future: {e}")
except KeyboardInterrupt:
print("\n\nİptal edildi!")
# Son kayıt
save_updated_ids(updated_ids)
print(f"\n\n{'='*60}")
print(f"Tamamlandı!")
print(f"Başarılı: {success_count}")
print(f"Başarısız: {fail_count}")
print(f"{'='*60}")
def show_video_links(mal_id: int):
"""Belirli anime için video linklerini göster."""
anime_row = db.get_anime_by_mal_id(mal_id)
if not anime_row:
print(f"MAL ID {mal_id} veritabanında bulunamadı!")
return
anime = cast(Dict[str, Any], anime_row)
anime_id = int(anime["id"])
print(f"\n{anime['title']} (MAL: {mal_id})")
print("=" * 60)
videos = db.get_video_links(anime_id=anime_id)
if not videos:
print("Video linki bulunamadı!")
return
current_episode = None
for video_row in videos:
video = cast(Dict[str, Any], video_row)
if video["episode_number"] != current_episode:
current_episode = video["episode_number"]
print(f"\n{video['episode_number']}. Bölüm")
print("-" * 40)
print(f" [{video['source_name']}] {video['fansub']} ({video['quality']})")
print(f" {str(video['video_url'])[:80]}...")
# ─────────────────────────────────────────────────────────────────────────────
# ANA FONKSİYON
# ─────────────────────────────────────────────────────────────────────────────
def main():
parser = argparse.ArgumentParser(
description="Anime Offline Veritabanı Yöneticisi",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Örnekler:
python main.py --init # Veritabanını başlat
python main.py --guncelle "Naruto" # İsme göre güncelle
python main.py --mal_id 20 # MAL ID'ye göre güncelle
python main.py --toplu # Toplu güncelle
python main.py --toplu --limit 100 # İlk 100'ü güncelle
python main.py --videolar 20 # Video linklerini göster
python main.py --sifirla # Güncelleme listesini sıfırla
"""
)
parser.add_argument("--init", action="store_true", help="Veritabanını başlat")
parser.add_argument("--guncelle", type=str, metavar="ISIM", help="İsme göre güncelle")
parser.add_argument("--mal_id", type=int, metavar="ID", help="MAL ID'ye göre güncelle")
parser.add_argument("--toplu", action="store_true", help="anime_ids.json'dan toplu güncelle")
parser.add_argument("--limit", type=int, help="Toplu güncellemede limit")
parser.add_argument("--parallel", type=int, default=10, help="Paralel işlem sayısı (varsayılan: 10, max: 50)")
parser.add_argument("--videolar", type=int, metavar="MAL_ID", help="Video linklerini göster")
parser.add_argument("--sifirla", action="store_true", help="Güncelleme listesini sıfırla")
parser.add_argument("--tekrar", action="store_true", help="Güncellenenleri de tekrar güncelle")
args = parser.parse_args()
# Hiçbir argüman verilmediyse yardım göster
if len(sys.argv) == 1:
parser.print_help()
return
# Veritabanını başlat
if args.init:
print("Veritabanı başlatılıyor...")
db.init_database()
return
# Güncelleme listesini sıfırla
if args.sifirla:
if os.path.exists(UPDATED_IDS_FILE):
os.remove(UPDATED_IDS_FILE)
print("Güncelleme listesi sıfırlandı.")
else:
print("Güncelleme listesi zaten boş.")
return
# İsme göre güncelle
if args.guncelle:
update_anime_by_title(args.guncelle)
return
# MAL ID'ye göre güncelle
if args.mal_id:
# anime_ids.json'dan extra ID'leri bul
anime_ids = load_anime_ids()
extra_ids = None
for anidb_id, ids in anime_ids.items():
if ids.get("mal_id") == args.mal_id:
extra_ids = {
"anidb_id": int(anidb_id),
"anilist_id": ids.get("anilist_id"),
"tvdb_id": ids.get("tvdb_id"),
"imdb_id": ids.get("imdb_id")
}
break
update_anime_by_mal_id(args.mal_id, extra_ids)
return
# Toplu güncelle
if args.toplu:
bulk_update(limit=args.limit, skip_updated=not args.tekrar, parallel=args.parallel)
return
# Video linklerini göster
if args.videolar:
show_video_links(args.videolar)
return
if __name__ == "__main__":
main()