はじめに
Spotifyの履歴は50曲しか保存されないし、デスクトップアプリからしか確認できません。プレイリストには1万曲まで追加できるようなので、再生した曲を全部プレイリストに自動追加してくれるスクリプトを作ってHerokuで動かしてみました。
Spotipy
でSpotify APIを叩きます。
今聴いている曲を取得
デベロッパーページ
https://developer.spotify.com/dashboard/applications
にアクセスしてCREATE AN APPからAPPを作成し、クライアントIDとクライアントシークレットを入手します。作ったAPPのEDIT SETTINGSから、Redirect URIsにhttps://example.com/callback/
を登録しておきます。
以下のコードのscope
にはbotの権限を指定します。今回は現在再生している曲を読み出したいのでuser-read-currently-playing
とplaylist-modify-public
を指定します。非公開プレイリストに曲を追加していきたい場合は代わりにplaylist-modify-private
を指定します。
import spotipy
from spotipy.oauth2 import SpotifyOAuth
import os
# 環境変数を設定する。代わりにbashrcファイルなどで設定してもOK
os.environ["SPOTIPY_CLIENT_ID"] = "クライアントID"
os.environ["SPOTIPY_CLIENT_SECRET"] = "クライアントシークレット"
os.environ["SPOTIPY_REDIRECT_URI"] = "https://example.com/callback/"
scope = "user-read-currently-playing playlist-modify-public"
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
current_playing = sp.current_user_playing_track()
track_name = current_playing['item']['name']
track_id = current_playing["item"]["id"]
artist_name = current_playing['item']['artists'][0]['name']
print(track_name + "/" + artist_name)
このコードを走らせると、Spotifyの認証ページが開くので同意ボタンを押します。"Example Domain"と書かれたページにリダイレクトされるので、そのページのURLをコピーし、
Enter the URL you were redirected to:
と表示しているコンソールにペーストします。今再生している曲名/アーティスト名が表示されれば成功です。
プレイリストに追加
曲を追加したいプレイリストのIDを調べます。プレイリストのシェア用URLはhttps://open.spotify.com/playlist/37i9dQZF1DX7HOk71GPfSw
のようになっていますが、最後の37i9dQZF1DX7HOk71GPfSw
の部分がプレイリストのIDです。
playlist_id = "プレイリストID"
sp.playlist_add_items(playlist_id, [track_id], position=0)
でtrack_id
の曲をプレイリストの先頭に追加することができます。
聴いた曲をプレイリストに追加
スクリプトをwhile Trueでずっと動かし続けるか、cronなどを使って定期的に動かすのかの2通りの方針があります。ローカル環境で実行するなら前者でも大丈夫ですが、Herokuなどで動かす場合は、24時間に一度スクリプトが停止するので後者のほうが良いでしょう。
方法1(While True)
30秒ごとに現在再生している曲を調べて、前回と異なる場合はプレイリストに追加します。
import spotipy
from spotipy.oauth2 import SpotifyOAuth
import time
import os
os.environ["SPOTIPY_CLIENT_ID"] = "クライアントID"
os.environ["SPOTIPY_CLIENT_SECRET"] = "クライアントシークレット"
os.environ["SPOTIPY_REDIRECT_URI"] = "https://example.com/callback/"
playlist_id = "プレイリストID"
scope = "user-read-currently-playing playlist-modify-public"
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
last_track_id = ""
while True:
try:
current_playing = sp.current_user_playing_track()
if current_playing is not None:
track_name = current_playing['item']['name']
track_id = current_playing["item"]["id"]
artist_name = current_playing['item']['artists'][0]['name']
if last_track_id != track_id:
last_track_id = track_id
print(track_name + "/" + artist_name)
sp.playlist_add_items(playlist_id, [track_id], position=0)
else:
print("no song is playing...")
except Exception as e:
print(e)
time.sleep(30)
方法2(定期的に実行)
直近1時間の再生履歴を調べて、プレイリストに追加します。このスクリプトは1時間ごとに定期実行する必要があります。
import spotipy
from spotipy.oauth2 import SpotifyOAuth
import time
import os
os.environ["SPOTIPY_CLIENT_ID"] = "クライアントID"
os.environ["SPOTIPY_CLIENT_SECRET"] = "クライアントシークレット"
os.environ["SPOTIPY_REDIRECT_URI"] = "https://example.com/callback/"
playlist_id = "プレイリストID"
scope = "user-read-currently-playing playlist-modify-public"
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
now_in_ms = int(time.time()*1000) # ミリ秒単位で現在のUINX時刻を取得
one_hour_in_ms = 60*60*1000 #1時間をミリ秒単位で表現
recently_played = sp.current_user_recently_played(after=now_in_ms-one_hour_in_ms)
if recently_played is not None:
items = recently_played["items"]
track_ids = [item["track"]["id"] for item in items]
sp.playlist_add_items(playlist_id, track_ids, position=0)
print("added:", track_ids)
Herokuで動かす
ローカル環境で実行してもいいですが、24時間動かすならサーバーから実行したほうが便利なので、Herokuの無料枠で動かしてみます。Herokuへのデプロイについては
を参考にしました。requirement.txt
は
pip3 freeze | grep spotipy > requirements.txt
で作成します。また、
python3 -V
で自分のPythonのバージョンを確認すると3.6.9
だったので、Herokuが対応しているPythonのバージョンを
で調べ、同系列の3.6.13
をruntime.txt
に指定しました。
python-3.6.13
認証の問題
スクリプトを実行してみると、コンソールに
Enter the URL you were redirected to:
が表示され、認証が必要なようです。しかしながら、Herokuからブラウザを開くことができないので、URLを手に入れることができず、困ってしまいました。
調べてみると、GithubのIssue
に解決策がありました。
まずローカルの環境で一度スクリプトを実行し、認証を済ませておきます。すると
{"access_token": "xxxxxxxx", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "xxxxxxxx", "scope": "playlist-modify-public user-read-recently-played", "expires_at": 1615812124}
のような中身のキャシュファイルが.cashe
にできています。中身を適当なファイルにコピペし、Herokuにpushしておきます。
そして、スクリプトの
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
を
cache_handler = spotipy.cache_handler.CacheFileHandler(cache_path="キャッシュファイルへのパス")
auth_manager = SpotifyOAuth(scope=scope, cache_handler=cache_handler,show_dialog=True)
sp = spotipy.Spotify(auth_manager=auth_manager)
に書き換えると、認証しなくても動くようになりました。
Heroku Schedulerのズレ問題
「方法2」のスクリプトをHeroku Schedulerで1時間ごとに実行してみました。しかし、Heroku Schedulerの実行時間に1~2分のズレがあり、その間の履歴を取り逃してしまうという問題が生じました。そこで、毎時10分にスケジュールしておき、例えばx時11分に実行されたならx-1時0分0秒からx時0分0秒までの履歴を取得することで解決しました。
以下のスクリプトを毎時10分に自動実行しています。
# 毎時10分に実行
import spotipy
from spotipy.oauth2 import SpotifyOAuth
import time, os
from datetime import datetime
os.environ["SPOTIPY_CLIENT_ID"] = "クライアントID"
os.environ["SPOTIPY_CLIENT_SECRET"] = "クライアントシークレット"
os.environ["SPOTIPY_REDIRECT_URI"] = "https://example.com/callback/"
playlist_id = "プレイリストID"
scope = "user-read-recently-played playlist-modify-public"
cache_handler = spotipy.cache_handler.CacheFileHandler(cache_path="cash_data.txt")
auth_manager = SpotifyOAuth(scope=scope, cache_handler=cache_handler,show_dialog=True)
sp = spotipy.Spotify(auth_manager=auth_manager)
now = datetime.now() # x時10分
oclock = datetime(now.year, now.month, now.day, now.hour,0) # x時0分
one_hour_in_ms = 60*60*1000 #1時間(ミリ秒単位)
before = int(oclock.timestamp()*1000) # x時0分のunix時(ミリ秒単位)
after = before - one_hour_in_ms # x-1時0分のunix時(ミリ秒単位)
# beforeとafterは同時に指定できない(泣)
recently_played = sp.current_user_recently_played(after=after)
# 日時の文字列をミリ秒単位のunix時に変換する関数
def parser(strtime):return int(datetime.strptime(strtime, \
"%Y-%m-%dT%H:%M:%S.%fZ").timestamp()*1000)
if recently_played is not None:
items = recently_played["items"]
track_ids = [item["track"]["id"] for item in items \
if parser(item["played_at"]) < before]
sp.playlist_add_items(playlist_id, track_ids, position=0)
#参考にした記事