Merge remote-tracking branch 'origin/master'
This commit is contained in:
17
.gitignore
vendored
17
.gitignore
vendored
@@ -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
1_NAHRAT_NA_SERVER.txt
Normal file
54807
1_NAHRAT_NA_SERVER.txt
Normal file
File diff suppressed because it is too large
Load Diff
2099
2_JIZ_EXISTUJI_JINDE.txt
Normal file
2099
2_JIZ_EXISTUJI_JINDE.txt
Normal file
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}")
|
||||
|
||||
74
AdobeFlatten/20 FlattenAdobe.py
Normal file
74
AdobeFlatten/20 FlattenAdobe.py
Normal file
@@ -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()
|
||||
145
MakeSmallerPDF/06 OCR Tesseract.py
Normal file
145
MakeSmallerPDF/06 OCR Tesseract.py
Normal file
@@ -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()
|
||||
205
MakeSmallerPDF/07 OCR tesseract lepší.py
Normal file
205
MakeSmallerPDF/07 OCR tesseract lepší.py
Normal file
@@ -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()
|
||||
66
MakeSmallerPDF/2 MakeSmaller.py
Normal file
66
MakeSmallerPDF/2 MakeSmaller.py
Normal file
@@ -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)
|
||||
37
MakeSmallerPDF/3 MakeSmaller.py
Normal file
37
MakeSmallerPDF/3 MakeSmaller.py
Normal file
@@ -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)
|
||||
87
MakeSmallerPDF/4 MakeSmaller.py
Normal file
87
MakeSmallerPDF/4 MakeSmaller.py
Normal file
@@ -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()
|
||||
95
MakeSmallerPDF/5 MakeSmaller.py
Normal file
95
MakeSmallerPDF/5 MakeSmaller.py
Normal file
@@ -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()
|
||||
67
MakeSmallerPDF/99 TestOCR.py
Normal file
67
MakeSmallerPDF/99 TestOCR.py
Normal file
@@ -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()
|
||||
67
MakeSmallerPDF/991 test oCR.py
Normal file
67
MakeSmallerPDF/991 test oCR.py
Normal file
@@ -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
SEZNAM_K_NAHRANI.txt
Normal file
55855
SEZNAM_K_NAHRANI.txt
Normal file
File diff suppressed because it is too large
Load Diff
136
Syncthing/01 LocalCleanerBasedOnRemoteHash.py
Normal file
136
Syncthing/01 LocalCleanerBasedOnRemoteHash.py
Normal file
@@ -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()
|
||||
134
Syncthing/02 SyncthingBlakeDoSouboru.py
Normal file
134
Syncthing/02 SyncthingBlakeDoSouboru.py
Normal file
@@ -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()
|
||||
150
Syncthing/03 Terminator.py
Normal file
150
Syncthing/03 Terminator.py
Normal file
@@ -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()
|
||||
128
Syncthing/04 CheckagainstTreesize.py
Normal file
128
Syncthing/04 CheckagainstTreesize.py
Normal file
@@ -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()
|
||||
1251
Syncthing/deleted_files.log
Normal file
1251
Syncthing/deleted_files.log
Normal file
File diff suppressed because it is too large
Load Diff
56120
Syncthing/local_hashes.jsonl
Normal file
56120
Syncthing/local_hashes.jsonl
Normal file
File diff suppressed because it is too large
Load Diff
1949854
Syncthing/physical_files_list.txt
Normal file
1949854
Syncthing/physical_files_list.txt
Normal file
File diff suppressed because it is too large
Load Diff
1
Syncthing/report_CHYBI_V_DB.txt
Normal file
1
Syncthing/report_CHYBI_V_DB.txt
Normal file
@@ -0,0 +1 @@
|
||||
/mnt/user/#ColdData/physical_files_list.txt
|
||||
4
Syncthing/report_NAVIC_V_DB.txt
Normal file
4
Syncthing/report_NAVIC_V_DB.txt
Normal file
@@ -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
|
||||
1251
Syncthing/would_delete.log
Normal file
1251
Syncthing/would_delete.log
Normal file
File diff suppressed because it is too large
Load Diff
152
TestDupliciSyncThing2.py
Normal file
152
TestDupliciSyncThing2.py
Normal file
@@ -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()
|
||||
153
TestduplicitSyncThing.py
Normal file
153
TestduplicitSyncThing.py
Normal file
@@ -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()
|
||||
52
report_DUPLIKATY.txt
Normal file
52
report_DUPLIKATY.txt
Normal file
@@ -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
report_NOVE_SOUBORY.txt
Normal file
56068
report_NOVE_SOUBORY.txt
Normal file
File diff suppressed because it is too large
Load Diff
32214
report_ROZDILNE_CESTY.txt
Normal file
32214
report_ROZDILNE_CESTY.txt
Normal file
File diff suppressed because it is too large
Load Diff
5258
report_SKUTECNE_DUPLIKATY.txt
Normal file
5258
report_SKUTECNE_DUPLIKATY.txt
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user