Пример #1
0
    def onAppStarted(self):
        """ The module has been loaded """
        self.tree = None
        self.cfgWin = None
        self.folders = prefs.get(__name__, 'media-folders',
                                 PREFS_DEFAULT_MEDIA_FOLDERS)
        self.scrolled = gtk.ScrolledWindow()
        self.currRoot = None
        self.treeState = prefs.get(__name__, 'saved-states', {})
        self.addByFilename = prefs.get(__name__, 'add-by-filename',
                                       PREFS_DEFAULT_ADD_BY_FILENAME)
        self.showHiddenFiles = prefs.get(__name__, 'show-hidden-files',
                                         PREFS_DEFAULT_SHOW_HIDDEN_FILES)

        self.scrolled.set_shadow_type(gtk.SHADOW_IN)
        self.scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        self.scrolled.show()

        for name in self.folders:
            modules.postMsg(
                consts.MSG_CMD_EXPLORER_ADD, {
                    'modName': MOD_L10N,
                    'expName': name,
                    'icon': icons.dirMenuIcon(),
                    'widget': self.scrolled
                })
Пример #2
0
    def onAppStarted(self):
        """ Real initialization function, called when this module has been loaded """
        self.currTrackLength = 0
        self.sclBeingDragged = False
        # Widgets
        wTree             = prefs.getWidgetsTree()
#        self.btnStop      = wTree.get_widget('btn-stop')
        self.btnPlay      = wTree.get_widget('btn-play')
#        self.btnNext      = wTree.get_widget('btn-next')
        self.btnPrev      = wTree.get_widget('btn-previous')
        self.sclSeek      = wTree.get_widget('scl-position')
        self.btnVolume    = wTree.get_widget('btn-volume')
        self.lblElapsed   = wTree.get_widget('lbl-elapsedTime')
        self.lblRemaining = wTree.get_widget('lbl-remainingTime')

        # Initial state
        self.onStopped()
        self.btnPlay.set_sensitive(False)
        # GTK handlers
#        self.btnStop.connect('clicked',      lambda widget: modules.postMsg(consts.MSG_CMD_STOP))
#        self.btnNext.connect('clicked',      lambda widget: modules.postMsg(consts.MSG_CMD_NEXT))
        self.btnPrev.connect('clicked',      lambda widget: modules.postMsg(consts.MSG_CMD_PREVIOUS))
        self.btnPlay.connect('clicked',      lambda widget: modules.postMsg(consts.MSG_CMD_TOGGLE_PAUSE))
        self.sclSeek.connect('change-value', self.onSeekChangeValue)
        # We need to keep the handler ID for this one to be able to disconnect it when needed
        self.seekHandler   = self.sclSeek.connect('value-changed',   self.onSeekValueChanged)
        self.volumeHandler = self.btnVolume.connect('value-changed', self.onVolumeValueChanged)
        # We must make sure that the handler will be called: this is not the case if the new value is the same as the old one
        volumeValue = prefs.get(__name__, 'volume', PREFS_DEFAULT_VOLUME)
        if self.btnVolume.get_value() != volumeValue: self.btnVolume.set_value(volumeValue)
        else:                                         self.onVolumeValueChanged(self.btnVolume, volumeValue)
Пример #3
0
    def onAppStarted(self):
        """ This is the real initialization function, called when the module has been loaded """
        wTree                  = tools.prefs.getWidgetsTree()
        self.playtime          = 0
        self.bufferedTrack     = None
        # Retrieve widgets
        self.window     = wTree.get_object('win-main')

        columns = (('',   [(gtk.CellRendererPixbuf(), gtk.gdk.Pixbuf), (gtk.CellRendererText(), TYPE_STRING)], True),
                   (None, [(None, TYPE_PYOBJECT)], False),
                  )

        self.tree = TrackTreeView(columns, use_markup=True)
        self.tree.enableDNDReordering()
        self.tree.setDNDSources([DND_INTERNAL_TARGET])

        wTree.get_object('scrolled-tracklist').add(self.tree)

        # GTK handlers
        self.tree.connect('exttreeview-button-pressed', self.onMouseButton)
        self.tree.connect('tracktreeview-dnd', self.onDND)
        self.tree.connect('key-press-event', self.onKeyboard)
        self.tree.get_model().connect('row-deleted', self.onRowDeleted)

        (options, args) = prefs.getCmdLine()

        self.savedPlaylist = os.path.join(consts.dirCfg, 'saved-playlist')
        self.paused = False


        # Populate the playlist with the saved playlist
        dump = None
        if os.path.exists(self.savedPlaylist):
            try:
                dump = pickleLoad(self.savedPlaylist)
            except:
                msg = '[%s] Unable to restore playlist from %s\n\n%s'
                log.logger.error(msg % (MOD_INFO[modules.MODINFO_NAME],
                                self.savedPlaylist, traceback.format_exc()))

        if dump:
            self.restoreTreeDump(dump)
            log.logger.info('[%s] Restored playlist' % MOD_INFO[modules.MODINFO_NAME])
            self.tree.collapse_all()
            self.select_last_played_track()
            self.onListModified()

        commands, args = tools.separate_commands_and_tracks(args)

        # Add commandline tracks to the playlist
        if args:
            log.logger.info('[%s] Filling playlist with files given on command line' % MOD_INFO[modules.MODINFO_NAME])
            tracks  = media.getTracks([os.path.abspath(arg) for arg in args])
            playNow = not 'stop' in commands and not 'pause' in commands
            modules.postMsg(consts.MSG_CMD_TRACKLIST_ADD, {'tracks': tracks, 'playNow': playNow})
        elif 'play' in commands:
            modules.postMsg(consts.MSG_CMD_TOGGLE_PAUSE)

        # Automatically save the content at regular intervals
        gobject.timeout_add_seconds(SAVE_INTERVAL, self.save_track_tree)
Пример #4
0
    def onSearch(self, query):
        self.should_stop = False
        self.found_something = False

        # Transform whitespace-separated query into OR-regex.
        regex = re.compile('|'.join(tools.get_pattern(word)
                           for word in query.split()), re.IGNORECASE)

        for dir in self.get_search_paths() + [consts.dirBaseUsr]:
            # Check if search has been aborted during filtering
            if self.should_stop:
                break

            # Only search in home folder if we haven't found anything yet.
            if dir == consts.dirBaseUsr and self.found_something:
                break

            results = self.search_dir(dir, query)

            # Check if search has been aborted during searching
            if results is None or self.should_stop:
                break

            dirs, files = self.filter_results(results, dir, regex)
            if not self.should_stop and (dirs or files):
                self.found_something = True
                modules.postMsg(consts.MSG_EVT_SEARCH_APPEND,
                                {'results': (dirs, files), 'query': query})

        modules.postMsg(consts.MSG_EVT_SEARCH_END)
