メインコンテンツへスキップ
見出し画像

Spotifyプレイリスト3ステップ一括取得ツール #1

    Spotifyプレイリスト管理ツールを“全部入り版”にした話

    はぁ〜、またプレイリストが散らかってる。

    ……いや、お前が入れたんやろって話なんやけど。

    消したい曲は残るし、入れた曲は埋もれるし、
    並べ替えようと思ったらSpotify開いてポチポチ……

    正直、ちょっとだるい。

    そんなときに思い出したんが、昔作ったこれ👇

    Spotifyのプレイリストから曲名とアーティスト名を取ってコンソールに表示するだけのやつ

    動く。たしかに動く。
    でも今見返すと、かなり“とりあえず動いたからヨシ!”の世界。

    で、最近またSpotipy触ってたら気づいてしまったんよ。

    「あれ、これ……APIでもっといろいろできるやん」

    そうなると、もう止まらん。

    というわけで今回は、
    プレイリスト操作をまとめて扱える全部入り版として作り直しました。


    何ができるようになったか

    一言でいうと、

    👉 プレイリストの「読み書き全部」に対応しました。

    はい、ほぼ別物です。


    できること

    • プレイリスト一覧を表示

    • プレイリスト内の曲を表示

    • CSVで保存

    • プレイリストを新規作成

    • 曲を追加

    • 曲を削除

    • 曲順を並べ替え


    #1との違い(これが一番分かりやすい)

    画像

    昔のやつは、いわば見る専。
    今回のやつは、現場監督。


    #1に潜んでたバグの話

    〜過去のワイ、ちょっとそこ座れ〜

    リニューアルついでに、昔のコードもちゃんと見直した。

    ……で、出てきた。


    バグ①:APIを2回呼んでた

    playlist_data = sp.user_playlist(user, playlist_id)  # ← 1回目
    
    def getTitleArtist(user, playlist_id):
        playlist = sp.user_playlist(user, playlist_id)   # ← 2回目

    完全に無駄。

    いや、なんで2回呼んでんねん。

    当時のワイ、たぶん
    「動いた!ヨシ!」で思考停止してる。

    Spotifyさん、あのときはすみませんでした。


    バグ②:アーティスト名がたまに違う

    artist = track['album']['artists'][0]['name']

    これ、アルバムアーティスト取ってたんよ。

    コンピレーションだと普通に
    👉 Various Artists
    とか返ってくる。

    正解はこっち👇

    artist = track['artists'][0]['name']

    たったこれだけの違い。

    でも意味は全然違う。

    これ、地味やけど一番やっかいなやつ。

    👉 「動くけど、ちょっと違う」

    技術の世界で一番怖いの、だいたいコイツです。


    さらにいうと、関数も時代が進んでた

    #1で使ってた user_playlist() は、今の流れだとちょい古め。

    今回は playlist_tracks() に変更して、
    100曲超でもちゃんと取れるようにページング対応しました。

    プレイリストって、放置すると増殖するからな。
    あれ、気づいたら100曲超えてる生き物や。


    変わった点:認証方式

    “見るだけ”から“触れる”へ

    #1は SpotifyClientCredentials。

    これ、ざっくりいうと
    👉 見る専モード

    今回使ってるのは SpotifyOAuth。

    👉 操作できるモード

    初回だけブラウザ開いて認証するやつです。

    一回通せば、あとは自動ログイン。

    つまり今回の進化はこれ👇

    • 読むだけのツール →

    • ちゃんと使える道具


    コード構造

    長くなったけど、やってることはシンプル。

    main()
    ├── list_playlists()
    ├── show_playlist()
    ├── create_playlist()
    ├── add_tracks()
    ├── remove_tracks()
    └── reorder_tracks()

    つまり、

    • 認証する

    • メニュー出す

    • 機能呼ぶ

    これだけ。

    行数は増えたけど、
    頭の中はできるだけ散らからんようにした。


    起動するとこんな感じ

    ╔══════════════════════════════════╗
    ║  Spotify Playlist Manager        ║
    ╠══════════════════════════════════╣
    ║  1. プレイリスト一覧を表示       ║
    ║  2. プレイリストの曲を表示/保存  ║
    ║  3. プレイリストを新規作成       ║
    ║  4. プレイリストに曲を追加       ║
    ║  5. プレイリストから曲を削除     ║
    ║  6. 曲の順番を並べ替え           ║
    ║  0. 終了                         ║
    ╚══════════════════════════════════╝

    番号選ぶだけ。

    派手さはない。

    でもこういうやつ、
    一番仕事する。


    セットアップ手順

    Step 1:インストール

    pip install spotipy

    Step 2:Spotify Developerでアプリ作成

    • Client ID

    • Client Secret

    取得。

    さらに👇

    http://localhost:8888/callback

    これをRedirect URIに設定。

    これ忘れると詰む。

    しかも静かに詰む。


    Step 3:コードに貼る

    CLIENT_ID     = "ここ"
    CLIENT_SECRET = "ここ"
    REDIRECT_URI  = "http://localhost:8888/callback"

    Step 4:実行

    python spotify_playlist_manager.py

    ここで出てくる敵

    〜ベタベタ星人たち〜

    初回セットアップで現れるのがこいつら👇

    • Redirect URIミス

    • Client ID貼りミス

    • スコープ不足

    こいつら、静かにくっついてきてエラー出すくせに、
    原因が分かりにくい。

    コードのせいにしたくなるけど、だいたい違う。

    👉 犯人は門番。


    使ってみた感想

    一番便利なのは、削除。

    これほんま。

    Spotifyアプリで1曲ずつ消してるときのワイ👇

    👉「もうええか…」

    ってなる。

    でもこのツールなら、

    • 番号指定

    • 複数削除

    • 確認付き

    なので、ちゃんと整理できる。


    並べ替えは?

    正直、GUIの方が直感的。

    でも、

    • 先頭に持ってくる

    • 特定位置に移動

    みたいな用途なら普通に使える。


    CSV保存が地味に強い

    これが今回の裏ボス。

    CSVにすると👇

    • Excel

    • Notion

    • Python分析

    に持っていける。

    さらに popularity 入れてるから、

    👉 自分の音楽の癖が見える

    プレイリストって、ただの再生リストじゃなくて
    ログなんよな。


    おわりに

    #1を書いたのは2024年6月。

    あのときは、とりあえず動けばOKやった。

    今回、それをちゃんと使える形にした。

    昔のコード見ると、ちょっと恥ずかしい。

    でも、その恥ずかしさがあるから今がある。

    👉 過去の自分もログとして残しておいた方が面白い。

    というわけで、今回は消さずに残しました。

    過去のワイも、いちおう共同執筆者ということで。


    次は、CSV使ってプレイリスト分析でもやるかも。

    • 人気度の分布

    • アーティスト偏り

    • 自分の音楽傾向

    このへん見たら、たぶんおもろい。

    ほなまた。

    Spotify整理に疲れた勇者、ぜひ使ってみてくれ。

    コード全文(貼り付け用)


    """
    Spotify Playlist Manager
    ========================
    プレイリストの取得・作成・曲追加・削除・並べ替えをまとめて操作するツール。
    
    必要なライブラリ:
        pip install spotipy
    
    Spotify Developer Dashboardで以下を取得して設定してください:
        https://developer.spotify.com/dashboard
    """
    
    import spotipy
    from spotipy.oauth2 import SpotifyOAuth
    import csv
    import os
    from pathlib import Path
    
    # ==============================
    # 設定エリア(ここを編集してください)
    # ==============================
    CLIENT_ID     = "YOUR_CLIENT_ID"
    CLIENT_SECRET = "YOUR_CLIENT_SECRET"
    REDIRECT_URI  = "http://localhost:8888/callback"
    
    # 必要なスコープ(読み取り+書き込み)
    SCOPE = (
        "playlist-read-private "
        "playlist-modify-public "
        "playlist-modify-private"
    )
    
    # CSV保存先(スクリプトと同じフォルダ)
    OUTPUT_DIR = Path(__file__).parent
    
    
    # ==============================
    # 認証
    # ==============================
    def get_spotify_client() -> spotipy.Spotify:
        auth = SpotifyOAuth(
            client_id=CLIENT_ID,
            client_secret=CLIENT_SECRET,
            redirect_uri=REDIRECT_URI,
            scope=SCOPE,
            open_browser=True,
        )
        return spotipy.Spotify(auth_manager=auth)
    
    
    # ==============================
    # ユーティリティ
    # ==============================
    def get_all_tracks(sp: spotipy.Spotify, playlist_id: str) -> list[dict]:
        """100曲超のプレイリストにも対応(ページング処理)"""
        tracks = []
        result = sp.playlist_tracks(playlist_id)
        while result:
            for item in result["items"]:
                track = item.get("track")
                if not track:
                    continue
                tracks.append({
                    "index":       len(tracks) + 1,
                    "title":       track["name"],
                    "artist":      track["artists"][0]["name"],
                    "album":       track["album"]["name"],
                    "uri":         track["uri"],
                    "popularity":  track["popularity"],
                    "duration_ms": track["duration_ms"],
                })
            result = sp.next(result) if result["next"] else None
        return tracks
    
    
    def ms_to_mmss(ms: int) -> str:
        total_sec = ms // 1000
        return f"{total_sec // 60}:{total_sec % 60:02d}"
    
    
    def print_tracks(tracks: list[dict]) -> None:
        print(f"\n{'No':>4}  {'曲名':<40} {'アーティスト':<25} {'人気度':>4}  {'時間'}")
        print("-" * 90)
        for t in tracks:
            title  = t["title"][:38] + ".." if len(t["title"]) > 40 else t["title"]
            artist = t["artist"][:23] + ".." if len(t["artist"]) > 25 else t["artist"]
            print(f"{t['index']:>4}  {title:<40} {artist:<25} {t['popularity']:>4}  {ms_to_mmss(t['duration_ms'])}")
        print(f"\n合計: {len(tracks)} 曲")
    
    
    def prompt(message: str) -> str:
        return input(f"\n{message}: ").strip()
    
    
    # ==============================
    # 機能 1:プレイリスト一覧を表示
    # ==============================
    def list_playlists(sp: spotipy.Spotify) -> list[dict]:
        me = sp.me()
        result = sp.user_playlists(me["id"])
        playlists = []
        while result:
            for p in result["items"]:
                playlists.append({"id": p["id"], "name": p["name"], "total": p["tracks"]["total"]})
            result = sp.next(result) if result["next"] else None
    
        print(f"\n{'No':>4}  {'プレイリスト名':<45} {'曲数':>5}")
        print("-" * 60)
        for i, p in enumerate(playlists, 1):
            name = p["name"][:43] + ".." if len(p["name"]) > 45 else p["name"]
            print(f"{i:>4}  {name:<45} {p['total']:>5}")
        return playlists
    
    
    # ==============================
    # 機能 2:プレイリストの曲を取得して表示/CSV保存
    # ==============================
    def show_playlist(sp: spotipy.Spotify) -> None:
        playlists = list_playlists(sp)
        choice = prompt("番号を入力(直接IDも可)")
    
        if choice.isdigit() and 1 <= int(choice) <= len(playlists):
            pid = playlists[int(choice) - 1]["id"]
            pname = playlists[int(choice) - 1]["name"]
        else:
            pid = choice
            pname = "playlist"
    
        try:
            tracks = get_all_tracks(sp, pid)
        except Exception as e:
            print(f"[エラー] 取得失敗: {e}")
            return
    
        print_tracks(tracks)
    
        save = prompt("CSVに保存しますか? [y/n]").lower()
        if save == "y":
            safe_name = "".join(c if c.isalnum() or c in " _-" else "_" for c in pname)
            out_path = OUTPUT_DIR / f"{safe_name}.csv"
            with open(out_path, "w", newline="", encoding="utf-8-sig") as f:
                writer = csv.DictWriter(f, fieldnames=["index", "title", "artist", "album", "popularity", "duration_ms", "uri"])
                writer.writeheader()
                writer.writerows(tracks)
            print(f"保存しました → {out_path}")
    
    
    # ==============================
    # 機能 3:プレイリストを新規作成
    # ==============================
    def create_playlist(sp: spotipy.Spotify) -> str:
        me = sp.me()
        name = prompt("新しいプレイリスト名")
        desc = prompt("説明(省略可、Enterでスキップ)")
        public_input = prompt("公開しますか? [y/n]").lower()
        public = public_input == "y"
    
        try:
            result = sp.user_playlist_create(
                user=me["id"],
                name=name,
                public=public,
                description=desc or "",
            )
            print(f"作成しました! → {result['external_urls']['spotify']}")
            return result["id"]
        except Exception as e:
            print(f"[エラー] 作成失敗: {e}")
            return ""
    
    
    # ==============================
    # 機能 4:プレイリストに曲を追加
    # ==============================
    def add_tracks(sp: spotipy.Spotify) -> None:
        playlists = list_playlists(sp)
        choice = prompt("追加先プレイリストの番号(直接IDも可)")
    
        if choice.isdigit() and 1 <= int(choice) <= len(playlists):
            pid = playlists[int(choice) - 1]["id"]
        else:
            pid = choice
    
        print("\n曲のSpotify URIまたはURLを1行ずつ入力してください。")
        print("(例: spotify:track:4iV5W9uYEdYUVa79Axb7Rh  または  https://open.spotify.com/track/... )")
        print("入力が終わったら空行でEnterを押してください。\n")
    
        uris = []
        while True:
            line = input("  > ").strip()
            if not line:
                break
            # URLからURIに変換
            if line.startswith("https://open.spotify.com/track/"):
                track_id = line.split("/track/")[1].split("?")[0]
                line = f"spotify:track:{track_id}"
            if line.startswith("spotify:track:"):
                uris.append(line)
            else:
                print(f"  [スキップ] 認識できない形式: {line}")
    
        if not uris:
            print("追加する曲がありませんでした。")
            return
    
        # Spotify APIは1回100件まで → バッチ処理
        try:
            for i in range(0, len(uris), 100):
                sp.playlist_add_items(pid, uris[i:i + 100])
            print(f"{len(uris)} 曲を追加しました。")
        except Exception as e:
            print(f"[エラー] 追加失敗: {e}")
    
    
    # ==============================
    # 機能 5:プレイリストから曲を削除
    # ==============================
    def remove_tracks(sp: spotipy.Spotify) -> None:
        playlists = list_playlists(sp)
        choice = prompt("削除対象プレイリストの番号(直接IDも可)")
    
        if choice.isdigit() and 1 <= int(choice) <= len(playlists):
            pid = playlists[int(choice) - 1]["id"]
        else:
            pid = choice
    
        try:
            tracks = get_all_tracks(sp, pid)
        except Exception as e:
            print(f"[エラー] 取得失敗: {e}")
            return
    
        print_tracks(tracks)
    
        nums_input = prompt("削除する曲の番号をカンマ区切りで入力(例: 1,3,5)")
        try:
            indices = [int(n.strip()) - 1 for n in nums_input.split(",")]
        except ValueError:
            print("[エラー] 番号の形式が正しくありません。")
            return
    
        to_remove = []
        for idx in indices:
            if 0 <= idx < len(tracks):
                to_remove.append(tracks[idx])
            else:
                print(f"[スキップ] 番号 {idx + 1} は範囲外です。")
    
        if not to_remove:
            print("削除対象がありませんでした。")
            return
    
        print("\n削除する曲:")
        for t in to_remove:
            print(f"  - {t['title']} / {t['artist']}")
        confirm = prompt("本当に削除しますか? [y/n]").lower()
        if confirm != "y":
            print("キャンセルしました。")
            return
    
        try:
            uris = [t["uri"] for t in to_remove]
            sp.playlist_remove_all_occurrences_of_items(pid, uris)
            print(f"{len(uris)} 曲を削除しました。")
        except Exception as e:
            print(f"[エラー] 削除失敗: {e}")
    
    
    # ==============================
    # 機能 6:プレイリストの曲を並べ替え(1曲を任意の位置に移動)
    # ==============================
    def reorder_tracks(sp: spotipy.Spotify) -> None:
        playlists = list_playlists(sp)
        choice = prompt("並べ替えるプレイリストの番号(直接IDも可)")
    
        if choice.isdigit() and 1 <= int(choice) <= len(playlists):
            pid = playlists[int(choice) - 1]["id"]
        else:
            pid = choice
    
        try:
            tracks = get_all_tracks(sp, pid)
        except Exception as e:
            print(f"[エラー] 取得失敗: {e}")
            return
    
        print_tracks(tracks)
    
        try:
            from_num = int(prompt("移動する曲の番号")) - 1
            to_num   = int(prompt("移動先の番号(その位置の前に挿入)")) - 1
        except ValueError:
            print("[エラー] 数字を入力してください。")
            return
    
        if not (0 <= from_num < len(tracks)) or not (0 <= to_num <= len(tracks)):
            print("[エラー] 番号が範囲外です。")
            return
    
        print(f"\n「{tracks[from_num]['title']}」を {from_num + 1} 番目 → {to_num + 1} 番目に移動します。")
        confirm = prompt("実行しますか? [y/n]").lower()
        if confirm != "y":
            print("キャンセルしました。")
            return
    
        try:
            sp.playlist_reorder_items(pid, range_start=from_num, insert_before=to_num)
            print("並べ替えました。")
        except Exception as e:
            print(f"[エラー] 並べ替え失敗: {e}")
    
    
    # ==============================
    # メインメニュー
    # ==============================
    MENU = """
    ╔══════════════════════════════════╗
    ║  Spotify Playlist Manager        ║
    ╠══════════════════════════════════╣
    ║  1. プレイリスト一覧を表示       ║
    ║  2. プレイリストの曲を表示/保存  ║
    ║  3. プレイリストを新規作成       ║
    ║  4. プレイリストに曲を追加       ║
    ║  5. プレイリストから曲を削除     ║
    ║  6. 曲の順番を並べ替え           ║
    ║  0. 終了                         ║
    ╚══════════════════════════════════╝"""
    
    
    def main() -> None:
        print("Spotify に接続中... ブラウザが開いたら認証してください。\n")
        try:
            sp = get_spotify_client()
            me = sp.me()
            print(f"ログイン成功: {me['display_name']} (@{me['id']})")
        except Exception as e:
            print(f"[エラー] 認証失敗: {e}")
            print("CLIENT_ID / CLIENT_SECRET / REDIRECT_URI を確認してください。")
            return
    
        while True:
            print(MENU)
            choice = prompt("番号を選択")
    
            if choice == "1":
                list_playlists(sp)
            elif choice == "2":
                show_playlist(sp)
            elif choice == "3":
                create_playlist(sp)
            elif choice == "4":
                add_tracks(sp)
            elif choice == "5":
                remove_tracks(sp)
            elif choice == "6":
                reorder_tracks(sp)
            elif choice == "0":
                print("終了します。")
                break
            else:
                print("0〜6 の番号を入力してください。")
    
    
    if __name__ == "__main__":
        main()

    過去記事

    概要

    このプログラムは、SpotifyのAPIを利用してユーザーの指定したプレイリストから楽曲の情報を取得します。ユーザーはGUIを介してプレイリストIDを入力し、そのIDに基づいてプレイリストの全てのトラック情報を表示。

    目的

    Spotifyから特定のプレイリストに含まれる楽曲のタイトルとアーティスト名を取得し、ユーザーに表示すること。これにより、プログラムを通じて簡単に音楽情報を参照できるようにします。

    機能要件

    1. Spotify API 認証:

      • SpotifyのクライアントIDとクライアントシークレットを用いて認証を行います。

    2. ユーザーインターフェース:

      • tkinterを使用したGUIでプレイリストIDの入力を受け付けます。

    3. データ取得:

      • 指定されたプレイリストIDに基づき、Spotifyからプレイリストの情報を取得します。

    4. 情報表示:

      • 取得した楽曲のタイトルとアーティスト名をコンソールに表示します。

    技術仕様

    • 言語: Python

    • 主要ライブラリ:

      • spotipy(Spotify Web APIのラッパー)

      • tkinter(GUIダイアログボックスの表示)

    使用方法

    1. プログラムを起動します。

    2. GUIダイアログボックスにプレイリストIDを入力します。

    3. プログラムがSpotifyからプレイリストの情報を取得し、楽曲のタイトルとアーティスト名をコンソールに表示します。

    import spotipy
    from spotipy.oauth2 import SpotifyClientCredentials
    import tkinter as tk
    from tkinter import simpledialog
    
    # Spotify APIのクライアントIDとクライアントシークレットを設定
    client_id = 'クライアントID'  # クライアントIDをここに入力
    client_secret = 'クライアントシークレット'  # クライアントシークレットをここに入力
    
    # Spotifyクライアント認証の設定
    credentials = SpotifyClientCredentials(client_id, client_secret)
    sp = spotipy.Spotify(client_credentials_manager=credentials)
    
    # ユーザー名を設定
    user = 'USER_NAME'  # ユーザー名をここに入力
    
    # GUIを起動してプレイリストIDを入力
    root = tk.Tk()
    root.withdraw()  # メインウィンドウを隠す
    playlist_id = simpledialog.askstring("Input", "Please enter the playlist ID:", parent=root)
    
    # プレイリストの情報を取得
    playlist_data = sp.user_playlist(user, playlist_id)
    tracks = playlist_data['tracks']
    items = tracks['items']
    
    # タイトルとアーティストを取得する関数を定義
    def getTitleArtist(user, playlist_id):
        playlist = sp.user_playlist(user, playlist_id)
        list_title_artist = []
        for item in playlist['tracks']['items']:
            track = item['track']
            title = track['name']
            artist = track['album']['artists'][0]['name']
            list_title_artist.append(f"{title} / {artist}")
        return list_title_artist
    
    # タイトルとアーティストを取得
    title_artist = getTitleArtist(user, playlist_id)
    
    # タイトルとアーティストを表示
    for idx, entry in enumerate(title_artist):
        print(f"{idx + 1}. {entry}")
    

    #Python #プログラミング #SpotifyAPI #Spotifyプレイリスト #Python自動化

     
     
    データと生成AIで「つくって壊して、また組み直す」実験ログ。 プロンプト・AIアート・分析ツールを公開中。 ツール・記事ネタを持ち帰れる場所です。 呼び名:にーやん/きたさん|PR・ご相談歓迎|Amazonアソシエイト参加中

    あなたへのおすすめ