aboutsummaryrefslogtreecommitdiffstats
path: root/fetchyoutube.js
blob: 8d77f06acf744fe2abdf8304c17913664af28f12 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// ==VimperatorPlugin==
// @name           Fetch YouTube Video
// @description    Fetch YouTube Video (fmt=22)
// @license        Creative Commons 2.1 (Attribution + Share Alike)
// @version        1.1
// @author         anekos (anekos@snca.net)
// @minVersion     2.0pre
// @maxVersion     2.0pre
// ==/VimperatorPlugin==
//
// Usage:
//    :fetchyoutube
//      Download YouTube video to default download directory.
//      (pref: browser.download.dir)
//
// Links:
//    http://d.hatena.ne.jp/nokturnalmortum/20081118#1227004197
//
// Refs:
//    http://creazy.net/2008/11/another_way_to_find_youtube_hd_file.html

(function () {

    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 makeURL (s) {
      var url = Cc["@mozilla.org/network/standard-url;1"].createInstance(Ci.nsIURL);
      url.spec = s;
      return url;
    }

    function fetch (arg) {
      let doc = content.document;
      if (!doc.location.href.match(/^http:\/\/(?:[^.]+\.)?youtube\.com\/watch/))
        return;
      let filepath = arg.string;
      let as = content.document.defaultView.wrappedJSObject.swfArgs;
      let title = doc.title.replace(/^YouTube - /, '');
      // XXX 今が、fmt=22 じゃなかったら確認した方が良い?
      let fmt = /^22/.test(as.fmt_map) ? '22' : '18';
      let url = 'http://www.youtube.com/get_video?fmt=' + fmt + '&video_id=' + as.video_id + '&t=' + as.t;

      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())
        file.appendRelativePath(fixFilename(title) + '.mp4');
      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);
      liberator.echo('maybe downloading started');
    }

    commands.addUserCommand(
      ['fetchyoutube', 'fetchyt'],
      'fecth YouTube HD video',
      fetch,
      {argCount: '*', completer: function (context) completion.file(context)},
      true
    );

    // fetch({});

})();

// vim:sw=2 ts=2 et si fdm=marker:

