今更ながら必要だったので作成。
以下のページを参考にさせて頂きました。
http://istb16.wordpress.com/2011/09/11/niconico_download_for_php/
goto構文使ってるのでphp5.3以上じゃないと動作しません。
<?php
/**
* ニコニコ動画保存用クラス
*
* $nico = new niconicoMovieDownloader();
* $nico->email = 'ログインメールアドレス';
* $nico->password = 'ログインパスワード';
* $nico->saveDir = '動画保存用ディレクトリ'; // 末端DS無し
* $nico->tmpDir = '作業用ディレクトリ'; // 末端DS無し
* $res = $nico->download('sm*******');
*
* @author Owner
*
*/
class niconicoMovieDownloader {
// ログイン用メールアドレス
public $email = '';
// ログインパスワード
public $password = '';
// 動画保存ディレクトリ(末端スラッシュ無し)
public $saveDir = '';
// 一時作業ディレクトリ(末端スラッシュ無し)
public $tmpDir = '';
// 動画ID(sm****)
protected $mid = '';
// 送信ヘッダーの入れ物
protected $headers = array();
// エラーメッセージの入れ物
protected $errors = array();
// input要素の名前設定(メールアドレス欄)
protected $formNameEmail = 'mail_tel';
// input要素の名前設定(パスワード欄)
protected $formNamePassword = 'password';
// input要素の名前設定(移動先URL欄)
protected $formNameNextUrl = 'next_url';
// ニコニコ動画ログインポストURL。間違えると動画ページを取得できないので注意。
protected $loginUrl = 'https://secure.nicovideo.jp/secure/login?site=niconico';
// 動画ページのURL設定
protected $watchUrl = 'http://www.nicovideo.jp/watch';
// ダウンロードURLを割り出すAPIの設定
protected $apiUrl = 'http://flapi.nicovideo.jp/api/getflv';
/**
* インスタンス生成時、送信ヘッダーを設定
*/
public function __construct() {
$this->headers[] = 'Connection: keep-alive';
$this->headers[] = 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
$this->headers[] = 'User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36';
$this->headers[] = 'Referer: http://www.nicovideo.jp/';
$this->headers[] = 'Accept-Language: ja,en-US;q=0.8,en;q=0.6';
}
/**
* ダウンロードの実行
* @param string $mid
* @return multitype:|boolean
*/
public function download($mid = null) {
$this->mid = $mid;
$this->checkParams();
if(!empty($this->errors)) return $this->errors;
// cookieを持ちまわすためのファイルパスを指定
$tmpCookiePath = $this->tmpDir . DIRECTORY_SEPARATOR . '_cookie';
$tmpWorkPath = $this->tmpDir . DIRECTORY_SEPARATOR . md5(microtime());
$headerFilePath = $this->tmpDir . DIRECTORY_SEPARATOR . 'h_' . $this->mid;
self::tp($tmpCookiePath);
self::tp($tmpWorkPath);
self::tp($headerFilePath);
$params = array(
$this->formNameEmail => $this->email,
$this->formNamePassword => $this->password,
$this->formNameNextUrl => $this->watchUrl . DIRECTORY_SEPARATOR . $this->mid
);
$ch = curl_init();
//curl_setopt($ch, CURLOPT_HTTPHEADER, $this->headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, $tmpCookiePath);
//curl_setopt($ch, CURLOPT_COOKIEFILE, $tmpCookiePath);
curl_setopt($ch, CURLOPT_URL, $this->loginUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
$output = curl_exec($ch);
$params = array('v' => $this->mid);
curl_setopt($ch, CURLOPT_URL, $this->apiUrl);
curl_setopt($ch, CURLOPT_HTTPGET, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
$output = curl_exec($ch);
// DLリンクの解析
preg_match("'url=(.*?)&link'", urldecode($output), $m);
if(empty($m)) goto exception;
curl_setopt($ch, CURLOPT_POSTFIELDS, array());
curl_setopt($ch, CURLOPT_URL, $m[1]);
// 動画DLセクション
$execResult = false;
$hdFp = fopen($headerFilePath, 'w');
if($hdFp) {
//プロセス実行中でも書き込んでくれるWRITEHEADERを利用
curl_setopt($ch, CURLOPT_WRITEHEADER, $hdFp);
$twFp = fopen($tmpWorkPath, 'wb');
if($twFp) {
curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
curl_setopt($ch, CURLOPT_FILE, $twFp);
$execResult = curl_exec($ch);
curl_close($ch);
fclose($twFp);
}
fclose($hdFp);
}
if(!$execResult) goto exception;
// headerFileの解析(ファイルタイプ、拡張子を取得)
$contentType = self::parseHeaderFile($headerFilePath);
$ext = self::getFileExtension($contentType);
// 実ファイルのパスを指定(お名前指定)
$filePath = $this->saveDir . DIRECTORY_SEPARATOR . $this->mid . $ext;
// ファイル移動
rename($tmpWorkPath, $filePath);
// 作業ファイル削除
self::u($tmpCookiePath);
self::u($tmpWorkPath);
self::u($headerFilePath);
return true;
if(false) {
exception:
self::u($tmpCookiePath);
self::u($tmpWorkPath);
self::u($headerFilePath);
curl_close($ch);
return false;
}
}
/**
* ファイルの存在確認と作成、及び権限の付与
* @param unknown $path
*/
protected function tp($path) {
if(!file_exists($path)) touch($path);
chmod($path, 0777);
}
/**
* ファイルの存在確認と削除
* @param unknown $path
*/
protected function u($path) {
if(file_exists($path)) unlink($path);
}
/**
* パラメーターチェック
*/
protected function checkParams() {
if(!self::validateEmail($this->email)) array_push($this->errors, 'please check your email address.');
if(empty($this->password)) array_push($this->errors, 'password is required but not set.');
if(!is_dir($this->saveDir)) array_push($this->errors, 'saveDir must be a directory.');
if(fileperms($this->saveDir) != '16895') array_push($this->errors, 'saveDir required to be permission 777.');
if(!is_dir($this->tmpDir)) array_push($this->errors, 'tmpDir must be a directory.');
if(fileperms($this->tmpDir) != '16895') array_push($this->errors, 'tmpDir required to be permission 777.');
if(empty($this->mid)) array_push($this->errors, 'mid is required but not set.');
if(!self::validateMid($this->mid)) array_push($this->errors, 'ID starting from nm is not allowed (sm****** only).');
}
/**
* メールアドレスのバリデーション
* @param unknown $check
* @return boolean
*/
protected function validateEmail($check) {
$hostname = '(?:[_a-z0-9][-_a-z0-9]*\.)*(?:[a-z0-9][-a-z0-9]{0,62})\.(?:(?:[a-z]{2}\.)?[a-z]{2,})';
$regex = '/^[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+)*@' . $hostname . '$/i';
if (preg_match($regex, $check)) return true;
return false;
}
/**
* 動画IDの正規バリデーション
* @param unknown $check
* @return boolean
*/
protected function validateMid($check) {
$regex = '/^(sm)[0-9]+$/';
if (preg_match($regex, $check)) return true;
return false;
}
/**
* ヘッダーファイルの情報を解析
* @param unknown $path
* @return Ambigous <NULL, string>
*/
protected function parseHeaderFile($path) {
$hd = file_get_contents($path);
$ct = null;
$k = 'Content-Type:';
$st = stripos($hd, $k);
if($st !== false) {
$st += strlen($k);
$ed = strpos($hd, "\n", $st);
if($ed === false) $ed = strlen($hd);
$l = $ed - $st;
$ct = strtolower(trim(substr($hd, $st, $l)));
}
return $ct;
}
/**
* コンテントタイプより動画ファイルの拡張子を分析し返却
* @param unknown $ct
* @return string
*/
protected function getFileExtension($ct) {
$e = '';
switch($ct) {
case 'video/3gpp':
$e = '.3gp';
return $e;
case 'video/mp4':
$e = '.mp4';
return $e;
case 'video/x-flv':
default:
$e = '.flv';
return $e;
}
}
}
コメントアウトにもあるけど、使い方は以下の通り。
$nico = new niconicoMovieDownloader();
$nico->email = 'ログインメールアドレス';
$nico->password = 'ログインパスワード';
$nico->saveDir = '動画保存用ディレクトリ'; // 末端DS無し
$nico->tmpDir = '作業用ディレクトリ'; // 末端DS無し
$res = $nico->download('sm*******');
気が向いたら動画再生ページのパーサー実装しようかな。