johnwmillr / LyricsGenius

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

Add option to get metadata of desired track #257

Open 808arc opened 1 year ago

808arc commented 1 year ago

Firstly, I would like to extend my appreciation for the exceptional tool you have developed!

Could I ask you please to add a method that will return also who produced a track? (I highlighted area with blue color) If it will be able to implement with return of producer names and their link_pages on Genius.com, I will be kindly appreciated!

Thank you for your attention to this matter and for creating such an impressive tool.

telegram-cloud-photo-size-2-5400194413306692971-y

allerter commented 1 year ago

You can get the producers of a song if you get the song info from their WebAPI:

genius = lyricsgenius.PublicAPI()

song = genius.song(1)["song"]
for artist in song["producer_artists"]:
    print(artist["name"], sep=", ")

There is a chance this might raise a HTTP 403 error which is referenced in #220

808arc commented 1 year ago

Thank you! Im trying to test this way ASAP

808arc commented 1 year ago

Hey @allerter,

Just wanted to drop a quick note to say thanks for showing me a way to tackle my idea. I gave it a try, and it worked like a charm!

By the way, I have a small question. It seems that when I set max_songs=10, the code crashes. It only works smoothly if I keep the value at 2 or below.

Here's the code snippet I'm referring to:

genius = lyricsgenius.PublicAPI()

# Search for 10 Drake songs using the 'search_artist' method
artist = genius.search_artist("Drake", max_songs=1)

# Iterate over the songs
for song in artist.songs:
    # Retrieve the information of the song
    song_info = genius.song(song.id)

    # Iterate over the producer artists of the song
    for artist in song_info["song"]["producer_artists"]:
        print(artist["name"], sep=", ")

If you have any tips or insights on how to overcome this issue, I'd really appreciate your help. Thanks again for your support!

allerter commented 1 year ago

That is likely because of the timeout errors. You could try increasing the sleep time between each request:

genius = lyricsgenius.PublicAPI(sleep_time=2)

This way, the client waits 2 seconds between each request to the API.

808arc commented 1 year ago

Hey @allerter, exciting news! I've fixed my code and now it's returning the exact data I need. I can easily grab the names of the production team members. But here's the deal: I'm now facing a challenge where I want to fetch the producer's Instagram link. Do you reckon it's possible to get it solely using this API? Or should I turn to other tools like the 'scrapy' library? My thought is to search for the producer's page on the Genius website and scrape it smoothly. Your thoughts on this would be highly appreciated!

allerter commented 1 year ago

Actually, the WebAPI already provides their Twitter and Instagram username. Try this:

genius = lyricsgenius.PublicAPI(sleep_time=2)
artist = genius.artist(264329)["artist"]
print(artist["instagram_name"])
808arc commented 1 year ago

Thank you, @allerter! It's really worked well on me! I will keep updating my code, because I feel I need to find first how to retrieve Genius Artist ID. In order to appreciate your time, does it possible to ask you where can I read more about work with WEB API?

allerter commented 1 year ago

The WebAPI is the API users use when browsing through the Genius's website. It has no documentation and isn't meant to be used by the developers. Genius has their own official Developer API, but it's very limited, so we resort to their unofficial WebAPI to get the info we want. You can explore the WebAPI by looking at the dictionary objects returned by the methods we use. We also have a reference docs that we've tried to implement all the WebAPI endpoints in.

Lionheartxx commented 5 months ago

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