駆け出しエンジニアの作業ノート

駆け出しエンジニアが作業ノート風にまとめるページ(関係無い事もしばしば)

「tkinter」を使ってみる

早速、以下のようなものを書いてみました。

 

import tkinter as tk


def clicked():
print("clicked")

def setting():
root.title("初期設定")
root.geometry()
button1_0 = tk.Button(root, text="Spotifyアカウント連携")
button1_0.pack()

def menu(root):

root.title("メインメニュー")
root.geometry()

button0 = tk.Button(root, text="プレイリスト生成", command=clicked)
button1 = tk.Button(root, text="初期設定", command=setting)

button0.pack()
button1.pack()

root.mainloop()

if __name__ == '__main__':
root = tk.Tk()
menu(root)

 

 

実行するとこのようなものが出てきました。

 

f:id:Psyduck_take_it_easy:20180514225357p:plain

 

「初期設定」にはボタン遷移を設定したのでクリックすると以下のようになりました。

 

f:id:Psyduck_take_it_easy:20180514225540p:plain

 

上の2つのボタンは画面から消えることを想定しましたが上手く出来ませんでした。

 

もう少し、色々と調べて見ようと思います。

 

逆引きPython標準ライブラリ 目的別の基本レシピ180+! (impress top gear)

逆引きPython標準ライブラリ 目的別の基本レシピ180+! (impress top gear)

 

 

社内LTで発表してきました

昨日、継続して製作してきたプレイリスト作成モジュールについて発表してきました。

 

社内でも好評を得て自分としては発表して良かったと思っています。

 

ストリーミング配信サービスが乱立している中で、最適なサービスを選べていないヒトは一定数いるのかなと感じました。よって、統合されたロジックを用いて曲を推薦して各サービスへの橋渡しをするアプリを目指して、まずはGUIで動くようにしてみようと考えています。

 

まずは、「Tkinter」あたりで頑張ってみたいと思います。

SpotifyとGoogleの両方でプレイリストを自動生成する

間隔が空いてしまいましたが、プレイリスト自動生成ツールについては相変わらず開発を続けています。

 

psyduck-take-it-easy.hatenablog.com

psyduck-take-it-easy.hatenablog.com

psyduck-take-it-easy.hatenablog.com

 

今回の更新で大きなトピックとしては、Google Musicにも対応した事です。Google ではGCPで各種APIを提供していますが、Google Musicに関しては特に提供されていません。今回は、非公式ながら提供されているAPIを用いました。

 

gmusicapi: an unofficial API for Google Play Music — gmusicapi 11.0.1 documentation

 

自分の感じとして、GoogleにはあるけどSpotifyには無いという楽曲にはいくつか出会っていますが、その逆が無かったので、今回はSpotifyでプレイリスト生成を行い、そこから漏れた楽曲でプレイリスト生成を行うという形で実装を行いました。

 

それ以外のトピックとしては、過去24時間以内に聴いた楽曲をプレイリストから外す実装を行いました。ただ、Last.fmの楽曲データと各媒体で提供されている楽曲表示が微妙に異なる事例があり、その場合にはLast.fm側の取得時に調整する方法で一つ一つ対応しています。

 

また、SpotifyAPIでアーティスト名が英語表記で返ってくる問題に関しては、Watsonの翻訳APIを通すことによって解消しました。

 

では、実際のコードです。

 

# main.py
import
spotipy
import re
import connect_lastfm_api
import create_playlist_to_Spotify_etc
from spotify_token import Spotify_token
from gmusicapi import Mobileclient

api_key = {YOUR_LASTFM_API_KEY}
username = {YOUR_SPOTIFY_USER_NAME}

def input_artist():
artist = input("検索したいアーティスト名を入力して下さい")
page = 1
search_song_list(artist, page)

def search_song_list(artist, page):
list = connect_lastfm_api.get_search_song_list(artist, api_key, page)
for i in range(len(list)):
song_list = list[i]
song = song_list["name"]
print(song)
yes_or_no = input("この中に探したい曲はありましたか?(y/n)")
if yes_or_no == "y":
search_track(artist)
elif yes_or_no == "n":
page = page + 1
search_song_list(artist, page)
else:
print("正しい入力ではありません")

def search_track(artist):
song = input("曲名を入力して下さい")
create_playlist(artist, song)

def create_playlist(artist, song):
playlist = []
first_input = (artist, song)
playlist.append(first_input)
count = 0
recent_track_list = connect_lastfm_api.recent_play_song(api_key)
genarate_playlist(artist, song, playlist, first_input, count, recent_track_list)

