示例#1
0
    def test_build_queries(self):
        spl = SmartPlaylistPlugin()
        self.assertEqual(spl._matched_playlists, None)
        self.assertEqual(spl._unmatched_playlists, None)

        config['smartplaylist']['playlists'].set([])
        spl.build_queries()
        self.assertEqual(spl._matched_playlists, set())
        self.assertEqual(spl._unmatched_playlists, set())

        config['smartplaylist']['playlists'].set([
            {'name': 'foo',
             'query': 'FOO foo'},
            {'name': 'bar',
             'album_query': ['BAR bar1', 'BAR bar2']},
            {'name': 'baz',
             'query': 'BAZ baz',
             'album_query': 'BAZ baz'}
        ])
        spl.build_queries()
        self.assertEqual(spl._matched_playlists, set())
        foo_foo = parse_query_string('FOO foo', Item)
        baz_baz = parse_query_string('BAZ baz', Item)
        baz_baz2 = parse_query_string('BAZ baz', Album)
        bar_bar = OrQuery((parse_query_string('BAR bar1', Album)[0],
                           parse_query_string('BAR bar2', Album)[0]))
        self.assertEqual(spl._unmatched_playlists, set([
            ('foo', foo_foo, (None, None)),
            ('baz', baz_baz, baz_baz2),
            ('bar', (None, None), (bar_bar, None)),
        ]))
示例#2
0
    def test_build_queries(self):
        spl = SmartPlaylistPlugin()
        self.assertEqual(spl._matched_playlists, None)
        self.assertEqual(spl._unmatched_playlists, None)

        config['smartplaylist']['playlists'].set([])
        spl.build_queries()
        self.assertEqual(spl._matched_playlists, set())
        self.assertEqual(spl._unmatched_playlists, set())

        config['smartplaylist']['playlists'].set([
            {'name': u'foo',
             'query': u'FOO foo'},
            {'name': u'bar',
             'album_query': [u'BAR bar1', u'BAR bar2']},
            {'name': u'baz',
             'query': u'BAZ baz',
             'album_query': u'BAZ baz'}
        ])
        spl.build_queries()
        self.assertEqual(spl._matched_playlists, set())
        foo_foo = parse_query_string(u'FOO foo', Item)
        baz_baz = parse_query_string(u'BAZ baz', Item)
        baz_baz2 = parse_query_string(u'BAZ baz', Album)
        bar_bar = OrQuery((parse_query_string(u'BAR bar1', Album)[0],
                           parse_query_string(u'BAR bar2', Album)[0]))
        self.assertEqual(spl._unmatched_playlists, set([
            (u'foo', foo_foo, (None, None)),
            (u'baz', baz_baz, baz_baz2),
            (u'bar', (None, None), (bar_bar, None)),
        ]))
示例#3
0
    def list_sole_tracks(self, lib):
        check_single_track = self.config["check_single_track"].get(True)
        artist_fields = self.config["artist_fields"].as_str_seq()
        check_fields = self.config["check_fields"].as_str_seq()
        sections = self.config["sections"].as_str_seq()

        base_check_query = self.config["check_query"].as_str()
        self._log.debug(f"Using check query {base_check_query}")
        base_check_query, _ = library.parse_query_string(
            base_check_query, library.Item)

        candidates_by_section = collections.defaultdict(set)
        checked_by_artist = collections.defaultdict(
            lambda: collections.defaultdict(set))

        self._log.debug("Gathering item artist information")
        for item in lib.items():
            base_match = self.base_query.match(item)
            check_match = base_check_query.match(item)
            if not base_match and not check_match:
                continue

            for section in sections:
                section_query, _ = library.parse_query_string(
                    section, library.Item)
                if section_query.match(item):
                    if base_match:
                        candidates_by_section[section].add(item)
                    if check_match:
                        for artist in self.artists(item, check_fields):
                            checked_by_artist[section][artist].add(item)

        seen = set()
        for section in sections:
            self._log.info(f"Checking section: {section}")
            for item in candidates_by_section[section]:
                if item in seen:
                    continue
                seen.add(item)

                if check_single_track:
                    if item.album and item._cached_album:
                        if len(item._cached_album.items()) > 1:
                            # Not a single track
                            continue

                item_artists = self.artists(item, artist_fields)
                self._log.debug(
                    f'Checking for artists ({", ".join(item_artists)})')
                for artist in item_artists:
                    other_items = [
                        i for i in checked_by_artist[section][artist]
                        if i != item
                    ]
                    if other_items:
                        yield item, False
                        break
                else:
                    yield item, True
