Merge pull request #475 from rahul-raturi/embed_search

Searching tracks and displaying similar tracks in a dialog box.
This commit is contained in:
Michael Wiencek
2016-08-14 21:52:30 -05:00
committed by GitHub
12 changed files with 701 additions and 104 deletions

View File

@@ -246,7 +246,7 @@ def media_formats_from_node(node):
def track_to_metadata(node, track):
m = track.metadata
recording_to_metadata(node.recording[0], track)
recording_to_metadata(node.recording[0], m, track)
m.add_unique('musicbrainz_trackid', node.id)
# overwrite with data we have on the track
for name, nodes in node.children.iteritems():
@@ -265,8 +265,7 @@ def track_to_metadata(node, track):
m['~length'] = format_time(m.length)
def recording_to_metadata(node, track):
m = track.metadata
def recording_to_metadata(node, m, track=None):
m.length = 0
m.add_unique('musicbrainz_recordingid', node.id)
for name, nodes in node.children.iteritems():
@@ -281,7 +280,7 @@ def recording_to_metadata(node, track):
m['~recordingcomment'] = nodes[0].text
elif name == 'artist_credit':
artist_credit_to_metadata(nodes[0], m)
if 'name_credit' in nodes[0].children:
if 'name_credit' in nodes[0].children and track:
for name_credit in nodes[0].name_credit:
if 'artist' in name_credit.children:
for artist in name_credit.artist:
@@ -375,7 +374,7 @@ def release_to_metadata(node, m, album=None):
m['barcode'] = nodes[0].text
elif name == 'relation_list':
_relations_to_metadata(nodes, m)
elif name == 'label_info_list' and nodes[0].count != '0':
elif name == 'label_info_list' and getattr(nodes[0], 'count', '0') != '0':
m['label'], m['catalognumber'] = label_info_from_node(nodes[0])
elif name == 'text_representation':
if 'language' in nodes[0].children:

View File

