Пример #1
0
    def configure(self, parent):
        """ Show the configuration window """
        if self.cfgWin is None:
            from pogo import gui

            # Create the window
            self.cfgWin = gui.window.Window('DesktopNotification.ui', 'vbox1',
                                            __name__,
                                            MOD_INFO[modules.MODINFO_L10N],
                                            355, 345)
            self.cfgWin.getWidget('btn-ok').connect('clicked', self.onBtnOk)
            self.cfgWin.getWidget('btn-help').connect('clicked',
                                                      self.onBtnHelp)
            self.cfgWin.getWidget('btn-cancel').connect(
                'clicked', lambda btn: self.cfgWin.hide())

            # Disable the 'Skip track' button if the server doesn't support buttons in notifications
            if 'actions' not in Notify.get_server_caps():
                self.cfgWin.getWidget('chk-skipTrack').set_sensitive(False)

        if not self.cfgWin.isVisible():
            self.cfgWin.getWidget('txt-title').set_text(
                prefs.get(__name__, 'title', PREFS_DEFAULT_TITLE))
            self.cfgWin.getWidget('spn-duration').set_value(
                prefs.get(__name__, 'timeout', PREFS_DEFAULT_TIMEOUT))
            self.cfgWin.getWidget('txt-body').get_buffer().set_text(
                prefs.get(__name__, 'body', PREFS_DEFAULT_BODY))
            self.cfgWin.getWidget('chk-skipTrack').set_active(
                prefs.get(__name__, 'skip-track', PREFS_DEFAULT_SKIP_TRACK))
            self.cfgWin.getWidget('btn-ok').grab_focus()

        self.cfgWin.show()
Пример #2
0
    def onBtnOk(self, btn):
        """ Save new preferences """
        # Skipping tracks
        newSkipTrack = self.cfgWin.getWidget('chk-skipTrack').get_active()
        oldSkipTrack = prefs.get(__name__, 'skip-track',
                                 PREFS_DEFAULT_SKIP_TRACK)
        prefs.set(__name__, 'skip-track', newSkipTrack)

        if oldSkipTrack != newSkipTrack and self.notif is not None:
            if newSkipTrack:
                self.notif.add_action('stop', _('Skip track'),
                                      self.onSkipTrack)
            else:
                self.notif.clear_actions()

        # Timeout
        newTimeout = int(self.cfgWin.getWidget('spn-duration').get_value())
        oldTimeout = prefs.get(__name__, 'timeout', PREFS_DEFAULT_TIMEOUT)

        prefs.set(__name__, 'timeout', newTimeout)

        if oldTimeout != newTimeout and self.notif is not None:
            self.notif.set_timeout(newTimeout * 1000)

        # Other preferences
        prefs.set(__name__, 'title',
                  self.cfgWin.getWidget('txt-title').get_text())
        (start,
         end) = self.cfgWin.getWidget('txt-body').get_buffer().get_bounds()
        prefs.set(
            __name__, 'body',
            self.cfgWin.getWidget('txt-body').get_buffer().get_text(
                start, end, False))
        self.cfgWin.hide()
Пример #3
0
    def __createNotification(self, title, body, icon):
        """ Create the Notification object """
        if not Notify.init(consts.appNameShort):
            logger.error('[%s] Initialization of python-notify failed' %
                         MOD_INFO[modules.MODINFO_NAME])

        self.notif = Notify.Notification.new(title, body, icon)
        self.notif.set_urgency(Notify.Urgency.LOW)
        self.notif.set_timeout(
            prefs.get(__name__, 'timeout', PREFS_DEFAULT_TIMEOUT) * 1000)

        if prefs.get(__name__, 'skip-track', PREFS_DEFAULT_SKIP_TRACK):
            self.notif.add_action('stop', _('Skip track'), self.onSkipTrack)
Пример #4
0
    def onAppStarted(self):
        """ The module has been loaded """
        self.tree = None
        self.cfgWin = None
        self.scrolled = Gtk.ScrolledWindow()
        self.treeState = prefs.get(__name__, 'saved-states', None)

        self.scrolled.set_shadow_type(Gtk.ShadowType.IN)
        self.scrolled.set_policy(Gtk.PolicyType.AUTOMATIC,
                                 Gtk.PolicyType.AUTOMATIC)
        self.scrolled.show()

        left_vbox = prefs.getWidgetsTree().get_object('vbox3')
        left_vbox.pack_start(self.scrolled, True, True, 0)

        self.static_paths = ['/', consts.dirBaseUsr]

        self.displaying_results = False
        self.populate_tree()

        music_paths = self.get_music_paths_from_tree()
        modules.postMsg(consts.MSG_EVT_MUSIC_PATHS_CHANGED,
                        {'paths': music_paths})

        self.tree.connect('drag-begin', self.onDragBegin)
