Mercurial > repos > other > import-rating-rhythmbox
comparison import-rating-rhythmbox.py @ 0:4fab77a9ac9d default tip
Initial commit of working script
author | IBBoard <dev@ibboard.co.uk> |
---|---|
date | Sat, 11 Nov 2017 16:27:22 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4fab77a9ac9d |
---|---|
1 #! /usr/bin/env python3 | |
2 | |
3 from xml.dom.minidom import parse | |
4 import sys | |
5 from os import path | |
6 from os.path import expanduser | |
7 from urllib.parse import urlparse, unquote | |
8 import mutagen | |
9 | |
10 | |
11 ### Usage: python3 ./quodlibet-to-rhythmbox.py > output.xml; \ | |
12 ### mv output.xml ~/.local/share/rhythmbox/rhythmdb.xml | |
13 | |
14 | |
15 # List of sub-keys that we will take POPM ratings from | |
16 # The order they are here is their priority - we take the first one we find | |
17 TRANSFER_KEYS = [ | |
18 'quodlibet@lists.sacredchao.net', | |
19 'Banshee' | |
20 ] | |
21 | |
22 DEBUG_LEVEL = 0 | |
23 | |
24 | |
25 def _debug(message, level=1): | |
26 if level <= DEBUG_LEVEL: | |
27 print(message, file=sys.stderr) | |
28 | |
29 | |
30 def _get_text(nodelist): | |
31 content = [] | |
32 for node in nodelist: | |
33 if node.nodeType == node.TEXT_NODE: | |
34 content.append(node.data) | |
35 return ''.join(content) | |
36 | |
37 | |
38 def _get_path_from_url(file_uri): | |
39 parsed = urlparse(file_uri) | |
40 return path.abspath(path.join(parsed.netloc, unquote(parsed.path))) | |
41 | |
42 | |
43 def _normalise_rating(rating): | |
44 # Take the Banshee approach to ratings, but assume that a rating of 5 | |
45 # or below is an exact rating (normally a rogue Banshee rating) | |
46 # https://git.gnome.org/browse/banshee/tree/src/Core/Banshee.Core/Banshee.Streaming/StreamRatingTagger.cs#n54 | |
47 if rating <= 5: | |
48 return rating | |
49 elif rating < 64: | |
50 return 1 | |
51 elif rating < 128: | |
52 return 2 | |
53 elif rating < 192: | |
54 return 3 | |
55 elif rating < 255: | |
56 return 4 | |
57 else: | |
58 return 5 | |
59 | |
60 | |
61 def _set_rating(doc, entry, value): | |
62 rating = _normalise_rating(value) | |
63 _debug("Setting rating to {} based on value {}".format(rating, value)) | |
64 rating_tag = doc.createElement("rating") | |
65 rating_tag.appendChild(doc.createTextNode(str(rating))) | |
66 entry.appendChild(rating_tag) | |
67 | |
68 def transfer_ratings(): | |
69 """ | |
70 Transfers the ratings from IDv3 POPM tags to Rhythmbox's XML database | |
71 and prints the resulting XML to stdout | |
72 """ | |
73 home = expanduser("~") | |
74 | |
75 doc_path = path.join(home, '.local/share/rhythmbox/rhythmdb.xml') | |
76 doc = parse(doc_path) | |
77 | |
78 if len(doc.childNodes) != 1: | |
79 _debug("Invalid document structure", 0) | |
80 exit(1) | |
81 | |
82 rhythmdb = doc.childNodes[0] | |
83 | |
84 if rhythmdb.nodeName != 'rhythmdb': | |
85 _debug("Invalid document structure", 0) | |
86 exit(1) | |
87 | |
88 for entry in rhythmdb.childNodes: | |
89 if entry.nodeType == entry.TEXT_NODE or entry.getAttribute('type') != 'song': | |
90 continue | |
91 if len(entry.getElementsByTagName('rating')) > 0: | |
92 continue | |
93 | |
94 locations = entry.getElementsByTagName('location') | |
95 | |
96 if len(locations) != 1: | |
97 _debug("Song did not have one location", 0) | |
98 continue | |
99 | |
100 location = locations[0] | |
101 file_path = _get_path_from_url(_get_text(location.childNodes)) | |
102 | |
103 if not path.isfile(file_path): | |
104 _debug("{} was not a file".format(file_path)) | |
105 | |
106 try: | |
107 track = mutagen.File(file_path) | |
108 | |
109 for key_suffix in TRANSFER_KEYS: | |
110 key = 'POPM:{}'.format(key_suffix) | |
111 if key in track.tags: | |
112 rating = track.tags[key].rating | |
113 if rating > 0: | |
114 _debug("Found rating from {} for {}".format(key_suffix, file_path)) | |
115 _set_rating(doc, entry, track.tags[key].rating) | |
116 break | |
117 except mutagen.MutagenError as ex: | |
118 _debug("Error processing {}: {}".format(file_path, ex), 0) | |
119 | |
120 print(doc.toxml()) | |
121 | |
122 | |
123 if __name__ == '__main__': | |
124 transfer_ratings() |