Improved Last.fm plugin.

This commit is contained in:
Lukáš Lalinský
2007-01-28 13:56:25 +01:00
parent bbb22795da
commit 99dc3e540f
4 changed files with 397 additions and 105 deletions

View File

@@ -1,105 +0,0 @@
# -*- coding: utf-8 -*-
PLUGIN_NAME = u'Last.fm'
PLUGIN_AUTHOR = u'Lukáš Lalinsky'
PLUGIN_DESCRIPTION = u''
from musicbrainz2.model import VARIOUS_ARTISTS_ID
from picard.metadata import register_album_metadata_processor, register_track_metadata_processor
from xml.dom.minidom import parse
import re
import urllib
# TODO: move this to an options page
MIN_TAG_COUNT = 15
JOIN_TAGS = None # use e.g. "/" to produce only one "genre" tag with "Tag1/Tag2"
IGNORE_TAGS = ["seen live"]
USE_TRACK_TAGS = True
USE_ARTIST_TAGS = True
USE_ARTIST_IMAGES = True
def get_text(root):
text = []
for node in root.childNodes:
if node.nodeType == node.TEXT_NODE:
text.append(node.data)
return "".join(text)
def get_tags(ws, url):
"""Get tags from an URL."""
try:
stream = ws.get_from_url(url)
except IOError:
return []
dom = parse(stream)
tags = []
for tag in dom.getElementsByTagName("toptags")[0].getElementsByTagName("tag"):
name = get_text(tag.getElementsByTagName("name")[0]).strip()
count = int(get_text(tag.getElementsByTagName("count")[0]).strip())
if count < MIN_TAG_COUNT:
break
tags.append(name)
stream.close()
return filter(lambda t: t not in IGNORE_TAGS, tags)
def get_track_tags(ws, artist, track):
"""Get track top tags."""
url = "http://ws.audioscrobbler.com/1.0/track/%s/%s/toptags.xml" % (urllib.quote(artist, ""), urllib.quote(track, ""))
return get_tags(ws, url)
def get_artist_tags(ws, artist):
"""Get artist top tags."""
url = "http://ws.audioscrobbler.com/1.0/artist/%s/toptags.xml" % (urllib.quote(artist, ""))
return get_tags(ws, url)
def get_artist_image(ws, artist):
"""Get the main artist image."""
url = "http://ws.audioscrobbler.com/ass/artistmetadata.php?%s" % (urllib.urlencode({"artist": artist}))
try:
stream = ws.get_from_url(url)
except IOError:
return None
res = stream.read()
stream.close()
res = res.split("\t")
if len(res) != 4:
return None
image_url = res[3][1:-1]
if not image_url.startswith("http://static.last.fm"):
return None
stream = ws.get_from_url(image_url)
data = stream.read()
stream.close()
return data
def process_album(tagger, metadata, release):
if USE_ARTIST_IMAGES and release.artist.id != VARIOUS_ARTISTS_ID:
artist = metadata["artist"].encode("utf-8")
if artist:
ws = tagger.get_web_service()
data = get_artist_image(ws, artist)
if data:
metadata.add("~artwork", ["image/jpeg", data])
def process_track(tagger, metadata, release, track):
if USE_TRACK_TAGS or USE_ARTIST_TAGS:
ws = tagger.get_web_service()
artist = metadata["artist"].encode("utf-8")
title = metadata["title"].encode("utf-8")
tags = []
if artist:
if USE_TRACK_TAGS:
tags = get_artist_tags(ws, artist)
# No tags for artist? Why trying track tags...
if not tags:
return
if title and USE_ARTIST_TAGS:
tags.extend(get_track_tags(ws, artist, title))
tags = list(set(tags))
if tags:
if JOIN_TAGS:
tags = JOIN_TAGS.join(tags)
metadata["genre"] = tags
register_track_metadata_processor(process_track)
register_album_metadata_processor(process_album)

View File