@@ -1262,6 +1262,51 @@ qt_resource_data = "\
\xb3\xa9\xe4\x06\xaa\xfb\x62\x36\x86\x02\x46\x8a\x63\x13\x00\x29\
\x51\x09\x03\x00\x20\x62\x2f\x00\x00\x00\x00\x49\x45\x4e\x44\xae\
\x42\x60\x82\
\x00\x00\x02\xa1\
\x47\
\x49\x46\x38\x39\x61\x10\x00\x10\x00\xf2\x00\x00\xff\xff\xff\x00\
\x00\x00\xc2\xc2\xc2\x42\x42\x42\x00\x00\x00\x62\x62\x62\x82\x82\
\x82\x92\x92\x92\x21\xff\x0b\x4e\x45\x54\x53\x43\x41\x50\x45\x32\
\x2e\x30\x03\x01\x00\x00\x00\x21\xfe\x1a\x43\x72\x65\x61\x74\x65\
\x64\x20\x77\x69\x74\x68\x20\x61\x6a\x61\x78\x6c\x6f\x61\x64\x2e\
\x69\x6e\x66\x6f\x00\x21\xf9\x04\x09\x0a\x00\x00\x00\x2c\x00\x00\
\x00\x00\x10\x00\x10\x00\x00\x03\x33\x08\xba\xdc\xfe\x30\xca\x49\
\x6b\x13\x63\x08\x3a\x08\x19\x9c\x07\x4e\x98\x66\x09\x45\xb1\x31\
\xc2\xba\x14\x99\xc1\xb6\x2e\x60\xc4\xc2\x71\xd0\x2d\x5b\x18\x39\
\xdd\xa6\x07\x39\x18\x0c\x07\x4a\x6b\xe7\x48\x00\x00\x21\xf9\x04\
\x09\x0a\x00\x00\x00\x2c\x00\x00\x00\x00\x10\x00\x10\x00\x00\x03\
\x34\x08\xba\xdc\xfe\x4e\x8c\x21\x20\x1b\x84\x0c\xbb\xb0\xe6\x8a\
\x44\x71\x42\x51\x54\x60\x31\x19\x20\x60\x4c\x45\x5b\x1a\xa8\x7c\
\x1c\xb5\x75\xdf\xed\x61\x18\x07\x80\x20\xd7\x18\xe2\x86\x43\x19\
\xb2\x25\x24\x2a\x12\x00\x21\xf9\x04\x09\x0a\x00\x00\x00\x2c\x00\
\x00\x00\x00\x10\x00\x10\x00\x00\x03\x36\x08\xba\x32\x23\x2b\xca\
\x41\xc8\x90\xcc\x94\x56\x2f\x06\x85\x63\x1c\x0e\xf4\x19\x4e\xf1\
\x49\x42\x61\x98\xab\x70\x1c\xf0\x0a\xcc\xb3\xbd\x1c\xc6\xa8\x2b\
\x02\x59\xed\x17\xfc\x01\x83\xc3\x0f\x32\xa9\x64\x1a\x9f\xbf\x04\
\x00\x21\xf9\x04\x09\x0a\x00\x00\x00\x2c\x00\x00\x00\x00\x10\x00\
\x10\x00\x00\x03\x33\x08\xba\x62\x25\x2b\xca\x32\x86\x91\xec\x9c\
\x56\x5f\x85\x8b\xa6\x09\x85\x21\x0c\x04\x31\x44\x87\x61\x1c\x11\
\xaa\x46\x82\xb0\xd1\x1f\x03\x62\x52\x5d\xf3\x3d\x1f\x30\x38\x2c\
\x1a\x8f\xc8\xa4\x72\x39\x4c\x00\x00\x21\xf9\x04\x09\x0a\x00\x00\
\x00\x2c\x00\x00\x00\x00\x10\x00\x10\x00\x00\x03\x32\x08\xba\x72\
\x27\x2b\x4a\xe7\x64\x14\xf0\x18\xf3\x4c\x81\x0c\x26\x76\xc3\x60\
\x5c\x62\x54\x94\x85\x84\xb9\x1e\x68\x59\x42\x29\xcf\xca\x40\x10\
\x03\x1e\xe9\x3c\x1f\xc3\x26\x2c\x1a\x8f\xc8\xa4\x52\x92\x00\x00\
\x21\xf9\x04\x09\x0a\x00\x00\x00\x2c\x00\x00\x00\x00\x10\x00\x10\
\x00\x00\x03\x33\x08\xba\x20\xc2\x90\x39\x17\xe3\x74\xe7\xbc\xda\
\x9e\x30\x19\xc7\x1c\xe0\x21\x2e\x42\xb6\x9d\xca\x57\xac\xa2\x31\
\x0c\x06\x0b\x14\x73\x61\xbb\xb0\x35\xf7\x95\x01\x81\x30\xb0\x09\
\x89\xbb\x9f\x6d\x29\x4a\x00\x00\x21\xf9\x04\x09\x0a\x00\x00\x00\
\x2c\x00\x00\x00\x00\x10\x00\x10\x00\x00\x03\x32\x08\xba\xdc\xfe\
\xf0\x09\x11\xd9\x9c\x55\x5d\x9a\x01\xee\xda\x71\x70\x95\x60\x88\
\xdd\x61\x9c\xdd\x34\x96\x85\x41\x46\xc5\x30\x14\x90\x60\x9b\xb6\
\x01\x0d\x04\xc2\x40\x10\x9b\x31\x80\xc2\xd6\xce\x91\x00\x00\x21\
\xf9\x04\x09\x0a\x00\x00\x00\x2c\x00\x00\x00\x00\x10\x00\x10\x00\
\x00\x03\x32\x08\xba\xdc\xfe\x30\xca\x49\xab\x65\x42\xd4\x9c\x29\
\xd7\x1e\x08\x08\xc3\x20\x8e\xc7\x71\x0e\x04\x31\x30\xa9\xca\xb0\
\xae\x50\x18\xc2\x61\x18\x07\x56\xda\xa5\x02\x20\x75\x62\x18\x82\
\x9e\x5b\x11\x90\x00\x00\x3b\x00\x00\x00\x00\x00\x00\x00\x00\x00\
\
\x00\x00\x00\x83\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@@ -3419,31 +3464,75 @@ qt_resource_data = "\
\x8f\xc4\xad\x06\x0f\xc4\xcd\x1e\x8f\x8e\x7f\x01\xd7\x2b\x79\xd4\
\xea\x76\x04\x5f\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
\
\x00\x00\x01\x62\
\x00\x00\x04\x2d\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
\x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\
\x00\x00\x01\x29\x49\x44\x41\x54\x78\x01\x63\x18\xa2\xe0\x3f\x63\
\xb9\x74\x6b\x5c\x99\x74\x5b\xfc\x9b\x62\xeb\xf8\x37\xa5\x36\x71\
\xff\x19\x18\x18\xe9\x62\x75\x85\x64\xbb\x71\x99\x74\xeb\x11\xa0\
\x03\xfe\x57\xc8\xb4\x96\xbf\x29\xb1\x29\x7f\x5d\x62\xfd\x1f\x88\
\xcf\xbc\x2c\xb2\xb4\xa2\x99\xc5\x95\xd2\x6d\xc2\x15\x52\x6d\x13\
\x81\x16\xff\x01\x59\x8e\xee\x00\x28\xfe\xf7\xba\xc4\x6a\xd1\xab\
\x52\x07\x09\xaa\x59\x9c\x66\x3c\x93\xb5\x5c\xa6\x35\x1f\x68\xe1\
\x07\x98\xc5\x38\x1d\x80\xc0\x5f\x5e\x97\xd8\x34\xdc\xce\xf5\x64\
\xa7\xc8\x72\x60\x50\x3b\x97\x4b\xb5\x5d\x81\x59\x48\xc8\x01\x98\
\xd8\xea\xd6\xeb\x52\x1b\x1f\xd2\xe3\x59\xa6\x5d\xa5\x5c\xba\x6d\
\x15\xcc\x22\xb2\x1d\x80\xc0\xbb\xdf\x16\xdb\x6a\x11\xb4\xb8\x44\
\xbc\x9b\x1b\x68\x68\x03\xd0\xf0\x1f\x50\x4b\x28\x77\x00\x02\xff\
\x02\xaa\x9d\xf8\x36\xd7\x9c\x0f\xab\xe5\xa5\xd2\xad\x06\x40\x43\
\x9f\xc1\x0c\xa7\xba\x03\x10\xf8\xd9\xab\x52\x2b\x03\xcc\x60\x97\
\x6e\x09\x05\x19\x4a\x07\x07\xfc\x7f\x55\x6a\x13\x3a\xe0\x0e\x18\
\x75\xc0\xa8\x03\x46\x1d\x30\xea\x80\x51\x07\x8c\x3a\x60\xc0\xab\
\x63\x42\x0d\x92\xef\xf4\x69\x90\xd0\xbf\x49\x46\xff\x46\x29\x10\
\x7b\x0f\x68\xb3\x7c\xc8\x77\x4c\x30\x73\x8b\x54\xab\x11\x8e\xae\
\xd9\xe9\xb7\x25\x56\x96\x43\xbd\x73\x4a\x7f\x00\x00\x00\x0b\xb8\
\x4b\xac\x5f\x46\xcf\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\
\x82\
\x00\x00\x20\x00\x00\x00\x20\x08\x03\x00\x00\x00\x44\xa4\x8a\xc6\
\x00\x00\x02\x1c\x50\x4c\x54\x45\x00\x00\x00\x00\x00\xff\xff\x00\
\x00\x80\x00\x80\xff\x80\x40\x71\x1c\x8e\xea\x6a\x40\x78\x1e\x87\
\x71\x1c\x80\xe8\x74\x3a\x7b\x1c\x84\xec\x71\x39\xe7\x78\x38\x75\
\x1b\x85\xec\x76\x39\xec\x74\x3c\x75\x1c\x86\xe9\x73\x39\x77\x1a\
\x84\x77\x1b\x85\xec\x75\x3c\xea\x73\x3c\x78\x1c\x85\x77\x1b\x84\
\xec\x75\x3a\x78\x1b\x85\xec\x74\x3c\xea\x74\x3a\x78\x1a\x86\x76\
\x1b\x85\xeb\x75\x3a\x78\x1b\x84\xeb\x74\x3a\xb3\x80\xac\xf6\xbe\
\x92\xeb\x73\x3a\xcb\xa8\xbb\xf8\xcc\xa1\x77\x1a\x85\x78\x1c\x85\
\xeb\x73\x3c\x78\x1b\x85\xeb\x74\x3b\x77\x1c\x85\xeb\x74\x3c\x77\
\x1b\x85\xeb\x74\x3b\x77\x1b\x85\xeb\x74\x3b\x77\x1b\x85\xeb\x74\
\x3b\x77\x1b\x85\x77\x1b\x85\x78\x1b\x85\x79\x1e\x87\x79\x1e\x88\
\x79\x1f\x86\x7a\x20\x87\x7a\x20\x89\x7d\x22\x8a\x7d\x23\x8c\x7e\
\x24\x8c\x7f\x25\x8c\x80\x26\x8d\x84\x2c\x92\x85\x2c\x92\x88\x30\
\x95\x88\x31\x96\x8a\x33\x97\x8c\x34\x98\x8c\x37\x9a\x8e\x38\x9c\
\x8f\x39\x9b\x90\x3a\x9d\x94\x40\xa2\x97\x51\x9a\x99\x46\xa6\x9a\
\x49\xa7\x9c\x49\xa9\x9f\x4e\xac\xa0\x4f\xac\xa5\x55\xb2\xa8\x58\
\xb4\xac\x5d\xb8\xac\x5e\xb8\xad\x60\xba\xaf\x62\xbb\xb0\x64\xbc\
\xb3\x66\xbe\xb4\x68\xc0\xb4\x6a\xc1\xb5\x69\xc1\xb5\x6a\xc2\xb6\
\x6b\xc2\xb9\x82\xb3\xbb\x75\xc3\xc0\x80\xc6\xc2\x84\xc6\xc7\x8e\
\xc8\xc8\xa2\xb9\xca\xa2\xbb\xcd\x99\xca\xcf\x9d\xcb\xd1\xb1\xbe\
\xd5\xaa\xcd\xd8\xb1\xce\xda\xb4\xce\xda\xbd\xc5\xe1\xc8\xcb\xe4\
\xd0\xca\xe6\xca\xd3\xea\xd3\xd3\xeb\x74\x3b\xeb\x75\x3d\xeb\x76\
\x3d\xec\x78\x40\xec\x79\x42\xec\x7b\x43\xec\x7c\x44\xec\x7e\x46\
\xec\x7e\x47\xed\x7f\x47\xed\x7f\x48\xed\x81\x4a\xed\x82\x4a\xed\
\x82\x4b\xed\x84\x4d\xee\x87\x51\xee\x88\x52\xee\x8b\x55\xee\x8b\
\x56\xf0\x94\x60\xf0\x97\x64\xf0\x98\x65\xf0\x9a\x66\xf1\x9c\x69\
\xf1\x9e\x6b\xf1\xe6\xd4\xf2\xa1\x6f\xf2\xa2\x70\xf2\xa4\x73\xf2\
\xa7\x75\xf2\xe3\xd7\xf2\xe9\xd3\xf3\xae\x7d\xf5\xb8\x88\xf5\xea\
\xd8\xf6\xbc\x8f\xf6\xee\xd6\xf7\xc9\x9e\xf8\xcb\xa0\xf8\xcf\xa4\
\xf8\xd0\xa6\xf9\xd6\xac\xf9\xd7\xae\xf9\xf4\xd8\xfb\xe0\xb9\xfb\
\xe2\xba\xfb\xe2\xbb\xfb\xe3\xbc\xfb\xf7\xda\xfc\xe9\xc3\xfc\xeb\
\xc5\xfc\xed\xc7\xfc\xf8\xda\xfd\xef\xca\xfd\xf0\xcb\xfd\xf3\xce\
\xfd\xfb\xda\xfe\xf4\xcf\xfe\xf6\xd1\xfe\xf6\xd2\xfe\xf7\xd3\xfe\
\xf8\xd5\xfe\xf9\xd4\xff\xfb\xd7\xff\xfc\xd9\xff\xfd\xda\xff\xfd\
\xdb\xff\xfe\xdb\xa6\x92\x1f\xb1\x00\x00\x00\x34\x74\x52\x4e\x53\
\x00\x01\x01\x02\x04\x09\x0c\x11\x12\x16\x1b\x1b\x20\x30\x36\x37\
\x3f\x47\x4d\x5e\x5e\x6f\x77\x83\x83\x84\x84\x88\x91\x99\x99\xa2\
\xa2\xa8\xb1\xb3\xbb\xbd\xc1\xc2\xc9\xd1\xd7\xde\xe3\xea\xee\xf3\
\xf6\xfc\xfd\xfe\x98\x06\x23\x55\x00\x00\x01\x8c\x49\x44\x41\x54\
\x38\xcb\x63\x60\x40\x01\x8c\x5c\xdc\x4c\x0c\x78\x00\xaf\xba\xa4\
\x94\x06\x3f\x4e\x69\x0e\x19\x43\x13\x49\xa9\x02\x23\x05\x4e\xac\
\xd2\xcc\xc2\xba\x26\x26\x20\x05\x05\x05\x7a\xa2\x2c\x98\xf2\x02\
\x9a\x26\x26\x30\x05\x05\x05\x5a\x82\x68\xd2\x5c\xf2\xc6\x26\xc8\
\x0a\x0a\x0a\x94\x79\x90\xa4\x59\xc5\xf4\x4d\x4c\xd0\x14\x14\x18\
\x48\xb3\xc1\xe4\x85\xb4\x4d\x4c\x30\x15\x14\x14\xe8\x88\x40\xe4\
\xc5\x21\x52\x36\xae\x26\xde\xc9\xb9\x2a\x48\x0a\x0a\x0a\x24\xc0\
\x0a\x64\x41\xd2\x76\xbe\x91\xf1\x39\x9b\xfb\xd3\x15\x21\x0a\x8a\
\xda\x9a\x0b\x81\x94\x1c\x5c\x81\x47\x74\x5a\xfe\xa4\x25\x71\x16\
\x30\x2b\x66\x6c\xde\x3c\x15\x45\x41\x40\xd2\xa6\xec\x29\x79\x51\
\x0e\x50\x05\x75\x9b\xbb\x7a\x36\x56\x22\x2b\x08\xce\x98\x6d\x99\
\xd2\x17\x0b\x53\x50\xba\x6e\xda\xf4\xd5\x25\x48\x0a\xcc\xa2\x13\
\x16\x74\xae\xc8\x8c\x31\x87\x2a\xa8\x5f\xbe\x6e\x61\x07\xb2\x15\
\x8e\xb1\xb1\x89\x59\xa9\xb1\x21\x50\x6f\x36\x2e\x5e\x5a\x0b\x76\
\x0a\x5c\x81\x67\x2c\x18\x04\x80\x15\xd4\x4c\x5e\xbf\xac\xa1\x00\
\x55\x81\x1f\x44\x81\x17\x50\x81\xd2\xdc\x0d\x9b\x37\x6f\x9c\xd5\
\x84\xaa\x20\x08\xa2\xc0\x19\xa8\x40\x75\xde\xc4\xea\xf2\xde\x45\
\x6b\x26\xb4\x17\x21\x14\x98\x46\x40\x14\x58\xc3\x83\xba\x62\xd5\
\xc6\x0d\xd3\x11\x0a\xec\x21\xf2\xe1\x88\xb8\x68\xd8\xdc\xde\xbd\
\xb9\x0a\xae\xc0\x35\x36\x36\x34\x2c\x36\x36\x10\xa1\xa0\x6c\xed\
\x8c\x99\x2b\x8b\xe1\x0a\x9c\xdc\x6d\x4d\xac\xfc\x63\x7d\x90\x62\
\xb3\x75\xce\xfc\x16\xe4\x90\x04\x01\x37\x17\xb4\xe8\x86\x29\x10\
\x37\x31\xc1\x9e\x1e\x60\xd1\x4d\x30\xc1\x10\x4e\x72\x84\x13\x2d\
\x11\xc9\x9e\x88\x8c\x03\x04\xec\xf8\xb3\x1e\x38\xf3\xaa\x01\x33\
\x2f\x1f\xbe\xec\x8d\x99\xfd\x01\x12\xd5\xd3\xad\x82\xe8\xbe\xc1\
\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
\x00\x00\x33\x3f\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@@ -4759,6 +4848,10 @@ qt_resource_name = "\
\x00\x6d\
\x00\x61\x00\x74\x00\x63\x00\x68\x00\x2d\x00\x70\x00\x65\x00\x6e\x00\x64\x00\x69\x00\x6e\x00\x67\x00\x2d\x00\x36\x00\x30\x00\x2e\
\x00\x70\x00\x6e\x00\x67\
\x00\x0a\
\x0a\xcb\x27\x16\
\x00\x6c\
\x00\x6f\x00\x61\x00\x64\x00\x65\x00\x72\x00\x2e\x00\x67\x00\x69\x00\x66\
\x00\x05\
\x00\x35\x9b\x52\
\x00\x32\
@@ -4935,78 +5028,79 @@ qt_resource_name = "\
qt_resource_struct = "\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x1d\x00\x00\x00\x02\
\x00\x00\x02\x72\x00\x00\x00\x00\x00\x01\x00\x00\x4d\x8d\
\x00\x00\x02\xf8\x00\x02\x00\x00\x00\x12\x00\x00\x00\x37\
\x00\x00\x02\x16\x00\x02\x00\x00\x00\x13\x00\x00\x00\x24\
\x00\x00\x00\x12\x00\x02\x00\x00\x00\x01\x00\x00\x00\x23\
\x00\x00\x01\x1a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x22\
\x00\x00\x01\x4e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x21\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x1e\x00\x00\x00\x02\
\x00\x00\x02\x8c\x00\x00\x00\x00\x00\x01\x00\x00\x50\x32\
\x00\x00\x03\x12\x00\x02\x00\x00\x00\x12\x00\x00\x00\x38\
\x00\x00\x02\x30\x00\x02\x00\x00\x00\x13\x00\x00\x00\x25\
\x00\x00\x00\x12\x00\x02\x00\x00\x00\x01\x00\x00\x00\x24\
\x00\x00\x01\x1a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x23\
\x00\x00\x01\x4e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x22\
\x00\x00\x00\x22\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x03\x4a\x00\x00\x00\x00\x00\x01\x00\x00\x54\x26\
\x00\x00\x03\x26\x00\x00\x00\x00\x00\x01\x00\x00\x53\x90\
\x00\x00\x00\x94\x00\x02\x00\x00\x00\x01\x00\x00\x00\x20\
\x00\x00\x01\x06\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1f\
\x00\x00\x02\xd4\x00\x00\x00\x00\x00\x01\x00\x00\x50\xac\
\x00\x00\x03\x64\x00\x00\x00\x00\x00\x01\x00\x00\x56\xcb\
\x00\x00\x03\x40\x00\x00\x00\x00\x00\x01\x00\x00\x56\x35\
\x00\x00\x00\x94\x00\x02\x00\x00\x00\x01\x00\x00\x00\x21\
\x00\x00\x01\x06\x00\x02\x00\x00\x00\x01\x00\x00\x00\x20\
\x00\x00\x02\xee\x00\x00\x00\x00\x00\x01\x00\x00\x53\x51\
\x00\x00\x01\x2a\x00\x00\x00\x00\x00\x01\x00\x00\x47\x52\
\x00\x00\x00\xd6\x00\x00\x00\x00\x00\x01\x00\x00\x46\xe5\
\x00\x00\x00\x48\x00\x00\x00\x00\x00\x01\x00\x00\x01\x3e\
\x00\x00\x00\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x03\x8e\
\x00\x00\x00\xbe\x00\x00\x00\x00\x00\x01\x00\x00\x43\x17\
\x00\x00\x02\xa6\x00\x00\x00\x00\x00\x01\x00\x00\x50\x23\
\x00\x00\x02\xc0\x00\x00\x00\x00\x00\x01\x00\x00\x52\xc8\
\x00\x00\x00\xa8\x00\x00\x00\x00\x00\x01\x00\x00\x40\xf4\
\x00\x00\x02\x44\x00\x00\x00\x00\x00\x01\x00\x00\x4d\x14\
\x00\x00\x02\x5e\x00\x00\x00\x00\x00\x01\x00\x00\x4f\xb9\
\x00\x00\x01\xe8\x00\x00\x00\x00\x00\x01\x00\x00\x4c\x05\
\x00\x00\x01\x9c\x00\x00\x00\x00\x00\x01\x00\x00\x4a\xf5\
\x00\x00\x03\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x54\xa3\
\x00\x00\x02\x16\x00\x00\x00\x00\x00\x01\x00\x00\x4c\x8d\
\x00\x00\x03\x84\x00\x00\x00\x00\x00\x01\x00\x00\x57\x48\
\x00\x00\x01\x5e\x00\x00\x00\x00\x00\x01\x00\x00\x48\x77\
\x00\x00\x03\x08\x00\x00\x00\x00\x00\x01\x00\x00\x52\xfc\
\x00\x00\x02\x88\x00\x00\x00\x00\x00\x01\x00\x00\x4f\x8a\
\x00\x00\x02\x26\x00\x00\x00\x00\x00\x01\x00\x00\x4c\x8d\
\x00\x00\x03\x22\x00\x00\x00\x00\x00\x01\x00\x00\x55\xa1\
\x00\x00\x02\xa2\x00\x00\x00\x00\x00\x01\x00\x00\x52\x2f\
\x00\x00\x02\x40\x00\x00\x00\x00\x00\x01\x00\x00\x4f\x32\
\x00\x00\x01\xca\x00\x00\x00\x00\x00\x01\x00\x00\x4b\x6b\
\x00\x00\x01\x7e\x00\x00\x00\x00\x00\x01\x00\x00\x4a\x6e\
\x00\x00\x04\x4a\x00\x00\x00\x00\x00\x01\x00\x00\xce\x2d\
\x00\x00\x04\x4a\x00\x00\x00\x00\x00\x01\x00\x01\x01\x70\
\x00\x00\x04\x4a\x00\x00\x00\x00\x00\x01\x00\x00\xc7\x62\
\x00\x00\x04\x4a\x00\x00\x00\x00\x00\x01\x00\x00\xcc\xc7\
\x00\x00\x04\x4a\x00\x00\x00\x00\x00\x01\x00\x01\x19\xca\
\x00\x00\x06\x4a\x00\x00\x00\x00\x00\x01\x00\x00\xbb\x5b\
\x00\x00\x04\x98\x00\x00\x00\x00\x00\x01\x00\x00\x92\x3c\
\x00\x00\x06\xc0\x00\x00\x00\x00\x00\x01\x00\x00\x85\x4d\
\x00\x00\x07\x44\x00\x00\x00\x00\x00\x01\x00\x00\xa2\xb9\
\x00\x00\x05\xca\x00\x00\x00\x00\x00\x01\x00\x00\xae\x5d\
\x00\x00\x07\x6e\x00\x00\x00\x00\x00\x01\x00\x00\xa4\xe2\
\x00\x00\x06\x94\x00\x00\x00\x00\x00\x01\x00\x00\x7e\x20\
\x00\x00\x03\xcc\x00\x00\x00\x00\x00\x01\x00\x00\x80\xde\
\x00\x00\x04\x64\x00\x00\x00\x00\x00\x01\x00\x00\x87\x7b\
\x00\x00\x06\xe4\x00\x00\x00\x00\x00\x01\x00\x00\x8c\xc7\
\x00\x00\x07\xbe\x00\x00\x00\x00\x00\x01\x00\x00\xc0\x5d\
\x00\x00\x04\xc0\x00\x00\x00\x00\x00\x01\x00\x00\x95\xcc\
\x00\x00\x04\x00\x00\x00\x00\x00\x00\x01\x00\x00\x82\xad\
\x00\x00\x05\x3a\x00\x00\x00\x00\x00\x01\x00\x00\xaa\xde\
\x00\x00\x06\x72\x00\x00\x00\x00\x00\x01\x00\x00\xc3\x00\
\x00\x00\x05\x06\x00\x00\x00\x00\x00\x01\x00\x00\x9d\xe9\
\x00\x00\x07\x16\x00\x00\x00\x00\x00\x01\x00\x00\x9a\x4a\
\x00\x00\x07\x96\x00\x00\x00\x00\x00\x01\x00\x00\xb7\x08\
\x00\x00\x06\x22\x00\x00\x00\x00\x00\x01\x00\x00\xb3\x89\
\x00\x00\x04\x1a\x00\x00\x00\x00\x00\x01\x00\x00\x59\x46\
\x00\x00\x06\x4a\x00\x00\x00\x00\x00\x01\x00\x00\x78\xd0\
\x00\x00\x04\x98\x00\x00\x00\x00\x00\x01\x00\x00\x5f\x84\
\x00\x00\x05\xca\x00\x00\x00\x00\x00\x01\x00\x00\x70\xcf\
\x00\x00\x03\xcc\x00\x00\x00\x00\x00\x01\x00\x00\x56\x1d\
\x00\x00\x06\x04\x00\x00\x00\x00\x00\x01\x00\x00\x73\xd5\
\x00\x00\x05\x5c\x00\x00\x00\x00\x00\x01\x00\x00\x69\xf6\
\x00\x00\x04\x64\x00\x00\x00\x00\x00\x01\x00\x00\x5c\x7b\
\x00\x00\x04\x4a\x00\x00\x00\x00\x00\x01\x00\x00\x5b\xa2\
\x00\x00\x04\xc0\x00\x00\x00\x00\x00\x01\x00\x00\x61\x46\
\x00\x00\x04\x00\x00\x00\x00\x00\x00\x01\x00\x00\x57\xa1\
\x00\x00\x05\xa8\x00\x00\x00\x00\x00\x01\x00\x00\x6e\xf8\
\x00\x00\x05\x3a\x00\x00\x00\x00\x00\x01\x00\x00\x67\xd3\
\x00\x00\x03\x98\x00\x00\x00\x00\x00\x01\x00\x00\x55\x27\
\x00\x00\x06\x72\x00\x00\x00\x00\x00\x01\x00\x00\x7c\x0e\
\x00\x00\x05\x06\x00\x00\x00\x00\x00\x01\x00\x00\x64\xbf\
\x00\x00\x05\x82\x00\x00\x00\x00\x00\x01\x00\x00\x6c\xd0\
\x00\x00\x06\x22\x00\x00\x00\x00\x00\x01\x00\x00\x76\x36\
\x00\x00\x04\x64\x00\x00\x00\x00\x00\x01\x00\x00\xd3\x9d\
\x00\x00\x04\x64\x00\x00\x00\x00\x00\x01\x00\x01\x06\xe0\
\x00\x00\x04\x64\x00\x00\x00\x00\x00\x01\x00\x00\xca\x07\
\x00\x00\x04\x64\x00\x00\x00\x00\x00\x01\x00\x00\xcf\x6c\
\x00\x00\x04\x64\x00\x00\x00\x00\x00\x01\x00\x01\x1f\x3a\
\x00\x00\x06\x64\x00\x00\x00\x00\x00\x01\x00\x00\xbe\x00\
\x00\x00\x04\xb2\x00\x00\x00\x00\x00\x01\x00\x00\x94\xe1\
\x00\x00\x06\xda\x00\x00\x00\x00\x00\x01\x00\x00\x87\xf2\
\x00\x00\x07\x5e\x00\x00\x00\x00\x00\x01\x00\x00\xa5\x5e\
\x00\x00\x05\xe4\x00\x00\x00\x00\x00\x01\x00\x00\xb1\x02\
\x00\x00\x07\x88\x00\x00\x00\x00\x00\x01\x00\x00\xa7\x87\
\x00\x00\x06\xae\x00\x00\x00\x00\x00\x01\x00\x00\x80\xc5\
\x00\x00\x03\xe6\x00\x00\x00\x00\x00\x01\x00\x00\x83\x83\
\x00\x00\x04\x7e\x00\x00\x00\x00\x00\x01\x00\x00\x8a\x20\
\x00\x00\x06\xfe\x00\x00\x00\x00\x00\x01\x00\x00\x8f\x6c\
\x00\x00\x07\xd8\x00\x00\x00\x00\x00\x01\x00\x00\xc3\x02\
\x00\x00\x04\xda\x00\x00\x00\x00\x00\x01\x00\x00\x98\x71\
\x00\x00\x04\x1a\x00\x00\x00\x00\x00\x01\x00\x00\x85\x52\
\x00\x00\x05\x54\x00\x00\x00\x00\x00\x01\x00\x00\xad\x83\
\x00\x00\x06\x8c\x00\x00\x00\x00\x00\x01\x00\x00\xc5\xa5\
\x00\x00\x05\x20\x00\x00\x00\x00\x00\x01\x00\x00\xa0\x8e\
\x00\x00\x07\x30\x00\x00\x00\x00\x00\x01\x00\x00\x9c\xef\
\x00\x00\x07\xb0\x00\x00\x00\x00\x00\x01\x00\x00\xb9\xad\
\x00\x00\x06\x3c\x00\x00\x00\x00\x00\x01\x00\x00\xb6\x2e\
\x00\x00\x04\x34\x00\x00\x00\x00\x00\x01\x00\x00\x5b\xeb\
\x00\x00\x06\x64\x00\x00\x00\x00\x00\x01\x00\x00\x7b\x75\
\x00\x00\x04\xb2\x00\x00\x00\x00\x00\x01\x00\x00\x62\x29\
\x00\x00\x05\xe4\x00\x00\x00\x00\x00\x01\x00\x00\x73\x74\
\x00\x00\x03\xe6\x00\x00\x00\x00\x00\x01\x00\x00\x58\xc2\
\x00\x00\x06\x1e\x00\x00\x00\x00\x00\x01\x00\x00\x76\x7a\
\x00\x00\x05\x76\x00\x00\x00\x00\x00\x01\x00\x00\x6c\x9b\
\x00\x00\x04\x7e\x00\x00\x00\x00\x00\x01\x00\x00\x5f\x20\
\x00\x00\x04\x64\x00\x00\x00\x00\x00\x01\x00\x00\x5e\x47\
\x00\x00\x04\xda\x00\x00\x00\x00\x00\x01\x00\x00\x63\xeb\
\x00\x00\x04\x1a\x00\x00\x00\x00\x00\x01\x00\x00\x5a\x46\
\x00\x00\x05\xc2\x00\x00\x00\x00\x00\x01\x00\x00\x71\x9d\
\x00\x00\x05\x54\x00\x00\x00\x00\x00\x01\x00\x00\x6a\x78\
\x00\x00\x03\xb2\x00\x00\x00\x00\x00\x01\x00\x00\x57\xcc\
\x00\x00\x06\x8c\x00\x00\x00\x00\x00\x01\x00\x00\x7e\xb3\
\x00\x00\x05\x20\x00\x00\x00\x00\x00\x01\x00\x00\x67\x64\
\x00\x00\x05\x9c\x00\x00\x00\x00\x00\x01\x00\x00\x6f\x75\
\x00\x00\x06\x3c\x00\x00\x00\x00\x00\x01\x00\x00\x78\xdb\
"
def qInitResources():