AAAQCAIAAACQkWg2AAAAVElEQVR4nGP8v5eBJMBEmnIs' + 'Gpz+Mzj9x6OBheEZNmGsguQ5iYXhHjZhrILk2vAVmzBWQXJt4MYmjFWQXBuUsAljFYRqwApi' + 'MCJ7CSOEZqR/4iMEAOh5DfER9lQKAAAAAElFTkSuQmCC'; const rhsRegExp = /[ \r\n]+/g; var _isEnable; var _isBindLocalKey = false; var _enableTabs = []; var _names; var feedKeys = liberator.modules ? liberator.modules.events.feedkeys : liberator.events.feedkeys; // utility function function cloneMap(org) { return new Map( org.modes, org.names, org.description, org.action, {flags:org.flags, rhs:org.rhs, noremap:org.noremap } ); } var Class = function() function() {this.initialize.apply(this, arguments);}; var LocalKeyMode = new Class(); LocalKeyMode.prototype = { // 初期化メソッド initialize: function() { this.storekeymaps = []; //キー待避用(戻し) this.delkeychars = []; //キー待避用(削除) this.keymapnames = []; // 対応URI保持 this.localkeymaps = []; // キーマップ保持用 this.completeNames; // 補完用 this.tabinfo = []; // タブごとの状態保持用 this.helpstring = ''; var global = liberator.globalVariables; this.panel = this.setupStatusBar(); this.isEnable = global.localkeymode_enable != undefined ? window.eval(global.localkeymode_enable) : false; this.setupEnvironment(); this.initLocalKeyMap(); }, // ステータスバーにアイコンを生成 setupStatusBar: function() { var self = this; var panel = document.createElement('statusbarpanel'); panel.setAttribute('id', 'localkeymode-status'); panel.setAttribute('class', 'statusbarpanel-iconic'); panel.setAttribute('src', self.isEnable ? ENABLE_ICON : DISABLE_ICON); panel.addEventListener('click', function(e) { self.isEnable = !self.isEnable; }, false); document.getElementById('status-bar').insertBefore( panel, document.getElementById('security-button').nextSibling); return panel; }, get isEnable() { return _isEnable; }, set isEnable(value) { this.panel.setAttribute('src', value ? ENABLE_ICON : DISABLE_ICON); _isEnable = value; this.loadKeyMap(); }, get isBinding() { return _isBindLocalKey; }, set isBinding(value) { this.panel.setAttribute('src', value ? BINDING_ICON : this.isEnable ? ENABLE_ICON : DISABLE_ICON ); _isBindLocalKey = value; }, // 初期処理 initLocalKeyMap: function() { if (liberator.globalVariables.localKeyMappings == undefined ) return; var list = liberator.globalVariables.localKeyMappings; if (!list) return; var self = this; list.forEach( function( items ) { if ( !(items instanceof Array) || items.length < 2 || !(items[1] instanceof Array) ) return; self.addLocalKeyMap( items[0], items[1] ); } ); this.completeNames = this.keymapnames.map( function(m) { m = (m+'').replace(/[\/\\]+/g, ''); return [m+'', 'maps for [' + m + ']']; } ); }, // ローカルキーマップの生成 addLocalKeyMap: function( uri, items ) { if (!uri) return; var keymaps = []; var delkeys = []; if (!(uri instanceof RegExp) ) uri = new RegExp(uri.replace(/(?=[^-0-9A-Za-z_@])/g, '\\')); for (let i=0; i<items.length; i++) { var item = items[i]; if (item.length < 1 || !item[0]) continue; if (item.length < 2) { delkeys = delkeys.concat( item[0].split(' ') ); continue; } var key = item[0] instanceof Array ? item[0] : [ item[0] ]; var command = item[1]; var extra = item[2] ? item[2]:new Object(); if (!extra || !extra.rhs) extra.rhs = (item[1]+'').replace(rhsRegExp, ' '); if (typeof command != 'function') { let commandName = command; if (command.charAt(0) == ':') command = extra.noremap ? function () commandline.open("", commandName, modes.EX) : function () liberator.execute(commandName); else command = function () feedKeys(command, extra.noremap, true); } keymaps.push(new Map([modes.NORMAL], key, 'localkeymap', command, extra) ); } this.keymapnames.push( uri ); this.localkeymaps.push( { keys:keymaps, removekeys:delkeys } ); }, releaseClosedTabCache: function() { var tabs = getBrowser().mTabs; var tabIds = []; var tabinfo = this.tabinfo; for (let i=0, l=tabs.length; i<l; i++) { tabIds.push( tabs[i].linkedPanel ); } for (let i=0; i<tabinfo.length; i++) { let isExist = false; for (let j=0, l=tabs.length; j<l; j++) { if (tabinfo[i].tabId == tabs[j]) { isExist = true; break; } } if (!isExist) tabinfo.splice(i, 1); } }, setupKeyMaps: function( keymaps ) { var self = this; keymaps.removekeys.forEach( function( key ) { var org = mappings.get( modes.NORMAL, key); if (org) self.storekeymaps.push( cloneMap(org) ); self.helpstring += key+' -> [Delete KeyMap]\n'; mappings.remove( modes.NORMAL, key); } ); keymaps.keys.forEach( function( m ) { m.names.forEach( function( key ) { var org = mappings.get(modes.NORMAL, key); if (org) self.storekeymaps.push( cloneMap(org) ); else self.delkeychars.push( key ); } ); mappings.addUserMap([modes.NORMAL], m.names, m.description, m.action, {flags:m.flags, rhs:m.rhs, noremap:m.noremap }); self.helpstring += m.names+' -> '+m.rhs+'\n'; } ); this.isBinding = true; }, deleteCurrentTabCache: function() { var tabId = getBrowser().selectedTab.linkedPanel; var tabinfo = this.tabinfo; for (let i=0; i<tabinfo.length; i++) { if (tabinfo[i].tabId == tabId) { tabinfo.splice(i, 1); break; } } }, // ローカルキーマップセット処理 loadKeyMap: function() { // 暫定処置 if (liberator.plugins.feedKey && liberator.plugins.feedKey.origMap.length >0) return; this.helpstring = ''; if (this.isBinding) this.restoreKeyMap(); if (!this.isEnable) { this.clearTabCache(); return; } var tabinfo = this.tabinfo; var settings = this.localkeymaps; var tabId = getBrowser().selectedTab.linkedPanel; for (let i=0, l=tabinfo.length; i<l; i++) { if (tabId == tabinfo[i].tabId) { this.setEnable = true; this.setupKeyMaps( settings[ tabinfo[i].keyMapIndex ] ); return; } } for (let i=0, l=settings.length; i<settings.length; i++) { if ( this.keymapnames[i].test(content.location.href) ) { this.setupKeyMaps( settings[i] ); break; } } }, clearTabCache: function() { for (;0 < this.tabinfo.length;) { this.tabinfo.shift(); } }, // 割り当てていたローカルキーの削除処理 restoreKeyMap: function() { if (this.isBinding) { for (; 0 < this.storekeymaps.length; ) { let m = this.storekeymaps.shift(); mappings.addUserMap([modes.NORMAL], m.names, m.description, m.action, {flags:m.flags, rhs:m.rhs, noremap:m.noremap}); } for (; 0 < this.delkeychars.length; ) { let keys = this.delkeychars.shift(); mappings.remove( modes.NORMAL, keys ); } this.isBinding = false; } }, // その他処理(ユーザコマンド追加等) setupEnvironment: function() { var self = this; commands.addUserCommand(['togglelocalkeymode'], 'Toggle local/global key mapping', function() { self.isEnable = !self.isEnable; }, {} ); commands.addUserCommand(['loadkeymaps', 'loadlocalkeymaps'], 'Load local key mapping', function(args) { if (!self.isEnable) { liberator.echoerr('localkeymode is disabled'); return; } var arg = (typeof args.string == undefined ? args : args.string); if (!arg) { liberator.echo(self.helpstring); return; } self.releaseClosedTabCache(); self.deleteCurrentTabCache(); var tabId = getBrowser().selectedTab.linkedPanel; var names = self.completeNames; for (let i=0, l=names.length; i<l; i++) { if (names[i][0] == arg) { self.tabinfo.push( {tabId: tabId, keyMapIndex: i} ); self.loadKeyMap(); return; } } }, { completer: function(context, arg, special){ let filter = context.filter; var names = self.completeNames; context.title = ['Name','Description']; if (!filter) { context.completions = names; return; } filter = filter.toLowerCase(); context.completions = names.filter( function(el) el[0].toLowerCase().indexOf(filter) == 0); } } ); commands.addUserCommand(['clearkeymaps', 'clearlocalkeymaps'], 'Clear local key mapping', function() { self.clearTabCache(); self.loadKeyMap(); }, { }); }, }; return new LocalKeyMode(); })(); autocommands.add('LocationChange', '.*', 'js liberator.plugins.LocalKeyMode.loadKeyMap();');