-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathabletonxmpfile.py
136 lines (119 loc) · 4.91 KB
/
abletonxmpfile.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
from lxml import etree
from datetime import datetime
from xml.sax.saxutils import quoteattr, escape
import os
class AbletonXMPFile:
def __init__(self, file_path=None):
self.file_path = file_path
self.is_changed = False
self.nsmap = {
"ablFR": "https://ns.ableton.com/xmp/fs-resources/1.0/",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
}
self.parser = etree.XMLParser(remove_blank_text=True)
try:
with open(file_path, "r", encoding="utf-8") as file:
self.root = etree.parse(file, self.parser).getroot()
except FileNotFoundError:
current_datetime = datetime.now().strftime("%Y-%m-%dT%H:%M:%S%z")
template = f"""
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.6.0">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:ablFR="https://ns.ableton.com/xmp/fs-resources/1.0/"
xmlns:xmp="http://ns.adobe.com/xap/1.0/">
<dc:format>application/vnd.ableton.folder</dc:format>
<ablFR:resource>folder</ablFR:resource>
<ablFR:platform>mac</ablFR:platform>
<ablFR:items>
<rdf:Bag>
</rdf:Bag>
</ablFR:items>
<xmp:CreatorTool>Updated by Ableton Index 12.0</xmp:CreatorTool>
<xmp:CreateDate>{current_datetime}</xmp:CreateDate>
<xmp:MetadataDate>{current_datetime}</xmp:MetadataDate>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
"""
self.root = etree.XML(template, self.parser)
def add_tag(self, file_path, keyword):
added = False
item_template = f"""
<rdf:li rdf:parseType="Resource"
xmlns:ablFR="https://ns.ableton.com/xmp/fs-resources/1.0/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<ablFR:filePath>{escape(file_path)}</ablFR:filePath>
<ablFR:keywords>
<rdf:Bag>
</rdf:Bag>
</ablFR:keywords>
</rdf:li>
"""
items = self.root.xpath(
f"//ablFR:items/rdf:Bag/rdf:li",
namespaces=self.nsmap,
)
# collect existing filepaths
existing_filepaths = [
item.xpath("ablFR:filePath", namespaces=self.nsmap)[0].text
for item in items
]
# check if the file path already exists
if file_path in existing_filepaths:
item = items[existing_filepaths.index(file_path)]
else:
item = etree.XML(item_template, self.parser)
self.root.xpath(
"//ablFR:items/rdf:Bag",
namespaces=self.nsmap,
)[
0
].append(item)
added = True
existing_keywords = item.xpath(
"ablFR:keywords/rdf:Bag/rdf:li",
namespaces=self.nsmap,
)
existing_keyword_values = [keyword.text for keyword in existing_keywords]
if keyword not in existing_keyword_values:
keywords_bag = item.xpath(
"ablFR:keywords/rdf:Bag",
namespaces=self.nsmap,
)[0]
new_keyword = etree.SubElement(
keywords_bag, "{http://www.w3.org/1999/02/22-rdf-syntax-ns#}li"
)
new_keyword.text = keyword
added = True
if added:
self.is_changed = True
return added
def dump(self):
metadata_date = self.root.xpath(
"//xmp:MetadataDate",
namespaces={
"xmp": "http://ns.adobe.com/xap/1.0/",
},
)
if metadata_date is not None and len(metadata_date) > 0:
metadata_date[0].text = datetime.now().strftime("%Y-%m-%dT%H:%M:%S%z")
else:
metadata_date = etree.Element("{http://ns.adobe.com/xap/1.0/}MetadataDate")
metadata_date.text = datetime.now().strftime("%Y-%m-%dT%H:%M:%S%z")
self.root.append(metadata_date)
etree.indent(self.root)
xml = etree.tostring(self.root, pretty_print=True).decode()
return xml
def save_if_changed(self, file_path=None):
if not self.is_changed:
return
if not file_path:
file_path = self.file_path
xml = self.dump()
folder_path = os.path.dirname(file_path)
os.makedirs(folder_path, exist_ok=True) # Ensure the folder exists
with open(file_path, "w", encoding="utf-8") as file:
file.write(xml)
self.is_changed = False