示例#4
0
    def build_queries(self):
        """
        Instanciate queries for the alternatives.

        Each Alternate has 2 queries, one for items and one for albums. 
        We must also remember its name.
        _unmatched_playlists is a set of tuples (name, (q, q_sort), 
        (album_q, album_q_sort)).
        """
        self._unmatched_alternatives = set()
        self._matched_alternatives = set()

        for alternative in self.config['alternatives'].get(list):
            if 'name' not in alternative:
                self._log.warning("Alternative configuration is missing name")
                continue

            alt_data = (alternative['name'], )

            try:
                for key, Model, in (('query', Item), ('album_query', Album)):
                    qs = alternative.get(key)
                    if qs is None:
                        query_and_sort = None, None
                    elif isinstance(qs, six.string_types):
                        query_and_sort = parse_query_string(qs, Model)
                    elif len(qs) == 1:
                        query_and_sort = parse_query_string(qs[0], Model)
                    else:
                        # multiple queries and sorts
                        queries, sorts = zip(*(parse_query_string(q, Model)
                                               for q in qs))
                        query = OrQuery(queries)
                        final_sorts = []
                        for s in sorts:
                            if s:
                                if isinstance(s, MultipleSort):
                                    final_sorts += s.sorts
                                else:
                                    final_sorts.append(s)
                        if not final_sorts:
                            sort = None
                        elif len(final_sorts) == 1:
                            sort, = final_sorts
                        else:
                            sort = MultipleSort(final_sorts)
                        query_and_sort = query, sort

                    alt_data += (query_and_sort, )

            except Exception as exc:
                self._log.warning("invalid query in alternative {}: {}",
                                  alternative['name'], exc)
                continue

            self._unmatched_alternatives.add(alt_data)
示例#5
0
    def build_queries(self):
        """
        Instanciate queries for the playlists.

        Each playlist has 2 queries: one or items one for albums, each with a
        sort. We must also remember its name. _unmatched_playlists is a set of
        tuples (name, (q, q_sort), (album_q, album_q_sort)).

        sort may be any sort, or NullSort, or None. None and NullSort are
        equivalent and both eval to False.
        More precisely
        - it will be NullSort when a playlist query ('query' or 'album_query')
          is a single item or a list with 1 element
        - it will be None when there are multiple items i a query
        """
        self._unmatched_playlists = set()
        self._matched_playlists = set()

        for playlist in self.config['playlists'].get(list):
            playlist_data = (playlist['name'],)
            for key, Model in (('query', Item), ('album_query', Album)):
                qs = playlist.get(key)
                if qs is None:
                    query_and_sort = None, None
                elif isinstance(qs, basestring):
                    query_and_sort = parse_query_string(qs, Model)
                elif len(qs) == 1:
                    query_and_sort = parse_query_string(qs[0], Model)
                else:
                    # multiple queries and sorts
                    queries, sorts = zip(*(parse_query_string(q, Model)
                                           for q in qs))
                    query = OrQuery(queries)
                    final_sorts = []
                    for s in sorts:
                        if s:
                            if isinstance(s, MultipleSort):
                                final_sorts += s.sorts
                            else:
                                final_sorts.append(s)
                    if not final_sorts:
                        sort = None
                    elif len(final_sorts) == 1:
                        sort, = final_sorts
                    else:
                        sort = MultipleSort(final_sorts)
                    query_and_sort = query, sort

                playlist_data += (query_and_sort,)

            self._unmatched_playlists.add(playlist_data)
