示例#1
0
    def get_search_suggestions(
        self, musiq: Musiq, query: str, playlist: bool
    ) -> List[Tuple[str, str]]:
        """Returns a list of suggested items for the given query.
        Returns playlists if :param playlist: is True, songs otherwise."""
        result = self.web_client.get(
            "search",
            params={
                "q": query,
                "limit": "20",
                "market": "from_token",
                "type": "playlist" if playlist else "track",
            },
        )

        if playlist:
            items = result["playlists"]["items"]
        else:
            items = result["tracks"]["items"]

        suggestions = []
        for item in items:
            external_url = item["external_urls"]["spotify"]
            title = item["name"]
            if playlist:
                displayname = title
            else:
                artist = item["artists"][0]["name"]
                # apply filter from the settings
                if song_utils.is_forbidden(musiq, artist) or song_utils.is_forbidden(
                    musiq, title
                ):
                    continue
                displayname = song_utils.displayname(artist, title)
            suggestions.append((displayname, external_url))

        # remove duplicates and filter by keywords
        chosen_displaynames = set()
        unique_suggestions = []
        for suggestion in suggestions:
            if suggestion[0] in chosen_displaynames:
                continue
            unique_suggestions.append(suggestion)
            chosen_displaynames.add(suggestion[0])
        return unique_suggestions
示例#2
0
    def get_suggestions(self, request):
        terms = request.GET['term'].split()

        remaining_songs = ArchivedQuery.objects.select_related('song') \
            .values('song__id', 'song__title', 'song__url', 'song__artist', 'song__counter', 'query')

        for term in terms:
            remaining_songs = remaining_songs.filter(
                Q(song__title__icontains=term)
                | Q(song__artist__icontains=term) | Q(query__icontains=term))

        remaining_songs = remaining_songs \
            .values('song__id', 'song__title', 'song__url', 'song__artist', 'song__counter') \
            .distinct() \
            .order_by('-song__counter') \
            [:20]

        results = []
        for song in remaining_songs:
            if song_utils.path_from_url(song['song__url']) is not None:
                cached = True
            else:
                cached = False
            # don't suggest online songs when we don't have internet
            if not self.musiq.base.settings.has_internet:
                if not cached:
                    continue
            result_dict = {
                'key':
                song['song__id'],
                'value':
                song_utils.displayname(song['song__artist'],
                                       song['song__title']),
                'counter':
                song['song__counter'],
                'type':
                'cached' if cached else 'online',
            }
            results.append(result_dict)

        return HttpResponse(json.dumps(results))
示例#3
0
    def analyse(self, request):
        startdate = request.POST.get('startdate')
        starttime = request.POST.get('starttime')
        enddate = request.POST.get('enddate')
        endtime = request.POST.get('endtime')
        if startdate is None or startdate == '' \
                or starttime is None or starttime == '' \
                or enddate is None or enddate == '' \
                or endtime is None or endtime == '':
            return HttpResponseBadRequest('All fields are required')

        start = dateparse.parse_datetime(startdate + 'T' + starttime)
        end = dateparse.parse_datetime(enddate + 'T' + endtime)

        if start is None or end is None:
            return HttpResponseBadRequest('invalid start-/endtime given')
        if start >= end:
            return HttpResponseBadRequest('start has to be before end')

        start = timezone.make_aware(start)
        end = timezone.make_aware(end)

        played = PlayLog.objects.all().filter(created__gte=start).filter(created__lt=end)
        requested = RequestLog.objects.all().filter(created__gte=start).filter(created__lt=end)
        played_count = played.values('song__url', 'song__artist', 'song__title').values('song__url', 'song__artist', 'song__title', count=models.Count('song__url')).order_by('-count')
        played_votes = PlayLog.objects.all().filter(created__gte=start).filter(created__lt=end).order_by('-votes')
        devices = requested.values('address').values('address', count=models.Count('address'))

        response = {}
        response['songs_played'] = len(played)
        response['most_played_song'] = song_utils.displayname(
                played_count[0]['song__artist'],
                played_count[0]['song__title']) + ' (' + str(played_count[0]['count']) + ')'
        response['highest_voted_song'] = played_votes[0].song.displayname() + ' (' + str(played_votes[0].votes) + ')'
        response['most_active_device'] = devices[0]['address'] + ' (' + str(devices[0]['count']) + ')'
        requested_by_ip = requested.filter(address=devices[0]['address'])
        for i in range(6):
            if i >= len(requested_by_ip):
                break
            response['most_active_device'] += '\n'
            if i == 5:
                response['most_active_device'] += '...'
            else:
                response['most_active_device'] += requested_by_ip[i].song.displayname()


        binsize = 3600
        number_of_bins = math.ceil((end - start).total_seconds() / binsize)
        request_bins = [0 for _ in range(number_of_bins)]

        for r in requested:
            seconds = (r.created - start).total_seconds()
            index = int(seconds / binsize)
            request_bins[index] += 1

        current_time = start
        current_index = 0
        response['request_activity'] = ''
        while current_time < end:
            response['request_activity'] += current_time.strftime('%H:%M')
            response['request_activity'] += ':\t' + str(request_bins[current_index])
            response['request_activity'] += '\n'
            current_time += timedelta(seconds=binsize)
            current_index += 1

        localtz = tz.gettz(settings.TIME_ZONE)
        playlist = ''
        for log in played:
            localtime = log.created.astimezone(localtz)
            playlist += '[{:02d}:{:02d}] {}\n'.format(localtime.hour, localtime.minute, log.song.displayname())
        response['playlist'] = playlist

        return JsonResponse(response)
