iTunesファイルをSSDに入れて別のPCで使えるようにする

本記事はiTunesのファイルをどうやって外部ストレージに保存し、 他のPCで聞けるようにするかについて説明します。

音楽好きの方々の中にはSpotifyなどAudio Streaming Platformに完全に移行できず、 ローカルにしか無い楽曲をiTunesで楽しんでる方もいるのではないかと思います。 そんな方々の音楽生活を豊かにするための参考になれば幸いです。

3行で

検証環境

Windowsは対象外です

iTunesのファイル構成

Macでは ~/Music/iTunesに以下のようなファイル群が格納されている

ファイル or ディレクトリ名 説明
iTunes Library Extras.itdb Gracenote CDDB※1の情報が格納されているファイル
iTunes Library Genius.itdb iTunes Genius※2に関するファイル
iTunes Library.itl ライブラリ内の曲とユーザーが作成したプレイリストのデータベース
iTunes Media 実際の楽曲ファイル
  • ※1音楽CDの演奏者や制作年度などの情報が格納されたデータベース
  • ※2ライブラリ内にすでにある音楽と関連のある新しい音楽をライブラリまたはiTunes Store内で見つける機能。

参考:

コピー手順

  1. SSD直下にiTunesディレクトリを作成し
  2. 以下をSSDiTunesディレクトリにコピー
    • ~/Music/iTunes/iTunes Library Extras.itdb
    • ~/Music/iTunes/iTunes Library Genius.itdb
    • ~/Music/iTunes/iTunes Library.itl
    • ~/Music/iTunes/iTunes Media

別PCとSSDにあるiTunesライブラリを連携

以下のシェルスクリプトiTunesにすでにあるファイル群を削除します。 すでにiTunesで楽曲を管理しているPCに適用する場合は注意してください。

#!/bin/bash

cd ~/Music/iTunes
rm -f ./iTunes\ Library.itl ./iTunes\ Library\ Extras.itdb ./iTunes\ Library\ Genius.itdb
rm -rf ./iTunes\ Media
ln -s /Volumes/<あなたの外部ストレージの名前>/iTunes/iTunes\ Library.itl ./iTunes\ Library.itl
ln -s /Volumes/<あなたの外部ストレージの名前>/iTunes/iTunes\ Library\ Extras.itdb ./iTunes\ Library\ Extras.itdb
ln -s /Volumes/<あなたの外部ストレージの名前>/iTunes/iTunes\ Library\ Genius.itdb ./iTunes\ Library\ Genius.itdb
ln -s /Volumes/<あなたの外部ストレージの名前>/maciTunes/iTunes\ Music/ ./iTunes\ Media
cd -

上記のシェルスクリプトはすでにあるiTunesのファイル群を削除し、SSDにあるファイル群のシンボリックリンクを追加します。 このシェルスクリプトを実行後、iTunesを起動すると楽曲にアクセスできるようになります。

所感

以下のSSDをつかって使っています。

少し反応が遅いときもありますが基本快適に過ごせています。ネット環境が厳しくても気にせず好きな音楽聞けるのは嬉しいですね。

HerokuとPythonとSlackによるお手軽可視化システム

pandasやmatplotlib、他科学計算系のpackageを用いデータを定期的に可視化しチェックしたいケースがある。

この際特に大げさなシステムを作る必要がないし、金もねぇ、時間もねぇときがある。でもRedashじゃできねぇ...

そんな状況の中作ったHeroku、Python、Slackを用いたシステムについての備忘録。

目次

  1. Heroku Schedulerを設定
  2. pyplotでグラフを保存しSlackに通知
  3. 動的にmoduleをロードし、スクリプト追加を簡略化

1. Heroku Schedulerを設定

Heroku上で定期的にスクリプトを実行できるHeroku Schedulerというものがある。

devcenter.heroku.com

適当にHerokuでappを作成した後、appにスクリプトファイルを配置する。 あとはSchedulerの設定画面でpython3 スクリプトファイルを実行するよう設定すれば pythonスクリプトが定期的に実行される。便利。 この場合特にProcfileとか用意する必要はない。

