Пример #1
0
    def addDiscToCache(self, discInfo, cddb):
        """ Add the given CDDB information to the cache """
        if not os.path.exists(self.cacheDir):
            os.mkdir(self.cacheDir)

        try:    tools.pickleSave(os.path.join(self.cacheDir, str(discInfo[DISC_CHECKSUM])), cddb)
        except: pass
Пример #2
0
 def __createEmptyLibrary(self, name):
     """ Create bootstrap files for a new library """
     # Make sure that the root directory of all libraries exists
     if not isdir(ROOT_PATH):
         os.mkdir(ROOT_PATH)
     # Start from an empty library
     libPath = os.path.join(ROOT_PATH, name)
     if isdir(libPath):
         shutil.rmtree(libPath)
     os.mkdir(libPath)
     pickleSave(os.path.join(libPath, 'files'), {})
Пример #3
0
    def save_track_tree(self):
        # Save playing track
        if self.tree.hasMark():
            last_path = self.tree.mark.get_path()
        else:
            last_path = None
        prefs.set(__name__, 'last-played-track', last_path)

        dump = self.getTreeDump()
        logging.info('Saving playlist')
        pickleSave(self.savedPlaylist, dump)
        # tell gobject to keep saving the content in regular intervals
        return True
Пример #4
0
    def save_track_tree(self):
        # Save playing track
        if self.tree.hasMark():
            last_path = tuple(self.tree.mark.get_path())
        else:
            last_path = None
        prefs.set(__name__, 'last-played-track', last_path)

        dump = self.getTreeDump()
        logging.info('Saving playlist')
        pickleSave(self.savedPlaylist, dump)
        # tell gobject to keep saving the content in regular intervals
        return True