def genarate_playlist(artist, song, playlist, first_input, count, recent_track_list):
similar_track_list = connect_lastfm_api.get_similar_track(artist, song, api_key)
print(similar_track_list)
print(recent_track_list)
playlist_limit = 15
if len(similar_track_list) == 0:
track = (artist, song)
if track in playlist:
pass
else:
playlist.append(track)
if len(playlist) >= playlist_limit:
print(playlist)
get_empty_Spotify_playlist(first_input, playlist)
else:
page = 1
try:
list = connect_lastfm_api.get_search_song_list(artist, api_key, page)
song_list = list[count]
song = song_list["name"]
count = count + 1
genarate_playlist(artist, song, playlist, first_input, count, recent_track_list)
except KeyError:
artist = connect_lastfm_api.similar_artist_search(artist, api_key)
count = 0
genarate_playlist(artist, song, playlist, first_input, count, recent_track_list)
except IndexError:
artist = connect_lastfm_api.similar_artist_search(artist, api_key)
count = 0
genarate_playlist(artist, song, playlist, first_input, count, recent_track_list)
else:
for i in range(len(similar_track_list)):
track = similar_track_list[i]
if len(playlist) >= playlist_limit:
break
elif track in playlist:
continue
elif track in recent_track_list:
continue
else:
playlist.append(track)
if len(playlist) < playlist_limit:
search_data = playlist[len(playlist)-1]
artist = search_data[0]
song = search_data[1]
count = count + 1
genarate_playlist(artist, song, playlist, first_input, count, recent_track_list)
else:
print(playlist)
get_empty_Spotify_playlist(first_input, playlist)

def get_empty_Spotify_playlist(first_input, playlist):
playlist_name = first_input[0] + "の" + first_input[1] + "から始まるおすすめプレイリスト"
ST = Spotify_token(username)
token = ST.set()
sp = spotipy.Spotify(auth=token)
sp.trace = False
playlistts = sp.user_playlist_create(username, playlist_name)
playlist_id = playlistts["id"]
song_ids = []
not_append_list = []
for i in range(len(playlist)):
search_str = playlist[i]
s_artist = search_str[0]
s_song = search_str[1]
search_str = s_artist + " " + s_song
result = sp.search(search_str, limit=1)
tracks = result["tracks"]
items = tracks["items"]
similar_artist_list = connect_lastfm_api.get_similar_artist_list(s_artist, api_key)
if items == []:
not_append_song = (s_artist, s_song)
not_append_list.append(not_append_song)
continue
api_artist = items[0]["album"]["artists"][0]["name"]
print(api_artist)
api_song = items[0]["name"]
api_song = re.sub("\t", "", api_song)
song_id = items[0]["id"]
print(api_song)
search_data = (s_artist, s_song)
api_data = (api_artist, api_song)
result = create_playlist_to_Spotify_etc.create_playlist(search_data, api_data, similar_artist_list)
if result == "add":
song_ids.append(song_id)
else:
not_append_song = (s_artist, s_song)
not_append_list.append(not_append_song)
Spotify_playlist_input(playlist_id, song_ids, not_append_list)

def Spotify_playlist_input(playlist_id, song_ids, not_append_list):
ST = Spotify_token(username)
token = ST.set()
if token:
sp = spotipy.Spotify(auth=token)
sp.trace = False
results = sp.user_playlist_add_tracks(username, playlist_id, song_ids)
print(results)
if len(not_append_list) != 0:
print("以下のリストの曲はSpotifyで配信されていないので、Google Play Musicで作成します")
print(not_append_list)
get_empty_google_music_playlist(not_append_list)
else:
print("Can't get token for", username)

def get_empty_google_music_playlist(not_append_list):
gmail = {YOUR_GOOGLE_ACCOUNT}
password = {YOUR_GOOGLE_PASSWORD}
android_id = Mobileclient.FROM_MAC_ADDRESS
name = "Spotifyから漏れた楽曲リスト"
api = Mobileclient()
logged_in = api.login(email=gmail, password=password, android_id=android_id, locale="ja")
playlist_id = api.create_playlist(name=name, description=None, public=False)
search_google(not_append_list, playlist_id, api)

def search_google(playlist, playlist_id, api):
song_ids = []
not_append_list = []
for i in range(len(playlist)):
track_data = playlist[i]
s_artist = track_data[0]
s_song = track_data[1]
search_str = s_artist + " " + s_song
search_data = api.search(query=search_str, max_results=None)
song_result = search_data["song_hits"]
try:
api_artist = song_result[0]["track"]["artist"]
api_song = song_result[0]["track"]["title"]
song_id = song_result[0]["track"]["storeId"]
print(api_artist)
print(api_song)
similar_artist_list = connect_lastfm_api.get_similar_artist_list(s_artist, api_key)
search_data = (s_artist, s_song)
api_data = (api_artist, api_song)
result = create_playlist_to_Spotify_etc.create_playlist(search_data, api_data, similar_artist_list)
if result == "add":
song_ids.append(song_id)
else:
not_append_song = (s_artist, s_song)
not_append_list.append(not_append_song)
except IndexError:
print(song_result)
not_append_song = (s_artist, s_song)
not_append_list.append(not_append_song)
google_playlist_input(playlist_id, song_ids, not_append_list, api)

