diff --git a/search_db_analyzer.py b/search_db_analyzer.py
index 6d87e3f..a2fbd40 100644
--- a/search_db_analyzer.py
+++ b/search_db_analyzer.py
@@ -10,6 +10,32 @@ APP_CONFIG = auth.return_app_config()
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
SEARCH_DB_PATH = os.path.join(BASE_DIR, "search.db")
+# Filetype buckets (extension-based, lowercase, leading dot).
+FILETYPE_BUCKETS = [
+ ("Liedtexte", {
+ ".sng"
+ }),
+ ("Image", {
+ ".png", ".gif", ".bmp", ".webp", ".tiff", ".tif", ".svg", ".ico"
+ }),
+ ("Video", {
+ ".mp4", ".m4v", ".mov", ".avi", ".mkv", ".webm", ".mpg", ".mpeg", ".3gp", ".3g2", ".wmv"
+ }),
+ ("Photo", {
+ ".jpg", ".jpeg", ".heic", ".heif", ".dng", ".cr2", ".cr3", ".arw", ".nef", ".orf", ".rw2", ".raf"
+ }),
+ ("Document", {
+ ".pdf", ".doc", ".docx", ".ppt", ".pptx", ".txt", ".rtf", ".md", ".odt", ".pages", ".key", ".note"
+ }),
+ ("Table", {
+ ".xls", ".xlsx", ".csv", ".ods", ".tsv"
+ }),
+ ("Audio", {
+ ".mp3", ".wav", ".m4a", ".flac", ".ogg", ".aac", ".wma", ".aiff", ".alac"
+ }),
+]
+FILETYPE_ORDER = [label for label, _ in FILETYPE_BUCKETS] + ["Misc"]
+
# Open search.db in read-only mode to avoid accidental writes.
search_db = sqlite3.connect(f"file:{SEARCH_DB_PATH}?mode=ro", uri=True, check_same_thread=False)
search_db.row_factory = sqlite3.Row
@@ -20,6 +46,32 @@ def _normalize_folder_path(folder_path: str) -> str:
return folder_path.replace("\\", "/").strip().strip("/")
+def _bucket_for_filetype(filetype: str) -> str:
+ if not filetype:
+ return "Misc"
+ ft = str(filetype).strip().lower()
+ if not ft:
+ return "Misc"
+
+ if "/" in ft and not ft.startswith("."):
+ if ft.startswith("image/"):
+ return "Image"
+ if ft.startswith("video/"):
+ return "Video"
+ if ft.startswith("audio/"):
+ return "Audio"
+ if ft.startswith("text/") or ft in ("application/pdf",):
+ return "Document"
+
+ if not ft.startswith("."):
+ ft = f".{ft}"
+
+ for label, exts in FILETYPE_BUCKETS:
+ if ft in exts:
+ return label
+ return "Misc"
+
+
def _list_children(parent: str = "") -> List[str]:
"""
Return the next folder level for the given parent.
@@ -82,13 +134,35 @@ def _query_counts(folder_path: str) -> Dict[str, Any]:
cursor = search_db.cursor()
rows = cursor.execute(sql, params).fetchall()
+ type_rows = cursor.execute(
+ f"""
+ SELECT COALESCE(filetype, '') AS filetype_label,
+ COUNT(*) AS file_count
+ FROM files
+ WHERE {where_sql}
+ GROUP BY filetype_label
+ """,
+ params
+ ).fetchall()
+
total = sum(row["file_count"] for row in rows)
categories = [
{"category": row["category_label"], "count": row["file_count"]}
for row in rows
]
- return {"total": total, "categories": categories}
+ type_totals: Dict[str, int] = {}
+ for row in type_rows:
+ bucket = _bucket_for_filetype(row["filetype_label"])
+ type_totals[bucket] = type_totals.get(bucket, 0) + row["file_count"]
+
+ filetypes = [
+ {"type": label, "count": type_totals[label]}
+ for label in FILETYPE_ORDER
+ if type_totals.get(label)
+ ]
+
+ return {"total": total, "categories": categories, "filetypes": filetypes}
def search_db_analyzer():
diff --git a/static/search_db_analyzer.js b/static/search_db_analyzer.js
index 950c8ad..46d28ff 100644
--- a/static/search_db_analyzer.js
+++ b/static/search_db_analyzer.js
@@ -20,6 +20,7 @@
const feedback = document.getElementById('analyzer-feedback');
const resultWrapper = document.getElementById('analyzer-result');
const resultBody = document.getElementById('result-body');
+ const filetypeBody = document.getElementById('filetype-body');
const totalCount = document.getElementById('totalCount');
const levelsContainer = document.getElementById('folder-levels');
@@ -198,6 +199,9 @@
}
resultBody.innerHTML = '';
+ if (filetypeBody) {
+ filetypeBody.innerHTML = '';
+ }
if (data.categories && data.categories.length) {
data.categories.forEach((row) => {
const tr = document.createElement('tr');
@@ -219,6 +223,29 @@
resultBody.appendChild(tr);
}
+ if (filetypeBody) {
+ if (data.filetypes && data.filetypes.length) {
+ data.filetypes.forEach((row) => {
+ const tr = document.createElement('tr');
+ const type = document.createElement('td');
+ const cnt = document.createElement('td');
+
+ type.textContent = row.type || 'Misc';
+ cnt.textContent = row.count;
+
+ tr.append(type, cnt);
+ filetypeBody.appendChild(tr);
+ });
+ } else {
+ const tr = document.createElement('tr');
+ const td = document.createElement('td');
+ td.colSpan = 2;
+ td.textContent = 'Keine Datei-Typen gefunden.';
+ tr.appendChild(td);
+ filetypeBody.appendChild(tr);
+ }
+ }
+
totalCount.textContent = `${data.total || 0} Dateien`;
resultWrapper.style.display = 'block';
} catch (error) {
diff --git a/templates/base.html b/templates/base.html
index f972b62..e5c2cec 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -52,7 +52,7 @@
Verbindungen
Dateizugriffe
Wiederholungen
- Dateiindex Analyse
+ Ordner auswerten
diff --git a/templates/search_db_analyzer.html b/templates/search_db_analyzer.html
index 11587c1..7a49071 100644
--- a/templates/search_db_analyzer.html
+++ b/templates/search_db_analyzer.html
@@ -5,7 +5,7 @@
{% block content %}
-
Index auswerten
+
Ordner auswerten
Ordner auswählen und die Anzahl der Dateien pro Kategorie aus der Dateiindex abrufen.
@@ -28,16 +28,33 @@
Ergebnis
-
-
-
-
- | Kategorie |
- Anzahl Dateien |
-
-
-
-
+
+
+
+
+
+
+ | Kategorie |
+ Anzahl Dateien |
+
+
+
+
+
+
+
+
+
+
+
+ | Dateityp |
+ Anzahl Dateien |
+
+
+
+
+
+