示例#4
0
    def get_suggestions(self, request: WSGIRequest) -> JsonResponse:
        """Returns suggestions for a given query.
        Combines online and offline suggestions."""
        terms = request.GET["term"].split()
        suggest_playlist = request.GET["playlist"] == "true"

        results: List[Dict[str, Union[str, int]]] = []

        if self.musiq.base.settings.has_internet:
            if self.musiq.base.settings.spotify_enabled:
                spotify_suggestions = Spotify().get_search_suggestions(
                    " ".join(terms), suggest_playlist)
                spotify_suggestions = spotify_suggestions[:2]
                for suggestion, external_url in spotify_suggestions:
                    results.append({
                        "key": external_url,
                        "value": suggestion,
                        "type": "spotify-online",
                    })

            if self.musiq.base.settings.soundcloud_enabled:
                spotify_suggestions = Soundcloud().get_search_suggestions(
                    " ".join(terms))
                soundcloud_suggestions = spotify_suggestions[:2]
                for suggestion in soundcloud_suggestions:
                    results.append({
                        "key": -1,
                        "value": suggestion,
                        "type": "soundcloud-online"
                    })

            if self.musiq.base.settings.youtube_enabled:
                youtube_suggestions = Youtube().get_search_suggestions(
                    " ".join(terms))
                # limit to the first three online suggestions
                youtube_suggestions = youtube_suggestions[:2]
                for suggestion in youtube_suggestions:
                    results.append({
                        "key": -1,
                        "value": suggestion,
                        "type": "youtube-online"
                    })

        # The following query is roughly equivalent to the following SQL code:
        # SELECT DISTINCT id, title, artist, counter
        # FROM core_archivedsong s LEFT JOIN core_archivedquery q ON q.song
        # WHERE forall term in terms: term in q.query or term in s.artist or term in s.title
        # ORDER BY -counter
        if suggest_playlist:
            remaining_playlists = ArchivedPlaylist.objects.prefetch_related(
                "queries")
            # exclude radios from suggestions
            remaining_playlists = remaining_playlists.exclude(
                list_id__startswith="RD").exclude(list_id__contains="&list=RD")

            for term in terms:
                remaining_playlists = remaining_playlists.filter(
                    Q(title__icontains=term)
                    | Q(queries__query__icontains=term))

            playlist_suggestions = (remaining_playlists.values(
                "id", "title", "counter").distinct().order_by("-counter")[:20])

            for playlist in playlist_suggestions:
                archived_playlist = ArchivedPlaylist.objects.get(
                    id=playlist["id"])
                result_dict: Dict[str, Union[str, int]] = {
                    "key": playlist["id"],
                    "value": playlist["title"],
                    "counter": playlist["counter"],
                    "type":
                    song_utils.determine_playlist_type(archived_playlist),
                }
                results.append(result_dict)
        else:
            remaining_songs = ArchivedSong.objects.prefetch_related("queries")

            for term in terms:
                remaining_songs = remaining_songs.filter(
                    Q(title__icontains=term)
                    | Q(artist__icontains=term)
                    | Q(queries__query__icontains=term))

            song_suggestions = (remaining_songs.values(
                "id", "title", "url", "artist",
                "counter").distinct().order_by("-counter")[:20])

            for song in song_suggestions:
                provider = SongProvider.create(self.musiq,
                                               external_url=song["url"])
                cached = provider.check_cached()
                # don't suggest local songs if they are not cached (=not at expected location)
                if not cached and provider.type == "local":
                    continue
                # don't suggest online songs when we don't have internet
                if not self.musiq.base.settings.has_internet and not cached:
                    continue
                # don't suggest youtube songs if it was disabled
                if (not self.musiq.base.settings.youtube_enabled
                        and provider.type == "youtube"):
                    continue
                # don't suggest spotify songs if we are not logged in
                if (not self.musiq.base.settings.spotify_enabled
                        and provider.type == "spotify"):
                    continue
                # don't suggest soundcloud songs if we are not logged in
                if (not self.musiq.base.settings.soundcloud_enabled
                        and provider.type == "soundcloud"):
                    continue
                result_dict = {
                    "key": song["id"],
                    "value": song_utils.displayname(song["artist"],
                                                    song["title"]),
                    "counter": song["counter"],
                    "type": provider.type,
                }
                results.append(result_dict)

        return JsonResponse(results, safe=False)
