Creating Songs for the 100 Best Albums of All Time

May 30, 2024

Earlier this month, Apple Music released their top 100 greatest albums ever list. I thought it would be fun to create a song with AI for each album.

Apple Music Top 100

Suno is a magical platform that allows you to create great music in seconds. All you have to do is describe the song's style and topic, and Suno will create the song for you. Suno also allows you to create songs with custom lyrics and sounds, or an instrumental version.

Suno's Simple Create

Suno does not generate songs based on a specific artist or song, rather using genres and vibes. In order to create a song for each album, I needed to learn about each of the 100 albums.

While I have not yet listened to each of the albums all the way through, I can extract the genre and vibe of each album from the internet.

Extracting Album Data

To paint a complete picture of each album, I gathered data from three sources:

  1. Album data from Apple Music Top 100 list (code here)

    • Rank
    • Artist name
    • Album name
    • Album description
  2. Album data from Spotify API (code here)

    • Release date
    • Album cover image
    • Track list (with preview URLs)
  3. Album's Wikipedia page (code here)

    • Genre(s)
    • Album duration
    • Background information about the album

Generating Song Descriptions

To generate song descriptions, I used all the data I collected with OpenAI's GPT-4o model to create a song description for each album. I did this in 2 steps:

  1. Determine an album's genres, vibe, styles, and themes
  2. Generate a song description for the album based on the that information

Here is the code I used to generate the song descriptions:

def generate_audio_prompt(album_data):
    album_name = album_data['album_name']
    artist_name = album_data['artist_name']
    description = album_data['description']
    genres = ', '.join(album_data['wikipedia_info']['info']['Genre'].split('\n'))
    wiki_content = album_data['wikipedia_info']['content']

    # First prompt: Determine genres, vibes, and styles
    first_prompt = (
        f"Based on the following album details, determine the genres, vibes, styles, and what it should be about for a song in plain textwithout mentioning the artist or album."
        f"Focus on details about the artist and the album such as the time period, message, story, etc."
        f"Album: {album_name} by {artist_name}. Description: {description}. Genres: {genres}. Additional context: {wiki_content}"
    )
    
    openai_payload = {
        "model": "gpt-4o",
        "messages": [
            {
                "role": "user",
                "content": first_prompt
            }
        ],
        "max_tokens": 100
    }
    
    response = requests.post(OPENAI_CHAT_ENDPOINT, json=openai_payload, headers=headers)
    extracted_info = response.json()['choices'][0]['message']['content'].strip()
    logging.info(f"{album_name} by {artist_name}: {extracted_info}")

    # Second prompt: Generate a song description based on extracted info
    second_prompt = (
        f"Generate a detailed song description in 15 plain text words, without mentioning the artist or album based on the below information."
        f"Focus on details about the artist and the album such as the gender, time period, message, story, etc. {extracted_info}."
    )
    
    openai_payload["messages"][0]["content"] = second_prompt
    response = requests.post(OPENAI_CHAT_ENDPOINT, json=openai_payload, headers=headers)
    detailed_description = response.json()['choices'][0]['message']['content'].strip()
    logging.info(f"Generated detailed song description: {detailed_description}")    

    return extracted_info, detailed_description

Here are a few examples of the output:

Abby Road by The Beatles

  • A timeless rock anthem capturing finality and celebration, blending nostalgia and future, harkening 60s roots effortlessly.

Nevermind by Nirvana

  • Raw '90s grunge and alternative rock, intense vocals, heavy guitar riffs, dynamic shifts, disillusionment, personal struggle.

Discovery by Daft Punk

  • A nostalgic yet futuristic danceable house track exploring themes of transformation, change, and reinvention.

Generating Songs

To generate the songs, I used an unofficial Suno API. I configured it with a cookie from the Suno website to allow it to create songs.

The following code is a simple example of how to use the unoffical Suno API to generate a song.

def generate_audio_by_prompt(prompt):
    payload = {
        "prompt": prompt,
        "make_instrumental": False,
        "wait_audio": False
    }
    response = requests.post(f"{BASE_URL}/api/generate", json=payload, headers={'Content-Type': 'application/json'})
    response.raise_for_status()
    return response.json()

def get_audio_information(audio_ids):
    response = requests.get(f"{BASE_URL}/api/get?ids={audio_ids}")
    response.raise_for_status()
    return response.json()

def generate_song(prompt):
    data = generate_audio_by_prompt(prompt)
    ids = ",".join([track['id'] for track in data])

    for _ in range(60):
        audio_data = get_audio_information(ids)
        if all(track["status"] == 'streaming' for track in audio_data):
            for track in audio_data:
                logging.info(f"{track['id']} ==> {track['audio_url']}")
            return audio_data
        time.sleep(5)
    logging.error("Failed to generate song within the expected time frame.")
    return None

This code outputs an id which can be used to get the song's audio URL.

For example this ID 73800411-1b26-41b5-8f69-671ed96f366e can be accessed at:

How Good Are the Songs?

The songs generated I think are great! However, they don't always fit the album. But also, of course they don't. These are some of the most influential music ever created. We cannot expect to replicate The Beatles, Rolling Stones, Lauryn Hill, or any other artist.

I think Suno shines the brightest when creating new songs which are unique.

Building a Music Site

I wanted to build a site that would allow me to listen to the songs I created, learn more about each of the albums, and play snippets of it. TailwindUI has a nice template for an audio player site.

