カテゴリー別アーカイブ: プログラミング

mkvをmp4に変換するバッチスクリプト

mkv2mp4_flac1

よく使う割には手順が多くて面倒なのでバッチを作った。

h.264 + FLACなmkvファイルをmp4に変換する。
中身のFLACファイルはmp4には入らないので、AACに変換。
可変フレームレート対応。

使い方など
mkv2mp4_flac1 -v 0 -a 1 file.mkv [file2.mkv] [file3.mkv] …
事前に、ファイルのトラック番号をffmpegなどで調べる。
-v < ビデオトラックID> -a < オーディオトラックID>
ファイルは指定しただけ処理される。

続きを読む mkvをmp4に変換するバッチスクリプト

WordPressの検索で検索結果が表示されなくて困った

WordPressの検索をいじっている最中に検索結果が出なくて困った。

pre_get_posts (にフックした関数)をいじっていたんだけれど、全然思ったような結果にならずに結構困っていた。

原因は色々あったんだけれど、二箇所で困ったのでメモしておく。

検索結果がでない、ということはSQLに問題がある。

なのでSQLの中身を確認してなぜ出ないかを検討することに。

以下のダンプをpre_get_postではなく、利用したいテンプレート側に記載。

$wp_query->requestは実行されたSQL文なんだけど、これはpre_get_postsの後に実行されたSQL文なので該当するテンプレートに記載しないと読み出せない。

<span style="line-height: 1.5;">var_dump($wp_query->request);</span>

まず一つはpost_typeが’post’にしかなっていない点。これはpre_get_postsの関数内で

$query->set('post_type', 'post');

を記載すること。これが実は結構やっかいで、他の条件文、例えばtax_queryなどと組み合わす際には無くても何とかなったりするので、条件文を組み立てている最中のデバッグと完成したカタチではpost_typeの指定が必要か不要か、結構迷うことになる。

post_typeに何も指定されていない場合に実際に中身を決定づけているのはwp-includes/query.phpのget_posts内でpost_type = ‘any’として処理していき、状況に応じていろんなのが入る。例えばattachmentとかpostなど。

このget_postsは結構大きなメソッドでこれまた結構読みづらいんだけど、詰まるたんびに読んでいればそのうち読めるようになるかも。

さて次いで検索結果がでない状況に当たったんだけど、これはSQL内に 0 = 1 なんていう文が。例えば下記のような感じに。

string(250) "SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND 0 = 1 AND wp_posts.post_type = 'custom_post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 5"

where句の二つ目にばっちり0 = 1の条件が。おかげさまで検索結果はゼロに。

wp-includes/query.phpのget_postsを丁寧に調べていくと下記の部分で付け加えられていた。

// Taxonomies
 if ( !$this->is_singular ) {
 $this->parse_tax_query( $q );

$clauses = $this->tax_query->get_sql( $wpdb->posts, 'ID' );

$join .= $clauses['join'];
 $where .= $clauses['where'];
 }

ここのtax_query->get_sql() の部分。

今回このpre_get_postsではtax_queryを使用していなかったのでどうやらそれが原因らしい。

結果としては今回対象となるページが http://www.example.com/search/ なんていう検索用のURLだったんだけど、searchを普通のpostのcategoryとして見做していたようで、get_posts内の$this->tax_queryという$qを元に作られたタクソノミクエリでは

object(WP_Tax_Query)#9 (2) {
 ["queries"]=>
 array(1) {
 [0]=>
 array(5) {
 ["taxonomy"]=>
 string(8) "category"
 ["terms"]=>
 array(1) {
 [0]=>
 string(6) "search"
 }
 ["include_children"]=>
 bool(true)
 ["field"]=>
 string(4) "slug"
 ["operator"]=>
 string(2) "IN"
 }
 }
 ["relation"]=>
 string(3) "AND"
}

という状態に。

$qや$parsed_query、$wp_queryを調べてみると’category_name’に’search’が設定されていた。

