johnwmillr / LyricsGenius

Download song lyrics and metadata from Genius.com 🎶🎤
http://www.johnwmillr.com/scraping-genius-lyrics/
MIT License
892 stars 158 forks source link

Properly get_full_info for songs and albums #181

Open Steffo99 opened 3 years ago

Steffo99 commented 3 years ago

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.

allerter commented 3 years ago

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 commented 3 years ago

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

or:

What do you think, @johnwmillr?

Steffo99 commented 3 years ago

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.

allerter commented 3 years ago

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

Steffo99 commented 3 years ago

@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!

allerter commented 1 year ago

@johnwmillr, could you please review and merge this?

Lionheartxx commented 6 months ago

@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!!!