const FLV_EXTENSION = "flv"; const MP4_EXTENSION = "mp4"; const SWF_EXTENSION = "swf"; const FLV_CONTENT_TYPE = "video/flv"; const MP4_CONTENT_TYPE = "video/mp4"; const SWF_CONTENT_TYPE = "application/x-shockwave-flash"; /* タイトルの編集 ex: videoID smXXXXXXX videoTitle ~てみた */ var makeVideoTitle = function(videoID, videoTitle) { // var title = videoTitle; /* Example */ var title = videoID+"."+videoTitle; // var title = videoTitle+"_"+videoID; return title; } var ucjsNicovideoDownloader = { init: function(){ var contentAreaContextMenu = document.getElementById("contentAreaContextMenu"); if (!contentAreaContextMenu){ return; } // コンテキストメニューにダウンロード用のアイテム追加 this._menuItem = document.createElement("menuitem"); this._menuItem.setAttribute("label", "Download FLV/MP4/SWF"); contentAreaContextMenu.insertBefore(this._menuItem, contentAreaContextMenu.firstChild); contentAreaContextMenu.addEventListener("popupshowing", function(){ ucjsNicovideoDownloader.onPopupShowing(this); }, false); }, onPopupShowing: function(aPopup){ this._menuItem.hidden = true; // リンク上のクリックならメニューを表示しない Ituki add. // リンク上でもメニューを開くなら次の行を//でコメントアウト if (gContextMenu.onLink) return; // nicovideo.jp かチェック var location = content.document.location; if(location.href.indexOf("nicovideo.jp/watch/") == -1 && location.href.indexOf("nicovideo.jp/watch_result/") == -1 ) return; // Video ID とタイトルの取得 /* var videoID = content.document.evaluate('.//object[@id="flvplayer"]/param[contains(@value,"flvplayer.swf")]',content.document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null); if(!videoID.singleNodeValue) return; videoID = videoID.singleNodeValue.value; if(!videoID.match(/flvplayer\.swf\?v=(\w+)/)) return; var videoID = RegExp.$1; */ var videoID = /nicovideo\.jp\/(watch|watch_result)\/(\w+)/.exec(location.href)[2]; /* change by いつき */ // ?パラメータオプション取得 var videoIDopt = ""; if (/nicovideo\.jp\/(watch|watch_result)\/[^\?]+\?(.+)/.exec(location.href)){ videoIDopt = /nicovideo\.jp\/(watch|watch_result)\/[^\?]+\?(.+)/.exec(location.href)[2]; } // いつき change http://blog.proj.jp/ituki/20080331.html#p15 //var title = content.document.evaluate('.//div/h1/a/text()',content.document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null); var xtitle = ""; if(location.href.indexOf("nicovideo.jp/watch_result/") != -1){ xtitle = content.document.evaluate('.//h1',content.document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null); }else{ xtitle = content.document.evaluate('.//h1/a[@class="video"]',content.document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null); } if(!xtitle.singleNodeValue) return; var title = String(xtitle.singleNodeValue.textContent); var videoTitle = makeVideoTitle(videoID,title).replace(/[\$\*\?\"\'\/\\\:]/g, "_"); // メニューの有効化 /* change by いつき */ this._menuItem.setAttribute("oncommand", "ucjsNicovideoDownloader.download('"+ videoTitle +"','"+ videoID +"','" + videoIDopt + "')"); this._menuItem.hidden = false; }, download: function(aTitle, aVideoID,aVideoIDOpt){ //window._content.document.location = "about:blank"; var downloader = new ucjsNicovideoDownloader.downloader(); downloader.download(aTitle, aVideoID,aVideoIDOpt); } } ucjsNicovideoDownloader.predownloader = function(){ this.SKIP_SELECT_PROMPT = true; } ucjsNicovideoDownloader.predownloader.prototype = { download: function(aTitle, aVideoID,aVideoIDopt){ this._title = aTitle; this._videoID = aVideoID; this._videoIDopt = aVideoIDopt; this._state = 0; // 空白ページへジャンプ //window._content.document.location = "about:blank"; this._ioService = Components.classes["@mozilla.org/network/io-service;1"] .getService(Components.interfaces.nsIIOService); this.wrappedJSObject = this; // URLをつつく var nicovideoURLSpec = "http://www.nicovideo.jp/watch/" + aVideoID; /* change by いつき */ if (aVideoIDopt != ""){ // オプション付加 nicovideoURLSpec += "&" + aVideoIDopt; } var nicovideoURL = this._ioService.newURI(nicovideoURLSpec, null, null); var httpChannel = this._ioService.newChannelFromURI(nicovideoURL) .QueryInterface(Components.interfaces.nsIHttpChannel); httpChannel.requestMethod = "GET"; httpChannel.redirectionLimit = 0; httpChannel.asyncOpen(this.getListener(), this); }, onStopRequest: function(aRequest, aContext, aStatus){ if (Components.isSuccessCode(aStatus)) { var downloader = new ucjsNicovideoDownloader.downloader(); downloader.download(this._title, this._videoID,this._videoIDopt); }else{ alert("ucjsNicovideoDownloader\n(pre) subRequest Fail"); } }, getListener: function(){ var listener = { onStartRequest: function(){}, /* データはいらないので onDataAvailableの中身 は無し */ onDataAvailable: function (aRequest, aContext, aistream, offset, count){}, onStopRequest: function(aRequest, aContext, aStatus){ aContext.wrappedJSObject.onStopRequest(aRequest, aContext, aStatus); } } return listener; } } ucjsNicovideoDownloader.downloader = function(){ this.SKIP_SELECT_PROMPT = true; } ucjsNicovideoDownloader.downloader.prototype = { download: function(aTitle, aVideoID,aVideoIDopt){ this._title = aTitle; this._state = 0; this._ioService = Components.classes["@mozilla.org/network/io-service;1"] .getService(Components.interfaces.nsIIOService); this.wrappedJSObject = this; this.streamData = ""; // Video ID から Player の URL を取得 var nicovideoURLSpec = "http://www.nicovideo.jp/api/getflv?v=" + aVideoID; /* change by いつき */ if (aVideoIDopt != ""){ // オプション付加 nicovideoURLSpec += "&" + aVideoIDopt; } var nicovideoURL = this._ioService.newURI(nicovideoURLSpec, null, null); var httpChannel = this._ioService.newChannelFromURI(nicovideoURL) .QueryInterface(Components.interfaces.nsIHttpChannel); httpChannel.requestMethod = "GET"; httpChannel.redirectionLimit = 0; httpChannel.asyncOpen(this.getListener(), this); }, onDataAvailable: function(aRequest, aContext, aistream, offset, count){ aRequest.QueryInterface(Components.interfaces.nsIHttpChannel); if(aRequest.responseStatus != 200){ alert("ERROR: ucjsNicovideoDownloader"); return; } var sstream = Components .classes ["@mozilla.org/scriptableinputstream;1"] .createInstance (Components.interfaces .nsIScriptableInputStream); sstream.init (aistream); /* change by いつき */ /* result = sstream.read (-1); */ this.streamData += sstream.read (-1); sstream.close (); /* change by いつき */ /* ここでやってしまうと、http://www.nicovideo.jp/watch/sm2438317 などの長いresponseを返してくるときに複数回呼ばれる */ /* var p = {}; for each(var a in result.split("&")) { if(typeof a != "string") break; var t = a.split("="); p[t[0]] = t[1]; } var url = unescape(p["url"]); this.saveVIDEO(this._title, url); */ }, /* change by いつき */ /* 参考文献 http://developer.mozilla.org/ja/docs/Creating_Sandboxed_HTTP_Connections */ onStopRequest: function(aRequest, aContext, aStatus){ if (Components.isSuccessCode(aStatus)) { var result = this.streamData; this.streamData = ""; var p = {}; for each(var a in result.split("&")) { if(typeof a != "string") break; var t = a.split("="); p[t[0]] = t[1]; } var url = unescape(p["url"]); this.saveVIDEO(this._title, url); }else{ alert("ucjsNicovideoDownloader\nsubRequest Fail"); this.streamData = ""; } }, getListener: function(){ var listener = { onStartRequest: function(){}, onDataAvailable: function (aRequest, aContext, aistream, offset, count){ aContext.wrappedJSObject.onDataAvailable(aRequest, aContext, aistream, offset, count); }, onStopRequest: function(aRequest, aContext, aStatus){ aContext.wrappedJSObject.onStopRequest(aRequest, aContext, aStatus); } } return listener; }, saveVIDEO: function(aTitle, aURLSpec){ var VIDEO_EXTENSION = FLV_EXTENSION; var VIDEO_CONTENT_TYPE = FLV_CONTENT_TYPE; // Thanks to いつき http://blog.proj.jp/ituki/20080305.html#p02 var media_type = "v"; if (/\.nicovideo\.jp\/smile\?(.)\=/.exec(aURLSpec)){ // smile video media_type = RegExp.$1; }else if (/\.nicovideo\.photozou\.jp\/.*\.([A-Za-z0-9\-\_]+)$/.exec(aURLSpec)){ var ext = RegExp.$1; if (ext == "flv"){ // media_type = "v"; }else{ alert("ucjsNicovideoDownloader\nphotozou.jp: Unknown type: URL = [" + aURLSpec + "]\nDefault Mode Download FLV"); } }else{ alert("ucjsNicovideoDownloader\nUnknown type: URL = [" + aURLSpec + "]\nDefault Mode Download FLV"); } if ( media_type ){ if ( media_type == "m" ){ // 多分mp4 download VIDEO_EXTENSION = MP4_EXTENSION; VIDEO_CONTENT_TYPE = MP4_CONTENT_TYPE; }else if( media_type == "s" ){ // 多分swf download VIDEO_EXTENSION = SWF_EXTENSION; VIDEO_CONTENT_TYPE = SWF_CONTENT_TYPE; }else if( media_type == "v" ){ // 多分flv デフォルトなので変更無し }else{ alert("ucjsNicovideoDownloader\nUnknown type: media_type = [" + media_type + "]\nDefault Mode Download FLV"); } } var videoName = aTitle + "." + VIDEO_EXTENSION; var videoURL = this._ioService.newURI(aURLSpec, null, null) .QueryInterface(Components.interfaces.nsIURL); var fileInfo = new FileInfo(videoName, videoName, aTitle, VIDEO_EXTENSION, videoURL); var fpParams = { fpTitleKey: "", isDocument: false, fileInfo: fileInfo, contentType: VIDEO_CONTENT_TYPE, saveMode: GetSaveModeForContentType(VIDEO_CONTENT_TYPE), saveAsType: 0, file: null, fileURL: null }; if(!getTargetFile(fpParams, this.SKIP_SELECT_PROMPT)){ return; } var fileURL = fpParams.fileURL || makeFileURI(fpParams.file); var persist = makeWebBrowserPersist(); var nsIWebBrowserPersist = Components.interfaces.nsIWebBrowserPersist; /* var persist = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"] .createInstance(nsIWebBrowserPersist); */ persist.persistFlags = nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES | nsIWebBrowserPersist.PERSIST_FLAGS_FROM_CACHE | // nsIWebBrowserPersist.PERSIST_FLAGS_BYPASS_CACHE | nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION; var transfer = Components.classes["@mozilla.org/transfer;1"] .createInstance(Components.interfaces.nsITransfer); transfer.init(fileInfo.uri, fileURL, "", null, null, null, persist); persist.progressListener = transfer; //window.close(); //for(var i=0;i<100000;++i){} //window._content.document.location = "about:blank"; persist.saveURI(fileInfo.uri, null, null, null, "", fileURL); } } ucjsNicovideoDownloader.init();