Esempio n. 1
0
class Itunes(MetaSource):

    item_types = {
        'itunes_rating':      types.INTEGER,  # 0..100 scale
        'itunes_playcount':   types.INTEGER,
        'itunes_skipcount':   types.INTEGER,
        'itunes_lastplayed':  DateType(),
        'itunes_lastskipped': DateType(),
    }

    def __init__(self, config, log):
        super(Itunes, self).__init__(config, log)

        config.add({'itunes': {
            'library': '~/Music/iTunes/iTunes Library.xml'
        }})

        # Load the iTunes library, which has to be the .xml one (not the .itl)
        library_path = config['itunes']['library'].as_filename()

        try:
            self._log.debug(
                u'loading iTunes library from {0}'.format(library_path))
            with create_temporary_copy(library_path) as library_copy:
                raw_library = plistlib.readPlist(library_copy)
        except IOError as e:
            raise ConfigValueError(u'invalid iTunes library: ' + e.strerror)
        except Exception:
            # It's likely the user configured their '.itl' library (<> xml)
            if os.path.splitext(library_path)[1].lower() != '.xml':
                hint = u': please ensure that the configured path' \
                       u' points to the .XML library'
            else:
                hint = ''
            raise ConfigValueError(u'invalid iTunes library' + hint)

        # Make the iTunes library queryable using the path
        self.collection = {_norm_itunes_path(track['Location']): track
                           for track in raw_library['Tracks'].values()
                           if 'Location' in track}

    def sync_from_source(self, item):
        result = self.collection.get(util.bytestring_path(item.path).lower())

        if not result:
            self._log.warning(u'no iTunes match found for {0}'.format(item))
            return

        item.itunes_rating = result.get('Rating')
        item.itunes_playcount = result.get('Play Count')
        item.itunes_skipcount = result.get('Skip Count')

        if result.get('Play Date UTC'):
            item.itunes_lastplayed = mktime(
                result.get('Play Date UTC').timetuple())

        if result.get('Skip Date'):
            item.itunes_lastskipped = mktime(
                result.get('Skip Date').timetuple())
Esempio n. 2
0
class MetaSyncPlugin(BeetsPlugin):

    item_types = {
        'amarok_rating': types.INTEGER,
        'amarok_score': types.FLOAT,
        'amarok_uid': types.STRING,
        'amarok_playcount': types.INTEGER,
        'amarok_firstplayed': DateType(),
        'amarok_lastplayed': DateType()
    }

    def __init__(self):
        super(MetaSyncPlugin, self).__init__()

    def commands(self):
        cmd = ui.Subcommand('metasync',
                            help='update metadata from music player libraries')
        cmd.parser.add_option('-p',
                              '--pretend',
                              action='store_true',
                              help='show all changes but do nothing')
        cmd.parser.add_option('-s',
                              '--source',
                              action='store_false',
                              default=self.config['source'].as_str_seq(),
                              help="select specific sources to import from")
        cmd.parser.add_format_option()
        cmd.func = self.func
        return [cmd]

    def func(self, lib, opts, args):
        """Command handler for the metasync function.
        """
        pretend = opts.pretend
        source = opts.source
        query = ui.decargs(args)

        sources = {}

        for player in source:
            __import__('beetsplug.metasync', fromlist=[str(player)])

            module = 'beetsplug.metasync.' + player

            if module not in modules.keys():
                log.error(u'Unknown metadata source \'' + player + '\'')
                continue

            classes = inspect.getmembers(modules[module], inspect.isclass)

            for entry in classes:
                if entry[0].lower() == player:
                    sources[player] = entry[1]()
                else:
                    continue

        for item in lib.items(query):
            for player in sources.values():
                player.get_data(item)

            changed = ui.show_model_changes(item)

            if changed and not pretend:
                item.store()
Esempio n. 3
0
class Amarok(MetaSource):

    item_types = {
        'amarok_rating': types.INTEGER,
        'amarok_score': types.FLOAT,
        'amarok_uid': types.STRING,
        'amarok_playcount': types.INTEGER,
        'amarok_firstplayed': DateType(),
        'amarok_lastplayed': DateType(),
    }

    queryXML = u'<query version="1.0"> \
                    <filters> \
                        <and><include field="filename" value="%s" /></and> \
                    </filters> \
                </query>'

    def __init__(self, config, log):
        super(Amarok, self).__init__(config, log)

        if not dbus:
            raise ImportError('failed to import dbus')

        self.collection = \
            dbus.SessionBus().get_object('org.kde.amarok', '/Collection')

    def sync_from_source(self, item):
        path = displayable_path(item.path)

        # amarok unfortunately doesn't allow searching for the full path, only
        # for the patch relative to the mount point. But the full path is part
        # of the result set. So query for the filename and then try to match
        # the correct item from the results we get back
        results = self.collection.Query(self.queryXML % escape(basename(path)))
        for result in results:
            if result['xesam:url'] != path:
                continue

            item.amarok_rating = result['xesam:userRating']
            item.amarok_score = result['xesam:autoRating']
            item.amarok_playcount = result['xesam:useCount']
            item.amarok_uid = \
                result['xesam:id'].replace('amarok-sqltrackuid://', '')

            if result['xesam:firstUsed'][0][0] != 0:
                # These dates are stored as timestamps in amarok's db, but
                # exposed over dbus as fixed integers in the current timezone.
                first_played = datetime(result['xesam:firstUsed'][0][0],
                                        result['xesam:firstUsed'][0][1],
                                        result['xesam:firstUsed'][0][2],
                                        result['xesam:firstUsed'][1][0],
                                        result['xesam:firstUsed'][1][1],
                                        result['xesam:firstUsed'][1][2])

                if result['xesam:lastUsed'][0][0] != 0:
                    last_played = datetime(result['xesam:lastUsed'][0][0],
                                           result['xesam:lastUsed'][0][1],
                                           result['xesam:lastUsed'][0][2],
                                           result['xesam:lastUsed'][1][0],
                                           result['xesam:lastUsed'][1][1],
                                           result['xesam:lastUsed'][1][2])
                else:
                    last_played = first_played

                item.amarok_firstplayed = mktime(first_played.timetuple())
                item.amarok_lastplayed = mktime(last_played.timetuple())
