--- /dev/null
+#!/usr/bin/env python2.5
+import os
+from utils import UrllibWrapper
+import dbus, time
+import string
+import urllib
+
+
+LASTFM_APIKEY = "1e1d53528c86406757a6887addef0ace"
+BASE_LASTFM = "http://ws.audioscrobbler.com/2.0/?method=album.getinfo"
+
+
+BASE_MSN = "http://www.bing.com/images/search?q="
+MSN_MEDIUM = "+filterui:imagesize-medium"
+MSN_SMALL = "+filterui:imagesize-medium"
+MSN_SQUARE = "+filterui:aspect-square"
+MSN_PHOTO = "+filterui:photo-graphics"
+
+CACHE_LOCATION = os.path.join (os.getenv ("HOME"), ".cache", "mussorgsky")
+# LastFM:
+# http://www.lastfm.es/api/show?service=290
+#
+
+
+import threading
+class AADownloadThread (threading.Thread):
+
+ def __init__ (self, url, artist, album, counter):
+ threading.Thread.__init__ (self, target=self.grab_image, args=(url,))
+ self.counter = counter
+ self.artistName = artist.replace (" ", "_")
+ self.albumName = album.replace (" ", "_")
+ self.image_path = None
+ self.urllib_wrapper = UrllibWrapper ()
+
+ def grab_image (self, image_url):
+ print "Working", self.counter
+ image = self.urllib_wrapper.get_url (image_url)
+ if (image):
+ self.image_path = os.path.join (CACHE_LOCATION, self.artistName + self.albumName + str(self.counter))
+ self.urllib_wrapper.save_content_into_file (image, self.image_path)
+
+ def get_result (self):
+ return self.image_path
+
+
+
+class MussorgskyAlbumArt:
+
+ def __init__ (self):
+ bus = dbus.SessionBus ()
+
+ if (not os.path.exists (CACHE_LOCATION)):
+ os.makedirs (CACHE_LOCATION)
+
+ self.urllib_wrapper = UrllibWrapper ()
+
+ def get_possible_url (self, artist, album, amount=4):
+ results_page = self.__msn_images (artist, album)
+ return self.__get_url_from_msn_results_page (results_page)
+
+
+ def get_album_art (self, albumItem, force=False):
+ """
+ Save the first available result as the albumart for that item
+ """
+ filename = albumItem.get_aa().get_media_art_path ()
+ if (os.path.exists (filename) and not force):
+ print "Album art already there " + filename
+ return
+
+ results_page = self.__msn_images (albumItem.artist, albumItem.title)
+ for online_resource in self.__get_url_from_msn_results_page (results_page):
+ print "Trying:", online_resource
+ content = self.urllib_wrapper.get_url (online_resource)
+ if (content):
+ print "Saved on: %s " % (filename)
+ self.urllib_wrapper.save_content_into_file (content, filename)
+ albumItem.album_art = filename
+ break
+
+ def get_alternatives (self, artist, album, max_alternatives=4):
+ """
+ return a list of images in the local disk
+ """
+ results_page = self.__msn_images (artist, album)
+ return self.__process_results_page (results_page, artist, album, max_alternatives)
+
+ def get_alternatives_free_text (self, search_text, max_alternatives=4):
+ results_page = self.__msn_images_free_text (search_text)
+ return self.__process_results_page (results_page, max_alternatives)
+
+ def __process_results_page (self, results_page, artist, album, max_alternatives):
+ counter = 0
+ threads = []
+ for image_url in self.__get_url_from_msn_results_page (results_page):
+ if (not image_url):
+ # Some searches doesn't return anything at all!
+ break
+
+ if (counter >= max_alternatives):
+ break
+
+ t = AADownloadThread (image_url, artist, album, counter)
+ t.start ()
+ threads.append (t)
+ counter += 1
+
+ for t in threads:
+ t.join (5)
+ if (t.isAlive ()):
+ yield None
+ else:
+ yield t.get_result ()
+
+
+ def save_alternative (self, artist, album, img_path, thumb_path):
+ """
+ This is done now in the controller
+ """
+ if not os.path.exists (img_path) or not os.path.exists (thumb_path):
+ print "**** CRITICAL **** image in path", path, "doesn't exist!"
+ return (None, None)
+
+ filename = getCoverArtFileName (album)
+ thumbnail = getCoverArtThumbFileName (album)
+
+ os.rename (img_path, filename)
+ os.rename (thumb_path, thumbnail)
+
+ return (filename, thumbnail)
+
+ def reset_alternative (self, artist, album):
+
+ for filepath in [getCoverArtFileName (album),
+ getCoverArtThumbFileName (album)]:
+ if os.path.exists (filepath):
+ os.remove (filepath)
+
+ def __msn_images (self, artist, album):
+
+ good_artist = self.__clean_string_for_search (artist)
+ good_album = self.__clean_string_for_search (album)
+
+ if (good_album and good_artist):
+ full_try = BASE_MSN + good_album + "+" + good_artist + MSN_MEDIUM + MSN_SQUARE
+ print "Searching (album + artist): %s" % (full_try)
+ result = self.urllib_wrapper.get_url (full_try)
+ if (result and result.find ("no_results") == -1):
+ return result
+
+ if (album):
+ if (album.lower ().find ("greatest hit") != -1):
+ print "Ignoring '%s': too generic" % (album)
+ pass
+ else:
+ album_try = BASE_MSN + good_album + MSN_MEDIUM + MSN_SQUARE
+ print "Searching (album): %s" % (album_try)
+ result = self.urllib_wrapper.get_url (album_try)
+ if (result and result.find ("no_results") == -1):
+ return result
+
+ if (artist):
+ artist_try = BASE_MSN + good_artist + "+CD+music" + MSN_SMALL + MSN_SQUARE + MSN_PHOTO
+ print "Searching (artist CD): %s" % (artist_try)
+ result = self.urllib_wrapper.get_url (artist_try)
+ if (result and result.find ("no_results") == -1):
+ return result
+
+ return None
+
+ def __msn_images_free_text (self, search_text):
+ full_try = BASE_MSN + self.__clean_string_for_search (search_text) + MSN_MEDIUM + MSN_SQUARE
+ result = self.urllib_wrapper.get_url (full_try)
+ return result
+
+
+ def __get_url_from_msn_results_page (self, page):
+ if (not page):
+ return
+
+ current_option = None
+ starting_at = 0
+
+ # 500 is just a safe limit
+ for i in range (0, 500):
+ # Iterate until find a jpeg
+ start = page.find ("imgurl:"", starting_at)
+ if (start == -1):
+ yield None
+ end = page.find ("&", start + len ("imgurl:""))
+ current_option = page [start + len ("imgurl:""): end].replace ("amp;", "")
+ if (current_option.lower().endswith (".jpg") or
+ current_option.lower().endswith (".jpeg")):
+ yield current_option
+ starting_at = end
+
+
+ def __clean_string_for_search (self, text):
+ if (not text or len (text) < 1):
+ return None
+
+ bad_stuff = "_:?\\-~"
+ clean = text
+ for c in bad_stuff:
+ clean = clean.replace (c, " ")
+
+ clean.replace ("/", "%2F")
+ clean = clean.replace (" CD1", "").replace(" CD2", "")
+ return urllib.quote(clean)
+
+
+if __name__ == "__main__":
+ import sys
+ from optparse import OptionParser
+
+ parser = OptionParser()
+ parser.add_option ("-p", "--print", dest="print_paths",
+ action="store_true", default=True,
+ help="Print the destination paths")
+ parser.add_option ("-r", "--retrieve", dest="retrieve",
+ action="store_true", default=False,
+ help="Try to retrieve the online content")
+ parser.add_option ("-m", "--multiple", dest="multiple",
+ action="store_true", default=False,
+ help="Show more than one option")
+ parser.add_option ("-a", "--artist", dest="artist", type="string",
+ help="ARTIST to look for", metavar="ARTIST")
+ parser.add_option ("-b", "--album", dest="album", type="string",
+ help="ALBUM to look for", metavar="ALBUM")
+
+ (options, args) = parser.parse_args ()
+ print options
+ if (not options.artist and not options.album):
+ parser.print_help ()
+ sys.exit (-1)
+
+ if (options.multiple and options.retrieve):
+ print "Multiple and retrieve are incompatible"
+ parser.print_help ()
+ sys.exit (-1)
+
+ if options.print_paths and not options.retrieve:
+ print "Album art:", getCoverArtFileName (options.album)
+ print "Thumbnail:", getCoverArtThumbFileName (options.album)
+
+ if options.retrieve:
+ maa = MussorgskyAlbumArt ()
+ maa.get_album_art (options.artist, options.album)
+
+ if options.multiple:
+ start = time.time ()
+ maa = MussorgskyAlbumArt ()
+ for (img, thumb) in maa.get_alternatives (options.artist, options.album, 5):
+ print img
+ print thumb
+ end = time.time ()
+ print end - start
--- /dev/null
+# -*- coding: utf-8 -*-
+from PySide.QtCore import QRegExp
+import re
+import hashlib
+import os
+
+MULTIWHITE = re.compile ("\s\s+")
+
+MEDIAART_PATH = os.path.join (os.environ["HOME"], ".cache", "media-art")
+GENERATED_PATH = os.path.join (os.environ["HOME"], ".cache", "music-suite", "generated")
+THUMB_PATH = os.path.join (os.environ["HOME"], ".cache", "music-suite", "thumbnails")
+
+#
+# Python translation of :
+# https://qt.gitorious.org/qt/qt/blobs/d12681a4cf1227d0e92fc7cf12aa3977e6ffe3fe/src/corelib/tools/qhash.cpp#line76
+#
+def qhash (inputstr):
+
+ if isinstance (inputstr, str):
+ instr = inputstr
+ elif isinstance (inputstr, unicode):
+ instr = inputstr.encode ("utf8")
+ else:
+ return -1
+
+ h = 0x00000000
+ for i in range (0, len (instr)):
+ h = (h << 4) + ord(instr[i])
+ h ^= (h & 0xf0000000) >> 23
+ h &= 0x0fffffff
+
+ return h
+
+class AlbumArt:
+ def __init__ (self, album, artist, flavor="common", aaType="album"):
+ self.album = album
+ self.albumMD5 = self.__get_md5 (album)
+ self.artist = artist
+ self.artistMD5 = self.__get_md5 (artist)
+ self.flavor = "common"
+ self.aaType = "album"
+
+ def get_media_art_path (self):
+ """
+ The raw media-art downloaded or taken from the folder with the music files
+ """
+ filename = "album-" + self.artistMD5 + "-" + self.albumMD5 + ".jpeg"
+ return os.path.join (MEDIAART_PATH, filename)
+
+ def get_generated (self):
+ filename = str (qhash (self.album + self.artist + self.flavor + self.aaType)) + ".png"
+ return os.path.join (GENERATED_PATH, self.flavor, filename)
+
+ def get_thumbnail (self):
+ filename = str (qhash (self.album + self.artist + self.flavor + self.aaType)) + ".jpeg"
+ return os.path.join (THUMB_PATH, self.flavor, filename)
+
+
+
+ def __get_md5 (self, something):
+ if not something or len (something) == 0:
+ something = " "
+ else:
+ # This can very probably be done with the native python regexp
+ blocks = QRegExp ("(\\([^\\)]*\\))" +
+ "|(\\[[^\\]]*\\])" +
+ "|(\\{[^\\}]*\\})" +
+ "|(\\<[^\\>]*\\>)")
+ something = blocks.replace (something, "")
+
+ evilchars = QRegExp ("[\\(\\)\\_\\{\\}\\[\\]\\!\\@\\#\\$\\^\\&\\*\\+\\=\\|\\\\\\/\\\"\\'\\?\\<\\>\\~\\`]+")
+ something = evilchars.replace (something, "")
+
+
+ something = MULTIWHITE.sub (" ", something)
+ something = something.strip ()
+
+ md5 = hashlib.md5 (something.lower ().encode ("utf-8"))
+ return md5.hexdigest ()
+
+
+if __name__ == "__main__":
+
+ aa = AlbumArt ("Breakfast in america", "Björk")
+ print "Source: \"" + aa.get_media_art_path () + "\""
+ print "Generated: \"" + aa.get_generated () + "\""
+ print "Thumbnail: \"" + aa.get_thumbnail () + "\""
+
--- /dev/null
+# -*- coding: utf-8 -*-
+import os
+import sys
+import time
+from PySide import QtCore
+from PySide import QtGui
+from PySide import QtDeclarative
+
+from albumArt import AlbumArt
+
+class AlbumItem (QtCore.QObject):
+
+ def __init__ (self, title, artist):
+ QtCore.QObject.__init__(self)
+ self._title = title
+ self._artist = artist
+ self.aa = AlbumArt (self._title, self._artist)
+ self.require_download = False
+ if os.path.exists (self.aa.get_media_art_path ()):
+ self._album_art = self.aa.get_media_art_path ()
+ elif os.path.exists (self.aa.get_generated ()):
+ self._album_art = self.aa.get_generated ()
+ self.require_download = True
+ else:
+ self.require_download = True
+ self._album_art = "images/blank_record.png"
+
+ def _title (self):
+ return self._title
+
+ def _artist (self):
+ return self._artist
+
+ def _albumArt (self):
+ return self._album_art
+
+ def _setAlbumArt (self, path):
+ print "Setting the new album art to", path
+ self.require_download = False
+ self._album_art = path
+ self.album_art_changed.emit ()
+
+ def resetAlbumArt (self):
+ print "Reset album art!"
+ #self._album_art = None
+ counter = 0
+ while counter < 3:
+ if os.path.exists (self.aa.get_generated ()):
+ break
+ time.sleep (1)
+ counter += 1
+
+ if os.path.exists (self.aa.get_generated ()):
+ print "Using the generated"
+ self._album_art = self.aa.get_generated ()
+ self.require_download = True
+ else:
+ print "Setting to none (", self.aa.get_generated (), " doesn't exist)"
+ self.require_download = True
+ self._album_art = "images/blank_record.png"
+ self.album_art_changed.emit ()
+
+ def get_aa (self):
+ return self.aa
+
+ prop_changed = QtCore.Signal ()
+ album_art_changed = QtCore.Signal ()
+
+ title = QtCore.Property (unicode, _title, notify=prop_changed)
+ artist = QtCore.Property (unicode, _artist, notify=prop_changed)
+ album_art = QtCore.Property (unicode,
+ _albumArt,
+ _setAlbumArt,
+ notify=album_art_changed)
+
--- /dev/null
+# -*- coding: utf-8 -*-
+import os
+import sys
+from PySide import QtCore
+from PySide import QtGui
+from PySide import QtDeclarative
+
+from albumItem import AlbumItem
+
+class AlbumModel (QtCore.QAbstractListModel):
+ COLUMNS = ('album', )
+
+ def __init__ (self, data):
+ QtCore.QAbstractListModel.__init__ (self)
+ self._albums = data
+ self.setRoleNames (dict(enumerate(AlbumModel.COLUMNS)))
+
+ def rowCount (self, parent=QtCore.QModelIndex()):
+ return len (self._albums)
+
+ def data (self, index, role):
+ if index.isValid () and role == AlbumModel.COLUMNS.index ('album'):
+ return self._albums[index.row ()]
+ return None
+
+ def getAlbumInRow (self, row):
+ assert row >= 0 and row < len (self._albums)
+ return self._albums [row]
+
+ def updateThumb (self, row, url):
+ print "Changing album_art from",self._albums[row].album_art, "to:", url
+ assert row >= 0 and row < len (self._albums)
+ self._albums[row].album_art = url
+
+ def get_albums (self):
+ return self._albums
--- /dev/null
+# -*- coding: utf-8 -*-
+import os
+import sys
+from PySide import QtCore
+from PySide import QtGui
+from PySide import QtDeclarative
+
+from aa_search import MussorgskyAlbumArt
+
+try:
+ from tracker_backend_gi import TrackerBackendGI as TrackerBackend
+except ImportError:
+ from tracker_backend_dbus import TrackerBackendDBus as TrackerBackend
+
+from albumItem import AlbumItem
+from coverModel import CoversModel
+
+class DownloadThread (QtCore.QThread):
+
+ def __init__ (self, model, album):
+ QtCore.QThread.__init__ (self)
+ self.downloader = MussorgskyAlbumArt ()
+ self.model = model
+ self.album = album
+
+ def run (self):
+ print "Running the thread"
+ MAX_OPTIONS = 4
+ counter = 0
+ for img in self.downloader.get_alternatives (self.album.artist,
+ self.album.title, MAX_OPTIONS):
+ if counter >= MAX_OPTIONS:
+ break
+
+ self.model.updateData (counter, img)
+ counter += 1
+
+
+class MassiveDownloadsThread (QtCore.QThread):
+
+ def __init__ (self, albumModel):
+ QtCore.QThread.__init__ (self)
+ self.downloader = MussorgskyAlbumArt ()
+ self.albumModel = albumModel
+
+ def run (self):
+ print "Download one cover per-album in a thread"
+ import time
+ time.sleep (4)
+ for albumItem in self.albumModel.get_albums ():
+ if albumItem.require_download:
+ self.downloader.get_album_art (albumItem)
+
+
+class MussorgskyController (QtCore.QObject):
+
+ def __init__ (self, rootContext):
+ QtCore.QObject.__init__ (self)
+ self.download = None
+ self.ctx = rootContext
+ self.tracker = TrackerBackend ()
+ self.__is_downloading = False
+
+ def _is_downloading (self):
+ return self.__is_downloading
+
+ def _set_is_downloading (self, value):
+ if (value != self.__is_downloading):
+ self.__is_downloading = value
+ self.is_downloading_changed.emit ()
+
+ is_downloading_changed = QtCore.Signal ()
+ is_downloading = QtCore.Property (bool, _is_downloading, notify=is_downloading_changed)
+
+ @QtCore.Slot (QtCore.QObject)
+ def download_all (self, albumModel):
+ self._set_is_downloading (True)
+ self.download_all_thread = MassiveDownloadsThread (albumModel)
+ self.download_all_thread.finished.connect (self.download_all_finished)
+ self.download_all_thread.start ()
+ print "now we are downloading..."
+
+ @QtCore.Slot ()
+ def download_all_finished (self):
+ self._set_is_downloading (False)
+
+
+ @QtCore.Slot (QtCore.QObject)
+ def get_options_for (self, albumItem):
+ print "Getting options for", albumItem.title
+ m = CoversModel (albumItem)
+ print m.rowCount ()
+ self.ctx.setContextProperty ("coversModel", m)
+ self.download = DownloadThread (m, albumItem)
+ self.download.start ()
+
+ @QtCore.Slot (QtCore.QObject, int)
+ def save_option_for (self, coversModel, index):
+ print "Saving option", index
+ coverItem = coversModel.getData (index)
+ coverItem.save (coversModel.albumItem.get_aa ().get_media_art_path ())
+ # Update the main model. Is this enough?
+ if not coverItem.initialImage and not coverItem.deleteAction:
+ coversModel.albumItem.album_art = None
+ coversModel.albumItem.album_art = coversModel.albumItem.get_aa().get_media_art_path ()
+ elif coverItem.deleteAction:
+ coversModel.albumItem.resetAlbumArt ()
+
+ coversModel.cleanCache ()
+
+
+ @QtCore.Slot ()
+ def stop_pending_jobs (self):
+ if self.download :
+ self.download.quit ()
+
+ def get_all_albums (self):
+ """
+ Return a list of AlbumItem objects to build the model
+ This is not called from QML, no need to make it a slot
+ """
+ results = []
+ for album_title, album_artist in self.tracker.get_all_albums ():
+ results.append (AlbumItem (album_title, album_artist))
+ return results
+
--- /dev/null
+# -*- coding: utf-8 -*-
+import os
+import sys
+from PySide import QtCore
+from PySide import QtGui
+from PySide import QtDeclarative
+
+
+class CoverItem (QtCore.QObject):
+
+ def __init__ (self, image, initialImage=False, deleteAction=False):
+ QtCore.QObject.__init__(self)
+ self.initialImage = initialImage
+ self.deleteAction = deleteAction
+ self._url = image
+
+ def _url (self):
+ return self._url
+
+ def _setUrl (self, url):
+ self._url = url
+ self.url_changed.emit ()
+
+ def save (self, destination):
+ if self.initialImage:
+ print " -> No changes"
+ return
+
+ if self.deleteAction:
+ print " -> Remove the album art"
+ if os.path.exists (destination):
+ os.remove (destination)
+ return
+
+ print " -> Saving:", self.url, "to", destination
+ os.rename (self.url, destination)
+
+
+ def remove (self):
+ print "Removing", self.url
+ if os.path.exists (self.url):
+ os.remove (self.url)
+
+
+ url_changed = QtCore.Signal ()
+ url = QtCore.Property (unicode, _url, _setUrl, notify=url_changed)
--- /dev/null
+# -*- coding: utf-8 -*-
+import os
+import sys
+from PySide import QtCore
+from PySide import QtGui
+from PySide import QtDeclarative
+
+from coverItem import CoverItem
+from albumItem import AlbumItem
+
+class CoversModel (QtCore.QAbstractListModel):
+ COLUMNS = ('cover', )
+
+ def __init__ (self, albumItem):
+ QtCore.QAbstractListModel.__init__ (self)
+ self.albumItem = albumItem
+
+ self.startOffset = 2
+ self._alternatives = [
+ CoverItem (self.albumItem.album_art, initialImage=True),
+ CoverItem ("images/blank_record.png", deleteAction = True),
+ CoverItem (None),
+ CoverItem (None),
+ CoverItem (None),
+ CoverItem (None)
+ ]
+
+ self.setRoleNames (dict(enumerate(CoversModel.COLUMNS)))
+
+ def rowCount (self, parent=QtCore.QModelIndex()):
+ return len (self._alternatives)
+
+ def data (self, index, role):
+ if index.isValid () and role == CoversModel.COLUMNS.index ('cover'):
+ return self._alternatives[index.row ()]
+ return None
+
+ def updateData (self, row, url):
+ position = row + self.startOffset
+ assert position >= 0 and position < len (self._alternatives)
+ self._alternatives[position].url = url
+
+ def getData (self, row):
+ assert row >= 0 and row < len (self._alternatives)
+ return self._alternatives[row]
+
+ def cleanCache (self):
+ """
+ The model contains the urls of the downloaded alternatives.
+ This method removes those files. The selected image should have already been copied
+ to its final location.
+ """
+ for i in range (self.startOffset, len (self._alternatives)):
+ # This url can be None if the image didn't finish downloading
+ if self._alternatives[i].url and os.path.exists (self._alternatives[i].url):
+ os.remove (self._alternatives[i].url)
--- /dev/null
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import sys
+from PySide.QtCore import *
+from PySide.QtGui import *
+from PySide.QtDeclarative import QDeclarativeView
+
+from albumModel import AlbumModel
+from controller import MussorgskyController
+
+# Create Qt application and the QDeclarative view
+app = QApplication(sys.argv)
+view = QDeclarativeView()
+
+controller = MussorgskyController (view.rootContext ())
+
+#from albumItem import AlbumItem
+#MOCK_DATA = [AlbumItem (u"x", u"y") for i in xrange (0, 100)]
+#albumModel = AlbumModel (MOCK_DATA)
+
+albumModel = AlbumModel (controller.get_all_albums())
+print "Model with", albumModel.rowCount(), "rows"
+
+#from coverModel import CoversModel
+#coverModel = CoversModel ()
+
+rc = view.rootContext ()
+rc.setContextProperty ('albumModel', albumModel)
+rc.setContextProperty ('missionControl', controller)
+#rc.setContextProperty ('coversModel', coverModel)
+
+# Create an URL to the QML file
+#url = QUrl('view.qml')
+url = QUrl ("../../ui/main.qml")
+# Set the QML file and show
+view.setSource(url)
+view.engine().quit.connect (app.quit)
+
+view.show()
+
+
+# Enter Qt main loop
+sys.exit(app.exec_())
+++ /dev/null
-#!/usr/bin/env python2.5
-import os
-from utils import UrllibWrapper
-import dbus, time
-import string
-import urllib
-
-
-LASTFM_APIKEY = "1e1d53528c86406757a6887addef0ace"
-BASE_LASTFM = "http://ws.audioscrobbler.com/2.0/?method=album.getinfo"
-
-
-BASE_MSN = "http://www.bing.com/images/search?q="
-MSN_MEDIUM = "+filterui:imagesize-medium"
-MSN_SMALL = "+filterui:imagesize-medium"
-MSN_SQUARE = "+filterui:aspect-square"
-MSN_PHOTO = "+filterui:photo-graphics"
-
-CACHE_LOCATION = os.path.join (os.getenv ("HOME"), ".cache", "mussorgsky")
-# LastFM:
-# http://www.lastfm.es/api/show?service=290
-#
-
-
-import threading
-class AADownloadThread (threading.Thread):
-
- def __init__ (self, url, artist, album, counter):
- threading.Thread.__init__ (self, target=self.grab_image, args=(url,))
- self.counter = counter
- self.artistName = artist.replace (" ", "_")
- self.albumName = album.replace (" ", "_")
- self.image_path = None
- self.urllib_wrapper = UrllibWrapper ()
-
- def grab_image (self, image_url):
- print "Working", self.counter
- image = self.urllib_wrapper.get_url (image_url)
- if (image):
- self.image_path = os.path.join (CACHE_LOCATION, self.artistName + self.albumName + str(self.counter))
- self.urllib_wrapper.save_content_into_file (image, self.image_path)
-
- def get_result (self):
- return self.image_path
-
-
-
-class MussorgskyAlbumArt:
-
- def __init__ (self):
- bus = dbus.SessionBus ()
-
- if (not os.path.exists (CACHE_LOCATION)):
- os.makedirs (CACHE_LOCATION)
-
- self.urllib_wrapper = UrllibWrapper ()
-
- def get_possible_url (self, artist, album, amount=4):
- results_page = self.__msn_images (artist, album)
- return self.__get_url_from_msn_results_page (results_page)
-
-
- def get_album_art (self, albumItem, force=False):
- """
- Save the first available result as the albumart for that item
- """
- filename = albumItem.get_aa().get_media_art_path ()
- if (os.path.exists (filename) and not force):
- print "Album art already there " + filename
- return
-
- results_page = self.__msn_images (albumItem.artist, albumItem.title)
- for online_resource in self.__get_url_from_msn_results_page (results_page):
- print "Trying:", online_resource
- content = self.urllib_wrapper.get_url (online_resource)
- if (content):
- print "Saved on: %s " % (filename)
- self.urllib_wrapper.save_content_into_file (content, filename)
- albumItem.album_art = filename
- break
-
- def get_alternatives (self, artist, album, max_alternatives=4):
- """
- return a list of images in the local disk
- """
- results_page = self.__msn_images (artist, album)
- return self.__process_results_page (results_page, artist, album, max_alternatives)
-
- def get_alternatives_free_text (self, search_text, max_alternatives=4):
- results_page = self.__msn_images_free_text (search_text)
- return self.__process_results_page (results_page, max_alternatives)
-
- def __process_results_page (self, results_page, artist, album, max_alternatives):
- counter = 0
- threads = []
- for image_url in self.__get_url_from_msn_results_page (results_page):
- if (not image_url):
- # Some searches doesn't return anything at all!
- break
-
- if (counter >= max_alternatives):
- break
-
- t = AADownloadThread (image_url, artist, album, counter)
- t.start ()
- threads.append (t)
- counter += 1
-
- for t in threads:
- t.join (5)
- if (t.isAlive ()):
- yield None
- else:
- yield t.get_result ()
-
-
- def save_alternative (self, artist, album, img_path, thumb_path):
- """
- This is done now in the controller
- """
- if not os.path.exists (img_path) or not os.path.exists (thumb_path):
- print "**** CRITICAL **** image in path", path, "doesn't exist!"
- return (None, None)
-
- filename = getCoverArtFileName (album)
- thumbnail = getCoverArtThumbFileName (album)
-
- os.rename (img_path, filename)
- os.rename (thumb_path, thumbnail)
-
- return (filename, thumbnail)
-
- def reset_alternative (self, artist, album):
-
- for filepath in [getCoverArtFileName (album),
- getCoverArtThumbFileName (album)]:
- if os.path.exists (filepath):
- os.remove (filepath)
-
- def __msn_images (self, artist, album):
-
- good_artist = self.__clean_string_for_search (artist)
- good_album = self.__clean_string_for_search (album)
-
- if (good_album and good_artist):
- full_try = BASE_MSN + good_album + "+" + good_artist + MSN_MEDIUM + MSN_SQUARE
- print "Searching (album + artist): %s" % (full_try)
- result = self.urllib_wrapper.get_url (full_try)
- if (result and result.find ("no_results") == -1):
- return result
-
- if (album):
- if (album.lower ().find ("greatest hit") != -1):
- print "Ignoring '%s': too generic" % (album)
- pass
- else:
- album_try = BASE_MSN + good_album + MSN_MEDIUM + MSN_SQUARE
- print "Searching (album): %s" % (album_try)
- result = self.urllib_wrapper.get_url (album_try)
- if (result and result.find ("no_results") == -1):
- return result
-
- if (artist):
- artist_try = BASE_MSN + good_artist + "+CD+music" + MSN_SMALL + MSN_SQUARE + MSN_PHOTO
- print "Searching (artist CD): %s" % (artist_try)
- result = self.urllib_wrapper.get_url (artist_try)
- if (result and result.find ("no_results") == -1):
- return result
-
- return None
-
- def __msn_images_free_text (self, search_text):
- full_try = BASE_MSN + self.__clean_string_for_search (search_text) + MSN_MEDIUM + MSN_SQUARE
- result = self.urllib_wrapper.get_url (full_try)
- return result
-
-
- def __get_url_from_msn_results_page (self, page):
- if (not page):
- return
-
- current_option = None
- starting_at = 0
-
- # 500 is just a safe limit
- for i in range (0, 500):
- # Iterate until find a jpeg
- start = page.find ("imgurl:"", starting_at)
- if (start == -1):
- yield None
- end = page.find ("&", start + len ("imgurl:""))
- current_option = page [start + len ("imgurl:""): end].replace ("amp;", "")
- if (current_option.lower().endswith (".jpg") or
- current_option.lower().endswith (".jpeg")):
- yield current_option
- starting_at = end
-
-
- def __clean_string_for_search (self, text):
- if (not text or len (text) < 1):
- return None
-
- bad_stuff = "_:?\\-~"
- clean = text
- for c in bad_stuff:
- clean = clean.replace (c, " ")
-
- clean.replace ("/", "%2F")
- clean = clean.replace (" CD1", "").replace(" CD2", "")
- return urllib.quote(clean)
-
-
-if __name__ == "__main__":
- import sys
- from optparse import OptionParser
-
- parser = OptionParser()
- parser.add_option ("-p", "--print", dest="print_paths",
- action="store_true", default=True,
- help="Print the destination paths")
- parser.add_option ("-r", "--retrieve", dest="retrieve",
- action="store_true", default=False,
- help="Try to retrieve the online content")
- parser.add_option ("-m", "--multiple", dest="multiple",
- action="store_true", default=False,
- help="Show more than one option")
- parser.add_option ("-a", "--artist", dest="artist", type="string",
- help="ARTIST to look for", metavar="ARTIST")
- parser.add_option ("-b", "--album", dest="album", type="string",
- help="ALBUM to look for", metavar="ALBUM")
-
- (options, args) = parser.parse_args ()
- print options
- if (not options.artist and not options.album):
- parser.print_help ()
- sys.exit (-1)
-
- if (options.multiple and options.retrieve):
- print "Multiple and retrieve are incompatible"
- parser.print_help ()
- sys.exit (-1)
-
- if options.print_paths and not options.retrieve:
- print "Album art:", getCoverArtFileName (options.album)
- print "Thumbnail:", getCoverArtThumbFileName (options.album)
-
- if options.retrieve:
- maa = MussorgskyAlbumArt ()
- maa.get_album_art (options.artist, options.album)
-
- if options.multiple:
- start = time.time ()
- maa = MussorgskyAlbumArt ()
- for (img, thumb) in maa.get_alternatives (options.artist, options.album, 5):
- print img
- print thumb
- end = time.time ()
- print end - start
+++ /dev/null
-# -*- coding: utf-8 -*-
-from PySide.QtCore import QRegExp
-import re
-import hashlib
-import os
-
-MULTIWHITE = re.compile ("\s\s+")
-
-MEDIAART_PATH = os.path.join (os.environ["HOME"], ".cache", "media-art")
-GENERATED_PATH = os.path.join (os.environ["HOME"], ".cache", "music-suite", "generated")
-THUMB_PATH = os.path.join (os.environ["HOME"], ".cache", "music-suite", "thumbnails")
-
-#
-# Python translation of :
-# https://qt.gitorious.org/qt/qt/blobs/d12681a4cf1227d0e92fc7cf12aa3977e6ffe3fe/src/corelib/tools/qhash.cpp#line76
-#
-def qhash (inputstr):
-
- if isinstance (inputstr, str):
- instr = inputstr
- elif isinstance (inputstr, unicode):
- instr = inputstr.encode ("utf8")
- else:
- return -1
-
- h = 0x00000000
- for i in range (0, len (instr)):
- h = (h << 4) + ord(instr[i])
- h ^= (h & 0xf0000000) >> 23
- h &= 0x0fffffff
-
- return h
-
-class AlbumArt:
- def __init__ (self, album, artist, flavor="common", aaType="album"):
- self.album = album
- self.albumMD5 = self.__get_md5 (album)
- self.artist = artist
- self.artistMD5 = self.__get_md5 (artist)
- self.flavor = "common"
- self.aaType = "album"
-
- def get_media_art_path (self):
- """
- The raw media-art downloaded or taken from the folder with the music files
- """
- filename = "album-" + self.artistMD5 + "-" + self.albumMD5 + ".jpeg"
- return os.path.join (MEDIAART_PATH, filename)
-
- def get_generated (self):
- filename = str (qhash (self.album + self.artist + self.flavor + self.aaType)) + ".png"
- return os.path.join (GENERATED_PATH, self.flavor, filename)
-
- def get_thumbnail (self):
- filename = str (qhash (self.album + self.artist + self.flavor + self.aaType)) + ".jpeg"
- return os.path.join (THUMB_PATH, self.flavor, filename)
-
-
-
- def __get_md5 (self, something):
- if not something or len (something) == 0:
- something = " "
- else:
- # This can very probably be done with the native python regexp
- blocks = QRegExp ("(\\([^\\)]*\\))" +
- "|(\\[[^\\]]*\\])" +
- "|(\\{[^\\}]*\\})" +
- "|(\\<[^\\>]*\\>)")
- something = blocks.replace (something, "")
-
- evilchars = QRegExp ("[\\(\\)\\_\\{\\}\\[\\]\\!\\@\\#\\$\\^\\&\\*\\+\\=\\|\\\\\\/\\\"\\'\\?\\<\\>\\~\\`]+")
- something = evilchars.replace (something, "")
-
-
- something = MULTIWHITE.sub (" ", something)
- something = something.strip ()
-
- md5 = hashlib.md5 (something.lower ().encode ("utf-8"))
- return md5.hexdigest ()
-
-
-if __name__ == "__main__":
-
- aa = AlbumArt ("Breakfast in america", "Björk")
- print "Source: \"" + aa.get_media_art_path () + "\""
- print "Generated: \"" + aa.get_generated () + "\""
- print "Thumbnail: \"" + aa.get_thumbnail () + "\""
-
+++ /dev/null
-# -*- coding: utf-8 -*-
-import os
-import sys
-import time
-from PySide import QtCore
-from PySide import QtGui
-from PySide import QtDeclarative
-
-from albumArt import AlbumArt
-
-class AlbumItem (QtCore.QObject):
-
- def __init__ (self, title, artist):
- QtCore.QObject.__init__(self)
- self._title = title
- self._artist = artist
- self.aa = AlbumArt (self._title, self._artist)
- self.require_download = False
- if os.path.exists (self.aa.get_media_art_path ()):
- self._album_art = self.aa.get_media_art_path ()
- elif os.path.exists (self.aa.get_generated ()):
- self._album_art = self.aa.get_generated ()
- self.require_download = True
- else:
- self.require_download = True
- self._album_art = "images/blank_record.png"
-
- def _title (self):
- return self._title
-
- def _artist (self):
- return self._artist
-
- def _albumArt (self):
- return self._album_art
-
- def _setAlbumArt (self, path):
- print "Setting the new album art to", path
- self.require_download = False
- self._album_art = path
- self.album_art_changed.emit ()
-
- def resetAlbumArt (self):
- print "Reset album art!"
- #self._album_art = None
- counter = 0
- while counter < 3:
- if os.path.exists (self.aa.get_generated ()):
- break
- time.sleep (1)
- counter += 1
-
- if os.path.exists (self.aa.get_generated ()):
- print "Using the generated"
- self._album_art = self.aa.get_generated ()
- self.require_download = True
- else:
- print "Setting to none (", self.aa.get_generated (), " doesn't exist)"
- self.require_download = True
- self._album_art = "images/blank_record.png"
- self.album_art_changed.emit ()
-
- def get_aa (self):
- return self.aa
-
- prop_changed = QtCore.Signal ()
- album_art_changed = QtCore.Signal ()
-
- title = QtCore.Property (unicode, _title, notify=prop_changed)
- artist = QtCore.Property (unicode, _artist, notify=prop_changed)
- album_art = QtCore.Property (unicode,
- _albumArt,
- _setAlbumArt,
- notify=album_art_changed)
-
+++ /dev/null
-# -*- coding: utf-8 -*-
-import os
-import sys
-from PySide import QtCore
-from PySide import QtGui
-from PySide import QtDeclarative
-
-from albumItem import AlbumItem
-
-class AlbumModel (QtCore.QAbstractListModel):
- COLUMNS = ('album', )
-
- def __init__ (self, data):
- QtCore.QAbstractListModel.__init__ (self)
- self._albums = data
- self.setRoleNames (dict(enumerate(AlbumModel.COLUMNS)))
-
- def rowCount (self, parent=QtCore.QModelIndex()):
- return len (self._albums)
-
- def data (self, index, role):
- if index.isValid () and role == AlbumModel.COLUMNS.index ('album'):
- return self._albums[index.row ()]
- return None
-
- def getAlbumInRow (self, row):
- assert row >= 0 and row < len (self._albums)
- return self._albums [row]
-
- def updateThumb (self, row, url):
- print "Changing album_art from",self._albums[row].album_art, "to:", url
- assert row >= 0 and row < len (self._albums)
- self._albums[row].album_art = url
-
- def get_albums (self):
- return self._albums
+++ /dev/null
-# -*- coding: utf-8 -*-
-import os
-import sys
-from PySide import QtCore
-from PySide import QtGui
-from PySide import QtDeclarative
-
-from aa_search import MussorgskyAlbumArt
-
-try:
- from tracker_backend_gi import TrackerBackendGI as TrackerBackend
-except ImportError:
- from tracker_backend_dbus import TrackerBackendDBus as TrackerBackend
-
-from albumItem import AlbumItem
-from coverModel import CoversModel
-
-class DownloadThread (QtCore.QThread):
-
- def __init__ (self, model, album):
- QtCore.QThread.__init__ (self)
- self.downloader = MussorgskyAlbumArt ()
- self.model = model
- self.album = album
-
- def run (self):
- print "Running the thread"
- MAX_OPTIONS = 4
- counter = 0
- for img in self.downloader.get_alternatives (self.album.artist,
- self.album.title, MAX_OPTIONS):
- if counter >= MAX_OPTIONS:
- break
-
- self.model.updateData (counter, img)
- counter += 1
-
-
-class MassiveDownloadsThread (QtCore.QThread):
-
- def __init__ (self, albumModel):
- QtCore.QThread.__init__ (self)
- self.downloader = MussorgskyAlbumArt ()
- self.albumModel = albumModel
-
- def run (self):
- print "Download one cover per-album in a thread"
- import time
- time.sleep (4)
- for albumItem in self.albumModel.get_albums ():
- if albumItem.require_download:
- self.downloader.get_album_art (albumItem)
-
-
-class MussorgskyController (QtCore.QObject):
-
- def __init__ (self, rootContext):
- QtCore.QObject.__init__ (self)
- self.download = None
- self.ctx = rootContext
- self.tracker = TrackerBackend ()
- self.__is_downloading = False
-
- def _is_downloading (self):
- return self.__is_downloading
-
- def _set_is_downloading (self, value):
- if (value != self.__is_downloading):
- self.__is_downloading = value
- self.is_downloading_changed.emit ()
-
- is_downloading_changed = QtCore.Signal ()
- is_downloading = QtCore.Property (bool, _is_downloading, notify=is_downloading_changed)
-
- @QtCore.Slot (QtCore.QObject)
- def download_all (self, albumModel):
- self._set_is_downloading (True)
- self.download_all_thread = MassiveDownloadsThread (albumModel)
- self.download_all_thread.finished.connect (self.download_all_finished)
- self.download_all_thread.start ()
- print "now we are downloading..."
-
- @QtCore.Slot ()
- def download_all_finished (self):
- self._set_is_downloading (False)
-
-
- @QtCore.Slot (QtCore.QObject)
- def get_options_for (self, albumItem):
- print "Getting options for", albumItem.title
- m = CoversModel (albumItem)
- print m.rowCount ()
- self.ctx.setContextProperty ("coversModel", m)
- self.download = DownloadThread (m, albumItem)
- self.download.start ()
-
- @QtCore.Slot (QtCore.QObject, int)
- def save_option_for (self, coversModel, index):
- print "Saving option", index
- coverItem = coversModel.getData (index)
- coverItem.save (coversModel.albumItem.get_aa ().get_media_art_path ())
- # Update the main model. Is this enough?
- if not coverItem.initialImage and not coverItem.deleteAction:
- coversModel.albumItem.album_art = None
- coversModel.albumItem.album_art = coversModel.albumItem.get_aa().get_media_art_path ()
- elif coverItem.deleteAction:
- coversModel.albumItem.resetAlbumArt ()
-
- coversModel.cleanCache ()
-
-
- @QtCore.Slot ()
- def stop_pending_jobs (self):
- if self.download :
- self.download.quit ()
-
- def get_all_albums (self):
- """
- Return a list of AlbumItem objects to build the model
- This is not called from QML, no need to make it a slot
- """
- results = []
- for album_title, album_artist in self.tracker.get_all_albums ():
- results.append (AlbumItem (album_title, album_artist))
- return results
-
+++ /dev/null
-# -*- coding: utf-8 -*-
-import os
-import sys
-from PySide import QtCore
-from PySide import QtGui
-from PySide import QtDeclarative
-
-
-class CoverItem (QtCore.QObject):
-
- def __init__ (self, image, initialImage=False, deleteAction=False):
- QtCore.QObject.__init__(self)
- self.initialImage = initialImage
- self.deleteAction = deleteAction
- self._url = image
-
- def _url (self):
- return self._url
-
- def _setUrl (self, url):
- self._url = url
- self.url_changed.emit ()
-
- def save (self, destination):
- if self.initialImage:
- print " -> No changes"
- return
-
- if self.deleteAction:
- print " -> Remove the album art"
- if os.path.exists (destination):
- os.remove (destination)
- return
-
- print " -> Saving:", self.url, "to", destination
- os.rename (self.url, destination)
-
-
- def remove (self):
- print "Removing", self.url
- if os.path.exists (self.url):
- os.remove (self.url)
-
-
- url_changed = QtCore.Signal ()
- url = QtCore.Property (unicode, _url, _setUrl, notify=url_changed)
+++ /dev/null
-# -*- coding: utf-8 -*-
-import os
-import sys
-from PySide import QtCore
-from PySide import QtGui
-from PySide import QtDeclarative
-
-from coverItem import CoverItem
-from albumItem import AlbumItem
-
-class CoversModel (QtCore.QAbstractListModel):
- COLUMNS = ('cover', )
-
- def __init__ (self, albumItem):
- QtCore.QAbstractListModel.__init__ (self)
- self.albumItem = albumItem
-
- self.startOffset = 2
- self._alternatives = [
- CoverItem (self.albumItem.album_art, initialImage=True),
- CoverItem ("images/blank_record.png", deleteAction = True),
- CoverItem (None),
- CoverItem (None),
- CoverItem (None),
- CoverItem (None)
- ]
-
- self.setRoleNames (dict(enumerate(CoversModel.COLUMNS)))
-
- def rowCount (self, parent=QtCore.QModelIndex()):
- return len (self._alternatives)
-
- def data (self, index, role):
- if index.isValid () and role == CoversModel.COLUMNS.index ('cover'):
- return self._alternatives[index.row ()]
- return None
-
- def updateData (self, row, url):
- position = row + self.startOffset
- assert position >= 0 and position < len (self._alternatives)
- self._alternatives[position].url = url
-
- def getData (self, row):
- assert row >= 0 and row < len (self._alternatives)
- return self._alternatives[row]
-
- def cleanCache (self):
- """
- The model contains the urls of the downloaded alternatives.
- This method removes those files. The selected image should have already been copied
- to its final location.
- """
- for i in range (self.startOffset, len (self._alternatives)):
- # This url can be None if the image didn't finish downloading
- if self._alternatives[i].url and os.path.exists (self._alternatives[i].url):
- os.remove (self._alternatives[i].url)
+++ /dev/null
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-import sys
-from PySide.QtCore import *
-from PySide.QtGui import *
-from PySide.QtDeclarative import QDeclarativeView
-
-from albumModel import AlbumModel
-from controller import MussorgskyController
-
-# Create Qt application and the QDeclarative view
-app = QApplication(sys.argv)
-view = QDeclarativeView()
-
-controller = MussorgskyController (view.rootContext ())
-
-#from albumItem import AlbumItem
-#MOCK_DATA = [AlbumItem (u"x", u"y") for i in xrange (0, 100)]
-#albumModel = AlbumModel (MOCK_DATA)
-
-albumModel = AlbumModel (controller.get_all_albums())
-print "Model with", albumModel.rowCount(), "rows"
-
-#from coverModel import CoversModel
-#coverModel = CoversModel ()
-
-rc = view.rootContext ()
-rc.setContextProperty ('albumModel', albumModel)
-rc.setContextProperty ('missionControl', controller)
-#rc.setContextProperty ('coversModel', coverModel)
-
-# Create an URL to the QML file
-#url = QUrl('view.qml')
-url = QUrl ("../../ui/main.qml")
-# Set the QML file and show
-view.setSource(url)
-view.engine().quit.connect (app.quit)
-
-view.show()
-
-
-# Enter Qt main loop
-sys.exit(app.exec_())
+++ /dev/null
-import dbus
-import os
-
-TRACKER = "org.freedesktop.Tracker1"
-TRACKER_OBJ = "/org/freedesktop/Tracker1/Resources"
-
-# (album name, artist1|artist2|... , number of artists)
-ALBUM_ARTISTS_COUNTER = """
-SELECT nie:title (?album) nmm:artistName (?artist) COUNT (?artist)
-WHERE
- {
- ?album a nmm:MusicAlbum ;
- nmm:albumArtist ?artist .
- }
-GROUP BY ?album
-"""
-
-SONGS = """
-SELECT ?u nmm:artistName(?artist) nmm:albumTitle(?album) WHERE
-{
- ?u a nmm:MusicPiece ;
- nmm:performer ?artist ;
- nmm:musicAlbum ?album .
-}
-"""
-
-class TrackerBackendDBus:
-
- def __init__ (self):
- self.dbus = dbus.SessionBus ()
- self.tracker = self.dbus.get_object (TRACKER, TRACKER_OBJ)
- self.resources = dbus.Interface (self.tracker,
- "org.freedesktop.Tracker1.Resources")
-
- def get_all_albums (self):
- results = self.resources.SparqlQuery (ALBUM_ARTISTS_COUNTER)
- return self.__iter_results (results)
-
- def get_all_songs (self):
- results = self.resources.SparqlQuery (SONGS)
- for uri, artist, album in results:
- yield (uri, artist, album)
-
- def __iter_results (self, results):
- for (title, artist, counter) in results:
- print "Executed the yield", counter
- if int(counter) > 1:
- yield (unicode(title), "Various artists")
- else:
- yield (unicode(title), unicode(artist))
-
-
-if __name__ == "__main__":
- b = TrackerBackendDBus ()
- for pair in b.get_all_songs ():
- print "Outter loop"
- print pair
- print "ok"
-
+++ /dev/null
-import gi
-from gi.repository import Tracker
-
-# (album name, artist1|artist2|... , number of artists)
-ALBUM_ARTISTS_COUNTER = """
-SELECT nie:title (?album) nmm:artistName (?artist) COUNT (?artist)
-WHERE
- {
- ?album a nmm:MusicAlbum ;
- nmm:albumArtist ?artist .
- }
-GROUP BY ?album
-"""
-
-class TrackerBackendGI:
-
- def __init__ (self):
- self.conn = Tracker.SparqlConnection.get (None)
-
- def get_all_albums (self):
- """
- Generator returning (album_title, album_artist)
- If the album has more than one artist, "Various artists"
- """
- cursor = self.conn.query (ALBUM_ARTISTS_COUNTER, None)
- while cursor.next (None):
- album_title = cursor.get_string (0)[0]
- if cursor.get_integer(2) > 1:
- album_artist = "Various artists"
- else:
- album_artist = cursor.get_string (1)[0]
-
- yield (album_title, album_artist)
-
-
-
-if __name__ == "__main__":
-
- tracker = TrackerBackendGI()
- tracker.get_all_albums ()
+++ /dev/null
-import gobject
-
-def escape_html (text, max_length=40):
- if (len (text) > max_length):
- cutpoint = text.find (' ', max_length-10)
- if (cutpoint == -1 or cutpoint > max_length):
- cutpoint = max_length
- text = text [0:cutpoint] + "..."
- return gobject.markup_escape_text (text)
-
-def is_empty (text):
- return not text or len (text.strip ()) == 0
-
-# Set socket timeout
-import socket
-import urllib2
-
-timeout = 5
-socket.setdefaulttimeout(timeout)
-
-class UrllibWrapper ():
-
- def save_content_into_file (self, content, filename):
- output = open (filename, 'w')
- output.write (content)
- output.close ()
-
- def get_url (self, url):
- request = urllib2.Request (url)
- request.add_header ('User-Agent', 'Mussorgsky/0.1 Test')
- opener = urllib2.build_opener ()
- try:
- return opener.open (request).read ()
- except:
- return None
-
-
-
-class Set:
-
- def __init__ (self):
- self.d = {}
- self.k = None
-
- def insert (self, element):
- if (not self.d.has_key (element)):
- self.d[element] = 1
- self.k = None
-
- def as_list (self):
- if (self.k):
- return self.k
-
- self.k = self.d.keys ()
- self.k.sort ()
- return self.k
-
-
-
--- /dev/null
+import dbus
+import os
+
+TRACKER = "org.freedesktop.Tracker1"
+TRACKER_OBJ = "/org/freedesktop/Tracker1/Resources"
+
+# (album name, artist1|artist2|... , number of artists)
+ALBUM_ARTISTS_COUNTER = """
+SELECT nie:title (?album) nmm:artistName (?artist) COUNT (?artist)
+WHERE
+ {
+ ?album a nmm:MusicAlbum ;
+ nmm:albumArtist ?artist .
+ }
+GROUP BY ?album
+"""
+
+SONGS = """
+SELECT ?u nmm:artistName(?artist) nmm:albumTitle(?album) WHERE
+{
+ ?u a nmm:MusicPiece ;
+ nmm:performer ?artist ;
+ nmm:musicAlbum ?album .
+}
+"""
+
+class TrackerBackendDBus:
+
+ def __init__ (self):
+ self.dbus = dbus.SessionBus ()
+ self.tracker = self.dbus.get_object (TRACKER, TRACKER_OBJ)
+ self.resources = dbus.Interface (self.tracker,
+ "org.freedesktop.Tracker1.Resources")
+
+ def get_all_albums (self):
+ results = self.resources.SparqlQuery (ALBUM_ARTISTS_COUNTER)
+ return self.__iter_results (results)
+
+ def get_all_songs (self):
+ results = self.resources.SparqlQuery (SONGS)
+ for uri, artist, album in results:
+ yield (uri, artist, album)
+
+ def __iter_results (self, results):
+ for (title, artist, counter) in results:
+ print "Executed the yield", counter
+ if int(counter) > 1:
+ yield (unicode(title), "Various artists")
+ else:
+ yield (unicode(title), unicode(artist))
+
+
+if __name__ == "__main__":
+ b = TrackerBackendDBus ()
+ for pair in b.get_all_songs ():
+ print "Outter loop"
+ print pair
+ print "ok"
+
--- /dev/null
+import gi
+from gi.repository import Tracker
+
+# (album name, artist1|artist2|... , number of artists)
+ALBUM_ARTISTS_COUNTER = """
+SELECT nie:title (?album) nmm:artistName (?artist) COUNT (?artist)
+WHERE
+ {
+ ?album a nmm:MusicAlbum ;
+ nmm:albumArtist ?artist .
+ }
+GROUP BY ?album
+"""
+
+class TrackerBackendGI:
+
+ def __init__ (self):
+ self.conn = Tracker.SparqlConnection.get (None)
+
+ def get_all_albums (self):
+ """
+ Generator returning (album_title, album_artist)
+ If the album has more than one artist, "Various artists"
+ """
+ cursor = self.conn.query (ALBUM_ARTISTS_COUNTER, None)
+ while cursor.next (None):
+ album_title = cursor.get_string (0)[0]
+ if cursor.get_integer(2) > 1:
+ album_artist = "Various artists"
+ else:
+ album_artist = cursor.get_string (1)[0]
+
+ yield (album_title, album_artist)
+
+
+
+if __name__ == "__main__":
+
+ tracker = TrackerBackendGI()
+ tracker.get_all_albums ()
--- /dev/null
+import gobject
+
+def escape_html (text, max_length=40):
+ if (len (text) > max_length):
+ cutpoint = text.find (' ', max_length-10)
+ if (cutpoint == -1 or cutpoint > max_length):
+ cutpoint = max_length
+ text = text [0:cutpoint] + "..."
+ return gobject.markup_escape_text (text)
+
+def is_empty (text):
+ return not text or len (text.strip ()) == 0
+
+# Set socket timeout
+import socket
+import urllib2
+
+timeout = 5
+socket.setdefaulttimeout(timeout)
+
+class UrllibWrapper ():
+
+ def save_content_into_file (self, content, filename):
+ output = open (filename, 'w')
+ output.write (content)
+ output.close ()
+
+ def get_url (self, url):
+ request = urllib2.Request (url)
+ request.add_header ('User-Agent', 'Mussorgsky/0.1 Test')
+ opener = urllib2.build_opener ()
+ try:
+ return opener.open (request).read ()
+ except:
+ return None
+
+
+
+class Set:
+
+ def __init__ (self):
+ self.d = {}
+ self.k = None
+
+ def insert (self, element):
+ if (not self.d.has_key (element)):
+ self.d[element] = 1
+ self.k = None
+
+ def as_list (self):
+ if (self.k):
+ return self.k
+
+ self.k = self.d.keys ()
+ self.k.sort ()
+ return self.k
+
+
+