def google_playlist_input(playlist_id, song_ids, not_append_list, api):
results = api.add_songs_to_playlist(playlist_id=playlist_id, song_ids=song_ids)
print(results)
if len(not_append_list) != 0:
print("以下のリストの曲はSpotify及びGoogle Play Musicで配信されていません")
print(not_append_list)


if __name__ == '__main__':
input_artist()

  

 

# connect_lastfm_api.py
import
urllib.request
import urllib.parse
import json
import time

def get_search_song_list(artist, api_key, page):
artist = urllib.parse.quote(artist)
artist_api1 = "http://ws.audioscrobbler.com/2.0/?method=artist.gettoptracks&artist="
artist_api2 = "&autocorrect=1&page="
page = str(page)
artist_api3 = "&api_key="
artist_api4 = "&format=json"
call_api = artist_api1 + artist + artist_api2 + page + artist_api3 + api_key + artist_api4
print(call_api)
address_json = urllib.request.urlopen(call_api)
data = json.loads(address_json.read())
track = data["toptracks"]
list = track["track"]
return list

def get_similar_track(artist, song, api_key):
artist = urllib.parse.quote(artist)
song = urllib.parse.quote(song)
track_api1 = "http://ws.audioscrobbler.com/2.0/?method=track.getsimilar&artist="
track_api2 = "&track="
track_api3 = "&autocorrect=1&api_key="
track_api4 = "&format=json"
call_api = track_api1 + artist + track_api2 + song + track_api3 + api_key + track_api4
print(call_api)
address_json = urllib.request.urlopen(call_api)
data = json.loads(address_json.read())
similartracks = data["similartracks"]
track = similartracks["track"]
similar_track_list = []
for i in range(len(track)):
list = track[i]
match = list["match"]
if match < 0.1:
continue
else:
song = list["name"]
similar_song_artist = list["artist"]["name"]
similar_track = (similar_song_artist, song)
similar_track_list.append(similar_track)
return similar_track_list

def similar_artist_search(artist, api_key):
artist = urllib.parse.quote(artist)
artist_api1 = "http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&artist="
artist_api2 = "&autocorrect=1&api_key="
artist_api3 = "&format=json"
call_api = artist_api1 + artist + artist_api2 + api_key + artist_api3
print(call_api)
address_json = urllib.request.urlopen(call_api)
data = json.loads(address_json.read())
similarartists = data["similarartists"]
artist_list = similarartists["artist"]
list = artist_list[0]
most_similar_artist = list["name"]
return most_similar_artist

def recent_play_song(api_key):
now = time.time()
recent = now - 86400
recent = str(recent)
recent_api1 = "http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user="
user_name = {YOUR_LASTFM_USER_NAME}
recent_api2 = "&from="
recent_api3 = "&api_key="
recent_api4 = "&format=json"
call_api = recent_api1 + user_name + recent_api2 + recent + recent_api3 + api_key + recent_api4
print(call_api)
address_json = urllib.request.urlopen(call_api)
data = json.loads(address_json.read())
recenttracks = data["recenttracks"]
track = recenttracks["track"]
recent_track_list = []
for i in range(len(track)):
list = track[i]
artist = list["artist"]["#text"]
song = list["name"]
recent_track = (artist, song)
if recent_track in recent_track_list:
continue
else:
recent_track_list.append(recent_track)
return recent_track_list

def get_loved_track(api_key):
loved_api1 = "http://ws.audioscrobbler.com/2.0/?method=user.getlovedtracks&user="
user_name = {YOUR_LASTFM_USER_NAME}
loved_api2 = "&api_key="
loved_api3 = "&format=json"
call_api = loved_api1 + user_name + loved_api2 + api_key + loved_api3
address_json = urllib.request.urlopen(call_api)
data = json.loads(address_json.read())
lovedtracks = data["lovedtracks"]
attr = lovedtracks["@attr"]
limit = attr["total"]
limit_api = "&limit="
call_api = loved_api1 + user_name + limit_api + limit + loved_api2 + api_key + loved_api3
print(call_api)
address_json = urllib.request.urlopen(call_api)
data = json.loads(address_json.read())
lovedtracks = data["lovedtracks"]
track = lovedtracks["track"]
loved_list = []
for i in range(len(track)):
list = track[i]
try:
song = list["name"]
artist = list["artist"]["name"]
loved_data = (song, artist)
loved_list.append(loved_data)
except KeyError:
continue
return loved_list

def get_similar_artist_list(artist,api_key):
artist = urllib.parse.quote(artist)
artist_api1 = "http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&artist="
artist_api2 = "&autocorrect=1&api_key="
artist_api3 = "&format=json"
call_api = artist_api1 + artist + artist_api2 + api_key + artist_api3
print(call_api)
address_json = urllib.request.urlopen(call_api)
data = json.loads(address_json.read())
try:
similarartists = data["similarartists"]
artist_list = similarartists["artist"]
similar_artist_list = []
for i in range(len(artist_list)):
list = artist_list[i]
similar_artist = list["name"]
match = list["match"]
match = float(match)
if match < 0.1:
continue
else:
similar_artist_list.append(similar_artist)
return similar_artist_list
except KeyError:
similar_artist_list = []
return similar_artist_list

 