Пример #5
0
    def onSearch(self, query):
        self.should_stop = False
        self.found_something = False

        # Transform whitespace-separated query into OR-regex.
        regex = re.compile(
            '|'.join(tools.get_pattern(word) for word in query.split()),
            re.IGNORECASE)

        for dir in self.get_search_paths() + [consts.dirBaseUsr]:
            # Check if search has been aborted during filtering
            if self.should_stop:
                break

            # Only search in home folder if we haven't found anything yet.
            if dir == consts.dirBaseUsr and self.found_something:
                break

            results = self.search_dir(dir, query)

            # Check if search has been aborted during searching
            if results is None or self.should_stop:
                break

            dirs, files = self.filter_results(results, dir, regex)
            if not self.should_stop and (dirs or files):
                self.found_something = True
                modules.postMsg(consts.MSG_EVT_SEARCH_APPEND, {
                    'results': (dirs, files),
                    'query': query
                })

        modules.postMsg(consts.MSG_EVT_SEARCH_END)
Пример #6
0
    def timerFunc(self):
        """ Move a bit the scales to their target value """
        isFinished = True

        # Move the scales a bit
        for i in xrange(10):
            currLvl    = self.scales[i].get_value()
            targetLvl  = self.targetLvls[i]
            difference = targetLvl - currLvl

            if abs(difference) <= 0.25:
                newLvl = targetLvl
            else:
                newLvl     = currLvl + (difference / 8.0)
                isFinished = False

            self.lvls[i] = newLvl
            self.scales[i].set_value(newLvl)

        # Set the equalizer to the new levels
        modules.postMsg(consts.MSG_CMD_SET_EQZ_LVLS, {'lvls': self.lvls})

        if isFinished:
            self.timer = None
            prefs.set(__name__, 'levels', self.lvls)

            # Make sure labels are up to date (sometimes they aren't when we're done with the animation)
            # Also unblock the handlers
            for i in xrange(10):
                self.scales[i].queue_draw()
                self.scales[i].handler_unblock_by_func(self.onScaleValueChanged)

            return False

        return True
Пример #7
0
    def onPopupMenu(self, statusIcon, button, time):
        """ The user asks for the popup menu """
        if self.popupMenu is None:
            wTree              = loadGladeFile('StatusIconMenu.ui')
            self.menuPlay      = wTree.get_object('item-play')
            self.menuStop      = wTree.get_object('item-stop')
            self.menuNext      = wTree.get_object('item-next')
            self.popupMenu     = wTree.get_object('menu-popup')
            self.menuPause     = wTree.get_object('item-pause')
            self.menuPrevious  = wTree.get_object('item-previous')
            self.menuSeparator = wTree.get_object('item-separator')
            # Connect handlers
            wTree.get_object('item-quit').connect('activate', lambda btn: modules.postQuitMsg())
            wTree.get_object('item-preferences').connect('activate', lambda btn: modules.showPreferences())
            self.menuPlay.connect('activate',     lambda btn: modules.postMsg(consts.MSG_CMD_TOGGLE_PAUSE))
            self.menuStop.connect('activate',     lambda btn: modules.postMsg(consts.MSG_CMD_STOP))
            self.menuNext.connect('activate',     lambda btn: modules.postMsg(consts.MSG_CMD_NEXT))
            self.menuPrevious.connect('activate', lambda btn: modules.postMsg(consts.MSG_CMD_PREVIOUS))
            self.menuPause.connect('activate',    lambda btn: modules.postMsg(consts.MSG_CMD_TOGGLE_PAUSE))
            self.popupMenu.show_all()

        # Enable only relevant menu entries
        self.menuStop.set_sensitive(self.isPlaying)
        self.menuNext.set_sensitive(self.isPlaying and self.trackHasNext)
        self.menuPause.set_sensitive(self.isPlaying and not self.isPaused)
        self.menuPrevious.set_sensitive(self.isPlaying and self.trackHasPrev)
        self.menuPlay.set_sensitive((not (self.isPlaying or self.emptyTracklist)) or self.isPaused)

        self.popupMenu.popup(None, None, gtk.status_icon_position_menu, button, time, statusIcon)
Пример #8
0
    def removeSelectedLibraries(self, list):
        """ Remove all selected libraries """
        import shutil

        from gui import questionMsgBox

        if list.getSelectedRowsCount() == 1:
            remark   = _('You will be able to recreate this library later on if you wish so.')
            question = _('Remove the selected library?')
        else:
            remark   = _('You will be able to recreate these libraries later on if you wish so.')
            question = _('Remove all selected libraries?')

        if questionMsgBox(self.cfgWindow, question, '%s %s' % (_('Your media files will not be removed.'), remark)) == gtk.RESPONSE_YES:
            for row in list.getSelectedRows():
                libName = row[0]

                if self.currLib == libName:
                    self.currLib = None

                # Remove the library from the disk
                libPath = os.path.join(ROOT_PATH, libName)
                if isdir(libPath):
                    shutil.rmtree(libPath)
                # Remove the corresponding explorer
                modules.postMsg(consts.MSG_CMD_EXPLORER_REMOVE, {'modName': MOD_L10N, 'expName': libName})
                del self.libraries[libName]
                # Remove tree states
                self.removeTreeStates(libName)
            # Clean up the listview
            list.removeSelectedRows()
Пример #9
0
    def timerFunc(self):
        """ Move a bit the scales to their target value """
        isFinished = True

        # Disconnect handlers before moving the scales
        for i in xrange(10):
            self.scales[i].disconnect(self.handlers[i])

        # Move the scales a bit
        for i in xrange(10):
            currLvl    = self.scales[i].get_value()
            targetLvl  = self.targetLvls[i]
            difference = targetLvl - currLvl

            if abs(difference) <= 0.25:
                newLvl = targetLvl
            else:
                newLvl     = currLvl + (difference / 8.0)
                isFinished = False

            self.lvls[i] = newLvl
            self.scales[i].set_value(newLvl)

        # Reconnect the handlers
        for i in xrange(10):
            self.handlers[i] = self.scales[i].connect('value-changed', self.onScaleValueChanged, i)

        # Set the equalizer to the new levels
        prefs.set(__name__, 'levels', self.lvls)
        modules.postMsg(consts.MSG_CMD_SET_EQZ_LVLS, {'lvls': self.lvls})

        return not isFinished
Пример #10
0
    def onModLoaded(self):
        """ The module has been loaded """
        txtRdrLen = gtk.CellRendererText()

        columns = (('',   [(gtk.CellRendererPixbuf(), gtk.gdk.Pixbuf), (txtRdrLen, gobject.TYPE_STRING), (gtk.CellRendererText(), gobject.TYPE_STRING)], True),
                   (None, [(None, gobject.TYPE_PYOBJECT)],                                                                                               False))

        # The album length is written in a smaller font, with a lighter color
        txtRdrLen.set_property('scale', 0.85)
        txtRdrLen.set_property('foreground', '#909090')

        self.tree     = extTreeview.ExtTreeView(columns, True)
        self.popup    = None
        self.cfgWin   = None
        self.expName  = MOD_L10N
        self.scrolled = gtk.ScrolledWindow()
        self.cacheDir = os.path.join(consts.dirCfg, MOD_INFO[modules.MODINFO_NAME])
        # Explorer
        self.tree.setDNDSources([consts.DND_TARGETS[consts.DND_DAP_TRACKS]])
        self.scrolled.add(self.tree)
        self.scrolled.set_shadow_type(gtk.SHADOW_IN)
        self.scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        self.scrolled.show()
        # GTK handlers
        self.tree.connect('drag-data-get',              self.onDragDataGet)
        self.tree.connect('key-press-event',            self.onKeyPressed)
        self.tree.connect('exttreeview-button-pressed', self.onButtonPressed)
        modules.postMsg(consts.MSG_CMD_EXPLORER_ADD, {'modName': MOD_L10N, 'expName': self.expName, 'icon': consts.icoCdrom, 'widget': self.scrolled})

        # Hide the album length when not drawing the root node
        self.tree.get_column(0).set_cell_data_func(txtRdrLen, self.__drawAlbumLenCell)