示例#5
0
    def analyse(self, request: WSGIRequest) -> HttpResponse:
        """Perform an analysis of the database in the given timeframe."""
        startdate = request.POST.get("startdate")
        starttime = request.POST.get("starttime")
        enddate = request.POST.get("enddate")
        endtime = request.POST.get("endtime")
        if not startdate or not starttime or not enddate or not endtime:
            return HttpResponseBadRequest("All fields are required")

        start = dateparse.parse_datetime(startdate + "T" + starttime)
        end = dateparse.parse_datetime(enddate + "T" + endtime)

        if start is None or end is None:
            return HttpResponseBadRequest("invalid start-/endtime given")
        if start >= end:
            return HttpResponseBadRequest("start has to be before end")

        start = timezone.make_aware(start)
        end = timezone.make_aware(end)

        played = (
            PlayLog.objects.all().filter(created__gte=start).filter(created__lt=end)
        )
        requested = (
            RequestLog.objects.all().filter(created__gte=start).filter(created__lt=end)
        )
        played_count = (
            played.values("song__url", "song__artist", "song__title")
            .values(
                "song__url",
                "song__artist",
                "song__title",
                count=models.Count("song__url"),
            )
            .order_by("-count")
        )
        played_votes = (
            PlayLog.objects.all()
            .filter(created__gte=start)
            .filter(created__lt=end)
            .order_by("-votes")
        )
        devices = requested.values("address").values(
            "address", count=models.Count("address")
        )

        response = {
            "songs_played": len(played),
            "most_played_song": (
                song_utils.displayname(
                    played_count[0]["song__artist"], played_count[0]["song__title"]
                )
                + f" ({played_count[0]['count']})"
            ),
            "highest_voted_song": (
                played_votes[0].song_displayname() + f" ({played_votes[0].votes})"
            ),
            "most_active_device": (devices[0]["address"] + f" ({devices[0]['count']})"),
        }
        requested_by_ip = requested.filter(address=devices[0]["address"])
        for i in range(6):
            if i >= len(requested_by_ip):
                break
            response["most_active_device"] += "\n"
            if i == 5:
                response["most_active_device"] += "..."
            else:
                response["most_active_device"] += requested_by_ip[i].item_displayname()

        binsize = 3600
        number_of_bins = math.ceil((end - start).total_seconds() / binsize)
        request_bins = [0 for _ in range(number_of_bins)]

        for request_log in requested:
            seconds = (request_log.created - start).total_seconds()
            index = int(seconds / binsize)
            request_bins[index] += 1

        current_time = start
        current_index = 0
        response["request_activity"] = ""
        while current_time < end:
            response["request_activity"] += current_time.strftime("%H:%M")
            response["request_activity"] += ":\t" + str(request_bins[current_index])
            response["request_activity"] += "\n"
            current_time += timedelta(seconds=binsize)
            current_index += 1

        localtz = tz.gettz(settings.TIME_ZONE)
        playlist = ""
        for play_log in played:
            localtime = play_log.created.astimezone(localtz)
            playlist += "[{:02d}:{:02d}] {}\n".format(
                localtime.hour, localtime.minute, play_log.song_displayname()
            )
        response["playlist"] = playlist

        return JsonResponse(response)