# create_playlist_to_Spotify_etc.py
import
lev_distance
import connect_watson_translate


def create_playlist(search_data, api_data, similar_artist_list):
s_artist = search_data[0]
s_song = search_data[1]
api_artist = api_data[0]
api_song = api_data[1]
if api_artist == s_artist:
print("アーティスト名完全一致")
print("OK")
result = "add"
# 類似アーティストの中に含まれていた場合
elif api_artist in similar_artist_list:
# 曲名完全一致の場合は拾う
if api_song == s_song:
print("曲名完全一致")
print("OK")
result = "add"
else:
song_word_distance = lev_distance.calc_distance(api_song, s_song)
if song_word_distance < 6:
print("曲名類似")
print("OK")
result = "add"
else:
result = "not"
else:
api_artist = connect_watson_translate.translate_word(api_artist)
artist_word_distance = lev_distance.calc_distance(api_artist, s_artist)
song_word_distance = lev_distance.calc_distance(api_song, s_song)
if artist_word_distance < 9 and song_word_distance < 9:
print("アーティスト名類似")
print("OK")
result = "add"
elif api_artist in similar_artist_list:
print("アーティスト名類似")
print("OK")
result = "add"
else:
result = "not"
return result

 

# spotify_token.py
import
spotipy.util as util

class Spotify_token:
def __init__(self, username):
self.username = username
self.scope = 'playlist-modify-public'

def set(self):
token = util.prompt_for_user_token(self.username, self.scope)
return token

  

 

# lev_distance.py
def
calc_distance(a, b):
if a == b: return 0
a_len = len(a)
b_len = len(b)
if a == "":
return b_len
if b == "":
return a_len
matrix = [[] for i in range(a_len + 1)]
for i in range(a_len + 1):
matrix[i] = [0 for j in range(b_len+1)]
for i in range(a_len + 1):
matrix[i][0] = i
for j in range(b_len + 1):
matrix[0][j] = j
for i in range(1, a_len + 1):
ac = a[i-1]
for j in range(1, b_len + 1):
bc = b[j-1]
cost = 0 if (ac == bc) else 1
matrix[i][j] = min([matrix[i-1][j]+1, matrix[i][j-1]+1,matrix[i-1][j-1] + cost])
return matrix[a_len][b_len]

 

# connect_watson_translate.py
from
watson_developer_cloud import LanguageTranslatorV2 as LanguageTranslator

def translate_word(text):
langage_translator = LanguageTranslator(username=
{YOUR_USER_NAME}, password= {YOUR_PASSWORD})
text_list = []
text_list.append(text)
translation = langage_translator.translate(
text=text_list, model_id="en-ja")
translations = translation[
"translations"]
word_list = translations[
0]
trans_word = word_list[
"translation"]
return trans_word

 

レーベンシュタイン距離計算のコードはこの本を参考にしました。

 

 

 Spotifyトークン取得は以下を参考にしています。

 

sakanaaas.hateblo.jp

 

では、前回と同じように乃木坂46シンクロニシティを入れてみます。

まず、生成された大元のプレイリストがこちらです。

 

[('乃木坂46', 'シンクロニシティ'), ('乃木坂46', '君の名は希望'), ('乃木坂46', '命は美しい'), ('E-Girls', 'ごめんなさいのKissing You'), ('E-Girls', 'Mr.Snowman'), ('HKT48', '桜、みんなで食べた'), ('HKT48', '12秒'), ('AKB48', 'Green Flash'), ('高橋みなみ', 'Jane Doe'), ('AKB48', '希望的リフレイン'), ('Not yet', '西瓜BABY'), ('前田敦子', 'タイムマシンなんていらない'), ('SKE48', 'オキドキ'), ('指原莉乃', '初恋ヒルズ'), ('渡辺美優紀', 'わるきー')]

 

Spotifyのプレイリストです。

 

f:id:Psyduck_take_it_easy:20180506181335p:plain

 

結果、以下の曲がSpotifyプレイリストから漏れてしまいました。

 

[('HKT48', '桜、みんなで食べた'), ('HKT48', '12秒'), ('AKB48', 'Green Flash'), ('Not yet', '西瓜BABY'), ('渡辺美優紀', 'わるきー')] 

 

漏れた楽曲よりGoogleのプレイリスト生成を行います。

 

f:id:Psyduck_take_it_easy:20180506181426p:plain

 

結果、最後に以下の曲が残ってしまいました。

 

[('AKB48', 'Green Flash'), ('渡辺美優紀', 'わるきー')] 

 