View File

@@ -271,9 +271,9 @@ class NonAlbumTrack(Track):
log.error(traceback.format_exc())
def _parse_recording(self, recording):
recording_to_metadata(recording, self)
self._customize_metadata()
m = self.metadata
recording_to_metadata(recording, m, self)
self._customize_metadata()
run_track_metadata_processors(self.album, m, None, recording)
if config.setting["enable_tagger_script"]:
script = config.setting["tagger_script"]

View File

@@ -257,6 +257,7 @@ class BaseTreeView(QtGui.QTreeWidget):
if obj.num_linked_files == 1:
menu.addAction(self.window.play_file_action)
menu.addAction(self.window.open_folder_action)
menu.addAction(self.window.tracks_search_action)
plugin_actions.extend(_file_actions)
menu.addAction(self.window.browser_lookup_action)
menu.addSeparator()
@@ -285,6 +286,7 @@ class BaseTreeView(QtGui.QTreeWidget):
menu.addSeparator()
menu.addAction(self.window.autotag_action)
menu.addAction(self.window.analyze_action)
menu.addAction(self.window.tracks_search_action)
plugin_actions = list(_file_actions)
elif isinstance(obj, Album):
if can_view_info:

View File

@@ -34,6 +34,7 @@ from picard.ui.filebrowser import FileBrowser
from picard.ui.tagsfromfilenames import TagsFromFileNamesDialog
from picard.ui.options.dialog import OptionsDialog
from picard.ui.infodialog import FileInfoDialog, AlbumInfoDialog, ClusterInfoDialog
from picard.ui.searchdialog import TrackSearchDialog
from picard.ui.infostatus import InfoStatus
from picard.ui.passworddialog import PasswordDialog
from picard.ui.logview import LogView, HistoryView
@@ -381,6 +382,10 @@ class MainWindow(QtGui.QMainWindow):
self.browser_lookup_action.setEnabled(False)
self.browser_lookup_action.triggered.connect(self.browser_lookup)
self.tracks_search_action = QtGui.QAction(icontheme.lookup('system-search'), _(u"Search similar tracks..."), self)
self.tracks_search_action.setStatusTip(_(u"View similar tracks and optionally choose a different release"))
self.tracks_search_action.triggered.connect(self.show_more_tracks)
self.show_file_browser_action = QtGui.QAction(_(u"File &Browser"), self)
self.show_file_browser_action.setCheckable(True)
if config.persist["view_file_browser"]:
@@ -674,7 +679,13 @@ class MainWindow(QtGui.QMainWindow):
"""Search for album, artist or track on the MusicBrainz website."""
text = self.search_edit.text()
type = self.search_combo.itemData(self.search_combo.currentIndex())
self.tagger.search(text, type, config.setting["use_adv_search_syntax"])
if config.setting["builtin_search"]:
if type == "track":
dialog = TrackSearchDialog(self)
dialog.search(text)
dialog.exec_()
else:
self.tagger.search(text, type, config.setting["use_adv_search_syntax"])
def add_files(self):
"""Add files to the tagger."""
@@ -791,6 +802,14 @@ class MainWindow(QtGui.QMainWindow):
QtGui.QMessageBox.Yes)
return ret == QtGui.QMessageBox.Yes
def show_more_tracks(self):
obj = self.selected_objects[0]
if isinstance(obj, Track):
obj = obj.linked_files[0]
dialog = TrackSearchDialog(self)
dialog.load_similar_tracks(obj)
dialog.exec_()
def view_info(self):
if isinstance(self.selected_objects[0], Album):
album = self.selected_objects[0]