Пример #11
0
    def tryToRestore(self, tracklist):
        """ Check whether it's possible to restore playback """
        (options, args) = prefs.getCmdLine()

        # Ignore if the user provided its own tracks on the command line
        if len(args) != 0:
            return

        # Ignore if no track was being played last time
        if not self.playing or self.currTrack is None:
            return

        # Make sure the playlist is the same one
        if len(tracklist) != len(self.currTracklist):
            return

        trackIdx = -1
        for i in xrange(len(tracklist)):
            if tracklist[i] != self.currTracklist[i]:
                return

            if tracklist[i] == self.currTrack:
                trackIdx = i

        # Once here, we know playback can be resumed
        if trackIdx != -1:
            if self.paused: modules.postMsg(consts.MSG_CMD_TRACKLIST_PLAY_PAUSE, {'idx': trackIdx, 'seconds': self.currPos})
            else:           modules.postMsg(consts.MSG_CMD_TRACKLIST_PLAY,       {'idx': trackIdx, 'seconds': self.currPos})
Пример #12
0
    def updateTree(self, discInfo):
        """ Update the tree using disc information from the cache, if any """
        cddb = self.getDiscFromCache(discInfo)

        # Create fake CDDB information if needed
        if cddb is None:
            cddb = {"DTITLE": "%s / %s" % (consts.UNKNOWN_ARTIST, consts.UNKNOWN_ALBUM)}
            for i in xrange(discInfo[DISC_NB_TRACKS]):
                cddb["TTITLE%u" % i] = consts.UNKNOWN_TITLE

        # Compute the length of each track
        trackLen = [
            int(round((discInfo[DISC_FRAME1 + i + 1] - discInfo[DISC_FRAME1 + i]) / 75.0))
            for i in xrange(discInfo[DISC_NB_TRACKS] - 1)
        ]
        trackLen.append(discInfo[DISC_LENGTH] - int(round(discInfo[DISC_FRAMEn] / 75.0)))

        # Update the root of the tree
        disc = cddb["DTITLE"].strip().decode("iso-8859-15", "replace")
        artist, album = disc.split(" / ")

        self.tree.setItem((0,), ROW_NAME, "%s" % tools.htmlEscape(disc))
        self.tree.setItem((0,), ROW_LENGTH, "[%s]" % sec2str(sum(trackLen)))

        # Update the explorer name
        modules.postMsg(
            consts.MSG_CMD_EXPLORER_RENAME, {"modName": MOD_L10N, "expName": self.expName, "newExpName": disc}
        )
        self.expName = disc

        # Optional information
        try:
            date = int(cddb["DYEAR"].strip().decode("iso-8859-15", "replace"))
        except:
            date = None

        try:
            genre = cddb["DGENRE"].strip().decode("iso-8859-15", "replace")
        except:
            genre = None

        # Update each track
        for i, child in enumerate(self.tree.iterChildren((0,))):
            title = cddb["TTITLE%u" % i].strip().decode("iso-8859-15", "replace")

            # Create the corresponding Track object
            track = CDTrack(str(i + 1))
            track.setTitle(title)
            track.setAlbum(album)
            track.setArtist(artist)
            track.setLength(trackLen[i])
            track.setNumber(i + 1)
            # Optional information
            if date is not None:
                track.setDate(date)
            if genre is not None:
                track.setGenre(genre)
            # Fill the tree
            self.tree.setItem(child, ROW_NAME, "%02u. %s" % (i + 1, tools.htmlEscape(title)))
            self.tree.setItem(child, ROW_TRACK, track)
Пример #13
0
    def remove(self, iter=None):
        """ Remove the given track, or the selection if iter is None """
        hadMark = self.tree.hasMark()

        iters = [iter] if iter else list(self.tree.iterSelectedRows())

        prev_iter = self.tree.get_prev_iter_or_parent(iters[0])

        # reverse list, so that we remove children before their fathers
        for iter in reversed(iters):
            track = self.tree.getTrack(iter)
            if track:
                self.playtime -= track.getLength()
            self.tree.removeRow(iter)

        self.tree.selection.unselect_all()

        if hadMark and not self.tree.hasMark():
            modules.postMsg(consts.MSG_CMD_STOP)

        # Select new track when old selected is deleted
        if prev_iter:
            self.tree.select(prev_iter)
        else:
            first_iter = self.tree.get_first_iter()
            if first_iter:
                self.tree.select(first_iter)

        self.onListModified()
Пример #14
0
    def onMediaKey(self, appName, action):
        """ A media key has been pressed """
        if appName == consts.appName:
#            if action == 'Stop':              modules.postMsg(consts.MSG_CMD_STOP)
#            elif action == 'Next':            modules.postMsg(consts.MSG_CMD_NEXT)
            if action == 'Previous':        modules.postMsg(consts.MSG_CMD_PREVIOUS)
            elif action in ['Play', 'Pause']: modules.postMsg(consts.MSG_CMD_TOGGLE_PAUSE)
Пример #15
0
    def onRemoveSelectedFolder(self, list):
        """ Remove the selected media folder """
        import gui

        if list.getSelectedRowsCount() == 1:
            remark   = _('You will be able to add this root folder again later on if you wish so.')
            question = _('Remove the selected entry?')
        else:
            remark   = _('You will be able to add these root folders again later on if you wish so.')
            question = _('Remove all selected entries?')

        if gui.questionMsgBox(self.cfgWin, question, '%s %s' % (_('Your media files will not be deleted.'), remark)) == gtk.RESPONSE_YES:
            for row in self.cfgList.getSelectedRows():
                name = row[0]
                modules.postMsg(consts.MSG_CMD_EXPLORER_REMOVE, {'modName': MOD_L10N, 'expName': name})
                del self.folders[name]

                # Remove the tree, if any, from the scrolled window
                if self.currRoot == name:
                    self.currRoot = None

                # Remove the saved state of the tree, if any
                if name in self.treeState:
                    del self.treeState[name]

            self.cfgList.removeSelectedRows()
Пример #16
0
    def remove(self, iter=None):
        """ Remove the given track, or the selection if iter is None """
        hadMark = self.tree.hasMark()

        iters = [iter] if iter else list(self.tree.iterSelectedRows())

        prev_iter = self.tree.get_prev_iter_or_parent(iters[0])

        # reverse list, so that we remove children before their fathers
        for iter in reversed(iters):
            track = self.tree.getTrack(iter)
            if track:
                self.playtime -= track.getLength()
            self.tree.removeRow(iter)

        self.tree.selection.unselect_all()

        if hadMark and not self.tree.hasMark():
            modules.postMsg(consts.MSG_CMD_STOP)

        # Select new track when old selected is deleted
        if prev_iter:
            self.tree.select(prev_iter)
        else:
            first_iter = self.tree.get_first_iter()
            if first_iter:
                self.tree.select(first_iter)

        self.onListModified()
