improve analyzer

This commit is contained in:
lelo 2026-01-26 17:10:03 +00:00
parent 6a27d47222
commit c855bfdbc1
4 changed files with 131 additions and 13 deletions

View File

@ -10,6 +10,32 @@ APP_CONFIG = auth.return_app_config()
BASE_DIR = os.path.dirname(os.path.abspath(__file__)) BASE_DIR = os.path.dirname(os.path.abspath(__file__))
SEARCH_DB_PATH = os.path.join(BASE_DIR, "search.db") 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. # 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 = sqlite3.connect(f"file:{SEARCH_DB_PATH}?mode=ro", uri=True, check_same_thread=False)
search_db.row_factory = sqlite3.Row search_db.row_factory = sqlite3.Row
@ -20,6 +46,32 @@ def _normalize_folder_path(folder_path: str) -> str:
return folder_path.replace("\\", "/").strip().strip("/") 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]: def _list_children(parent: str = "") -> List[str]:
""" """
Return the next folder level for the given parent. 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() cursor = search_db.cursor()
rows = cursor.execute(sql, params).fetchall() 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) total = sum(row["file_count"] for row in rows)
categories = [ categories = [
{"category": row["category_label"], "count": row["file_count"]} {"category": row["category_label"], "count": row["file_count"]}
for row in rows 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(): def search_db_analyzer():

View File

@ -20,6 +20,7 @@
const feedback = document.getElementById('analyzer-feedback'); const feedback = document.getElementById('analyzer-feedback');
const resultWrapper = document.getElementById('analyzer-result'); const resultWrapper = document.getElementById('analyzer-result');
const resultBody = document.getElementById('result-body'); const resultBody = document.getElementById('result-body');
const filetypeBody = document.getElementById('filetype-body');
const totalCount = document.getElementById('totalCount'); const totalCount = document.getElementById('totalCount');
const levelsContainer = document.getElementById('folder-levels'); const levelsContainer = document.getElementById('folder-levels');
@ -198,6 +199,9 @@
} }
resultBody.innerHTML = ''; resultBody.innerHTML = '';
if (filetypeBody) {
filetypeBody.innerHTML = '';
}
if (data.categories && data.categories.length) { if (data.categories && data.categories.length) {
data.categories.forEach((row) => { data.categories.forEach((row) => {
const tr = document.createElement('tr'); const tr = document.createElement('tr');
@ -219,6 +223,29 @@
resultBody.appendChild(tr); 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`; totalCount.textContent = `${data.total || 0} Dateien`;
resultWrapper.style.display = 'block'; resultWrapper.style.display = 'block';
} catch (error) { } catch (error) {

View File

@ -52,7 +52,7 @@
<li><a class="dropdown-item" href="{{ url_for('connections') }}" data-ajax-nav>Verbindungen</a></li> <li><a class="dropdown-item" href="{{ url_for('connections') }}" data-ajax-nav>Verbindungen</a></li>
<li><a class="dropdown-item" href="{{ url_for('file_access') }}" data-ajax-nav>Dateizugriffe</a></li> <li><a class="dropdown-item" href="{{ url_for('file_access') }}" data-ajax-nav>Dateizugriffe</a></li>
<li><a class="dropdown-item" href="{{ url_for('songs_dashboard') }}" data-ajax-nav>Wiederholungen</a></li> <li><a class="dropdown-item" href="{{ url_for('songs_dashboard') }}" data-ajax-nav>Wiederholungen</a></li>
<li><a class="dropdown-item" href="{{ url_for('search_db_analyzer') }}" data-ajax-nav>Dateiindex Analyse</a></li> <li><a class="dropdown-item" href="{{ url_for('search_db_analyzer') }}" data-ajax-nav>Ordner auswerten</a></li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -5,7 +5,7 @@
{% block content %} {% block content %}
<div class="container"> <div class="container">
<h2>Index auswerten</h2> <h2>Ordner auswerten</h2>
<p class="text-muted"> <p class="text-muted">
Ordner auswählen und die Anzahl der Dateien pro Kategorie aus der Dateiindex abrufen. Ordner auswählen und die Anzahl der Dateien pro Kategorie aus der Dateiindex abrufen.
</p> </p>
@ -28,6 +28,8 @@
<h4 class="mb-0">Ergebnis</h4> <h4 class="mb-0">Ergebnis</h4>
<span class="badge bg-secondary" id="totalCount"></span> <span class="badge bg-secondary" id="totalCount"></span>
</div> </div>
<div class="row g-3">
<div class="col-lg-7">
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped align-middle"> <table class="table table-striped align-middle">
<thead> <thead>
@ -40,6 +42,21 @@
</table> </table>
</div> </div>
</div> </div>
<div class="col-lg-5">
<div class="table-responsive">
<table class="table table-striped align-middle">
<thead>
<tr>
<th>Dateityp</th>
<th>Anzahl Dateien</th>
</tr>
</thead>
<tbody id="filetype-body"></tbody>
</table>
</div>
</div>
</div>
</div>
</div> </div>
<script type="application/json" id="search-db-analyzer-data"> <script type="application/json" id="search-db-analyzer-data">
{{ { {{ {