I customized the site to pull all of the album data and render it in a 100 to 1 list with the rank, artist, album, album cover, and song description.

Album List

Each detail page shows the same information along with the release date.

Album List

To help add more context about the album, I generated a description for each album using the album's Wikipedia page. It focuses on the album's genres, background, notable tracks, reception, impact, and legacy.

Album List

Some albums also have previews available, and the site allows you to play the snippets of the album's tracks.

Album Detail

What's Next?

First things first, I want a job at Suno :)

As for the website:

  • I'd like a way to create multiple versions of a song and iterate on the original
  • I want people to be able to upvote songs they really like
  • I want people to be able to comment on the songs and provide feedback
  • I want to be able to create songs for more albums in a more automated way
  • I'd like to generate deviations of the album's cover image.
    • I tried to use gpt-4o to generate variations of the album's cover image, but it didn't work (see code here).
    • I was trying to send in the photo to generate a description then use that description to generate the variation, but ended up with bad results.

Thank you

I hope this is helpful, and I'm always looking for feedback and ways to improve this.

Please reach out to me on X at @nick_mandal or email at hello@nickmandal.com.

Code Examples

Scraping Apple Music Top 100

import requests
from bs4 import BeautifulSoup
import logging

def scrape_apple_music_top_100():
    albums = []
    for i in range(1, 101):
        url = f'https://100best.music.apple.com/us/album/{i}'
        try:
            response = requests.get(url)
            response.raise_for_status()
            soup = BeautifulSoup(response.content, 'html.parser')

            artist_name = soup.find('p', {'data-testid': 'article-album-artist'})
            album_name = soup.find('h2', {'data-testid': 'article-album-name'})
            description = soup.find('p', {'data-testid': 'taglinetext'})

            artist_name = artist_name.text.strip() if artist_name else 'N/A'
            album_name = album_name.text.strip() if album_name else 'N/A'
            description = description.text.strip() if description else 'N/A'

            albums.append({
                'rank': i,
                'artist_name': artist_name,
                'album_name': album_name,
                'description': description,
                'url': url
            })

            logging.info(f"Scraped album {i}: {album_name} by {artist_name}")

        except requests.RequestException as e:
            logging.error(f"Error scraping {url}: {e}")

    return albums

Which gives us a list of 100 albums with the following format:

[
    {
        "rank": 1,
        "artist_name": "Lauryn Hill",
        "album_name": "The Miseducation of Lauryn Hill",
        "description": "Raw, profound, and era-defining, its mastery has never been duplicated.",
        "apple_music_url": "https://100best.music.apple.com/us/album/1",
    }
    ...
]

This is a good start, but we won't be able to generate a song from this data.

Spotify API

import requests
import logging

def get_spotify_album_info_from_playlist(playlist_id="1jN3oHRBVDUhxe1uaVaj3N"):
    access_token = get_spotify_access_token()
    headers = {"Authorization": f"Bearer {access_token}"}
    api_url = f"https://api.spotify.com/v1/playlists/{playlist_id}"

    response = requests.get(api_url, headers=headers)
    response.raise_for_status()
    playlist_data = response.json()

    tracks = playlist_data['tracks']['items']
    next_url = playlist_data['tracks']['next']

    while next_url:
        response = requests.get(next_url, headers=headers)
        response.raise_for_status()
        data = response.json()
        tracks.extend(data['items'])
        next_url = data['next']

    album_ids = []
    for track in tracks:
        album_id = track['track']['album']['id']
        album_ids.append(album_id)

    albums_info = []
    for album_id in list(dict.fromkeys(album_ids)):
        api_url = f"https://api.spotify.com/v1/albums/{album_id}"
        response = requests.get(api_url, headers=headers)
        response.raise_for_status()
        album_data = response.json()

        album_info = {
            'spotify_id': album_data['id'],
            'name': album_data['name'],
            'artist': album_data['artists'][0]['name'],
            'release_date': album_data['release_date'],
            'total_tracks': album_data['total_tracks'],
            'album_type': album_data['album_type'],
            'external_urls': album_data['external_urls'],
            'images': album_data['images'],
            'popularity': album_data['popularity'],
            'tracks': [
                {
                    'name': track['name'],
                    'artists': [artist['name'] for artist in track['artists']],
                    'duration_ms': track['duration_ms'],
                    'explicit': track['explicit'],
                    'preview_url': track['preview_url'],
                    'disc_number': track['disc_number'],
                    'track_number': track['track_number']
                } for track in album_data['tracks']['items']
            ]
        }

        logging.info(f"Retrieved Spotify info for album: {album_info['name']} by {album_info['artist']}")
        albums_info.append(album_info)

    return albums_info

Which gives us the following data:

"spotify_info": {
    "spotify_id": "1BZoqf8Zje5nGdwZhOjAtD",
    "name": "The Miseducation of Lauryn Hill",
    "artist": "Ms. Lauryn Hill",
    "release_date": "1998-08-25",
    "total_tracks": 16,
    "album_type": "album",
    "external_urls": {
        "spotify": "https://open.spotify.com/album/1BZoqf8Zje5nGdwZhOjAtD"
    },
    "images": [
        {
            "height": 640,
            "url": "https://i.scdn.co/image/ab67616d0000b273e08b1250db5f75643f1508c9",
            "width": 640
        },
        {
            "height": 300,
            "url": "https://i.scdn.co/image/ab67616d00001e02e08b1250db5f75643f1508c9",
            "width": 300
        },
        {
            "height": 64,
            "url": "https://i.scdn.co/image/ab67616d00004851e08b1250db5f75643f1508c9",
            "width": 64
        }
    ],
    "popularity": 74,
    "tracks": [
        {
            "name": "Intro",
            "artists": [
                "Ms. Lauryn Hill"
            ],
            "duration_ms": 47293,
            "explicit": false,
            "preview_url": "https://p.scdn.co/mp3-preview/cf0818802a4ed0adf3bb62da85e33a89eb359d98?cid=77aae9b46889406d9ab2deac2fe2c9be",
            "disc_number": 1,
            "track_number": 1
        },
        {
            "name": "Lost Ones",
            "artists": [
                "Ms. Lauryn Hill"
            ],
            "duration_ms": 333906,
            "explicit": false,
            "preview_url": "https://p.scdn.co/mp3-preview/354650d6f30dbe60e4772d045a6956402640c5a8?cid=77aae9b46889406d9ab2deac2fe2c9be",
            "disc_number": 1,
            "track_number": 2
        },
        {
            "name": "Ex-Factor",
            "artists": [
                "Ms. Lauryn Hill"
            ],
            "duration_ms": 326533,
            "explicit": false,
            "preview_url": "https://p.scdn.co/mp3-preview/c0ab315b7378564e99b76c1125a315c9dfc75174?cid=77aae9b46889406d9ab2deac2fe2c9be",
            "disc_number": 1,
            "track_number": 3
        },
        {
            "name": "To Zion (feat. Carlos Santana)",
            "artists": [
                "Ms. Lauryn Hill",
                "Carlos Santana"
            ],
            "duration_ms": 369293,
            "explicit": false,
            "preview_url": "https://p.scdn.co/mp3-preview/73e5f396a6cb144fd808e2a3733aadda329ba2eb?cid=77aae9b46889406d9ab2deac2fe2c9be",
            "disc_number": 1,
            "track_number": 4
        },
        {
            "name": "Doo Wop (That Thing)",
            "artists": [
                "Ms. Lauryn Hill"
            ],
            "duration_ms": 320266,
            "explicit": false,
            "preview_url": "https://p.scdn.co/mp3-preview/feb1c988c1940d63a3af836f2965f04177dc24c9?cid=77aae9b46889406d9ab2deac2fe2c9be",
            "disc_number": 1,
            "track_number": 5
        },
        {
            "name": "Superstar",
            "artists": [
                "Ms. Lauryn Hill"
            ],
            "duration_ms": 297106,
            "explicit": false,
            "preview_url": "https://p.scdn.co/mp3-preview/de94dce4b3f926b370bf5c279cf7aba2a5747097?cid=77aae9b46889406d9ab2deac2fe2c9be",
            "disc_number": 1,
            "track_number": 6
        },
        {
            "name": "Final Hour",
            "artists": [
                "Ms. Lauryn Hill"
            ],
            "duration_ms": 256000,
            "explicit": false,
            "preview_url": "https://p.scdn.co/mp3-preview/80f135d0ec1fceca18030931c7a602bc4edd44db?cid=77aae9b46889406d9ab2deac2fe2c9be",
            "disc_number": 1,
            "track_number": 7
        },
        {
            "name": "When It Hurts so Bad",
            "artists": [
                "Ms. Lauryn Hill"
            ],
            "duration_ms": 342200,
            "explicit": false,
            "preview_url": "https://p.scdn.co/mp3-preview/63298be3407fbb560b2a65db25ea784735d219e2?cid=77aae9b46889406d9ab2deac2fe2c9be",
            "disc_number": 1,
            "track_number": 8
        },
        {
            "name": "I Used to Love Him (feat. Mary J. Blige)",
            "artists": [
                "Ms. Lauryn Hill",
                "Mary J. Blige"
            ],
            "duration_ms": 339866,
            "explicit": false,
            "preview_url": "https://p.scdn.co/mp3-preview/21acc6c3382f124a80e56c84e92316c10a56d014?cid=77aae9b46889406d9ab2deac2fe2c9be",
            "disc_number": 1,
            "track_number": 9
        },
        {
            "name": "Forgive Them Father",
            "artists": [
                "Ms. Lauryn Hill"
            ],
            "duration_ms": 315293,
            "explicit": false,
            "preview_url": "https://p.scdn.co/mp3-preview/3c596d6dcaba909b1a94728989fd2e5601f442f8?cid=77aae9b46889406d9ab2deac2fe2c9be",
            "disc_number": 1,
            "track_number": 10
        },
        {
            "name": "Every Ghetto, Every City",
            "artists": [
                "Ms. Lauryn Hill"
            ],
            "duration_ms": 314706,
            "explicit": false,
            "preview_url": "https://p.scdn.co/mp3-preview/097b5c00ab8940dde379af150fe198b7a94360e8?cid=77aae9b46889406d9ab2deac2fe2c9be",
            "disc_number": 1,
            "track_number": 11
        },
        {
            "name": "Nothing Even Matters (feat. D'Angelo)",
            "artists": [
                "Ms. Lauryn Hill",
                "D'Angelo"
            ],
            "duration_ms": 350533,
            "explicit": false,
            "preview_url": "https://p.scdn.co/mp3-preview/8356d050621dbf9777084e6930a017cf3614783c?cid=77aae9b46889406d9ab2deac2fe2c9be",
            "disc_number": 1,
            "track_number": 12
        },
        {
            "name": "Everything Is Everything",
            "artists": [
                "Ms. Lauryn Hill"
            ],
            "duration_ms": 293266,
            "explicit": false,
            "preview_url": "https://p.scdn.co/mp3-preview/bd36c2d123b5d45957d55168ec6871cfd7f94074?cid=77aae9b46889406d9ab2deac2fe2c9be",
            "disc_number": 1,
            "track_number": 13
        },
        {
            "name": "The Miseducation of Lauryn Hill",
            "artists": [
                "Ms. Lauryn Hill"
            ],
            "duration_ms": 235466,
            "explicit": false,
            "preview_url": "https://p.scdn.co/mp3-preview/d60c89aab58d563ac24ce4f3c2a34b983322fbcc?cid=77aae9b46889406d9ab2deac2fe2c9be",
            "disc_number": 1,
            "track_number": 14
        },
        {
            "name": "Can't Take My Eyes Off of You - (I Love You Baby)",
            "artists": [
                "Ms. Lauryn Hill"
            ],
            "duration_ms": 221466,
            "explicit": false,
            "preview_url": "https://p.scdn.co/mp3-preview/cfeb0464fde5d2473830418a70f7f9bcfaa781a0?cid=77aae9b46889406d9ab2deac2fe2c9be",
            "disc_number": 1,
            "track_number": 15
        },
        {
            "name": "Tell Him",
            "artists": [
                "Ms. Lauryn Hill"
            ],
            "duration_ms": 280760,
            "explicit": false,
            "preview_url": "https://p.scdn.co/mp3-preview/15e458696e2f1c39a8991af3a15feeb93cc7bce6?cid=77aae9b46889406d9ab2deac2fe2c9be",
            "disc_number": 1,
            "track_number": 16
        }
    ]
},