View File

@@ -40,6 +40,7 @@ class InterfaceOptionsPage(OptionsPage):
options = [
config.BoolOption("setting", "toolbar_show_labels", True),
config.BoolOption("setting", "toolbar_multiselect", False),
config.BoolOption("setting", "builtin_search", False),
config.BoolOption("setting", "use_adv_search_syntax", False),
config.BoolOption("setting", "quit_confirmation", True),
config.TextOption("setting", "ui_language", u""),
@@ -77,6 +78,7 @@ class InterfaceOptionsPage(OptionsPage):
def load(self):
self.ui.toolbar_show_labels.setChecked(config.setting["toolbar_show_labels"])
self.ui.toolbar_multiselect.setChecked(config.setting["toolbar_multiselect"])
self.ui.builtin_search.setChecked(config.setting["builtin_search"])
self.ui.use_adv_search_syntax.setChecked(config.setting["use_adv_search_syntax"])
self.ui.quit_confirmation.setChecked(config.setting["quit_confirmation"])
current_ui_language = config.setting["ui_language"]
@@ -87,6 +89,7 @@ class InterfaceOptionsPage(OptionsPage):
def save(self):
config.setting["toolbar_show_labels"] = self.ui.toolbar_show_labels.isChecked()
config.setting["toolbar_multiselect"] = self.ui.toolbar_multiselect.isChecked()
config.setting["builtin_search"] = self.ui.builtin_search.isChecked()
config.setting["use_adv_search_syntax"] = self.ui.use_adv_search_syntax.isChecked()
config.setting["quit_confirmation"] = self.ui.quit_confirmation.isChecked()
self.tagger.window.update_toolbar_style()

462
picard/ui/searchdialog.py Normal file
View File

@@ -0,0 +1,462 @@
# -*- coding: utf-8 -*-
#
# Picard, the next-generation MusicBrainz tagger
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from PyQt4 import QtGui, QtCore, QtNetwork
from operator import itemgetter
from functools import partial
from collections import namedtuple
from picard import config
from picard.file import File
from picard.ui import PicardDialog
from picard.ui.util import StandardButton, ButtonLineEdit
from picard.util import format_time, icontheme
from picard.mbxml import (
recording_to_metadata,
release_to_metadata,
release_group_to_metadata
)
from picard.i18n import ugettext_attr
from picard.metadata import Metadata
from picard.webservice import escape_lucene_query
from picard.track import Track
class ResultTable(QtGui.QTableWidget):
def __init__(self, column_titles):
QtGui.QTableWidget.__init__(self, 0, len(column_titles))
self.setHorizontalHeaderLabels(column_titles)
self.setSelectionMode(
QtGui.QAbstractItemView.SingleSelection)
self.setSelectionBehavior(
QtGui.QAbstractItemView.SelectRows)
self.setEditTriggers(
QtGui.QAbstractItemView.NoEditTriggers)
self.horizontalHeader().setStretchLastSection(True)
self.horizontalHeader().setResizeMode(
QtGui.QHeaderView.Stretch)
self.horizontalHeader().setResizeMode(
QtGui.QHeaderView.Interactive)
class SearchBox(QtGui.QWidget):
def __init__(self, parent):
self.parent = parent
QtGui.QWidget.__init__(self, parent)
self.search_action = QtGui.QAction(icontheme.lookup('system-search'),
_(u"Search"), self)
self.search_action.triggered.connect(self.search)
self.setupUi()
def setupUi(self):
self.layout = QtGui.QVBoxLayout(self)
self.search_row_widget = QtGui.QWidget(self)
self.search_row_layout = QtGui.QHBoxLayout(self.search_row_widget)
self.search_row_layout.setContentsMargins(1, 1, 1, 1)
self.search_row_layout.setSpacing(1)
self.search_edit = ButtonLineEdit(self.search_row_widget)
self.search_row_layout.addWidget(self.search_edit)
self.search_button = QtGui.QToolButton(self.search_row_widget)
self.search_button.setAutoRaise(True)
self.search_button.setDefaultAction(self.search_action)
self.search_button.setIconSize(QtCore.QSize(22, 22))
self.search_row_layout.addWidget(self.search_button)
self.search_row_widget.setLayout(self.search_row_layout)
self.layout.addWidget(self.search_row_widget)
self.adv_opt_row_widget = QtGui.QWidget(self)
self.adv_opt_row_layout = QtGui.QHBoxLayout(self.adv_opt_row_widget)
self.adv_opt_row_layout.setAlignment(QtCore.Qt.AlignLeft)
self.adv_opt_row_layout.setContentsMargins(1, 1, 1, 1)
self.adv_opt_row_layout.setSpacing(1)
self.use_adv_search_syntax = QtGui.QCheckBox(self.adv_opt_row_widget)
self.use_adv_search_syntax.setText(_("Use advanced query syntax"))
self.adv_opt_row_layout.addWidget(self.use_adv_search_syntax)
self.adv_syntax_help = QtGui.QLabel(self.adv_opt_row_widget)
self.adv_syntax_help.setOpenExternalLinks(True)
self.adv_syntax_help.setText(_(
"&#160;(<a href='https://musicbrainz.org/doc/Indexed_Search_Syntax'>"
"Syntax Help</a>)"))
self.adv_opt_row_layout.addWidget(self.adv_syntax_help)
self.adv_opt_row_widget.setLayout(self.adv_opt_row_layout)
self.layout.addWidget(self.adv_opt_row_widget)
self.layout.setContentsMargins(1, 1, 1, 1)
self.layout.setSpacing(1)
self.setMaximumHeight(60)
def search(self):
self.parent.search(self.search_edit.text())
def restore_checkbox_state(self):
self.use_adv_search_syntax.setChecked(config.setting["use_adv_search_syntax"])
def save_checkbox_state(self):
config.setting["use_adv_search_syntax"] = self.use_adv_search_syntax.isChecked()
Retry = namedtuple("Retry", ["function", "query"])
class SearchDialog(PicardDialog):
options = [
config.Option("persist", "searchdialog_window_size", QtCore.QSize(720, 360)),
config.Option("persist", "searchdialog_header_state", QtCore.QByteArray())
]
def __init__(self, parent=None):
PicardDialog.__init__(self, parent)
self.search_results = []
self.table = None
self.setupUi()
self.restore_state()
def setupUi(self):
self.verticalLayout = QtGui.QVBoxLayout(self)
self.verticalLayout.setObjectName(_("vertical_layout"))
self.search_box = SearchBox(self)
self.search_box.setObjectName(_("search_box"))
self.verticalLayout.addWidget(self.search_box)
self.center_widget = QtGui.QWidget(self)
self.center_widget.setObjectName(_("center_widget"))
self.center_layout = QtGui.QVBoxLayout(self.center_widget)
self.center_layout.setObjectName(_("center_layout"))
self.center_layout.setContentsMargins(1, 1, 1, 1)
self.center_widget.setLayout(self.center_layout)
self.verticalLayout.addWidget(self.center_widget)
self.buttonBox = QtGui.QDialogButtonBox(self)
self.buttonBox.setObjectName(_("button_box"))
self.load_button = QtGui.QPushButton(_("Load into Picard"))
self.load_button.setEnabled(False)
self.buttonBox.addButton(
self.load_button,
QtGui.QDialogButtonBox.AcceptRole)
self.buttonBox.addButton(
StandardButton(StandardButton.CANCEL),
QtGui.QDialogButtonBox.RejectRole)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
self.verticalLayout.addWidget(self.buttonBox)
def add_widget_to_center_layout(self, widget):
"""Updates child widget of center_widget.
Child widgets represent dialog's current state, like progress,
error, and displaying fetched results.
"""
wid = self.center_layout.takeAt(0)
if wid:
if wid.widget().objectName() == "results_table":
self.table = None
wid.widget().deleteLater()
self.center_layout.addWidget(widget)
def show_progress(self):
"""Displays feedback while results are being fetched from server."""
self.progress_widget = QtGui.QWidget(self)
self.progress_widget.setObjectName("progress_widget")
layout = QtGui.QVBoxLayout(self.progress_widget)
text_label = QtGui.QLabel(_('<strong>Loading...</strong>'), self.progress_widget)
text_label.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignBottom)
gif_label = QtGui.QLabel(self.progress_widget)
movie = QtGui.QMovie(":/images/loader.gif")
gif_label.setMovie(movie)
movie.start()
gif_label.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop)
layout.addWidget(text_label)
layout.addWidget(gif_label)
layout.setContentsMargins(1, 1, 1, 1)
self.progress_widget.setLayout(layout)
self.add_widget_to_center_layout(self.progress_widget)
def show_error(self, error, show_retry_button=False):
"""Displays error inside the dialog.
Args:
error -- Error string
show_retry_button -- Whether to display retry button or not
"""
self.error_widget = QtGui.QWidget(self)
self.error_widget.setObjectName("error_widget")
layout = QtGui.QVBoxLayout(self.error_widget)
error_label = QtGui.QLabel(error, self.error_widget)
error_label.setWordWrap(True)
error_label.setAlignment(QtCore.Qt.AlignCenter)
error_label.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
layout.addWidget(error_label)
if show_retry_button:
retry_widget = QtGui.QWidget(self.error_widget)
retry_layout = QtGui.QHBoxLayout(retry_widget)
retry_button = QtGui.QPushButton(_("Retry"), self.error_widget)
retry_button.clicked.connect(self.retry)
retry_button.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Maximum, QtGui.QSizePolicy.Fixed))
retry_layout.addWidget(retry_button)
retry_layout.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop)
retry_widget.setLayout(retry_layout)
layout.addWidget(retry_widget)
self.error_widget.setLayout(layout)
self.add_widget_to_center_layout(self.error_widget)
def show_table(self, column_headers):
"""Displays results table inside the dialog."""
self.table = ResultTable(self.table_headers)
self.table.setObjectName("results_table")
self.table.cellDoubleClicked.connect(self.row_double_clicked)
self.table.horizontalHeader().sectionResized.connect(
self.save_table_header_state)
self.restore_table_header_state()
self.add_widget_to_center_layout(self.table)
def enable_loading_button():
self.load_button.setEnabled(True)
self.table.itemSelectionChanged.connect(
enable_loading_button)
def row_double_clicked(self, row):
"""Handle function for double click event inside the table."""
self.load_selection(row)
self.accept()
def network_error(self, reply, error):
error_msg = _("<strong>Following error occurred while fetching results:<br><br></strong>"
"Network request error for %s:<br>%s (QT code %d, HTTP code %s)<br>" % (
reply.request().url().toString(QtCore.QUrl.RemoveUserInfo),
reply.errorString(),
error,
repr(reply.attribute(QtNetwork.QNetworkRequest.HttpStatusCodeAttribute)))
)
self.show_error(error_msg, show_retry_button=True)
def no_results_found(self):
error_msg = _("<strong>No results found. Please try a different search query.</strong>")
self.show_error(error_msg)
def accept(self):
if self.table:
sel_rows = self.table.selectionModel().selectedRows()
if sel_rows:
sel_row = sel_rows[0].row()
self.load_selection(sel_row)
self.save_state()
QtGui.QDialog.accept(self)
def reject(self):
self.save_state()
QtGui.QDialog.reject(self)
def restore_state(self):
size = config.persist["searchdialog_window_size"]
if size:
self.resize(size)
self.search_box.restore_checkbox_state()
def restore_table_header_state(self):
header = self.table.horizontalHeader()
state = config.persist["searchdialog_header_state"]
if state:
header.restoreState(state)
header.setResizeMode(QtGui.QHeaderView.Interactive)
def save_state(self):
"""Saves dialog state i.e. window size, checkbox state, and table
header size.
"""
if self.table:
self.save_table_header_state()
config.persist["searchdialog_window_size"] = self.size()
self.search_box.save_checkbox_state()
def save_table_header_state(self):
state = self.table.horizontalHeader().saveState()
config.persist["searchdialog_header_state"] = state
class TrackSearchDialog(SearchDialog):
def __init__(self, parent):
super(TrackSearchDialog, self).__init__(parent)
self.file_ = None
self.setWindowTitle(_("Track Search Results"))
self.table_headers = [
_("Name"),
_("Length"),
_("Artist"),
_("Release"),
_("Date"),
_("Country"),
_("Type")
]
def search(self, text):
"""Performs search using query provided by the user."""
self.retry_params = Retry(self.search, text)
self.search_box.search_edit.setText(text)
self.show_progress()
self.tagger.xmlws.find_tracks(self.handle_reply,
query=text,
search=True,
limit=25)
def load_similar_tracks(self, file_):
"""Performs search by using existing metadata information
from the file."""
self.retry_params = Retry(self.load_similar_tracks, file_)
self.file_ = file_
metadata = file_.orig_metadata
query = {
'track': metadata['title'],
'artist': metadata['artist'],
'release': metadata['album'],
'tnum': metadata['tracknumber'],
'tracks': metadata['totaltracks'],
'qdur': str(metadata.length / 2000),
'isrc': metadata['isrc'],
}
if config.setting["use_adv_search_syntax"]:
# Display the query in advance syntax format.
query_str = ' '.join(['%s:(%s)' % (item, escape_lucene_query(value))
for item, value in query.iteritems() if value])
else:
# Display only the track title
query_str = query["track"]
# `query_str` is used only for presenting purpose. Actual query consists of all filters and follows
# advanced query syntax.
query["limit"] = 25
self.search_box.search_edit.setText(query_str)
self.show_progress()
self.tagger.xmlws.find_tracks(
self.handle_reply,
**query)
def retry(self):
"""Retries the search using information from `retry_params`."""
self.retry_params.function(self.retry_params.query)
def handle_reply(self, document, http, error):
if error:
self.network_error(http, error)
return
try:
tracks = document.metadata[0].recording_list[0].recording
except (AttributeError, IndexError):
self.no_results_found()
return
if self.file_:
sorted_results = sorted(
(self.file_.orig_metadata.compare_to_track(
track,
File.comparison_weights)
for track in tracks),
reverse=True,
key=itemgetter(0))
tracks = [item[3] for item in sorted_results]
del self.search_results[:] # Clear existing data
self.parse_tracks_from_xml(tracks)
self.display_results()
def display_results(self):
self.show_table(self.table_headers)
for row, obj in enumerate(self.search_results):
track = obj[0]
table_item = QtGui.QTableWidgetItem
self.table.insertRow(row)
self.table.setItem(row, 0, table_item(track.get("title", "")))
self.table.setItem(row, 1, table_item(track.get("~length", "")))
self.table.setItem(row, 2, table_item(track.get("artist", "")))
self.table.setItem(row, 3, table_item(track.get("album", "")))
self.table.setItem(row, 4, table_item(track.get("date", "")))
self.table.setItem(row, 5, table_item(track.get("country", "")))
self.table.setItem(row, 6, table_item(track.get("releasetype", "")))
def parse_tracks_from_xml(self, tracks_xml):
"""Extracts track information from XmlNode objects and stores that into Metadata objects.
Args:
tracks_xml -- list of XmlNode objects
"""
for node in tracks_xml:
if "release_list" in node.children and "release" in node.release_list[0].children:
for rel_node in node.release_list[0].release:
track = Metadata()
recording_to_metadata(node, track)
release_to_metadata(rel_node, track)
rg_node = rel_node.release_group[0]
release_group_to_metadata(rg_node, track)
if "release_event_list" in rel_node.children:
# Extract contries list from `release_event_list` element
# Don't use `country` element as it contains information of a single release
# event and is basically for backward compatibility.
country = []
for re in rel_node.release_event_list[0].release_event:
try:
country.append(
re.area[0].iso_3166_1_code_list[0].iso_3166_1_code[0].text)
except AttributeError:
pass
track["country"] = ", ".join(country)
self.search_results.append((track, node))
else:
# This handles the case when no release is associated with a track
# i.e. the track is a NAT
track = Metadata()
recording_to_metadata(node, track)
track["album"] = _("Standalone Recording")
self.search_results.append((track, node))
def load_selection(self, row):
"""Loads album corresponding to selected track.
If the search is performed for a file, also associates the file to
corresponding track in the album.
"""
track, node = self.search_results[row]
if track.get("musicbrainz_albumid"):
# The track is not an NAT
self.tagger.get_release_group_by_id(track["musicbrainz_releasegroupid"]).loaded_albums.add(
track["musicbrainz_albumid"])
if self.file_:
# Search is performed for a file
# Have to move that file from its existing album to the new one
if isinstance(self.file_.parent, Track):
album = self.file_.parent.album
self.tagger.move_file_to_track(self.file_, track["musicbrainz_albumid"], track["musicbrainz_recordingid"])
if album._files == 0:
# Remove album if it has no more files associated
self.tagger.remove_album(album)
else:
self.tagger.move_file_to_track(self.file_, track["musicbrainz_albumid"], track["musicbrainz_recordingid"])
else:
# No files associated. Just a normal search.
self.tagger.load_album(track["musicbrainz_albumid"])
else:
# The track is a NAT
if self.file_:
album = self.file_.parent.album
self.tagger.move_file_to_nat(track["musicbrainz_recordingid"])
if album._files == 0:
self.tagger.remove_album(album)
else:
self.tagger.load_nat(track["musicbrainz_recordingid"], node)

