今更ながら必要だったので作成。
以下のページを参考にさせて頂きました。
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*******');
気が向いたら動画再生ページのパーサー実装しようかな。