Scraping Wikipedia

import requests
from bs4 import BeautifulSoup
import logging

logging.basicConfig(level=logging.INFO)

def get_soup(url):
    response = requests.get(url)
    response.raise_for_status()
    return BeautifulSoup(response.content, 'html.parser')

def extract_album_info(url):
    soup = get_soup(url)
    
    title = soup.find('h1', class_='firstHeading').text
    
    infobox = soup.find('table', class_='infobox vevent haudio')
    if infobox:
        info = {}
        for row in infobox.find_all('tr'):
            header = row.find('th')
            if header:
                key = header.text.strip()
                value = row.find('td')
                if not value:
                    continue
                info[key] = value.text.strip()
    else:
        info = None
        
    content = soup.find('div', class_='mw-content-ltr')
    
    # Get all elements
    elements = content.find_all(['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'ul', 'ol', 'li'])
    
    # Define the sections to extract
    sections_to_extract = [
        'Background', 'Recording and production', 'Music and lyrics', 'Composition', 'Themes',
        'Critical reception', 'Legacy and influence', 'Commercial performance', 'Accolades', 'Impact'
    ]
    
    # Extract and clean text from the relevant sections
    relevant_text = []
    capture_text = False
    
    for element in elements:
        if element.name in ['h2']:
            text = element.get_text(separator=" ", strip=True)
            if any(section.lower() in text.lower() for section in sections_to_extract):
                capture_text = True
            else:
                capture_text = False
        
        if capture_text:
            relevant_text.append(element.get_text(separator=" ", strip=True))
    
    # Join all relevant text into a single string
    full_text = "\n".join(relevant_text)
    
    return {
        'title': title,
        'info': info,
        'content': full_text
    }

def scrape_albums(url):
    soup = get_soup(url)
    
    table = soup.find('table', class_='wikitable sortable')
    rows = table.find_all('tr')[1:]  # skip header row
    
    albums = []
    for row in rows:
        cells = row.find_all('td')
        rank = cells[0].text.strip()
        album = cells[1].text.strip()
        artist = cells[2].text.strip()
        
        link = cells[1].find('a')
        if link:
            album_url = 'https://en.wikipedia.org' + link['href']
            album_data = extract_album_info(album_url)
        else:
            album_data = {'title': album}
        
        album_data['rank'] = rank
        album_data['artist'] = artist
        albums.append(album_data)
        
    return albums

def scrape_wikipedia_albums(url='https://en.wikipedia.org/wiki/Apple_Music_100_Best_Albums'):
    return scrape_albums(url)

Which gives us the following data:

"wikipedia_info": {
    "title": "The Miseducation of Lauryn Hill",
    "info": {
        "Released": "August 25, 1998",
        "Recorded": "September 1997 \u2013 June 1998",
        "Studio": "RPMChung KingSony MusicThe Hit FactoryRight TracksPerfect PairMarley Music, Inc.Tuff GongHouse",
        "Genre": "Neo soul\nR&B\nhip hop\nhip hop soul\nreggae\nprogressive rap",
        "Length": "77:39",
        "Label": "Ruffhouse\nColumbia",
        "Producer": "Lauryn Hill\nChe Pope\nVada Nobles"
    },
    "content": "Background [ edit ]\nIn 1996, Lauryn Hill met Rohan Marley while touring as a member of the Fugees . The two gradually formed a close relationship, and while on tour, Hill became pregnant with his child. [8] The pregnancy and other circumstances in her life inspired her to record a solo album. After contributing to fellow Fugees member Wyclef Jean 's 1997 solo record Wyclef Jean Presents The Carnival , Hill took time off from touring and recording due to her pregnancy and cases of writer's block . [9] This pregnancy, however, renewed Hill's creativity, as she recalled in an interview several years later: \"When some women are pregnant, their hair and their nails grow, but for me it was my mind and ability to create. I had the desire to write in a capacity that I hadn't done in a while. I don't know if it's a hormonal or emotional thing\u00a0... I was very in touch with my feelings at the time.\" Of the early writing process, Hill said, \"Every time I got hurt, every time I was disappointed, every time I learned, I just wrote a song.\" [10]\nWhile inspired, Hill wrote over thirty songs in her attic studio in South Orange , New Jersey . [11] Many of these songs drew upon the turbulence in the Fugees, as well as past love experiences. [12] In the summer of 1997, as Hill was due to give birth to her first child, she was requested to write a song for gospel musician CeCe Winans . [11] Several months later, she went to Detroit to work with soul singer Aretha Franklin , writing and producing her single \" A Rose is Still a Rose \". Franklin would later have Hill direct the song's music video. [13] Shortly after this, Hill did writing work for Whitney Houston . [14] Having written songs for artists in gospel , hip hop , and R&B , she drew on these influences and experiences to record The Miseducation of Lauryn Hill . [15]\nRecording and production [ edit ]\nHill began recording The Miseducation in late 1997 at Chung King Studios in New York City, [16] and completed it in June 1998 at Tuff Gong Studios in Kingston, Jamaica. [17] In an interview, Hill described the first day of recording, stating: \"The first day in the studio I ordered every instrument I ever fell in love with: harps, strings, timpani, organs, clarinets. It was my idea to record it so the human element stayed in. I didn't want it to be too technically perfect.\" [18] Initially, Jean did not support Hill recording a solo album, but eventually offered to help as a producer, which she did not accept. [19] [20] Aside from doing work at Chung King Studios, Hill also recorded at Perfect Pair Studios in New Jersey, as well as Sony Studios, [21] with some songs having different elements recorded at different studios. [21] The bulk of the album, however, was recorded at Tuff Gong Studios in Kingston, Jamaica, the studio built by reggae musician Bob Marley . [22] Regarding this shift in environment, Hill stated: \"When I started recording in New York and New Jersey, lots of people were talking to me about going different routes. I could feel people up in my face, and I was picking up on bad vibes. I wanted a place where there was good vibes, where I was among family, and it was Tuff Gong.\" [23] Many members of the Marley family were present in the studio during the recording sessions, among them Julian Marley , who added guitar elements to \"Forgive Them Father\". [22]\nIn an interview, recording engineer Gordon \"Commissioner Gordon\" Williams recalled the recording of \" Lost Ones \", stating: \"It was our first morning in Jamaica and I saw all of these kids gathered around Lauryn, screaming and dancing. Lauryn was in the living room next to the studio with about fifteen Marley grandchildren around her, the children of Ziggy , and Stephen , and Julian, and she starts singing this rap verse, and all the kids start repeating the last word of each line, chiming in very spontaneously because they were so into the song.\" [24] Columbia Records considered bringing in an outside producer for the album and had early talks with RZA of the Wu-Tang Clan . However, Hill was adamant about writing, arranging, and producing the album herself: \"It would have been more difficult to articulate to other people. Hey, it's my album. Who can tell my story better than me?\" [25] She recalled Ruffhouse Records executive Chris Swartz ensuring her artistic freedom while recording the album: \"I had total control of the album. Chris Swartz at Ruffhouse, my label, said, 'Listen, you've never done anything stupid thus far, so let me let you do your thing.'\" [26]\nMusic and lyrics [ edit ]\nThe Miseducation of Lauryn Hill is considered a neo soul album, according to Christopher John Farley of Time [32] and Rhapsody writer Mosi Reeves; [33] Complex magazine refers to it more generally as R&B . [34] Its music incorporates styles such as soul, hip hop, and reggae, [35] with some songs based in hip hop soul , according to the Encyclopedia of African American Music (2010). [36] \"When It Hurts So Bad\" is musically old roots reggae mixed with soul. While mostly in English, \"Forgive Them Father\" and \"Lost Ones\" both feature singing in patois , which is the common dialect in Jamaica. Although heavily R&B, the song \"Superstar\" contains an interpolation of the rock song \" Light My Fire \" by The Doors . Hill said that she \"didn't want to come out with a [Fugees] type of sound\", but create \"something that was uniquely and very clearly a Lauryn Hill album.\" [26] She also said that she did not intend for the album's sound to be commercially appealing: \"There's too much pressure to have hits these days. Artists are watching Billboard instead of exploring themselves. Look at someone like Aretha, she didn't hit with her first album, but she was able to grow up and find herself. I wanted to make honest music. I don't like things to be too perfect, or too polished. People may criticize me for that, but I grew up listening to Al Green and Sam Cooke . When they hit a high note, you actually felt it.\" [37]\nMuch of Hill's lyrics dealt with motherhood, the Fugees, reminiscence, love, heartbreak, and God. [11] Commenting on the album's gospel content, Hill stated \"Gospel music is music inspired by the gospels. In a huge respect, a lot of this music turned out to be just that. During this album, I turned to the Bible and wrote songs that I drew comfort from.\" [40] Several of the album's songs, such as \"Lost Ones\", \"Superstar\", \"Ex-Factor\" and \"Forgive Them Father\" were widely speculated as direct attacks at Fugee members Wyclef and Pras. [41] [42] \"Ex-Factor\" was originally intended for a different artist, however, Hill decided to keep it after it was completed, due to its personal content. [43] Although a large portion of the album's love songs would turn out to be bitter from Hill's previous relationship, \"Nothing Even Matters\", [44] a duet performed by Hill and R&B singer D'Angelo , showcased a brighter, more intimate perspective on the subject. The song was inspired by Hill's relationship with Rohan Marley. Speaking about \"Nothing Even Matters\"' lyrics, Hill remarked: \"I wanted to make a love song, \u00e1 la Roberta Flack & Donny Hathaway , and give people a humanistic approach to love again without all the physicality and overt sexuality.\" [45]\n\" To Zion \", among the more introspective tracks on the album, spoke about how Hill's family comes before her career [27] and her decision to have her first child, even though many at the time encouraged her to abort the pregnancy, so as to not conflict with her burgeoning career. [42] In an interview she discussed the song's origin and significance, commenting \"Names wouldn't come when I was ready to have him. The only name that came to me was Zion. I was like, 'is Zion too much of a weight to carry?' But this little boy, man. I would say he personally delivered me from my emotional and spiritual drought. He just replenished my newness. When he was born, I felt like I was born again.\" [46] She further stated: \"I wanted it to be a revolutionary song about a spiritual movement, and also about my spiritual change, going from one place to another because of my son.\" [47]\nThroughout The Miseducation of Lauryn Hill , several interludes of a teacher speaking to what is implied to be a classroom of children are played. The \"teacher\" was played by American poet and politician Ras Baraka speaking to a group of children in the living room of Hill's New Jersey home. [42] Hill requested that Baraka speak to the children about the concept of love, to which he improvised in the lecture. [42] Slant Magazine ' s Paul Schrodt remarked on the title's reference to Carter G. Woodson 's The Mis-Education of the Negro : \"[Hill] adopts Woodson's thesis and makes it part of her own artistic process. Like the songs themselves, the intro/outro classroom scenes suggest a larger community working to redefine itself.\" [28] Along with Woodson's book, the album's title was inspired by the film and autobiographical novel The Education of Sonny Carson . [42]\nCritical reception [ edit ]\nThe Miseducation of Lauryn Hill was met with widespread critical acclaim; [116] [117] according to Los Angeles Times journalist Geoff Boucher, it was the most acclaimed album of 1998. Reviewers frequently praised Hill's presentation of a female's view on life and love. [118] Eric Weisbard from Spin called her a \"genre-bender\" whose confident singing and rapping was balanced by vulnerable themes and sentiment. [31] In The New York Times , Ann Powers found it \"miraculous\" and \"exceptional\" for Hill to use \"her faith, based more in experience and feeling than in doctrine,\" as a means of connecting \"the sacred to the secular in music that touches the essence of soul.\" [119] AllMusic 's John Bush was impressed by how she produced most of the album, \"not as a crossover record, but as a collection of overtly personal and political statements\", while demonstrating \"performing talents, vocal range, and songwriting smarts\". [27] David Browne , writing in Entertainment Weekly , called it \"an album of often-astonishing power, strength, and feeling\", as well as \"one of the rare hip-hop soul albums\" to not lose focus with frivolous guest appearances . Browne applauded Hill's artistic vision and credited her for \"easily flowing from singing to rapping, evoking the past while forging a future of her own\". [110] Dream Hampton of The Village Voice said she seamlessly \"travels her realm within any given song\", [120] while Chicago Tribune critic Greg Kot deemed the record a \"vocal tour de force\" with arrangements that \"bristle with great ideas\". [121] XXL gave the album a perfect \"XXL\" rating, [122] with the magazine saying that it \"not only verifies [Hill] as the most exciting voice of a young, progressive hip-hop nation, it raises the standards for it.\" [123]\nIn a less enthusiastic review, Q magazine's Dom Phillips felt the music's only flaw was \"a lack of memorable melody\" on some songs that did not use interesting samples , [115] while John Mulvey from NME quibbled about what he felt were redundant skits and Hill's \"propensity\" for histrionics and declarations of \"how brilliant God is\" on an otherwise \"essential\" album. [39] Pitchfork ' s Neil Lieberman found some of the ballads tedious and the melodies \"cheesy\". [29] Citing \"Lost Ones\" and \"Superstar\" as highlights, The Village Voice music editor Robert Christgau deemed it the \" PC record of the year\", featuring exceptionally understated production and skillful rapping but also inconsistent lyrics, average singing, and superfluous skits. [124] He appreciated the \"knowledge [and] moral authority\" of Hill's perspective and values, although he lamented her appraisal of God on record. [125] In the Los Angeles Times , Soren Baker believed Hill was more effective as a critical rapper than a singer on the more emotional songs, where her voice was \"too thin to carry such heavy subject matter\". [112]\nAccolades [ edit ]\nAt the end of 1998, The Miseducation of Lauryn Hill topped numerous critics polls of the year's best albums, [126] including Rolling Stone , [127] Billboard , [128] Spin , [129] and Time . [130] It was also voted the second best record of the year in the Pazz & Jop , an annual poll of American critics published in The Village Voice . [131] Hill was nominated ten times for the 1999 Grammy Awards, making her the first woman to ever be nominated that many times in one year. She won five Grammys, including awards in the Best New Artist , Best R&B Song , Best Female R&B Vocal Performance , and Best R&B Album categories. The Miseducation of Lauryn Hill also won the Grammy Award for Album of the Year , [132] making it the first hip hop album to ever receive that award. Hill set a new record in the industry, as she also became the first woman to win five Grammys in one night. Hill was the big winner of the night at the 1999 MTV Video Music Awards , taking home four Moonmen, including Best Female Video and Video of the Year , for the music video for her single \"Doo Wop (That Thing)\", becoming the first hip hop video to win the award. [133] It also earned her nominations at the NAACP Image Awards for Outstanding Female Artist, Outstanding Album, and Outstanding Song (\"Doo Wop (That Thing)\"). [134] At the Billboard Music Awards , the record won in the R&B Album of the Year category, while \"Doo Wop\" won Best R&B/Urban New Artist Clip, [135] and at the 1999 American Music Awards , Hill won the award for Best New Soul/R&B artist. [88] She also won a Soul Train award and received a nomination for Best International Female Solo Artist at the Brit Awards . [109]\nImpact [ edit ]\nInfluence on contemporaries [ edit ]\nSeveral artists have cited the album as an inspiration for their musical work including Omar Apollo , [165] H.E.R. , [166] Ella Mai , [167] , Rin [168] , Rachel Platten , [169] Maren Morris , [170] Dua Lipa , [171] Jay-Z , [172] Alicia Keys , [173] Florence and the Machine , [174] Tierra Whack , [175] and Macy Gray . [176] [177] Furthermore Rihanna , [178] Dan Smith of Bastille [179] [177] and Adele [180] have all called The Miseducation of Lauryn Hill their personal favorite album. American entertainer Donald Glover stated that it's his most-listened to album, [181] while Zendaya , [182] along with rappers J. Cole [183] and Kendrick Lamar [184] have cited it as their favorite album by a female artist.\nProducer Savan Kotecha told Vulture that he and Ariana Grande listened to The Miseducation of Lauryn Hill album during the recording of Grande's fourth studio album Sweetener . Kotecha stated that the chord changes in Grande's song \" No Tears Left to Cry \" was modeled after the album. [185] Beyonc\u00e9 cited Hill as one of her primary inspirations for her fourth album 4 . [186] The Miseducation also inspired the albums Daytona by Pusha T , [187] The College Dropout by Kanye West , [188] Confessions by Usher , [189] and Immunity by Clairo . [190] Talent manager Nick Shymansky, shared with BET that after being inspired by The Miseducation of Lauryn Hill , his search for a Lauryn Hill-like talent led him to discovering Amy Winehouse in the early aughts . [191] Winehouse eventually began working on her debut album Frank (2003), with many of the producers that Hill has worked with. [192]\nCountry singer Lucinda Williams stated that her album World Without Tears (2003), as well her song \" Righteously \" were influenced by the album and its hip hop elements. [193] Maroon 5 's album Songs About Jane (2002) was also inspired by the LP, most notably on the track \" Sweetest Goodbye \" which drew from The Miseducation of Lauryn Hill song \"Tell Him\". [194] [195] Numerous artists have titled their projects after the album including Freddie Gibbs with his album The Miseducation of Freddie Gibbs , [196] Calboy & Lil Wayne on their single \"Miseducation\", [197] and Lil' Kim on her mixtape track \" Mis-education of Lil' Kim \". [198] Christian rap artist Lecrae credited the album for introducing him to gospel music, due to its religious themes. [199]\nMusic industry [ edit ]\nFollowing the success of The Miseducation of Lauryn Hill , Hill rose to international superstardom and established herself as a pioneering woman in hip-hop, [200] [201] as magazines ranging from Harper's Bazaar to Esquire to Teen People vied to place her on their front covers. In 1999, she was described as a \"Hip Hop icon\" by Jet . [202] Music Journalist Brandon Tensley argued that she achieved \"icon status through the strength of her debut solo album alone.\" [203]\nRadio personality Ed Lover argued that The Miseducation of Lauryn Hill offered a different perspective from other woman in hip-hop, who generally rapped about sex or being \"rugged\" and \"rough\" at the time of its release. \"Women tuned into her like she was a Ms. Luther King \" according to American rapper Redman , while further adding that the album \"made women cocky\" and empowered them. [204] Journalist Danyel Smith stated that it \"dragged rap back to the land of the living after the twin drive-by murders of Tupac Shakur (1996) and Notorious B.I.G. (1997)\". [205]\nIn a February 8, 1999, Time cover-story, Hill was credited for helping fully assimilate hip-hop into mainstream music, and became the first hip-hop artist to ever appear on the magazine's front cover. [206] [207] Later that month, Hill broke numerous records at the 41st Annual Grammy Awards . [208] Among the awards she received that night was Album of the Year , which has often been recognized as the most prestigious award in American music, [209] [210] and marked the first time a hip-hop artist won the award. According to music executive Clive Davis , the win helped the Grammy Awards become more accepting of rap and hip hop music. Former senior music editor for Amazon , Pete Hilgendorf stated it marked the start of when \"the progression of R&B moving into hip-hop became evident\". [211]\nThe Rough Guide to Rock (2003) hailed the album as the \"ultimate cross-over album of the hip-hop era \". [212] The album has been cited as one of the earliest to fully blend rapping and singing, [213] with Genius dubbing Hill as \"the first superstar to excel at both singing and rapping\". [214] Writing for The New York Times in 2018, Journalist Jon Caramanica noted that by \"the mid-to-late 2000s, singing became a full-fledged part of hip-hop, owing to the success of Drake , one of the first stars \u2014Lauryn Hill got there earlier \u2014 who toggled cleanly between rapping and singing and understood them as variations of each other, not oppositional forces. Rappers are singers now, to the point where the framework of singing has been refracted almost wholly through the needs of hip-hop.\" [215] When speaking to Pitchfork about the album's influence, rapper Vince Staples stated that \"Nowadays we get a combination of singing and rapping in a lot of music. But back then, it was a risk. So for her to sing like that early on, combined with the subject matter, the arrangement of the album with its throughline, and how it just flows with you. it's definitely a classic body of work.\" [216] Janelle Mon\u00e1e shared a similar sentiment arguing that Hill \"was hip-hop and R&B, but nobody had used [the combination] in the way she did. She created something that we had never tasted before.\" [217] Cyndi Lauper argued that the album \"changed everything and everybody. Lauryn Hill changed phrasing . She started a whole new kind of singing, taking church and hip-hop and stirring it with this freaking great feeling and voice.\" [218]\nAlong with Brown Sugar by D'Angelo , Erykah Badu 's Baduizm , and Maxwell's Urban Hang Suite by Maxwell , [219] [220] The Miseducation of Lauryn Hill is considered to be one of the most important and definitive releases in the history of neo soul music. [221] According to Ebony magazine, it brought the neo soul genre to the forefront of popular music, [222] and became the genre's most critically acclaimed and popular album. [36] The Encyclopedia of African American Music (2010), noted that \"some tracks are based more in hip hop soul than neo soul, but the record is filled with live musicians and layered harmonies, and therefore it is a trendsetting record that connects modern hip hop, R&B, and classic soul music together, creating groundwork for what followed it in the neo soul genre.\" [36] In conversation with the Los Angeles Times about the success of the 1999 album Black Diamond by Angie Stone , editor Emil Wilbekin of Vibe stated \"I think [1998's] The Miseducation of Lauryn Hill changed the way a lot of R&B artists are presenting their music,\" and added \"With Lauryn and Erykah Badu and D'Angelo and Maxwell, there's been a return to live instruments, real singing and real love stories. I think Angie Stone is an outgrowth of that.\" [223]",
    "rank": "1",
    "artist": "Lauryn Hill"
},

Generating Album covers

import base64
import os
import requests
from PIL import Image
from io import BytesIO
import logging

logging.basicConfig(level=logging.INFO)

OPENAI_CHAT_ENDPOINT = "https://api.openai.com/v1/chat/completions"
OPENAI_IMAGE_ENDPOINT = "https://api.openai.com/v1/images/generations"

def describe_image(image_url):
    headers = {
        "Authorization": f"Bearer {OPENAI_API_KEY}",
        "Content-Type": "application/json"
    }

    payload = {
        "model": "gpt-4o",
        "messages": [
            {
                "role": "user",
                "content": f"Describe the image in vivid detail such that I would be able to recreate the image from only your description {image_url}"
            }
        ],
        "max_tokens": 300
    }

    try:
        response = requests.post(OPENAI_CHAT_ENDPOINT, headers=headers, json=payload)
        response.raise_for_status()
        description = response.json()["choices"][0]["message"]["content"]
        logging.info(f"Image description: {description}")
        return description

    except requests.RequestException as e:
        logging.error(f"Error describing image: {e}")
        return None

def generate_image(prompt, size='1024x1024'):
    headers = {
        "Authorization": f"Bearer {OPENAI_API_KEY}",
        "Content-Type": "application/json"
    }

    payload = {
        "model": "dall-e-3",
        "prompt": f"Generate a musical record album cover based on the following description: {prompt}",
        "n": 1,
        "size": size,
        "style": "vivid",
        "quality": "hd",
        "response_format": "url"
    }

    try:
        response = requests.post(OPENAI_IMAGE_ENDPOINT, headers=headers, json=payload)
        response.raise_for_status()
        image_url = response.json()["data"][0]["url"]
        logging.info(f"Generated image URL: {image_url}")
        return image_url

    except requests.RequestException as e:
        logging.error(f"Error generating image: {e}")
        return None

def create_new_album_cover_from_original(image_url, album_metadata):
    # Describe the image
    logging.info(f"Generated image from {image_url}")
    description = describe_image(image_url)
    if description:
        # Generate a new image based on the description
        new_image_url = generate_image(description)
        if new_image_url:
            album_name = album_metadata.get("album_name", "album")
            artist_name = album_metadata.get("artist_name", "artist")
            logging.info(f"Generated image URL from {album_name} by {artist_name}: {new_image_url}")
            # Download the new image
            new_image = requests.get(new_image_url)
            if new_image.status_code == 200:
                # Save the image with album metadata
                try:

                    image_name = f"{album_name}_{artist_name}.jpg"
                    with open(image_name, 'wb') as f:
                        f.write(new_image.content)
                    logging.info(f"New image saved as: {image_name}")
                    return (new_image_url, description)
                except Exception as e:
                    logging.error(f"Error saving image: {e}")
                    return (None, None)
            else:
                logging.error("Failed to download the new image.")
                return (None, None)
        else:
            logging.error("Failed to generate a new image.")
            return (None, None)
    else:
        logging.error("Failed to describe the image.")
        return (None, None)

This ended up generating completely hallucinated images.

I think I need to try a different approach with some more experimentation.