@@ -0,0 +1,159 @@
# -*- coding: utf-8 -*-
PLUGIN_NAME = u'Last.fm'
PLUGIN_AUTHOR = u'Lukáš Lalinsky'
PLUGIN_DESCRIPTION = u''
import re
import urllib
from PyQt4 import QtGui
from musicbrainz2.model import VARIOUS_ARTISTS_ID
from picard.metadata import register_album_metadata_processor, register_track_metadata_processor
from picard.ui.options import register_options_page, OptionsPage
from xml.dom.minidom import parse
from picard.config import BoolOption, IntOption, TextOption
from picard.plugins.lastfm.ui_options_lastfm import Ui_LastfmOptionsPage
# TODO: move this to an options page
JOIN_TAGS = None # use e.g. "/" to produce only one "genre" tag with "Tag1/Tag2"
TRANSLATE_TAGS = {
"hip hop": "hip-hop",
"synth-pop": "synthpop",
"electronica": "electronic",
}
TITLE_CASE = True
def get_text(root):
text = []
for node in root.childNodes:
if node.nodeType == node.TEXT_NODE:
text.append(node.data)
return "".join(text)
def get_tags(ws, url, min_usage, ignore):
"""Get tags from an URL."""
try:
stream = ws.get_from_url(url)
except IOError:
return []
dom = parse(stream)
tags = []
for tag in dom.getElementsByTagName("toptags")[0].getElementsByTagName("tag"):
name = get_text(tag.getElementsByTagName("name")[0]).strip()
count = int(get_text(tag.getElementsByTagName("count")[0]).strip())
if count < min_usage:
break
try: name = TRANSLATE_TAGS[name]
except KeyError: pass
tags.append(name.title())
stream.close()
return filter(lambda t: t not in ignore, tags)
def get_track_tags(ws, artist, track, min_usage, ignore):
"""Get track top tags."""
url = "http://ws.audioscrobbler.com/1.0/track/%s/%s/toptags.xml" % (urllib.quote(artist, ""), urllib.quote(track, ""))
return get_tags(ws, url, min_usage, ignore)
def get_artist_tags(ws, artist, min_usage, ignore):
"""Get artist top tags."""
url = "http://ws.audioscrobbler.com/1.0/artist/%s/toptags.xml" % (urllib.quote(artist, ""))
return get_tags(ws, url, min_usage, ignore)
def get_artist_image(ws, artist):
"""Get the main artist image."""
url = "http://ws.audioscrobbler.com/ass/artistmetadata.php?%s" % (urllib.urlencode({"artist": artist}))
try:
stream = ws.get_from_url(url)
except IOError:
return None
res = stream.read()
stream.close()
res = res.split("\t")
if len(res) != 4:
return None
image_url = res[3][1:-1]
if not image_url.startswith("http://static.last.fm"):
return None
stream = ws.get_from_url(image_url)
data = stream.read()
stream.close()
return data
def process_album(tagger, metadata, release):
if tagger.config.setting["lastfm_use_artist_images"] and release.artist.id != VARIOUS_ARTISTS_ID:
artist = metadata["artist"].encode("utf-8")
if artist:
ws = tagger.get_web_service()
data = get_artist_image(ws, artist)
if data:
metadata.add("~artwork", ["image/jpeg", data])
def process_track(tagger, metadata, release, track):
use_track_tags = tagger.config.setting["lastfm_use_track_tags"]
use_artist_tags = tagger.config.setting["lastfm_use_artist_tags"]
min_tag_usage = tagger.config.setting["lastfm_min_tag_usage"]
ignore_tags = tagger.config.setting["lastfm_ignore_tags"].split(",")
if use_track_tags or use_artist_tags:
ws = tagger.get_web_service()
artist = metadata["artist"].encode("utf-8")
title = metadata["title"].encode("utf-8")
tags = []
if artist:
if use_track_tags:
tags = get_artist_tags(ws, artist, min_tag_usage, ignore_tags)
# No tags for artist? Why trying track tags...
if not tags:
return
if title and use_artist_tags:
tags.extend(get_track_tags(ws, artist, title, min_tag_usage, ignore_tags))
tags = list(set(tags))
if tags:
if JOIN_TAGS:
tags = JOIN_TAGS.join(tags)
metadata["genre"] = tags
class LastfmOptionsPage(OptionsPage):
NAME = "lastfm"
TITLE = "Last.fm"
PARENT = "plugins"
options = [
BoolOption("setting", "lastfm_use_track_tags", False),
BoolOption("setting", "lastfm_use_artist_tags", False),
BoolOption("setting", "lastfm_use_artist_images", False),
IntOption("setting", "lastfm_min_tag_usage", 15),
TextOption("setting", "lastfm_ignore_tags", "seen live"),
]
def __init__(self, parent=None):
super(LastfmOptionsPage, self).__init__(parent)
self.ui = Ui_LastfmOptionsPage()
self.ui.setupUi(self)
def load(self):
self.ui.use_track_tags.setChecked(self.config.setting["lastfm_use_track_tags"])
self.ui.use_artist_tags.setChecked(self.config.setting["lastfm_use_artist_tags"])
self.ui.use_artist_images.setChecked(self.config.setting["lastfm_use_artist_images"])
self.ui.min_tag_usage.setValue(self.config.setting["lastfm_min_tag_usage"])
self.ui.ignore_tags.setText(self.config.setting["lastfm_ignore_tags"])
def save(self):
self.config.setting["lastfm_use_track_tags"] = self.ui.use_track_tags.isChecked()
self.config.setting["lastfm_use_artist_tags"] = self.ui.use_artist_tags.isChecked()
self.config.setting["lastfm_use_artist_images"] = self.ui.use_artist_images.isChecked()
self.config.setting["lastfm_min_tag_usage"] = self.ui.min_tag_usage.value()
self.config.setting["lastfm_ignore_tags"] = unicode(self.ui.ignore_tags.text())
register_track_metadata_processor(process_track)
register_album_metadata_processor(process_album)
register_options_page(LastfmOptionsPage)

