diff --git a/resource/sites/www.myanonamouse.net/config.json b/resource/sites/www.myanonamouse.net/config.json new file mode 100644 index 000000000..97f3dad9a --- /dev/null +++ b/resource/sites/www.myanonamouse.net/config.json @@ -0,0 +1,215 @@ +{ + "name": "MyAnonaMouse", + "description": "Friendliness, Warmth and Sharing", + "url": "https://www.myanonamouse.net/", + "icon": "https://cdn.myanonamouse.net/favicon.ico", + "tags": [ + "电子书", + "有声书" + ], + "schema": "MyAnonaMouse", + "host": "www.myanonamouse.net", + "collaborator": "tongyifan", + "supportedFeatures": { + "search": true, + "imdbSearch": false, + "userData": true, + "sendTorrent": true + }, + "plugins": [ + { + "name": "种子详情页面", + "pages": [ + "/t/\\d+" + ], + "scripts": [ + "/schemas/NexusPHP/common.js", + "details.js" + ] + }, + { + "name": "种子列表", + "pages": [ + "/tor/browse.php", + "/stats/top10Tor.php" + ], + "styles": [ + "/libs/album/style.css" + ], + "scripts": [ + "/schemas/NexusPHP/common.js", + "/libs/album/album.js", + "/schemas/Common/torrents.js" + ] + } + ], + "searchEntryConfig": { + "page": "/tor/js/loadSearch2.php", + "resultType": "html", + "queryString": "tor%5Btext%5D=$key$&tor%5BsrchIn%5D%5Btitle%5D=true&tor%5BsrchIn%5D%5Bauthor%5D=true&tor%5BsearchType%5D=all&tor%5BsearchIn%5D=torrents&tor%5Bcat%5D%5B%5D=0&tor%5BbrowseFlagsHideVsShow%5D=0&tor%5BsortType%5D=default&tor%5BstartNumber%5D=0&thumbnail=true", + "parseScriptFile": "getSearchResult.js", + "resultSelector": "table.newTorTable", + "skipIMDbId": true, + "firstDataRowIndex": 1 + }, + "searchEntry": [ + { + "name": "全站", + "enabled": true + } + ], + "torrentTagSelectors": [ + { + "name": "Free", + "selector": "img[alt='freeleech']" + }, + { + "name": "VIP", + "selector": "img[alt='VIP']" + } + ], + "selectors": { + "userBaseInfo": { + "page": "/index.php", + "fields": { + "id": { + "selector": [ + "li.myInfo > a" + ], + "attribute": "href", + "filters": [ + "query ? query.match(/(\\d+)/)[1]:''" + ] + }, + "name": { + "selector": [ + "a#userMenu" + ], + "filters": [ + "query ? query.text().replace(\"↓\", \"\").trim() : ''" + ] + }, + "isLogged": { + "selector": [ + "a[href='/preferences/index.php']" + ], + "filters": [ + "query.length>0" + ] + }, + "messageCount": { + "selector": [ + "a.tmnb, a.tmn, a.tmng" + ], + "filters": [ + "query.text().match(/(\\d+)/g)", + "query ? query.map(Number).reduce((sum, current) => {return sum + current;}, 0) : 0" + ] + } + } + }, + "userExtendInfo": { + "page": "/u/$user.id$", + "fields": { + "uploaded": { + "selector": [ + "td.rowhead:contains('Uploaded'):eq(0) + td" + ], + "filters": [ + "query.text().replace(/,/g,'').match(/([\\d.]+ ?[ZEPTGMK]?i?B)/)", + "(query && query.length==2)?(query[1]).sizeToNumber():0" + ] + }, + "downloaded": { + "selector": [ + "td.rowhead:contains('Downloaded'):eq(0) + td" + ], + "filters": [ + "query.text().replace(/,/g,'').match(/([\\d.]+ ?[ZEPTGMK]?i?B)/)", + "(query && query.length==2)?(query[1]).sizeToNumber():0" + ] + }, + "levelName": { + "selector": [ + "td.rowhead:contains('Class') + td" + ], + "filters": [ + "query.text()" + ] + }, + "bonus": { + "selector": [ + "a#tmBP" + ], + "filters": [ + "query.text().replace(/,/g,'').match(/Bonus: ([\\d.]+)/)", + "(query && query.length==2)?parseFloat(query[1]):0" + ] + }, + "joinTime": { + "selector": [ + "td.rowhead:contains('Join'):contains('date') + td" + ], + "filters": [ + "query.text().split(' (')[0]", + "dateTime(query).isValid()?dateTime(query).valueOf():query" + ] + } + } + }, + "userSeedingTorrents": { + "page": "/snatch_summary.php", + "parser": "getUserSeedingTorrents.js" + }, + "common": { + "fields": { + "downloadURLs": { + "selector": [ + "a[href*='/tor/download.php/']" + ], + "filters": [ + "query.toArray()" + ] + }, + "confirmSize": { + "selector": [ + "table.newTorTable > tbody > tr > td:contains(\"MB\")", + "table.newTorTable > tbody > tr > td:contains(\"GB\")", + "table.newTorTable > tbody > tr > td:contains(\"KB\")", + "table.newTorTable > tbody > tr > td:contains(\"TB\")" + ], + "filters": [ + "query.text().match(/\\[(.+ ?[ZEPTGMK]?i?B)\\]/)", + "(query && query.length==2)?query[1]:0" + ] + }, + "downloadURL": { + "selector": [ + "a#tddl" + ], + "attribute": "href", + "filters": [ + "query" + ] + }, + "size": { + "selector": [ + "div#size > div:eq(1) > span" + ], + "filters": [ + "query.text().replace(/[, ]/g,'').match(/([\\d.]+ ?[ZEPTGMK]?i?B)/)", + "(query && query.length>1)?(query[1]).sizeToNumber():0" + ] + }, + "sayThanksButton": { + "selector": [ + "button#giveThanks" + ], + "filters": [ + "query" + ] + } + } + } + } +} diff --git a/resource/sites/www.myanonamouse.net/details.js b/resource/sites/www.myanonamouse.net/details.js new file mode 100644 index 000000000..64a19cbfd --- /dev/null +++ b/resource/sites/www.myanonamouse.net/details.js @@ -0,0 +1,55 @@ +(function ($, window) { + console.log("this is details.js"); + + class App extends window.NexusPHPCommon { + init() { + this.initButtons(); + // 设置当前页面 + PTService.pageApp = this; + } + + /** + * 初始化按钮列表 + */ + initButtons() { + this.showTorrentSize(); + this.initDetailButtons(); + } + + /** + * 获取下载链接 + */ + getDownloadURL() { + let url = PTService.getFieldValue("downloadURL"); + + return this.getFullURL(url); + } + + /** + * 获取种子大小 + */ + showTorrentSize() { + let size = $("div#size > div:eq(1) > span"); + // eslint-disable-next-line no-irregular-whitespace + size = size.text().match(/([\d.]+[  ]?[ZEPTGMK]?i?B)/); + size = (size && size.length > 1) ? size[1] : 0; + + if (size) { + PTService.addButton({ + title: "当前种子大小", + icon: "attachment", + label: size + }); + } + } + + /** + * 获取当前种子标题 + */ + getTitle() { + return $(".TorrentTitle").text(); + } + } + + new App().init(); +})(jQuery, window); diff --git a/resource/sites/www.myanonamouse.net/getSearchResult.js b/resource/sites/www.myanonamouse.net/getSearchResult.js new file mode 100644 index 000000000..f8957ab8c --- /dev/null +++ b/resource/sites/www.myanonamouse.net/getSearchResult.js @@ -0,0 +1,131 @@ +/** + * 通用搜索解析脚本 + */ +(function (options, Searcher) { + class Parser { + constructor() { + this.haveData = false; + // 判断是否已登录 + if ( + options.entry.loggedRegex && + !new RegExp(options.entry.loggedRegex, "").test(options.responseText) + ) { + // 需要登录后再搜索 + options.status = ESearchResultParseStatus.needLogin; + return; + } + + options.isLogged = true; + + this.haveData = true; + this.site = options.site; + } + + /** + * 获取搜索结果 + */ + getResult() { + if (!this.haveData) { + return []; + } + let selector = options.resultSelector; + let dataRowSelector = options.entry.dataRowSelector || "> tbody > tr"; + selector = selector.replace(dataRowSelector, ""); + // 获取数据表格 + let table = options.page.find(selector); + // 获取种子列表行 + let rows = table.find(dataRowSelector); + if (rows.length === 0) { + // 没有定位到种子列表,或没有相关的种子 + options.status = ESearchResultParseStatus.torrentTableIsEmpty; + return []; + } + let results = []; + let beginRowIndex = options.entry.firstDataRowIndex || 0; + + try { + // 遍历数据行 + for (let index = beginRowIndex; index < rows.length; index++) { + const row = rows.eq(index); + + let torrentId = row.attr("id"); + // 跳过无种子ID的行 + if (!torrentId) { + continue; + } else { + torrentId = torrentId.match(/tdr-(\d+)/)[1]; + } + + // 下载链接,无法下载的VIP种子置为ONLY_FOR_VIP + let url = row.find(".directDownload").attr("href"); + if (!url) { + url = "ONLY_FOR_VIP" + } + + // 解析种子大小 + let size = row.find("> td:eq(4)").text().split("[")[1].replace("]", ""); + + // 解析发布时间和发布者 + let addedAndUploader = row.find("> td:eq(5)").text().split("["); + let time = addedAndUploader[0]; + let author = addedAndUploader[1].replace("]", ""); + + // 做种/下载/完成 + let seedLeechSnatched = row.find("> td:eq(6) > p"); + let seeders = parseInt(seedLeechSnatched.eq(0).text()); + let leechers = parseInt(seedLeechSnatched.eq(1).text()); + let completed = parseInt(seedLeechSnatched.eq(2).text()); + + let data = { + title: row.find(".torTitle").text(), + subTitle: row.find(".torRowDesc").text(), + link: this.getFullURL(`/t/${torrentId}`), + url: this.getFullURL(url), + size: size || 0, + time: time, + author: author, + seeders: seeders, + leechers: leechers, + completed: completed, + comments: row.find(" >td:eq(2)").text().match(/(\d+) comments/)[1], + site: this.site, + tags: Searcher.getRowTags(this.site, row), + entryName: options.entry.name + }; + results.push(data); + } + } catch (error) { + // 获取种子信息出错 + options.status = ESearchResultParseStatus.parseError; + options.errorMsg = error.stack; + } + + // 没有搜索到相关的种子 + if (results.length === 0 && !options.errorMsg) { + options.status = ESearchResultParseStatus.noTorrents; + } + + return results; + } + + /** + * 获取完整的URL地址 + * @param {string} url + */ + getFullURL(url) { + let URL = PTServiceFilters.parseURL(this.site.url); + if (url.substr(0, 2) === "//") { + url = `${URL.protocol}${url}`; + } else if (url.substr(0, 1) === "/") { + url = `${URL.origin}${url}`; + } else if (url.substr(0, 4) !== "http") { + url = `${URL.origin}/${url}`; + } + return url; + } + } + + let parser = new Parser(options); + options.results = parser.getResult(); + console.log(options.results); +})(options, options.searcher); diff --git a/resource/sites/www.myanonamouse.net/getUserSeedingTorrents.js b/resource/sites/www.myanonamouse.net/getUserSeedingTorrents.js new file mode 100644 index 000000000..cab3806c8 --- /dev/null +++ b/resource/sites/www.myanonamouse.net/getUserSeedingTorrents.js @@ -0,0 +1,74 @@ +(function (options, User) { + class Parser { + constructor(options) { + this.options = options; + this.body = null; + this.result = { + seeding: 0, + seedingSize: 0 + }; + this.load(); + } + + /** + * 完成 + */ + done() { + this.options.resolve(this.result); + } + + /** + * 加载当前做种数据 + */ + load() { + const types = [ + "seedUnsat", + "seedHnr", + "sSat", + "upAct" + ] + for (const type of types) { + User.getCookie(options.site, "mam_id").then(mamId => { + $.getJSON("https://cdn.myanonamouse.net/json/loadUserDetailsTorrents.php", { + uid: options.userInfo.id, + iteration: 0, + type: type, + cacheTime: Math.round(Date.now() / 1000), + mam_id: decodeURIComponent(mamId) + }).done(data => { + data.rows.forEach(item => { + this.result.seeding += 1; + this.result.seedingSize += item.size.sizeToNumber() + }) + + // 如果到了最后一个type + if (type === types[types.length - 1]) { + this.done(); + } + }).fail(error => { + console.log(error); + this.done(); + }) + }).catch(err => { + console.log(err); + this.done(); + }); + } + } + } + + new Parser(options); +})(_options, _self); +/** + * + _options 表示当前参数 + { + site, + rule, + userInfo, + resolve, + reject + } + + _self 表示 User(/src/background/user.ts) 类实例 + */ diff --git a/src/background/user.ts b/src/background/user.ts index a4d7b1e5f..86e525b2a 100644 --- a/src/background/user.ts +++ b/src/background/user.ts @@ -460,4 +460,24 @@ export class User { } }); } + + // MAM需要在访问API时传入存于Cookies中的mam_id,构建这个辅助方法以便获取Cookie + public getCookie(site: Site, needle: String): Promise { + return new Promise((resolve, reject) => { + PPF.checkPermissions(["cookies"]).then(() => { + this.service.config.getCookiesFromSite(site).then((result) => { + for (const cookie of result.cookies) { + if (cookie["name"] === needle) { + resolve(cookie["value"]); + } + } + resolve(""); + }).catch(error => { + reject(error); + }); + }).catch(error => { + reject(error); + }); + }); + } }