From b3d153f2c3aba2602c7ba4dc710269445a71d81c Mon Sep 17 00:00:00 2001 From: anekos Date: Tue, 25 Nov 2008 16:56:13 +0000 Subject: :stfetch 追加 Vimperator 依存を減らす git-svn-id: http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk@24879 d0d07461-0603-4401-acd4-de1884942a52 --- stella.js | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 162 insertions(+), 15 deletions(-) (limited to 'stella.js') diff --git a/stella.js b/stella.js index 0a01b9b..bb80e93 100644 --- a/stella.js +++ b/stella.js @@ -2,7 +2,7 @@ // @name すてら // @description-ja ステータスラインに動画の再生時間などを表示する。 // @license Creative Commons Attribution-Share Alike 3.0 Unported -// @version 0.03 +// @version 0.05 // @author anekos (anekos@snca.net) // @minVersion 2.0pre // @maxVersion 2.0pre @@ -38,6 +38,8 @@ * Utils {{{ *********************************************************************************/ + const InVimperator = !!(liberator && modules && modules.liberator); + function isNum (v) (typeof v === 'number' && !isNaN(v)); @@ -57,6 +59,67 @@ function id (value) value; + function makeURL (s) { + let url = Cc["@mozilla.org/network/standard-url;1"].createInstance(Ci.nsIURL); + url.spec = s; + return url; + } + + function fixFilename (filename) { + const badChars = /[\\\/:;*?"<>|]/g; + return filename.replace(badChars, '_'); + } + + function makeFile (s) { + var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); + file.initWithPath(s); + return file; + } + + function download (url, filepath, ext, title) { + let dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager); + let wbp = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Ci.nsIWebBrowserPersist); + let file; + + if (filepath) { + file = io.getFile(io.expandPath(filepath)); + } else { + file = dm.userDownloadsDirectory; + } + if (file.isDirectory() && title) + file.appendRelativePath(fixFilename(title) + ext); + if (file.exists()) + return liberator.echoerr('The file already exists! -> ' + file.path); + file = makeFileURI(file); + + let dl = dm.addDownload(0, makeURL(url, null, null), file, title, null, null, null, null, wbp); + wbp.progressListener = dl; + wbp.persistFlags |= wbp.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION; + wbp.saveURI(makeURL(url), null, null, null, null, file); + + return true; + } + + function httpRequest (uri, onComplete) { + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () { + if (xhr.readyState == 4) { + if (xhr.status == 200) + onComplete && onComplete(xhr); + else + raise(xhr.statusText); + } + }; + xhr.open('GET', uri, !!onComplete); + xhr.send(null); + return xhr; + } + + function currentURL () + content.document.location.href; + + let raise = InVimperator ? function (error) {throw new Error(error)} + : function (error) liberator.echoerr(error); // }}} @@ -79,6 +142,7 @@ setf('playOrPause', this.has('play', 'x', 'pause', 'x') && 'x'); setf('turnUpDownVolume', this.has('volume', 'rw') && 'x'); setf('maxVolume', this.has('volume', 'rw') && 'r'); + setf('fetch', this.has('fileURL', 'r') && 'x'); } Player.ST_PLAYING = 'playing'; @@ -100,8 +164,11 @@ playEx: '', pause: '', muted: '', - repeating: '' - // auto setting => seek, seekRelative, playOrPause, turnUpDownVolume, maxVolume + repeating: '', + fileURL: '', + title: '', + fileExtension: 'r', + // auto setting => seek, seekRelative, playOrPause, turnUpDownVolume, maxVolume, fetch }, icon: null, @@ -120,6 +187,12 @@ get statusText () this.timeCodes, + get fileURL () undefined, + + get title () undefined, + + get fileExtension () '', + is: function (state) (this.state == state), has: function (name, ms) @@ -169,12 +242,18 @@ get state () undefined, + get FileURL() undefined, + toggle: function (name) { if (!this.has(name, 'rw')) return; let v = this[name]; this[name] = !v; return !v; + }, + + fetch: function (filepath) { + download(this.fileURL, filepath, this.fileExtension, this.title); } }; @@ -200,7 +279,9 @@ playEx: 'x', pause: 'x', muted: 'rwt', - repeating: 'rw' + repeating: 'rw', + title: 'r', + fileURL: 'r' }, icon: 'http://www.youtube.com/favicon.ico', @@ -217,6 +298,13 @@ get volume () parseInt(this.player.getVolume()), set volume (value) parseInt(this.player.setVolume(value)), + get fileURL () + let (as = content.document.defaultView.wrappedJSObject.swfArgs) + ('http://www.youtube.com/get_video?fmt=22&video_id=' + as.video_id + '&t=' + as.t), + + get title () + content.document.title.replace(/^YouTube - /, ''), + play: function () this.player.playVideo(), pause: function () this.player.pauseVideo(), @@ -224,6 +312,8 @@ get muted () this.player.isMuted(), set muted (value) (value ? this.player.mute() : this.player.unMute()), + get fileExtension () '.mp4', + get state () { switch (this.player.getPlayerState()) { case 'ended': @@ -264,7 +354,12 @@ pause: 'x', muted: 'rwt', repeating: 'rwt', - comment: 'rwt' + comment: 'rwt', + title: 'r', + fileURL: '', + id: 'r', + fetch: 'x', + title: 'r' }, icon: 'http://www.nicovideo.jp/favicon.ico', @@ -281,6 +376,10 @@ get volume () parseInt(this.player.ext_getVolume()), set volume (value) parseInt(this.player.ext_setVolume(value)), + get title () content.document.title.replace(/\s*\u2010\s*\u30CB\u30B3\u30CB\u30B3\u52D5\u753B(.+)$/, ''), + + get fileExtension () '.flv', + playOrPause: function () { if (this.is(Player.ST_PLAYING)) { this.pause(); @@ -303,6 +402,10 @@ get muted () this.player.ext_isMute(), set muted (value) this.player.ext_setMute(value), + get id () + let (m = currentURL().match(/\/watch\/([a-z]{2}\d+)/)) + (m && m[1]), + get state () { switch (this.player.ext_getStatus()) { case 'end': @@ -315,6 +418,17 @@ default: return Player.ST_OTHER; } + }, + + fetch: function (filepath) { + liberator.log(this.id) + let onComplete = function (xhr) { + let res = xhr.responseText; + let info = {}; + res.split(/&/).forEach(function (it) let ([n, v] = it.split(/=/)) (info[n] = v)); + download(decodeURIComponent(info.url), filepath, this.fileExtension, this.title); + }; + httpRequest('http://www.nicovideo.jp/api/getflv?v=' + this.id, bindr(this, onComplete)); } }; @@ -382,6 +496,33 @@ // }}} + /********************************************************************************* + * Event {{{ + *********************************************************************************/ + + function WebProgressListener (listeners) { + let self = this; + for (let [name, listener] in Iterator(listeners)) + this[name] = listener; + getBrowser().addProgressListener(this); + // これは必要? + window.addEventListener('unload', bindr(this.uninstall), false); + } + + WebProgressListener.prototype = { + onStatusChange: function (webProgress, request, stateFlags, staus) undefined, + onProgressChange: function (webProgress, request, curSelfProgress, + maxSelfProgress, curTotalProgress, maxTotalProgress) undefined, + onLocationChange: function (webProgress, request, location) undefined, + onStateChange: function(webProgress, request, status, message) undefined, + onSecurityChange: function(webProgress, request, state) undefined, + uninstall: function () { + getBrowser().removeProgressListener(this); + } + }; + + // }}} + /********************************************************************************* * Stella {{{ *********************************************************************************/ @@ -397,21 +538,25 @@ Stella.prototype = { // new 時に呼ばれる initialize: function () { + let self = this; + this.players = { niconico: new NicoPlayer(), youtube: new YouTubePlayer() }; + this.createStatusPanel(); - this.addAutoCommand(); this.onLocationChange(); + this.__onResize = window.addEventListener('resize', bindr(this, this.onResize), false); - this.addUserCommands(); + this.progressListener = new WebProgressListener({onLocationChange: bindr(this, this.onLocationChange)}); }, // もちろん、勝手に呼ばれたりはしない。 finalize: function () { this.removeStatusPanel(); this.disable(); + this.progressListener.uninstall(); window.removeEventListener('resize', this.__onResize, false); }, @@ -442,8 +587,8 @@ (funcS instanceof Function) ? funcS : function (arg, bang) { - if (!stella.where) - return liberator.echoerr('Stella: Current page is not supported'); + if (!stella.valid) + raise('Stella: Current page is not supported'); let p = stella.player; let func = bang ? funcB : funcS; if (p.has(func, 'rwt')) @@ -466,6 +611,7 @@ add('comment', 'comment'); add('volume', 'volume', 'turnUpDownVolume'); add('seek', 'seek', 'seekRelative'); + add('fetch', 'fetch'); }, removeStatusPanel: function () { @@ -536,10 +682,6 @@ stbar.insertBefore(panel, document.getElementById('liberator-statusline').nextSibling); }, - // FIXME - addAutoCommand: function () - autocommands.add('LocationChange', /.*/, "js liberator.plugins.nico_statusline.onLocationChange()"), - update: function () { this.labels.main.text = this.player.statusText; this.labels.volume.text = this.player.volume; @@ -625,15 +767,20 @@ *********************************************************************************/ let (nsl = liberator.plugins.nico_statusline) { + let install = function () { + let stella = liberator.plugins.nico_statusline = new Stella(); + stella.addUserCommands(); + liberator.log('Stella: installed.') + } if (nsl) { nsl.finalize(); - liberator.plugins.nico_statusline = new Stella(); + install(); } else { window.addEventListener( 'DOMContentLoaded', function () { window.removeEventListener('DOMContentLoaded', arguments.callee, false); - liberator.plugins.nico_statusline = new Stella(); + install(); }, false ); -- cgit v1.2.3