// ==UserScript== // @name MoviePlus // @name:zh-CN 豆瓣电影增强 // @namespace https://github.com/DCjanus/userscripts // @description 在豆瓣电影页面右边增加若干按钮,一键搜索电影相关资源。Fork 自 https://github.com/94leon/movie.plus // @author DCjanus // @match https://movie.douban.com/subject/*/ // @match https://movie.douban.com/subject/*/?* // @exclude https://movie.douban.com/subject/*/*/ // @icon https://raw.githubusercontent.com/DCjanus/userscripts/master/assets/douban.svg // @version 20260504 // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/469243/MoviePlus.user.js // @updateURL https://update.greasyfork.icu/scripts/469243/MoviePlus.meta.js // ==/UserScript== 'use strict'; const myScriptStyle = document.createElement('style'); myScriptStyle.innerHTML = '@charset utf-8;.c-aside {margin-bottom: 30px} .c-aside-body {*letter-spacing: normal} .c-aside-body a {border-radius: 6px;box-sizing: border-box;color: #37A;display: inline-block;letter-spacing: normal;margin: 0 8px 8px 0;overflow: hidden;padding: 0 8px;text-align: center;text-overflow: ellipsis;white-space: nowrap;width: 82px} .c-aside-body a:link, .c-aside-body a:visited {background-color: #f5f5f5;color: #37A} .c-aside-body a:hover, .c-aside-body a:active {background-color: #e8e8e8;color: #37A} .c-aside-body a.disabled {text-decoration: line-through} .c-aside-body a.available {background-color: #5ccccc;color: #006363} .c-aside-body a.available:hover, .c-aside-body a.available:active {background-color: #3cc} .c-aside-body a.honse {background-color: #fff0f5;color: #006363} .c-aside-body a.honse:hover, .c-aside-body a.honse:active {background-color: #3cc} .c-aside-body a.sites_r0 {text-decoration: line-through}'; myScriptStyle.innerHTML += ' .db-series-link, .db-series-link:link, .db-series-link:visited, .db-series-link:hover, .db-series-link:active { color: inherit !important; text-decoration: none !important; }'; document.getElementsByTagName('head')[0].appendChild(myScriptStyle); const aside_html = '

四字标题· · · · · ·