Heroku CLIをインストールした後、以下のコマンドでappを作成

$ heroku create

以下URLに従いgitの設定をする

https://devcenter.heroku.com/articles/git

# heroku createした場合
$ git remove -v
or 
# すでにherokuのappが存在する場合
$ heroku git:remote -a app名

なにかしらgitが整ったらHerokuのgitにpushし

$ git push heroku master

以下コマンドでHeroku Schedulerをappに追加

$ heroku addons:create scheduler:standard

tips

# schedulerの設定を開きたいときは
$ heroku addons:open scheduler

# ログを見たいとき
$ heroku logs

Schedulerの設定では以下のようにpythonスクリプトを実行するように設定する。 f:id:koyamay:20190711235541p:plain

2. pyplotでグラフを保存しSlackに通知

必要なもの:

Slack Web Apiを使うためにSlackのBotが必要になる。 python-slackclientはファイルアップロード機能を使うために利用する。

※当初SlackのIncoming Webhoookでbase64の画像をアップロードしようと思っていたが無理でした。

以下のようなスクリプトでSlackに簡単に画像が投稿できる

from matplotlib import pyplot
matplotlib.use('Agg')
import io
import numpy
import os
import slack

def main(argv):
  filename = "result.png"
  plot(filename)
  post(filename)
  os.remove(filename)

def plot(filename):
    x = numpy.linspace(0, 10)
    y = numpy.sin(x)
    pyplot.plot(x, y)
    pyplot.savefig(filename, format="png")

def post(filename):
    client = slack.WebClient(token=os.environ['SLACK_API_TOKEN'])

    response = client.files_upload(
        channels='#random',
        file=filename,
        title="test",
        filename=filename)

if __name__ == '__main__':
    main(sys.argv)

画像の生成はtempfile.NamedTemporaryFileを使う方が好ましく当初それで作成していた。 なぜかうまくSlackの投稿でサムネイルが表示されなくなったため、result.pngを保存するようにした。

SLACK_API_TOKEN環境変数を参考するようにしており(参考)、 開発時にはpython-dotenvを使うと良いと思われる。 Herokuには以下のコマンドで環境変数を設定する

$ heroku config:set SLACK_API_TOKEN=トークン

あとはHerokuになんのpackageを使っているか伝えるためrequirements.txtを配置する。

$ pip freeze > requirements.txt

これで下記画像のようなものがSlackに投稿される。

f:id:koyamay:20190712001256p:plain

3. 動的にmoduleをロードし、スクリプト追加を簡略化

今後も追跡したいデータとそれにまつわる可視化のグラフは増えそう。 簡単にグラフを追加できるようにする。

以下のようなディレクトリ構成

- app.py
- jobs
  |- some_job
  |   |- __init__.py
  |   |- analyzer.py
  |
  |- another_job
      |- __init__.py
      |- analyzer.py

app.pyは2.のものを改変し以下のような形にする

from datetime import datetime
from dotenv import load_dotenv
load_dotenv()
import importlib
import os
imporo sys
import slack

def main(argv):
    jobs_dir = os.listdir('jobs')
    for job_dir in jobs_dir:
        m = importlib.import_module("jobs.%s.analyzer" % job_dir)
        filename = "result.png"
        try:
            if os.path.isfile(filename):
               os.remove(filename)
            m.plot(filename)
            post(job_dir, filename)
        finally:
            if os.path.isfile(filename):
               os.remove(filename)

def post(job_dir, filename):
    client = slack.WebClient(token=os.environ['SLACK_API_TOKEN'])

    response = client.files_upload(
        channels='#random',
        file=filename, title="%s_%s.png" % (job_dir, datetime.now().strftime("%Y_%m_%d")),
        filename=filename)

if __name__ == '__main__':
    main(sys.argv)

ディレクトリ名、メソッド名はもうちょい良い感じにしていただければ。