Пример #17
0
    def onScroll(self, statusIcon, scrollEvent):
        """ The mouse is scrolled on the status icon """
        if scrollEvent.direction == gtk.gdk.SCROLL_UP or scrollEvent.direction == gtk.gdk.SCROLL_RIGHT:
            self.volume = min(1.0, self.volume + 0.05)
        else:
            self.volume = max(0.0, self.volume - 0.05)

        modules.postMsg(consts.MSG_CMD_SET_VOLUME, {'value': self.volume})
Пример #18
0
    def onAddFolder(self, btn):
        """ Let the user add a new folder to the list """
        result = selectPath.SelectPath(MOD_L10N, self.cfgWin, self.folders.keys()).run()

        if result is not None:
            name, path = result
            self.folders[name] = path
            self.populateFolderList()
            modules.postMsg(consts.MSG_CMD_EXPLORER_ADD, {'modName': MOD_L10N, 'expName': name, 'icon': None, 'widget': self.scrolled})
Пример #19
0
    def onAppStarted(self):
        """ This is the real initialization function, called when the module has been loaded """
        wTree                  = tools.prefs.getWidgetsTree()
        self.playtime          = 0
        self.bufferedTrack     = None
        self.previousTracklist = None
        # Retrieve widgets
        self.window     = wTree.get_object('win-main')
        self.btnClear   = wTree.get_object('btn-tracklistClear')
        self.btnRepeat  = wTree.get_object('btn-tracklistRepeat')
        self.btnShuffle = wTree.get_object('btn-tracklistShuffle')
        self.btnClear.set_sensitive(False)
        self.btnShuffle.set_sensitive(False)
        # Create the list and its columns
        txtLRdr   = gtk.CellRendererText()
        txtRRdr   = gtk.CellRendererText()
        pixbufRdr = gtk.CellRendererPixbuf()
        txtRRdr.set_property('xalign', 1.0)

        # 'columns-visibility' may be broken, we should not use it (#311293)
        visible = tools.prefs.get(__name__, 'columns-visibility-2', PREFS_DEFAULT_COLUMNS_VISIBILITY)
        for (key, value) in PREFS_DEFAULT_COLUMNS_VISIBILITY.iteritems():
            if key not in visible:
                visible[key] = value

        columns = (('#',           [(pixbufRdr, gtk.gdk.Pixbuf), (txtRRdr, TYPE_INT)], (ROW_NUM, ROW_TIT),                            False, visible[COL_TRCK_NUM]),
                   (_('Title'),    [(txtLRdr, TYPE_STRING)],                           (ROW_TIT,),                                    True,  visible[COL_TITLE]),
                   (_('Artist'),   [(txtLRdr, TYPE_STRING)],                           (ROW_ART, ROW_ALB, ROW_NUM, ROW_TIT),          True,  visible[COL_ARTIST]),
                   (_('Album'),    [(txtLRdr, TYPE_STRING)],                           (ROW_ALB, ROW_NUM, ROW_TIT),                   True,  visible[COL_ALBUM]),
                   (_('Length'),   [(txtRRdr, TYPE_INT)],                              (ROW_LEN,),                                    False, visible[COL_LENGTH]),
                   (_('Bit Rate'), [(txtRRdr, TYPE_STRING)],                           (ROW_BTR, ROW_ART, ROW_ALB, ROW_NUM, ROW_TIT), False, visible[COL_BITRATE]),
                   (_('Genre'),    [(txtLRdr, TYPE_STRING)],                           (ROW_GNR, ROW_ART, ROW_ALB, ROW_NUM, ROW_TIT), False, visible[COL_GENRE]),
                   (_('Date'),     [(txtLRdr, TYPE_INT)],                              (ROW_DAT, ROW_ART, ROW_ALB, ROW_NUM, ROW_TIT), False, visible[COL_DATE]),
                   (_('Filename'), [(txtLRdr, TYPE_STRING)],                           (ROW_FIL,),                                    False, visible[COL_FILENAME]),
                   (_('Path'),     [(txtLRdr, TYPE_STRING)],                           (ROW_PTH,),                                    False, visible[COL_PATH]),
                   (None,          [(None, TYPE_PYOBJECT)],                            (None,),                                       False, False))

        self.list = ExtListView(columns, sortable=True, dndTargets=consts.DND_TARGETS.values(), useMarkup=False, canShowHideColumns=True)
        self.list.get_column(1).set_cell_data_func(txtLRdr, self.__fmtColumnColor)
        self.list.get_column(4).set_cell_data_func(txtRRdr, self.__fmtLengthColumn)
        self.list.enableDNDReordering()
        wTree.get_object('scrolled-tracklist').add(self.list)
        # GTK handlers
        self.list.connect('extlistview-dnd', self.onDND)
        self.list.connect('key-press-event', self.onKeyboard)
        self.list.connect('extlistview-modified', self.onListModified)
        self.list.connect('extlistview-button-pressed', self.onButtonPressed)
        self.list.connect('extlistview-selection-changed', self.onSelectionChanged)
        self.list.connect('extlistview-column-visibility-changed', self.onColumnVisibilityChanged)
        self.btnClear.connect('clicked', lambda widget: modules.postMsg(consts.MSG_CMD_TRACKLIST_CLR))
        self.btnRepeat.connect('toggled', self.onButtonRepeat)
        self.btnShuffle.connect('clicked', lambda widget: modules.postMsg(consts.MSG_CMD_TRACKLIST_SHUFFLE))
        # Restore preferences
        self.btnRepeat.set_active(tools.prefs.get(__name__, 'repeat-status', PREFS_DEFAULT_REPEAT_STATUS))
        # Set icons
        wTree.get_object('img-repeat').set_from_icon_name('stock_repeat', gtk.ICON_SIZE_BUTTON)
        wTree.get_object('img-shuffle').set_from_icon_name('stock_shuffle', gtk.ICON_SIZE_BUTTON)
Пример #20
0
    def playPaths(self, tree, paths, replace):
        """
            Replace/extend the tracklist
            If the list 'paths' is None, use the current selection
        """
        tracks = self.__getTracksFromPaths(tree, paths)

        if replace: modules.postMsg(consts.MSG_CMD_TRACKLIST_SET, {'tracks': tracks, 'playNow': True})
        else:       modules.postMsg(consts.MSG_CMD_TRACKLIST_ADD, {'tracks': tracks})
Пример #21
0
    def renameLibrary(self, oldName, newName):
        """ Rename a library """
        self.libraries[newName] = self.libraries[oldName]
        del self.libraries[oldName]

        oldPath = os.path.join(ROOT_PATH, oldName)
        newPath = os.path.join(ROOT_PATH, newName)
        shutil.move(oldPath, newPath)

        modules.postMsg(consts.MSG_CMD_EXPLORER_RENAME, {'modName': MOD_L10N, 'expName': oldName, 'newExpName': newName})
