Hatena::ブログ(Diary)

La Faïence

2012-01-16

DirectoPlay

前作ったブックマークレット、Apache等々のファイル一覧画面から再帰的に音楽・動画のプレイリストを生成するブラウザプレーヤーのGoogle Chrome拡張対応版を作りました。

こちらからインストールできます→DirectoPlay (Google Chrome Extention) Download

たとえば、この画面はnginxのディレクトリ一覧画面なんですけど、このようにリトバスのサントラが入ってるフォルダ4つに対してDirectoPlayを起動すると

から

のように、ブラウザ内でプレーヤーが立ち上がります。再帰的にファイルリストの取得がなされる上、MP3のタグにも完全対応しているのでそれなりに使えます。まぁ、タグに対応させるためにいちいち一曲ずつサーバにリクエストしているんですけど。

あと、動画があれば右下あたりに再生されます。対応形式はブラウザによります。

作業用BGMにどうぞ。

Chromeなんて使いたくねーという方はほかのブラウザ用のブックマークレットもあります。ブックマークバーに入れてポチッと押すだけで一発変換です。

ところで、いまこいつの再帰の部分を利用して、アレゲな画像をクロールしてくるビューワーを作りかけています。まぁ、いろいろ難しいので時間が取れればいつかできあがると思います。

2011-12-11

ASIO4ALLで音が出ないとき

サウンド->[再生]タブ->デバイスをダブルクリック->[詳細]タブ

うちは以下のチェックボックス外すだけで直った(再生デバイス、つまりヘッドフォン・スピーカーのすべてに対してこの設定を行う)。あとWindows Media Player落としたりしたけど。

f:id:famnet:20111211190547p:image

2011-11-14

Apacheやnginxのディレクトリ一覧にある音楽/動画ファイルをプレイリスト化するブックマークレット

自分用の音楽/動画用のサーバがあって,いたるところ,あらゆるデバイス(PC,iPadiPhone)で音楽を楽しみたいときのためのブラウザ プレイヤーです.

ApacheのIndexes(ディレクトリのリスト)をパースし,プレイリスト化,そして再生できるやつです.しかもMP3/AACのIDv3対応!

f:id:famnet:20111114002552p:image

プレイリスト化↓

f:id:famnet:20111114135912p:image

ディレクトリがあるとどんなに深い階層でも再帰的にプレイリスト化します.MP3/AAC(MP4Audio)のタグ対応なので,曲名もアーティストも出てきます.すばらしいですね!

SMBじゃiPadで再生できないし,ブラウザのインデックス画面だといろいろめんどいときにこいつをポチっとすると一発起動です.

ただしSafariGoogle ChromeでしかMP3/MP4再生はできません.

Chrome拡張つくりました

インストールはこちらから

http://www.keiyac.org/files/js5p/js5p.crx

Bookmarklet作りました

これをブックマークバーとかに登録しておくと,ファイル一覧のインデックス画面でこれを押すとプレイヤーが起動します.

注意)IE9だとSCRIPT58644: エラー c00ce514 のため操作を完了できませんでした。

とかいう意味不明のエラーでおかしくなる

javascript:(function(){var s=document.createElement('script');s.type='text/javascript';s.src='http://www.keiyac.org/files/js5p/js5p.js';document.getElementsByTagName('head')[0].appendChild(s);var wrap=document.createElement('div');wrap.id='_js5p_wp';document.getElementsByTagName('body')[0].appendChild(wrap);var c=document.createElement('LINK');c.rel='stylesheet';c.href='http://www.keiyac.org/files/js5p/js5p.css';c.type='text/css';c.media='screen';document.getElementsByTagName('head')[0].appendChild(c);})();

ブックマークレット表示

デモページとか

もちろんコピーレフトのやつです

http://www.keiyac.org/files/js5p/bookmarklet.html