示例#6
0
    def build_queries(self):
        """
        Instanciate queries for the playlists.

        Each playlist has 2 queries: one or items one for albums, each with a
        sort. We must also remember its name. _unmatched_playlists is a set of
        tuples (name, (q, q_sort), (album_q, album_q_sort)).

        sort may be any sort, or NullSort, or None. None and NullSort are
        equivalent and both eval to False.
        More precisely
        - it will be NullSort when a playlist query ('query' or 'album_query')
          is a single item or a list with 1 element
        - it will be None when there are multiple items i a query
        """
        self._unmatched_playlists = set()
        self._matched_playlists = set()

        for playlist in self.config['playlists'].get(list):
            playlist_data = (playlist['name'], )
            for key, Model in (('query', Item), ('album_query', Album)):
                qs = playlist.get(key)
                if qs is None:
                    query_and_sort = None, None
                elif isinstance(qs, basestring):
                    query_and_sort = parse_query_string(qs, Model)
                elif len(qs) == 1:
                    query_and_sort = parse_query_string(qs[0], Model)
                else:
                    # multiple queries and sorts
                    queries, sorts = zip(*(parse_query_string(q, Model)
                                           for q in qs))
                    query = OrQuery(queries)
                    final_sorts = []
                    for s in sorts:
                        if s:
                            if isinstance(s, MultipleSort):
                                final_sorts += s.sorts
                            else:
                                final_sorts.append(s)
                    if not final_sorts:
                        sort = None
                    elif len(final_sorts) == 1:
                        sort, = final_sorts
                    else:
                        sort = MultipleSort(final_sorts)
                    query_and_sort = query, sort

                playlist_data += (query_and_sort, )

            self._unmatched_playlists.add(playlist_data)
示例#7
0
    def find_items_to_analyse(self):
        # Parse the incoming query
        parsed_query, parsed_sort = parse_query_string(" ".join(self.query),
                                                       Item)
        combined_query = parsed_query

        # Add unprocessed items query
        if not self.cfg_force:
            # Set up the query for unprocessed items
            subqueries = []
            target_maps = ["low_level_targets", "high_level_targets"]
            for map_key in target_maps:
                target_map = self.config[map_key]
                for fld in target_map:
                    if target_map[fld]["required"].exists(
                    ) and target_map[fld]["required"].get(bool):
                        fast = fld in Item._fields
                        query_item = dbcore.query.MatchQuery(fld,
                                                             None,
                                                             fast=fast)
                        subqueries.append(query_item)

            unprocessed_items_query = dbcore.query.OrQuery(subqueries)
            combined_query = dbcore.query.AndQuery(
                [parsed_query, unprocessed_items_query])

        self._say("Combined query: {}".format(combined_query))

        # Get the library items
        self.items_to_analyse = self.lib.items(combined_query, parsed_sort)
        if len(self.items_to_analyse) == 0:
            self._say("No items to process")
            return
示例#8
0
def _items_for_query(lib, playlist, album=False):
    """Get the matching items for a playlist's configured queries.
    `album` indicates whether to process the item-level query or the
    album-level query (if any).
    """
    key = 'album_query' if album else 'query'
    if key not in playlist:
        return []

    # Parse quer(ies). If it's a list, join the queries with OR.
    query_strings = playlist[key]
    if not isinstance(query_strings, (list, tuple)):
        query_strings = [query_strings]
    model = library.Album if album else library.Item
    query = dbcore.OrQuery(
        [library.parse_query_string(q, model)[0] for q in query_strings])

    # Execute query, depending on type.
    if album:
        result = []
        for album in lib.albums(query):
            result.extend(album.items())
        return result
    else:
        return lib.items(query)