Пример #22
0
    def crop(self):
        """ Remove the unselected tracks """
        hadMark                = self.list.hasMark()
        self.previousTracklist = [row[ROW_TRK] for row in self.list]

        self.playtime = sum([row[ROW_LEN] for row in self.list.iterSelectedRows()])
        self.list.cropSelectedRows()

        if hadMark and not self.list.hasMark():
            modules.postMsg(consts.MSG_CMD_STOP)
Пример #23
0
    def onStop(self):
        """ Stop playing """
        self.__stopUpdateTimer()
        self.player.stop()
        self.nextURI = None

        if self.playbackTimer is not None:
            gobject.source_remove(self.playbackTimer)

        modules.postMsg(consts.MSG_EVT_STOPPED)
Пример #24
0
    def renameFolder(self, oldName, newName):
        """ Rename a folder """
        self.folders[newName] = self.folders[oldName]
        del self.folders[oldName]

        if oldName in self.treeState:
            self.treeState[newName] = self.treeState[oldName]
            del self.treeState[oldName]

        modules.postMsg(consts.MSG_CMD_EXPLORER_RENAME, {'modName': MOD_L10N, 'expName': oldName, 'newExpName': newName})
Пример #25
0
    def play(self, replace, path=None):
        """
            Replace/extend the tracklist
            If 'path' is None, use the current selection
        """
        if path is None: tracks = media.getTracks([row[ROW_FULLPATH] for row in self.tree.getSelectedRows()], self.addByFilename, not self.showHiddenFiles)
        else:            tracks = media.getTracks([self.tree.getRow(path)[ROW_FULLPATH]], self.addByFilename, not self.showHiddenFiles)

        if replace: modules.postMsg(consts.MSG_CMD_TRACKLIST_SET, {'tracks': tracks, 'playNow': True})
        else:       modules.postMsg(consts.MSG_CMD_TRACKLIST_ADD, {'tracks': tracks, 'playNow': False})
Пример #26
0
    def onStop(self):
        """ Stop playing """
        self.__stopUpdateTimer()
        self.player.stop()
        self.nextURI = None

        if self.playbackTimer is not None:
            GObject.source_remove(self.playbackTimer)

        modules.postMsg(consts.MSG_EVT_STOPPED)
Пример #27
0
 def on_searchbox_activate(self, entry):
     self.stop_searches()
     query = self.searchbox.get_text().strip()
     if len(query) < MIN_CHARS:
         msg = 'Search term has to have at least %d characters' % MIN_CHARS
         logging.info(msg)
         return
     query = self.searchbox.get_text().decode('utf-8')
     logging.info('Query: %s' % query)
     modules.postMsg(consts.MSG_EVT_SEARCH_START, {'query': query})
Пример #28
0
 def on_searchbox_activate(self, entry):
     self.stop_searches()
     query = self.searchbox.get_text().strip()
     if len(query) < MIN_CHARS:
         msg = 'Search term has to have at least %d characters' % MIN_CHARS
         logging.info(msg)
         return
     query = self.searchbox.get_text()
     logging.info('Query: %s' % query)
     modules.postMsg(consts.MSG_EVT_SEARCH_START, {'query': query})
Пример #29
0
    def play(self, path=None):
        """
            Replace/extend the tracklist
            If 'path' is None, use the current selection
        """
        if path is None:
            track_paths = [row[ROW_FULLPATH] for row in self.tree.getSelectedRows()]
        else:
            track_paths = [self.tree.getRow(path)[ROW_FULLPATH]]

        modules.postMsg(consts.MSG_EVT_LOAD_TRACKS, {'paths': track_paths})
Пример #30
0
    def onListModified(self):
        """ Some rows have been added/removed/moved """
        # Getting the trackdir takes virtually no time, so we can do it on every
        # paylist change
        tracks = self.getTrackDir()
        self.playtime = tracks.get_playtime()

        modules.postMsg(consts.MSG_EVT_NEW_TRACKLIST, {'tracks': tracks, 'playtime': self.playtime})

        if self.tree.hasMark():
            modules.postMsg(consts.MSG_EVT_TRACK_MOVED, {'hasPrevious': self.__hasPreviousTrack(), 'hasNext':  self.__hasNextTrack()})
Пример #31
0
    def onListModified(self):
        """ Some rows have been added/removed/moved """
        # Getting the trackdir takes virtually no time, so we can do it on every
        # paylist change
        tracks = self.getTrackDir()
        self.playtime = tracks.get_playtime()

        modules.postMsg(consts.MSG_EVT_NEW_TRACKLIST, {'tracks': tracks, 'playtime': self.playtime})

        if self.tree.hasMark():
            modules.postMsg(consts.MSG_EVT_TRACK_MOVED, {'hasPrevious': self.__hasPreviousTrack(), 'hasNext':  self.__hasNextTrack()})
Пример #32
0
    def updateTimerHandler(self):
        """ Regularly called during playback (can be paused) """
        if self.player.isPlaying():
            position  = self.player.getPosition()
            remaining = self.player.getDuration() - position

            modules.postMsg(consts.MSG_EVT_TRACK_POSITION, {'seconds': int(position / 1000000000)})

            if remaining < 4000000000 and self.nextURI is None:
                modules.postMsg(consts.MSG_EVT_NEED_BUFFER)

        return True
