Move the qml/* files to the src folder. Now it is the main version
authorIvan Frade <ivan.frade@gmail.com>
Tue, 5 Jul 2011 07:28:03 +0000 (10:28 +0300)
committerIvan Frade <ivan.frade@gmail.com>
Tue, 5 Jul 2011 07:28:03 +0000 (10:28 +0300)
22 files changed:
src/aa_search.py [new file with mode: 0644]
src/albumArt.py [new file with mode: 0644]
src/albumItem.py [new file with mode: 0644]
src/albumModel.py [new file with mode: 0644]
src/controller.py [new file with mode: 0644]
src/coverItem.py [new file with mode: 0644]
src/coverModel.py [new file with mode: 0644]
src/mussorgsky-qml.py [new file with mode: 0644]
src/qml/aa_search.py [deleted file]
src/qml/albumArt.py [deleted file]
src/qml/albumItem.py [deleted file]
src/qml/albumModel.py [deleted file]
src/qml/controller.py [deleted file]
src/qml/coverItem.py [deleted file]
src/qml/coverModel.py [deleted file]
src/qml/mussorgsky-qml.py [deleted file]
src/qml/tracker_backend_dbus.py [deleted file]
src/qml/tracker_backend_gi.py [deleted file]
src/qml/utils.py [deleted file]
src/tracker_backend_dbus.py [new file with mode: 0644]
src/tracker_backend_gi.py [new file with mode: 0644]
src/utils.py [new file with mode: 0644]

diff --git a/src/aa_search.py b/src/aa_search.py
new file mode 100644 (file)
index 0000000..db716c5
--- /dev/null
@@ -0,0 +1,258 @@
+#!/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:&quot;", starting_at)
+            if (start == -1):
+                yield None
+            end = page.find ("&", start + len ("imgurl:&quot;"))
+            current_option = page [start + len ("imgurl:&quot;"): 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
diff --git a/src/albumArt.py b/src/albumArt.py
new file mode 100644 (file)
index 0000000..74a6432
--- /dev/null
@@ -0,0 +1,88 @@
+# -*- 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 () + "\""
+    
diff --git a/src/albumItem.py b/src/albumItem.py
new file mode 100644 (file)
index 0000000..0cf25c8
--- /dev/null
@@ -0,0 +1,75 @@
+# -*- 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)
+    
diff --git a/src/albumModel.py b/src/albumModel.py
new file mode 100644 (file)
index 0000000..a8190c9
--- /dev/null
@@ -0,0 +1,36 @@
+# -*- 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
diff --git a/src/controller.py b/src/controller.py
new file mode 100644 (file)
index 0000000..db4f296
--- /dev/null
@@ -0,0 +1,126 @@
+# -*- 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
+
diff --git a/src/coverItem.py b/src/coverItem.py
new file mode 100644 (file)
index 0000000..5033364
--- /dev/null
@@ -0,0 +1,46 @@
+# -*- 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)
diff --git a/src/coverModel.py b/src/coverModel.py
new file mode 100644 (file)
index 0000000..9e15909
--- /dev/null
@@ -0,0 +1,56 @@
+# -*- 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)
diff --git a/src/mussorgsky-qml.py b/src/mussorgsky-qml.py
new file mode 100644 (file)
index 0000000..bc97a75
--- /dev/null
@@ -0,0 +1,44 @@
+#!/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_())
diff --git a/src/qml/aa_search.py b/src/qml/aa_search.py
deleted file mode 100644 (file)
index db716c5..0000000
+++ /dev/null
@@ -1,258 +0,0 @@
-#!/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:&quot;", starting_at)
-            if (start == -1):
-                yield None
-            end = page.find ("&", start + len ("imgurl:&quot;"))
-            current_option = page [start + len ("imgurl:&quot;"): 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
diff --git a/src/qml/albumArt.py b/src/qml/albumArt.py
deleted file mode 100644 (file)
index 74a6432..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-# -*- 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 () + "\""
-    
diff --git a/src/qml/albumItem.py b/src/qml/albumItem.py
deleted file mode 100644 (file)
index 0cf25c8..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-# -*- 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)
-    
diff --git a/src/qml/albumModel.py b/src/qml/albumModel.py
deleted file mode 100644 (file)
index a8190c9..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-# -*- 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
diff --git a/src/qml/controller.py b/src/qml/controller.py
deleted file mode 100644 (file)
index db4f296..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-# -*- 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
-
diff --git a/src/qml/coverItem.py b/src/qml/coverItem.py
deleted file mode 100644 (file)
index 5033364..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-# -*- 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)
diff --git a/src/qml/coverModel.py b/src/qml/coverModel.py
deleted file mode 100644 (file)
index 9e15909..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- 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)
diff --git a/src/qml/mussorgsky-qml.py b/src/qml/mussorgsky-qml.py
deleted file mode 100644 (file)
index bc97a75..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/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_())
diff --git a/src/qml/tracker_backend_dbus.py b/src/qml/tracker_backend_dbus.py
deleted file mode 100644 (file)
index 0a78d4a..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-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"
-                
diff --git a/src/qml/tracker_backend_gi.py b/src/qml/tracker_backend_gi.py
deleted file mode 100644 (file)
index f5329a0..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-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 ()
diff --git a/src/qml/utils.py b/src/qml/utils.py
deleted file mode 100644 (file)
index daffb06..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-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
-        
-
-
diff --git a/src/tracker_backend_dbus.py b/src/tracker_backend_dbus.py
new file mode 100644 (file)
index 0000000..0a78d4a
--- /dev/null
@@ -0,0 +1,59 @@
+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"
+                
diff --git a/src/tracker_backend_gi.py b/src/tracker_backend_gi.py
new file mode 100644 (file)
index 0000000..f5329a0
--- /dev/null
@@ -0,0 +1,40 @@
+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 ()
diff --git a/src/utils.py b/src/utils.py
new file mode 100644 (file)
index 0000000..daffb06
--- /dev/null
@@ -0,0 +1,59 @@
+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
+        
+
+