'; const en_total_reg = /^[a-zA-Z\d\s-:·,/`~!@#$%^&*()_+<>?"{}.…;'[\]]+$/; const en_end_reg = /\s[a-zA-Z\d\s-:·,/`~!@#$%^&*()_+<>?"{}.…;'[\]]+$/; const cn_start_reg = /^[\u4e00-\u9fa5a-zA-Z\d\s-::·,,/`~!@#$%^&*()_+<>?"{}.…;'[\]!¥(—);“”‘、|《。》?【】]+/; const cn_total_reg = /^[\u4e00-\u9fa5a-zA-Z\d\s-::·,,/`~!@#$%^&*()_+<>?"{}.…;'[\]!¥(—);“”‘、|《。》?【】]+$/; const symbol_delete_reg = /[-::·,,/`~!@#$%^&*()_+<>?"{}.…;[\]!¥(—);“”‘、|《。》?【】]/g; function parseURL(url) { let a; a = document.createElement('a'); a.href = url; return { source: url, protocol: a.protocol.replace(':', ''), host: a.hostname, port: a.port, query: a.search, params: (function () { let i, len, ret, s, seg; ret = {}; seg = a.search.replace(/^\?/, '').split('&'); len = seg.length; i = 0; s = void 0; while (i < len) { if (!seg[i]) { i++; continue; } s = seg[i].split('='); ret[s[0]] = s[1]; i++; } return ret; })(), file: (a.pathname.match(/\/([^\/?#]+)$/i) || [, ''])[1], hash: a.hash.replace('#', ''), path: a.pathname.replace(/^([^\/])/, '/$1'), relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [, ''])[1], segments: a.pathname.replace(/^\//, '').split('/'), }; } function update_bt_site(title, year, douban_ID, IMDb_ID, title_cn) { let name, sites; const siteList = document.querySelector('#content div.site-bt-body ul'); if (!siteList) return; title = title.trim(); const cnSearchKeyword = encodeURIComponent( get_cn_site_search_keyword(title_cn, title), ); sites = { 爱壹帆: 'https://www.yfsp.tv/search/' + cnSearchKeyword, 'BTDigg EN': 'https://www.btdig.com/search?q=' + title + ' ' + year + ' 1080p', 'BTDigg 中': 'https://www.btdig.com/search?q=' + title_cn, 'EXT.TO': 'https://ext.to/browse/?q=' + title, 独播库: 'https://www.dbku.tv/vodsearch/-------------.html?wd=' + cnSearchKeyword, 动漫花园: 'https://dmhy.org/topics/list?keyword=' + title, }; if (is_series(title)) { sites['BTDigg EN'] = 'https://www.btdig.com/search?q=' + title + ' 1080p'; } for (name in sites) { let link = parse_sites(name, sites); siteList.append(link); } } function get_cn_site_search_keyword(title_cn, title) { const keyword = title_cn.trim(); if (!keyword) return title; if (!/[\u4e00-\u9fa5]/.test(keyword)) return keyword; return keyword.replace(/\s+[a-zA-Z][\s\S]*$/, '').trim() || keyword; } function update_sub_site(title, douban_ID, IMDb_ID) { let name, sites; const siteList = document.querySelector('#content div.site-sub-body ul'); if (!siteList) return; title = encodeURI(title); sites = { SubHD: 'https://subhd.tv/d/' + douban_ID, 字幕库: 'https://srtku.com/search?q=' + IMDb_ID, 伪射手: 'https://assrt.net/sub/?searchword=' + title, }; for (name in sites) { let link = parse_sites(name, sites); siteList.append(link); } } function parse_sites(name, sites) { let link = sites[name], link_parsed = parseURL(link); const aTag = document.createElement('a'); aTag.href = link; aTag.dataset.host = link_parsed.host; aTag.target = '_blank'; aTag.rel = 'noopener noreferrer nofollow'; aTag.textContent = name; return aTag; } function get_other_title_en(other_title) { let other_title_en = ''; //获取第一个英文副标题 other_title.split('/').some((item) => { if (en_total_reg.test(item)) { other_title_en = item; return true; } }); return other_title_en; } function is_series(name) { return /S\d+$/.test(name); } function not_series_01(name) { return /S\d+$/.test(name) & !name.endsWith('S01'); } function format_series_name(name) { if (!/\sSeason\s\d+$/.test(name)) return name; let name_arr = name.split('Season'); let series_id = name_arr.slice(-1)[0].trim().padStart(2, '0'); return name_arr[0] + 'S' + series_id; } function create_aside_section(title, sectionClass) { const template = document.createElement('template'); template.innerHTML = aside_html.trim(); const section = template.content.firstElementChild; section.classList.add(sectionClass); section .querySelector('div.c-aside-body') .classList.add(sectionClass + '-body'); section.querySelector('h2 i').textContent = title; return section; } function on_ready(callback) { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', callback, { once: true }); return; } callback(); } function main() { const seBwhA = document.createElement('a'); seBwhA.id = 'seBwhA'; document.getElementsByTagName('html')[0].appendChild(seBwhA); on_ready(() => { const selector = document.querySelector('#content div.aside'); const h1_span = document.querySelectorAll('#content > h1 > span'); const info = document.querySelector('#info'); if (!selector || h1_span.length < 2 || !info) return; let site_sub = create_aside_section('字幕直达', 'site-sub'); site_sub.classList.add('name-offline'); selector.prepend(site_sub); let site_bt = create_aside_section('BT 搜索', 'site-bt'); site_bt.classList.add('site_bt'); selector.prepend(site_bt); let title_cn, title_en, title_en_sub, bt_title, year, douban_ID, IMDb_ID; let title_all = h1_span[0].textContent; if (cn_total_reg.test(title_all)) { //名称只有中英文时匹配英文—————————————— title_en = title_all.match(en_end_reg); title_en = title_en ? title_en[0] : ''; } if (title_en) { //有英文名时匹配中文—————————————— title_cn = title_en ? title_all.split(title_en)[0] : ''; } else { //直接匹配中文—————————————— title_cn = title_all.match(cn_start_reg); title_cn = title_cn ? title_cn[0] : ''; } //检查名称—————————————— if (title_all.length !== (title_en + title_cn).length) { title_cn = ''; let title_array = title_all.split(' '); title_array.some((item) => { if (!cn_total_reg.test(item)) return true; title_cn += item + ' '; }); title_en = ''; } // 中文标题若包含“第x季”,将序列名替换为搜索链接 if (title_cn) { const m = title_cn.match( /^(.+?)\s*第([一二三四五六七八九十百\d]+)季/, ); if (m && m[1]) { const seriesName = m[1].trim(); const searchUrl = 'https://search.douban.com/movie/subject_search?search_text=' + encodeURIComponent(seriesName); const titleSpanEl = h1_span[0]; const escapeForRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const leadingPattern = new RegExp( '^' + escapeForRegExp(seriesName), ); titleSpanEl.innerHTML = titleSpanEl.innerHTML.replace( leadingPattern, `${seriesName}`, ); } } //解析info内容 let info_text = info.innerText, info_map = {}; info_text.split('\n').forEach((line) => { let index = line.indexOf(':'); if (index > 0) info_map[line.slice(0, index).trim()] = line .slice(index + 1) .trim(); }); //匹配备用英文名—————————————— title_en_sub = info_map['又名']; title_en_sub = title_en_sub ? get_other_title_en(title_en_sub) : ''; bt_title = title_en || title_en_sub || title_cn; //规范的命名只保留英文字母 bt_title = bt_title .replaceAll(symbol_delete_reg, ' ') .replace("'", '') .replace(/\s+/g, ' ') .trim(); bt_title = format_series_name(bt_title); year = h1_span[1].textContent.substr(1, 4); douban_ID = location.href.split('/')[4] || title_cn; IMDb_ID = info_map['IMDb']; IMDb_ID = IMDb_ID ? IMDb_ID : title_cn; update_bt_site(bt_title, year, douban_ID, IMDb_ID, title_cn); update_sub_site(title_cn, douban_ID, IMDb_ID); }); } main();