このうち、「わるきー」に関しては配信されているものの、楽曲表記が異なるため上手く拾えませんでした。「Green Flash」に関しては配信されていないようです。詳細は不明です。

 

今後についてはサーバー等で本来の再生順に即して、外部から再生制御が行えないか研究してみたいと思います。

日本語表記とローマ字表記の変換が難しい

SpotifyAPIを叩いて曲を取得していますが、返ってくるレスポンスにクセがありました。アーティスト名は英語で返ってきており、曲名は日本語で返ってきているのです。

 

問答無用で最初に上がってきた曲を入れれば良いという案もありますが、これでは、配信していないアーティストの場合オルゴール曲が入ってしまうので、適切ではありません。

 

import spotipy
import re
import connect_lastfm_api
import connect_sandbox
import lev_distance
from spotify_token import Spotify_token

api_key =
username =

def input_artist():
#artist = input("検索したいアーティスト名を入力して下さい")
artist = "乃木坂46"
page = 1
search_song_list(artist, page)

def search_song_list(artist, page):
list = connect_lastfm_api.get_search_song_list(artist, api_key, page)
for i in range(len(list)):
song_list = list[i]
song = song_list["name"]
print(song)
#yes_or_no = input("この中に探したい曲はありましたか?(y/n)")
yes_or_no = "y"
if yes_or_no == "y":
search_track(artist)
elif yes_or_no == "n":
page = page + 1
search_song_list(artist, page)
else:
print("正しい入力ではありません")

def search_track(artist):
#song = input("曲名を入力して下さい")
song = "制服のマネキン"
create_playlist(artist, song)

def create_playlist(artist, song):
playlist = []
first_input = (artist, song)
playlist.append(first_input)
count = 0
genarate_playlist(artist, song, playlist, first_input, count)

def genarate_playlist(artist, song, playlist, first_input, count):
similar_track_list = connect_lastfm_api.get_similar_track(artist, song, api_key)
print(similar_track_list)
playlist_limit = 15
if len(similar_track_list) == 0:
track = (artist, song)
if track in playlist:
pass
else:
playlist.append(track)
if len(playlist) >= playlist_limit:
print(playlist)
get_empty_Spotify_playlist(first_input, playlist)
else:
page = 1
try:
list = connect_lastfm_api.get_search_song_list(artist, api_key, page)
song_list = list[count]
song = song_list["name"]
count = count + 1
genarate_playlist(artist, song, playlist, first_input, count)
except KeyError:
artist = connect_lastfm_api.similar_artist_search(artist, api_key)
count = 0
genarate_playlist(artist, song, playlist, first_input, count)
except IndexError:
artist = connect_lastfm_api.similar_artist_search(artist, api_key)
count = 0
genarate_playlist(artist, song, playlist, first_input, count)
else:
recent_track_list = connect_lastfm_api.recent_play_song(api_key)
print(recent_track_list)
for i in range(len(similar_track_list)):
track = similar_track_list[i]
if len(playlist) >= playlist_limit:
break
elif track in playlist:
continue
elif track in recent_track_list:
continue
else:
playlist.append(track)
if len(playlist) < playlist_limit:
search_data = playlist[len(playlist)-1]
artist = search_data[0]
song = search_data[1]
count = count + 1
genarate_playlist(artist, song, playlist, first_input, count)
else:
print(playlist)
get_empty_Spotify_playlist(first_input, playlist)

def get_empty_Spotify_playlist(first_input, playlist):
playlist_name = first_input[0] + "の" + first_input[1] + "から始まるおすすめプレイリスト"
ST = Spotify_token(username)
token = ST.set()
sp = spotipy.Spotify(auth=token)
sp.trace = False
playlistts = sp.user_playlist_create(username, playlist_name)
playlist_id = playlistts["id"]
song_ids = []
not_append_list = []
for i in range(len(playlist)):
search_str = playlist[i]
s_artist = search_str[0]
s_song = search_str[1]
search_str = s_artist + " " + s_song
result = sp.search(search_str, limit=1)
similar_artist_list = connect_sandbox.get_similar_artist_list(s_artist, api_key)
for item in result["tracks"]["items"]:
print(item)
artist_url = item["artists"][0]
artist_url = artist_url["external_urls"]["spotify"]
print(artist_url)
api_artist = item["artists"][0]["name"]
print(api_artist)
api_song = item["name"]
api_song = re.sub("\t", "", api_song)
print(api_song)
if api_artist == s_artist:
print("OK")
song_id = item["id"]
song_ids.append(song_id)
elif api_artist in similar_artist_list:
if api_song == s_song:
print("OK")
song_id = item["id"]
song_ids.append(song_id)
else:
not_append_song = (s_artist, s_song)
not_append_list.append(not_append_song)
else:
word_distance = lev_distance.calc_distance(api_artist, s_artist)
if word_distance > 9:
not_append_song = (s_artist, s_song)
not_append_list.append(not_append_song)
else:
print("OK")
song_id = item["id"]
song_ids.append(song_id)
playlist_input(playlist_id, song_ids, not_append_list)

