DJ選曲術を読んだり、田中フミヤのviaのダイジェストを見たりしてたら、選曲というものが楽しくなってきた。どういう曲のつながりを気持いいと思うかは結局自分をよく知ることになるので意味のある行為ではないかと思う。
で、iTunesで聴いてる曲のつながりを記録するCUIアプリをPythonで書いてみた。単にヘッドとテイルを記録するだけの有向グラフなんだけど、sqliteにぶち込んでいるので後から検索するとき便利だと思う。データが溜まったらチェインをたどりながらplaylistに書きだすようなスクリプトも用意しようかな。
あとは選曲のつながりぐあいをnetworkxとかcytoscape視覚化して見てみたいという個人的な興味もあったりするのでそういうデータが欲しかったりする。

h(head)を押すとヘッドに現在聴いてるiTunesの曲名が、t(tail)だとテイルに挿入されてヘッドとテイルが揃うとグラフとして追加されるようにしてある。q押すとquitするようにしてあるんだけどquitしないのでなんかおかしい。後でどうにかする
そのうちGitHubで管理する予定
以下コード
#!/usr/bin/python
# -*- encoding:utf-8 -*-
# kzfm <kerolinq@gmail.com>
import curses
import os
from ScriptingBridge import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine,Column,Integer,String,ForeignKey,UniqueConstraint
from sqlalchemy.orm import scoped_session,sessionmaker
DATABASE = '/Users/kzfm/djutil.db'
engine = create_engine('sqlite:///'+DATABASE)
Session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = declarative_base(bind=engine)
class Music(Base):
__tablename__ = 'music'
__table_args__ = (UniqueConstraint('title','artist'),{})
id = Column(Integer, primary_key=True)
title = Column(String(128))
artist = Column(String(128))
class Graph(Base):
__tablename__ = 'graph'
__table_args__ = (UniqueConstraint('head','tail'),{})
id = Column(Integer, primary_key=True)
head = Column(Integer, ForeignKey('music.id'))
tail = Column(Integer, ForeignKey('music.id'))
def main(stdscr):
if not os.path.isfile(DATABASE):
Base.metadata.create_all()
session = Session()
iTunes = SBApplication.applicationWithBundleIdentifier_("com.apple.iTunes")
stdscr.nodelay(1)
head_music = None
tail_music = None
while True:
c = stdscr.getch()
y,x = stdscr.getyx()
if c == ord('h'):
artist = iTunes.currentTrack().artist()
title = iTunes.currentTrack().name()
head_music = session.query(Music).filter(Music.artist == artist) \
.filter(Music.title == title) \
.first()
if head_music == None:
head_music = Music(artist=artist,title=title)
session.add(head_music)
session.commit()
if tail_music != None:
mg = session.query(Graph).filter(Graph.head == head_music.id) \
.filter(Graph.tail == tail_music.id) \
.first()
if mg == None:
mg = Graph(head=head_music.id,tail=tail_music.id)
session.add(mg)
session.commit()
stdscr.addstr("%s / %s -> %s / %s" % (head_music.title,head_music.artist,tail_music.title,tail_music.artist))
stdscr.move(y+1, 0)
tail_music = None
else:
stdscr.addstr("%s / %s -> [] / []" % (head_music.title,head_music.artist))
stdscr.move(y, 0)
elif c == ord('t'):
artist = iTunes.currentTrack().artist()
title = iTunes.currentTrack().name()
tail_music = session.query(Music).filter(Music.artist == artist) \
.filter(Music.title == title) \
.first()
if tail_music == None:
tail_music = Music(artist=artist,title=title)
session.add(tail_music)
session.commit()
if head_music != None:
mg = session.query(Graph).filter(Graph.head == head_music.id) \
.filter(Graph.tail == tail_music.id) \
.first()
if mg == None:
mg = Graph(head=head_music.id,tail=tail_music.id)
session.add(mg)
session.commit()
stdscr.addstr("%s / %s -> %s / %s" % (head_music.title,head_music.artist,tail_music.title,tail_music.artist))
stdscr.move(y+1, 0)
tail_music = None
else:
stdscr.addstr("[] / [] -> %s / %s" % (tail_music.title,tail_music.artist))
stdscr.move(y, 0)
elif c == ord('q'):
exit
elif c != -1:
# debug
stdscr.addstr(str(c))
stdscr.refresh()
stdscr.move(0, 0)
if __name__ == '__main__':
curses.wrapper(main)