let INFO =
teramako
MIT
特定ページの画像とかのURLを取ってきて一気にZIPにしてダウンロードするお
-
:zipd :zipdownload
:zipdownload -list -filter=filter downloadPath
downloadPathへZIPでアーカイブする。
downloadPathがディレクトリの場合、"ページタイトル.zip"となる。
省略された場合、以下の順に値を見て、そのディレクトリへダウンロードされる。
- g:zipDownloadDir (liberator.globalVariables.zipDownloadDir)
- browser.download.lastDir (Preference)
- ホームディレクトリ
-listオプションを指定すると、ダウンロードされるURLをリストする。
(ダウンロードはされない)
-filterオプションを指定すると、マッチするURLのアイテムのみダウンロードする。
-
g:zipDownloadDir
g:zipDownloadDir
liberator.globalVariables.zipDownloadDir
ダウンロード先ディレクトリ。downloadPathを省略した場合に、使用される。
例
:let g:zipDownloadDir="~/downloads"
-
g:zipDownloadFilter
g:zipDownloadFilter
liberator.globalVariables.zipDownloadFilter
デフォルトのフィルタfilterを省略した場合に、使用される。
例
:let g:zipDownloadFilter="\.(jpe?g|gif|png)$"
-
plugins.zipDeDownload.SITE_INFO
plugins.zipDeDownload.SITE_INFO
ページ毎の設定。詳細はコードを見よ。(見れば分かると思う)
;
// FIXME: 将来的には、storageに入れるべき
// FIXME: あと、それぞれダウンロード先を指定できた方が良い(?)
// XXX: WeData化してもOK
let SITE_INFO = [
{
label: "みんくちゃんねる",
site: "http://minkch\\.com/archives/.*\\.html",
xpath: '//a[img[@class="pict"]]|//div/img[@class="pict"]',
filter: "\\.(jpe?g|gif|png)$"
}, {
label: "カナ速",
site: "http://kanasoku\\.blog82\\.fc2\\.com/blog-entry-.*\\.html",
xpath: '//div[@class="entry_body"]//a[img]',
filter: "\\.(jpe?g|gif|png)$"
}, {
label: "がぞう~速報",
site: "http://stalker\\.livedoor\\.biz/archives/.*\\.html",
xpath: '//div[@class="main" or @class="mainmore"]//a/img[@class="pict"]/..',
filter: "\\.(jpe?g|gif|png)$"
}, {
label: "ギャルゲーブログ",
site: "http://suiseisekisuisui\\.blog107\\.fc2\\.com/blog-entry-.*.html",
xpath: '//div[@class="ently_text"]/a[img]',
filter: "\\.(jpe?g|gif|png)$"
}, {
label: "わくてか速報",
site: "http://blog\\.livedoor\\.jp/wakusoku/archives/.*\\.html",
xpath: '//div[@class="article-body-inner" or @class="article-body-more"]//a[//img[@class="pict"]]',
filter: "\\.(jpe?g|gif|png)$"
}, {
label: "らばQ",
site: "http://labaq\\.com/archives/.*\\.html",
xpath: '//img[@class="pict"]',
}, {
labe: "【2ch】ニュー速クオリティ",
site: "http://news4vip\\.livedoor\\.biz/archives/.*\\.html",
xpath: '//a[img[@class="pict"]] | //div/img[@class="pict"]',
filter: "\\.(jpe?g|gif|png)$"
}, {
label: "ねとねた",
site: "http://vitaminabcdefg\\.blog6\\.fc2\\.com/blog-entry-.*\\.html",
xpath: '//div[@class="mainEntryBody" or @class="mainEntryMore"]//a[img]',
filter: "\\.(jpe?g|gif|png)$"
}, {
label: "PINK速報",
site: "http://pinkimg\\.blog57\\.fc2\\.com/blog-entry-.*\\.html",
xpath: '//div[@class="entry_text"]/a[img]',
filter: "\\.(jpe?g|gif|png)$"
}
];
(function(){
// nsIZipWriter#open io-flags
const PR_RDONLY = 0x01;
const PR_WRONLY = 0x02;
const PR_RDWR = 0x04;
const PR_CREATE_FILE = 0x08;
const PR_APPEND = 0x10;
const PR_TRUNCATE = 0x20;
const PR_SYNC = 0x40;
const PR_EXCL = 0x80;
const mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
const zipWriter = Components.Constructor("@mozilla.org/zipwriter;1", "nsIZipWriter");
function getFile(aFile){
return liberator.modules.io.File(aFile);
}
function createChannel(url){
return liberator.modules.services.get("io").newChannel(url, "UTF-8", null);
}
function getEntryName(uri, mimeType){
let mime;
try {
mime = mimeService.getTypeFromURI(uri);
} catch(e) {
liberator.reportError(e);
};
let ext = mimeService.getPrimaryExtension(mime ? mime : mimeType, null)
let name = uri.path.split("/").pop();
name = (name ? name : "index") + (mime ? "" : "." + ext);
return name;
}
function getDownloadDirectory(){
let path = liberator.globalVariables.zipDownloadDir ||
liberator.modules.options.getPref("browser.download.lastDir", null) ||
liberator.modules.services.get("directory").get("Home", Ci.nsIFile).path;
return getFile(path);
}
function fixFilename(filename){
const badChars = /[\\\/:;\*\?\"\<\>\|\#]/g;
return liberator.has('windows') ? filename.replace(badChars, '_') : filename;
}
function getXPathFromExtensions(exts){
function getXPath(elem){
if (!elem)
return '';
// 連番かもしれない id は無視する
let id = elem.getAttribute('id');
if (id && !/\d/.test(id))
return 'id("' + id + '")';
return getXPath(elem.parentNode) + '/' + elem.tagName.toLowerCase();
}
let extPattern = RegExp('(' + exts.join('|')+')(\\W|$)');
let links =
Array.slice( content.document.querySelectorAll('a')).filter(
function (link) (link.href && extPattern.test(link.href)));
let xs = {};
for each(let link in links){
let xpath = getXPath(link);
if (xs[xpath])
xs[xpath]++;
else
xs[xpath] = 1;
}
let result = null, max = 0;
for(let [xpath, count] in Iterator(xs)){
if (count > max)
[result, max] = [xpath, count];
}
return result;
}
function extensionValidator(vs)
vs && vs.every(function (v) /^[\da-zA-Z]+$/.test(v));
let self = {
downloadZip: function(path, urls, comment, isAppend){
let zipW = new zipWriter();
urls = [url for each(url in urls)];
liberator.assert(urls.length > 0, "None of URLs");
if (!(/\.zip$/i).test(path)){
path += ".zip";
}
let zipFile = getFile(path);
if (isAppend && zipFile.exists()){
zipW.open(zipFile, PR_RDWR | PR_APPEND);
} else {
zipW.open(zipFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
}
if (comment)
zipW.comment = comment;
let i = 0;
for each(let url in urls){
let ch = createChannel(url);
try {
let stream = ch.open();
let entryName = ("000" + ++i).slice(-3) + "-" + getEntryName(ch.URI, ch.contentType);
liberator.echomsg("zip: " + url + " to " + entryName, commandline.FORCE_SINGLELINE);
zipW.addEntryStream(entryName, Date.now() * 1000, Ci.nsIZipWriter.COMPRESSION_DEFAULT, stream, false);
} catch (e) {
// XXX エラー分を通知すべき?
liberator.log('zip-de-download: error: ' + e);
}
}
zipW.close();
return zipFile;
},
getInfoFromBuffer: function(){
for each(data in SITE_INFO){
let reg = new RegExp(data.site);
if (reg.test(liberator.modules.buffer.URL)){
return data;
}
}
return null;
},
getURLs: function(info){
let filter = new RegExp(info.filter || liberator.globalVariables.zipDownloadFilter || ".");
let i = 0;
for (let elm in liberator.modules.util.evaluateXPath(info.xpath, content.document)){
let url;
if (elm instanceof Ci.nsIDOMHTMLAnchorElement)
url = elm.href;
else if (elm instanceof Ci.nsIDOMHTMLImageElement)
url = elm.src;
else
continue;
if (filter.test(url))
yield url;
}
},
download: function(zipPath, listOnly, option){
let info = this.getInfoFromBuffer() || {};
if (option){
let infoBuf = {};
for (let key in info){
infoBuf[key] = info[key];
}
for (let key in option){
infoBuf[key] = option[key];
}
info = infoBuf;
}
liberator.assert(info.xpath, "not registered in SITE_INFO");
let urls = this.getURLs(info);
let title = fixFilename(liberator.modules.buffer.title);
let comment = [title, liberator.modules.buffer.URL].join("\n");
let file;
if (!zipPath){
file = getDownloadDirectory();
file.append(title + ".zip");
} else {
file = getFile(zipPath);
if (file.exists() && file.isDirectory()){
file.append(title + ".zip");
}
}
if (listOnly){
return [file, urls, comment];
}
return this.downloadZip(file.path, urls, comment, info.append);
}
};
// ---------------------------------------------------
// Commands
// ---------------------------------------------------
liberator.modules.commands.addUserCommand(
["zipd[ownload]"], "download and archive to ZIP",
function (arg){
let option = {}
option.append = ("-append" in arg);
if ("-auto-detect" in arg){
option.xpath = getXPathFromExtensions(arg["-auto-detect"]);
}
if ("-xpath" in arg){
option.xpath = arg["-xpath"];
}
if ("-filter" in arg){
option.filter = arg["-filter"];
}
if ("-list" in arg){
let [file, urls, comment] = self.download(arg[0], true, option);
let xml = <>
Download :{file.path}
{comment}
{liberator.modules.template.map(urls, function(url) - {url}
)}
>;
liberator.echo(xml, true);
return;
}
liberator.echo("Started DownloadZip");
setTimeout(function () {
let zipFile = self.download(arg[0], false, option);
liberator.echo("Completed DownloadZip: " + zipFile.path);
}, 0);
}, {
argCount: "?",
literal: true,
options: [
[["-list", "-l"], liberator.modules.commands.OPTION_NOARG],
[["-append", "-a"], liberator.modules.commands.OPTION_NOARG],
[["-xpath", "-x"], liberator.modules.commands.OPTION_STRING],
[["-auto-detect", "-d"], liberator.modules.commands.OPTION_LIST, extensionValidator,
[["jpeg,jpg,png", "images"]]],
[["-filter", "-f"], liberator.modules.commands.OPTION_STRING]
],
completer: liberator.modules.completion.file
}, true);
util.extend(__context__, self);
})();
// vim: sw=2 ts=2 et: