@ahojnnes: Der Vorteil wäre für mich, dass es diese hässliche Parserei von dem XML nur einmal gäbe und man sich danach komplett in "Python-Land" befindet. Dieses Umwandeln in Listen und mittels Index auf das nächste Element nach dem was eine bestimmte Bedingung erfüllt, finde ich jedenfalls ziemlich gruselig. Das sind ja, wenn ich das richtig interpretiere Dictionaries mit `key`-Tags und `value`-Tags (die Du nicht als solche sondern eben über den hässlichen Index-Offset ansprichst). Widerverwendbarkeit ist IMHO schon gegeben, denn das Parsen der verschiedenen Teilbäume kann ja die gleiche Funktion erledigen. Wenn es so ein `parse_node()` gäbe was einen übergebenen XML-Knoten in eine Python-Datenstruktur übrtführt, dann könnte die `parse_playlist()` zum Beispiel so aussehen (ungetestet):
Code: Alles auswählen
def parse_playlist(self):
for playlists in imap(parse_node, self.etree.findall('dict/array/dict')):
for track in playlists.get(self.playlist, []):
if 'Track ID' in track:
self.playlist_tracks.append(track['Track ID'])
IMHO sieht die so viel aufgeräumter und einfacher lesbar aus. Und wenn man `plistlib` benutzt, braucht man ja noch nicht mal ein `parse_node()` selber schreiben.
Drei ``for``-Schleifen gehen ja noch, aber in der `sync_dir()` muss man in der tiefsten Ebene ganze sieben Entscheidungen im Kopf haben wenn man wissen will, unter welchen Umständen man zu dem jeweiligen Punkt im Programmablauf kommt. Ist für mein armes Hirn jedenfalls etwas viel.
Die `sync_dir()` würde ich zuerst einmal in zwei Methoden aufteilen. Und die Verschachtelung im zweiten Teil könnte man durch Rekursion wegbekommen, denke ich. Ungetestet:
Code: Alles auswählen
def _create_and_copy(self):
artist_paths = set()
album_paths = set()
track_paths = set()
for track in self.playlist_tracks:
#: save and normalize (unicode comparison) paths to delete
#: needless files afterwards
artist_path = os.path.join(self.directory, track.artist)
artist_path = ud.normalize('NFC', artist_path)
artist_paths.add(artist_path)
album_path = os.path.join(artist_path, track.album)
album_path = ud.normalize('NFC', album_path)
album_paths.add(album_path)
track_path = os.path.join(album_path, track.filename)
track_path = ud.normalize('NFC', track_path)
track_paths.add(track_path)
for path in [artist_path, album_path]:
try:
os.mkdir(path)
except OSError:
pass # Intentionally ignored.
if (
os.path.exists(album_path)
and os.path.exists(track.path)
and not os.path.exists(track_path)
):
self.verbose_print('--> Copying %s/%s/%s' % (track.artist,
track.album,
track.filename))
shutil.copy(track.path, track_path)
return artist_paths, album_paths, track_paths
def _delete(self, base_path, paths_seq, sub_paths=()):
if not paths_seq:
return
paths = paths_seq[0]
for path in os.listdir(base_path):
full_path = os.path.join(basepath, path)
if ud.normalize('NFC', full_path) not in paths:
try:
if os.path.isdir(full_path):
shutil.rmtree(full_path)
else:
os.remove(full_path)
except OSError:
pass # Intentionally ignored.
else:
self.verbose_print(
'--> Deleted %s' % '/'.join(sub_paths + (path,))
)
else:
self._delete(full_path, paths_seq[1:], sub_paths + (path,))
def sync_dir(self):
self._delete(unicode(self.directory, 'UTF-8'), self._create_and_copy())
Ich habe da nicht tiefer drüber nachgedacht, aber sind wirklich drei verschiedene `set`\s für die Pfade nötig? Überschneidungen der Werte kann es ja eigentlich nicht geben, denn jede Ebene hat garantiert andere Pfade als die jeweils anderen Ebenen, oder!?