示例#9
0
    def create(self, lib: Library, name: str, qs: str, playlist_dir: str,
               relative_to: str):
        if playlist_dir is None:
            playlist_dir = self.config_playlist_dir()
        if relative_to is None:
            relative_to = self.config_relative_to()

        # Try to parse the query
        try:
            if qs is None:
                query, sort = None, None
            else:
                query, sort = parse_query_string(qs, Item)
        except ParsingError as ex:
            self._log.warning(u'invalid query: {}', ex)
            return

        # Map items to their paths
        items = lib.items(query, sort)
        item_path: Callable[[Item], str] = lambda item: path.relpath(
            item.path.decode('utf-8'), relative_to)
        paths = [item_path(item) for item in items]

        filename = path.join(playlist_dir, name + '.m3u')
        with open(filename, 'w+') as file:
            write_str = '\n'.join(paths)
            file.write(write_str)
示例#10
0
    def __init__(self, query_string):
        self.query_string = query_string
        if self.query_string in self._cached_results:
            albums = self.albums = self._cached_results[self.query_string]
        else:
            query, _ = parse_query_string(query_string, Item)
            album_only_query = AndQuery(
                [query, NotQuery(MatchQuery(u'album_id', 0))])
            items = self.lib.items(album_only_query)

            def item_album(i):
                return i.album_id

            items_by_album = sorted(items, key=item_album)
            grouped_items = groupby(items_by_album, item_album)
            albums = set()
            for album_id, items in grouped_items:
                items = list(items)
                album = items[0]._cached_album()
                all_items = album.items()
                if len(items) == len(all_items):
                    albums.add(album_id)

            self._cached_results[self.query_string] = self.albums = albums
        self.album_query = OrQuery([MatchQuery('id', id) for id in albums])
示例#11
0
 def parse_item_query(self, name):
     if name not in config['item_queries']:
         # Fall back to album
         if name in config['album_queries']:
             return self.parse_album_query(name)
     querystring = config['item_queries'][name].as_str()
     query, _ = parse_query_string(querystring, Item)
     return query
示例#12
0
 def do_i_hate_this(cls, task, action_patterns):
     """Process group of patterns (warn or skip) and returns True if
     task is hated and not whitelisted.
     """
     if action_patterns:
         for query_string in action_patterns:
             query, _ = parse_query_string(query_string, Album if task.is_album else Item)
             if any(query.match(item) for item in task.imported_items()):
                 return True
     return False
示例#13
0
 def __init__(self, query_string):
     self.query_string = query_string
     if self.query_string in self._cached_results:
         albums = self.albums = self._cached_results[self.query_string]
     else:
         query, _ = parse_query_string(query_string, Item)
         album_only_query = AndQuery(
             [query, NotQuery(MatchQuery(u'album_id', 0))])
         items = self.lib.items(album_only_query)
         albums = set(i.album_id for i in items)
         self._cached_results[self.query_string] = self.albums = albums
     self.album_query = OrQuery([MatchQuery('id', id) for id in albums])
示例#14
0
 def handle_common_args(self, opts, args):
     self.config.set_args(opts)
     query = ui.decargs(args)
     if query:
         self.config["query"] = query
         self._log.debug(f'Using base query {" ".join(query)}')
         base_query, _ = library.parse_query_parts(query, library.Item)
     else:
         query = self.config["query"].as_str()
         self._log.debug(f"Using base query {query}")
         base_query, _ = library.parse_query_string(query, library.Item)
     self.base_query = base_query
示例#15
0
 def __init__(self, query_string):
     self.query_string = query_string
     if self.query_string in self._cached_album_results:
         albums = self.albums = self._cached_album_results[
             self.query_string]
     else:
         query, _ = parse_query_string(query_string, Album)
         albums = self.lib.albums(query)
         self._cached_album_results[
             self.query_string] = self.albums = albums
     self.item_query = OrQuery(
         [MatchQuery('album_id', album.id) for album in albums])
示例#16
0
 def get_modifies(self, items, model_cls, context):
     modifies = []
     for query, modify in items:
         modify = modify.as_str()
         mod_query, mods, dels = self.parse_modify(modify, model_cls)
         if mod_query:
             raise ui.UserError(u'modifyonimport.{0}["{1}"]: unexpected query `{2}` in value'.format(context, query, mod_query))
         elif not mods and not dels:
             raise ui.UserError(u'modifyonimport.{0}["{1}"]: no modifications found'.format(context, query))
         dbquery, _ = parse_query_string(util.as_string(query), model_cls)
         modifies.append((dbquery, mods, dels))
     return modifies