ご注意

  • 動画は右下(bottom:0;right:0)で表示されます
  • MP3のID3タグを取得するために,初期ロード時サーバにものすごい勢いでアクセス(100ms間隔/ファイルごと)します
  • 著作権を守ってください
    • 公開サーバで利用しなければいいと思う
  • nginxは実は動作確認していない
  • ずっと全曲リピートだと思います.
    • play()関数の最初らへんのifを消してreturnすればリピートなくなる.
  • キーバインドないので,てきとうにイベントリスナ作れば良いと思う
  • SOP(Same Origin Policy)の制約上,下記HTMLを置くサーバとファイルを置くサーバは同じにしないとダメです

ソース

download

<!DOCTYPE html>
<!--
js5player v. 1.0.0 keiyac https://keiyac.appspot.com/
Copyright (C) 2011 Keiya Chinen
-->
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/combo?2.9.0/build/reset/reset-min.css" /> 
<style>
header {
	position:fixed;
	top:0;
	left:5%;
	height:50px;
	width:100%;
	color:#f88;
	font-size:2em;
}
html {
	height:100%;
	width:100%;
	min-width:768px;
	background-color:#000;
	color:#eee;
	font-family:sans-serif;
}
a,a:visited {
	color:#eee;
}
#resource_index {
	display:none;
}
#sidebar {
	position:fixed;
	top:50px;
	left:0;
	width:20%;
	background-color:#111;
}
#playlist {
	position:absolute;
	top:50px;
	left:20%;
	width:70%:
	background-color:#222;
	overflow:auto;
}

#playlist table {
	position:relative;
	width:100%;
}

#playlist tr.song {
	line-height:1.75em;
}
#playlist tr.song.playing {
	background-color:#333;
}
#playlist tr.song:hover {
	background-color:#333;
}
#playlist tr.song td {
	overflow:hidden;
	white-space:nowrap;
}
#playlist .name {
	width: 40%;
}
#playlist .artist {
	width: 20%;
}
#playlist .album {
	width: 20%;
}
#playlist .tracknumber {
	width: 20%;
}
audio {
	position:fixed;
	width:100%;
	bottom:0;
}
video {
	position:fixed;
	bottom:0;
	right:0;
}
</style>
<title>js5player</title>
<body>
<header>js5player</header>
<div id='sidebar'></div>
<div id='resource_index'></div>
<div id='playlist'></div>
<div id='player'></div>
<!--
<button id='prev' href='#';>Prev</button>
<button id='next' href='#';>Next</button>
-->
<script src='http://code.jquery.com/jquery-1.7.min.js'></script>
<script src='id3.js'></script>
<script>
(function(){
/* js5player v. 1.0.0 keiyac https://keiyac.appspot.com/
 * Copyright (C) 2011 Keiya Chinen
 */
var idx = 0;
var resource_index = 'http://192.168.24.170/~keiya/play/';
var playlist = [];
var extentions = {
	'm4a':'audio/mp4',
	'm4v':'video/mp4',
	'mp3':'audio/mpeg',
	'mp4':'video/mp4',
	'oga':'audio/ogg',
	'ogg':'audio/ogg',
	'ogv':'video/ogg',
	'webm':'video/webm',
	'wav':'audio/vnd.wave'
};
var cnt = 0;
function create_plist(base) {
	$('#resource_index').html();
	$('#resource_index').load(base+' a',function(){
		$('#resource_index a').each(function(){
			var uri = $(this).attr('href');
			var short_filename = $(this).text();
			var filename = decodeURIComponent(uri);
			var directory_regexp = new RegExp("^[^/].+/$");
			var query_regexp = new RegExp("^\\?");
			var different_origin_regexp = new RegExp("^(https?:)?//");
			var file_regexp = new RegExp("^[^/]");
			var extention_regexp = new RegExp("\\.([\\w\\d]{3,4})$");
			if (uri.match(directory_regexp)) {
				create_plist(base + uri);
			}
			else if (uri.match(query_regexp)) {
			}
			else if (uri.match(different_origin_regexp)) {
				console.log('diff '+uri);
			}
			else if (uri.match(file_regexp)) {
					uri.match(extention_regexp);
					var obj = {'uri':base+uri,'filename':filename,'ext':RegExp.$1,'mime':extentions[RegExp.$1]}
					playlist.push(obj);
			}
		});
		show_plist();
		//first_play();
	});
	return;
}
function show_plist() {
	var dom_elems = '';
	for (i in playlist) {
		if (playlist[i].mime) {
		var filename = playlist[i].filename;
		dom_elems += 
		'<tr class="song" data-idx="'+i+'">'+
		'<td class="name"><a href="#">'+filename+'</a></td>'+
		'<td class="artist"></td>'+
		'<td class="album"></td>'+
		'<td class="tracknumber"></td></tr>'
		}
	}
	$('#playlist').html('<table>'+dom_elems+'</table>');
	show_tags();
}
function show_tags() {
	var i = 1;
	$('.song').each(function(){
		var $each = $(this);
		var idx = $each.attr('data-idx');
		var uri = playlist[idx].uri;
		$each.find('a').click(function(){
			var $a = $each.find('.name a');
			play(idx);
			return false;
		});
		if (playlist[idx].mime.split('/')[0] == 'audio') {
		setTimeout(function(){
			ID3.loadTags(uri,function(){
				var tags = ID3.getAllTags(uri);
				$each.find('.name a').text(tags.title);
				$each.find('.artist').text(tags.artist);
				$each.find('.album').text(tags.album);
				$each.find('.tracknumber').text(tags.track);
			})
		},i*100);
		i++;
		}
	});
}
function play(idx) {
	if (!playlist[idx]) {
		idx = 0;
//		play();
	}
	var options = 'controls preload autoplay ';
	var mime_type = playlist[idx].mime.split('/')[0];
	if (mime_type == 'video') {
		$('#player').html('<video class="queue" src="'+playlist[idx].uri+'" '+options+' />');
		$('.song[data-idx!="'+idx+'"]').each(function(){
			$(this).removeClass('playing');
		});
		$('.song[data-idx="'+idx+'"]').addClass('playing');
		$('.queue').get(0).play();
		$('.queue').bind("ended",function(){
			idx++;
			play(idx);
		});
	}
	else if (mime_type == 'audio') {
		$('#player').html('<audio class="queue" src="'+playlist[idx].uri+'" '+options+' />');
		$('.song[data-idx!="'+idx+'"]').each(function(){
			$(this).removeClass('playing');
		});
		$('.song[data-idx="'+idx+'"]').addClass('playing');
		$('.queue').get(0).play();
		$('.queue').bind("ended",function(){
			idx++;
			play(idx);
		});
	}
}
$(window).load(function(){
create_plist(resource_index);
});

}())
</script>
</body>