def playlist_input(playlist_id, song_ids, not_append_list):
ST = Spotify_token(username)
token = ST.set()
if token:
sp = spotipy.Spotify(auth=token)
sp.trace = False
results = sp.user_playlist_add_tracks(username, playlist_id, song_ids)
print(results)
if len(not_append_list) != 0:
print("以下のリストの曲はSpotifyで配信されていないか、入力されたキーワードが違う可能性があります。")
print(not_append_list)
else:
print("Can't get token for", username)

if __name__ == '__main__':
input_artist()

 

SpotifyAPIから返ってくるレスポンスのアーティスト名と、実際に調べたいアーティスト名のレーベンシュタイン距離を測り、そのスコアで分類しようと考えました。

 

実際のレーベンシュタイン距離の計算は以下の本を参考にしました。

 

 

しかし、結果として特に個人名のローマ字から漢字表記の距離が長く、短い距離でオルゴール曲が混ざってしまう事がわかり、残念ながら使えない事が判明しました。

 

一つ一つ、DBに登録して変換させるように対応しないといけないかもしれないですね… 

 

「Speee もくもく会 #39」に行って作業してきました

4月28日に「Speee もくもく会 #39」に行って先頃から作成しているSpotifyのプレイリスト自動生成モジュールの作業をしてきました。

 

speee.connpass.com

 

以前より気になっていたイベントですが、初参加枠がすぐ埋まってしまうのでなかなか参加出来ずにいました。今回初めて参加する事が出来ました。会場はその名の通り、Speeeさんのオフィスでした。オフィスには多くの本が並んでおり、勉強熱心な方には良いオフィスだと思いました。

 

 

また折りをみて参加したいと思います。

仕様変更とリファクタリングをしました

仕様を大きく変更しました。前のコードではlast.fmAPIのデータを収録したDBを経由していましたが、メンテナンスコストが負担になっていたので、DBを経由せずにAPIを直接参照するように切り替えました。

 

また、API参照の部分をべた書きすると、今度は本体部分のメンテナンスがし辛くなってしまったので、API参照の部分は別ファイルを参照する方式にしました。

 

では、まずは本体部分です。

import spotipy
import re
import get_similar_track_sandbox
from spotify_token import Spotify_token

api_key =
username =

def input_artist():
artist = input("検索したいアーティスト名を入力して下さい")
page = 1
search_song_list(artist, page)

def search_song_list(artist, page):
list = get_similar_track_sandbox.get_search_song_list(artist, api_key, page)
for i in range(len(list)):
song_list = list[i]
song = song_list["name"]
print(song)
yes_or_no = input("この中に探したい曲はありましたか?(y/n)")
#yes_or_no = "y"
if yes_or_no == "y":
search_track(artist)
elif yes_or_no == "n":
page = page + 1
search_song_list(artist, page)
else:
print("正しい入力ではありません")

def search_track(artist):
song = input("曲名を入力して下さい")
create_playlist(artist, song)

def create_playlist(artist, song):
playlist = []
first_input = (artist, song)
playlist.append(first_input)
k = 0
genarate_playlist(first_input, artist, song, playlist, k)

def genarate_playlist(first_input, artist, song, playlist, k):
track = get_similar_track_sandbox.get_similar_track(artist, song, api_key)
similar_artist_list = get_similar_track_sandbox.similar_artist_search(artist, api_key)
playlist_limit = 15
for i in range(len(similar_artist_list)):
similar_artist = similar_artist_list[i]
for j in range(len(track)):
if len(playlist) >= playlist_limit:
break
else:
song_list = track[j]
similar_song = song_list["name"]
similar_song_artist = song_list["artist"]["name"]
match = song_list["match"]
match = float(match)
result = (similar_song_artist, similar_song)
if match < 0.1:
continue
elif result in playlist:
continue
elif similar_song_artist == similar_artist:
playlist.append(result)
else:
continue
try:
if len(playlist) < playlist_limit -1:
print(playlist)
k = k + 1
search_word = playlist[k]
artist = search_word[0]
song = search_word[1]
genarate_playlist(first_input, artist, song, playlist, k)
else:
print(playlist)
get_empty_Spotify_playlist(first_input, playlist)
except IndexError:
print(playlist)
page = 1
list = get_similar_track_sandbox.get_search_song_list(artist, api_key, page)
print(list)
song_list = list[0]
song = song_list["name"]
k = 1
search_word = (artist, song)
playlist.append(search_word)
genarate_playlist(first_input, artist, song, playlist, k)

