/* jshint esversion: 9 */
// ==UserScript==
// @name Fantia downloader
// @name:en Fantia downloader
// @name:ja Fantia downloader
// @namespace http://tampermonkey.net/
// @version 2.5.4
// @description Download your Fantia rewards more easily!
// @description:en Download your Fantia rewards more easily!
// @description:ja Download your Fantia rewards more easily!
// @author suzumiyahifumi
// @match https://fantia.jp/posts/*
// @icon https://www.google.com/s2/favicons?domain=fantia.jp
// @require https://code.jquery.com/jquery-3.4.1.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.2.0/jszip.min.js
// @grant none
// @downloadURL none
// ==/UserScript==
(function () {
'use strict';
Date.prototype.Format = function (fmt) {
let o = {
"M+": this.getMonth() + 1,
"d+": this.getDate(),
"h+": this.getHours(),
"m+": this.getMinutes(),
"s+": this.getSeconds(),
"q+": Math.floor((this.getMonth() + 3) / 3),
"S": this.getMilliseconds()
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (let k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
};
$('head').append(``);
$('nav.scroll-tabs>div').append(`擷取下載`);
window.getDownLoadButton = () => {
$('div.btn-group-tabs').each((i, div) => {
$(div).append(``);
$('#set').remove();
});
$("div#page").append(`
`);
$("div#page").append(setting.settingCenterTemplate());
setting.renderSettingParams();
setting.paramsTemplate();
return;
};
let init = setInterval(() => {
if ($('nav.post-next-prev-buttons').length != 0) {
$(`div.image-thumbnails`).each((i, div) => {
let b = $(div).closest('div.content-block').find(`div[ng-if='$ctrl.isVisibleAndMulti()']`);
if (b.length == 0) $(div).before(``);
});
if ($('div.btn-group-tabs').length != 0) {
window.setting = new Setting();
window.getDownLoadButton();
clearInterval(init);
} else {
$('#set').remove();
clearInterval(init);
}
}
}, 500);
class Setting {
constructor() {
let authorId = $("h1.fanclub-name>a").attr(`href`).split("/").pop();
const keyArr = [`cookieSave`, `lang`, `authorId_${authorId}`, `generalSaveZIP`, `generalSaveFile`, `authorSaveZIP_${authorId}`, `authorSaveFile_${authorId}`, `dateFormat`];
this.cookieOri = document.cookie;
this.cookie = this.cookieParser(keyArr);
this.cookieSave = this.cookie.cookieSave || this.defaultCookie(`cookieSave`);
this.setCookie({
cookieSave: this.cookieSave,
lang: (this.cookieSave == `On`) ? this.cookie.lang : this.getOriLang(),
generalSave: this.defaultCookie(`generalSave`),
authorSave: this.defaultCookie(`authorSave`),
authorSaveCheck: this.cookie[`authorId_${authorId}`] || this.defaultCookie(`authorSaveCheck`),
authorId: authorId,
dateFormat: (this.cookieSave == `On`) ? this.cookie.dateFormat : this.defaultCookie(`dateFormat`)
});
if (this.cookieSave == `On`) {
let g = this.cookieParser([`generalSaveZIP`, `generalSaveFile`]);
this.setCookie({
generalSave: {
zipName: g.generalSaveZIP,
fileName: g.generalSaveFile
}
});
if (this.cookie[`authorId_${authorId}`] || this.cookie[`authorId_${authorId}`] == `On`) {
let a = this.cookieParser([`authorSaveZIP_${authorId}`, `authorSaveFile_${authorId}`]);
this[`authorId_${authorId}`] = `On`;
this.setCookie({
authorSaveCheck: `On`,
authorSave: {
zipName: a[`authorSaveZIP_${authorId}`],
fileName: a[`authorSaveFile_${authorId}`]
}
});
}
}
return this;
}
saveCookie() {
if (this.cookie.cookieSave == 'On') {
let date = new Date();
date.setDate(date.getDate() + 75);
let cookie = {
cookieSave: this.cookie.cookieSave,
lang: this.cookie.lang,
dateFormat: this.cookie.dateFormat,
domain: `https://fantia.jp`,
path: `/`,
Expires: date.toUTCString()
};
cookie.generalSaveZIP = this.cookie.generalSave.zipName;
cookie.generalSaveFile = this.cookie.generalSave.fileName;
if (this[`authorId_${this.authorId}`] == 'On') {
cookie[`authorId_${this.authorId}`] = 'On';
cookie[`authorSaveZIP_${this.authorId}`] = this.cookie.authorSave.zipName;
cookie[`authorSaveFile_${this.authorId}`] = this.cookie.authorSave.fileName;
}
this.updateCookie(cookie);
} else {
let date = new Date();
date.setDate(date.getTime() - 1);
let cookie = {
cookieSave: 'Off',
Expires: date.toUTCString()
};
cookie[`authorId_${this.authorId}`] = 'Off';
this.updateCookie(cookie);
}
return alert(this.getDefault(`saveMessage`, this.lang));
}
updateCookie(cookie = undefined) {
let Expires = cookie.Expires;
for (let [key, value] of Object.entries(cookie)) {
document.cookie = `${key}=${value}; Expires=${Expires}`;
}
this.cookieOri = document.cookie;
return this.cookieOri;
}
cookieParser(keyArr) {
return (typeof keyArr == `string`) ? document.cookie.split('; ').map(row => row.split('=')).filter(row => keyArr == row[0])[1] || undefined : Object.fromEntries(document.cookie.split('; ').map(row => row.split('=')).filter(row => keyArr.includes(row[0])));
}
setCookie(settingObj) {
for (let [key, value] of Object.entries(settingObj)) {
this.cookie[key] = value;
this[key] = value;
}
return this.cookie;
}
setLang(Lang) {
let lang = Lang || this.getOriLang().toLowerCase();
if (/^ja\b/.test(lang)) this.lang = `ja`;
if (/^zh\b/.test(lang)) this.lang = `zh`;
return this.lang;
}
getOriLang() {
let res = `en`;
let lang = (navigator.language || navigator.browserLanguage).toLowerCase();
if (/^ja\b/.test(lang)) res = `ja`;
if (/^zh\b/.test(lang)) res = `zh`;
return res;
}
defaultCookie(key) {
let defaultSetting = {
cookieSave: "Off",
lang: this.getOriLang(),
generalSave: {
zipName: `{postTitle}_{boxTitle}`,
fileName: `{imgIndex:0}`
},
authorSave: {
zipName: `[{user}]{postTitle}_{boxTitle}`,
fileName: `{boxTitle}_{imgIndex:0}`
},
authorSaveCheck: `Off`,
dateFormat: `yyyyMMdd`
};
return (key) ? defaultSetting[key] : defaultSetting;
}
getDefault(key, lang = undefined) {
let defaultSetting = {
en: {
downloadImg: `Download Images`,
downloadImgZip: `Download ZIP`,
retrieving: `Retrieving Link`,
zipping: `Zipping`,
processing: `Processing`,
done: `Done`,
// 按紐
settingCenter: `Setting`,
cookieButton: `Cookie`,
languageButton: `言語設定/Language/界面語言`,
generalSave: `Global Save`,
authorSave: `Author Save`,
zipName: `ZIP file name`,
fileName: `image file name`,
dateFormat: `Date Format`,
saveSetting: `Apply Setting`,
closeSetting: `Close`,
tableParams: `Parameter`,
tableMean: `Mean`,
saveMessage: `Setting has save!`,
// 參數
user: `Creator Name`,
uid: `Creator ID`,
postTitle: `Post Title`,
postId: `Post ID`,
boxTitle: `Image Box Title`,
"imgIndex:0": `Index for image, Number is first index number.`,
plan: `Name of Plan`,
fee: `fee of Plan`,
postDate: `Post Date`,
taskDate: `Download Date`,
ext: `Filename Extension`
},
ja: {
downloadImg: `ダウンロード`,
downloadImgZip: `ZIPダウンロード`,
retrieving: `擷取連結中`,
zipping: `zip圧縮`,
processing: `圧縮`,
done: `ダウンロード完了`,
// 按紐
settingCenter: `設定`,
cookieButton: `Cookie`,
languageButton: `Language/言語設定/界面語言`,
generalSave: `保存の設定`,
authorSave: `クリエイターに保存`,
zipName: `ZIPフォルダ`,
fileName: `ファイル`,
dateFormat: `日期時間格式`,
saveSetting: `設定を保存する`,
closeSetting: `閉める`,
tableParams: `パラメータ`,
tableMean: `效果`,
saveMessage: `設定が保存しだ!`,
// 參數
user: `クリエイターの名前`,
uid: `クリエイターのID`,
postTitle: `投稿のタイトル`,
postId: `投稿のID`,
boxTitle: `イラストボックスのタイトル`,
"imgIndex:0": `画像のインデックス、番号は最初のインデックス番号です`,
plan: `プランの名`,
fee: `プランの月料`,
postDate: `リリース時間`,
taskDate: `ダウンロード時間`,
ext: `拡張子`
},
zh: {
downloadImg: `全圖片下載`,
downloadImgZip: `ZIP 下載`,
retrieving: `擷取連結中`,
zipping: `壓縮檔案中`,
processing: `壓縮`,
done: `下載已完成`,
// 按紐
settingCenter: `設定`,
cookieButton: `Cookie`,
languageButton: `Language/界面語言/言語設定`,
generalSave: `全域設定`,
authorSave: `作者特訂`,
zipName: `壓縮檔檔名`,
fileName: `圖片檔檔名`,
dateFormat: `日期時間格式`,
saveSetting: `儲存設定`,
closeSetting: `關閉`,
tableParams: `參數`,
tableMean: `效果`,
saveMessage: `設定已儲存!`,
// 參數
user: `創作者名稱`,
uid: `創作者 ID`,
postTitle: `投稿標題`,
postId: `投稿 ID`,
boxTitle: `圖片區標題`,
"imgIndex:0": `檔案排序號,數字決定起始數字`,
plan: `訂閱方案名`,
fee: `瀏覽月費`,
postDate: `投稿日期`,
taskDate: `下載日期`,
ext: `檔案副檔名`
}
};
return (key) ? defaultSetting[lang || this.lang][key] : defaultSetting[lang || this.lang];
}
renderSettingParams() {
$(`#cookie${this.cookieSave}`).attr("checked", true);
$(`#${(this.authorSaveCheck == `On`)? `authorSave` : `generalSave` }`).attr("checked", true);
$(`#selectSetting option[value='${this.lang}']`).attr("selected", true);
$(`#zipNameInput`).val(this[(this.authorSaveCheck == `On`) ? `authorSave` : `generalSave`].zipName);
$(`#fileNameInput`).val(this[(this.authorSaveCheck == `On`) ? `authorSave` : `generalSave`].fileName);
$(`#dateFormatInput`).val(this.dateFormat);
$(`#paramsTable`).html(this.paramsTemplate(this.lang));
return;
}
changeStyle(mode) {
let blur = CSS.supports(`backdrop-filter`, `blur(15px)`);
switch(mode){
case `Blur`:
return;
case `unBlur`:
return;
default:
return;
}
}
changeLang() {
let lang = $("#selectSetting option:selected").val();
this.setCookie({
lang: lang
});
$("#titleSetting").text(this.getDefault(`settingCenter`, lang));
$("#cookieSetting").text(this.getDefault(`cookieButton`, lang));
$("#langSetting").text(this.getDefault(`languageButton`, lang));
$("#generalSaveSetting").text(this.getDefault(`generalSave`, lang));
$("#authorSaveSetting").text(this.getDefault(`authorSave`, lang));
$("#zipNameSetting").text(this.getDefault(`zipName`, lang));
$("#fileNameSetting").text(this.getDefault(`fileName`, lang));
$("#dateFormatSetting").text(this.getDefault(`dateFormat`, lang));
$("#saveSetting").text(this.getDefault(`saveSetting`, lang));
$("#closeSetting").text(this.getDefault(`closeSetting`, lang));
$(".downloadSpan").text(this.getDefault('downloadImg', lang));
$(".downloadSpanZip").text(this.getDefault('downloadImgZip', lang));
$("#paramsTable").html(this.paramsTemplate(this.lang));
return;
}
changeName(type) {
let name = $(`#${type}NameInput`).val();
let setting = $(`input[name='saveCheck']:checked`).attr("id");
if (name == ``) {
this.changeSaveSetting(setting);
} else {
this[setting][`${type}Name`] = name;
}
return;
}
changeSaveSetting(id) {
let setting = this[id];
$(`#zipNameInput`).val(setting.zipName);
$(`#zipNameInput`).attr("placeholder", setting.zipName);
$(`#fileNameInput`).val(setting.fileName);
$(`#fileNameInput`).attr("placeholder", setting.fileName);
return;
}
paramsTemplate(lang = this.lang) {
let table = `
| ${setting.getDefault(`tableParams`, lang)} |
${setting.getDefault(`tableMean`, lang)} |
`;
let a = [`user`, `uid`, `postTitle`, `postId`, `boxTitle`, `imgIndex:0`, `plan`, `fee`, `postDate`, `taskDate`, `ext`].forEach((p, i) => {
table += `
| {${p}} |
${setting.getDefault(p, lang)} |
`;
});
table += `
`;
return table;
}
settingCenterTemplate(lang) {
return `
${this.getDefault(`settingCenter`, lang)}
${this.getDefault(`cookieButton`, lang)}
${this.getDefault(`languageButton`, lang)}
`;
}
}
class downloadButton {
constructor(event) {
this.button = ($(event.target).is("button")) ? $(event.target) : $(event.target).closest("button");
this.type = (this.button.hasClass(`zip`))? `zip` : `file`;
this.zipName = `${setting[`${(setting.authorSaveCheck == 'On')? `author` : `general`}Save`].zipName}.zip`;
this.fileName = `${setting[`${(setting.authorSaveCheck == 'On')? `author` : `general`}Save`].fileName}.{ext}`;
this.dateFormat = setting.dateFormat;
this.zipfmt = ``;
this.zipImgIndex0 = 0;
this.filefmt = ``;
this.fileImgIndex0 = 0;
this.d = window.getDigits(Number($($(event.target).closest('div.content-block').find('div.image-thumbnails')[0]).find('img').length));
return this;
}
changeButton(mode, input = false) {
let button = this.button;
switch (mode) {
case `start`:
button.addClass(['active', 'hdr']);
if(this.type == `file`){
button.find('i').removeClass('fa-download').addClass(['fa-spinner', 'fa-pulse']);
} else {
button.find('i').removeClass('fa-file-archive-o').addClass(['fa-spinner', 'fa-pulse']);
}
break;
case `catchLink`:
button.find('span').text(setting.getDefault(`retrieving`));
break;
case `countTask`:
button.find('span').text(`${this.finCount} / ${this.imgSrc.length}`);
break;
case `pickUp`:
button.find('span').text(setting.getDefault(`zipping`));
break;
case `end`:
if (this.type == `file`) {
button.find('i').removeClass(['fa-spinner', 'fa-pulse']).addClass('fa-download');
} else {
button.find('i').removeClass(['fa-spinner', 'fa-pulse']).addClass('fa-file-archive-o');
}
button.removeClass(['active', 'hdr']);
button.find('span').text(setting.getDefault(`done`));
break;
case `log`:
button.find('span').text(input);
break;
default:
return this;
}
return this;
}
paramsParser(type, fmt) {
let o = {
user: () => {
return $("h1.fanclub-name>a").text();
},
uid: () => {
return this.authorId;
},
postTitle: () => {
return $("h1.post-title").text();
},
postId: () => {
return window.location.href.split("/").pop();
},
boxTitle: () => {
return this.button.closest("div.post-content-inner").find('h2').text();
},
plan: () => {
let feeStr = this.button.closest("div.post-content-inner").find(`div.post-content-for strong.ng-binding`).text();
let match = feeStr.match(new RegExp(/(\d+円)以上限定$/g));
if (match != null) return feeStr.replace(match[0], ``);
return ``;
},
fee: () => {
let feeStr = this.button.closest("div.post-content-inner").find(`div.post-content-for strong.ng-binding`).text();
let match = feeStr.match(new RegExp(/((\d+)円)以上限定$/g));
if(match != null) return RegExp.$1;
return ``;
},
postDate: () => {
return new Date($(`small.post-date>span`).text()).Format(this.dateFormat);
},
taskDate: () => {
return new Date().Format(this.dateFormat);
}
};
for (let k in o) {
if (new RegExp('(\\{' + k + '\\})', 'g').test(fmt)) fmt = fmt.replace(RegExp.$1, o[k]());
}
let s = (/\{imgIndex(\:(\d+))?\}/g.test(fmt)) ? RegExp.$2 : 0;
if (/\{imgIndex(\:(\d+))?\}/g.test(fmt)) fmt = fmt.replace(RegExp.$1, ``);
this[`${type}fmt`] = fmt;
this[`${type}ImgIndex0`] = s;
return this;
}
nextName(type, index, mimeType) {
return this[`${type}fmt`].replace(`{imgIndex}`, (Number(index) + Number(this[`${type}ImgIndex0`])).toString().padStart(this.d, 0)).replace(`{ext}`, mimeType.toString().split(`/`)[1]);
}
}
window.openSettingCenter = () => {
if ($(`#settingCenterDiv`).hasClass(`close`)) {
$(`#settingCenterDiv`).removeClass(`close`);
}
return;
};
window.getImg = (event) => {
return checkBrowser(event, (event) => {
let srcArr = [];
let dataCont = 0;
const downloadB = new downloadButton(event);
downloadB.changeButton(`start`);
downloadB.changeButton(`catchLink`);
$($(event.target).closest('div.content-block').find('div.image-thumbnails')[0]).find('img').each((i, img) => {
let src = $(img).attr('src');
let id = /file\/\d+\//g.exec(src);
if (id[0] != null) {
srcArr.push(`https://fantia.jp/posts/SRC/post_content_photo/SRC`.replaceAll(`SRC`, /\d+/.exec(id)[0]));
}
});
downloadB.changeButton(`log`, `${dataCont} / ${srcArr.length}`);
var zip = (downloadB.type == `zip`) ? new JSZip() : undefined;
srcArr.digits = window.getDigits(Number(srcArr.length));
downloadB.paramsParser(`zip`, downloadB.zipName);
downloadB.paramsParser(`file`, downloadB.fileName);
srcArr.forEach((url, i) => {
$.get(url, function (data) {
let match = data.match(new RegExp(`
{
let L = 0;
while (i >= 1) {
i = i / 10;
L += 1;
}
return `${L}`;
};
window.loadAsArrayBuffer = (url, callback) => {
let xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "arraybuffer";
xhr.onerror = function (error) {
return new Error(`ERROR`);
};
xhr.onload = function () {
if (xhr.status === 200) {
callback(xhr.response, xhr.getResponseHeader("Content-Type"));
} else {
return new Error(`ERROR`);
}
};
xhr.send();
};
window.checkBrowser = (event, callBack) => {
try{
let excludes = [];
let ex = excludes.map(b => navigator.userAgent.indexOf(b)).filter(b => (b!=-1)?true: false);
if (ex.length >= 1) {
alert(`請使用 Firefox 下載圖片!\nPlease run this script on Firefox!`);
return;
} else {
return callBack(event);
}
}
catch(err){
console.log(err);
alert(`出了些問題!你可以嘗試使用 Firefox 下載圖片!\nThere are some ERROR, You can try this script on Firefox!`);
return;
}
};
})();