Merge remote-tracking branch 'origin/master'
This commit is contained in:
+5
-12
@@ -1,14 +1,7 @@
|
||||
# PyCharm / IntelliJ
|
||||
.venv/*
|
||||
.idea/
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
*.log
|
||||
|
||||
# Virtualenv
|
||||
.venv/
|
||||
# created by virtualenv automatically
|
||||
.venv/Lib
|
||||
# Neignorovat tyto typy
|
||||
!*.jsonl
|
||||
!*.txt
|
||||
!*.log
|
||||
+54807
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
import fitz
|
||||
from pathlib import Path
|
||||
|
||||
BASE_DIR = Path(r"z:\Dropbox\Ordinace\Dokumentace_ke_zpracování\AdobeFlattenStamp")
|
||||
BASE_DIR = Path(r"u:\Dropbox\Ordinace\Dokumentace_ke_zpracování\AdobeFlattenStamp")
|
||||
|
||||
def flatten_pdf_rasterize(input_pdf: Path):
|
||||
print(f"Processing: {input_pdf.name}")
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import fitz
|
||||
from pathlib import Path
|
||||
import platform
|
||||
import sys
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
HOSTNAME = platform.node().upper()
|
||||
|
||||
if HOSTNAME in {"SESTRA", "POHODA"}:
|
||||
DROPBOX_DRIVE = Path(r"Z:\\")
|
||||
elif HOSTNAME == "Z230":
|
||||
DROPBOX_DRIVE = Path(r"U:\\")
|
||||
else:
|
||||
print(f"❌ Unknown computer name: {HOSTNAME}")
|
||||
sys.exit(1)
|
||||
|
||||
BASE_DIR = (
|
||||
DROPBOX_DRIVE
|
||||
/ "Dropbox"
|
||||
/ "Ordinace"
|
||||
/ "Dokumentace_ke_zpracování"
|
||||
/ "AdobeFlattenStamp"
|
||||
)
|
||||
|
||||
def flatten_pdf_rasterize_overwrite(input_pdf: Path):
|
||||
print(f"Processing: {input_pdf.name}")
|
||||
|
||||
doc = fitz.open(input_pdf)
|
||||
new_doc = fitz.open()
|
||||
|
||||
for page in doc:
|
||||
pix = page.get_pixmap(dpi=400)
|
||||
new_page = new_doc.new_page(
|
||||
width=page.rect.width,
|
||||
height=page.rect.height
|
||||
)
|
||||
new_page.insert_image(new_page.rect, pixmap=pix)
|
||||
|
||||
# safe overwrite
|
||||
with tempfile.NamedTemporaryFile(
|
||||
suffix=".pdf",
|
||||
delete=False,
|
||||
dir=input_pdf.parent
|
||||
) as tmp:
|
||||
tmp_path = Path(tmp.name)
|
||||
|
||||
new_doc.save(tmp_path, deflate=True)
|
||||
new_doc.close()
|
||||
doc.close()
|
||||
|
||||
os.replace(tmp_path, input_pdf)
|
||||
print(f" ✔ Overwritten: {input_pdf.name}")
|
||||
|
||||
def main():
|
||||
print(f"🖥 Running on: {HOSTNAME}")
|
||||
print(f"📁 Base directory: {BASE_DIR}")
|
||||
|
||||
if not BASE_DIR.exists():
|
||||
print("❌ Base directory does not exist!")
|
||||
sys.exit(1)
|
||||
|
||||
pdfs = list(BASE_DIR.glob("*.pdf"))
|
||||
if not pdfs:
|
||||
print("No PDF files found.")
|
||||
return
|
||||
|
||||
for pdf in pdfs:
|
||||
flatten_pdf_rasterize_overwrite(pdf)
|
||||
|
||||
print("\n✅ All files processed and originals overwritten.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,145 @@
|
||||
import fitz # PyMuPDF
|
||||
import pytesseract
|
||||
from PIL import Image
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
|
||||
# --- NASTAVENÍ ---
|
||||
INPUT_FOLDER = r'u:\Dropbox\Ordinace\Dokumentace_ke_zpracování\AdobeMakeSmaller'
|
||||
OUTPUT_FOLDER = os.path.join(INPUT_FOLDER, '_HOTOVO_ULTIMATE')
|
||||
|
||||
# Cesta k Tesseractu
|
||||
PATH_TO_TESSERACT = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
|
||||
|
||||
# 1. Jak moc "ošklivý" (malý) má být viditelný obrázek?
|
||||
VISUAL_DPI = 150
|
||||
VISUAL_THRESHOLD = 150
|
||||
|
||||
# 2. Jak kvalitní má být předloha pro čtení (OCR)?
|
||||
OCR_DPI = 300
|
||||
|
||||
|
||||
def setup_tesseract():
|
||||
if not os.path.exists(PATH_TO_TESSERACT):
|
||||
print(f"CHYBA: Tesseract nenalezen na: {PATH_TO_TESSERACT}")
|
||||
return False
|
||||
pytesseract.pytesseract.tesseract_cmd = PATH_TO_TESSERACT
|
||||
return True
|
||||
|
||||
|
||||
def get_rc_from_text(text):
|
||||
"""Najde RČ v textu a vrátí ho bez lomítka."""
|
||||
# Hledá formát: 6 čísel, volitelně lomítko/mezera, 3-4 čísla
|
||||
matches = re.findall(r'\b(\d{6})\s*[\/]?\s*(\d{3,4})\b', text)
|
||||
for head, tail in matches:
|
||||
full_rc = f"{head}{tail}"
|
||||
# Základní kontrola délky a čísel
|
||||
if len(full_rc) in [9, 10] and full_rc.isdigit():
|
||||
return full_rc
|
||||
return None
|
||||
|
||||
|
||||
def process_ultimate_method():
|
||||
if not setup_tesseract():
|
||||
return
|
||||
|
||||
if not os.path.exists(OUTPUT_FOLDER):
|
||||
os.makedirs(OUTPUT_FOLDER)
|
||||
|
||||
files = [f for f in os.listdir(INPUT_FOLDER) if f.lower().endswith('.pdf')]
|
||||
print(f"Startuji 'ULTIMATE' zpracování {len(files)} souborů.")
|
||||
print("-" * 70)
|
||||
|
||||
for filename in files:
|
||||
input_path = os.path.join(INPUT_FOLDER, filename)
|
||||
|
||||
try:
|
||||
print(f"Zpracovávám: {filename} ...")
|
||||
|
||||
doc_src = fitz.open(input_path)
|
||||
final_doc = fitz.open()
|
||||
|
||||
detected_rc = None
|
||||
|
||||
for page_num, page in enumerate(doc_src):
|
||||
# --- KROK A: OCR (Vysoká kvalita pro čtení) ---
|
||||
# 1. Vyrenderujeme kvalitní obrázek pro Tesseract
|
||||
mat_ocr = fitz.Matrix(OCR_DPI / 72, OCR_DPI / 72)
|
||||
pix_ocr = page.get_pixmap(matrix=mat_ocr, colorspace=fitz.csGRAY)
|
||||
img_ocr = Image.frombytes("L", [pix_ocr.width, pix_ocr.height], pix_ocr.samples)
|
||||
|
||||
# 2. Tesseract vytvoří PDF stránku (Text + Velký obrázek)
|
||||
pdf_bytes = pytesseract.image_to_pdf_or_hocr(img_ocr, extension='pdf', lang='ces')
|
||||
|
||||
# 3. Otevřeme tuto stránku v paměti
|
||||
ocr_page_doc = fitz.open("pdf", pdf_bytes)
|
||||
ocr_page = ocr_page_doc[0]
|
||||
|
||||
# Přečteme text pro hledání RČ
|
||||
if not detected_rc:
|
||||
text_content = ocr_page.get_text()
|
||||
detected_rc = get_rc_from_text(text_content)
|
||||
|
||||
# --- KROK B: Visual (Nízká kvalita pro oči/úsporu) ---
|
||||
# 1. Vyrenderujeme malý obrázek
|
||||
mat_vis = fitz.Matrix(VISUAL_DPI / 72, VISUAL_DPI / 72)
|
||||
pix_vis = page.get_pixmap(matrix=mat_vis, colorspace=fitz.csGRAY)
|
||||
img_vis = Image.frombytes("L", [pix_vis.width, pix_vis.height], pix_vis.samples)
|
||||
|
||||
# 2. Binarizace (Threshold)
|
||||
img_vis_bw = img_vis.point(lambda x: 255 if x > VISUAL_THRESHOLD else 0, mode='1')
|
||||
|
||||
# 3. Uložíme do bufferu
|
||||
buffer_vis = io.BytesIO()
|
||||
img_vis_bw.save(buffer_vis, format="PNG", optimize=True)
|
||||
|
||||
# --- KROK C: Výměna (The Switch) ---
|
||||
# 1. Smažeme velký obrázek z Tesseract stránky (text zůstane)
|
||||
images = ocr_page.get_images()
|
||||
if images:
|
||||
xref = images[0][0]
|
||||
ocr_page.delete_image(xref)
|
||||
|
||||
# 2. Vložíme náš malý obrázek
|
||||
# Použijeme rect (rozměry) stránky, aby obrázek seděl přesně na celou stranu
|
||||
ocr_page.insert_image(ocr_page.rect, stream=buffer_vis.getvalue(), keep_proportion=True)
|
||||
|
||||
# 3. Vložíme upravenou stránku do finálního PDF
|
||||
final_doc.insert_pdf(ocr_page_doc)
|
||||
|
||||
# --- KROK D: Uložení ---
|
||||
if detected_rc:
|
||||
new_filename = f"{detected_rc}.pdf"
|
||||
print(f" -> Nalezeno RČ: {detected_rc}")
|
||||
else:
|
||||
new_filename = f"{os.path.splitext(filename)[0]}_ocr.pdf"
|
||||
print(f" -> RČ nenalezeno.")
|
||||
|
||||
# Řešení duplicit (pokud soubor už existuje)
|
||||
counter = 1
|
||||
base_name_check = os.path.splitext(new_filename)[0]
|
||||
while os.path.exists(os.path.join(OUTPUT_FOLDER, new_filename)):
|
||||
new_filename = f"{base_name_check}_{counter}.pdf"
|
||||
counter += 1
|
||||
|
||||
output_path = os.path.join(OUTPUT_FOLDER, new_filename)
|
||||
|
||||
# Uložení s kompresí
|
||||
final_doc.save(output_path, garbage=4, deflate=True)
|
||||
final_doc.close()
|
||||
doc_src.close()
|
||||
|
||||
# Výpis velikosti
|
||||
size_kb = os.path.getsize(output_path) / 1024
|
||||
print(f" -> Uloženo: {new_filename} ({size_kb:.1f} kB)")
|
||||
print("-" * 40)
|
||||
|
||||
except Exception as e:
|
||||
print(f"CHYBA u {filename}: {e}")
|
||||
|
||||
print("\nHOTOVO.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
process_ultimate_method()
|
||||
@@ -0,0 +1,205 @@
|
||||
import fitz # PyMuPDF
|
||||
import pytesseract
|
||||
from PIL import Image
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import fdb # Firebeird database library
|
||||
|
||||
# ==========================================
|
||||
# KONFIGURACE
|
||||
# ==========================================
|
||||
|
||||
# 1. Cesty k souborům
|
||||
INPUT_FOLDER = r'u:\Dropbox\Ordinace\Dokumentace_ke_zpracování\AdobeMakeSmaller'
|
||||
OUTPUT_FOLDER = os.path.join(INPUT_FOLDER, '_HOTOVO_MEDICUS')
|
||||
|
||||
# 2. Tesseract OCR
|
||||
PATH_TO_TESSERACT = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
|
||||
|
||||
# 3. Kvalita výstupu (Ultimate metoda)
|
||||
VISUAL_DPI = 150 # Pro zobrazení (malé)
|
||||
VISUAL_THRESHOLD = 150 # Práh černé
|
||||
OCR_DPI = 300 # Pro čtení (velké)
|
||||
|
||||
# 4. Databáze MEDICUS (Firebird)
|
||||
DB_CONFIG = {
|
||||
'host': "192.168.1.4",
|
||||
'port': 3050,
|
||||
'database': r"z:\Medicus 3\data\MEDICUS.FDB",
|
||||
'user': "SYSDBA",
|
||||
'password': "masterkey",
|
||||
'charset': "WIN1250" # Čeština ve Windows
|
||||
}
|
||||
|
||||
|
||||
# ==========================================
|
||||
# FUNKCE
|
||||
# ==========================================
|
||||
|
||||
def setup_tesseract():
|
||||
if not os.path.exists(PATH_TO_TESSERACT):
|
||||
print(f"CHYBA: Tesseract nenalezen na: {PATH_TO_TESSERACT}")
|
||||
return False
|
||||
pytesseract.pytesseract.tesseract_cmd = PATH_TO_TESSERACT
|
||||
return True
|
||||
|
||||
|
||||
def get_rc_candidates(text):
|
||||
"""Vrátí seznam RČ nalezených v textu, očistěných od mezer a lomítek."""
|
||||
candidates = set()
|
||||
# Regex bere i mezery: 75 05 12 / 1234
|
||||
pattern = r'\b\d{2}\s*\d{2}\s*\d{2}\s*[\/]?\s*\d{3,4}\b'
|
||||
matches = re.findall(pattern, text)
|
||||
|
||||
for match in matches:
|
||||
# Odstraníme vše kromě čísel -> vznikne čisté RČ pro DB
|
||||
clean = re.sub(r'[^\d]', '', match)
|
||||
if len(clean) in [9, 10]:
|
||||
candidates.add(clean)
|
||||
return list(candidates)
|
||||
|
||||
|
||||
def get_patient_from_db(rc_clean):
|
||||
"""
|
||||
Vrátí (True, "Prijmeni, Jmeno") pokud najde RČ v tabulce KAR.
|
||||
"""
|
||||
con = None
|
||||
try:
|
||||
con = fdb.connect(**DB_CONFIG)
|
||||
cur = con.cursor()
|
||||
|
||||
# Medicus má RČ bez lomítka, takže se ptáme rovnou
|
||||
sql = "SELECT prijmeni, jmeno FROM kar WHERE rodcis = ?"
|
||||
cur.execute(sql, (rc_clean,))
|
||||
row = cur.fetchone()
|
||||
|
||||
if row:
|
||||
# row[0] = Prijmeni, row[1] = Jmeno
|
||||
# .strip() je nutný, databáze vrací mezery na konci (CHAR field)
|
||||
prijmeni = str(row[0]).strip()
|
||||
jmeno = str(row[1]).strip()
|
||||
|
||||
formatted_name = f"{prijmeni}, {jmeno}"
|
||||
return True, formatted_name
|
||||
else:
|
||||
return False, None
|
||||
|
||||
except Exception as e:
|
||||
print(f" [DB ERROR] {e}")
|
||||
return False, None
|
||||
finally:
|
||||
if con:
|
||||
con.close()
|
||||
|
||||
|
||||
def process_medicus_final():
|
||||
if not setup_tesseract():
|
||||
return
|
||||
|
||||
if not os.path.exists(OUTPUT_FOLDER):
|
||||
os.makedirs(OUTPUT_FOLDER)
|
||||
|
||||
files = [f for f in os.listdir(INPUT_FOLDER) if f.lower().endswith('.pdf')]
|
||||
print(f"Startuji zpracování {len(files)} souborů.")
|
||||
print(f"Výstupní formát: 'RC Prijmeni, Jmeno.pdf'")
|
||||
print("-" * 70)
|
||||
|
||||
for filename in files:
|
||||
input_path = os.path.join(INPUT_FOLDER, filename)
|
||||
|
||||
try:
|
||||
print(f"Zpracovávám: {filename}")
|
||||
|
||||
doc_src = fitz.open(input_path)
|
||||
final_doc = fitz.open()
|
||||
|
||||
found_rc = None
|
||||
found_name = None
|
||||
|
||||
# --- 1. PRŮCHOD STRÁNKAMI ---
|
||||
for page_num, page in enumerate(doc_src):
|
||||
|
||||
# A) PŘÍPRAVA OCR VRSTVY (High Res)
|
||||
mat_ocr = fitz.Matrix(OCR_DPI / 72, OCR_DPI / 72)
|
||||
pix_ocr = page.get_pixmap(matrix=mat_ocr, colorspace=fitz.csGRAY)
|
||||
img_ocr = Image.frombytes("L", [pix_ocr.width, pix_ocr.height], pix_ocr.samples)
|
||||
|
||||
# Tesseract vytvoří PDF stránku
|
||||
pdf_bytes = pytesseract.image_to_pdf_or_hocr(img_ocr, extension='pdf', lang='ces')
|
||||
ocr_page_doc = fitz.open("pdf", pdf_bytes)
|
||||
ocr_page = ocr_page_doc[0]
|
||||
|
||||
# B) HLEDÁNÍ PACIENTA (pokud ještě nemáme)
|
||||
if not found_rc:
|
||||
text = ocr_page.get_text()
|
||||
candidates = get_rc_candidates(text)
|
||||
|
||||
for cand in candidates:
|
||||
print(f" -> Ověřuji v DB: {cand} ...", end=" ")
|
||||
exists, name_str = get_patient_from_db(cand)
|
||||
|
||||
if exists:
|
||||
print(f"NALEZEN: {name_str}")
|
||||
found_rc = cand
|
||||
found_name = name_str
|
||||
break # Máme ho!
|
||||
else:
|
||||
print("Nenalezen.")
|
||||
|
||||
# C) PŘÍPRAVA VIZUÁLNÍ VRSTVY (Low Res - Zmenšení)
|
||||
mat_vis = fitz.Matrix(VISUAL_DPI / 72, VISUAL_DPI / 72)
|
||||
pix_vis = page.get_pixmap(matrix=mat_vis, colorspace=fitz.csGRAY)
|
||||
img_vis = Image.frombytes("L", [pix_vis.width, pix_vis.height], pix_vis.samples)
|
||||
# Threshold
|
||||
img_vis_bw = img_vis.point(lambda x: 255 if x > VISUAL_THRESHOLD else 0, mode='1')
|
||||
|
||||
buffer_vis = io.BytesIO()
|
||||
img_vis_bw.save(buffer_vis, format="PNG", optimize=True)
|
||||
|
||||
# D) PROHOZENÍ OBRÁZKŮ (Ultimate trick)
|
||||
images = ocr_page.get_images()
|
||||
if images:
|
||||
xref = images[0][0]
|
||||
ocr_page.delete_image(xref) # Smaže velký
|
||||
|
||||
# Vloží malý
|
||||
ocr_page.insert_image(ocr_page.rect, stream=buffer_vis.getvalue(), keep_proportion=True)
|
||||
|
||||
# Přidá stránku do finálního PDF
|
||||
final_doc.insert_pdf(ocr_page_doc)
|
||||
|
||||
# --- 2. ULOŽENÍ SOUBORU ---
|
||||
if found_rc and found_name:
|
||||
# Formát: 1234567890 NOVAK, JAN.pdf
|
||||
# Odstraníme případné nepovolené znaky ve jméně (pro jistotu)
|
||||
safe_name = re.sub(r'[\\/*?:"<>|]', "", found_name)
|
||||
new_filename = f"{found_rc} {safe_name}.pdf"
|
||||
else:
|
||||
new_filename = f"{os.path.splitext(filename)[0]}_neovereno.pdf"
|
||||
|
||||
# Řešení konfliktů (kdyby soubor už existoval)
|
||||
counter = 1
|
||||
base_name = os.path.splitext(new_filename)[0]
|
||||
while os.path.exists(os.path.join(OUTPUT_FOLDER, new_filename)):
|
||||
new_filename = f"{base_name}_{counter}.pdf"
|
||||
counter += 1
|
||||
|
||||
output_path = os.path.join(OUTPUT_FOLDER, new_filename)
|
||||
|
||||
final_doc.save(output_path, garbage=4, deflate=True)
|
||||
final_doc.close()
|
||||
doc_src.close()
|
||||
|
||||
size_kb = os.path.getsize(output_path) / 1024
|
||||
print(f" -> Uloženo: {new_filename} ({size_kb:.1f} kB)")
|
||||
print("-" * 70)
|
||||
|
||||
except Exception as e:
|
||||
print(f"CHYBA: {e}")
|
||||
|
||||
print("\nHOTOVO.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
process_medicus_final()
|
||||
@@ -0,0 +1,66 @@
|
||||
import fitz # PyMuPDF
|
||||
import os
|
||||
|
||||
|
||||
def compress_and_grayscale_pdf(input_path, quality=60, dpi=150):
|
||||
"""
|
||||
input_path: Cesta k souboru
|
||||
quality: Kvalita JPG komprese (1-100). 60 je dobrý kompromis pro čitelnost a velikost.
|
||||
dpi: Rozlišení. 150 DPI stačí pro čtení na monitoru/běžný tisk. Pro menší velikost zkuste 96 nebo 72.
|
||||
"""
|
||||
|
||||
# Kontrola existence souboru
|
||||
if not os.path.exists(input_path):
|
||||
print(f"Chyba: Soubor nebyl nalezen: {input_path}")
|
||||
return
|
||||
|
||||
try:
|
||||
# Otevření zdrojového PDF
|
||||
doc = fitz.open(input_path)
|
||||
new_doc = fitz.open() # Vytvoření nového prázdného PDF
|
||||
|
||||
print(f"Zpracovávám: {os.path.basename(input_path)}")
|
||||
print(f"Původní velikost: {os.path.getsize(input_path) / 1024:.2f} KB")
|
||||
|
||||
for page_num, page in enumerate(doc):
|
||||
# Nastavení matice pro změnu rozlišení (DPI)
|
||||
# Standardní PDF má 72 DPI. Zoom = požadované DPI / 72
|
||||
zoom = dpi / 72
|
||||
matrix = fitz.Matrix(zoom, zoom)
|
||||
|
||||
# 1. Renderování stránky do obrázku (Pixmap)
|
||||
# colorspace=fitz.csGRAY zajistí převod do černobílé (Grayscale)
|
||||
pix = page.get_pixmap(matrix=matrix, colorspace=fitz.csGRAY)
|
||||
|
||||
# 2. Vytvoření nové stránky v cílovém dokumentu se stejnými rozměry
|
||||
# Používáme původní rozměry stránky (rect), aby se PDF "nezmenšilo" vizuálně na monitoru
|
||||
new_page = new_doc.new_page(width=page.rect.width, height=page.rect.height)
|
||||
|
||||
# 3. Vložení zmenšeného a odbarveného obrázku zpět
|
||||
# stream=pix.tobytes("jpg") zajistí silnou kompresi
|
||||
new_page.insert_image(page.rect, stream=pix.tobytes("jpg", jpg_quality=quality))
|
||||
|
||||
# Vytvoření názvu výstupního souboru
|
||||
output_path = input_path.replace(".pdf", "_bw_small.pdf")
|
||||
|
||||
# Uložení s garbage collection (odstranění nepotřebných dat) a deflací
|
||||
new_doc.save(output_path, garbage=4, deflate=True)
|
||||
new_doc.close()
|
||||
doc.close()
|
||||
|
||||
# Výpis výsledku
|
||||
new_size = os.path.getsize(output_path)
|
||||
print(f"Hotovo. Uloženo jako: {output_path}")
|
||||
print(f"Nová velikost: {new_size / 1024:.2f} KB")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Došlo k chybě: {e}")
|
||||
|
||||
|
||||
# --- SPUŠTĚNÍ ---
|
||||
# Použití raw stringu (r''), aby Python neinterpretoval zpětná lomítka jako escape znaky
|
||||
file_path = r'u:\Dropbox\Ordinace\Dokumentace_ke_zpracování\AdobeMakeSmaller\90df6f84-72c8-40b8-8993-ce7e244b0cea.pdf'
|
||||
|
||||
# Můžete experimentovat s parametry.
|
||||
# Pokud bude text nečitelný, zvyšte dpi na 200. Pokud je soubor moc velký, snižte quality na 40.
|
||||
compress_and_grayscale_pdf(file_path, quality=50, dpi=120)
|
||||
@@ -0,0 +1,37 @@
|
||||
import fitz # PyMuPDF
|
||||
|
||||
file_path = r'u:\Dropbox\Ordinace\Dokumentace_ke_zpracování\AdobeMakeSmaller\90df6f84-72c8-40b8-8993-ce7e244b0cea.pdf'
|
||||
|
||||
doc = fitz.open(file_path)
|
||||
|
||||
print(f"Soubor: {file_path}")
|
||||
print("-" * 40)
|
||||
|
||||
for i, page in enumerate(doc):
|
||||
# Získání seznamu obrázků na stránce
|
||||
images = page.get_images(full=True)
|
||||
|
||||
# Rozměry stránky v palcích (PDF body / 72)
|
||||
page_w_inch = page.rect.width / 72
|
||||
page_h_inch = page.rect.height / 72
|
||||
|
||||
if images:
|
||||
for img in images:
|
||||
xref = img[0]
|
||||
base_img = doc.extract_image(xref)
|
||||
w = base_img["width"]
|
||||
h = base_img["height"]
|
||||
ext = base_img["ext"]
|
||||
|
||||
# Výpočet DPI (Pixely / Palce)
|
||||
dpi_x = w / page_w_inch
|
||||
dpi_y = h / page_h_inch
|
||||
|
||||
print(f"Strana {i + 1}:")
|
||||
print(f" - Rozměry obrázku: {w} x {h} px")
|
||||
print(f" - Formát: {ext}")
|
||||
print(f" - Reálné DPI: cca {int(dpi_x)}")
|
||||
else:
|
||||
print(f"Strana {i + 1}: Žádný vložený obrázek (vektorový text).")
|
||||
|
||||
print("-" * 40)
|
||||
@@ -0,0 +1,87 @@
|
||||
import fitz # PyMuPDF
|
||||
from PIL import Image
|
||||
import io
|
||||
import os
|
||||
|
||||
# --- NASTAVENÍ ---
|
||||
INPUT_FILE = r'u:\Dropbox\Ordinace\Dokumentace_ke_zpracování\AdobeMakeSmaller\90df6f84-72c8-40b8-8993-ce7e244b0cea.pdf'
|
||||
|
||||
# 1. Prahové hodnoty: Zúžíme výběr na nejpravděpodobnější rozsah
|
||||
THRESHOLDS = [110, 130, 150, 170]
|
||||
|
||||
# 2. DPI Varianty k testování
|
||||
# 131 = Přesná polovina originálu (váš nápad)
|
||||
# 150 = Standard
|
||||
# 200 = Vysoká kvalita
|
||||
# 262 = Originál (žádná ztráta detailů, jen ztráta barev)
|
||||
DPI_LIST = [131, 150, 200, 262]
|
||||
|
||||
|
||||
def generate_final_matrix():
|
||||
if not os.path.exists(INPUT_FILE):
|
||||
print(f"Chyba: Soubor nenalezen.")
|
||||
return
|
||||
|
||||
# Příprava složky
|
||||
base_folder = os.path.dirname(INPUT_FILE)
|
||||
filename = os.path.basename(INPUT_FILE)
|
||||
name_without_ext = os.path.splitext(filename)[0]
|
||||
|
||||
output_dir = os.path.join(base_folder, "_TEST_DPI_MATH")
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
print(f"Generuji varianty do: {output_dir}")
|
||||
print("-" * 80)
|
||||
print(f"{'DPI':<5} | {'Práh':<6} | {'Velikost':<12} | {'Poznámka'}")
|
||||
print("-" * 80)
|
||||
|
||||
doc = fitz.open(INPUT_FILE)
|
||||
|
||||
for dpi in DPI_LIST:
|
||||
# Přednačtení stránek v daném DPI
|
||||
pages_at_dpi = []
|
||||
for page in doc:
|
||||
zoom = dpi / 72
|
||||
matrix = fitz.Matrix(zoom, zoom)
|
||||
pix = page.get_pixmap(matrix=matrix, colorspace=fitz.csGRAY)
|
||||
pages_at_dpi.append(pix)
|
||||
|
||||
for t in THRESHOLDS:
|
||||
new_doc = fitz.open()
|
||||
|
||||
for i, pix in enumerate(pages_at_dpi):
|
||||
# Binarizace
|
||||
img = Image.frombytes("L", [pix.width, pix.height], pix.samples)
|
||||
img_bw = img.point(lambda x: 255 if x > t else 0, mode='1')
|
||||
|
||||
# Uložení
|
||||
buffer = io.BytesIO()
|
||||
img_bw.save(buffer, format="PNG", optimize=True)
|
||||
|
||||
# Vložení zpět
|
||||
orig_rect = doc[i].rect
|
||||
new_page = new_doc.new_page(width=orig_rect.width, height=orig_rect.height)
|
||||
new_page.insert_image(orig_rect, stream=buffer.getvalue())
|
||||
|
||||
# Uložení
|
||||
output_name = f"dpi{dpi}_t{t}.pdf"
|
||||
output_path = os.path.join(output_dir, output_name)
|
||||
new_doc.save(output_path, garbage=4, deflate=True)
|
||||
new_doc.close()
|
||||
|
||||
# Výpis
|
||||
size_kb = os.path.getsize(output_path) / 1024
|
||||
|
||||
note = ""
|
||||
if dpi == 131: note = "(Polovina originálu)"
|
||||
if dpi == 262: note = "(Originální rozlišení)"
|
||||
|
||||
print(f"{dpi:<5} | {t:<6} | {size_kb:<9.2f} KB | {note}")
|
||||
|
||||
doc.close()
|
||||
print("-" * 80)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
generate_final_matrix()
|
||||
@@ -0,0 +1,95 @@
|
||||
import fitz # PyMuPDF
|
||||
from PIL import Image
|
||||
import io
|
||||
import os
|
||||
import shutil
|
||||
|
||||
# --- VAŠE VÍTĚZNÉ NASTAVENÍ ---
|
||||
CHOSEN_DPI = 150
|
||||
CHOSEN_THRESHOLD = 150
|
||||
|
||||
# Cesty
|
||||
INPUT_FOLDER = r'u:\Dropbox\Ordinace\Dokumentace_ke_zpracování\AdobeMakeSmaller'
|
||||
OUTPUT_SUBFOLDER = '_HOTOVO'
|
||||
|
||||
|
||||
def process_all_files():
|
||||
# Vytvoření výstupní složky
|
||||
output_dir = os.path.join(INPUT_FOLDER, OUTPUT_SUBFOLDER)
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
# Nalezení všech PDF (ignorujeme již hotové v podsložkách)
|
||||
files = [f for f in os.listdir(INPUT_FOLDER) if f.lower().endswith('.pdf')]
|
||||
|
||||
print(f"Startuji zpracování {len(files)} souborů.")
|
||||
print(f"Nastavení: DPI={CHOSEN_DPI}, Práh={CHOSEN_THRESHOLD}")
|
||||
print("-" * 60)
|
||||
|
||||
success_count = 0
|
||||
|
||||
for filename in files:
|
||||
input_path = os.path.join(INPUT_FOLDER, filename)
|
||||
|
||||
# Přidáme '_bw' do názvu, aby se to nepletlo
|
||||
name_without_ext = os.path.splitext(filename)[0]
|
||||
output_filename = f"{name_without_ext}_bw.pdf"
|
||||
output_path = os.path.join(output_dir, output_filename)
|
||||
|
||||
try:
|
||||
# Zpracování jednoho souboru
|
||||
process_single_pdf(input_path, output_path)
|
||||
|
||||
# Výpis výsledku
|
||||
orig_size = os.path.getsize(input_path) / 1024
|
||||
new_size = os.path.getsize(output_path) / 1024
|
||||
ratio = (1 - (new_size / orig_size)) * 100
|
||||
|
||||
print(f"[OK] {filename}")
|
||||
print(f" {orig_size:.1f} kB -> {new_size:.1f} kB (úspora {ratio:.0f} %)")
|
||||
success_count += 1
|
||||
|
||||
# --- VOLITELNÉ: SMAZÁNÍ ORIGINÁLU ---
|
||||
# Pokud chcete, aby skript po úspěchu smazal původní velký soubor,
|
||||
# odkomentujte následující řádek (odstraňte mřížku #):
|
||||
# os.remove(input_path)
|
||||
|
||||
except Exception as e:
|
||||
print(f"[CHYBA] {filename}: {e}")
|
||||
|
||||
print("-" * 60)
|
||||
print(f"Hotovo. Úspěšně zpracováno {success_count} z {len(files)} souborů.")
|
||||
print(f"Výstupy jsou ve složce: {output_dir}")
|
||||
|
||||
|
||||
def process_single_pdf(input_path, output_path):
|
||||
doc = fitz.open(input_path)
|
||||
new_doc = fitz.open()
|
||||
|
||||
for page in doc:
|
||||
# 1. Renderování stránky (Downsampling na 150 DPI)
|
||||
zoom = CHOSEN_DPI / 72
|
||||
matrix = fitz.Matrix(zoom, zoom)
|
||||
pix = page.get_pixmap(matrix=matrix, colorspace=fitz.csGRAY)
|
||||
|
||||
# 2. Binarizace (Threshold 150)
|
||||
img = Image.frombytes("L", [pix.width, pix.height], pix.samples)
|
||||
img_bw = img.point(lambda x: 255 if x > CHOSEN_THRESHOLD else 0, mode='1')
|
||||
|
||||
# 3. Uložení 1-bitového obrázku
|
||||
buffer = io.BytesIO()
|
||||
img_bw.save(buffer, format="PNG", optimize=True)
|
||||
|
||||
# 4. Vložení do nového PDF
|
||||
# Zachováme fyzické rozměry stránky (aby se netiskla jako známka)
|
||||
new_page = new_doc.new_page(width=page.rect.width, height=page.rect.height)
|
||||
new_page.insert_image(page.rect, stream=buffer.getvalue())
|
||||
|
||||
# Uložení s maximální kompresí
|
||||
new_doc.save(output_path, garbage=4, deflate=True)
|
||||
new_doc.close()
|
||||
doc.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
process_all_files()
|
||||
@@ -0,0 +1,67 @@
|
||||
import fitz # PyMuPDF
|
||||
import pytesseract
|
||||
from PIL import Image
|
||||
import os
|
||||
import re
|
||||
|
||||
# --- NASTAVENÍ ---
|
||||
PATH_TO_TESSERACT = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
|
||||
INPUT_FILE = r'u:\Dropbox\Ordinace\Dokumentace_ke_zpracování\AdobeMakeSmaller\6ed31e44-0e7d-4118-8912-c3e583781459.pdf'
|
||||
|
||||
# Nastavíme Tesseract
|
||||
pytesseract.pytesseract.tesseract_cmd = PATH_TO_TESSERACT
|
||||
|
||||
|
||||
def debug_ocr_output():
|
||||
if not os.path.exists(INPUT_FILE):
|
||||
print(f"Soubor nenalezen: {INPUT_FILE}")
|
||||
return
|
||||
|
||||
print(f"ANALÝZA SOUBORU: {os.path.basename(INPUT_FILE)}")
|
||||
doc = fitz.open(INPUT_FILE)
|
||||
|
||||
# Projdeme první stranu (RČ bývá tam)
|
||||
for i, page in enumerate(doc):
|
||||
if i > 0: break # Stačí nám 1. strana pro test
|
||||
|
||||
print(f"\n--- STRANA {i + 1} (300 DPI) ---")
|
||||
|
||||
# 1. Renderování ve vysoké kvalitě (stejně jako v 'Ultimate' skriptu)
|
||||
# Pokud je sken nekvalitní, Tesseract potřebuje velké rozlišení
|
||||
zoom = 300 / 72
|
||||
matrix = fitz.Matrix(zoom, zoom)
|
||||
pix = page.get_pixmap(matrix=matrix, colorspace=fitz.csGRAY)
|
||||
img = Image.frombytes("L", [pix.width, pix.height], pix.samples)
|
||||
|
||||
# 2. OCR - Získání surového textu
|
||||
# config='--psm 3' je standardní automatická segmentace stránky
|
||||
raw_text = pytesseract.image_to_string(img, lang='ces', config='--psm 3')
|
||||
|
||||
print(">>> SUROVÝ TEXT Z TESSERACTU (ZAČÁTEK) <<<")
|
||||
print("-" * 50)
|
||||
print(raw_text)
|
||||
print("-" * 50)
|
||||
print(">>> SUROVÝ TEXT Z TESSERACTU (KONEC) <<<")
|
||||
|
||||
# 3. Test Regexu na tomto textu
|
||||
print("\n--- TEST HLEDÁNÍ RČ ---")
|
||||
|
||||
# Standardní Regex
|
||||
strict_pattern = r'\b\d{6}\s*[\/]?\s*\d{3,4}\b'
|
||||
strict_matches = re.findall(strict_pattern, raw_text)
|
||||
print(f"Přísný filtr (očekává jen čísla): {strict_matches}")
|
||||
|
||||
# Volnější Regex (hledá i chyby OCR jako 'O' místo '0' nebo 'l' místo '1')
|
||||
# \d -> číslice
|
||||
# [O0] -> nula nebo O
|
||||
# [lI1] -> jednička, malé L nebo velké i
|
||||
loose_pattern = r'\b[0-9O]{6}\s*[\/ilI1]?\s*[0-9O]{3,4}\b'
|
||||
loose_matches = re.findall(loose_pattern, raw_text)
|
||||
|
||||
if loose_matches and not strict_matches:
|
||||
print(f"⚠️ POZOR: Našel jsem RČ jen pomocí volného filtru (obsahuje překlepy OCR): {loose_matches}")
|
||||
print(" -> Bude potřeba upravit čistící funkci.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
debug_ocr_output()
|
||||
@@ -0,0 +1,67 @@
|
||||
import fitz # PyMuPDF
|
||||
import pytesseract
|
||||
from PIL import Image
|
||||
import os
|
||||
import re
|
||||
|
||||
# --- NASTAVENÍ ---
|
||||
PATH_TO_TESSERACT = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
|
||||
INPUT_FILE = r'u:\Dropbox\Ordinace\Dokumentace_ke_zpracování\AdobeMakeSmaller\9b79f621-30ad-4468-85fe-6f2fd76289ce.pdf'
|
||||
|
||||
# Nastavíme Tesseract
|
||||
pytesseract.pytesseract.tesseract_cmd = PATH_TO_TESSERACT
|
||||
|
||||
|
||||
def debug_ocr_output():
|
||||
if not os.path.exists(INPUT_FILE):
|
||||
print(f"Soubor nenalezen: {INPUT_FILE}")
|
||||
return
|
||||
|
||||
print(f"ANALÝZA SOUBORU: {os.path.basename(INPUT_FILE)}")
|
||||
doc = fitz.open(INPUT_FILE)
|
||||
|
||||
# Projdeme první stranu (RČ bývá tam)
|
||||
for i, page in enumerate(doc):
|
||||
if i > 0: break # Stačí nám 1. strana pro test
|
||||
|
||||
print(f"\n--- STRANA {i + 1} (300 DPI) ---")
|
||||
|
||||
# 1. Renderování ve vysoké kvalitě (stejně jako v 'Ultimate' skriptu)
|
||||
# Pokud je sken nekvalitní, Tesseract potřebuje velké rozlišení
|
||||
zoom = 300 / 72
|
||||
matrix = fitz.Matrix(zoom, zoom)
|
||||
pix = page.get_pixmap(matrix=matrix, colorspace=fitz.csGRAY)
|
||||
img = Image.frombytes("L", [pix.width, pix.height], pix.samples)
|
||||
|
||||
# 2. OCR - Získání surového textu
|
||||
# config='--psm 3' je standardní automatická segmentace stránky
|
||||
raw_text = pytesseract.image_to_string(img, lang='ces', config='--psm 3')
|
||||
|
||||
print(">>> SUROVÝ TEXT Z TESSERACTU (ZAČÁTEK) <<<")
|
||||
print("-" * 50)
|
||||
print(raw_text)
|
||||
print("-" * 50)
|
||||
print(">>> SUROVÝ TEXT Z TESSERACTU (KONEC) <<<")
|
||||
|
||||
# 3. Test Regexu na tomto textu
|
||||
print("\n--- TEST HLEDÁNÍ RČ ---")
|
||||
|
||||
# Standardní Regex
|
||||
strict_pattern = r'\b\d{6}\s*[\/]?\s*\d{3,4}\b'
|
||||
strict_matches = re.findall(strict_pattern, raw_text)
|
||||
print(f"Přísný filtr (očekává jen čísla): {strict_matches}")
|
||||
|
||||
# Volnější Regex (hledá i chyby OCR jako 'O' místo '0' nebo 'l' místo '1')
|
||||
# \d -> číslice
|
||||
# [O0] -> nula nebo O
|
||||
# [lI1] -> jednička, malé L nebo velké i
|
||||
loose_pattern = r'\b[0-9O]{6}\s*[\/ilI1]?\s*[0-9O]{3,4}\b'
|
||||
loose_matches = re.findall(loose_pattern, raw_text)
|
||||
|
||||
if loose_matches and not strict_matches:
|
||||
print(f"⚠️ POZOR: Našel jsem RČ jen pomocí volného filtru (obsahuje překlepy OCR): {loose_matches}")
|
||||
print(" -> Bude potřeba upravit čistící funkci.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
debug_ocr_output()
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
+55855
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,136 @@
|
||||
import os
|
||||
import pymysql
|
||||
import sys
|
||||
import blake3 # Nutné: pip install blake3
|
||||
|
||||
# ==============================
|
||||
# ⚙️ NASTAVENÍ
|
||||
# ==============================
|
||||
LOCAL_ROOT = r"U:\#Syncthing"
|
||||
|
||||
# !!! AŽ SI BUDEŠ JISTÝ, ZMĚŇ NA True !!!
|
||||
DELETE_MODE = False
|
||||
|
||||
# Hledáme shodu pouze v souborech, které jsou na serveru v této cestě:
|
||||
REMOTE_FOLDER_FILTER = "/#ColdData/Porno/"
|
||||
|
||||
# DB Nastavení
|
||||
DB_HOST = "192.168.1.76"
|
||||
DB_PORT = 3307
|
||||
DB_USER = "root"
|
||||
DB_PASS = "Vlado9674+"
|
||||
DB_NAME = "torrents"
|
||||
DB_TABLE = "file_md5_index"
|
||||
|
||||
|
||||
# ==============================
|
||||
|
||||
def get_db_connection():
|
||||
try:
|
||||
return pymysql.connect(
|
||||
host=DB_HOST, port=DB_PORT, user=DB_USER, password=DB_PASS,
|
||||
database=DB_NAME, cursorclass=pymysql.cursors.DictCursor
|
||||
)
|
||||
except Exception as e:
|
||||
sys.exit(f"❌ Chyba DB: {e}")
|
||||
|
||||
|
||||
def calculate_blake3(file_path):
|
||||
"""Spočítá BLAKE3 hash souboru"""
|
||||
h = blake3.blake3()
|
||||
try:
|
||||
with open(file_path, "rb") as f:
|
||||
while True:
|
||||
chunk = f.read(4 * 1024 * 1024) # 4MB chunks
|
||||
if not chunk:
|
||||
break
|
||||
h.update(chunk)
|
||||
return h.digest()
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
print(f"🚀 TERMINÁTOR: Mazání podle obsahu (BLAKE3)")
|
||||
print(f" 📂 Zdroj: {LOCAL_ROOT}")
|
||||
print(f" 🎯 DB filtr: {REMOTE_FOLDER_FILTER}")
|
||||
print(f" 🛠 Mód: {'🔴 OSTRÝ (MAZÁNÍ)' if DELETE_MODE else '🟢 SIMULACE (BEZPEČNÝ)'}")
|
||||
print("=====================================================")
|
||||
|
||||
# 1. NAČTENÍ HASHŮ Z DB
|
||||
print("📡 Stahuji hashe ze serveru...")
|
||||
remote_hashes = set()
|
||||
|
||||
conn = get_db_connection()
|
||||
with conn.cursor() as cursor:
|
||||
sql = f"SELECT blake3 FROM {DB_TABLE} WHERE full_path LIKE %s"
|
||||
cursor.execute(sql, (f"%{REMOTE_FOLDER_FILTER}%",))
|
||||
rows = cursor.fetchall()
|
||||
|
||||
for row in rows:
|
||||
remote_hashes.add(row['blake3'])
|
||||
|
||||
print(f"✅ V paměti mám {len(remote_hashes):,} unikátních hashů ze serveru.")
|
||||
print("=====================================================")
|
||||
|
||||
# 2. SKENOVÁNÍ A MAZÁNÍ
|
||||
count_scanned = 0
|
||||
count_match = 0
|
||||
size_freed = 0
|
||||
|
||||
for root, dirs, files in os.walk(LOCAL_ROOT):
|
||||
# Přeskočit syncthing složky
|
||||
if ".stfolder" in dirs: dirs.remove(".stfolder")
|
||||
if ".stversions" in dirs: dirs.remove(".stversions")
|
||||
|
||||
for filename in files:
|
||||
file_path = os.path.join(root, filename)
|
||||
count_scanned += 1
|
||||
|
||||
# Výpis práce
|
||||
print(f"⏳ [{count_scanned}] Zpracovávám: {filename}")
|
||||
|
||||
# Spočítat lokální hash
|
||||
local_hash = calculate_blake3(file_path)
|
||||
|
||||
if local_hash is None:
|
||||
print(f" ⚠️ Chyba čtení souboru!")
|
||||
continue
|
||||
|
||||
# Zobrazíme kousek hashe pro kontrolu (převedeme na hex text)
|
||||
hash_preview = local_hash.hex()[:8]
|
||||
|
||||
# JE TEN HASH V DATABÁZI?
|
||||
if local_hash in remote_hashes:
|
||||
count_match += 1
|
||||
file_size = os.path.getsize(file_path)
|
||||
size_freed += file_size
|
||||
|
||||
print(f" ✅ SHODA (Hash: {hash_preview}...) -> {'🗑️ MAŽU' if DELETE_MODE else '📦 NAŠEL BYCH'}")
|
||||
|
||||
if DELETE_MODE:
|
||||
try:
|
||||
os.remove(file_path)
|
||||
# Pokus o smazání prázdné složky
|
||||
try:
|
||||
os.rmdir(root)
|
||||
except:
|
||||
pass
|
||||
except OSError as e:
|
||||
print(f" ❌ Chyba mazání: {e}")
|
||||
else:
|
||||
print(f" ❌ Žádná shoda (Hash: {hash_preview}...) -> Nechávám být")
|
||||
|
||||
print("\n\n📊 VÝSLEDEK")
|
||||
print("=====================================================")
|
||||
print(f"🔎 Prošlo rukama: {count_scanned:,} souborů")
|
||||
print(f"🗑️ Smazáno/Shoda: {count_match:,} souborů")
|
||||
print(f"💾 Ušetřené místo: {size_freed / (1024 * 1024):.2f} MB")
|
||||
print("=====================================================")
|
||||
|
||||
if not DELETE_MODE and count_match > 0:
|
||||
print("💡 TIP: Až budeš připraven, změň v kódu: DELETE_MODE = True")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,134 @@
|
||||
import os
|
||||
import blake3
|
||||
import sys
|
||||
import json
|
||||
from tqdm import tqdm
|
||||
|
||||
# ==============================
|
||||
# ⚙️ NASTAVENÍ
|
||||
# ==============================
|
||||
LOCAL_ROOT = r"U:\#Syncthing"
|
||||
CACHE_FILE = "local_hashes.jsonl"
|
||||
ERROR_LOG = "errors_scan.txt"
|
||||
|
||||
|
||||
def calculate_blake3_hex(file_path):
|
||||
h = blake3.blake3()
|
||||
# Zde ošetříme chyby při otevírání/čtení souboru
|
||||
try:
|
||||
with open(file_path, "rb") as f:
|
||||
while True:
|
||||
chunk = f.read(4 * 1024 * 1024)
|
||||
if not chunk: break
|
||||
h.update(chunk)
|
||||
return h.hexdigest()
|
||||
except Exception as e:
|
||||
# Vyhodíme výjimku nahoru, abychom ji zachytili v hlavní smyčce i s názvem souboru
|
||||
raise e
|
||||
|
||||
|
||||
def count_files(path):
|
||||
print("⏳ Sčítám soubory...", end="\r")
|
||||
total = 0
|
||||
for root, dirs, files in os.walk(path):
|
||||
if ".stfolder" in dirs: dirs.remove(".stfolder")
|
||||
if ".stversions" in dirs: dirs.remove(".stversions")
|
||||
total += len(files)
|
||||
print(f"✅ Celkem souborů: {total:,}")
|
||||
return total
|
||||
|
||||
|
||||
def load_processed_files(cache_file):
|
||||
"""Načte cesty souborů, které už máme hotové"""
|
||||
processed = set()
|
||||
if os.path.exists(cache_file):
|
||||
print("📥 Načítám již hotové záznamy pro navázání...")
|
||||
try:
|
||||
with open(cache_file, "r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line: continue
|
||||
try:
|
||||
data = json.loads(line)
|
||||
processed.add(data["p"]) # "p" je cesta
|
||||
except:
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f"⚠️ Chyba při čtení cache (nevadí, jedeme dál): {e}")
|
||||
|
||||
print(f"⏭ Přeskočím {len(processed):,} již hotových souborů.")
|
||||
return processed
|
||||
|
||||
|
||||
def main():
|
||||
print("🚀 KROK 1: Generování cache (RESUMABLE + SAFE MODE)")
|
||||
print(f"📂 Zdroj: {LOCAL_ROOT}")
|
||||
print(f"💾 Výstup: {CACHE_FILE}")
|
||||
print("===================================")
|
||||
|
||||
# 1. Zjistit celkový počet
|
||||
total_files = count_files(LOCAL_ROOT)
|
||||
|
||||
# 2. Zjistit, co už máme hotové
|
||||
processed_paths = load_processed_files(CACHE_FILE)
|
||||
|
||||
errors = 0
|
||||
newly_processed = 0
|
||||
|
||||
# Otevřeme v režimu 'a' (APPEND) - připisování na konec
|
||||
with open(CACHE_FILE, "a", encoding="utf-8") as f_out:
|
||||
|
||||
with tqdm(total=total_files, unit="file", dynamic_ncols=True) as pbar:
|
||||
|
||||
# Pokud už máme nějaká data, aktualizujeme progress bar
|
||||
if processed_paths:
|
||||
pbar.update(len(processed_paths))
|
||||
|
||||
for root, dirs, files in os.walk(LOCAL_ROOT):
|
||||
if ".stfolder" in dirs: dirs.remove(".stfolder")
|
||||
if ".stversions" in dirs: dirs.remove(".stversions")
|
||||
|
||||
for filename in files:
|
||||
file_path = os.path.join(root, filename)
|
||||
|
||||
# === SKIPPER: Pokud už to máme, jdeme dál ===
|
||||
if file_path in processed_paths:
|
||||
continue
|
||||
# ============================================
|
||||
|
||||
# Pro dlouhé názvy ořízneme text v progress baru
|
||||
short_name = (filename[:30] + '...') if len(filename) > 30 else filename
|
||||
pbar.set_description(f"Hash: {short_name}")
|
||||
|
||||
# === SAFE BLOCK: Try-Except uvnitř smyčky ===
|
||||
try:
|
||||
hex_hash = calculate_blake3_hex(file_path)
|
||||
|
||||
if hex_hash:
|
||||
record = {"h": hex_hash, "p": file_path}
|
||||
f_out.write(json.dumps(record, ensure_ascii=False) + "\n")
|
||||
# Flush, aby se to zapsalo na disk hned (pro jistotu)
|
||||
if newly_processed % 100 == 0:
|
||||
f_out.flush()
|
||||
newly_processed += 1
|
||||
|
||||
except Exception as e:
|
||||
errors += 1
|
||||
error_msg = f"❌ CHYBA: {filename} -> {e}\n"
|
||||
# Zapíšeme chybu do logu, aby to nerušilo output
|
||||
with open(ERROR_LOG, "a", encoding="utf-8") as ferr:
|
||||
ferr.write(error_msg)
|
||||
|
||||
# Posuneme progress bar
|
||||
pbar.update(1)
|
||||
|
||||
print("\n\n✅ HOTOVO.")
|
||||
print(f"📄 Nově přidáno: {newly_processed}")
|
||||
print(f"⏭ Přeskočeno: {len(processed_paths)}")
|
||||
|
||||
if errors > 0:
|
||||
print(f"⚠️ Počet chyb: {errors} (Detaily viz soubor '{ERROR_LOG}')")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,150 @@
|
||||
import os
|
||||
import pymysql
|
||||
import sys
|
||||
import json
|
||||
from tqdm import tqdm
|
||||
|
||||
# ==============================
|
||||
# ⚙️ NASTAVENÍ
|
||||
# ==============================
|
||||
CACHE_FILE = "local_hashes.jsonl"
|
||||
|
||||
# !!! ZMĚŇ NA True PRO SKUTEČNÉ MAZÁNÍ !!!
|
||||
DELETE_MODE = True
|
||||
|
||||
# Filtr DB (Hledáme shodu jen v této složce na serveru)
|
||||
REMOTE_FOLDER_FILTER = "/#ColdData/Porno/"
|
||||
|
||||
DB_CONFIG = {
|
||||
"host": "192.168.1.76",
|
||||
"port": 3307,
|
||||
"user": "root",
|
||||
"password": "Vlado9674+",
|
||||
"database": "torrents",
|
||||
}
|
||||
|
||||
|
||||
# ==============================
|
||||
|
||||
def get_remote_hashes():
|
||||
print("📡 Stahuji hashe z DB (filtr: " + REMOTE_FOLDER_FILTER + ")...")
|
||||
hashes = set()
|
||||
try:
|
||||
conn = pymysql.connect(**DB_CONFIG, cursorclass=pymysql.cursors.DictCursor)
|
||||
with conn.cursor() as cursor:
|
||||
# Stáhneme hashe souborů, které jsou v cílové složce
|
||||
sql = f"SELECT blake3 FROM file_md5_index WHERE full_path LIKE %s"
|
||||
cursor.execute(sql, (f"%{REMOTE_FOLDER_FILTER}%",))
|
||||
|
||||
for row in cursor.fetchall():
|
||||
# DB vrací bytes, my potřebujeme HEX string (stejný jako v JSONL)
|
||||
hashes.add(row['blake3'].hex())
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
sys.exit(f"❌ Chyba DB: {e}")
|
||||
return hashes
|
||||
|
||||
|
||||
def count_lines(filename):
|
||||
"""Spočítá řádky pro progress bar"""
|
||||
print("⏳ Sčítám záznamy v cache...", end="\r")
|
||||
with open(filename, "r", encoding="utf-8") as f:
|
||||
return sum(1 for _ in f)
|
||||
|
||||
|
||||
def main():
|
||||
print("🚀 KROK 2: Terminátor (Mazání podle cache)")
|
||||
print(f"📂 Cache soubor: {CACHE_FILE}")
|
||||
print(f"🛠 Mód: {'🔴 OSTRÝ (MAZÁNÍ)' if DELETE_MODE else '🟢 SIMULACE (JEN VÝPIS)'}")
|
||||
print("===================================")
|
||||
|
||||
if not os.path.exists(CACHE_FILE):
|
||||
sys.exit("❌ Cache soubor neexistuje! Spusť nejdřív KROK 1.")
|
||||
|
||||
# 1. Stáhnout DB
|
||||
remote_hashes = get_remote_hashes()
|
||||
print(f"✅ DB načtena: {len(remote_hashes):,} unikátních hashů ze serveru.")
|
||||
|
||||
# 2. Spočítat řádky pro progress bar
|
||||
total_lines = count_lines(CACHE_FILE)
|
||||
print(f"✅ Cache obsahuje: {total_lines:,} lokálních souborů ke kontrole.")
|
||||
|
||||
print("🔍 Jdu porovnávat...")
|
||||
|
||||
count_deleted = 0
|
||||
freed_space = 0
|
||||
errors = 0
|
||||
|
||||
# Otevření souboru pro logování smazaných (volitelné)
|
||||
log_name = "deleted_files.log" if DELETE_MODE else "would_delete.log"
|
||||
|
||||
with open(CACHE_FILE, "r", encoding="utf-8") as f_in, \
|
||||
open(log_name, "w", encoding="utf-8") as f_log:
|
||||
|
||||
# Progress bar
|
||||
with tqdm(total=total_lines, unit="file", dynamic_ncols=True) as pbar:
|
||||
|
||||
for line in f_in:
|
||||
pbar.update(1)
|
||||
line = line.strip()
|
||||
if not line: continue
|
||||
|
||||
try:
|
||||
data = json.loads(line)
|
||||
local_hash = data["h"]
|
||||
file_path = data["p"]
|
||||
except:
|
||||
continue # Vadný řádek
|
||||
|
||||
# Kontrola existence souboru (mohli jsme ho už smazat ručně)
|
||||
if not os.path.exists(file_path):
|
||||
continue
|
||||
|
||||
# --- HLAVNÍ LOGIKA ---
|
||||
if local_hash in remote_hashes:
|
||||
# SHODA! Soubor je na serveru -> SMAZAT lokálně
|
||||
|
||||
try:
|
||||
size = os.path.getsize(file_path)
|
||||
|
||||
if DELETE_MODE:
|
||||
os.remove(file_path)
|
||||
status = "SMAZÁNO"
|
||||
# Pokus o smazání prázdné složky
|
||||
try:
|
||||
os.rmdir(os.path.dirname(file_path))
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
status = "NAŠEL BYCH"
|
||||
|
||||
count_deleted += 1
|
||||
freed_space += size
|
||||
|
||||
# Zápis do logu
|
||||
f_log.write(f"{file_path}\n")
|
||||
|
||||
# Aktualizace popisku (jen občas, aby to neblikalo moc rychle)
|
||||
if count_deleted % 10 == 0:
|
||||
pbar.set_description(f"Mazání: {count_deleted} files")
|
||||
|
||||
except OSError as e:
|
||||
errors += 1
|
||||
# print(f"❌ Chyba u {file_path}: {e}")
|
||||
|
||||
print("\n\n📊 VÝSLEDEK")
|
||||
print("===================================")
|
||||
print(f"🔎 Zkontrolováno: {total_lines:,}")
|
||||
print(f"🗑️ {'Smazáno' if DELETE_MODE else 'K smazání'}: {count_deleted:,}")
|
||||
print(f"💾 Ušetřeno místo: {freed_space / (1024 * 1024 * 1024):.2f} GB")
|
||||
print(f"📝 Seznam souborů: '{log_name}'")
|
||||
|
||||
if errors > 0:
|
||||
print(f"⚠️ Chyb při mazání: {errors}")
|
||||
|
||||
if not DELETE_MODE and count_deleted > 0:
|
||||
print("\n💡 LÍBÍ SE TI VÝSLEDEK? Změň nahoře: DELETE_MODE = True")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,128 @@
|
||||
import pymysql
|
||||
import sys
|
||||
import os
|
||||
from tqdm import tqdm
|
||||
|
||||
# ==============================
|
||||
# ⚙️ NASTAVENÍ
|
||||
# ==============================
|
||||
|
||||
# Soubor je nyní přímo ve složce s tímto skriptem
|
||||
PHYSICAL_LIST_FILE = "physical_files_list.txt"
|
||||
|
||||
# DB Nastavení
|
||||
DB_CONFIG = {
|
||||
"host": "192.168.1.76",
|
||||
"port": 3307,
|
||||
"user": "root",
|
||||
"password": "Vlado9674+",
|
||||
"database": "torrents",
|
||||
}
|
||||
|
||||
|
||||
# ==============================
|
||||
|
||||
def get_db_paths():
|
||||
print("📡 Stahuji seznam cest z MySQL (to chvilku potrvá)...")
|
||||
paths = set()
|
||||
try:
|
||||
conn = pymysql.connect(**DB_CONFIG)
|
||||
cursor = conn.cursor()
|
||||
# Stáhneme VŠECHNY cesty, které jsou v #ColdData
|
||||
# LIKE optimalizace: MySQL použije index, pokud sloupec full_path má index
|
||||
sql = "SELECT full_path FROM file_md5_index WHERE full_path LIKE '/mnt/user/#ColdData/%'"
|
||||
cursor.execute(sql)
|
||||
|
||||
for row in cursor.fetchall():
|
||||
paths.add(row[0])
|
||||
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
sys.exit(f"❌ Chyba DB: {e}")
|
||||
return paths
|
||||
|
||||
|
||||
def main():
|
||||
print("🚀 AUDIT: Fyzický disk vs. Databáze")
|
||||
print(f"📂 Vstup: {PHYSICAL_LIST_FILE} (Lokální)")
|
||||
print("=====================================================")
|
||||
|
||||
if not os.path.exists(PHYSICAL_LIST_FILE):
|
||||
sys.exit(f"❌ Soubor '{PHYSICAL_LIST_FILE}' nenalezen ve složce projektu!")
|
||||
|
||||
# 1. Načíst DB do paměti
|
||||
db_paths = get_db_paths()
|
||||
print(f"✅ V DB načteno: {len(db_paths):,} záznamů.")
|
||||
|
||||
# 2. Příprava na hledání "Ghost files"
|
||||
ghost_check_set = db_paths.copy()
|
||||
|
||||
# 3. Spočítat řádky pro progress bar
|
||||
print("⏳ Sčítám řádky v souboru...", end="\r")
|
||||
try:
|
||||
total_lines = sum(1 for _ in open(PHYSICAL_LIST_FILE, 'r', encoding='utf-8', errors='ignore'))
|
||||
except:
|
||||
total_lines = 0
|
||||
|
||||
# 4. Porovnání
|
||||
print(f"🔍 Jdu porovnávat {total_lines:,} souborů...")
|
||||
|
||||
missing_in_db = 0
|
||||
found_ok = 0
|
||||
physical_count = 0
|
||||
|
||||
f_missing = open("report_CHYBI_V_DB.txt", "w", encoding="utf-8")
|
||||
|
||||
with open(PHYSICAL_LIST_FILE, "r", encoding="utf-8", errors="ignore") as f:
|
||||
with tqdm(total=total_lines, unit="file", dynamic_ncols=True) as pbar:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line: continue
|
||||
|
||||
# Očekáváme formát: /mnt/user/.../soubor|velikost
|
||||
try:
|
||||
parts = line.rsplit("|", 1)
|
||||
path = parts[0]
|
||||
except IndexError:
|
||||
continue
|
||||
|
||||
physical_count += 1
|
||||
|
||||
# --- KONTROLA ---
|
||||
if path in db_paths:
|
||||
found_ok += 1
|
||||
# Odebrat ze seznamu duchů (našli jsme ho)
|
||||
if path in ghost_check_set:
|
||||
ghost_check_set.remove(path)
|
||||
else:
|
||||
# Je na disku, není v DB
|
||||
missing_in_db += 1
|
||||
f_missing.write(f"{path}\n")
|
||||
|
||||
pbar.update(1)
|
||||
|
||||
f_missing.close()
|
||||
|
||||
# Zbytek jsou duchové
|
||||
ghost_files = list(ghost_check_set)
|
||||
|
||||
with open("report_NAVIC_V_DB.txt", "w", encoding="utf-8") as f_ghost:
|
||||
f_ghost.write("TOTO JSOU ZÁZNAMY V DB, KTERÉ NA DISKU NEEXISTUJÍ:\n")
|
||||
for p in ghost_files:
|
||||
f_ghost.write(f"{p}\n")
|
||||
|
||||
print("\n\n📊 VÝSLEDEK AUDITU")
|
||||
print("=====================================================")
|
||||
print(f"💾 Fyzických souborů: {physical_count:,}")
|
||||
print(f"✅ V pořádku (V DB): {found_ok:,}")
|
||||
print("-----------------------------------------------------")
|
||||
print(f"❌ CHYBÍ V DB (Re-index): {missing_in_db:,}")
|
||||
print(f" -> 'report_CHYBI_V_DB.txt'")
|
||||
print("-----------------------------------------------------")
|
||||
print(f"👻 NAVÍC V DB (Smazat): {len(ghost_files):,}")
|
||||
print(f" -> 'report_NAVIC_V_DB.txt'")
|
||||
print("=====================================================")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
/mnt/user/#ColdData/physical_files_list.txt
|
||||
@@ -0,0 +1,4 @@
|
||||
TOTO JSOU ZÁZNAMY V DB, KTERÉ NA DISKU NEEXISTUJÍ:
|
||||
/mnt/user/#ColdData/#40 SP1 SBS2003 ENG SP1/#2 WSS2003SP1-kb841876-fullfile-ENU_2.exe
|
||||
/mnt/user/#ColdData/#40 SP1 SBS2003 ENG SP1/tcmd852ax32.exe
|
||||
/mnt/user/#ColdData/#40 SP1 SBS2003 ENG SP1/#2 WSS2003SP1-kb841876-fullfile-ENU.exe
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,152 @@
|
||||
import os
|
||||
import pymysql
|
||||
import sys
|
||||
|
||||
# ==============================
|
||||
# ⚙️ NASTAVENÍ
|
||||
# ==============================
|
||||
LOCAL_ROOT = r"U:\#Syncthing"
|
||||
|
||||
# Cíl, kam to chceš nahrát (pro kontrolu přesné shody)
|
||||
TARGET_SUBFOLDER = "Porno"
|
||||
REMOTE_SHARE_KEY = "#ColdData"
|
||||
|
||||
# DB Nastavení
|
||||
DB_HOST = "192.168.1.76"
|
||||
DB_PORT = 3307
|
||||
DB_USER = "root"
|
||||
DB_PASS = "Vlado9674+"
|
||||
DB_NAME = "torrents"
|
||||
DB_TABLE = "file_md5_index"
|
||||
|
||||
|
||||
# ==============================
|
||||
|
||||
def get_db_connection():
|
||||
try:
|
||||
return pymysql.connect(
|
||||
host=DB_HOST, port=DB_PORT, user=DB_USER, password=DB_PASS,
|
||||
database=DB_NAME, cursorclass=pymysql.cursors.DictCursor
|
||||
)
|
||||
except Exception as e:
|
||||
sys.exit(f"❌ Chyba DB: {e}")
|
||||
|
||||
|
||||
def normalize_path(path):
|
||||
return path.replace("\\", "/").lower()
|
||||
|
||||
|
||||
def main():
|
||||
print(f"🚀 KŘÍŽOVÁ KONTROLA (Co nahrát vs. Co už někde je)")
|
||||
print("=====================================================")
|
||||
|
||||
# 1. NAČÍST CELOU DB (pro hledání zatoulaných souborů)
|
||||
print("📡 Stahuji kompletní index serveru (Jméno + Velikost)...")
|
||||
|
||||
# Mapa: (jmeno, velikost) -> [seznam cest]
|
||||
global_map = {}
|
||||
|
||||
conn = get_db_connection()
|
||||
with conn.cursor() as cursor:
|
||||
# Stáhneme vše z #ColdData
|
||||
sql = f"SELECT file_name, file_size, full_path FROM {DB_TABLE} WHERE full_path LIKE %s"
|
||||
cursor.execute(sql, (f"%/{REMOTE_SHARE_KEY}/%",))
|
||||
rows = cursor.fetchall()
|
||||
|
||||
for row in rows:
|
||||
fname = row['file_name'].lower()
|
||||
size = row['file_size']
|
||||
path = row['full_path']
|
||||
|
||||
key = (fname, size)
|
||||
if key not in global_map:
|
||||
global_map[key] = []
|
||||
global_map[key].append(path)
|
||||
|
||||
print(f"✅ Index načten ({len(rows):,} souborů).")
|
||||
|
||||
# 2. DEFINICE CÍLOVÉ CESTY (pro kontrolu přesné shody)
|
||||
# Cesta v DB vypadá např: /mnt/disk1/#ColdData/Porno/Slozka/Film.avi
|
||||
# My budeme kontrolovat, jestli soubor už je v "cílové logické cestě"
|
||||
target_pattern_part = f"/{REMOTE_SHARE_KEY}/{TARGET_SUBFOLDER}/".lower()
|
||||
|
||||
# 3. SKENOVÁNÍ LOKÁLNÍHO DISKU
|
||||
print(f"📂 Skenuji {LOCAL_ROOT} a třídím...")
|
||||
|
||||
count_upload = 0
|
||||
count_move = 0
|
||||
count_ok = 0
|
||||
|
||||
f_upload = open("1_NAHRAT_NA_SERVER.txt", "w", encoding="utf-8")
|
||||
f_move = open("2_JIZ_EXISTUJI_JINDE.txt", "w", encoding="utf-8")
|
||||
|
||||
f_upload.write("TOTO JSOU DATA, KTERÁ NA SERVERU VŮBEC NEJSOU:\n")
|
||||
f_move.write("TOTO NAHRAJEŠ ZBYTEČNĚ (Už to tam je, ale v jiné složce):\n")
|
||||
|
||||
for root, dirs, files in os.walk(LOCAL_ROOT):
|
||||
if ".stfolder" in dirs: dirs.remove(".stfolder")
|
||||
if ".stversions" in dirs: dirs.remove(".stversions")
|
||||
|
||||
for filename in files:
|
||||
fname_lower = filename.lower()
|
||||
local_full_path = os.path.join(root, filename)
|
||||
|
||||
try:
|
||||
local_size = os.path.getsize(local_full_path)
|
||||
# Relativní cesta, kam bys to nahrál (např. Herci/Film.avi)
|
||||
rel_path = os.path.relpath(local_full_path, LOCAL_ROOT)
|
||||
clean_rel_path = normalize_path(rel_path)
|
||||
except OSError:
|
||||
continue
|
||||
|
||||
key = (fname_lower, local_size)
|
||||
|
||||
# --- LOGIKA ROZHODOVÁNÍ ---
|
||||
|
||||
if key in global_map:
|
||||
# Soubor (stejné jméno a velikost) na serveru EXISTUJE.
|
||||
# Otázka je: Je ve správné složce?
|
||||
|
||||
server_paths = global_map[key]
|
||||
is_in_target = False
|
||||
|
||||
# Zkontrolujeme, zda alespoň jedna cesta na serveru odpovídá té, kam to chceš nahrát
|
||||
# Tedy jestli obsahuje /#ColdData/Porno/ + tvoji relativní cestu
|
||||
|
||||
# Hledaný koncový řetězec: "porno/herci/film.avi"
|
||||
target_suffix = f"{TARGET_SUBFOLDER}/{clean_rel_path}".lower()
|
||||
|
||||
for path in server_paths:
|
||||
if path.lower().endswith(target_suffix):
|
||||
is_in_target = True
|
||||
break
|
||||
|
||||
if is_in_target:
|
||||
# Je to tam, kde to má být.
|
||||
count_ok += 1
|
||||
else:
|
||||
# Je to na serveru, ale JINDE.
|
||||
count_move += 1
|
||||
f_move.write(f"SOUBOR: {clean_rel_path}\n")
|
||||
f_move.write(f" >>> Nalezen jinde: {server_paths[0]}\n")
|
||||
else:
|
||||
# Na serveru není vůbec (ani jinde).
|
||||
count_upload += 1
|
||||
f_upload.write(f"{local_full_path}\n")
|
||||
|
||||
if (count_upload + count_move + count_ok) % 1000 == 0:
|
||||
print(f" ... zpracováno {count_upload + count_move + count_ok} ...", end="\r")
|
||||
|
||||
f_upload.close()
|
||||
f_move.close()
|
||||
|
||||
print("\n\n📊 FINÁLNÍ REPORT")
|
||||
print("=====================================================")
|
||||
print(f"✅ UŽ TAM JE (Správně): {count_ok:,}")
|
||||
print(f"📦 UŽ TAM JE (Ale jinde): {count_move:,} -> Koukni do 2_JIZ_EXISTUJI_JINDE.txt")
|
||||
print(f"🚀 MUSÍŠ NAHRÁT (Nové): {count_upload:,} -> Seznam v 1_NAHRAT_NA_SERVER.txt")
|
||||
print("=====================================================")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,153 @@
|
||||
import os
|
||||
import pymysql
|
||||
import sys
|
||||
|
||||
# ==============================
|
||||
# ⚙️ NASTAVENÍ
|
||||
# ==============================
|
||||
LOCAL_ROOT = r"U:\#Syncthing"
|
||||
|
||||
# DB Připojení
|
||||
DB_HOST = "192.168.1.76"
|
||||
DB_PORT = 3307 # Port pro tvou DB
|
||||
DB_USER = "root"
|
||||
DB_PASS = "Vlado9674+" # Tvé heslo
|
||||
DB_NAME = "torrents"
|
||||
DB_TABLE = "file_md5_index"
|
||||
REMOTE_SHARE_KEY = "#ColdData"
|
||||
|
||||
|
||||
# ==============================
|
||||
|
||||
def get_db_connection():
|
||||
try:
|
||||
return pymysql.connect(
|
||||
host=DB_HOST,
|
||||
port=DB_PORT,
|
||||
user=DB_USER,
|
||||
password=DB_PASS,
|
||||
database=DB_NAME,
|
||||
cursorclass=pymysql.cursors.DictCursor
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"❌ CHYBA: Nelze se připojit k MySQL na {DB_HOST}:{DB_PORT}.")
|
||||
print(f" Detail: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def normalize_path(path):
|
||||
"""
|
||||
1. Převede Windows '\\' na Linux '/'
|
||||
2. Převede vše na malá písmena (aby 'Foto.jpg' == 'foto.JPG')
|
||||
"""
|
||||
return path.replace("\\", "/").lower()
|
||||
|
||||
|
||||
def get_local_files(root_path):
|
||||
print(f"📂 Skenuji lokální disk: {root_path}")
|
||||
local_files = set()
|
||||
|
||||
if not os.path.exists(root_path):
|
||||
print(f"❌ Chyba: Cesta {root_path} neexistuje.")
|
||||
return local_files
|
||||
|
||||
count = 0
|
||||
for root, dirs, files in os.walk(root_path):
|
||||
# Ignorovat systémové složky Syncthingu
|
||||
if ".stfolder" in dirs: dirs.remove(".stfolder")
|
||||
if ".stversions" in dirs: dirs.remove(".stversions")
|
||||
|
||||
for file in files:
|
||||
full_path = os.path.join(root, file)
|
||||
|
||||
try:
|
||||
rel_path = os.path.relpath(full_path, root_path)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
# NORMALIZACE (Lomítka + Malá písmena)
|
||||
clean_path = normalize_path(rel_path)
|
||||
|
||||
local_files.add(clean_path)
|
||||
count += 1
|
||||
|
||||
if count % 1000 == 0:
|
||||
print(f" ... načteno {count} lokálních souborů ...", end="\r")
|
||||
|
||||
print(f"✅ Lokálně nalezeno: {len(local_files):,} souborů.")
|
||||
return local_files
|
||||
|
||||
|
||||
def get_remote_files_from_db():
|
||||
print(f"📡 Připojuji se k DB '{DB_NAME}' a stahuji seznam...")
|
||||
conn = get_db_connection()
|
||||
remote_files = set()
|
||||
|
||||
try:
|
||||
with conn.cursor() as cursor:
|
||||
sql = f"SELECT full_path FROM {DB_TABLE} WHERE full_path LIKE %s"
|
||||
cursor.execute(sql, (f"%/{REMOTE_SHARE_KEY}/%",))
|
||||
|
||||
rows = cursor.fetchall()
|
||||
print(f" ... DB vrátila {len(rows):,} řádků. Zpracovávám...")
|
||||
|
||||
for row in rows:
|
||||
full_path_db = row['full_path']
|
||||
|
||||
if f"/{REMOTE_SHARE_KEY}/" in full_path_db:
|
||||
parts = full_path_db.split(f"/{REMOTE_SHARE_KEY}/")
|
||||
if len(parts) > 1:
|
||||
rel_path = parts[1]
|
||||
|
||||
# NORMALIZACE (Lomítka + Malá písmena)
|
||||
clean_path = normalize_path(rel_path)
|
||||
remote_files.add(clean_path)
|
||||
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
print(f"✅ V DB nalezeno: {len(remote_files):,} unikátních cest.")
|
||||
return remote_files
|
||||
|
||||
|
||||
def main():
|
||||
print("🚀 HLEDÁNÍ DUPLIKÁTŮ (Case Insensitive)")
|
||||
print("=======================================")
|
||||
|
||||
local_set = get_local_files(LOCAL_ROOT)
|
||||
remote_set = get_remote_files_from_db()
|
||||
|
||||
print("\n🔍 Porovnávám (všechna písmena ignorují velikost)...")
|
||||
|
||||
# Průnik
|
||||
duplicates = local_set.intersection(remote_set)
|
||||
only_local = local_set - remote_set
|
||||
|
||||
print("\n📊 VÝSLEDKY")
|
||||
print("=======================================")
|
||||
print(f"🏠 Celkem lokálně: {len(local_set):,}")
|
||||
print(f"☁️ Celkem v DB: {len(remote_set):,}")
|
||||
print("---------------------------------------")
|
||||
print(f"👯 SHODA (DUPLIKÁTY): {len(duplicates):,}")
|
||||
print(f"🆕 UNIKÁTNÍ (JEN DOMA): {len(only_local):,}")
|
||||
print("=======================================\n")
|
||||
|
||||
if duplicates:
|
||||
file_dup = "report_DUPLIKATY.txt"
|
||||
with open(file_dup, "w", encoding="utf-8") as f:
|
||||
for item in sorted(duplicates):
|
||||
f.write(f"{item}\n")
|
||||
print(f"💾 Seznam duplikátů (malá písmena) uložen do: {file_dup}")
|
||||
|
||||
if only_local:
|
||||
file_new = "report_NOVE_SOUBORY.txt"
|
||||
with open(file_new, "w", encoding="utf-8") as f:
|
||||
for item in sorted(only_local):
|
||||
f.write(f"{item}\n")
|
||||
print(f"💾 Seznam nových souborů uložen do: {file_new}")
|
||||
|
||||
input("\nStiskni ENTER pro ukončení...")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,52 @@
|
||||
záleský vojtěch - převzetí (2025)/01 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/02 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/03 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/04 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/05 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/06 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/07 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/08 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/09 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/10 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/11 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/12 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/13 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/14 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/15 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/16 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/17 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/18 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/19 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/20 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/21 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/22 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/23 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/24 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/25 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/26 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/27 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/28 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/29 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/30 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/31 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/32 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/33 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/34 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/35 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/36 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/37 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/38 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/39 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/40 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/41 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/42 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/43 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/44 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/45 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/46 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/47 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/48 - [vojtěch záleský] - převzetí.mp3
|
||||
záleský vojtěch - převzetí (2025)/audiokniha-prevzeti-vojtech-zalesky-original.jpg
|
||||
záleský vojtěch - převzetí (2025)/audiokniha-prevzeti-vojtech-zalesky.jpg
|
||||
záleský vojtěch - převzetí (2025)/folder.jpg
|
||||
záleský vojtěch - převzetí (2025)/info.txt
|
||||
+56068
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user