View File

@@ -0,0 +1,139 @@
<ui version="4.0" >
<class>LastfmOptionsPage</class>
<widget class="QWidget" name="LastfmOptionsPage" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>281</width>
<height>305</height>
</rect>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QGroupBox" name="rename_files" >
<property name="title" >
<string>Last.fm</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>2</number>
</property>
<item>
<widget class="QCheckBox" name="use_track_tags" >
<property name="text" >
<string>Use track tags</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_artist_tags" >
<property name="text" >
<string>Use artist tags</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_artist_images" >
<property name="text" >
<string>Use artist images</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="rename_files_2" >
<property name="title" >
<string>Tags</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>2</number>
</property>
<item>
<widget class="QLabel" name="ignore_tags_2" >
<property name="text" >
<string>Ignore tags:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="ignore_tags" />
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QLabel" name="label_4" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>5</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string>Minimal tag usage:</string>
</property>
<property name="buddy" >
<cstring>min_tag_usage</cstring>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="min_tag_usage" >
<property name="suffix" >
<string> %</string>
</property>
<property name="maximum" >
<number>100</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<size>
<width>121</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<tabstops>
<tabstop>use_track_tags</tabstop>
<tabstop>ignore_tags</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,99 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'ui/options_lastfm.ui'
#
# Created: Sun Jan 28 13:39:17 2007
# by: PyQt4 UI code generator 4.1
#
# WARNING! All changes made in this file will be lost!
import sys
from PyQt4 import QtCore, QtGui
class Ui_LastfmOptionsPage(object):
def setupUi(self, LastfmOptionsPage):
LastfmOptionsPage.setObjectName("LastfmOptionsPage")
LastfmOptionsPage.resize(QtCore.QSize(QtCore.QRect(0,0,281,305).size()).expandedTo(LastfmOptionsPage.minimumSizeHint()))
self.vboxlayout = QtGui.QVBoxLayout(LastfmOptionsPage)
self.vboxlayout.setMargin(9)
self.vboxlayout.setSpacing(6)
self.vboxlayout.setObjectName("vboxlayout")
self.rename_files = QtGui.QGroupBox(LastfmOptionsPage)
self.rename_files.setObjectName("rename_files")
self.vboxlayout1 = QtGui.QVBoxLayout(self.rename_files)
self.vboxlayout1.setMargin(9)
self.vboxlayout1.setSpacing(2)
self.vboxlayout1.setObjectName("vboxlayout1")
self.use_track_tags = QtGui.QCheckBox(self.rename_files)
self.use_track_tags.setObjectName("use_track_tags")
self.vboxlayout1.addWidget(self.use_track_tags)
self.use_artist_tags = QtGui.QCheckBox(self.rename_files)
self.use_artist_tags.setObjectName("use_artist_tags")
self.vboxlayout1.addWidget(self.use_artist_tags)
self.use_artist_images = QtGui.QCheckBox(self.rename_files)
self.use_artist_images.setObjectName("use_artist_images")
self.vboxlayout1.addWidget(self.use_artist_images)
self.vboxlayout.addWidget(self.rename_files)
self.rename_files_2 = QtGui.QGroupBox(LastfmOptionsPage)
self.rename_files_2.setObjectName("rename_files_2")
self.vboxlayout2 = QtGui.QVBoxLayout(self.rename_files_2)
self.vboxlayout2.setMargin(9)
self.vboxlayout2.setSpacing(2)
self.vboxlayout2.setObjectName("vboxlayout2")
self.ignore_tags_2 = QtGui.QLabel(self.rename_files_2)
self.ignore_tags_2.setObjectName("ignore_tags_2")
self.vboxlayout2.addWidget(self.ignore_tags_2)
self.ignore_tags = QtGui.QLineEdit(self.rename_files_2)
self.ignore_tags.setObjectName("ignore_tags")
self.vboxlayout2.addWidget(self.ignore_tags)
self.hboxlayout = QtGui.QHBoxLayout()
self.hboxlayout.setMargin(0)
self.hboxlayout.setSpacing(6)
self.hboxlayout.setObjectName("hboxlayout")
self.label_4 = QtGui.QLabel(self.rename_files_2)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Policy(7),QtGui.QSizePolicy.Policy(5))
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth())
self.label_4.setSizePolicy(sizePolicy)
self.label_4.setObjectName("label_4")
self.hboxlayout.addWidget(self.label_4)
self.min_tag_usage = QtGui.QSpinBox(self.rename_files_2)
self.min_tag_usage.setMaximum(100)
self.min_tag_usage.setObjectName("min_tag_usage")
self.hboxlayout.addWidget(self.min_tag_usage)
self.vboxlayout2.addLayout(self.hboxlayout)
self.vboxlayout.addWidget(self.rename_files_2)
spacerItem = QtGui.QSpacerItem(121,20,QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Expanding)
self.vboxlayout.addItem(spacerItem)
self.label_4.setBuddy(self.min_tag_usage)
self.retranslateUi(LastfmOptionsPage)
QtCore.QMetaObject.connectSlotsByName(LastfmOptionsPage)
LastfmOptionsPage.setTabOrder(self.use_track_tags,self.ignore_tags)
def retranslateUi(self, LastfmOptionsPage):
self.rename_files.setTitle(_(u"Last.fm"))
self.use_track_tags.setText(_(u"Use track tags"))
self.use_artist_tags.setText(_(u"Use artist tags"))
self.use_artist_images.setText(_(u"Use artist images"))
self.rename_files_2.setTitle(_(u"Tags"))
self.ignore_tags_2.setText(_(u"Ignore tags:"))
self.label_4.setText(_(u"Minimal tag usage:"))
self.min_tag_usage.setSuffix(_(u" %"))