示例#6
0
def analyse(request: WSGIRequest) -> HttpResponse:
    """Perform an analysis of the database in the given timeframe."""

    try:
        start, end = _parse_datetimes(request)
    except ValueError as error:
        return HttpResponseBadRequest(error.args[0])

    played = PlayLog.objects.all().filter(created__gte=start).filter(created__lt=end)
    if not played.exists():
        return HttpResponseBadRequest("No songs played in the given time span")

    request_logs = (
        RequestLog.objects.all().filter(created__gte=start).filter(created__lt=end)
    )
    played_count = (
        played.values("song__url", "song__artist", "song__title")
        .annotate(count=models.Count("song__url"))
        .values("song__url", "song__artist", "song__title", "count")
        .order_by("-count")
    )
    played_votes = (
        PlayLog.objects.all()
        .filter(created__gte=start)
        .filter(created__lt=end)
        .order_by("-votes")
    )
    devices = (
        request_logs.values("session_key")
        .annotate(count=models.Count("session_key"))
        .values("session_key", "count")
        .order_by("-count")
    )

    response = {
        "songsPlayed": len(played),
        "mostPlayedSong": (
            song_utils.displayname(
                played_count[0]["song__artist"], played_count[0]["song__title"]
            )
            + f" ({played_count[0]['count']})"
        ),
        "highestVotedSong": (
            played_votes[0].song_displayname() + f" ({played_votes[0].votes})"
        ),
    }
    response["mostActiveDevice"] = _most_active_device(
        devices[0]["session_key"],
        devices[0]["count"],
        request_logs.filter(session_key=devices[0]["session_key"]),
    )

    response["requestActivity"] = _request_activity(start, end, request_logs)

    localtz = tz.gettz(conf.TIME_ZONE)
    playlist = ""
    for play_log in played:
        localtime = play_log.created.astimezone(localtz)
        playlist += f"[{localtime.hour:02d}:{localtime.minute:02d}] {play_log.song_displayname()}\n"
    response["playlist"] = playlist

    return JsonResponse(response)
示例#7
0
 def displayname(self) -> str:
     """Formats the song using the utility method."""
     return song_utils.displayname(self.artist, self.title)
示例#8
0
    def get_suggestions(self, request: WSGIRequest) -> JsonResponse:
        """Returns suggestions for a given query.
        Combines online and offline suggestions."""
        query = request.GET["term"]
        suggest_playlist = request.GET["playlist"] == "true"

        results: List[Dict[str, Union[str, int]]] = []

        if (self.musiq.base.settings.online_suggestions
                and self.musiq.base.settings.has_internet):

            number_of_suggestions = 2
            if [
                    self.musiq.base.settings.spotify_enabled,
                    self.musiq.base.settings.soundcloud_enabled,
                    self.musiq.base.settings.youtube_enabled,
            ].count(True) > 1:
                # If there is more than one active service,
                # only offer one online suggestion per service
                number_of_suggestions = 1

            if self.musiq.base.settings.spotify_enabled:
                spotify_suggestions = Spotify().get_search_suggestions(
                    query, suggest_playlist)
                spotify_suggestions = spotify_suggestions[:
                                                          number_of_suggestions]
                for suggestion, external_url in spotify_suggestions:
                    results.append({
                        "key": external_url,
                        "value": suggestion,
                        "type": "spotify-online",
                    })

            if self.musiq.base.settings.soundcloud_enabled:
                soundcloud_suggestions = Soundcloud().get_search_suggestions(
                    query)
                soundcloud_suggestions = soundcloud_suggestions[:
                                                                number_of_suggestions]
                for suggestion in soundcloud_suggestions:
                    results.append({
                        "key": -1,
                        "value": suggestion,
                        "type": "soundcloud-online"
                    })

            if self.musiq.base.settings.youtube_enabled:
                youtube_suggestions = Youtube().get_search_suggestions(query)
                # limit to the first three online suggestions
                youtube_suggestions = youtube_suggestions[:
                                                          number_of_suggestions]
                for suggestion in youtube_suggestions:
                    results.append({
                        "key": -1,
                        "value": suggestion,
                        "type": "youtube-online"
                    })

        if suggest_playlist:
            search_results = watson.search(query,
                                           models=(ArchivedPlaylist, ))[:20]

            for playlist in search_results:
                playlist_info = playlist.meta
                archived_playlist = ArchivedPlaylist.objects.get(
                    id=playlist_info["id"])
                result_dict: Dict[str, Union[str, int]] = {
                    "key": playlist_info["id"],
                    "value": playlist_info["title"],
                    "counter": playlist_info["counter"],
                    "type":
                    song_utils.determine_playlist_type(archived_playlist),
                }
                results.append(result_dict)
        else:
            search_results = watson.search(query, models=(ArchivedSong, ))[:20]

            for search_result in search_results:
                song_info = search_result.meta
                provider = SongProvider.create(self.musiq,
                                               external_url=song_info["url"])
                cached = provider.check_cached()
                # don't suggest local songs if they are not cached (=not at expected location)
                if not cached and provider.type == "local":
                    continue
                # don't suggest online songs when we don't have internet
                if not self.musiq.base.settings.has_internet and not cached:
                    continue
                # don't suggest youtube songs if it was disabled
                if (not self.musiq.base.settings.youtube_enabled
                        and provider.type == "youtube"):
                    continue
                # don't suggest spotify songs if we are not logged in
                if (not self.musiq.base.settings.spotify_enabled
                        and provider.type == "spotify"):
                    continue
                # don't suggest soundcloud songs if we are not logged in
                if (not self.musiq.base.settings.soundcloud_enabled
                        and provider.type == "soundcloud"):
                    continue
                result_dict = {
                    "key":
                    song_info["id"],
                    "value":
                    song_utils.displayname(song_info["artist"],
                                           song_info["title"]),
                    "counter":
                    song_info["counter"],
                    "type":
                    provider.type,
                }
                results.append(result_dict)

        return JsonResponse(results, safe=False)
示例#9
0
    def get_suggestions(self, request: WSGIRequest) -> JsonResponse:
        """Returns suggestions for a given query.
        Combines online and offline suggestions."""
        query = request.GET["term"]
        suggest_playlist = request.GET["playlist"] == "true"

        results = self._online_suggestions(query, suggest_playlist)
        basic_settings = self.musiq.base.settings.basic

        if suggest_playlist:
            search_results = watson.search(query, models=(
                ArchivedPlaylist, ))[:basic_settings.number_of_suggestions]

            for playlist in search_results:
                playlist_info = playlist.meta
                archived_playlist = ArchivedPlaylist.objects.get(
                    id=playlist_info["id"])
                result_dict: Dict[str, Union[str, int]] = {
                    "key": playlist_info["id"],
                    "value": playlist_info["title"],
                    "counter": playlist.object.counter,
                    "type":
                    song_utils.determine_playlist_type(archived_playlist),
                }
                results.append(result_dict)
        else:
            search_results = watson.search(
                query,
                models=(ArchivedSong, ))[:basic_settings.number_of_suggestions]

            for search_result in search_results:
                song_info = search_result.meta
                provider = SongProvider.create(self.musiq,
                                               external_url=song_info["url"])
                cached = provider.check_cached()
                # don't suggest local songs if they are not cached (=not at expected location)
                if not cached and provider.type == "local":
                    continue
                # don't suggest online songs when we don't have internet
                if not self.musiq.base.settings.basic.has_internet and not cached:
                    continue
                # don't suggest youtube songs if it was disabled
                if (not self.musiq.base.settings.platforms.youtube_enabled
                        and provider.type == "youtube"):
                    continue
                # don't suggest spotify songs if we are not logged in
                if (not self.musiq.base.settings.platforms.spotify_enabled
                        and provider.type == "spotify"):
                    continue
                # don't suggest soundcloud songs if we are not logged in
                if (not self.musiq.base.settings.platforms.soundcloud_enabled
                        and provider.type == "soundcloud"):
                    continue
                result_dict = {
                    "key":
                    song_info["id"],
                    "value":
                    song_utils.displayname(song_info["artist"],
                                           song_info["title"]),
                    "counter":
                    search_result.object.counter,
                    "type":
                    provider.type,
                }
                results.append(result_dict)

        return JsonResponse(results, safe=False)
示例#10
0
 def displayname(self):
     return song_utils.displayname(self.artist, self.title)
示例#11
0
    def get_suggestions(self, request):
        terms = request.GET['term'].split()
        suggest_playlist = request.GET['playlist'] == 'true'

        results = []
        if suggest_playlist:
            remaining_playlists = ArchivedPlaylist.objects.prefetch_related(
                'queries')
            # exclude radios from suggestions
            remaining_playlists = remaining_playlists.exclude(
                list_id__startswith='RD').exclude(list_id__contains='&list=RD')

            for term in terms:
                remaining_playlists = remaining_playlists.filter(
                    Q(title__icontains=term)
                    | Q(queries__query__icontains=term))

            remaining_playlists = remaining_playlists \
                                      .values('id', 'title', 'counter') \
                                      .distinct() \
                                      .order_by('-counter') \
                [:20]

            for playlist in remaining_playlists:
                cached = False
                archived_playlist = ArchivedPlaylist.objects.get(
                    id=playlist['id'])
                result_dict = {
                    'key': playlist['id'],
                    'value': playlist['title'],
                    'counter': playlist['counter'],
                    'type':
                    song_utils.determine_playlist_type(archived_playlist),
                }
                results.append(result_dict)
        else:
            remaining_songs = ArchivedSong.objects.prefetch_related('queries')

            for term in terms:
                remaining_songs = remaining_songs.filter(
                    Q(title__icontains=term) | Q(artist__icontains=term)
                    | Q(queries__query__icontains=term))

            remaining_songs = remaining_songs \
                                  .values('id', 'title', 'url', 'artist', 'counter') \
                                  .distinct() \
                                  .order_by('-counter') \
                [:20]

            for song in remaining_songs:
                provider = SongProvider.create(self.musiq,
                                               external_url=song['url'])
                cached = provider.check_cached()
                # don't suggest local songs if they are not cached (=not at expected location)
                if not cached and provider.type == 'local':
                    continue
                # don't suggest online songs when we don't have internet
                if not self.musiq.base.settings.has_internet and not cached:
                    continue
                # don't suggest spotify songs if we are not logged in
                if not self.musiq.base.settings.spotify_enabled and provider.type == 'spotify':
                    continue
                result_dict = {
                    'key': song['id'],
                    'value': song_utils.displayname(song['artist'],
                                                    song['title']),
                    'counter': song['counter'],
                    'type': provider.type
                }
                results.append(result_dict)

        return HttpResponse(json.dumps(results))
        """ query for the suggestions
示例#12
0
    def get_suggestions(self, request):
        terms = request.GET['term'].split()
        suggest_playlist = request.GET['playlist'] == 'true'

        results = []
        if suggest_playlist:
            remaining_playlists = ArchivedPlaylist.objects.prefetch_related(
                'queries')
            # exclude radios from suggestions
            remaining_playlists = remaining_playlists.exclude(
                list_id__startswith='RD').exclude(list_id__contains='&list=RD')

            for term in terms:
                remaining_playlists = remaining_playlists.filter(
                    Q(title__icontains=term)
                    | Q(queries__query__icontains=term))

            remaining_playlists = remaining_playlists \
                .values('id', 'title', 'counter') \
                .distinct() \
                .order_by('-counter') \
                [:20]

            for playlist in remaining_playlists:
                cached = False
                result_dict = {
                    'key': playlist['id'],
                    'value': playlist['title'],
                    'counter': playlist['counter'],
                    'type': 'cached' if cached else 'online',
                }
                results.append(result_dict)
        else:
            remaining_songs = ArchivedSong.objects.prefetch_related('queries')

            for term in terms:
                remaining_songs = remaining_songs.filter(
                    Q(title__icontains=term) | Q(artist__icontains=term)
                    | Q(queries__query__icontains=term))

            remaining_songs = remaining_songs \
                .values('id', 'title', 'url', 'artist', 'counter') \
                .distinct() \
                .order_by('-counter') \
                [:20]

            for song in remaining_songs:
                if song_utils.path_from_url(song['url']) is not None:
                    cached = True
                else:
                    cached = False
                # don't suggest online songs when we don't have internet
                if not self.musiq.base.settings.has_internet:
                    if not cached:
                        continue
                result_dict = {
                    'key': song['id'],
                    'value': song_utils.displayname(song['artist'],
                                                    song['title']),
                    'counter': song['counter'],
                    'type': 'cached' if cached else 'online',
                }
                results.append(result_dict)

        return HttpResponse(json.dumps(results))
示例#13
0
def _offline_song_suggestions(query: str) -> List[SuggestionResult]:
    results: List[SuggestionResult] = []
    terms = query.split()
    song_results: Iterable[Mapping[str, Any]]
    if settings.DEBUG:
        # Testing the whole table whether it contains any term is quite costly.
        # Used for sqlite3 which does not have a similarity function.
        matching_songs = ArchivedSong.objects.prefetch_related("queries")
        for term in terms:
            matching_songs = matching_songs.filter(
                Q(title__icontains=term)
                | Q(artist__icontains=term)
                | Q(queries__query__icontains=term))

        song_results = (
            matching_songs.order_by("-counter")
            # annotate with same values as in the postgres case to have a consistent interface
            .annotate(
                u_id=F("id"),
                u_url=F("url"),
                u_artist=F("artist"),
                u_title=F("title"),
                u_duration=F("duration"),
                u_counter=F("counter"),
                u_cached=F("cached"),
            ).values(*u_values_list).distinct()
            [:storage.get("number_of_suggestions")])

        # Perhaps this could be combined with the similarity search
        # to improve usability with the right weighting.
        # matching_songs = matching_songs.annotate(
        #    artist_similarity=Coalesce(TrigramWordSimilarity(query, "artist"), 0),
        #    title_similarity=Coalesce(TrigramWordSimilarity(query, "title"), 0),
        #    query_similarity=Coalesce(TrigramWordSimilarity(query, "queries__query"), 0),
        #    max_similarity=Greatest(
        #        "artist_similarity", "title_similarity", "query_similarity"
        #    ),
        # )

        # To combine, use union instead of | (or) in order to access the annotated values
        # similar_songs = union(matching_songs)
    else:
        song_results = _postgres_song_results(query)

    has_internet = redis.get("has_internet")
    for song in song_results:
        if song_utils.is_forbidden(
                song["u_artist"]) or song_utils.is_forbidden(song["u_title"]):
            continue

        platform = song_utils.determine_url_type(song["u_url"])
        # don't suggest online songs when we don't have internet
        if not has_internet and not song["u_cached"]:
            continue
        if platform == "local":
            # don't suggest local songs if they are not cached (=not at expected location)
            if not song["u_cached"]:
                continue
        else:
            # don't suggest songs if the respective platform is disabled
            assert platform in ["youtube", "spotify", "soundcloud", "jamendo"]
            if not storage.get(cast(PlatformEnabled, f"{platform}_enabled")):
                continue
        result_dict: SuggestionResult = {
            "key": song["u_id"],
            "value": song_utils.displayname(song["u_artist"], song["u_title"]),
            "counter": song["u_counter"],
            "type": platform,
            "durationFormatted": song_utils.format_seconds(song["u_duration"]),
        }
        results.append(result_dict)
    # mark suggestions whose displayname is identical
    seen_values: Dict[str, int] = {}
    for index, result in enumerate(results):
        if result["value"] in seen_values:
            result["confusable"] = True
            results[seen_values[result["value"]]]["confusable"] = True
        seen_values[result["value"]] = index
    return results