import nntplib import os import time import socket from dotenv import load_dotenv from datetime import datetime, UTC from db import get_conn from psycopg.types.json import Json # --- ZVÝŠENÍ LIMITŮ PRO STABILITU --- nntplib._MAXLINE = 1048576 # 1MB limit pro dlouhé hlavičky socket.setdefaulttimeout(30) # Prevence nekonečného čekání na síť # ================= CONFIG ================= GROUP = "alt.binaries.e-book.magazines" BATCH_SIZE = 5000 # Menší batch pro častější feedback v konzoli # ========================================= load_dotenv() EWEKA_USER = os.getenv("EWEKA_USER") EWEKA_PASS = os.getenv("EWEKA_PASS") def get_current_frontier(cur): print(f"🔍 Hledám v databázi hranici spodního bloku pro skupinu {GROUP}...") cur.execute(""" SELECT MAX(article_number) FROM articles WHERE newsgroup = %s AND article_number < 10000000 """, (GROUP,)) res = cur.fetchone()[0] val = int(res) if res else None print(f"📍 Nalezena hranice: {val if val else 'Žádná (začínám od nuly)'}") return val def is_batch_missing(cur, start, end): # Tady print nedáváme, aby to nespamovalo 1000x za sekundu cur.execute(""" SELECT EXISTS ( SELECT 1 FROM articles WHERE newsgroup = %s AND article_number BETWEEN %s AND %s ) """, (GROUP, start, end)) return not cur.fetchone()[0] def fast_fill(): print(f"🚀 Startuji proces: {datetime.now().strftime('%H:%M:%S')}") conn = get_conn() conn.autocommit = True cur = conn.cursor() current_id = get_current_frontier(cur) if not current_id: print("⚠️ Varování: Spodní hranice nenalezena, zkusím se zeptat serveru na nejstarší článek.") print(f"🔌 Připojuji se k Eweka NNTP SSL (news.eweka.nl)...") try: with nntplib.NNTP_SSL("news.eweka.nl", 563, user=EWEKA_USER, password=EWEKA_PASS) as nntp: resp, count, first, last, name = nntp.group(GROUP) first, last = int(first), int(last) if not current_id: current_id = first print(f"📊 INFO O SKUPINĚ:") print(f" Název: {name}") print(f" Článků na serveru: {count}") print(f" Rozsah: {first} až {last}") print(f" Aktuální pozice: {current_id}") print(f" Zbývá zpracovat cca: {last - current_id} článků") print("-" * 50) total_downloaded = 0 start_process_time = time.time() while current_id < last: batch_start = current_id + 1 batch_end = min(batch_start + BATCH_SIZE - 1, last) # 1. KROK: Kontrola existence if not is_batch_missing(cur, batch_start, batch_end): # Zkusíme skočit dál cur.execute( "SELECT MAX(article_number) FROM articles WHERE newsgroup = %s AND article_number BETWEEN %s AND %s", (GROUP, batch_start, batch_end + 100000)) res = cur.fetchone()[0] if res: current_id = int(res) print(f"⏩ [SKIP] Data existují. Přeskakuji na ID: {current_id}", end="\r") else: current_id = batch_end # Sychr continue # 2. KROK: Stahování (tady dáváme hodně printů) print(f"\n💎 [DÍRA] Nalezena mezera: {batch_start} - {batch_end}") # --- SÍŤOVÁ ČÁST --- print(f" 📡 NNTP XOVER...", end=" ", flush=True) t_net_start = time.time() try: resp, overviews = nntp.xover(batch_start, batch_end) t_net_end = time.time() print(f"OK ({len(overviews)} hlaviček za {t_net_end - t_net_start:.2f}s)") rows = [] for art_num, fields in overviews: rows.append(( GROUP, art_num, fields.get("message-id"), Json({"group": GROUP, "article_number": art_num, **fields}), datetime.now(UTC) )) # --- DATABÁZOVÁ ČÁST --- if rows: print(f" 💾 POSTGRES INSERT...", end=" ", flush=True) t_db_start = time.time() cur.executemany( """ INSERT INTO articles (newsgroup, article_number, message_id, metadata, fetched_at) VALUES (%s, %s, %s, %s, %s) ON CONFLICT DO NOTHING """, rows ) t_db_end = time.time() total_downloaded += len(rows) print(f"OK ({t_db_end - t_db_start:.2f}s)") else: print(f" ℹ️ Žádná data k uložení (prázdný rozsah na serveru)") current_id = batch_end except Exception as e: print(f"\n❌ CHYBA v bloku {batch_start}-{batch_end}: {e}") print(" Zkouším popojít o jeden BATCH dál...") current_id += BATCH_SIZE time.sleep(2) # Krátká pauza při chybě except Exception as e: print(f"\n💥 KRITICKÁ CHYBA PŘIPOJENÍ: {e}") finally: total_time = time.time() - start_process_time print("\n" + "=" * 50) print(f"🏁 KONEC RELACE") print(f"⏱️ Celkový čas: {total_time:.1f} sekund") print(f"📦 Celkem staženo nových hlaviček: {total_downloaded}") if total_time > 0: print(f"🚀 Průměrná rychlost: {total_downloaded / total_time:.1f} hlaviček/s") print("=" * 50) cur.close() conn.close() if __name__ == "__main__": fast_fill()