読者です 読者をやめる 読者になる 読者になる
スポンサーリンク

Windowsバッチで,iTunesのプレイリストを読み書きして,音楽ファイルをまとめて抽出する方法 (WSH/JScriptでiTunesを自動操作)

WSH/JScript sound


Windowsバッチで,iTunesを自動操作しよう。

とくに,「プレイリスト内の全曲の音楽ファイルを抜き出して,一箇所にまとめる」という処理を自動化する。


まずは基礎コードとして,プレイリスト内の全曲の情報を読み取るところから。

基礎

下記は,指定した名前のプレイリストについて,全曲の情報を表示するWSHコード。

hoge.jsで保存してダブルクリックで実行:

// iTunesオブジェクト
var itunes = WScript.CreateObject("iTunes.Application");

// 指定した名前のプレイリストを取得
var playlist = itunes
	.LibrarySource
	.Playlists
	.ItemByName( "プレイリストの名前" )
;

s = "";

// プレイリスト内の全曲について
for( var i = 1; i <= playlist.Tracks.Count; i++ ){

	// プレイリストから一曲取り出す
	var track = playlist.Tracks(i);

	// この曲の情報を取得
	var name     = track.Name;     // 曲名
	var artist   = track.Artist;   // アーティスト
	var filepath = track.Location; // ファイルのフルパス

	s += name + "/" + artist + "/" + filepath + "\n";
}


WScript.Echo(s);

これで,特定のプレイリスト内の全曲の情報が表示される。

もっとコーディングしやすく

上記のコードでは,曲の集合を配列オブジェクトとして扱っていない。

each()やmap()などのメソッドを使うために,配列に変換してみよう。

// 配列のイテレータ
Array.prototype.each = function( func ){
	for( var i = 0; i < this.length; i ++ ){
		func.call( this, this[i], i ); 
	}
	return this; // チェインを継続
};

// map
Array.prototype.map = function( func ){
	var _arr = [];
	this.each(function( item, ind ){
		_arr.push( func.call( _arr, item, ind ) );
	});
	return _arr;
};

// オブジェクトを配列に変換
Array.src = function( iterable, func_item, func_length ){
	var arr = [];
	
	var length = null;
	if( func_length ){
		length = func_length( iterable );
	}else{
		iterable.length;
	}
	
	for( var i = 0; i < length; i ++ ){
		if( func_item ){
			arr.push( func_item( iterable, i ) );
		}else{
			arr.push( iterable[ i ] );
		}
	}
	return arr;
};



// メイン処理



// iTunesオブジェクト
var itunes = WScript.CreateObject("iTunes.Application");

// 指定した名前のプレイリストを取得
var playlist = itunes
	.LibrarySource
	.Playlists
	.ItemByName( "プレイリストの名前" )
;


// プレイリストを配列に変換
var arr = Array.src( 
	playlist,
	function( _arr, i ){ return _arr.Tracks( i + 1 ); }, // 1始まりを0始まりに詰め替える
	function( _arr ){ return _arr.Tracks.Count; }
);


// プレイリスト内の全曲について
var s = arr.map(function(track){

	// この曲の情報を取得
	var name     = track.Name;     // 曲名
	var artist   = track.Artist;   // アーティスト
	var filepath = track.Location; // ファイルのフルパス

	return name + "/" + artist + "/" + filepath;
	
}).join("\n");


WScript.Echo(s);

これで,iTunes上の曲の集合に対してイテレータメソッドを適用できる。

応用:プレイリスト内の全曲のファイルを一箇所にまとめて抜き出すバッチ

iTunesのプレイリストは,異なるアルバム内の曲をまとめて聞ける機能だ。

なので,ファイルの実体があちこちに分散しており,回収しづらいという難点がある。


そこで,バッチを使って全曲のファイルをまとめてコピーしよう。

iTunes上で選択されている全曲について,hogeというフォルダの中にファイルをコピーしてみる。


全曲抜き出し.bat で保存して,iTunes上で複数曲を選択した状態でダブルクリック:

@if(0)==(0) ECHO OFF

cscript.exe //nologo //E:JScript "%~f0" "%~dp0" >out.log
@pause

GOTO :EOF
@end



// ----- ライブラリ関数 -----


// ファイルパス文字列からファイル名を抽出
String.prototype.file_path_to_file_name = function(){
	return this.replace(/^.+\\([^\\]+)$/, "$1");
};


