예제 #1
0
def _find_item_in_lib(lib, track_name, artist_name):
    """Finds an Item in the library based on the track_name.

    The track_name is not guaranteed to be perfect (i.e. as soon on MB),
    so in that case we query MB and look for the track id and query our
    lib with that.
    """
    # Query the library based on the track name
    query = MatchQuery('title', track_name)
    lib_results = lib._fetch(Item, query=query)

    # Maybe the provided track name isn't all too good
    # Search for the track on MusicBrainz, and use that info to retry our lib
    if not lib_results:
        mb_candidate = _get_mb_candidate(track_name, artist_name)
        if mb_candidate:
            query = OrQuery((
                        AndQuery((
                            MatchQuery('title', mb_candidate.title),
                            MatchQuery('artist', mb_candidate.artist),
                        )),
                        MatchQuery('mb_trackid', mb_candidate.track_id)
                    ))
            lib_results = lib._fetch(Item, query=query)

    if not lib_results:
        return None

    # If we get multiple Item results from our library, choose best match
    # using the distance
    if len(lib_results) > 1:
        return _get_best_match(lib_results, track_name, artist_name)[0]

    return lib_results[0]
예제 #2
0
 def update_tags(self, playlist_dict, lib):
     with lib.transaction():
         for query, playlist_tag in playlist_dict.items():
             query = AndQuery([MatchQuery("artist", query[0]),
                               MatchQuery("album", query[1]),
                               MatchQuery("title", query[2])])
             items = lib.items(query)
             if not items:
                 self._log.warn(u"{} | track not found ({})", playlist_tag,
                                query)
                 continue
             for item in items:
                 item.subsonic_playlist = playlist_tag
                 item.try_sync(write=True, move=False)
예제 #3
0
    def resource_object(artist_id):
        """Construct a JSON:API resource object for the given artist.

        Args:
            artist_id: A string which is the artist's name.
        """
        # Get tracks where artist field exactly matches artist_id
        query = MatchQuery("artist", artist_id)
        tracks = current_app.config["lib"].items(query)
        if not tracks:
            return None

        # Get artist information from the first track
        # NOTE: It could be that the first track doesn't have a
        # MusicBrainz id but later tracks do, which isn't ideal.
        attributes = {}
        # Use aura => beets attribute map, e.g. artist => name
        for aura_attr, beets_attr in ARTIST_ATTR_MAP.items():
            a = getattr(tracks[0], beets_attr)
            # Only set attribute if it's not None, 0, "", etc.
            # NOTE: This could mean required attributes are not set
            if a:
                attributes[aura_attr] = a

        relationships = {
            "tracks": {
                "data": [{
                    "type": "track",
                    "id": str(t.id)
                } for t in tracks]
            }
        }
        album_query = MatchQuery("albumartist", artist_id)
        albums = current_app.config["lib"].albums(query=album_query)
        if len(albums) != 0:
            relationships["albums"] = {
                "data": [{
                    "type": "album",
                    "id": str(a.id)
                } for a in albums]
            }

        return {
            "type": "artist",
            "id": artist_id,
            "attributes": attributes,
            "relationships": relationships,
        }
예제 #4
0
    def resource_object(album):
        """Construct a JSON:API resource object from a beets Album.

        Args:
            album: A beets Album object.
        """
        attributes = {}
        # Use aura => beets attribute name map
        for aura_attr, beets_attr in ALBUM_ATTR_MAP.items():
            a = getattr(album, beets_attr)
            # Only set attribute if it's not None, 0, "", etc.
            # NOTE: This could mean required attributes are not set
            if a:
                attributes[aura_attr] = a

        # Get beets Item objects for all tracks in the album sorted by
        # track number. Sorting is not required but it's nice.
        query = MatchQuery("album_id", album.id)
        sort = FixedFieldSort("track", ascending=True)
        tracks = current_app.config["lib"].items(query, sort)
        # JSON:API one-to-many relationship to tracks on the album
        relationships = {
            "tracks": {
                "data": [{
                    "type": "track",
                    "id": str(t.id)
                } for t in tracks]
            }
        }
        # Add images relationship if album has associated images
        if album.artpath:
            path = py3_path(album.artpath)
            filename = path.split("/")[-1]
            image_id = f"album-{album.id}-{filename}"
            relationships["images"] = {
                "data": [{
                    "type": "image",
                    "id": image_id
                }]
            }
        # Add artist relationship if artist name is same on tracks
        # Tracks are used to define artists so don't albumartist
        # Check for all tracks in case some have featured artists
        if album.albumartist in [t.artist for t in tracks]:
            relationships["artists"] = {
                "data": [{
                    "type": "artist",
                    "id": album.albumartist
                }]
            }

        return {
            "type": "album",
            "id": str(album.id),
            "attributes": attributes,
            "relationships": relationships,
        }
예제 #5
0
    def retrieve_library_items(self):
        cmd_query = self.query
        parsed_cmd_query, parsed_ordering = parse_query_parts(cmd_query, Item)

        if self.cfg_force:
            full_query = parsed_cmd_query
        else:
            parsed_plg_query = OrQuery([
                NumericQuery('year', '0'),
                MatchQuery('year', ''),
                NoneQuery('year'),
                NumericQuery('original_year', '0'),
                MatchQuery('original_year', ''),
                NoneQuery('original_year'),
            ])
            full_query = AndQuery([parsed_cmd_query, parsed_plg_query])

        self._say("Selection query: {}".format(full_query))

        return self.lib.items(full_query, parsed_ordering)
예제 #6
0
    def get_mean_value_for_artist(self, item: Item, field_name):
        answer = None

        query = MatchQuery('mb_artistid', item.get("mb_artistid"))
        items = self.lib.items(query)
        values = []
        for it in items:
            if it.get(field_name):
                val = int(it.get(field_name))
                if 0 < val < 2100:
                    values.append(val)

        if values:
            answer = int(round(sum(values) / len(values)))

        return answer
예제 #7
0
 def translate_filters(self):
     """Translate filters from request arguments to a beets Query."""
     # The format of each filter key in the request parameter is:
     # filter[<attribute>]. This regex extracts <attribute>.
     pattern = re.compile(r"filter\[(?P<attribute>[a-zA-Z0-9_-]+)\]")
     queries = []
     for key, value in request.args.items():
         match = pattern.match(key)
         if match:
             # Extract attribute name from key
             aura_attr = match.group("attribute")
             # Get the beets version of the attribute name
             beets_attr = self.attribute_map.get(aura_attr, aura_attr)
             converter = self.get_attribute_converter(beets_attr)
             value = converter(value)
             # Add exact match query to list
             # Use a slow query so it works with all fields
             queries.append(MatchQuery(beets_attr, value, fast=False))
     # NOTE: AURA doesn't officially support multiple queries
     return AndQuery(queries)