بۇ قوليازمىنى بىۋاسىتە قاچىلاشقا بولمايدۇ. بۇ باشقا قوليازمىلارنىڭ ئىشلىتىشى ئۈچۈن تەمىنلەنگەن ئامبار بولۇپ، ئىشلىتىش ئۈچۈن مېتا كۆرسەتمىسىگە قىستۇرىدىغان كود: // @require https://update.greasyfork.org/scripts/383027/698668/MB%20%28MyBook%29%20Downloader%20Factory.js
// ==UserScript==
// @namespace https://github.com/nntoan/UserScripts
// @exclude *
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/ui/widget.min.js
// @require https://unpkg.com/[email protected]/dist/jepub.min.js
// @require https://unpkg.com/[email protected]/dist/FileSaver.min.js
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @grant GM_deleteValue
// @grant GM_listValues
// @run-at document-idle
// @connect self
// @noframes
// ==UserLibrary==
// @name MB (MyBook) Downloader Factory
// @description A small jQuery widget which contains all required functionality to scraping data from story/novel on the Internet.
// @version 0.1.8
// @icon https://i.imgur.com/1Wyz9je.jpg
// @author Toan Nguyen
// @oujs:author nntoan
// @license MIT; https://nntoan.mit-license.org/
// ==/UserScript==
// ==/UserLibrary==
/*jshint evil:true newcap:false*/
/*global GM_getValue, GM_setValue, GM_xmlhttpRequest, GM_registerMenuCommand, GM_deleteValue, GM_listValues, console, location, jEpub, saveAs*/
(function($, window, document) {
'use strict';
$.widget('nntoan.mbDownloader', {
jepub: null,
processing: {
count: 0,
begin: '',
end: '',
endDownload: false,
beginEnd: '',
titleError: [],
chapListSize: 0,
},
elements: {
$window: $(window),
$downloadBtn: $('<a>', {
class: 'btn blue btn-download',
href: 'javascript:;',
text: 'Tải xuống'
}),
$downloadWrapper: null,
$novelId: null,
$infoBlock: null,
},
options: {
errorAlert: true,
createDownloadWrapper: false,
insertMode: 'appendTo',
credits: '<p>UserScript được viết bởi: <a href="https://nntoan.com/">Toan Nguyen</a></p>',
general: {
host: location.host,
pathname: location.pathname,
referrer: location.protocol + '//',
pageName: document.title,
},
processing: {
ebookFileName: null,
ebookFileExt: '.epub',
documentTitle: '[...] Vui lòng chờ trong giây lát',
},
regularExp: {
chapter: ['\s*Chương\s*\d+\s?:.*[^<\n]', 'g'], //eslint-disable-line
novel: ['\s*Tiểu\s*thuyết\s?:.*[^<\n]', 'g'], //eslint-disable-line
chineseSpecialChars: ['[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF]+', 'gm'],
alphanumeric: ['\s[a-zA-Z0-9]{6,8}(="")?\s', 'gm'], //eslint-disable-line
alphabet: ['[A-Z]'],
number: ['\d+'], //eslint-disable-line
buttons: ['\([^(]+<button[^/]+<\/button>[^)]*\)\s*', 'gi'], //eslint-disable-line
eoctext: ['(ps:|hoan nghênh quảng đại bạn đọc quang lâm|Huyền ảo khoái trí ân cừu)', 'i'],
breakline: ['\n', 'g'],
chapList: ['(?:href=")[^")]+(?=")', 'g'],
},
classNames: {
novelId: null,
infoBlock: null,
chapterContent: null,
chapterNotContent: null,
chapterVip: '#btnChapterVip',
ebookTitle: null,
ebookAuthor: null,
ebookCover: null,
ebookDesc: null,
ebookType: null,
downloadBtnStatus: 'btn-primary btn-success btn-info btn-warning btn-danger blue success warning info danger error',
downloadAppendTo: null,
downloadWrapper: null,
},
ebook: {
title: null,
author: null,
publisher: location.host,
description: null,
tags: [],
},
chapters: {
chapList: [],
chapListSlice: [6, -1],
chapId: null,
chapTitle: null,
},
xhr: {
chapter: {
type: 'GET',
url: null,
data: {},
},
content: {
type: 'GET',
url: null,
xhrFields: {
withCredentials: true
}
}
}
},
_create: function () {
// Register core elements
this.elements.$novelId = $(this.options.classNames.novelId);
this.elements.$infoBlock = $(this.options.classNames.infoBlock);
if (!this.elements.$novelId.length || !this.elements.$infoBlock.length) {
return;
}
// Works with options
this.options.general.referrer = this.options.general.referrer + this.options.general.host + this.options.general.pathname;
this.options.xhr.content.url = this.options.general.pathname + this.options.chapters.chapId + '/';
// Prepare & register jEpub instance
this.getBookInfo();
this.jepub = new jEpub(this.options.ebook).uuid(this.generateUUID()); //eslint-disable-line
// Works with download button
if (this.createDownloadWrapper === true) {
this.elements.$downloadWrapper = $(this.options.classNames.downloadWrapper);
this.elements.$downloadWrapper.append(this.options.classNames.downloadAppendTo);
this.elements.$downloadBtn.appendTo(this.options.classNames.downloadWrapper);
} else {
this.elements.$downloadBtn.appendTo(this.options.classNames.downloadAppendTo);
}
this.registerEventHandlers(this.elements.$downloadBtn, 'dl');
},
/**
* Retrieve/update book information.
*
* @returns void
*/
getBookInfo: function () {
var options = this.options,
$infoBlock = this.elements.$infoBlock;
options.ebook = $.extend(options.ebook, {
title: $infoBlock.find(options.classNames.ebookTitle).text().trim(),
author: $infoBlock.find(options.classNames.ebookAuthor).find('p').text().trim(),
cover: $infoBlock.find(options.classNames.ebookCover).find('img').attr('src'),
description: $infoBlock.find(options.classNames.ebookDesc).html(),
});
var $ebookType = $infoBlock.find(options.classNames.ebookType);
if ($ebookType.length) {
$ebookType.each(function () {
options.ebook.tags.push($(this).text().trim());
});
}
},
/**
* Update chapter ID before get ajax content.
*
* @param {Object} that Current widget
* @param {Object} options Widget options
* @returns void
*/
updateChapId: function (that, options) {
options.chapters.chapId = options.chapters.chapList[that.processing.count];
options.xhr.content.url = options.general.pathname + options.chapters.chapId + '/';
that._trigger('chapIdUpdated', null, options);
},
/**
* Create new RegExp instance from array.
*
* @param {Array} regExp Regular expression array
* @returns {RegExp}
*/
createRegExp: function (regExp) {
if (!regExp.length) {
return;
}
return new RegExp(regExp[0], regExp[1]);
},
/**
* Register all event handlers.
*
* @param {Element} $widget Current widget DOM element
* @param {String} event Type of event
* @returns void
*/
registerEventHandlers: function ($widget, event) {
var self = this,
options = this.options;
if (event === 'dl') {
$widget.one('click contextmenu', function (e) {
e.preventDefault();
document.title = options.processing.documentTitle;
$.ajax(options.xhr.chapter).done(function (response) {
options.chapters.chapList = response.match(self.createRegExp(options.regularExp.chapList));
options.chapters.chapList = options.chapters.chapList.map(function (val) {
return self.chapListValueFilter(options, val);
});
if (e.type === 'contextmenu') {
$widget.off('click');
var startFrom = prompt('Nhập ID chương truyện bắt đầu tải:', options.chapters.chapList[0]);
startFrom = options.chapters.chapList.indexOf(startFrom);
if (startFrom !== -1) {
options.chapters.chapList = options.chapters.chapList.slice(startFrom);
}
} else {
$widget.off('contextmenu');
}
self.processing.chapListSize = options.chapters.chapList.length;
if (self.processing.chapListSize > 0) {
self.elements.$window.on('beforeunload', function () {
return 'Truyện đang được tải xuống...';
});
$widget.one('click', function (e) {
e.preventDefault();
self.saveEbook($widget);
});
self.getContent($widget);
}
}).fail(function (error) {
$widget.text('Lỗi danh mục');
self.downloadStatus('error');
console.error(error); //eslint-disable-line
});
});
}
},
/**
* Get chapter content process.
*
* @param {Element} $widget Current widget DOM element
* @returns void
*/
getContent: function ($widget) {
var self = this,
options = this.options;
if (self.processing.endDownload === true) {
return;
}
this.updateChapId(self, options);
$.ajax(options.xhr.content).done(function (response) {
var $data = $(response),
$chapter = $data.find(options.classNames.chapterContent),
$notContent = $chapter.find(options.classNames.chapterNotContent),
$referrer = $chapter.find('[style]').filter(function () {
return (this.style.fontSize === '1px' || this.style.fontSize === '0px' || this.style.color === 'white');
}),
chapContent;
if (self.processing.endDownload === true) {
return;
}
options.chapters.chapTitle = $data.find('h2').text().trim();
if (options.chapters.chapTitle === '') {
options.chapters.chapTitle = 'Chương ' + options.chapters.chapId.match(self.createRegExp(options.regularExp.number))[0];
}
if (!$chapter.length) {
chapContent = self.downloadError('Không có nội dung');
} else {
if ($chapter.find(options.classNames.chapterVip).length) {
chapContent = self.downloadError('Chương VIP');
} else if ($chapter.filter(function () {
return (this.textContent.toLowerCase().indexOf('vui lòng đăng nhập để đọc chương này') !== -1);
}).length) {
chapContent = self.downloadError('Chương yêu cầu đăng nhập');
} else {
var $img = $chapter.find('img');
if ($img.length) {
$img.replaceWith(function () {
return '<br /><a href="' + this.src + '">Click để xem ảnh</a><br />';
});
}
if ($notContent.length) $notContent.remove();
if ($referrer.length) $referrer.remove();
if ($chapter.text().trim() === '') {
chapContent = self.downloadError('Nội dung không có');
} else {
if (!$widget.hasClass('error')) {
self.downloadStatus('warning');
}
chapContent = self.parseHtml($chapter.html());
}
}
}
self.jepub.add(options.chapters.chapTitle, chapContent);
if (self.processing.count === 0) {
self.processing.begin = options.chapters.chapTitle;
}
self.processing.end = options.chapters.chapTitle;
$widget.html('Đang tải: ' + Math.floor((self.processing.count / self.processing.chapListSize) * 100) + '%');
self.processing.count++;
document.title = '[' + self.processing.count + '] ' + options.general.pageName;
if (self.processing.count >= self.processing.chapListSize) {
self.saveEbook($widget);
} else {
self.getContent($widget);
}
}).fail(function (error) {
self.downloadError('Kết nối không ổn định', error);
self.saveEbook($widget);
});
},
/**
* Callback function to handle chap list values.
*
* @param {object} options
* @param {string} val
* @returns {string}
*/
chapListValueFilter: function (options, val) {
val = val.slice(options.chapters.chapListSlice[0], options.chapters.chapListSlice[1]);
val = val.replace(options.general.referrer, '');
return val.trim();
},
/**
* Update CSS of download button.
*
* @param {string} status Download status
* @returns void
*/
downloadStatus: function (status) {
var self = this,
options = this.options;
self.elements.$downloadBtn.removeClass(options.classNames.downloadBtnStatus).addClass('btn-' + status).addClass(status);
},
/**
* Handle error event of downloading process.
*
* @param {boolean} error
* @param {string} message
* @returns {string}
*/
downloadError: function (error, message) {
var options = this.options;
this.downloadStatus('error');
this.processing.titleError.push(options.chapters.chapTitle);
if (options.errorAlert) {
options.errorAlert = confirm('Lỗi! ' + message + '\nBạn có muốn tiếp tục nhận cảnh báo?');
}
if (error) {
console.error(message); //eslint-disable-line
}
return '<p class="no-indent"><a href="' + options.general.referrer + options.chapters.chapId + '">' + message + '</a></p>';
},
/**
* Parse chapter content and wrap a <div> tag for ePub.
*
* @param {string} html Chapter content as HTML
* @returns {string}
*/
parseHtml: function (html) {
var options = this.options;
html = html.replace(this.createRegExp(options.regularExp.chapter), '');
html = html.replace(this.createRegExp(options.regularExp.novel), '');
html = html.replace(this.createRegExp(options.regularExp.chineseSpecialChars), ''); // eslint-disable-line
html = html.replace(this.createRegExp(options.regularExp.alphanumeric), function (key, attr) {
if (attr) return ' ';
if (!isNaN(key)) return key;
if (key.split(this.createRegExp(options.regularExp.alphabet)).length > 2) return ' ';
if (key.split(this.createRegExp(options.regularExp.number)).length > 1) return ' ';
return key;
});
html = html.replace(this.createRegExp(options.regularExp.buttons), '');
html = html.split(this.createRegExp(options.regularExp.eoctext))[0];
html = html.replace(this.createRegExp(options.regularExp.breakline), '<br />');
return '<div>' + html + '</div>';
},
/**
* Save ebook process.
*
* @param {Element} $widget Current DOM element
* @returns void
*/
saveEbook: function ($widget) {
var self = this,
options = this.options;
if (self.processing.endDownload) {
return;
}
self.processing.endDownload = true;
$widget.html('Đang nén EPUB...');
if (self.processing.titleError.length) {
self.processing.titleError = '<p class="no-indent"><strong>Các chương lỗi: </strong>' + self.processing.titleError.join(', ') + '</p>';
} else {
self.processing.titleError = '';
}
self.processing.beginEnd = '<p class="no-indent">Nội dung từ <strong>' + self.processing.begin + '</strong> đến <strong>' + self.processing.end + '</strong></p>';
self.jepub.notes(self.processing.beginEnd + self.processing.titleError + '<br /><br />' + options.credits);
console.log(self);
console.log(self.jepub);
console.log($widget);
console.log(options.processing.ebookFileName + options.processing.ebookFileExt);
self.jepub.generate().then(function (epubZipContent) {
var options = self.options,
ebookFilepath = options.processing.ebookFileName + options.processing.ebookFileExt;
consonle.log('processEbook');
self._trigger('processEbook', null, self);
document.title = '[⇓] ' + options.ebook.title;
self.elements.$window.off('beforeunload');
$widget.attr({
href: window.URL.createObjectURL(epubZipContent),
download: ebookFilepath
}).text('✓ Hoàn thành').off('click');
if (!$widget.hasClass('error')) {
self.downloadStatus('success');
}
self._trigger('beforeSave', null, self);
saveAs(epubZipContent, ebookFilepath); //eslint-disable-line
self._trigger('complete', null, self);
}).catch(function (error) {
self.downloadStatus('error');
console.error(error); //eslint-disable-line
});
},
/**
* Create BLOB and save file to browser.
*
* @param {Object} that
* @param {Element} $widget
* @param {Blob} epubZipContent
* @returns void
*/
releaseTheKraken: function (that, $widget, epubZipContent) {
var options = that.options,
ebookFilepath = options.processing.ebookFileName + options.processing.ebookFileExt;
document.title = '[⇓] ' + options.ebook.title;
that.elements.$window.off('beforeunload');
$widget.attr({
href: window.URL.createObjectURL(epubZipContent),
download: ebookFilepath
}).text('✓ Hoàn thành').off('click');
if (!$widget.hasClass('error')) {
self.downloadStatus('success');
}
that._trigger('beforeSave', null, that);
saveAs(epubZipContent, ebookFilepath); //eslint-disable-line
that._trigger('complete', null, that);
},
/**
* Generate UUID.
*
* @returns {string} Universally Unique Identifier
*/
generateUUID : function () {
// Universally Unique Identifier
var d = new Date().getTime();
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c == 'x' ? r : (r & 0x7 | 0x8)).toString(16);
});
return uuid;
},
});
})(jQuery, window, document);