// ファイルパス文字列からフォルダパスを抽出
String.prototype.file_path_to_dir_path = function(){
	return this.replace(/\\[^\\]+$/, "");
};



// 配列のイテレータ
Array.prototype.each = function( func ){
	for( var i = 0; i < this.length; i ++ ){
		func.call( this, this[i], i ); 
	}
	return this; // チェインを継続
};

// map
Array.prototype.map = function( func ){
	var _arr = [];
	this.each(function( item, ind ){
		_arr.push( func.call( _arr, item, ind ) );
	});
	return _arr;
};

// オブジェクトを配列に変換
Array.src = function( iterable, func_item, func_length ){
	var arr = [];
	
	var length = null;
	if( func_length ){
		length = func_length( iterable );
	}else{
		iterable.length;
	}
	
	for( var i = 0; i < length; i ++ ){
		if( func_item ){
			arr.push( func_item( iterable, i ) );
		}else{
			arr.push( iterable[ i ] );
		}
	}
	return arr;
};



function log(s){ WScript.Echo(s); }

//
function err(s){
	try{
		WScript.StdErr.WriteLine(s); 
		// リダイレクトされずにコンソールにも表示される
	}catch(e){
		// GUIで実行された場合は標準出力だけでよい
	}
	
	log(s);
}



// コマンド実行結果を行ごとに配列として取得
// http://language-and-engineering.hatenablog.jp/entry/20081225/1230198688
function exec_cmd( str_cmd, params ){

	// ------ パラメータを取得 -----

	// コマンドを実行したいカレントフォルダのパス
	var current_dir    = ( params ) ? params.current_dir : null;
	
	// カレントフォルダを,このスクリプトの存在するフォルダとするか
	var current_dir_same_as_this_script = ( params ) ? params.current_dir_same_as_this_script : false;
	
	// 通信コマンドが終了するまで待つかどうか
	var wait_while_net = ( params ) ? params.wait_while_net : false;
	
	// 出力行から一行抜き出すための正規表現
	var get_one_output_re = ( params ) ? params.get_one_output_as : null;

	// 出力行から抜き出した一行をキャプチャするための文字列
	var capture_output_str = ( params ) ? params.capture_output_as : null;

	// コマンドを実際には実行しないデバッグモード
	var just_debug_mode = ( params ) ? params.just_debug : false;
	
	// ------ パラメータ取得終わり -----
	

	var ws = WScript.CreateObject("WScript.Shell");
	
	// 必要ならカレントフォルダをセット
	if( current_dir ){
		ws.CurrentDirectory = params.current_dir;
	}else
	if( current_dir_same_as_this_script ){
		ws.CurrentDirectory = WScript.ScriptFullName.file_path_to_dir_path();
	}

	// もしデバッグモードなら,ここで終了
	if( just_debug_mode ){
		err( "カレントフォルダ:" + ws.CurrentDirectory + ", コマンド:" + str_cmd );
		err( "デバッグモードのため,コマンドは実行しません。" );
		
		return;
	}

	err( "実行します:カレントフォルダ:" + ws.CurrentDirectory + ", コマンド:" + str_cmd );

	// コマンド実行
		var proc = ws.Exec( "cmd /c " + str_cmd );
	
	// もしメール送信など通信系のコマンドであれば,終了まで待つ
	if( wait_while_net ){
		while( proc.Status == 0 ){
			WScript.Sleep(100);
		}
		// http://language-and-engineering.hatenablog.jp/entry/20100913/p1
	}
	
	// 出力を取得
	var str_out = proc.StdOut.ReadAll();
	
	// 出力の末尾の空行を削除
	var output_arr = str_out.split("\r\n");
	output_arr.pop();
	
	// 出力から一行だけ取り出す?
	if( get_one_output_re ){
		log("出力から一行取り出します。");
		var match_result = null;
		
		// 行ごとに検査
		output_arr.each(function(line){
			var m = line.match( get_one_output_re );
			
			// 初回マッチについて
			if( m && ( ! match_result ) ){
				// マッチ結果からキャプチャした文字列を保管
				match_result = line.replace(
					get_one_output_re, 
					capture_output_str 
				);
					err("マッチ結果を発見:" + match_result);
			}
		});
		
		// 全行を検査し終わってもマッチしていなかったらNG
		if( ! match_result ){
			throw("出力のどの行もマッチしませんでした。");
		}
		
		// マッチ結果を返す
		return match_result;
	}
	
	// 通常は出力の配列をそのまま返す
	return output_arr;
}