Пример #5
0
    def onAppStarted(self):
        """ Real initialization function, called when this module has been loaded """
        # Widgets
        self.currTrack = None

        show_thumb = prefs.get(__name__, 'show_thumb', True)
        self.cover_spot = CoverSpot(show_thumb)
Пример #6
0
def main():
    log.logger.info('Started')

    # Localization
    locale.setlocale(locale.LC_ALL, '')
    gettext.textdomain(consts.appNameShort)
    gettext.bindtextdomain(consts.appNameShort, consts.dirLocale)

    # Command line
    prefs.setCmdLine((optOptions, optArgs))

    # Create the GUI
    wTree = loadGladeFile('MainWindow.ui')
    paned = wTree.get_object('pan-main')
    window = wTree.get_object('win-main')
    prefs.setWidgetsTree(wTree)

    window.set_icon_list([
        GdkPixbuf.Pixbuf.new_from_file(consts.fileImgIcon16),
        GdkPixbuf.Pixbuf.new_from_file(consts.fileImgIcon24),
        GdkPixbuf.Pixbuf.new_from_file(consts.fileImgIcon32),
        GdkPixbuf.Pixbuf.new_from_file(consts.fileImgIcon48),
        GdkPixbuf.Pixbuf.new_from_file(consts.fileImgIcon64),
        GdkPixbuf.Pixbuf.new_from_file(consts.fileImgIcon128)])

    # RGBA support
    # TODO: Is this still needed?
    visual = window.get_screen().get_rgba_visual()
    window.set_visual(visual)

    # Show all widgets and restore the window size BEFORE hiding some of them
    # when restoring the view mode
    # Resizing must be done before showing the window to make sure that the WM
    # correctly places the window
    if prefs.get(__name__, 'win-is-maximized', DEFAULT_MAXIMIZED_STATE):
        window.maximize()

    height = prefs.get(__name__, 'win-height', DEFAULT_WIN_HEIGHT)
    window.resize(prefs.get(__name__, 'win-width', DEFAULT_WIN_WIDTH), height)
    window.show_all()

    paned.set_position(prefs.get(__name__, 'paned-pos', DEFAULT_PANED_POS))

    # Initialization done, let's continue the show
    GObject.idle_add(realStartup, window, paned)
    Gtk.main()
Пример #7
0
    def onResize(win, rect):
        """ Save the new size of the window """
        maximized = win.get_state() & Gdk.WindowState.MAXIMIZED
        if not maximized:
            prefs.set(__name__, 'win-width', rect.width)
            prefs.set(__name__, 'win-height', rect.height)

            view_mode = prefs.get(__name__, 'view-mode', DEFAULT_VIEW_MODE)
            if view_mode in (consts.VIEW_MODE_FULL, consts.VIEW_MODE_PLAYLIST):
                prefs.set(__name__, 'full-win-height', rect.height)
Пример #8
0
    def showNotification(self):
        """ Show the notification based on the current track """
        self.timeout = None

        # Can this happen?
        if self.currTrack is None:
            return False

        # Contents
        body = self.currTrack.formatHTMLSafe(
            prefs.get(__name__, 'body', PREFS_DEFAULT_BODY))
        title = self.currTrack.format(
            prefs.get(__name__, 'title', PREFS_DEFAULT_TITLE))

        # Icon
        if self.currCover is None:
            img = consts.fileImgIcon64
        else:
            img = self.currCover

        if os.path.isfile(img):
            icon = 'file://' + img
        else:
            icon = Gtk.STOCK_DIALOG_INFO

        # Create / Update the notification and show it
        if self.notif is None:
            self.__createNotification(title, body, icon)
        else:
            self.notif.update(title, body, icon)

        # Catch errors that occur when pynotify is not installed properly.
        try:
            self.notif.show()
        except GObject.GError:
            pass

        return False
Пример #9
0
    def configure(self, parent):
        """ Show the configuration window """
        if self.cfgWin is None:
            from pogo.gui.window import Window

            self.cfgWin = Window('Covers.ui', 'vbox1', __name__,
                                 MOD_INFO[modules.MODINFO_L10N], 320, 265)
            self.cfgWin.getWidget('btn-ok').connect('clicked', self.onBtnOk)
            self.cfgWin.getWidget('img-lastfm').set_from_file(
                os.path.join(consts.dirPix, 'audioscrobbler.png'))
            self.cfgWin.getWidget('btn-help').connect('clicked',
                                                      self.onBtnHelp)
            self.cfgWin.getWidget('chk-downloadCovers').connect(
                'toggled', self.onDownloadCoversToggled)
            self.cfgWin.getWidget('btn-cancel').connect(
                'clicked', lambda btn: self.cfgWin.hide())

        if not self.cfgWin.isVisible():
            downloadCovers = prefs.get(__name__, 'download-covers',
                                       PREFS_DFT_DOWNLOAD_COVERS)
            preferUserCovers = prefs.get(__name__, 'prefer-user-covers',
                                         PREFS_DFT_PREFER_USER_COVERS)
            userCoverFilenames = prefs.get(__name__, 'user-cover-filenames',
                                           PREFS_DFT_USER_COVER_FILENAMES)

            self.cfgWin.getWidget('btn-ok').grab_focus()
            self.cfgWin.getWidget('txt-filenames').set_text(
                ', '.join(userCoverFilenames))
            self.cfgWin.getWidget('chk-downloadCovers').set_active(
                downloadCovers)
            self.cfgWin.getWidget('chk-preferUserCovers').set_active(
                preferUserCovers)
            self.cfgWin.getWidget('chk-preferUserCovers').set_sensitive(
                downloadCovers)

        self.cfgWin.show()