def get_empty_Spotify_playlist(first_input, playlist):
playlist_name = first_input[0] + "の" + first_input[1] + "から始まるおすすめプレイリスト"
ST = Spotify_token(username)
token = ST.set()
sp = spotipy.Spotify(auth=token)
sp.trace = False
playlistts = sp.user_playlist_create(username, playlist_name)
playlist_id = playlistts["id"]
song_ids = []
for i in range(len(playlist)):
search_str = playlist[i]
s_artist = search_str[0]
s_song = search_str[1]
search_str = s_artist + " " + s_song
result = sp.search(search_str, limit=1)
for item in result["tracks"]["items"]:
print(item["artists"][0]["name"])
print(item["name"])
print(type(item["name"]))
print(item["id"])
api_song = item["name"]
api_song = re.sub("\t", "", api_song)
if api_song == s_song:
print("OK")
song_id = item["id"]
song_ids.append(song_id)
else:
continue
playlist_input(playlist_id, song_ids)

def playlist_input(playlist_id, song_ids):
ST = Spotify_token(username)
token = ST.set()
if token:
sp = spotipy.Spotify(auth=token)
sp.trace = False
results = sp.user_playlist_add_tracks(username, playlist_id, song_ids)
print(results)
else:
print("Can't get token for", username)

if __name__ == '__main__':
input_artist()

 

次に、API参照部分です。

 

import urllib.request
import urllib.parse
import json

def get_search_song_list(artist, api_key, page):
artist = urllib.parse.quote(artist)
artist_api1 = "http://ws.audioscrobbler.com/2.0/?method=artist.gettoptracks&artist="
artist_api2 = "&autocorrect=1&page="
page = str(page)
artist_api3 = "&api_key="
artist_api4 = "&format=json"
call_api = artist_api1 + artist + artist_api2 + page + artist_api3 + api_key + artist_api4
print(call_api)
address_json = urllib.request.urlopen(call_api)
data = json.loads(address_json.read())
track = data["toptracks"]
list = track["track"]
return list

def get_similar_track(artist, song, api_key):
artist = urllib.parse.quote(artist)
song = urllib.parse.quote(song)
track_api1 = "http://ws.audioscrobbler.com/2.0/?method=track.getsimilar&artist="
track_api2 = "&track="
track_api3 = "&autocorrect=1&api_key="
track_api4 = "&format=json"
call_api = track_api1 + artist + track_api2 + song + track_api3 + api_key + track_api4
print(call_api)
address_json = urllib.request.urlopen(call_api)
data = json.loads(address_json.read())
similartracks = data["similartracks"]
track = similartracks["track"]
return track

def similar_artist_search(artist, api_key):
artist = urllib.parse.quote(artist)
artist_api1 = "http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&artist="
artist_api2 = "&autocorrect=1&api_key="
artist_api3 = "&format=json"
call_api = artist_api1 + artist + artist_api2 + api_key + artist_api3
print(call_api)
address_json = urllib.request.urlopen(call_api)
data = json.loads(address_json.read())
similarartists = data["similarartists"]
artist_list = similarartists["artist"]
similar_artist_list = []
artist = urllib.parse.unquote(artist)
similar_artist_list.append(artist)
for i in range(len(artist_list)):
list = artist_list[i]
similar_artist = list["name"]
match = list["match"]
match = float(match)
if match > 0.1:
similar_artist_list.append(similar_artist)
else:
break
return similar_artist_list

 

では、ここに乃木坂46の新曲「シンクロニシティ」を入れてみます。

 

シンクロニシティ

シンクロニシティ

  • provided courtesy of iTunes

 

システムに基づくプレイリストは以下の通りです。

 

[('乃木坂46', 'シンクロニシティ'), ('乃木坂46', '制服のマネキン'), ('乃木坂46', '君の名は希望'), ('乃木坂46', '命は美しい'), ('HKT48', '桜、みんなで食べた'), ('HKT48', '12秒'), ('E-Girls', 'ごめんなさいのKissing You'), ('E-Girls', 'Mr.Snowman'), ('山本彩', '夢のdead body'), ('山本彩', 'ジャングルジム'), ('高橋みなみ', 'Jane Doe'), ('高橋みなみ', '右肩'), ('E-Girls', 'E.G. Anthem -WE ARE VENUS-'), ('AKB48', '希望的リフレイン'), ('AKB48', 'overture')] 

 

では、実際に出来たSpotifyプレイリストを見てみましょう。

 

f:id:Psyduck_take_it_easy:20180426235913p:plain

 

HKT48山本彩さんの楽曲が入っていません。これは、Spotifyでは配信されていない為です。E-girlsの「E.G. Anthem -WE ARE VENUS-」が入っていませんが、Spotifyでの表記が若干異なることから入らなかったようです。ちなみに、Google Play Musicではリスト曲全て配信されています。

 

自分としては、Google Play MusicAPIが出ることを切に願っています。

 

今後は24時間以内に再生した楽曲を除去する機能を搭載し、プレイリストの自動生成を何度も用いても同じ曲が再生されないような仕組み作りを行います。

Last.fmのデータを使って、Spotifyのプレイリストを作成するモジュールが出来た