// 2桁に0埋め
function dig2( n ){
	if( n < 10 ){
		return "0" + n;
	}else{
		return "" + n;
	}
}



// ----- メイン処理 -----




// 曲を集めるためのフォルダを作る
var dir_name = "hoge";
exec_cmd( "mkdir " + dir_name, {
	current_dir_same_as_this_script : true
});


// iTunesオブジェクト
var itunes = WScript.CreateObject("iTunes.Application");


// 現在,iTunes上で選択されている全ての曲を取得
var tracks = itunes.SelectedTracks;

// 配列に変換
var arr = Array.src( 
	tracks,
	function( _arr, i ){ return _arr.Item( i + 1 ); }, // 1始まりを0始まりに詰め替える
	function( _arr ){ return _arr.Count; }
);


// プレイリスト内の全曲について
arr.map(function(track){

	return track.Location; // ファイルのフルパス

}).each(function(file_path, index){

	// ファイル名を抜き出す
	var file_name = file_path.file_path_to_file_name();
	
	// プレイリスト内の順番を保持するために,
	// 先頭の数字を付け替える
	var new_file_name = file_name
		.replace(/^\d+/, dig2( index + 1 ) )
	;
	
	var copy_command = 'copy "'
		+ file_path
		+ '" "'
		+ dir_name
		+ '\\'
		+ new_file_name
		+ '"'
	;
	
	// コピー実行
	exec_cmd( copy_command, {
		current_dir_same_as_this_script : true,
		just_debug : false
	});
})


log("プレイリスト内の全曲をフォルダ内にコピー完了");


ポイントは下記の部分。

// 現在,iTunes上で選択されている全ての曲を取得
var tracks = itunes.SelectedTracks;

これさえ実行できれば,あとはファイル操作だけで済む。


参考:

実はすごい簡単なiTunesスクリプトの書き方(Windows版iTunes) - 犬も歩けば棒も歩く
http://d.hatena.ne.jp/prime503/200909...

  • 「無題のプレイリスト」というプレイリストの中に入っている曲の曲名とアーティスト名を表示するスクリプト


iTunes COM Interface: IiTunes Interface Reference
http://www.joshkunz.com/iTunesControl...

  • HRESULT IiTunes::SelectedTracks ( [out, retval] IITTrackCollection ** iTrackCollection )
  • Returns a collection containing the currently selected track or tracks. The frontmost visible window in iTunes must be a browser or playlist window. If there is no frontmost visible window (e.g. iTunes is minimized to the system tray), the main browser window is used.


Automatically Number Songs in iTunes | Apple Support Communities
https://discussions.apple.com/thread/...

  • If you use a Mac, there is an AppleScript called "Albumize Selection" that accomplishes this task, but after many, many hours spent searching the internet, I was unable to find a solution for Windows.
  • With my limited programming knowledge, I was able to write a simple script that will accomplish this task, and I thought I would share it with everyone. This script (JavaScript) runs on the built-in Windows Script Host - it does not require any third party software.


iTunesスクリプト 曲をライブラリ上からだけでなく、ファイル自体も削除 - 犬も歩けば棒も歩く
http://d.hatena.ne.jp/prime503/200909...

  • ファイルパスが同じなら、指定した曲と同じ曲 if(target.Location == track.Location)

関連する記事:

iTunesで管理している音楽の「ボーカルキャンセル版」を作成する方法
http://language-and-engineering.hatenablog.jp/entry/20120128/p1


バッチで,wavなどの音声を再生しよう (コマンドプロンプトから音を鳴らす方法)
http://language-and-engineering.hatenablog.jp/entry/20100729/p1


バッチで,レジストリの値の読み取り・書き込み・存在判定をしよう(WSH/JScriptでレジストリ操作のサンプルコード)
http://language-and-engineering.hatenablog.jp/entry/20100908/p1


コマンドラインからwaveファイルを作る方法  (テキストを.wavに変換)
http://language-and-engineering.hatenablog.jp/entry/20081107/1226080582


バッチで,画像を生成・加工・一括処理しよう (WSH/JScriptでImageMagickを呼び出す方法)
http://language-and-engineering.hatenablog.jp/entry/20111019/p1