これはパーマリンク設定で記事名を/%category%/%postname%/のようにしていたことで、最初のスラッシュからスラッシュの間をカテゴリ名として認識していたから、のようだ。

なのでpre_get_postsの中で

$query->set('category_name', '');

としてやることで不必要なtax_queryを省くことができた。

wgetでベーシック認証をかける方法

こんにちは、最近スノボによく行っている岡田洋一です。

スノボに行ったりしている間はサーバが自動で処理をしてくれたりする機能、いわゆるバッチ処理があるんですが、こちらがエラーになっていたのでその対処。

shell> wget -O - http://www.example.com/action.php
--2014-01-22 19:23:10-- http://www.example.com/action.php
Resolving www.example.com (www.example.com)... 192.168.1.1
Connecting to www.example.com (www.example.com)|192.168.1.1|:80... connected.
HTTP request sent, awaiting response... 401 Authorization Required
Authorization failed.

見ればベーシック認証でコケている様子。前はどこからでもアクセスできたんですが、今はアクセス制限をしているのでその対応ですね。

wgetのオプションを調べると –userと–passwordで設定する様子。

shell> wget -O - --user=username --password=somepassword http://www.example.com/action.php

HTTP request sent, awaiting response... 401 Authorization Required
Reusing existing connection to www.example.co:80.
HTTP request sent, awaiting response... 200 OK
Length: 11206 (11K) 
Saving to: `STDOUT'

というわけでうまくいきました。

複数人開発の時にデータベースの同期をうまく取る方法

最近「意外に若かったんだね」と言われるようになりました、その言葉の指すところについては考えないようにしています岡田洋一ですコンニチハ。

さて、僕はしばらくチームで開発を行っています。大人数というわけでも無く、2〜3人での小規模プロジェクトチームです。少人数制のチームではありがちで、僕以外のメンバーもプログラム〜インフラ周りも修正することが多く、データベースのテーブル構造を修正することが各自にありえます。

さてそんなときにデータベースの同期を取る方法について考えてみました。

現状はテーブルのCREATE分を出力させ、プロジェクトのwikiに書き込んでいます。

wikiは変更情報を保存しますので、ある程度のバージョン管理ができました。が、いかんせん「出力、編集、保存、確認、伝達、適用」と、修正内容をチーム内で共有するためのステップが多いのです。少人数チームでステップが多いのは良くないですね。素早さこそがウリですから。

次に「テーブルの差分になるALTERのみを書く」ということについて勘案しました。が、こちらも開発を兼ねているため、テーブルの変更となるALTER分はそこそこの数が出てしまい、すべてのALTERを記録して適用することは面倒となりました。素早さがなくなってしまいます。

 

というわけで、現在のテーブル情報を書き出しつつバージョン管理システムで共有、という手段にしました。

現在プロジェクトはSVNで管理されていたので、テーブル情報のみをファイルに書き出してコミット、それを手元へ適用という手段をとりました。ちなみにデータベースはMySQLです。

テーブル情報のみを書き出す為に必要なコードは

shell> mysqldump -h mysqlhost -u user -ppassword --no-data target_schema | sed 's/ AUTO_INCREMENT=[0-9]*//' > tables.sql

shell> svn commit tables.sql -m 'update table sql.'

となりました。mysqlのホスト名、ユーザ名、パスワード、対象スキーマは読み替えて下さい。パスワードはナシにするとプロンプトで聞いてきます。

パイプでsedに渡してAUTO_INCREMENTを消しています。

最後にこの出力をファイルに書き出して、コミットして共有します。

 

これだと差分をうまく見ることができるので便利ですね!

WordPressのテーマ/テンプレート開発の時に役に立つコード

ダイエットが終わらない岡田洋一です、コンニチハ。

さて、最近ひょんなことからWordPressのテンプレートを修正する機会がありました。WordPressはで見た目をいじるときにはテーマ(テンプレート)ファイルを編集する必要があります。

WordPressでのページ表示には色々な仕組みがあるのですが、今回僕が編集することになったのは「カスタム投稿タイプのカスタムタクソノミーのアーカイブ」だったんです。この辺りについてはテンプレートヒエラルキーなんていう聞き慣れない仕組みが関わってきています。

さて、このテンプレートヒエラルキーというのは、簡単に言ってしまえば「テンプレートを適用する順序」を決めるモノなのです。「カスタムタクソノミーのアーカイブ」であれば「taxonomy-archive.php」とかになるのですが、これがなかなかうまく見つけられなかったりします。

さてこんな時、開発を行うにあたっては、現在どのテンプレートファイルを表示しているか、というのが分かると非常に助かります。

今回はこの「どのテンプレートファイルで表示しているか」を調べるためのコードを紹介します。

下記のコードを header.php の好きな位置に埋めて下さい。

<?php if(is_user_logged_in()) : ?></span>

<!-- <?php global $template; print_r($template); ?> -->
 <?php endif; ?>

Screen Shot 2013-10-01 at 4.48.45 PM

こうすることでHTMLのヘッダにコメントとして、「表示に使用しているテンプレート」が書き込まれます。

ちなみに if文で「ログインしている状態にのみ」に表示されるようになっていますので、知らない人には表示されません!ちょっと安心ですね。

Screen Shot 2013-10-01 at 4.55.28 PM

これでどのテンプレートを使っているか迷わずに開発ができますね!

.484 slugging percent but also an.876 operations. However in the other half

Stay away from a Bryce Harper MVP

Bryce Harper actually holds NL MVP voters an uncomplicated possibility. He was always the appropriate player carrying out work more than competitor, Individuals are trustworthy excellent lastly omit to associated with playoffs. Pollock, And furthermore Cincinnati’s Joey Votto.

Votto, Particularly, Is usually stimulating. He was a great player using the first half plus a.277 standard,.392 in bottom level proportionate amount,.484 slugging percent but also an.876 operations. However in the other half, Fresh submitted statistics truth best player in smallish little group a great aluminium softball baseball bat might not practice:.397/.575/.718/1.293. We have been debating climbing on bottom level very near to 60 portion of times.

Except he is accomplishing this covering the situation having to do with comment not for rivalry the grays took the subway on the bad business in the first half(.453 back again %) So that you terrible(.360). Not surprisingly, Votto not really a contributing factor Cincy isn’t good. Despite the fact that since yellows can be harmful, Votto haven’t shot whatever at all coming a great enjoyable, Pressurised at softball baseball bat. Should certainly that can seem planned vs. the,

リバースプロキシを使っているとWordPressで記事の参照がループしてしまう問題について

0. WordPressで記事の参照がループしてしまう問題について

WordPressで記事ページを開いたときに無限ループしてしまう。記事ページはいわゆるsingle.php シングルというページ。ブラウザの表示は

“このウェブページにはリダイレクト ループが含まれています” (chrome)
“ページの自動転送設定が正しくありません” (FireFox)

といった感じでループがおかしいという事を告げられる。
開発ツールで転送設定を見てみると、確かに同じアドレス宛に301の “Moved Permanently” が連続して返却されている。
このままでは利用者が入って来れないので調査をしてみた。

1

1. WordPressでのリダイレクト

WordPressで転送はwp-includes/canonical.phpというファイルが担当する。
このファイルはリクエストURLを見てカスタムパーマリンク設定などに照らし合わせて最適なURLへ誘導してくれる機能を持っている。

例えばパーマリンクを投稿名にしているとき、 http://www.example.com/?p=123 というURLへアクセスがあった場合、
http://www.example.com/test-post/ というパーマリンクへ301(こっちのURLが正しいですよ、というブラウザへの命令)などのコードで転送してやるのだ。

他にも http://www.example.com/test-pos/ なんていうのを http://www.example.com/test-post/ へ転送することも行っている。(推測です)

こういったことを担当しているcanonical.phpなんだけれど、その中身はちょっと複雑。主に301系の転送を担当してはいるものの、いろいろな処理が入り乱れている。

2. 問題点

そこでこの処理を超ナナメ読みして今回の問題点を洗い出すとこうだ。
以下に二つのURLがある。
パーマリンクを元に作成された、記事自体のパーマリンク。リダイレクトしたいURLを redirect_url と呼ぶ。
リダイレクトされる前の、実際に今アクセスされているURLを requested_url とする。
この二つが一致しないので延々とredurect_urlにリダイレクトさせようとしてしまっている。

canonical.phpにあるredirect_canonicalという関数。すでにかなーり縦長で読みにくい上、一度だけ自身を呼び出すプチ再帰になってるのでこれまたわかりにくい。中にデバッグ用のコードがコメントアウトされて配布されてるところをみても苦労が窺える。
canonical.phpにあるredirect_canonicalという関数。すでにかなーり縦長で読みにくい上、一度だけ自身を呼び出すプチ再帰になってるのでこれまたわかりにくい。
中にデバッグ用のコードがコメントアウトされて配布されてるところをみても苦労が窺える。

普通の情況であれば一度リダイレクトされればredirect_urlはrequested_urlとなるんだけれど、今回の問題は違った。
じつはこのrequested_urlは$_SERVERを元に作成される。サーバ環境変数。つまり見せかけ上のURL(ドメイン名)と実際にPHPが動いているドメイン名が違っていると無限ループする、というわけだ。
この見せかけ上のドメインとPHPが動いているサーバのドメイン名が違う、というのはリバースプロキシを使っていると起こってしまう。
リバースプロキシは大規模なウェブサイトになると負荷分散の為に用いられる手法だ。

今回このサイトではapacheのmod_proxyを利用した簡単なリバースプロキシを噛ましていた。
手前で動いていてWordPressに登録されているドメイン名はwww.example.comなのに、背後のウェブサーバではwww1.example.comという具合にドメイン名が違う。
なのでredirect_urlとrequested_urlが延々と違う、ということになってしまったのだ。

3. 解決方法

この問題を解決するにはいくつかの方法がある。
一つはcanonical.phpを書き換えてしまう方法。
requested_urlが$_SERVERから取得しているので該当部分を書き換えてしまえば良い。
ただしWordPressのコアファイルを書き換える、というのは禁断の果実を食べるに等しいのであまりオススメしない。

次にフィルタを書いてやる、という方法。redirect_url側をrequested_url側のドメイン名に揃えてやる、というやり方。これならば問題は起きにくいだろう。

// Note that you can use the "redirect_canonical" filter to cancel a canonical redirect for whatever reason by returning false
    $redirect_url = apply_filters('redirect_canonical', $redirect_url, $requested_url);

というわけでridirect_urlを書き換えるフィルタを書いてみました。これをfunctions.phpに加えておけばURLのドメイン部分がリバースプロキシの奥側のドメインに一致します。

add_action('redirect_canonical', 'change_requested_url');
function change_requested_url($redirect_url, $requested_url) {
 $redirect_url = is_ssl() ? 'https://' : 'http://';
 $redirect_url .= $_SERVER['HTTP_HOST'];
 $redirect_url .= $_SERVER['REQUEST_URI'];
 return $redirect_url;
}

今回のサイトでは複雑なクエリの組み立てなども行っていないのでこれで問題なさそうです。元のコードは先ほどのcanonical.phpから拝借してきました。

今回は見事、上記のフィルタ動作にてうまく動きました。めでたしめでたし。

ブログの記事が見つからなかったときトップページに飛ばす方法

WordPressの記事が見つからなかったときにトップページに飛ばす方法について。404 on chrome

WordPressで記事を書いたり作ったりしていると記事が無いことがある。原因は主に二通りで、一つはリンク先のアドレスを間違えてしまったリンクミス、もう一つは過去には存在したけれど削除されてしまったケース。

これらのケースにおいてWordPressではどんな対応を取るべきなだろうか。

WordPressではデフォルトで404.phpというテーマファイルがくっついてくる。
これはHTTP Status Codeが404の場合、つまり記事が見つからなかった場合に表示されるページのテーマだ。
“おっと、失礼しました。”と表示されるのはこの404.phpが表示されるケースだ。

ブログの運営方針にもよるが、今回は記事が見つからなかった場合にトップページに飛ばすことにした。
ここで注意しなければいけないことが二点。
一つはトップページに“飛ばす”ということ。実はテンプレートヒエラルキーなんていうムズカシイ言葉があって、404.phpを削除するだけで”記事が見つからなかった場合にトップページを表示“させることができる。このやり方だと見た目はトップページなんだけれどURLが無くなった記事そのままになってしまう。これはダサい。
注意点の二つ目はHTTP Status Code。HTTPステータスコードというのはブラウザに送る機械的な意味だ。良く目にする404というのは”記事が見つかりませんでした”という意味を確実に的確に機械的にブラウザに伝えるための番号なのだ。
さて実はこのHTTPステータスコードなんだけれど、その確実性ゆえ、検索エンジンにも番号は理解される。つまり記事ID123番のページに検索エンジンのボットが来た、でも記事がないよーというとき、404を返しておけば「このページは削除されたんだね」と解釈してくれる。(厳密にはそうで無い場合もあるのだけれど)
なので極力このステータスコードを返してあげた方が良い。

というわけでfunctinons.phpあたりにこのへんをまとめて書いてみた。

add_action('wp_head', 'redirectToIndex');
function redirectToIndex() {
if (is_404()) {
header('HTTP/1.0 404 Not Found');
header('Location: '.get_bloginfo('url').'/');
}
}

これで404を送りつつLocationを指定してページをジャンプさせることができる。Locationの行を変えることでジャンプ先を任意のページにすることもできる。

 

おわりに
HTTPステータスコードはなんだかなじみが無くてなかなか利用価値がわからなかったりするんだけれど、例えばサーバの負荷を減らすためのキャッシュの制御だったりだとか、一時的なメンテナンスを通知したりだとか、そういった用途に使える。
とくにメンテナンス通知に使われる503は日時指定もできて何時何分ごろには復旧しますよー、なんてこともできたりする。
検索エンジンにメンテナンス画面がキャッシュされてしまうことも防げたり、これは防げないこともあったり。でも気を遣うに超したことはないよね。

 

というわけで404をリダイレクトでジャンプさせる、でした。

CakePHPでセションメッセージ、フラッシュメッセージを二度以上使いたかった

CakePHPでセションメッセージを二度以上使いたかった。

CakePHPでセションを使ってメッセージを表示することができる。通常はエラーメッセージを表示させたりするためにeditやaddなどでエラーメッセージを収めておき、リダイレクト先のviewでflush()を呼んで表示している。

便利な機能なのだけれど、setFlashという名前の通り、このメッセージは一度限り設定できる。コントローラが大きくなってくるとセションメッセージの設定位置が散らばったりするため、意図せずしてセションメッセージを後から上書きしてしまうことになってしまう。今回はこの問題を調べてなんとかしてみた。

1. やりたいこと
下のようなコードがあったとする。

Class HogeController extends AppController {
public function action() {
$this->Session->setFlash('hoge');
$this->Session->setFlash('fuga');
}
}

このとき、actionのviewでflush()でセションのメッセージを呼び出したとき、hogeは無くなってしまってfugaが表示される。
今回はここで何とかして hogefuga という風に連結したメッセージを表示させてみる。

$this->Session->setFlash('hoge');
$flashMessage = $this->Session->read('Message.flash');
$this->Session->setFlash($flashMessage['message'] .= 'fuga');

これでセションのフラッシュメッセージを追加できる。この場合だと hogefuga が表示される。

こうすると動かせるようになるのは以下の理由。
SessionComponent.phpにあるsetFlashのメソッドの定義を見てみる。

public function setFlash($message, $element = 'default', $params = array(), $key = 'flash') {
CakeSession::write('Message.' . $key, compact('message', 'element', 'params'));
}

setFlashというのはCakeSessionのwriteメソッドを呼んでいる。このメソッドの定義はCakeSession.phpにある。このクラスにはwriteの他にreadもある。このreadを使って上記のように取り出して追記している。

public static function read($name = null) {
if (!self::started() && !self::start()) {
return false;
}
if (is_null($name)) {
return self::_returnSessionVars();
}
if (empty($name)) {
return false;
}
$result = Hash::get($_SESSION, $name);

if (isset($result)) {
return $result;
}
self::_setError(2, "$name doesn't exist");
return null;
}
public static function write($name, $value = null) {
if (!self::started() && !self::start()) {
return false;
}
if (empty($name)) {
return false;
}
$write = $name;
if (!is_array($name)) {
$write = array($name => $value);
}
foreach ($write as $key => $val) {
self::_overwrite($_SESSION, Hash::insert($_SESSION, $key, $val));
if (Hash::get($_SESSION, $key) !== $val) {
return false;
}
}
return true;
}

readとwriteするときにはキーによって制御している。このキーはsetFlashを使ったときには ‘Message.flash’ が用いられている。なのでこれを手動でreadして改めて連結してsetFlashしている。

これでセションのメッセージを追記することができる。
なにか別名を与えてsetFlashの代わりにこれを全部使っても良いかなと思うのだけど、しばらく様子をみてからにしよう。

emacsのphp-modeでarrayのインデントを改善した

1. はじめに

emacsでphp-modeを利用している。とても便利なelispなんだけどデフォルトだといくつか不便な点がある。

基本的にはPEARに準拠したコーディング規約にしたかったので下記のブログ記事を参考にしてinit.elの設定を行った。

(setq php-mode-force-pear t)

PHP開発で追加しておきたいEmacs Lisp 8選 : アシアルブログ

だけれどもこれだけだとarrayのインデントがおかしい。

2. おかしいインデント

<?php
function hoge(){
    $hoge = array(
                 'hoge' => 1,
    );
}

このように連想配列の添え字(キー名)のインデント位置が前述の括弧の位置まで下がってしまうのだ。 コレは見づらい。できれば $hoge の開始位置から規定のタブ幅(4とか)で字下げして欲しい。

2. 解決方法は見かけるのだけれど

この問題でググってみるといくつか解決方法が出てきた。だけれどもその辺のelispを適用してみてもうまくいかない。バージョンがダメなのか他のelispと競合しているのか、とにかく動かなかった。というかelispに対する理解が少ないというのが一番の根本的な問題かな。

で、しばらくこの問題は放置していたのだけれど、あまりにも気になると言うことで気合いを入れて調べてみた。すると意外にも(?)公式的なみんな大好き emacswiki が出てきた。

3. 解決

Indentation of arrays
The php-mode indentation for array is not good, It will indent like this:

$post=Post::model()->find(array(
                              'select'=>'title',
                              'condition'=>'postID=:postID',
                              'params'=>array(':postID'=>10),
                              ));

The better indentation is like:

$post=Post::model()->find(array(
    'select'=>'title',
    'condition'=>'postID=:postID',
    'params'=>array(':postID'=>10),
));

Add this to php-mode-hook:

(add-hook 'php-mode-hook (lambda ()
    (defun ywb-php-lineup-arglist-intro (langelem)
      (save-excursion
        (goto-char (cdr langelem))
        (vector (+ (current-column) c-basic-offset))))
    (defun ywb-php-lineup-arglist-close (langelem)
      (save-excursion
        (goto-char (cdr langelem))
        (vector (current-column))))
    (c-set-offset 'arglist-intro 'ywb-php-lineup-arglist-intro)
    (c-set-offset 'arglist-close 'ywb-php-lineup-arglist-close)))

EmacsWiki: Php Mode Indentation of arrays

さて早速コレを適用。init.elを開いて、php-completionですでにphp-mode-hookがあったのでそこに追記して再起動。

うまくいきました。

<?php
function hoge(){
    $hoge = array(
        'hoge' => 1,
    );
}

めでたしめでたし。