MuxtapeMp3Downloader.uc.js
ユーザーページのプレイリストにダウンロード用のリンクを提供する。ダウンロードされるファイル名は、そのプレイリスト上の Artist - Title.mp3 になる。リンクをクリックしても何も起きてないように見えるが、ちゃんとダウンロードされている。どうも、 Muxtape 上のプレイヤーで再生・読み込みをしても(ブラウザ側に?)キャッシュされないのか、一からダウンロードする。
一部、 YoutubeFlvDownloader と NicovideoDownloader のコードを拝借したため、こっちで公開。あっちは全部自分のにしたいから。
著作権物の違法ダウンロードには注意してね。知らないよ。
- MuxtapeMp3Downloader.uc.js (ver. 0.0.1)
// ==UserScript== // @name Muxtape MP3 Downloader // @version 0.0.1 // @description Adds MP3 download links on Muxtape. // @author Shinya // @homepage // @namespace http://www.code-404.net/ // @compatibility Firefox 2.0 // @include chrome://browser/content/browser.xul // @note // ==/UserScript== (function(){ var MuxtapeDownloader = { init: function(aEvent){ var doc = aEvent.originalTarget; // document object // Don't work without muxtape.com if(doc.location.href.match(/http:\/\/\w+\.muxtape\.com\/\w*/) == null) return; // Gets file URIs var uris = {}; var win = doc.window; for(var obj in win){ if(win[obj].ladles != undefined){ for(var song in win[obj].ladles){ uris["song" + win[obj].ladles[song].hex] = win[obj].ladles[song].songurl; } break; } } // Adds download links var songs = $X(".//li[@class='song']", doc.body); for(var i = 0, l = songs.length; i < l; i++){ var span = document.createElementNS("http://www.w3.org/1999/xhtml", "span"); $X(".//div[@class='info']", songs[i])[0].appendChild(span); span.setAttribute("class", "buy"); var anchor = document.createElementNS("http://www.w3.org/1999/xhtml", "a"); span.appendChild(anchor); span.insertBefore(document.createTextNode(" - "), span.lastChild); anchor.setAttribute("href", uris[songs[i].id]); anchor.addEventListener("click", MuxtapeDownloader.download, false); // anchor.appendChild(document.createTextNode("Download this MP3")); } }, download: function(aEvent){ var downloader = new MuxtapeDownloader.downloader(); downloader.download(aEvent.target); aEvent.preventDefault(); aEvent.stopPropagation(); // Don't action muxtape player } }; // Like a "window.addEventListener('load', MuxtapeDownloader.init, false); document.getElementById("appcontent").addEventListener("DOMContentLoaded", MuxtapeDownloader.init, false); MuxtapeDownloader.downloader = function(){ this.SKIP_SELECT_PROMPT = true; } MuxtapeDownloader.downloader.prototype = { download: function(aNode){ // Get "Artist - Title" (File name) var title = $X("../../../div[@class='name']/text()", aNode, String); title = title.replace(/^\s+/, "").replace(/\s+$/, "").replace(/[\$\*\?\!\:\;\,\<\>\|\"\'\/\\]/g, "_"); this._title = title; this._uri = aNode.getAttribute("href"); this._ioService = Components.classes["@mozilla.org/network/io-service;1"] .getService(Components.interfaces.nsIIOService); /* Do without comment if you want download after resource checked this.wrappedJSObject = this; var uri = this._ioService.newURI(aNode.getAttribute("href"), null, null); var httpChannel = this._ioService.newChannelFromURI(uri) .QueryInterface(Components.interfaces.nsIHttpChannel); httpChannel.requestMethod = "GET"; httpChannel.redirectionLimit = 0; httpChannel.asyncOpen(this.getListener(), this); */ // Do comment out if you want download after resource checked this.saveMp3(this._title, this._uri); }, /* Do without comment if you want download after resource checked getListener: function(){ var listener = { onStartRequest: function(){}, onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount){ aContext.wrappedJSObject.onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount); }, onStopRequest: function(){} } return listener; }, onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount){ aRequest.QueryInterface(Components.interfaces.nsIHttpChannel); alert(aRequest.responseStatus); // if(aRequest.responseStatus != 200){ alert("ERROR: Access Denied!! Try again later..."); return; } this.saveMp3(this._title, this._uri); }, */ saveMp3: function(aTitle, aMp3UriSpec){ const EXTENSION = "mp3"; var CONTENT_TYPE = "audio/mpeg"; var fileName = aTitle + "." + EXTENSION; var fileUri = this._ioService.newURI(aMp3UriSpec, null, null) .QueryInterface(Components.interfaces.nsIURL); var fileInfo = new FileInfo(fileName, fileName, aTitle, EXTENSION, fileUri); var fpParams = { fpTitleKey: "", isDocument: false, fileInfo: fileInfo, contentType: CONTENT_TYPE, saveMode: GetSaveModeForContentType(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; persist.persistFlags = nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES || nsIWebBrowserPersist.PERSIST_FLAGS_FROM_CACHE; var transfer = Components.classes["@mozilla.org/transfer;1"] .createInstance(Components.interfaces.nsITransfer); transfer.init(fileInfo.uri, fileURL, "", null, null, null, persist); persist.progressListener = transfer; persist.saveURI(fileInfo.uri, null, null, null, null, fileURL); } } //===================================== // XPath function from nulog // http://lowreal.net/blog/2007/11/17/1 // // Thanks, cho45. //===================================== function $X (exp, context, type /* want type */) { if (typeof context == "function") { type = context; context = null; } if (!context) context = document; var exp = (context.ownerDocument || context).createExpression(exp, function (prefix) { var o = document.createNSResolver(context).lookupNamespaceURI(prefix); if (o) return o; return (document.contentType == "application/xhtml+xml") ? "http://www.w3.org/1999/xhtml" : ""; }); switch (type) { case String: return exp.evaluate( context, XPathResult.STRING_TYPE, null ).stringValue; case Number: return exp.evaluate( context, XPathResult.NUMBER_TYPE, null ).numberValue; case Boolean: return exp.evaluate( context, XPathResult.BOOLEAN_TYPE, null ).booleanValue; case Array: var result = exp.evaluate( context, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null ); var ret = []; for (var i = 0, len = result.snapshotLength; i < len; i++) { ret.push(result.snapshotItem(i)); } return ret; case undefined: var result = exp.evaluate(context, XPathResult.ANY_TYPE, null); switch (result.resultType) { case XPathResult.STRING_TYPE : return result.stringValue; case XPathResult.NUMBER_TYPE : return result.numberValue; case XPathResult.BOOLEAN_TYPE: return result.booleanValue; case XPathResult.UNORDERED_NODE_ITERATOR_TYPE: { // not ensure the order. var ret = []; var i = null; while (i = result.iterateNext()) { ret.push(i); } return ret; } } return null; default: throw(TypeError("$X: specified type is not valid type.")); } } })();