ぜんぶのコード書くのに7時間くらいかかった.

Groovesharkとか意識しかけたけど,頭痛だしやめた.いちおうサイドバー領域あるので,適当にごにょってください.

ipadでも!

f:id:famnet:20111114002553p:image

20歳なりました

先月末に20歳なりました.老害の始まりです.

2011-11-08

今更だけどGDD2011で書いたコード置いておく系

GDD2011終わってからエントリを書くという変な話。結局、GDDはなぜか行けました。ありがとうGoogleさん。もらった+1ステッカーはMacBook Proに貼りましたよ〜。あとzeemoteとかいうやつ貰ったのでエロゲ捗る。

で、下のgistは例の数学クイズみたいなやつのたいしたこと無いけど回答コード。二分木つかって深さ優先でトラバースした。時間経ちすぎてコードの解説無理

https://gist.github.com/1261072#file_gdd2011quiz_tree.pl

Cf. http://d.hatena.ne.jp/famnet/20110831/1314781889

フォントをひたすら画像化しまくるPerl+Automatorを書いてみた

f:id:famnet:20111108193531p:image

フォントをアルファチャネルPNG画像に変換して、何かに使うためのスクリプトです。フォントには著作権がありますし、契約によってはこいつを使って変換した画像をWebサイトにつかっちゃったりしちゃったりすると、無料ホテルにしばらくのあいだ滞在せざるをえなくなる可能性があるので、注意してください。