Пример #10
0
    def getUserCover(self, trackPath):
        """ Return the path to a cover file in trackPath, None if no cover found """
        # Create a dictionary with candidates
        candidates = {}
        for (file, path) in tools.listDir(trackPath, True):
            (name, ext) = os.path.splitext(file.lower())
            if ext in ACCEPTED_FILE_FORMATS:
                candidates[name] = path

        # Check each possible name using the its index in the list as its priority
        for name in prefs.get(__name__, 'user-cover-filenames',
                              PREFS_DFT_USER_COVER_FILENAMES):
            if name in candidates:
                return candidates[name]

            if name == '*' and len(candidates) != 0:
                return next(iter(candidates.values()))

        return None
Пример #11
0
 def modInit(self):
     """ Initialize the module """
     self.lvls = prefs.get(__name__, 'levels',
                           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
     self.preset = prefs.get(__name__, 'preset', 'Flat')
     self.cfgWindow = None
Пример #12
0
                func(*args)
            else:
                self.handlers[msg](**params)


# --== Entry point ==--

mModDir = os.path.dirname(__file__)  # Where modules are located
mModules = {}  # All known modules associated to an 'active' boolean
mHandlers = dict([(msg, set()) for msg in range(consts.MSG_END_VALUE)
                  ])  # For each message, store the set of registered modules
mModulesLock = threading.Lock(
)  # Protects the modules list from concurrent access
mHandlersLock = threading.Lock(
)  # Protects the handlers list from concurrent access
mEnabledModules = prefs.get(__name__, 'enabled_modules',
                            [])  # List of modules currently enabled

# Do not load modules in blacklist. They also won't show up in the preferences.
blacklist = ['__init__.py']


def load_enabled_modules():
    # Find modules, instantiate those that are mandatory or that have been previously enabled by the user
    sys.path.append(mModDir)
    for file in sorted(
            os.path.splitext(file)[0] for file in os.listdir(mModDir)
            if file.endswith('.py') and file not in blacklist):
        try:
            pModule = __import__(file)
            modInfo = getattr(pModule, 'MOD_INFO')
Пример #13
0
def realStartup(window, paned):
    """
    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
    """

    # Is the application started for the first time?
    first_start = prefs.get(__name__, 'first-time', True)
    logging.debug('First start: {}'.format(first_start))
    if first_start:
        prefs.set(__name__, 'first-time', False)

        # Enable some modules by default
        prefs.set('modules', 'enabled_modules', ['Covers', 'Desktop Notification'])

    import atexit
    import signal
    import dbus.mainloop.glib

    from pogo import modules

    modules.load_enabled_modules()

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

    def onResize(win, rect):
        """ Save the new size of the window """
        maximized = win.get_state() & Gdk.WindowState.MAXIMIZED
        if not maximized:
            prefs.set(__name__, 'win-width', rect.width)
            prefs.set(__name__, 'win-height', rect.height)

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

    def onPanedResize(win, rect):
        prefs.set(__name__, 'paned-pos', paned.get_position())

    def onState(win, event):
        """ Save the new state of the window """
        if event.changed_mask & Gdk.WindowState.MAXIMIZED:
            maximized = bool(event.new_window_state & Gdk.WindowState.MAXIMIZED)
            prefs.set(__name__, 'win-is-maximized', 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)

    # Register some handlers (Signal SIGKILL cannot be caught)
    atexit.register(atExit)
    signal.signal(signal.SIGINT, lambda _sig, _frame: onDelete(window, None))
    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', onPanedResize)

    # Let's go
    GObject.idle_add(modules.postMsg, consts.MSG_EVT_APP_STARTED)
Пример #14
0
 def select_last_played_track(self):
     last_path = prefs.get(__name__, 'last-played-track', None)
     if last_path:
         parent_path = (last_path[0],)
         GObject.idle_add(self.tree.scroll_to_cell, parent_path)
         self.tree.get_selection().select_path(parent_path)
Пример #15
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
                })