Пример #5
0
    def refreshLibrary(self, parent, libName, path, creation=False):
        """ Refresh the given library, must be called through idle_add() """
        # First show a progress dialog
        if creation: header = _('Creating library')
        else:        header = _('Refreshing library')

        progress = ProgressDlg(parent, header, _('The directory is scanned for media files. This can take some time.\nPlease wait.'))
        yield True

        libPath = os.path.join(ROOT_PATH, libName)   # Location of the library

        # If the version number has changed or does not exist, don't reuse any existing file and start from scratch
        if not os.path.exists(os.path.join(libPath, 'VERSION_%u' % VERSION)):
            self.__createEmptyLibrary(libName)

        db         = {}                                                                # The dictionnary used to create the library
        queue      = collections.deque((path,))                                        # Faster structure for appending/removing elements
        mediaFiles = []                                                                # All media files found
        newLibrary = {}                                                                # Reflect the current file structure of the library
        oldLibrary = pickleLoad(os.path.join(libPath, 'files'))                        # Previous file structure of the same library

        # Make sure the root directory still exists
        if not os.path.exists(path):
            queue.pop()

        while len(queue) != 0:
            currDir      = queue.pop()
            currDirMTime = os.stat(currDir).st_mtime

            # Retrieve previous information on the current directory, if any
            if currDir in oldLibrary: oldDirMTime, oldDirectories, oldFiles = oldLibrary[currDir]
            else:                     oldDirMTime, oldDirectories, oldFiles = -1, [], {}

            # If the directory has not been modified, keep old information
            if currDirMTime == oldDirMTime:
                files, directories = oldFiles, oldDirectories
            else:
                files, directories = {}, []
                for (filename, fullPath) in tools.listDir(currDir):
                    if isdir(fullPath):
                        directories.append(fullPath)
                    elif isfile(fullPath) and media.isSupported(filename):
                        if filename in oldFiles: files[filename] = oldFiles[filename]
                        else:                    files[filename] = [-1, FileTrack(fullPath)]

            # Determine which files need to be updated
            for filename, (oldMTime, track) in files.iteritems():
                mTime = os.stat(track.getFilePath()).st_mtime
                if mTime != oldMTime:
                    files[filename] = [mTime, media.getTrackFromFile(track.getFilePath())]

            newLibrary[currDir] = (currDirMTime, directories, files)
            mediaFiles.extend([track for mTime, track in files.itervalues()])
            queue.extend(directories)

            # Update the progress dialog
            try:
                text = ngettext('Scanning directories (one track found)', 'Scanning directories (%(nbtracks)u tracks found)', len(mediaFiles))
                progress.pulse(text % {'nbtracks': len(mediaFiles)})
                yield True
            except progressDlg.CancelledException:
                progress.destroy()
                if creation:
                    shutil.rmtree(libPath)
                yield False

        # From now on, the process should not be cancelled
        progress.setCancellable(False)
        if creation: progress.pulse(_('Creating library...'))
        else:        progress.pulse(_('Refreshing library...'))
        yield True

        # Create the database
        for track in mediaFiles:
            album = track.getExtendedAlbum()

            if track.hasAlbumArtist(): artist = track.getAlbumArtist()
            else:                      artist = track.getArtist()

            if artist in db:
                allAlbums = db[artist]
                if album in allAlbums: allAlbums[album].append(track)
                else:                  allAlbums[album] = [track]
            else:
                db[artist] = {album: [track]}

        progress.pulse()
        yield True

        # If an artist name begins with a known prefix, put it at the end (e.g., Future Sound of London (The))
        prefixes = prefs.get(__name__, 'prefixes', PREFS_DEFAULT_PREFIXES)
        for artist in db.keys():
            artistLower = artist.lower()
            for prefix in prefixes:
                if artistLower.startswith(prefix):
                    db[artist[len(prefix):] + ' (%s)' % artist[:len(prefix)-1]] = db[artist]
                    del db[artist]

        progress.pulse()
        yield True

        # Re-create the library structure on the disk
        if isdir(libPath):
            shutil.rmtree(libPath)
            os.mkdir(libPath)

        # Put a version number
        tools.touch(os.path.join(libPath, 'VERSION_%u' % VERSION))

        overallNbAlbums  = 0
        overallNbTracks  = 0
        overallNbArtists = len(db)

        # The 'artists' file contains all known artists with their index, the 'files' file contains the file structure of the root path
        allArtists = sorted([(artist, str(indexArtist), len(db[artist])) for indexArtist, artist in enumerate(db)], key = lambda a: a[0])
        pickleSave(os.path.join(libPath, 'files'),   newLibrary)
        pickleSave(os.path.join(libPath, 'artists'), allArtists)

        for (artist, indexArtist, nbAlbums) in allArtists:
            artistPath       = os.path.join(libPath, indexArtist)
            overallNbAlbums += nbAlbums
            os.mkdir(artistPath)

            albums = []
            for index, (name, tracks) in enumerate(db[artist].iteritems()):
                length           = sum([track.getLength() for track in tracks])
                overallNbTracks += len(tracks)

                albums.append((name, str(index), len(tracks), length))
                pickleSave(os.path.join(artistPath, str(index)), sorted(tracks, key = lambda track: track.getNumber()))

            albums.sort(cmp = lambda a1, a2: cmp(db[artist][a1[0]][0], db[artist][a2[0]][0]))
            pickleSave(os.path.join(artistPath, 'albums'), albums)
            progress.pulse()
            yield True

        self.libraries[libName] = (path, overallNbArtists, overallNbAlbums, overallNbTracks)
        self.fillLibraryList()
        if creation:
            modules.postMsg(consts.MSG_CMD_EXPLORER_ADD, {'modName': MOD_L10N, 'expName': libName, 'icon': None, 'widget': self.scrolled})
        progress.destroy()

        # If the refreshed library is currently displayed, refresh the treeview as well
        if self.currLib == libName:
            treeState = self.tree.saveState(ROW_NAME)
            self.loadLibrary(self.tree, self.currLib)
            self.tree.restoreState(treeState, ROW_NAME)

        yield False
Пример #6
0
 def saveFavorites(self, libName, favorites):
     """ Save favorites to the disk """
     pickleSave(os.path.join(ROOT_PATH, libName, 'favorites'), favorites)
