LyricsGenius icon indicating copy to clipboard operation
LyricsGenius copied to clipboard

Properly get_full_info for songs and albums

Open Steffo99 opened this issue 3 years ago • 7 comments

Previously, the full song info wasn't fetched, as song_id never was None, and the check if song_id is None stopped the retrieval from executing.

I corrected that line, and added the new fields to the Song type; some of them should probably have a corresponding type too, but I don't currently have time to implement them right now.

Steffo99 avatar Feb 10 '21 14:02 Steffo99

Hi. Thanks for contributing to LyricsGenius. The if song_id is None was meant to avoid fetching the song twice in case the user had provided a song ID. But as you mentioned, the song_id is never None. I had a look and the same thing also happens in search_album as well. Although your solution solves the problem, now there will be a redundant request if the user provides a song_id in the first place. I suggest keeping the if statement as it is, but moving it before song_id gets overwritten. As for the second part of your PR concerning the new fields, we were reluctant to add more fields because the value of some fields could be misleading. For example, what does a None value for Song.featured_video mean? Does it mean the Song had no featured video, or it just wasn't in the request dict? That's why we avoided adding more fields to Song.

allerter avatar Feb 10 '21 16:02 allerter

For the Song fields, I can think of two ways:

  • Removing get_full_info altogether and update Song to have all fields.

or:

  • Breaking Song into two types: SearchSong and FullSong.

What do you think, @johnwmillr?

allerter avatar Feb 10 '21 16:02 allerter

For example, what does a None value for Song.featured_video mean? Does it mean the Song had no featured video, or it just wasn't in the request dict? That's why we avoided adding more fields to Song.

Another solution might be to use Ellipsis (...) for values which weren't retrieved, allowing them to be distinguished from None/null values returned from the API.

Steffo99 avatar Feb 10 '21 17:02 Steffo99

Your PR is about ready. Just apply the change in the last conversation and remove the changes to song.py.

allerter avatar Feb 13 '21 17:02 allerter

@Allerter I've done as you asked.

I checked, and confirm that search_artists isn't affected by this bug. (I found a few unused statements that I will probably fix in a future pull request.)

Thanks for your time!

Steffo99 avatar Feb 14 '21 04:02 Steffo99

@johnwmillr, could you please review and merge this?

allerter avatar May 27 '23 17:05 allerter

@johnwmillr, could you please review and merge this?

Hi Dear @allerter I have the same error!!! If i deploy my telegram bot project to server i get 403 forbidden error but when i run local in PyCharm everything works This my Code:

import numpy as np
from requests.exceptions import HTTPError, Timeout
import lyricsgenius

from aiogram import types
from data.config import GENIUS_ACCESS_TOKEN,GENIUS_CLIENT_ID,GENIUS_CLIENT_SECRET,GENIUS_REDIRECT_URI
from keyboards.inline.SelectButton import SelectSong
from aiogram.types import CallbackQuery
from aiogram.dispatcher.filters import Command
from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.dispatcher import FSMContext

from oauthlib.oauth2 import BackendApplicationClient
from requests_oauthlib import OAuth2Session

from loader import dp

class OAuth2Genius(StatesGroup):
    waiting_for_code = State()


GENIUS_API_BASE_URL = 'https://api.genius.com'

def create_genius_session(token=None, state=None):
    client = BackendApplicationClient(client_id=GENIUS_CLIENT_ID)
    genius3 = OAuth2Session(client=client, token=token, redirect_uri=GENIUS_REDIRECT_URI, state=state)
    return genius3

@dp.message_handler(Command("start"))
async def start_auth(message: types.Message):
    # Start the Genius OAuth2 flow
    genius3 = create_genius_session()
    authorization_url, state = genius3.authorization_url('https://api.genius.com/oauth/authorize')

    await message.answer("To authorize, click on this [link]({})".format(authorization_url), parse_mode='Markdown')
    await OAuth2Genius.waiting_for_code.set()

@dp.message_handler(state=OAuth2Genius.waiting_for_code)
async def receive_auth_code(message: types.Message, state: FSMContext):
    # Receive the Genius OAuth2 code and exchange it for a token
    genius3 = create_genius_session(state=state)
    token = genius3.fetch_token('https://api.genius.com/oauth/token', authorization_response=message.text,
                               client_secret=GENIUS_CLIENT_SECRET)

    # Save the token to use later in your requests
    await state.finish()
    await message.answer("Authorization successful! You can now use Genius API.")
    # Store the token somewhere secure for future use (e.g., a database)

    global genius2
    genius2 = create_genius_session(token=token)

genius = lyricsgenius.Genius(GENIUS_ACCESS_TOKEN)
numbered_songs = []

@dp.message_handler()
async def Send_List(message: types.Message):
    def ListOfSongs(search_songs):
        try:
            song = genius.search_songs(f"{search_songs}")

            list_of_artist = []
            list_of_song = []

            for hit in song['hits']:
                list_of_artist.append(hit['result']['primary_artist']['name'])
                list_of_song.append(hit['result']['title'])

            arr = np.dstack((list_of_artist, list_of_song))
            len_arr = len(arr[0])

            for res in arr[0][range(0, len_arr)]:
                numbered_songs.append(f"{res[0]} - {res[1]}")

        except HTTPError as e:
            print(e.errno)
            print(e.args[0])
            print(e.args[1])
        except Timeout:
            pass

    if len(numbered_songs) == 0:
        ListOfSongs(search_songs=message.text)
    else:
        numbered_songs.clear()
        ListOfSongs(search_songs=message.text)

    result_count = min(len(numbered_songs), 10)

    if result_count >= 1:
        await message.answer(
            f"<b>Результаты 1-{result_count}</b>\n\n"
            + "\n".join(f"{i + 1}.  {numbered_songs[i]}" for i in range(result_count)),
            reply_markup=SelectSong
        )
    else:
        await message.answer("Такой песен не найдено 🤔")

@dp.callback_query_handler(lambda call: call.data.isdigit())
async def Send_Lyric(call: CallbackQuery):
    index = int(call.data)

    if 1 <= index <= len(numbered_songs):
        await call.message.delete()
        await call.message.answer("🔎 Ищу тексты песен...")

        full_text = await GetText(numbered_songs[index - 1])

        if full_text:
            # Telegramning maksimal belgilar soni 4040 ta
            if len(full_text) <= 4020:
                await call.message.answer(full_text)
            else:
                short_text = full_text[:4020]
                await call.message.answer(short_text + f"...\n\n<i>через @MusixMBot</i>")
        else:
            await call.message.answer("Извините, такого текста не найдено 🫥")

async def GetText(song_name):
    artist_name, song_get_name = map(str.strip, song_name.split("-"))

    try:
        artist = genius.search_artist(artist_name, max_songs=1)
        song_text = artist.song(song_get_name)
        text = song_text.lyrics

        return f"<code>{artist_name} - {song_get_name}</code>\n\n{text[:-5]}\n\n<i>через @MusixMBot</i>"

    except HTTPError as e:
        print(e.errno)
        print(e.args[0])
        print(e.args[1])

    except Timeout:
        pass

    return None

image

I used the liyricsgenius package. But I don't know how to integrate Genius authentication method into Code. If anyone knows, please help!!!

Lionheartxx avatar Feb 01 '24 09:02 Lionheartxx