// ==UserScript==
// @name 豆瓣侠
// @version 1.0.2
// @description 云盘搜索 + 电影/音乐/图书简介生成 + 查看原图统一修复 + 音乐封面生成修复
// @author 全网搜:wpys.cc
// @match https://movie.douban.com/subject/*
// @match https://book.douban.com/subject/*
// @match https://music.douban.com/subject/*
// @grant GM_setClipboard
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @connect api.wmdb.tv
// @require https://code.jquery.com/jquery-3.6.0.min.js
// ==/UserScript==
(function() {
'use strict';
GM_addStyle(`
.c-aside { margin-bottom: 30px; }
.c-aside h2 { font-size: 14px; color: #333; margin-bottom: 10px; }
.c-aside-body a {
border-radius: 6px; color: #006400; display: inline-block;
margin: 0 8px 8px 0; padding: 0 8px; text-align: center;
width: 65px; background-color: #f5f5f5; text-decoration: none;
font-size: 12px; line-height: 24px; font-weight: bold;
}
.c-aside-body a:hover { background-color: #e8e8e8; }
#md-generator {
margin: 20px 0;
padding: 15px;
background: #f9f9f9;
border-radius: 6px;
border-left: 4px solid #00a65a;
}
#md-generator-btn {
background: #00a65a; color: white; border: none;
padding: 8px 20px; border-radius: 4px; cursor: pointer;
font-size: 14px; font-weight: bold;
}
#md-generator-btn:disabled {
background: #ccc;
cursor: wait;
}
#md-generator-btn:hover:not(:disabled) { background: #008d4c; }
#md-preview {
margin-top: 15px; padding: 15px; background: #f5f5f5;
border-radius: 4px; border: 1px solid #ddd; display: none;
}
#md-preview pre {
margin: 0; white-space: pre-wrap; font-family: 'Courier New', monospace;
font-size: 13px; line-height: 1.6; background: #f5f5f5; padding: 10px;
}
.preview-header {
display: flex; justify-content: space-between; align-items: center;
margin-bottom: 10px; padding-bottom: 8px; border-bottom: 1px dashed #ccc;
}
.preview-header-left {
display: flex; align-items: center;
}
.preview-header-left span {
font-weight: bold;
margin-right: 15px;
}
#md-copy-btn {
background: #337ab7; color: white; border: none;
padding: 4px 12px; border-radius: 4px; cursor: pointer;
font-size: 12px;
}
#md-copy-btn:hover { background: #286090; }
#md-copy-btn.copied { background: #5cb85c; }
#loading-status {
margin-left: 10px;
font-size: 12px;
color: #666;
}
.success { color: #5cb85c; }
.error { color: #d9534f; }
#doudanxia-settings {
position: fixed; top: 80px; right: 20px; width: 380px;
background: white; border: 1px solid #ddd; border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 9999;
padding: 20px; font-size: 13px;
}
.settings-header {
display: flex; justify-content: space-between; align-items: center;
margin-bottom: 15px; padding-bottom: 10px; border-bottom: 2px solid #00a65a;
}
.settings-header h3 { margin: 0; color: #00a65a; }
.settings-section {
margin-bottom: 20px; padding: 12px; background: #f9f9f9; border-radius: 6px;
}
.site-tag {
display: inline-block; background: #e8e8e8; padding: 4px 12px;
border-radius: 20px; font-size: 12px; margin: 0 5px 5px 0;
}
.close-btn {
background: #f0f0f0; border: none; padding: 5px 20px;
border-radius: 20px; cursor: pointer; font-size: 12px;
}
.close-btn:hover { background: #e0e0e0; }
.view-original {
display: inline-block;
margin-left: 10px;
font-size: 12px;
color: #00a65a;
text-decoration: none;
}
.view-original:hover { text-decoration: underline; }
`);
const PAN_SITES = [
{ name: '网盘论坛', url: 'https://wpzy.cc/?q=' },
{ name: '网盘影视', url: 'https://wpzy.me/?q=' },
{ name: '网盘资源社', url: 'https://www.wpzy.cc/?q=' },
{ name: '全网搜索', url: 'https://wpys.cc/s/' },
{ name: '博哥影视', url: 'https://blog.wpys.cc/search/' }
];
const enableGenerator = GM_getValue('enable-generator', true);
$(document).ready(function() {
const pageType = location.host.split('.')[0];
const subjectId = location.href.match(/(\d{7,8})/);
if (!subjectId) return;
// 根据页面类型正确提取标题
let title = '';
if (pageType === 'music') {
let titleElement = $('#wrapper > h1 > span');
if (titleElement.length) {
title = titleElement.first().text().trim();
} else {
title = $('#content h1').first().text().trim();
}
} else if (pageType === 'book') {
title = $('#wrapper > h1 > span').first().text().trim();
} else {
title = $('#content h1 span').first().text().trim();
}
let searchTitle = title;
if (title.includes(' ')) {
searchTitle = title.split(' ')[0];
}
const searchKeyword = encodeURIComponent(searchTitle);
$('#content div.aside').prepend(`
云盘搜索· · · · · ·
${PAN_SITES.map(site =>
`
${site.name}`
).join('')}
`);
$("#db-global-nav > div > div.top-nav-info").append(`⚙️ 豆瓣侠设置`);
$("#doudanxia-setting-btn").click(showSettingsPanel);
// ===== 电影页面查看原图 =====
if (pageType === 'movie') {
setTimeout(function() {
if ($('#mainpic').length) {
let posterImg = document.querySelector('#mainpic > a > img');
if (posterImg) {
let postersUrl = posterImg.getAttribute('src');
let rawUrl = postersUrl.replace(/photo\/[sl](_ratio_poster|pic)\/public\/(p\d+).+$/, "photo/l/public/$2.jpg");
if (rawUrl === postersUrl) {
rawUrl = postersUrl.split('?')[0];
}
if ($('#mainpic p.gact').length === 0) {
$("#mainpic").append("");
}
$('#mainpic p.gact').after(`查看原图`);
}
}
}, 500);
}
// ===== 图书页面查看原图 =====
if (pageType === 'book') {
setTimeout(function() {
if ($('#mainpic').length) {
let posterAnchor = document.querySelector('#mainpic > a.nbg');
if (posterAnchor) {
let postersUrl = posterAnchor.getAttribute('href');
if ($('#mainpic p.gact').length === 0) {
$("#mainpic").append("");
}
$('#mainpic p.gact').after(`查看原图`);
}
}
}, 500);
}
// ===== 音乐页面查看原图(仿图书页面方式)=====
if (pageType === 'music') {
setTimeout(function() {
if ($('#mainpic').length) {
let posterAnchor = document.querySelector('#mainpic > a.nbg');
if (posterAnchor) {
let postersUrl = posterAnchor.getAttribute('href');
// 处理原图链接
let rawUrl = postersUrl;
if (postersUrl.includes('/m/')) {
rawUrl = postersUrl.replace('/m/', '/l/');
} else if (postersUrl.includes('/small/')) {
rawUrl = postersUrl.replace('/small/', '/large/');
} else if (postersUrl.includes('/icon/')) {
rawUrl = postersUrl.replace('/icon/', '/large/');
}
// 如果不存在 p.gact,就创建一个
if ($('#mainpic p.gact').length === 0) {
$("#mainpic").append("");
}
// 在 p.gact 后面添加查看原图链接
$('#mainpic p.gact').after(`查看原图`);
}
}
}, 500);
// 再加一个延迟执行,确保豆瓣的DOM完全加载
setTimeout(function() {
if ($('#mainpic').length && $('#mainpic p.gact').length && !$('#mainpic .view-original').length) {
let posterAnchor = document.querySelector('#mainpic > a.nbg') || document.querySelector('#mainpic a');
if (posterAnchor) {
let postersUrl = posterAnchor.getAttribute('href');
let rawUrl = postersUrl.replace('/m/', '/l/');
$('#mainpic p.gact').after(`查看原图`);
}
}
}, 1500);
}
// 电影页面:IMDb链接加回去
if (pageType === 'movie') {
let imdb_anchor = $('#info span.pl:contains("IMDb")');
if (imdb_anchor.length > 0) {
let imdb_text = imdb_anchor[0].nextSibling?.nodeValue;
if (imdb_text) {
let imdb_id = imdb_text.trim();
let imdb_link = `https://www.imdb.com/title/${imdb_id}/`;
$(imdb_anchor[0].nextSibling).replaceWith(` ${imdb_id}`);
}
}
}
if (enableGenerator) {
if (pageType === 'movie') {
insertMovieGenerator();
} else if (pageType === 'book') {
insertBookGenerator();
} else if (pageType === 'music') {
insertMusicGenerator();
}
}
});
function showSettingsPanel() {
if ($('#doudanxia-settings').length) {
$('#doudanxia-settings').remove();
return;
}
const sitesHtml = PAN_SITES.map(site =>
`${site.name}`
).join('');
const currentEnable = GM_getValue('enable-generator', true);
$('body').append(`
📁 云盘搜索 (5个站点)
${sitesHtml}
点击链接自动搜索当前资源
`);
$('#close-settings').click(() => $('#doudanxia-settings').remove());
$('#enable-generator').change(function() {
GM_setValue('enable-generator', $(this).is(':checked'));
if (confirm('设置已保存,是否刷新页面?')) location.reload();
});
$(document).mouseup(function(e) {
const container = $('#doudanxia-settings');
if (!container.is(e.target) && container.has(e.target).length === 0) {
container.remove();
}
});
}
// ===== 电影生成器 =====
function insertMovieGenerator() {
$('#info').after(`
`);
$('#md-generator-btn').click(async function() {
if ($('#md-preview').is(':visible')) {
$('#md-preview').slideUp(300);
$(this).text('📋 生成电影简介(带海报)');
} else {
const btn = $(this);
const status = $('#loading-status');
btn.prop('disabled', true).text('⏳ 获取数据...');
status.removeClass('success error').text('正在从WMDB获取数据...');
try {
await generateMovieMarkdown();
$('#md-preview').slideDown(300);
btn.text('📋 收缩预览');
status.addClass('success').text('✅ 生成成功');
} catch(e) {
status.addClass('error').text('❌ 生成失败: ' + e.message);
btn.text('📋 重试');
} finally {
btn.prop('disabled', false);
}
}
});
$('#md-copy-btn').click(copyToClipboard);
}
async function generateMovieMarkdown() {
const doubanId = window.location.href.match(/\/(\d{7,8})\//)?.[1];
if (!doubanId) throw new Error('未找到豆瓣ID');
const apiUrl = `https://api.wmdb.tv/movie/api?id=${doubanId}`;
const movieData = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url: apiUrl,
onload: function(res) {
if (res.status === 200) {
try {
const data = JSON.parse(res.responseText);
resolve(data);
} catch (e) {
reject(new Error('解析返回数据失败'));
}
} else {
reject(new Error(`API请求失败: ${res.status}`));
}
},
onerror: function() {
reject(new Error('网络错误'));
}
});
});
if (!movieData || !movieData.data) throw new Error('API返回数据为空');
const cnData = movieData.data.find(d => d.lang === 'Cn') || movieData.data[0];
let poster = '';
if (cnData?.poster) {
// 保持宽高比,宽度强制1080p,输出WebP格式
poster = `https://images.weserv.nl/?url=${encodeURIComponent(cnData.poster)}&w=1080&fit=inside&output=webp`;
}
let md = '';
if (poster) md += `\n\n`;
md += `◎片 名 ${cnData?.name || movieData.originalName || ''}\n`;
if (movieData.year) md += `◎年 代 ${movieData.year}\n`;
if (cnData?.country) md += `◎产 地 ${cnData.country}\n`;
if (cnData?.genre) md += `◎类 别 ${cnData.genre.replace(/\//g, ' / ')}\n`;
if (cnData?.language) md += `◎语 言 ${cnData.language}\n`;
if (movieData.dateReleased) md += `◎上映日期 ${movieData.dateReleased}\n`;
if (movieData.imdbId) md += `◎IMDb链接 \n`;
if (movieData.doubanRating) {
md += `◎豆瓣评分 ${movieData.doubanRating}/10 from ${movieData.doubanVotes || 0} users\n`;
}
md += `◎豆瓣链接 https://movie.douban.com/subject/${doubanId}/\n`;
if (movieData.duration) {
const minutes = Math.floor(movieData.duration / 60);
md += `◎片 长 ${minutes}分钟\n`;
}
if (movieData.director?.[0]?.data) {
const directors = movieData.director[0].data
.filter(d => d.lang === 'Cn')
.map(d => d.name)
.join(' / ');
if (directors) md += `◎导 演 ${directors}\n`;
}
if (movieData.writer?.[0]?.data) {
const writers = movieData.writer[0].data
.filter(w => w.lang === 'Cn')
.map(w => w.name)
.join(' / ');
if (writers) md += `◎编 剧 ${writers}\n`;
}
if (movieData.actor?.length) {
const actors = movieData.actor
.slice(0, 10)
.map(a => a.data?.find(d => d.lang === 'Cn')?.name)
.filter(Boolean)
.join(' / ');
if (actors) {
md += `◎主 演 ${actors}\n`;
}
}
if (cnData?.description) {
md += `\n◎简 介\n\n`;
md += ` ${cnData.description.replace(/\n/g, '\n ')}\n`;
}
$('#md-content').text(md);
}
// ===== 图书生成器(带清除浮动)=====
function insertBookGenerator() {
$('#info').after(`
`);
$('#md-generator-btn').click(function() {
if ($('#md-preview').is(':visible')) {
$('#md-preview').slideUp(300);
$(this).text('📋 生成图书简介(带封面)');
} else {
generateBookMarkdown();
$('#md-preview').slideDown(300);
$(this).text('📋 收缩预览');
}
});
$('#md-copy-btn').click(copyToClipboard);
}
function generateBookMarkdown() {
const data = {};
data.title = $('#wrapper > h1 > span').first().text().trim();
// 封面:用豆瓣的图,走代理
let coverImg = document.querySelector('#mainpic > a > img');
if (coverImg) {
let coverUrl = coverImg.getAttribute('src').split('?')[0];
// 保持宽高比,宽度强制1080p,输出WebP格式
data.cover = `https://images.weserv.nl/?url=${encodeURIComponent(coverUrl)}&w=1080&fit=inside&output=webp`;
}
const infoText = $('#info').text();
// 作者
data.author = $('#info a[href*="search?cat=1003"]').map(function() {
return $(this).text().trim();
}).get().join(' / ');
const publisherMatch = infoText.match(/出版社:\s*([^\n]+)/);
data.publisher = publisherMatch ? publisherMatch[1].trim() : '';
const seriesMatch = infoText.match(/丛书:\s*([^\n]+)/);
data.series = seriesMatch ? seriesMatch[1].trim() : '';
const isbnMatch = infoText.match(/ISBN:\s*([0-9X-]+)/);
data.isbn = isbnMatch ? isbnMatch[1].trim() : '';
data.rating = $('strong.ll.rating_num').text().trim();
data.votes = $('a.rating_people span').text().trim();
// 简介(过滤掉脚本和样式)
let introElement = $('#link-report .all').length ? $('#link-report .all') : $('#link-report');
let introRaw = introElement.clone()
.find('script, style, .btn-report, .link-report')
.remove()
.end()
.html() || '';
let introText = introRaw.replace(/<[^>]+>/g, ' ')
.replace(/\s+/g, ' ')
.replace(/ /g, ' ')
.trim();
const doubanId = window.location.href.match(/\d{7,8}/)?.[0] || '';
let md = '';
if (data.cover) md += `\n\n`;
md += `◎书 名 ${data.title}\n`;
if (data.author) md += `◎作 者 ${data.author}\n`;
if (data.publisher) md += `◎出版社 ${data.publisher}\n`;
if (data.series) md += `◎丛 书 ${data.series}\n`;
if (data.isbn) md += `◎ISBN ${data.isbn}\n`;
if (data.rating) md += `◎豆瓣评分 ${data.rating}/10 from ${data.votes} users\n`;
md += `◎豆瓣链接 https://book.douban.com/subject/${doubanId}/\n\n`;
if (introText) md += `◎简 介\n\n ${introText}\n`;
$('#md-content').text(md);
}
// ===== 音乐生成器(带清除浮动,使用和图书页面完全一样的封面生成方式)=====
function insertMusicGenerator() {
$('#info').after(`
`);
$('#md-generator-btn').click(function() {
if ($('#md-preview').is(':visible')) {
$('#md-preview').slideUp(300);
$(this).text('📋 生成音乐简介(带封面)');
} else {
generateMusicMarkdown();
$('#md-preview').slideDown(300);
$(this).text('📋 收缩预览');
}
});
$('#md-copy-btn').click(copyToClipboard);
}
function generateMusicMarkdown() {
const data = {};
// 标题
let titleElement = $('#wrapper > h1 > span');
if (titleElement.length) {
data.title = titleElement.first().text().trim();
} else {
data.title = $('#content h1').first().text().trim();
}
// ===== 封面获取:使用和图书页面完全一样的代码 =====
let coverImg = document.querySelector('#mainpic > a > img');
if (!coverImg) {
coverImg = document.querySelector('#mainpic img');
}
if (coverImg) {
let coverUrl = coverImg.getAttribute('src');
if (coverUrl) {
// 去掉URL参数
coverUrl = coverUrl.split('?')[0];
// 处理缩略图链接,获取大图
if (coverUrl.includes('/m/')) {
coverUrl = coverUrl.replace('/m/', '/l/');
} else if (coverUrl.includes('/small/')) {
coverUrl = coverUrl.replace('/small/', '/large/');
}
// 保持宽高比,宽度强制1080p,输出WebP格式
data.cover = `https://images.weserv.nl/?url=${encodeURIComponent(coverUrl)}&w=1080&fit=inside&output=webp`;
}
}
// 表演者
data.artist = $('#info a[href*="search?cat=1003"]').first().text().trim();
const infoText = $('#info').text();
const typeMatch = infoText.match(/专辑类型:\s*([^\n]+)/);
data.type = typeMatch ? typeMatch[1].trim() : '';
const mediaMatch = infoText.match(/介质:\s*([^\n]+)/);
data.media = mediaMatch ? mediaMatch[1].trim() : '';
const dateMatch = infoText.match(/发行时间:\s*([^\n]+)/);
data.date = dateMatch ? dateMatch[1].trim() : '';
const publisherMatch = infoText.match(/出版者:\s*([^\n]+)/);
data.publisher = publisherMatch ? publisherMatch[1].trim() : '';
data.rating = $('strong.ll.rating_num').text().trim();
data.votes = $('a.rating_people span').text().trim();
// 曲目
data.tracks = [];
$('.track-list li, #content .song-item').each(function() {
const track = $(this).text().trim();
if (track) data.tracks.push(track);
});
// 简介(过滤掉脚本和样式)
let introElement = $('#link-report .all').length ? $('#link-report .all') : $('#link-report');
let introRaw = introElement.clone()
.find('script, style, .btn-report, .link-report')
.remove()
.end()
.html() || '';
let introText = introRaw.replace(/<[^>]+>/g, ' ')
.replace(/\s+/g, ' ')
.replace(/ /g, ' ')
.trim();
const doubanId = window.location.href.match(/\d{7,8}/)?.[0] || '';
let md = '';
// ===== 添加封面(和图书页面完全一样)=====
if (data.cover) {
md += `\n\n`;
}
md += `◎专辑名称 ${data.title}\n`;
if (data.artist) md += `◎表演者 ${data.artist}\n`;
if (data.type) md += `◎专辑类型 ${data.type}\n`;
if (data.media) md += `◎介质 ${data.media}\n`;
if (data.date) md += `◎发行时间 ${data.date}\n`;
if (data.publisher) md += `◎出版者 ${data.publisher}\n`;
if (data.rating) md += `◎豆瓣评分 ${data.rating}/10 from ${data.votes} users\n`;
md += `◎豆瓣链接 https://music.douban.com/subject/${doubanId}/\n\n`;
if (data.tracks.length) {
md += `◎曲 目\n\n`;
data.tracks.forEach((track, i) => {
md += ` ${i+1}. ${track}\n`;
});
md += `\n`;
}
if (introText) {
md += `◎简 介\n\n ${introText}\n`;
}
$('#md-content').text(md);
}
function copyToClipboard() {
const text = $('#md-content').text();
if (!text) return;
const textWithBreaks = text + '\n\n\n';
GM_setClipboard(textWithBreaks);
const btn = $('#md-copy-btn');
btn.addClass('copied').text('✅ 复制成功');
setTimeout(() => {
btn.removeClass('copied').text('复制到剪贴板');
}, 2000);
}
})();