Skip to content

Instantly share code, notes, and snippets.

@me-suzy
Last active July 21, 2026 06:12
Show Gist options
  • Select an option

  • Save me-suzy/f82bd3810c017413bae8e2a7739e6d6d to your computer and use it in GitHub Desktop.

Select an option

Save me-suzy/f82bd3810c017413bae8e2a7739e6d6d to your computer and use it in GitHub Desktop.
archive_org_pdf_uploader_v2.py
from __future__ import annotations
import argparse
import configparser
import os
import re
import shutil
import sqlite3
import sys
import tempfile
import threading
import time
import tkinter as tk
import unicodedata
from pathlib import Path
from tkinter import filedialog, messagebox, scrolledtext, ttk
from urllib.parse import quote, urlparse
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.service import Service
from selenium.webdriver.support.ui import WebDriverWait
DEFAULT_FOLDER = Path(r"G:\Buletin")
FIREFOX_BINARY = Path(r"C:\Program Files\Mozilla Firefox\firefox.exe")
GECKODRIVER_BINARY = Path(r"C:\WINDOWS\geckodriver.exe")
YEAR_RE = re.compile(r"(?<!\d)((?:18|19|20)\d{2})(?:\s*[-–]\s*((?:18|19|20)\d{2}))?(?!\d)")
OPEN_FIREFOX_SESSIONS: list[tuple[webdriver.Firefox, Path]] = []
def archive_identifier(value: str) -> str:
value = value.strip()
parsed = urlparse(value if "://" in value else "https://archive.org/details/" + value)
parts = [part for part in parsed.path.split("/") if part]
if len(parts) >= 2 and parts[0] in {"details", "edit", "download"}:
identifier = parts[1]
elif parsed.path == "/upload/":
from urllib.parse import parse_qs
identifier = parse_qs(parsed.query).get("identifier", [""])[0]
elif len(parts) == 1:
identifier = parts[0]
else:
identifier = ""
if not re.fullmatch(r"[A-Za-z0-9._-]+", identifier):
raise ValueError("Link sau identifier Archive.org invalid.")
return identifier
def pdf_sort_key(path: Path) -> tuple[int, int, str]:
match = YEAR_RE.search(path.name)
if match:
return (-int(match.group(1)), -int(match.group(2) or match.group(1)), path.name.casefold())
return (1, 1, path.name.casefold())
def find_pdfs(folder: Path) -> list[Path]:
return sorted((p.resolve() for p in folder.rglob("*.pdf") if p.is_file()), key=pdf_sort_key)
def filename_key(name: str) -> str:
return unicodedata.normalize("NFC", name).casefold()
def unique_local_pdfs(files: list[Path]) -> tuple[list[Path], list[Path]]:
unique: list[Path] = []
duplicates: list[Path] = []
seen: set[str] = set()
for path in files:
key = filename_key(path.name)
if key in seen:
duplicates.append(path)
else:
seen.add(key)
unique.append(path)
return unique, duplicates
def read_existing_pdf_names(driver: webdriver.Firefox, identifier: str, log) -> set[str]:
edit_url = f"https://archive.org/edit.php?edit-files=1&identifier={quote(identifier)}"
log(f"Verific fisierele existente: {edit_url}")
driver.get(edit_url)
WebDriverWait(driver, 90).until(
lambda d: d.find_elements(By.CSS_SELECTOR, ".x-tree-node-icon.file-pdf")
or "File editor for" in (d.find_element(By.TAG_NAME, "body").get_attribute("textContent") or "")
)
time.sleep(2)
names = driver.execute_script(
"""
return Array.from(document.querySelectorAll('.x-tree-node-icon.file-pdf'))
.map(img => {
const node = img.closest('.x-tree-node-el');
const span = node ? node.querySelector('a span') : null;
return span ? span.textContent.trim() : '';
})
.filter(Boolean);
"""
)
result = {filename_key(name) for name in names}
log(f"Archive.org contine deja {len(result)} fisiere PDF cu nume distincte.")
return result
def normalized_words(value: str) -> set[str]:
value = "".join(c for c in unicodedata.normalize("NFKD", value) if not unicodedata.combining(c)).lower()
return set(re.findall(r"[a-z0-9]+", value))
def relevant_pdfs(folder: Path, url: str) -> list[Path]:
identifier = archive_identifier(url)
ignored = {"buletinul", "institutului", "politehnic", "din", "iasi", "bucuresti", "seria"}
keywords = normalized_words(identifier) - ignored
threshold = max(1, (len(keywords) + 1) // 2)
files = find_pdfs(folder)
matches = [p for p in files if len(normalized_words(p.stem) & keywords) >= threshold]
return matches or files
def default_firefox_profile() -> Path:
base = Path(os.environ["APPDATA"]) / "Mozilla" / "Firefox"
ini = configparser.ConfigParser()
ini.read(base / "profiles.ini", encoding="utf-8")
install_sections = [s for s in ini.sections() if s.startswith("Install")]
rel = ini[install_sections[0]].get("Default", "") if install_sections else ""
if not rel:
for section in ini.sections():
if section.startswith("Profile") and ini[section].getboolean("Default", fallback=False):
rel = ini[section].get("Path", "")
break
profile = base / rel
if not profile.is_dir():
raise FileNotFoundError("Nu am găsit profilul Firefox implicit.")
return profile
def clone_firefox_profile(source: Path, target: Path) -> None:
target.mkdir(parents=True, exist_ok=True)
cookies = source / "cookies.sqlite"
if cookies.exists():
source_db = sqlite3.connect(f"file:{cookies.as_posix()}?mode=ro", uri=True, timeout=10)
target_db = sqlite3.connect(target / "cookies.sqlite")
try:
source_db.backup(target_db)
finally:
target_db.close()
source_db.close()
for name in ("permissions.sqlite", "cert9.db", "key4.db", "pkcs11.txt"):
item = source / name
if item.is_file():
shutil.copy2(item, target / name)
def upload_files(driver: webdriver.Firefox, files: list[Path], delay: float, log, finalize: bool) -> None:
wait = WebDriverWait(driver, 60)
def visible_row_name(row) -> str:
value = row.find_element(By.CSS_SELECTOR, "span.file").get_attribute("textContent")
return (value or "").strip()
expected_names = [path.name for path in files]
if len(expected_names) != len(set(expected_names)):
raise RuntimeError("Lista locala contine nume PDF duplicate. Operatia a fost oprita.")
log(f"Adaug {len(files)} PDF-uri individual, la interval de {delay:.1f} secunde.")
files_to_add_individually = files
for index, path in enumerate(files_to_add_individually, 1):
rows_before = driver.find_elements(By.CSS_SELECTOR, "#file_table tbody tr")
before_count = len(rows_before)
if before_count != index - 1:
raise RuntimeError(
f"Protectie anti-duplicate: tabelul are {before_count} randuri in loc de {index - 1}."
)
inputs = driver.find_elements(By.CSS_SELECTOR, "input[type='file']")
if not inputs:
raise RuntimeError("Nu găsesc controlul de selectare a fișierelor pe pagina Archive.org.")
control_id = "file_input_initial" if index == 1 else "file_input_add"
control = wait.until(lambda d: d.find_element(By.ID, control_id))
if index > 1:
driver.execute_script("arguments[0].value = '';", control)
control.send_keys(str(path))
wait.until(lambda d: len(d.find_elements(By.CSS_SELECTOR, "#file_table tbody tr")) >= before_count + 1)
rows_after = driver.find_elements(By.CSS_SELECTOR, "#file_table tbody tr")
if len(rows_after) != before_count + 1:
raise RuntimeError(
f"Protectie anti-duplicate: fisierul {index} a produs {len(rows_after) - before_count} randuri."
)
displayed_names = [visible_row_name(row) for row in rows_after]
if len(displayed_names) != len(set(displayed_names)):
raise RuntimeError("Protectie anti-duplicate: tabelul contine nume repetate.")
log(f"[{index}/{len(files)}] Adăugat: {path.name}")
if index != len(files):
time.sleep(delay)
def completed_unique_table(d):
rows = d.find_elements(By.CSS_SELECTOR, "#file_table tbody tr")
if len(rows) != len(files):
return False
names = [visible_row_name(row) for row in rows]
if any(not name for name in names):
return False
if len(names) != len(set(names)):
return False
return names
try:
final_names = WebDriverWait(driver, 180, poll_frequency=0.5).until(completed_unique_table)
except TimeoutException:
final_rows = driver.find_elements(By.CSS_SELECTOR, "#file_table tbody tr")
visible_names = [visible_row_name(row) for row in final_rows]
empty_count = sum(not name for name in visible_names)
duplicate_count = len(visible_names) - len(set(name for name in visible_names if name)) - empty_count
raise RuntimeError(
f"Verificarea finala a expirat: {len(final_rows)} randuri, "
f"{empty_count} nume necompletate, {max(0, duplicate_count)} duplicate reale."
)
log(f"Verificare reusita: {len(final_names)} randuri, toate numele sunt completate si unice.")
if finalize:
button = wait.until(lambda d: d.find_element(By.ID, "upload_button"))
log("Lista este completă; trimit fișierele către item...")
driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", button)
time.sleep(1)
button.click()
log("Butonul «Add files to existing item» a fost apăsat.")
log("Aștept finalizarea transferului; nu închide fereastra Firefox...")
WebDriverWait(driver, 6 * 60 * 60, poll_frequency=2).until(
lambda d: "/upload/" not in d.current_url
or any(text in d.find_element(By.TAG_NAME, "body").text.lower() for text in ("upload complete", "files added", "item updated"))
)
log(f"Transfer finalizat. Pagina curentă: {driver.current_url}")
else:
log("Mod verificare: lista a fost pregătită, dar butonul final NU a fost apăsat.")
def run_browser(url: str, folder: Path, delay: float, finalize: bool, log) -> None:
identifier = archive_identifier(url)
all_files = find_pdfs(folder)
if not all_files:
raise FileNotFoundError(f"Nu există PDF-uri în {folder}")
files, local_duplicates = unique_local_pdfs(all_files)
log(f"Am gasit {len(all_files)} PDF-uri locale si {len(local_duplicates)} dubluri locale dupa nume.")
for path in local_duplicates:
log(f"SKIP dublura locala: {path}")
temp_root = Path(tempfile.mkdtemp(prefix="archive-uploader-firefox-"))
profile = temp_root / "profile"
clone_firefox_profile(default_firefox_profile(), profile)
options = Options()
options.binary_location = str(FIREFOX_BINARY)
options.profile = str(profile)
options.add_argument("-no-remote")
driver = webdriver.Firefox(options=options, service=Service(executable_path=str(GECKODRIVER_BINARY)))
completed = False
try:
existing_names = read_existing_pdf_names(driver, identifier, log)
upload_paths = [path for path in files if filename_key(path.name) not in existing_names]
skipped_online = [path for path in files if filename_key(path.name) in existing_names]
for path in skipped_online:
log(f"SKIP exista deja pe Archive.org: {path.name}")
log(
f"Rezumat V2: {len(all_files)} locale, {len(skipped_online)} deja online, "
f"{len(local_duplicates)} dubluri locale, {len(upload_paths)} de incarcat."
)
if not upload_paths:
log("Nu exista fisiere noi de incarcat. Operatia s-a incheiat fara upload.")
completed = True
driver.quit()
shutil.rmtree(temp_root, ignore_errors=True)
return
upload_url = f"https://archive.org/upload/?identifier={quote(identifier)}"
log(f"Deschid: {upload_url}")
driver.get(upload_url)
WebDriverWait(driver, 60).until(lambda d: d.find_elements(By.CSS_SELECTOR, "#file_input_initial, input[type='file']"))
if "/account/login" in driver.current_url:
raise RuntimeError("Sesiunea clonată nu este autentificată. Autentifică-te în fereastra Firefox deschisă de script și pornește din nou.")
upload_files(driver, upload_paths, delay, log, finalize)
completed = True
finally:
if completed and finalize:
if driver.session_id:
OPEN_FIREFOX_SESSIONS.append((driver, temp_root))
log("Firefox ramane activ pana la terminarea transferului.")
else:
if driver.session_id:
OPEN_FIREFOX_SESSIONS.append((driver, temp_root))
if completed:
log("Lista este pregatita. Firefox ramane deschis pentru verificare manuala.")
else:
log("A aparut o eroare. Firefox ramane deschis in starea curenta pentru verificare.")
class App(tk.Tk):
def __init__(self) -> None:
super().__init__()
self.title("Archive.org – uploader PDF V2 fără dubluri")
self.geometry("900x620")
self.url = tk.StringVar(value="https://archive.org/details/buletinul-institutului-politehnic-din-iasi-electrotehnica-energetica-electronica")
self.folder = tk.StringVar(value=str(DEFAULT_FOLDER))
self.delay = tk.DoubleVar(value=1.0)
self.finalize = tk.BooleanVar(value=True)
self._build()
self.preview()
def _build(self) -> None:
frame = ttk.Frame(self, padding=12)
frame.pack(fill="both", expand=True)
ttk.Label(frame, text="Linkul principal Archive.org:").grid(row=0, column=0, sticky="w")
ttk.Entry(frame, textvariable=self.url).grid(row=1, column=0, columnspan=3, sticky="ew", pady=(2, 10))
ttk.Label(frame, text="Folderul cu PDF-uri (include toate subfolderele):").grid(row=2, column=0, sticky="w")
ttk.Entry(frame, textvariable=self.folder).grid(row=3, column=0, sticky="ew", pady=(2, 10))
ttk.Button(frame, text="Alege folder", command=self.choose_folder).grid(row=3, column=1, padx=6)
ttk.Button(frame, text="Actualizează lista", command=self.preview).grid(row=3, column=2)
ttk.Label(frame, text="Pauză între fișiere (secunde):").grid(row=4, column=0, sticky="w")
ttk.Spinbox(frame, from_=1.0, to=30.0, increment=0.5, textvariable=self.delay, width=8).grid(row=4, column=1, sticky="w")
ttk.Checkbutton(frame, text="Apasă automat butonul final «Add files to existing item»", variable=self.finalize).grid(row=5, column=0, columnspan=3, sticky="w", pady=8)
self.output = scrolledtext.ScrolledText(frame, wrap="none", height=24)
self.output.grid(row=6, column=0, columnspan=3, sticky="nsew")
self.start_button = ttk.Button(frame, text="Pornește upload-ul", command=self.start)
self.start_button.grid(row=7, column=0, columnspan=3, pady=12)
frame.columnconfigure(0, weight=1)
frame.rowconfigure(6, weight=1)
def choose_folder(self) -> None:
chosen = filedialog.askdirectory(initialdir=self.folder.get())
if chosen:
self.folder.set(chosen)
self.preview()
def log(self, message: str) -> None:
self.after(0, lambda: (self.output.insert("end", message + "\n"), self.output.see("end")))
def preview(self) -> None:
self.output.delete("1.0", "end")
folder = Path(self.folder.get())
if not folder.is_dir():
self.output.insert("end", "Folder inexistent.\n")
return
try:
files = find_pdfs(folder)
except ValueError as exc:
self.output.insert("end", str(exc) + "\n")
return
self.output.insert("end", f"{len(files)} PDF-uri, în ordinea upload-ului:\n\n")
for i, path in enumerate(files, 1):
self.output.insert("end", f"{i:03}. {path}\n")
def start(self) -> None:
try:
archive_identifier(self.url.get())
folder = Path(self.folder.get()).resolve()
files = find_pdfs(folder)
if not files:
raise ValueError("Folderul nu conține PDF-uri.")
except Exception as exc:
messagebox.showerror("Date invalide", str(exc))
return
action = "încărca și finaliza" if self.finalize.get() else "pregăti fără finalizare"
if not messagebox.askyesno("Confirmare", f"Scriptul va {action} {len(files)} PDF-uri în itemul:\n{archive_identifier(self.url.get())}\n\nContinui?"):
return
self.start_button.config(state="disabled")
def worker() -> None:
try:
run_browser(self.url.get(), folder, max(1.0, self.delay.get()), self.finalize.get(), self.log)
self.after(0, lambda: messagebox.showinfo("Gata", "Operația s-a încheiat."))
except Exception as exc:
error_message = str(exc)
self.log(f"EROARE: {error_message}")
self.after(0, lambda message=error_message: messagebox.showerror("Eroare", message))
finally:
self.after(0, lambda: self.start_button.config(state="normal"))
threading.Thread(target=worker, daemon=True).start()
def self_test() -> None:
assert archive_identifier("https://archive.org/details/test-item") == "test-item"
assert archive_identifier("https://archive.org/edit/test-item") == "test-item"
assert archive_identifier("https://archive.org/upload/?identifier=test-item") == "test-item"
files = find_pdfs(DEFAULT_FOLDER)
assert files and all(p.suffix.lower() == ".pdf" for p in files)
unique, duplicates = unique_local_pdfs(files)
assert len(unique) + len(duplicates) == len(files)
assert all(filename_key(path.name) == filename_key(path.name) for path in files)
print(f"OK V2: {len(files)} PDF-uri locale; {len(unique)} nume unice; {len(duplicates)} dubluri locale.")
def browser_test(url: str) -> None:
identifier = archive_identifier(url)
temp_root = Path(tempfile.mkdtemp(prefix="archive-uploader-test-"))
try:
profile = temp_root / "profile"
clone_firefox_profile(default_firefox_profile(), profile)
options = Options()
options.binary_location = str(FIREFOX_BINARY)
options.profile = str(profile)
options.add_argument("-no-remote")
driver = webdriver.Firefox(options=options, service=Service(executable_path=str(GECKODRIVER_BINARY)))
try:
target = f"https://archive.org/upload/?identifier={quote(identifier)}"
driver.get(target)
WebDriverWait(driver, 60).until(lambda d: d.find_elements(By.CSS_SELECTOR, "#file_input_initial, input[type='file']"))
print(f"OK browser: {driver.current_url}; selectorul de upload există; niciun fișier trimis.")
finally:
driver.quit()
finally:
shutil.rmtree(temp_root, ignore_errors=True)
if __name__ == "__main__":
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8")
parser = argparse.ArgumentParser()
parser.add_argument("--self-test", action="store_true")
parser.add_argument("--browser-test", metavar="URL")
args = parser.parse_args()
if args.self_test:
self_test()
elif args.browser_test:
browser_test(args.browser_test)
else:
App().mainloop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment