Beispiel #1
0
    def extract_id(self) -> Optional[str]:
        if self.key is not None:
            try:
                archived_song = ArchivedSong.objects.get(id=self.key)
                return self.__class__.get_id_from_external_url(archived_song.url)
            except ArchivedSong.DoesNotExist:
                return None
        if self.query is not None:
            url_type = song_utils.determine_url_type(self.query)
            if url_type == "youtube":
                from core.musiq.youtube import YoutubeSongProvider

                return YoutubeSongProvider.get_id_from_external_url(self.query)
            if url_type == "spotify":
                from core.musiq.spotify import SpotifySongProvider

                return SpotifySongProvider.get_id_from_external_url(self.query)
            if url_type == "soundcloud":
                from core.musiq.soundcloud import SoundcloudSongProvider

                return SoundcloudSongProvider.get_id_from_external_url(self.query)
            # interpret the query as an external url and try to look it up in the database
            try:
                archived_song = ArchivedSong.objects.get(url=self.query)
                return self.__class__.get_id_from_external_url(archived_song.url)
            except ArchivedSong.DoesNotExist:
                return None
        logging.error("Can not extract id because neither key nor query are known")
        return None
Beispiel #2
0
    def __init__(self, musiq: "Musiq", query: Optional[str],
                 key: Optional[int]) -> None:
        super().__init__(musiq, query, key)
        self.ok_message = "song queued"

        if query:
            url_type = song_utils.determine_url_type(query)
            if url_type != self.type and url_type != "unknown":
                raise WrongUrlError(
                    f"Tried to create a {self.type} provider with: {query}")
Beispiel #3
0
    def __init__(self, query: Optional[str], key: Optional[int]) -> None:
        super().__init__(query, key)
        self.id = self.extract_id()
        self.ok_message = "song queued"
        self.queued_song: Optional[QueuedSong] = None
        self.metadata: "Metadata" = {}

        if query:
            url_type = song_utils.determine_url_type(query)
            if url_type not in (self.type, "unknown"):
                raise WrongUrlError(
                    f"Tried to create a {self.type} provider with: {query}")
Beispiel #4
0
    def create(
        query: Optional[str] = None,
        key: Optional[int] = None,
        external_url: Optional[str] = None,
    ) -> "SongProvider":
        """Factory method to create a song provider.
        Either (query and key) or external url need to be specified.
        Detects the type of provider needed and returns one of corresponding type."""
        if key is not None:
            if query is None:
                logging.error(
                    "archived song requested but no query given (key %s)", key)
                raise ValueError()
            try:
                archived_song = ArchivedSong.objects.get(id=key)
            except ArchivedSong.DoesNotExist as error:
                logging.error("archived song requested for nonexistent key %s",
                              key)
                raise ValueError() from error
            external_url = archived_song.url
        if external_url is None:
            raise ValueError(
                "external_url was provided and could not be inferred from remaining attributes."
            )
        provider_class: Optional[Type[SongProvider]] = None
        url_type = song_utils.determine_url_type(external_url)
        if url_type == "local":
            from core.musiq.local import LocalSongProvider

            provider_class = LocalSongProvider
        elif storage.get("youtube_enabled") and url_type == "youtube":
            from core.musiq.youtube import YoutubeSongProvider

            provider_class = YoutubeSongProvider
        elif storage.get("spotify_enabled") and url_type == "spotify":
            from core.musiq.spotify import SpotifySongProvider

            provider_class = SpotifySongProvider
        elif storage.get("soundcloud_enabled") and url_type == "soundcloud":
            from core.musiq.soundcloud import SoundcloudSongProvider

            provider_class = SoundcloudSongProvider
        elif storage.get("jamendo_enabled") and url_type == "jamendo":
            from core.musiq.jamendo import JamendoSongProvider

            provider_class = JamendoSongProvider
        if not provider_class:
            raise NotImplementedError(
                f"No provider for given song: {external_url}")
        if not query and external_url:
            query = external_url
        provider = provider_class(query, key)
        return provider
Beispiel #5
0
    def extract_id(self) -> Optional[str]:
        """Tries to extract the id from the given query.
        Returns the id if possible, otherwise None"""
        if self.key is not None:
            try:
                archived_song = ArchivedSong.objects.get(id=self.key)
                return self.__class__.get_id_from_external_url(
                    archived_song.url)
            except ArchivedSong.DoesNotExist:
                return None
        if self.query is not None:
            url_type = song_utils.determine_url_type(self.query)
            provider_class: Optional[Type[SongProvider]] = None
            if url_type == "local":
                from core.musiq.local import LocalSongProvider

                provider_class = LocalSongProvider
            if storage.get("youtube_enabled") and url_type == "youtube":
                from core.musiq.youtube import YoutubeSongProvider

                provider_class = YoutubeSongProvider
            if storage.get("spotify_enabled") and url_type == "spotify":
                from core.musiq.spotify import SpotifySongProvider

                provider_class = SpotifySongProvider
            if storage.get("soundcloud_enabled") and url_type == "soundcloud":
                from core.musiq.soundcloud import SoundcloudSongProvider

                provider_class = SoundcloudSongProvider
            if storage.get("jamendo_enabled") and url_type == "jamendo":
                from core.musiq.jamendo import JamendoSongProvider

                provider_class = JamendoSongProvider
            if provider_class is not None:
                return provider_class.get_id_from_external_url(self.query)
            try:
                archived_song = ArchivedSong.objects.get(url=self.query)
                return self.__class__.get_id_from_external_url(
                    archived_song.url)
            except ArchivedSong.DoesNotExist:
                return None
        logging.error(
            "Can not extract id because neither key nor query are known")
        return None
Beispiel #6
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