View File

@@ -26,6 +26,9 @@ class Ui_InterfaceOptionsPage(object):
self.toolbar_multiselect = QtGui.QCheckBox(self.groupBox_2)
self.toolbar_multiselect.setObjectName(_fromUtf8("toolbar_multiselect"))
self.vboxlayout1.addWidget(self.toolbar_multiselect)
self.builtin_search = QtGui.QCheckBox(self.groupBox_2)
self.builtin_search.setObjectName("builtin_search")
self.vboxlayout1.addWidget(self.builtin_search)
self.use_adv_search_syntax = QtGui.QCheckBox(self.groupBox_2)
self.use_adv_search_syntax.setObjectName(_fromUtf8("use_adv_search_syntax"))
self.vboxlayout1.addWidget(self.use_adv_search_syntax)
@@ -69,6 +72,7 @@ class Ui_InterfaceOptionsPage(object):
self.groupBox_2.setTitle(_("Miscellaneous"))
self.toolbar_show_labels.setText(_("Show text labels under icons"))
self.toolbar_multiselect.setText(_("Allow selection of multiple directories"))
self.builtin_search.setText(_("Use builtin search rather than looking in browser"))
self.use_adv_search_syntax.setText(_("Use advanced query syntax"))
self.quit_confirmation.setText(_("Show a quit confirmation dialog for unsaved changes"))
self.starting_directory.setText(_("Begin browsing in the following directory:"))

