Metadati: campo 'Origine (macOS · kMDItemWhereFroms)'

kMDItemWhereFroms non e un tag ID3/MP4/Vorbis: e' un extended
attribute filesystem di macOS che Safari/Mail/AirDrop e altri tool
impostano per ricordare l'origine di un file scaricato.

- core/metadata.py: _read_where_from / _write_where_from via subprocess
  xattr + plistlib (lettura/scrittura del plist binario embedded
  nell'attributo)
- read_metadata espone 'where_from' (lista URL) e 'is_macos'
- write_metadata applica l'xattr se data['where_from'] e fornito;
  lista vuota -> rimuove l'attributo
- webui: textarea 'Origine' visibile solo su macOS (is_macos=true),
  una URL per riga

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
luzadevandClaude Opus 4.7 committed 2026-06-08 10:36:18 +02:00
1 parent 61879c42d3
commit 1d7832e246
3 files changed
+79 -1

No files matched your search

+7
View File
@@ -305,6 +305,13 @@
<label class="field-label">Commento</label>
<textarea id="metaComment" class="input textarea" rows="3"></textarea>
</div>
<div class="field full" id="metaWhereFromField" hidden>
<label class="field-label">
Origine (macOS · kMDItemWhereFroms)
</label>
<textarea id="metaWhereFrom" class="input textarea" rows="2" placeholder="https://... (uno per riga)"></textarea>
<div class="cover-hint">Extended attribute macOS — una URL per riga. Vuoto = rimuove l'attributo.</div>
</div>
</div>
</div>
+13
View File
@@ -414,6 +414,15 @@ function populateMetaForm(d) {
$("#metaKey").value = d.key || "";
$("#metaComment").value = d.comment || "";
// WhereFroms (macOS only)
if (d.is_macos) {
$("#metaWhereFromField").hidden = false;
$("#metaWhereFrom").value = (d.where_from || []).join("\n");
} else {
$("#metaWhereFromField").hidden = true;
$("#metaWhereFrom").value = "";
}
// Info riga sotto il path
const info = $("#metaInfo");
info.hidden = false;
@@ -511,6 +520,10 @@ $("#metaSaveBtn").addEventListener("click", async () => {
bpm: $("#metaBpm").value,
key: $("#metaKey").value,
comment: $("#metaComment").value,
where_from: $("#metaWhereFrom").value
.split("\n")
.map(s => s.trim())
.filter(Boolean),
},
cover_path: state.meta.newCoverPath,
remove_cover: state.meta.removeCover,