importlibを用いてjobsディレクトリ配下のmoduleをimportする。そのためには空でもいいので__init__.pyが必要らしいので作成しておく(間違ってる可能性あるのでツッコミください)。

各moduleにはanalyzer.pyというファイルがあり、plotメソッドでresult.pngという画像を保存する。

以下例:

analyzer.py

import matplotlib
from matplotlib import pyplot
matplotlib.use('Agg')
import numpy
import io

def plot(filename):
    image = io.BytesIO()
    x = numpy.linspace(0, 10)
    y = numpy.sin(x)
    pyplot.plot(x, y)
    pyplot.savefig(filename, format="png")

このような各moduleのanalyzer.pyapp.pyから動的に読み込み、plotメソッドを随時呼び出して画像を作成、そしてSlackに投稿という流れになる。

新しく定期的にplotしたいものが増えた場合、jobsディレクトリにディレクトリを新規作成しanalyzer.pyを用意しplotメソッドを定義すればよい。

まとめ

HerokuとPythonとSlackによる可視化システムについて説明しました。時間がほぼないなかさっとつくったにしては結構便利です。 前々からRedashだけだと足りないが、壮大なシステムを作るほどでもないケースが多かったのでこれからも重宝しそう。 個人的な理想としてはRedashと定期的にJupyter Notebookが動作するようなシステムがあればそれで十分なんだけどなーと思うのですがあるのでしょうか。 あったら教えてください偉い人!

余談ですが、これまでpipenvをつかっていたが、venvが便利なことに気づく。

qiita.com

Pythonは四半期に1回ちょっと使う程度なので間違いがあればご指摘お願いします。

twirp-ruby概要

はじめに

twirpというgRPCと似たようなRPCフレームワークを触っている中でのメモ。twirpはgRPCと似ていますがHTTP2に限定されずHTTP1.1でも使用できる便利なやつです。

基本

github.com

twirp-rubyはtwirpをRubyで動かすやつです

登場人物

  • Service...ProtocolBufferで定義されたService。その中身は実はRackアプリケーション

  • Handler...Serviceがどう動くかを定義

  • Client...Service呼ぶやつ

ServiceはServiceをRackアプリケーショとして定義するが、どう動くかについては定義していない。Handlerを差しこむことで中身の動きを定義する 例:

HogeService.new(FugaHandler)

ちなみに以下のような形でHookポイントがあるので、各Hookポイントで処理を書くことが可能

routing -> before -> handler -> on_success
                             -> on_error

具体的なService, Handler, Clientのコードは以下となる

module Example
  class HelloWorldService < Twirp::Service
    package "example"
    service "HelloWorld"
    rpc :Hello, HelloRequest, HelloResponse, :ruby_method => :hello
  end

  class HelloWorldClient < Twirp::Client
    client_for HelloWorldService
  end
end

class HelloWorldHandler
  def hello(req, env)
    if req.name.empty?
      return Twirp::Error.invalid_argument("name is mandatory")
    end
    {message: "Hello #{req.name}"}
  end
end

Service内で使用しているメソッド詳細

  • package... Serviceのパッケージ名を定義。まじでパッケージ名的なことにしか使われない
  • service...Serviceのサービス名を定義。まじでサービス名的なことにしか使われない
  • rpc... Twirp::Serviceにはrpcsというrpcの定義をためる変数がある。そこにrpcを追加する

rpcsには以下のようなデータが追加される

twirp-ruby/service_dsl.rb at 3ec36d2653a14eb8cc73409fb20889e8484db371 · twitchtv/twirp-ruby · GitHub

rpcdef = {
  rpc_method: rpc_method.to_sym, # as defined in the Proto file.
  input_class: input_class, # google/protobuf Message class to serialize the input (proto request).
  output_class: output_class, # google/protobuf Message class to serialize the output (proto response).
  ruby_method: opts[:ruby_method].to_sym, # method on the handler or client to handle this rpc requests.
}

@rpcs ||= {}
@rpcs[rpc_method.to_s] = rpcdef