View File

@@ -63,7 +63,7 @@ CLIENT_STRING = str(QUrl.toPercentEncoding('%s %s-%s' % (PICARD_ORG_NAME,
PICARD_VERSION_STR)))
def _escape_lucene_query(text):
def escape_lucene_query(text):
return re.sub(r'([+\-&|!(){}\[\]\^"~*?:\\/])', r'\\\1', text)
@@ -458,16 +458,28 @@ class XmlWebService(QtCore.QObject):
host = config.setting["server_host"]
port = config.setting["server_port"]
filters = []
query = []
for name, value in kwargs.items():
if name == 'limit':
filters.append((name, str(value)))
limit = kwargs.pop("limit")
if limit:
filters.append(("limit", limit))
is_search = kwargs.pop("search", False)
if is_search:
if config.setting["use_adv_search_syntax"]:
query = kwargs["query"]
else:
value = _escape_lucene_query(value).strip().lower()
query = escape_lucene_query(kwargs["query"]).strip().lower()
filters.append(("dismax", 'true'))
else:
query = []
for name, value in kwargs.items():
value = escape_lucene_query(value).strip().lower()
if value:
query.append('%s:(%s)' % (name, value))
query = ' '.join(query)
if query:
filters.append(('query', ' '.join(query)))
filters.append(("query", query))
queryargs = {}
for name, value in filters:
value = QUrl.toPercentEncoding(unicode(value))