示例#17
0
 def do_i_hate_this(cls, task, action_patterns):
     """Process group of patterns (warn or skip) and returns True if
     task is hated and not whitelisted.
     """
     if action_patterns:
         for query_string in action_patterns:
             query, _ = parse_query_string(
                 query_string,
                 Album if task.is_album else Item,
             )
             if any(query.match(item) for item in task.imported_items()):
                 return True
     return False
示例#18
0
    def import_begin(self, session):
        self.should_write = ui.should_write()
        self.should_move = ui.should_move()

        for name, model_cls in [('album', Album), ('singleton', Item)]:
            modifies = self.get_modifies(self.config['modify_' + name].items(), model_cls, 'modify_' + name)
            setattr(self, name + '_modifies', modifies)

        self.album_item_modifies = []
        for albumquery, itemmodifies in self.config['modify_album_items'].items():
            albumdbquery, _ = parse_query_string(util.as_string(albumquery), Album)
            modifies = self.get_modifies(itemmodifies.items(), Item, u'modify_album_items.{0}'.format(albumquery))
            self.album_item_modifies.append((albumdbquery, modifies))
示例#19
0
def should_transcode(item, fmt):
    """Determine whether the item should be transcoded as part of
    conversion (i.e., its bitrate is high or it has the wrong format).
    """
    no_convert_queries = config['convert']['no_convert'].as_str_seq()
    if no_convert_queries:
        for query_string in no_convert_queries:
            query, _ = parse_query_string(query_string, Item)
            if query.match(item):
                return False
    if config['convert']['never_convert_lossy_files'] and \
            not (item.format.lower() in LOSSLESS_FORMATS):
        return False
    maxbr = config['convert']['max_bitrate'].get(int)
    return fmt.lower() != item.format.lower() or item.bitrate >= 1000 * maxbr
示例#20
0
def should_transcode(item, fmt):
    """Determine whether the item should be transcoded as part of
    conversion (i.e., its bitrate is high or it has the wrong format).
    """
    no_convert_queries = config['convert']['no_convert'].as_str_seq()
    if no_convert_queries:
        for query_string in no_convert_queries:
            query, _ = parse_query_string(query_string, Item)
            if query.match(item):
                return False
    if config['convert']['never_convert_lossy_files'] and \
            not (item.format.lower() in LOSSLESS_FORMATS):
        return False
    maxbr = config['convert']['max_bitrate'].get(int)
    return fmt.lower() != item.format.lower() or \
        item.bitrate >= 1000 * maxbr
示例#21
0
    def parse_config(self, config):
        if 'paths' in config:
            path_config = config['paths']
        else:
            path_config = beets.config['paths']
        self.path_formats = get_path_formats(path_config)
        self.query, _ = parse_query_string(config['query'].get(unicode), Item)

        self.removable = config.get(dict).get('removable', True)

        if 'directory' in config:
            dir = config['directory'].get(str)
        else:
            dir = self.name
        if not os.path.isabs(dir):
            dir = os.path.join(self.lib.directory, dir)
        self.directory = bytestring_path(dir)
    def parse_config(self, config):
        if 'paths' in config:
            path_config = config['paths']
        else:
            path_config = beets.config['paths']
        self.path_formats = get_path_formats(path_config)
        query = get_unicode_config(config, 'query')
        self.query, _ = parse_query_string(query, Item)

        self.removable = config.get(dict).get('removable', True)

        if 'directory' in config:
            dir = config['directory'].get(str)
        else:
            dir = self.name
        if not os.path.isabs(dir):
            dir = os.path.join(self.lib.directory, dir)
        self.directory = bytestring_path(dir)
