🎧

SpotifyAPIで未知の曲を漁ってみた

2023/03/21に公開

概要

PythonでSpoyifyAPIを使って完全にランダムな曲をプレイリストに入れてみました。

背景

普段皆さんは音楽の配信サービスは使用していますか?自分はSpotifyを使って、仕事中や移動中、ご飯食べてるときなどほぼ一日中音楽を聴いています。ただ、こういうサブスクのサービスって、どうしても試聴する音楽のジャンルが偏ったり、未知の音楽を発見しづらいという欠点があります。自分はJPOPからアフリカの民族音楽まで何でも聴くような人間なので、それでは困ります。特にDJみたいに、新しい音楽を発見するのが仕事といった人には不向きな機能なんじゃないんでしょうか。しかし、まったく聞いたことのないジャンルを自分で想像しながら、手動で未知の音楽にたどり着くというのには限界があります。
今回は、SpotifyAPIを用いて完全にランダムな楽曲を取得することを試みました。

参考にしたもの

自分と同じようなことを考えていた人はいたようで、以下の記事を参考にさせていただきました。

Spotify APIで完全ランダムなプレイリストを生成する(Python) - Qiita
https://qiita.com/heyt/items/50daa854cc8b3751decf

こちらの記事ではランダムな英数字(A-Z,0-9)か、Unicode文字全体(あらゆる言語、絵文字なども含む)の中のランダムな文字列を元に楽曲を検索するようになっています。ただ、実際に試すと圧倒的に前者の方がヒットする割合が多く、個人的には後者の割合が増やしたいと思いました。というか、完全にランダムな楽曲を取得するなら後者だけでいいような気がしました。

SpotifyAPI

SpotifyAPIはSpotifyが提供しているAPIで、こちらを使うと楽曲メタデータの取得、プレイリストの作成、楽曲の再生といった操作が可能となります。
今回はPython用のSpotipyという(やや安直なネーミングの)ライブラリを使用します。

SpotifyAPIの使い方は以下を参考にしました。Spotifyの開発者向けページからシークレット情報を取得しておきます。

Pythonを用いてSpotifyのプレイリストを作成&再生してみる - せかいの世界(備忘録)
https://tori114.hatenablog.com/entry/2018/10/14/090525

ランダムな文字列の生成

Spotify上で楽曲を検索するための文字列を生成します。各Unicode文字に対応する整数値(0~1114111)の中からランダムな数字を選択して文字に変換し、ワイルドカードを付与します。ワイルドカードは文字の前、文字の前後、文字の後ろの三通りで付与します。

def get_random_search():
    """Get a random character of unicode.
    """

    rand_char = ''

    while rand_char == '':
        rand_char = chr(random.randint(0, 1114111))

    random_search = ''

    if random.randint(0, 2) == 0:
        random_search = rand_char + '%'
    elif random.randint(0, 2) == 1:
        random_search = '%' + rand_char + '%'
    else:
        random_search = '%' + rand_char

    return random_search

main関数

こちらで既存のプレイリストに曲を追加します。追加する曲数およびプレイリスト内の既存の曲を削除するかどうかを引数で指定します。曲を追加する際にはアーティスト名と楽曲名が表示されます。

ちなみに関数名は1990年代のHipHopクルーDiggin In The Crates(D.I.T.C.)から採用しました。HipHopのDJは日常的にあらゆるレコードやCDから常にビート(ラップを乗せる音源)のサンプリング元となる音源を漁っています。Diggin In The Cratesには「レコード箱を漁る」「ビート探し」という意味があります。今回のプログラムはそれを連想させるのでこちらの名前にしてみました。

def diggin_in_the_crate(num_tracks=30, remove_current_items=False):
    """
    Search completely random tracks.

    Args:
        num_tracks (int, optional): Number of tracks to add to the playlist. Defaults to 30.
        remove_current_items (bool, optional): Whether to remove current items in the playlist or not.
    """

    # Retrieve current items in the playlist
    current_items = sp.playlist_items(playlist_id)

    # Delete existing tracks if remove_current_items is True
    if remove_current_items and current_items['items']:
        remove_items = [item['track']['uri']
                        for item in current_items['items']]
        sp.playlist_remove_all_occurrences_of_items(playlist_id, remove_items)
        print("Removed current tracks.")

    # Get a list of country codes
    country_codes = sp.country_codes
    country_num = len(country_codes)

    track_ids = []

    # Add tracks
    print("Start searching...")

    while len(track_ids) < num_tracks:
        random_market = random.choice(country_codes)
        query = get_random_search()
        random_offset = random.randint(0, 999)
        results = sp.search(type='track', offset=random_offset,
                            limit=1, q=query, market=[random_market])

        if results and len(results['tracks']['items']) >= 1:
            track = results['tracks']['items'][0]
            track_name = track['name']
            track_artists = [artist['name'] for artist in track['artists']]

            # Add track ids
            track_ids.append(track['id'])
            sp.user_playlist_add_tracks(username, playlist_id, [track_ids[-1]])
            print(
                f"Added {track_name} - {', '.join(track_artists)} to the playlist.")

    print("Updated playlist.")

実行結果

こちらが30曲追加した場合の実行結果です。見てもらえばわかる通り、中国語が入っていたり、アラビア文字のようなものが入っていたりして、言語やジャンル問わずランダムな楽曲が入っていることが確認できます。

感想

追加される楽曲はほんとにランダムで、よく分からないドイツ語の子供向け絵本の読み聞かせCDや、韓国の仏教音楽なんかも出てきてかなりカオスで個人的には面白いです。ただ問題点として、やや処理に時間がかかります。30曲だとだいたい7~8分はかかります。あらゆる文字からランダムな文字列を生成してるので、マイナーな言語の文字や絵文字なんかが入ってくると検索でヒットしないためだと思われます。Pythonの並列処理や並行処理も試そうと思ったものの、そもそも生成した文字列が検索でヒットするまでの試行回数が毎回不定で、定量的な処理時間の検証が出来ないので止めました。なんかいい方法はないでしょうか。。。

Discussion