BIN
resources/images/loader.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

View File

@@ -21,14 +21,15 @@ def natsort_key(s):
return [ tryint(c) for c in re.split('(\d+)', s) ]
def find_files(topdir, directory, pattern):
def find_files(topdir, directory, patterns):
tdir = os.path.join(topdir, directory)
for root, dirs, files in os.walk(tdir):
for basename in files:
if fnmatch.fnmatch(basename, pattern):
filepath = os.path.join(root, basename)
filename = os.path.relpath(filepath, topdir)
yield filename
for pattern in patterns:
if fnmatch.fnmatch(basename, pattern):
filepath = os.path.join(root, basename)
filename = os.path.relpath(filepath, topdir)
yield filename
def main():
@@ -36,7 +37,7 @@ def main():
topdir = os.path.abspath(os.path.join(scriptdir, ".."))
resourcesdir = os.path.join(topdir, "resources")
qrcfile = os.path.join(resourcesdir, "picard.qrc")
images = [i for i in find_files(resourcesdir, 'images', '*.png')]
images = [i for i in find_files(resourcesdir, 'images', ['*.gif', '*.png'])]
newimages = 0
for filename in images:
filepath = os.path.join(resourcesdir, filename)

View File

@@ -46,6 +46,7 @@
<file>images/arrow.png</file>
<file>images/file-pending.png</file>
<file>images/file.png</file>
<file>images/loader.gif</file>
<file>images/match-50.png</file>
<file>images/match-60.png</file>
<file>images/match-70.png</file>