Last.fmという音楽系のAPIがあります。オライリーの本で「Audioscrobbler」として紹介されているものです。このデータを用いてSpotifyのプレイリストを作成するモジュールが完成しました。

 

import psycopg2
import psycopg2.extras
import spotipy
import spotipy.util as util
import pprint
import re
from spotify_token import Spotify_token

host_name =
port_number =
dbname =
rolename =
passwd =

username =
# API式に切り替える時にアーティスト名から検索可能な曲名の一覧を出す

def get_artist_similar():
conn = psycopg2.connect(database=dbname, host=host_name, port=port_number, user=rolename, password=passwd)
cur = conn.cursor()
cur.execute("SELECT to_artist FROM similar_artist WHERE from_artist = %s AND match_index > 0.01 ORDER BY match_index DESC", (artist,))
'''conn.query("SELECT * FROM similar_artist")'''
similar_artist_list = cur.fetchall()
conn.close()
return similar_artist_list

def get_most_similar_song():
conn = psycopg2.connect(database=dbname, host=host_name, port=port_number, user=rolename, password=passwd)
cur = conn.cursor()
similar_artist_list = get_artist_similar()
playlist = []
playlist.append(first_input)
if similar_artist_list == []:
print("類似するアーティストが登録されていないか、打ち間違いの可能性があります。")
else:
cur.execute(
"SELECT to_artist, to_song FROM similar_track WHERE from_artist = %s AND from_song = %s AND match_index = 1.0",
first_input)
similar_tracklist = cur.fetchall()
if similar_tracklist == []:
cur.execute("SELECT from_artist, from_song FROM similar_track WHERE from_artist = ?", (artist,))
similar_tracklist = cur.fetchall()
similar_track = similar_tracklist[0]
playlist.append(similar_track)
return playlist
else:
similar_track = similar_tracklist[0]
playlist.append(similar_track)
return playlist
conn.close()

def generate_playlist():
conn = psycopg2.connect(database=dbname, host=host_name, port=port_number, user=rolename, password=passwd)
cur = conn.cursor()
similar_artist_list = get_artist_similar()
playlist = get_most_similar_song()
for i in range(len(similar_artist_list) - 1):
if len(playlist) == 10:
return playlist
else:
similar_artist = similar_artist_list[i]
search_word = (first_input[0], first_input[1], similar_artist[0])
cur.execute("SELECT to_artist, to_song FROM similar_track WHERE from_artist = %s AND from_song = %s AND to_artist = %s AND match_index > 0.01 ORDER BY match_index DESC", search_word)
similar_tracklist = cur.fetchall()
for j in range(len(similar_tracklist) - 1):
trackdata = similar_tracklist[j]
if trackdata in playlist:
continue
else:
playlist.append(trackdata)
print(playlist)
conn.close()

def playlist_print():
playlist = generate_playlist()
print(playlist)
get_empty_Spotify_playlist(playlist)
return playlist

def get_empty_Spotify_playlist(playlist):
playlist_name = artist + "の" + song + "から始まるおすすめプレイリスト"
ST = Spotify_token(username)
token = ST.set()
sp = spotipy.Spotify(auth=token)
sp.trace = False
playlistts = sp.user_playlist_create(username, playlist_name)
pprint.pprint(playlistts)
playlist_id = playlistts["id"]
print(playlist_id)
song_ids = []
for i in range(len(playlist)):
search_str = playlist[i]
s_artist = search_str[0]
s_song = search_str[1]
search_str = s_artist + " " + s_song
result = sp.search(search_str, limit=1)
print(result)
for item in result["tracks"]["items"]:
print(item["artists"][0]["name"])
print(item["name"])
print(item["id"])
api_song = item["name"]
api_song = re.sub("\t", "", api_song)
print(api_song)
song_id = item["id"]
print(song_id)
song_ids.append(song_id)
print(song_ids)
playlist_input(playlist_id, song_ids)

def playlist_input(playlist_id, song_ids):
ST = Spotify_token(username)
token = ST.set()
if token:
sp = spotipy.Spotify(auth=token)
sp.trace = False
results = sp.user_playlist_add_tracks(username, playlist_id, song_ids)
print(results)
else:
print("Can't get token for", username)

if __name__ == '__main__':
artist = input("検索したいアーティスト名を入力して下さい")
song = input("次にアーティストの楽曲名を入力して下さい")
first_input = (artist, song)
playlist_print()

 

 

なお、後半のSpotifyとの接続は以下の記事を参考にしました。

 

sakanaaas.hateblo.jp

 

現状、以下の現象を確認しています。

・DBに未収録の楽曲はプレイリストが出来ない

・入力を間違えるとエラーになる

Spotifyに配信されていない楽曲が混ざると、出来るプレイリストが想定と違う

 

なお、今後はDBでは無くAPIを直接参照するように改修します。また、アーティスト名を入れたあとに曲名を表示されるようにします。