Пример #7
0
    def __getFromInternet(self, artist, album):
        """
            Try to download the cover from the Internet
            If successful, add it to the cache and return the path to it
            Otherwise, return None
        """
        import socket, urllib.request, urllib.error, urllib.parse

        # Make sure to not be blocked by the request
        socket.setdefaulttimeout(consts.socketTimeout)

        # Request information to Last.fm
        # Beware of UTF-8 characters: we need to percent-encode all characters
        try:
            url = 'http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=%s&artist=%s&album=%s' % (
                AS_API_KEY, tools.percentEncode(artist),
                tools.percentEncode(album))
            request = urllib.request.Request(
                url, headers={'User-Agent': USER_AGENT})
            stream = urllib.request.urlopen(request)
            data = stream.read().decode('utf-8')
        except urllib.error.HTTPError as err:
            if err.code == 400:
                logger.error('[%s] No known cover for %s / %s' %
                             (MOD_NAME, artist, album))
            else:
                logger.error('[%s] Information request failed\n\n%s' %
                             (MOD_NAME, traceback.format_exc()))
            return None
        except urllib.error.URLError:
            logger.info('[%s] Could not fetch cover. No internet connection.' %
                        MOD_NAME)
            return None
        except:
            logger.error('[%s] Information request failed\n\n%s' %
                         (MOD_NAME, traceback.format_exc()))
            return None

        # Extract the URL to the cover image
        malformed = True
        startIdx = data.find(AS_TAG_START)
        endIdx = data.find(AS_TAG_END, startIdx)
        if startIdx != -1 and endIdx != -1:
            coverURL = data[startIdx + len(AS_TAG_START):endIdx]
            coverFormat = os.path.splitext(coverURL)[1].lower()
            if coverURL.startswith(
                ('http://',
                 'https://')) and coverFormat in ACCEPTED_FILE_FORMATS:
                malformed = False

        if malformed:
            ## Do not show the data in the log every time no cover is found
            if coverURL:
                logger.error('[%s] Received malformed data\n\n%s' %
                             (MOD_NAME, data))
            return None

        # Download the cover image
        try:
            request = urllib.request.Request(
                coverURL, headers={'User-Agent': USER_AGENT})
            stream = urllib.request.urlopen(request)
            data = stream.read()

            if len(data) < 1024:
                raise Exception(
                    'The cover image seems incorrect (%u bytes is too small)' %
                    len(data))
        except:
            logger.error('[%s] Cover image request failed' % MOD_NAME)
            return None

        # So far, so good: let's cache the image
        cachePath = os.path.join(self.cacheRootPath, str(abs(hash(artist))))
        cacheIdxPath = os.path.join(cachePath, 'INDEX')

        if not os.path.exists(cachePath):
            os.mkdir(cachePath)

        try:
            cacheIdx = tools.pickleLoad(cacheIdxPath)
        except:
            cacheIdx = {}

        nextInt = len(cacheIdx) + 1
        filename = str(nextInt) + coverFormat
        coverPath = os.path.join(cachePath, filename)

        cacheIdx[artist + album] = filename
        tools.pickleSave(cacheIdxPath, cacheIdx)

        try:
            output = open(coverPath, 'wb')
            output.write(data)
            output.close()
            return coverPath
        except:
            logger.error('[%s] Could not save the downloaded cover\n\n%s' %
                         (MOD_NAME, traceback.format_exc()))

        return None
Пример #8
0
 def handleMsg(self, msg, params):
     """ Handle messages sent to this modules """
     if msg == consts.MSG_EVT_NEW_TRACKLIST:
         tools.pickleSave(self.savedPlaylist, [track.serialize() for track in params['tracks']])
     elif msg == consts.MSG_EVT_APP_STARTED:
         self.onAppStarted()
Пример #9
0
def save():
    """ Save user preferences to the disk """
    with __mutex:
        tools.pickleSave(tools.consts.filePrefs, __usrPrefs)
        os.chmod(tools.consts.filePrefs, 0o600)
Пример #10
0
        # So far, so good: let's cache the image
        cachePath    = os.path.join(self.cacheRootPath, str(abs(hash(artist))))
        cacheIdxPath = os.path.join(cachePath, 'INDEX')

        if not os.path.exists(cachePath):
            os.mkdir(cachePath)

        try:    cacheIdx = tools.pickleLoad(cacheIdxPath)
        except: cacheIdx = {}

        nextInt   = len(cacheIdx) + 1
        filename  = str(nextInt) + coverFormat
        coverPath = os.path.join(cachePath, filename)

        cacheIdx[artist + album] = filename
        tools.pickleSave(cacheIdxPath, cacheIdx)

        try:
            output = open(coverPath, 'wb')
            output.write(data)
            output.close()
            return coverPath
        except:
            logger.error('[%s] Could not save the downloaded cover\n\n%s' % (MOD_NAME, traceback.format_exc()))

        return None


    def getFromInternet(self, artist, album):
        """ Wrapper for __getFromInternet(), manage blacklist """
        coverKey = artist + album
Пример #11
0
def save():
    """ Save user preferences to the disk """
    __mutex.acquire()
    tools.pickleSave(tools.consts.filePrefs, __usrPrefs)
    os.chmod(tools.consts.filePrefs, 0600)
    __mutex.release()
Пример #12
0
 def onNewTracklist(self, tracks, playtime):
     """ A new tracklist has been set """
     pickleSave(self.savedPlaylist, [track.serialize() for track in tracks])