Пример #33
0
    def updateTimerHandler(self):
        """ Regularly called during playback (can be paused) """
        if self.player.isPlaying():
            position  = self.player.getPosition()
            remaining = self.player.getDuration() - position

            modules.postMsg(consts.MSG_EVT_TRACK_POSITION, {'seconds': int(position // 1000000000)})

            if remaining < 5000000000 and self.nextURI is None:
                modules.postMsg(consts.MSG_EVT_NEED_BUFFER)

        return True
Пример #34
0
    def playPaths(self, tree, paths, replace):
        """
            Replace/extend the tracklist
            If the list 'paths' is None, use the current selection
        """
        if self.tree.getNbChildren((0,)) != 0:
            tracks = self.getTracksFromPaths(tree, paths)

            if replace:
                modules.postMsg(consts.MSG_CMD_TRACKLIST_SET, {"tracks": tracks, "playNow": True})
            else:
                modules.postMsg(consts.MSG_CMD_TRACKLIST_ADD, {"tracks": tracks, "playNow": False})
Пример #35
0
    def onListModified(self, list):
        """ Some rows have been added/removed/moved """
#        self.btnClear.set_sensitive(len(list) != 0)
        self.btnShuffle.set_sensitive(len(list) != 0)

        # Update playlist length and playlist position for all tracks
        for position, row in enumerate(self.list.getAllRows()):
            row[ROW_TRK].setPlaylistPos(position + 1)
            row[ROW_TRK].setPlaylistLen(len(self.list))

        modules.postMsg(consts.MSG_EVT_NEW_TRACKLIST, {'tracks': self.getAllTracks(), 'playtime': self.playtime})
        modules.postMsg(consts.MSG_EVT_TRACK_MOVED,   {'hasPrevious': self.__getPreviousTrackIdx() != -1, 'hasNext':  self.__getNextTrackIdx() != -1})
Пример #36
0
    def renameFolder(self, oldName, newName):
        """ Rename a folder """
        self.folders[newName] = self.folders[oldName]
        del self.folders[oldName]

        savedStates = prefs.get(__name__, 'saved-states', {})
        if oldName in savedStates:
            savedStates[newName] = savedStates[oldName]
            del savedStates[oldName]
            prefs.set(__name__, 'saved-states', savedStates)

        modules.postMsg(consts.MSG_CMD_EXPLORER_RENAME,   {'modName': MOD_L10N, 'expName': oldName, 'newExpName': newName})
def realStartup():
    """
        Perform all the initialization stuff which is not mandatory to display the window
        This function should be called within the GTK main loop, once the window has been displayed
    """
    import atexit, dbus.mainloop.glib, gui.about, modules, webbrowser

    def onDelete(win, event):
        """ Use our own quit sequence, that will itself destroy the window """
        window.hide()
        modules.postQuitMsg()
        return True

    def onResize(win, rect):
        """ Save the new size of the window """
        if win.window is not None and not win.window.get_state() & gtk.gdk.WINDOW_STATE_MAXIMIZED:
            prefs.set(__name__, 'win-width',  rect.width)
            prefs.set(__name__, 'win-height', rect.height)

            if prefs.get(__name__, 'view-mode', DEFAULT_VIEW_MODE)in (consts.VIEW_MODE_FULL, consts.VIEW_MODE_PLAYLIST):
                prefs.set(__name__, 'full-win-height', rect.height)

    def onState(win, evt):
        """ Save the new state of the window """
        prefs.set(__name__, 'win-is-maximized', bool(evt.new_window_state & gtk.gdk.WINDOW_STATE_MAXIMIZED))

    def atExit():
        """ Final function, called just before exiting the Python interpreter """
        prefs.save()
        log.logger.info('Stopped')

    # D-Bus
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

    # Make sure to perform a few actions before exiting the Python interpreter
    atexit.register(atExit)
    signal.signal(signal.SIGTERM, lambda sig, frame: onDelete(window, None))

    # GTK handlers
    window.connect('delete-event', onDelete)
    window.connect('size-allocate', onResize)
    window.connect('window-state-event', onState)
    paned.connect('size-allocate', lambda win, rect: prefs.set(__name__, 'paned-pos', paned.get_position()))
    wTree.get_widget('menu-mode-mini').connect('activate', onViewMode, consts.VIEW_MODE_MINI)
    wTree.get_widget('menu-mode-full').connect('activate', onViewMode, consts.VIEW_MODE_FULL)
    wTree.get_widget('menu-mode-playlist').connect('activate', onViewMode, consts.VIEW_MODE_PLAYLIST)
    wTree.get_widget('menu-quit').connect('activate', lambda item: onDelete(window, None))
    wTree.get_widget('menu-about').connect('activate', lambda item: gui.about.show(window))
    wTree.get_widget('menu-help').connect('activate', lambda item: webbrowser.open(consts.urlHelp))
    wTree.get_widget('menu-preferences').connect('activate', lambda item: modules.showPreferences())

    # Let's go
    modules.postMsg(consts.MSG_EVT_APP_STARTED)
Пример #38
0
    def renameFolder(self, oldName, newName):
        """ Rename a folder """
        self.folders[newName] = self.folders[oldName]
        del self.folders[oldName]

        if oldName in self.treeState:
            self.treeState[newName] = self.treeState[oldName]
            del self.treeState[oldName]

        modules.postMsg(consts.MSG_CMD_EXPLORER_RENAME, {
            'modName': MOD_L10N,
            'expName': oldName,
            'newExpName': newName
        })
Пример #39
0
 def onMediaKey(self, appName, action):
     """ A media key has been pressed """
     if action == 'Stop': modules.postMsg(consts.MSG_CMD_STOP)
     elif action == 'Next': modules.postMsg(consts.MSG_CMD_NEXT)
     elif action == 'Previous': modules.postMsg(consts.MSG_CMD_PREVIOUS)
     elif action in ['Play', 'Pause']:
         modules.postMsg(consts.MSG_CMD_TOGGLE_PAUSE)
Пример #40
0
    def onScaleValueChanged(self, scale, idx):
        """ The user has moved one of the scales """
        # Add a 'custom' entry to the presets if needed
        if self.preset is not None:
            self.preset = None
            prefs.set(__name__, 'preset', self.preset)
            self.combo.handler_block_by_func(self.onPresetChanged)
            self.comboStore.insert(0, (False, _('Custom'), None))
            self.comboStore.insert(1, (True, '', None))
            self.combo.set_active(0)
            self.combo.handler_unblock_by_func(self.onPresetChanged)

        self.lvls[idx] = scale.get_value()
        prefs.set(__name__, 'levels', self.lvls)
        modules.postMsg(consts.MSG_CMD_SET_EQZ_LVLS, {'lvls': self.lvls})
Пример #41
0
    def onTogglePause(self):
        """ Switch between play/pause """
        if self.player.isPaused():
            if self.queuedSeek is not None:
                self.player.seek(self.queuedSeek*1000000000)
                self.queuedSeek = None
            self.player.play()
            modules.postMsg(consts.MSG_EVT_UNPAUSED)
        elif self.player.isPlaying():

            if self.playbackTimer is not None:
                GObject.source_remove(self.playbackTimer)

            self.player.pause()
            modules.postMsg(consts.MSG_EVT_PAUSED)
Пример #42
0
    def onShowPopupMenu(self, tree, button, time, path):
        # Keep reference after method exits.
        self.popup_menu = Gtk.Menu()

        # Remove
        remove = Gtk.MenuItem.new_with_label(_('Remove'))
        self.popup_menu.append(remove)
        if path is None:
            remove.set_sensitive(False)
        else:
            remove.connect('activate', lambda item: self.remove())

        # Clear
        clear = Gtk.MenuItem.new_with_label(_('Clear Playlist'))
        self.popup_menu.append(clear)

        # Save to m3u
        export_m3u = Gtk.MenuItem.new_with_label(_('Export playlist to file'))
        self.popup_menu.append(export_m3u)

        # Save to dir
        export_dir = Gtk.MenuItem.new_with_label(_('Export playlist to directory'))
        self.popup_menu.append(export_dir)

        if len(tree.store) == 0:
            clear.set_sensitive(False)
            export_m3u.set_sensitive(False)
            export_dir.set_sensitive(False)
        else:
            clear.connect('activate', lambda item: modules.postMsg(consts.MSG_CMD_TRACKLIST_CLR))
            export_m3u.connect('activate', lambda item: self.export_playlist_to_m3u())
            export_dir.connect('activate', lambda item: self.export_playlist_to_dir())

        self.popup_menu.show_all()
        self.popup_menu.popup(None, None, None, None, button, time)
Пример #43
0
    def onKeyboard(self, list, event):
        """ Keyboard shortcuts """
        keyname = Gdk.keyval_name(event.keyval)

        if keyname == 'Delete':   self.remove()
        elif keyname == 'Return': self.jumpTo(self.tree.getFirstSelectedRow())
        elif keyname == 'space':  modules.postMsg(consts.MSG_CMD_TOGGLE_PAUSE)
        elif keyname == 'Escape': modules.postMsg(consts.MSG_CMD_STOP)
        elif keyname == 'Left':   modules.postMsg(consts.MSG_CMD_STEP, {'seconds': -5})
        elif keyname == 'Right':  modules.postMsg(consts.MSG_CMD_STEP, {'seconds': 5})
Пример #44
0
    def onAddFolder(self, btn):
        """ Let the user add a new folder to the list """
        from gui import selectPath

        result = selectPath.SelectPath(MOD_L10N, self.cfgWin,
                                       self.folders.keys()).run()

        if result is not None:
            name, path = result
            self.folders[name] = path
            self.populateFolderList()
            modules.postMsg(
                consts.MSG_CMD_EXPLORER_ADD, {
                    'modName': MOD_L10N,
                    'expName': name,
                    'icon': icons.dirMenuIcon(),
                    'widget': self.scrolled
                })
Пример #45
0
    def onModUnloaded(self):
        """ The module has been unloaded """
        if self.currTrack is not None:
            modules.postMsg(
                consts.MSG_CMD_SET_COVER, {
                    'track': self.currTrack,
                    'pathThumbnail': None,
                    'pathFullSize': None
                })

        # Delete covers that have been generated by this module
        for covers in self.coverMap.values():
            if os.path.exists(covers[CVR_THUMB]):
                os.remove(covers[CVR_THUMB])
            if os.path.exists(covers[CVR_FULL]):
                os.remove(covers[CVR_FULL])
        self.coverMap = None

        # Delete blacklist
        self.coverBlacklist = None
Пример #46
0
    def set(self, tracks, playNow):
        """ Replace the tracklist, clear it if tracks is None """
        self.playtime = 0

        if type(tracks) == list:
            trackdir = media.TrackDir(None, flat=True)
            trackdir.tracks = tracks
            tracks = trackdir

        if self.tree.hasMark() and ((not playNow) or (tracks is None) or tracks.empty()):
            modules.postMsg(consts.MSG_CMD_STOP)

        self.tree.clear()

        if tracks is not None and not tracks.empty():
            self.insert(tracks, playNow=playNow)

        self.tree.collapse_all()

        self.onListModified()
Пример #47
0
    def onTrackEnded(self, withError):
        """ The current track has ended, jump to the next one if any """
        current_iter = self.tree.getMark()

        # If an error occurred with the current track, flag it as such
        if withError and current_iter:
            self.tree.setItem(current_iter, ROW_ICO, icons.errorMenuIcon())

        # Find the next 'playable' track (not already flagged)
        next = self.__getNextTrackIter()
        if next:
            send_play_msg = True
            if current_iter:
                track_name = self.tree.getTrack(current_iter).getURI()
                send_play_msg = (track_name != self.bufferedTrack)
            self.jumpTo(next, sendPlayMsg=send_play_msg, forced=False)
            self.bufferedTrack = None
            return

        self.bufferedTrack = None
        modules.postMsg(consts.MSG_CMD_STOP)
Пример #48
0
    def jumpTo(self, iter, sendPlayMsg=True, forced=True):
        """ Jump to the track located at the given iter """
        if not iter:
            return

        mark = self.tree.getMark()
        if mark:
            self.set_track_playing(mark, False)

        self.tree.setMark(iter)
        self.tree.scroll(iter)

        # Check track
        track = self.tree.getTrack(iter)
        if not track:
            # Row may be a directory
            self.jumpTo(self.__getNextTrackIter())
            return

        self.set_track_playing(iter, True)
        self.paused = False

        if sendPlayMsg:
            modules.postMsg(consts.MSG_CMD_PLAY, {'uri': track.getURI(), 'forced': forced})

        modules.postMsg(consts.MSG_EVT_NEW_TRACK,   {'track': track})
        modules.postMsg(consts.MSG_EVT_TRACK_MOVED, {'hasPrevious': self.__hasPreviousTrack(), 'hasNext': self.__hasNextTrack()})
Пример #49
0
    def timerFunc(self):
        """ Move a bit the scales to their target value """
        isFinished = True

        # Move the scales a bit
        for i in range(10):
            currLvl = self.scales[i].get_value()
            targetLvl = self.targetLvls[i]
            difference = targetLvl - currLvl

            if abs(difference) <= 0.25:
                newLvl = targetLvl
            else:
                newLvl = currLvl + (difference / 8.0)
                isFinished = False

            self.lvls[i] = newLvl
            self.scales[i].set_value(newLvl)

        # Set the equalizer to the new levels
        modules.postMsg(consts.MSG_CMD_SET_EQZ_LVLS, {'lvls': self.lvls})

        if isFinished:
            self.timer = None
            prefs.set(__name__, 'levels', self.lvls)

            # Make sure labels are up to date (sometimes they aren't when we're done with the animation)
            # Also unblock the handlers
            for i in range(10):
                self.scales[i].queue_draw()
                self.scales[i].handler_unblock_by_func(
                    self.onScaleValueChanged)

            return False

        return True
Пример #50
0
    def play(self, replace, path=None):
        """
            Replace/extend the tracklist
            If 'path' is None, use the current selection
        """
        if path is None:
            tracks = media.getTracks(
                [row[ROW_FULLPATH] for row in self.tree.getSelectedRows()],
                self.addByFilename, not self.showHiddenFiles)
        else:
            tracks = media.getTracks([self.tree.getRow(path)[ROW_FULLPATH]],
                                     self.addByFilename,
                                     not self.showHiddenFiles)

        if replace:
            modules.postMsg(consts.MSG_CMD_TRACKLIST_SET, {
                'tracks': tracks,
                'playNow': True
            })
        else:
            modules.postMsg(consts.MSG_CMD_TRACKLIST_ADD, {
                'tracks': tracks,
                'playNow': False
            })
Пример #51
0
    def onRemoveSelectedFolder(self, list):
        """ Remove the selected media folder """
        import gui

        if list.getSelectedRowsCount() == 1:
            remark = _(
                'You will be able to add this root folder again later on if you wish so.'
            )
            question = _('Remove the selected entry?')
        else:
            remark = _(
                'You will be able to add these root folders again later on if you wish so.'
            )
            question = _('Remove all selected entries?')

        if gui.questionMsgBox(
                self.cfgWin, question,
                '%s %s' % (_('Your media files will not be deleted.'),
                           remark)) == gtk.RESPONSE_YES:
            for row in self.cfgList.getSelectedRows():
                name = row[0]
                modules.postMsg(consts.MSG_CMD_EXPLORER_REMOVE, {
                    'modName': MOD_L10N,
                    'expName': name
                })
                del self.folders[name]

                # Remove the tree, if any, from the scrolled window
                if self.currRoot == name:
                    self.currRoot = None

                # Remove the saved state of the tree, if any
                if name in self.treeState:
                    del self.treeState[name]

            self.cfgList.removeSelectedRows()
Пример #52
0
    def __onTrackEnded(self, error):
        """ Called to signal eos and errors """
        self.nextURI = None

        if error: modules.postMsg(consts.MSG_EVT_TRACK_ENDED_ERROR)
        else:     modules.postMsg(consts.MSG_EVT_TRACK_ENDED_OK)
Пример #53
0
 def onSkipTrack(self, notification, action):
     """ The user wants to skip the current track """
     if self.hasNext: modules.postMsg(consts.MSG_CMD_NEXT)
     else: modules.postMsg(consts.MSG_CMD_STOP)
Пример #54
0
 def onAppStarted(self):
     """ The application has started """
     self.modInit()
     modules.postMsg(consts.MSG_CMD_ENABLE_EQZ)
     modules.postMsg(consts.MSG_CMD_SET_EQZ_LVLS, {'lvls': self.lvls})
Пример #55
0
    def onNewTrack(self, track):
        """ A new track is being played, try to retrieve the corresponding cover """
        # Make sure we have enough information
        if track.getArtist() == consts.UNKNOWN_ARTIST or track.getAlbum(
        ) == consts.UNKNOWN_ALBUM:
            modules.postMsg(consts.MSG_CMD_SET_COVER, {
                'track': track,
                'pathThumbnail': None,
                'pathFullSize': None
            })
            return

        album = track.getAlbum().lower()
        artist = track.getArtist().lower()
        rawCover = None
        self.currTrack = track

        # Let's see whether we already have the cover
        if (artist, album) in self.coverMap:
            covers = self.coverMap[(artist, album)]
            pathFullSize = covers[CVR_FULL]
            pathThumbnail = covers[CVR_THUMB]

            # Make sure the files are still there
            if os.path.exists(pathThumbnail) and os.path.exists(pathFullSize):
                modules.postMsg(
                    consts.MSG_CMD_SET_COVER, {
                        'track': track,
                        'pathThumbnail': pathThumbnail,
                        'pathFullSize': pathFullSize
                    })
                return

        # Should we check for a user cover?
        if not prefs.get(__name__, 'download-covers', PREFS_DFT_DOWNLOAD_COVERS)        \
            or prefs.get(__name__, 'prefer-user-covers', PREFS_DFT_PREFER_USER_COVERS):
            rawCover = self.getUserCover(os.path.dirname(track.getFilePath()))

        # Is it in our cache?
        if rawCover is None:
            rawCover = self.getFromCache(artist, album)

        # If we still don't have a cover, maybe we can try to download it
        if rawCover is None:
            modules.postMsg(consts.MSG_CMD_SET_COVER, {
                'track': track,
                'pathThumbnail': None,
                'pathFullSize': None
            })

            if prefs.get(__name__, 'download-covers',
                         PREFS_DFT_DOWNLOAD_COVERS):
                rawCover = self.getFromInternet(artist, album)

        # If we still don't have a cover, too bad
        # Otherwise, generate a thumbnail and a full size cover, and add it to our cover map
        if rawCover is not None:
            import tempfile

            thumbnail = tempfile.mktemp() + '.png'
            fullSizeCover = tempfile.mktemp() + '.png'
            self.generateThumbnail(rawCover, thumbnail, 'PNG')
            self.generateFullSizeCover(rawCover, fullSizeCover, 'PNG')
            if os.path.exists(thumbnail) and os.path.exists(fullSizeCover):
                self.coverMap[(artist, album)] = (thumbnail, fullSizeCover)
                modules.postMsg(
                    consts.MSG_CMD_SET_COVER, {
                        'track': track,
                        'pathThumbnail': thumbnail,
                        'pathFullSize': fullSizeCover
                    })
            else:
                modules.postMsg(consts.MSG_CMD_SET_COVER, {
                    'track': track,
                    'pathThumbnail': None,
                    'pathFullSize': None
                })
Пример #56
0
 def onBufferingNeeded(self):
     """ The current track is close to its end, so we try to buffer the next one to avoid gaps """
     where = self.__getNextTrackIter()
     if where:
         self.bufferedTrack = self.tree.getItem(where, ROW_TRK).getURI()
         modules.postMsg(consts.MSG_CMD_BUFFER, {'uri': self.bufferedTrack})
Пример #57
0
 def onLoadTracks(self, paths):
     modules.postMsg(consts.MSG_CMD_TRACKLIST_ADD, {
         'tracks': media.getTracks(paths),
         'playNow': True
     })
Пример #58
0
 def on_searchbox_changed(self, entry):
     if self.searchbox.get_text().strip() == '':
         self.stop_searches()
         modules.postMsg(consts.MSG_EVT_SEARCH_RESET, {})
Пример #59
0
    def onAppStarted(self):
        """ This is the real initialization function, called when the module has been loaded """
        wTree                  = tools.prefs.getWidgetsTree()
        self.playtime          = 0
        self.bufferedTrack     = None
        # Retrieve widgets
        self.window     = wTree.get_object('win-main')

        columns = (('',   [(Gtk.CellRendererPixbuf(), GdkPixbuf.Pixbuf), (Gtk.CellRendererText(), GObject.TYPE_STRING)], True),
                   (None, [(None, GObject.TYPE_PYOBJECT)], False),
                  )

        self.tree = TrackTreeView(columns, use_markup=True)

        self.tree.enableDNDReordering()
        target = Gtk.TargetEntry.new(*DND_INTERNAL_TARGET)
        targets = Gtk.TargetList.new([target])
        self.tree.setDNDSources(targets)

        wTree.get_object('scrolled-tracklist').add(self.tree)

        # GTK handlers
        self.tree.connect('exttreeview-button-pressed', self.onMouseButton)
        self.tree.connect('tracktreeview-dnd', self.onDND)
        self.tree.connect('key-press-event', self.onKeyboard)
        self.tree.get_model().connect('row-deleted', self.onRowDeleted)

        (options, args) = prefs.getCmdLine()

        self.savedPlaylist = os.path.join(consts.dirCfg, 'saved-playlist')
        self.paused = False


        # Populate the playlist with the saved playlist
        dump = None
        if os.path.exists(self.savedPlaylist):
            try:
                dump = pickleLoad(self.savedPlaylist)
            except (EOFError, IOError):
                msg = '[%s] Unable to restore playlist from %s\n\n%s'
                log.logger.error(msg % (MOD_INFO[modules.MODINFO_NAME],
                                self.savedPlaylist, traceback.format_exc()))

        if dump:
            self.restoreTreeDump(dump)
            log.logger.info('[%s] Restored playlist' % MOD_INFO[modules.MODINFO_NAME])
            self.tree.collapse_all()
            self.select_last_played_track()
            self.onListModified()

        commands, args = tools.separate_commands_and_tracks(args)

        # Add commandline tracks to the playlist
        if args:
            log.logger.info('[%s] Filling playlist with files given on command line' % MOD_INFO[modules.MODINFO_NAME])
            tracks  = media.getTracks([os.path.abspath(arg) for arg in args])
            playNow = not 'stop' in commands and not 'pause' in commands
            modules.postMsg(consts.MSG_CMD_TRACKLIST_ADD, {'tracks': tracks, 'playNow': playNow})
        elif 'play' in commands:
            modules.postMsg(consts.MSG_CMD_TOGGLE_PAUSE)

        # Automatically save the content at regular intervals
        GObject.timeout_add_seconds(SAVE_INTERVAL, self.save_track_tree)