Esempio n. 4
0
class RadioStreamPlugin(BeetsPlugin):

    item_types = {
        'rating': types.INTEGER,  # 0..100 scale
        'playcount': types.INTEGER,
        'skipcount': types.INTEGER,
        'lastplayed': DateType(),
        'lastskipped': DateType(),
    }

    def __init__(self):
        super(RadioStreamPlugin, self).__init__()

    def preview_playlist_command(self, lib, opts, args):
        name_column_length = 60
        count = 10

        self._log.info(config.user_config_path())

        if opts.count:
            count = int(opts.count)

        if opts.playlist:
            if opts.playlist not in _settings.playlists:
                self._log.error(u'Playlist not defined: {}'.format(
                    opts.playlist))
                return
            query = _settings.playlists[opts.playlist].query.split(u" ")
        else:
            query = decargs(args)

        query = u" ".join(query)
        query = ignore_deleted_in_query(query)
        items = playlist_generator.generate_playlist(lib, _settings.rules,
                                                     count, opts.shuffle,
                                                     query)

        for item in items:
            score_string = ", ".join([
                '%s: %s' % (key.replace("rule_", ""), value)
                for (key, value) in sorted(item.scores.items())
            ])
            score_sum = round(sum(item.scores.values()), 2)
            item_name = unicode(item)
            if len(item_name) > name_column_length - 5:
                item_name = item_name[:name_column_length - 5] + "..."
            item_string = item_name.ljust(name_column_length)
            print_(u"Track: {0} Scores: {1}=[{2}]".format(
                item_string, score_sum, score_string))

    def start_server_command(self, lib, opts, args):
        global _lastFmNetwork

        args = ui.decargs(args)

        if lastFmUsername and lastFmPassword:
            try:
                _lastFmNetwork = pylast.LastFMNetwork(
                    api_key=LAST_FM_API_KEY,
                    api_secret=LAST_FM_API_SECRET,
                    username=str(lastFmUsername),
                    password_hash=pylast.md5(str(lastFmPassword)))
                print("Last.fm scrobbling enabled")
            except Exception as e:
                print("ERROR: Failed to initialize LastFm service: " + str(e))
        else:
            _lastFmNetwork = None
            print("NOTE: LastFm not configured")

        self.config.add({
            'host': u'0.0.0.0',
            'port': 5000,
            'cors': '',
        })

        if args:
            self.config['host'] = args.pop(0)
        if args:
            self.config['port'] = int(args.pop(0))

        app.config['lib'] = lib
        # Enable CORS if required.
        if self.config['cors']:
            self._log.info(u'Enabling CORS with origin: {0}',
                           self.config['cors'])
            from flask.ext.cors import CORS
            app.config['CORS_ALLOW_HEADERS'] = "Content-Type"
            app.config['CORS_RESOURCES'] = {
                r"/*": {
                    "origins": self.config['cors'].get(str)
                }
            }
            CORS(app)
        # Start the web application.
        app.run(host=self.config['host'].get(unicode),
                port=self.config['port'].get(int),
                debug=opts.debug,
                threaded=True)

    def commands(self):
        server_command = ui.Subcommand('radio', help=u'start the sever')
        server_command.parser.add_option(u'-d',
                                         u'--debug',
                                         action='store_true',
                                         default=False,
                                         help=u'debug mode')
        server_command.func = self.start_server_command

        preview_command = ui.Subcommand(
            'radio-preview',
            help=
            u'preview generated playlists, e.g: radio-preview -c 10 genre:metal'
        )
        preview_command.parser.add_option(u'-c',
                                          u'--count',
                                          dest='count',
                                          default=30,
                                          help="generated track count")
        preview_command.parser.add_option(u'-s',
                                          u'--shuffle',
                                          action='store_true',
                                          help="shuffle the result")
        preview_command.parser.add_option(u'-p',
                                          u'--playlist',
                                          dest='playlist',
                                          help="preview specified playlist")
        preview_command.func = self.preview_playlist_command

        return [server_command, preview_command]