Clientからの呼び出し方法

twitchtv/twirp-ruby

# Equivalent RPC calls
client.rpc(:Hello, HelloRequest.new(name: "World")) # generic rpc method
client.hello(HelloRequest.new(name: "World")) # DSL method with message object
client.hello(name: "World") # DSL method with message attributes

gRPCと違い、Requestクラスを使わずともHashで呼べる

Client呼び出し時の戻り値

The response is a Twirp::ClientResponse object with the properties data (success) and error (failure).

しかも、errorは Twirp::Error が返される。gRPCと違い直接ProtocolBufferで定義したmessageを返さないので注意。

Rails連携

github.com

2018年振り返り

前年は2Bサービスの提供や課金、SEO対策などをやっていました。 2017年に立てた目標が達成できているか、2018年を振り返ってみます。

3行で:

  • 2017年にたてた目標はまぁまぁ達成できました
  • プライベートもなかなかしんどい時期ありましたが私は元気です
  • 2019年からは目黒や自由が丘にいます
続きを読む

RequestId付きのRailsのログをvimで便利に見る

Railsではconfig.log_tagsuuidを指定することでRequestId付きのログを出力できます。 RequestIdつきのログを確認するとき、以下のような欲望があります。

  1. 特定のパターンでフィルタしたい
  2. マッチした行だけでなく、関連するRequestIdの行を全部表示し、リクエスト全体の流れが知りたい。

例えば特定のControllrのActionのログをフィルタしたいときや、特定のユーザーのログを見たいときなどです。 vimで関数を定義することで実現できたので記しておきます。

検証環境:

Railsのログはconfig/application.rbとかに以下のようなconfigがはいってるやつです。

config.log_tags = [:uuid]
続きを読む

Parser gemを用いたプロファイルコードの挿入

Rubyで簡易なプロファイル用のコードを挿入し、プロファイリングするにはどうしたらいいのか興味が出たのでやってみました。最終的にはwhitequark/parser を用いたRewrite処理で実現できました。

JavaScriptの関数の開始と終了にプロファイル用のコードを挿入し、シンプルなプロファイリングをするという記事が以下:

itchyny.hatenablog.com

ASTの一つの使い方としてとても面白いです。

続きを読む

データ起点でのプロダクト改善の取り組み: 8月

こんにちは。普段はClearと呼ばれる教育系アプリに対して、Ruby on Railsでの開発やインフラの整備、Androidのレビューを行っています。趣味でアクセスログ解析を行っていました。

2018年8月からプロダクトの開発方針を変更し、データの観点からもアプリの各種KPI向上を目指すことになりました。 そこでの取り組みについて、まだまだ手探りですが誰かの役に立つかもしれないので備忘録を兼ねて記しておきます。

前提

  • 世界で200万DLくらいのC to C学習アプリ
  • アプリからのアクセスログAWS Redshiftにたまっている
  • データ観点での施策は既存機能にまつわる課題の解決に留める。より大きな目線での課題解決はデザイナーが担当

なぜやるのか

これまでの開発方針ではMAUなどの主要KPIの増加を狙うため施策を行っていましたが、個人的に以下の問題があると感じていました

  • 施策に効果があったか評価をしない
  • 施策に効果があるかどうかわからないまま結構な工数を消費しがち

またこれまでユーザーへのインタビューなど行い、ペルソナなどもつくっていましたが、うまくそれをプロダクト開発に活用できていないのも気になっていました。仮説に対する学びを得る、無駄な工数を減らす、ユーザーから得られた情報を活かすという想いから開発方針の変更に至りました。

こんな人におすすめ(だと思う)

  • リーンスタートアップやInspiredをよんだが何したらいいのかいまいちわからない人
    • 僕もよくわからないので試行錯誤してる最中です
  • プロダクトチーム5人以下の小さな会社
    • おそらく大きな会社だとグロースチームや分析チームなどいろいろな役職の人がいるので、より効率的なやり方があると思います。
続きを読む