示例#23
0
    def parse_config(self, config):
        if 'paths' in config:
            path_config = config['paths']
        else:
            path_config = beets.config['paths']
        self.path_formats = get_path_formats(path_config)
        query = config['query'].as_str()
        self.query, _ = parse_query_string(query, Item)

        self.removable = config.get(dict).get('removable', True)
        self.copy_album_art = config.get(dict).get('copy_album_art', False)

        if 'directory' in config:
            dir = config['directory'].as_str()
        else:
            dir = self.name
        dir = bytestring_path(dir)
        if not os.path.isabs(syspath(dir)):
            dir = os.path.join(self.lib.directory, dir)
        self.directory = dir
示例#24
0
def _items_for_query(lib, playlist, album=False):
    """Get the matching items for a playlist's configured queries.
    `album` indicates whether to process the item-level query or the
    album-level query (if any).
    """
    key = 'album_query' if album else 'query'
    if key not in playlist:
        return []

    # Parse quer(ies). If it's a list, perform the queries and manually
    # concatenate the results
    query_strings = playlist[key]
    if not isinstance(query_strings, (list, tuple)):
        query_strings = [query_strings]
    model = library.Album if album else library.Item
    results = []
    for q in query_strings:
        query, sort = library.parse_query_string(q, model)
        if album:
            new = lib.albums(query, sort)
        else:
            new = lib.items(query, sort)
        results.extend(new)
    return results
示例#25
0
def _items_for_query(lib, playlist, album=False):
    """Get the matching items for a playlist's configured queries.
    `album` indicates whether to process the item-level query or the
    album-level query (if any).
    """
    key = 'album_query' if album else 'query'
    if key not in playlist:
        return []

    # Parse quer(ies). If it's a list, perform the queries and manually
    # concatenate the results
    query_strings = playlist[key]
    if not isinstance(query_strings, (list, tuple)):
        query_strings = [query_strings]
    model = library.Album if album else library.Item
    results = []
    for q in query_strings:
        query, sort = library.parse_query_string(q, model)
        if album:
            new = lib.albums(query, sort)
        else:
            new = lib.items(query, sort)
        results.extend(new)
    return results
示例#26
0
 def parse_album_query(self, name):
     querystring = config['album_queries'][name].as_str()
     query, _ = parse_query_string(querystring, Album)
     return query
示例#27
0
    def build_queries(self):
        """
        Instantiate queries for the playlists.

        Each playlist has 2 queries: one or items one for albums, each with a
        sort. We must also remember its name. _unmatched_playlists is a set of
        tuples (name, (q, q_sort), (album_q, album_q_sort)).

        sort may be any sort, or NullSort, or None. None and NullSort are
        equivalent and both eval to False.
        More precisely
        - it will be NullSort when a playlist query ('query' or 'album_query')
          is a single item or a list with 1 element
        - it will be None when there are multiple items i a query
        """
        self._unmatched_playlists = set()
        self._matched_playlists = set()

        for playlist in self.config['playlists'].get(list):
            if 'name' not in playlist:
                self._log.warning(u"playlist configuration is missing name")
                continue

            playlist_data = (playlist['name'], )
            try:
                for key, model_cls in (('query', Item), ('album_query',
                                                         Album)):
                    qs = playlist.get(key)
                    if qs is None:
                        query_and_sort = None, None
                    elif isinstance(qs, six.string_types):
                        query_and_sort = parse_query_string(qs, model_cls)
                    elif len(qs) == 1:
                        query_and_sort = parse_query_string(qs[0], model_cls)
                    else:
                        # multiple queries and sorts
                        queries, sorts = zip(
                            *(parse_query_string(q, model_cls) for q in qs))
                        query = OrQuery(queries)
                        final_sorts = []
                        for s in sorts:
                            if s:
                                if isinstance(s, MultipleSort):
                                    final_sorts += s.sorts
                                else:
                                    final_sorts.append(s)
                        if not final_sorts:
                            sort = None
                        elif len(final_sorts) == 1:
                            sort, = final_sorts
                        else:
                            sort = MultipleSort(final_sorts)
                        query_and_sort = query, sort

                    playlist_data += (query_and_sort, )

            except ParsingError as exc:
                self._log.warning(u"invalid query in playlist {}: {}",
                                  playlist['name'], exc)
                continue

            self._unmatched_playlists.add(playlist_data)