MacのAutomatorに、標準入力のテキストを画像に変換するという機能がデフォでついてます。そいつとスクリプトの標準出力を利用して、PNG画像を出力しまくるソリューションを作ってみましょう。

まずはPerlのほうですが、これは何をするのかというとただひたすら文字コードをインクリメントしていって、ひたすらその数値に対応する文字を出していくものです。原理的すべての文字(改行コードやタブ等々も全部)を出すことになります。

#!/usr/bin/perl
#
use strict;
use warnings;
use utf8;
binmode (STDOUT,'utf8');

open(RFH, "</Users/keiya/Documents/perl/lastnum.txt");
my $lastnum = <RFH>;
open(WFH, ">/Users/keiya/Documents/perl/lastnum.txt");
chomp($lastnum);
print chr($lastnum+1);
print WFH ($lastnum+1)."\n";
close(RFH);
close(WFH);

汚ねぇコードだな!

とりあえず、MacのAutomatorはよくわからないし、変数のインクリメントもちょっと探したくらいじゃ見つからなかったので、最後に変換した文字コードをファイルに置きます。PerlはAutomatorのループごとに呼び出され、ステートが保存されないのでこうするしかないんです。たぶん。

次にAutomatorでコードを組むわけですが、こいつはかなり初心者向けみたいな感じ(というか言語ですらないが)です。で、ビジュアルプログラミングっぽくてコードが無いwのでスクリーンショットを載せます。

シェルスクリプトからPerlを起こし、文字コードに対応する文字をstdoutで吐きます。これをAutomatorの「テキストからバナーイメージを作成」につっこむと、PNGができます。こんなかんじ。

f:id:famnet:20111108002830p:image

できあがり --> font_gen.zip 直 (Perlコードは含まれていない;あと適当にディレクトリとか変更しないと動かない)

いい忘れていた、初期値をいれないと動かん

lastnum.txtに改行なしで「32」とかって数字書いて保存しておかないと、まずPerlが実行されないかも。これを書くとAscii code 33番目の文字からはじめるって意味になる。

このファイルはAutomatorが回るたび(厳密にはPerlコードが起動される度)にインクリメントされる。

いい忘れていた、Mac OS X Lion以降必要かも

Snow Leopardには画像に変換する機能なさそう。

2011-10-06

エロゲーやりながらコード書くために。

エロゲーって無駄に長いですよね。なんで、ぼくはコード書きながらやることにしました。

しかし、ニコニコ動画でアニメ見ながらコードを書いたりするのは全然出来ますが、エロゲーはわざわざEnterを押さなければならない。あるいはオートモードがあるわけですが、主人公にボイスが無かったりすると見逃してしまいます。

そこで、非アクティブウィンドウにEnterキーを送るやつを HSP で書いたので、載せておきます。いいですねHSP。中学生のときにいじってました。ですが、わりとスクリプト言語としてはWin32APIとかも薄く広くラップしてくれているので、重い言語とくらべてサクっと書けますね。

titleっていう変数にEnterイベントを送る先のタイトルバーのテキストを入れます。

#include "hspext.as"

width 200,64,0,0
title = "ヨスガノソラ"
aplsel title
if stat : dialog "could not aplsel: " + title : end
mes "captured: "+refstr

*eventloop
	stick a,0,0
	if a=64 : gosub *send
	wait 1
	goto *eventloop

*send
	aplkeyd 13
	wait 1
	aplkeyu 13
	wait 1
	return

動作確認はこんなかんじ。

動作

沙耶の唄

ヨスガノソラ

Fate/stay night 体験版

だめぽ

STEINS;GATE version1.20

CLANNAD [Full Voice]

智代アフター

Key系のゲームはタイトルバーのテキストが変わるためか、もしくはソフト側のキーボードイベント取得が別の方法かで無理だった。

あと、Firefoxがフォアグラウンドだと、キーイベントが吸収されるっぽくて飛ばなかった。残念。

Ctrlだとわりとコーディングに差し支えるとかだったら、Ctrl+Escとかに割り当てるといいかなーとおもったけど、それはスタートメニューに割り当てられてるんだったな。あんがい難しい。