165 lines
5.9 KiB
Python
165 lines
5.9 KiB
Python
#!/usr/bin/env python3
|
||
import os
|
||
import sys
|
||
import json
|
||
from pathlib import Path
|
||
|
||
# Gibt das Verzeichnis zurück, in dem dieses Skript liegt
|
||
def script_dir():
|
||
return Path(__file__).parent.resolve()
|
||
|
||
# Lädt die JSONC-Konfiguration (Kommentare via // werden ignoriert)
|
||
def load_config(filename="config.jsonc"):
|
||
config_path = script_dir() / filename
|
||
if not config_path.exists():
|
||
print(" ⚠ Konfigurationsdatei nicht gefunden:", config_path)
|
||
sys.exit(1)
|
||
with open(config_path, encoding="utf-8") as f:
|
||
# Entfernt //-Kommentare vor dem Parsen
|
||
return json.loads("".join(line for line in f if not line.strip().startswith("//")))
|
||
|
||
# Parsen der Kommandozeilenargumente
|
||
def parse_args():
|
||
args = sys.argv[1:]
|
||
parsed = {
|
||
"scan": None, # Verzeichnisse zum Scannen (List[str])
|
||
"ignore": [], # Verzeichnisse, die ignoriert werden sollen (List[str])
|
||
"reset": False, # Setzt das Logfile zurück
|
||
"hilfe": False, # Zeigt die Hilfe an
|
||
}
|
||
|
||
# Argumente durchgehen und zuweisen
|
||
while args:
|
||
arg = args.pop(0)
|
||
if arg in ("-h", "--hilfe"):
|
||
parsed["hilfe"] = True
|
||
elif arg == "--reset":
|
||
parsed["reset"] = True
|
||
elif arg in ("-s", "--scan") and args:
|
||
parsed["scan"] = [entry.strip() for entry in args.pop(0).split(",") if entry.strip()]
|
||
elif arg in ("-x", "--ignore") and args:
|
||
parsed["ignore"] = [entry.strip() for entry in args.pop(0).split(",") if entry.strip()]
|
||
else:
|
||
print(f" ⚠ Unbekannter Parameter: {arg}")
|
||
parsed["hilfe"] = True
|
||
break
|
||
|
||
return parsed
|
||
|
||
# Ausgabe der Hilfetexte für CLI-Nutzer
|
||
def show_help():
|
||
print("""
|
||
(C) 2025 - Adam Skotarczak (ionivation.com)
|
||
|
||
🛈 Tool das Verzeichnisse nach Markdown-Dateien durchsucht und diese an eine definierte Liste anhängt als Markdown-Link.
|
||
Verwendung: python3 link_collector.py [OPTIONEN]
|
||
|
||
-s, --scan Kommagetrennte Liste von Verzeichnissen zum Durchsuchen (relativ zum Aufrufpfad)
|
||
-x, --ignore Kommagetrennte Liste von Verzeichnissen, die ignoriert werden sollen
|
||
--reset Löscht das Logfile 'processed.log' und beendet sich
|
||
-h, --hilfe Zeigt diese Hilfe
|
||
|
||
Beispiel:
|
||
python3 link_collector.py -s docs,notes -x "docs/alt"
|
||
""")
|
||
|
||
# Generator: Findet Markdown-Dateien (rekursiv), ignoriert dabei definierte Pfade
|
||
def find_md_files(root_dirs, ignore_dirs, extensions):
|
||
for root in root_dirs:
|
||
for dirpath, _, filenames in os.walk(root):
|
||
# Ignorierpfade überspringen
|
||
if any(str(Path(dirpath)).startswith(str(ignored)) for ignored in ignore_dirs):
|
||
continue
|
||
for fname in filenames:
|
||
if any(fname.endswith(ext) for ext in extensions):
|
||
yield Path(dirpath) / fname
|
||
|
||
# Extrahiert den ersten Markdown-Titel (# ...) als Linktext
|
||
def extract_title(filepath):
|
||
try:
|
||
with open(filepath, encoding="utf-8") as f:
|
||
for line in f:
|
||
if line.strip().startswith("#"):
|
||
return line.strip("# ").strip()
|
||
except Exception as e:
|
||
print(f"⚠ Fehler beim Lesen von {filepath}: {e}")
|
||
# Fallback: Dateiname ohne Endung
|
||
return filepath.stem
|
||
|
||
# Liest bereits verarbeitete Dateien aus dem Log (Pfadangaben im POSIX-Format)
|
||
def load_processed(logfile):
|
||
if not logfile.exists():
|
||
return set()
|
||
with open(logfile, encoding="utf-8") as f:
|
||
# Vereinheitlichung auf POSIX-Konvention
|
||
return set(Path(line.strip()).as_posix() for line in f)
|
||
|
||
# Hängt neue Markdown-Links ans Output-Dokument an
|
||
def append_to_output(output_path, links):
|
||
with open(output_path, "a", encoding="utf-8") as f:
|
||
for line in links:
|
||
f.write(line + "\n")
|
||
|
||
# Ergänzt das Logfile um neu verarbeitete Dateien (POSIX-Format)
|
||
def update_processed(log_path, new_paths):
|
||
with open(log_path, "a", encoding="utf-8") as f:
|
||
for path in new_paths:
|
||
f.write(path.as_posix() + "\n")
|
||
|
||
# Hauptfunktion mit gesamtem Ablauf
|
||
def main():
|
||
config = load_config()
|
||
args = parse_args()
|
||
|
||
if args["hilfe"]:
|
||
show_help()
|
||
return
|
||
|
||
log_path = script_dir() / config.get("processed_log", "processed.log")
|
||
|
||
# Optionaler Reset des Logs
|
||
if args["reset"]:
|
||
if log_path.exists():
|
||
log_path.unlink()
|
||
print("🧹 Logfile gelöscht:", log_path)
|
||
else:
|
||
print("ℹ Logfile existierte nicht:", log_path)
|
||
return
|
||
|
||
cwd = Path.cwd()
|
||
output_file = cwd / config.get("output_file", "output.md")
|
||
|
||
# Verzeichnisse aus CLI oder Konfiguration
|
||
root_dirs = args["scan"] or config.get("root_dirs", [])
|
||
root_dirs = [Path(d).resolve() for d in root_dirs if d.strip()]
|
||
ignore_dirs = [Path(x).resolve() for x in args["ignore"]]
|
||
|
||
extensions = config.get("extensions", [".md"])
|
||
processed = load_processed(log_path)
|
||
|
||
new_links = []
|
||
new_processed = []
|
||
|
||
# Dateien durchsuchen und neue Markdown-Dateien verlinken
|
||
for md_file in find_md_files(root_dirs, ignore_dirs, extensions):
|
||
if md_file.resolve() == output_file.resolve():
|
||
continue # nicht sich selbst verlinken
|
||
rel_path = md_file.relative_to(cwd)
|
||
rel_path_posix = rel_path.as_posix()
|
||
if rel_path_posix in processed:
|
||
continue # bereits im Logfile
|
||
title = extract_title(md_file)
|
||
new_links.append(f"- [{title}]({rel_path_posix})")
|
||
new_processed.append(rel_path)
|
||
|
||
# Ergebnisse schreiben
|
||
if new_links:
|
||
append_to_output(output_file, new_links)
|
||
update_processed(log_path, new_processed)
|
||
print(f"✔ {len(new_links)} neue Links hinzugefügt.")
|
||
else:
|
||
print(" ℹ Keine neuen Dateien gefunden.")
|
||
|
||
if __name__ == "__main__":
|
||
main()
|