|
|
|
|
|
|
|
const doubanTags = ['热门', '最新', '经典', '豆瓣高分','古装剧', '冷门佳片', '科幻' ,'喜剧', '综艺', '欧美', '电视剧', '韩国', '日本', '动漫','动画片']; |
|
let doubanCurrentTag = localStorage.getItem('doubanCurrentTag') || '热门'; |
|
let doubanPageStart = 0; |
|
const doubanPageSize = 16; |
|
|
|
|
|
function initDouban() { |
|
|
|
const doubanToggle = document.getElementById('doubanToggle'); |
|
if (doubanToggle) { |
|
const isEnabled = localStorage.getItem('doubanEnabled') === 'true'; |
|
doubanToggle.checked = isEnabled; |
|
|
|
|
|
const toggleBg = doubanToggle.nextElementSibling; |
|
const toggleDot = toggleBg.nextElementSibling; |
|
if (isEnabled) { |
|
toggleBg.classList.add('bg-pink-600'); |
|
toggleDot.classList.add('translate-x-6'); |
|
} |
|
|
|
|
|
doubanToggle.addEventListener('change', function(e) { |
|
const isChecked = e.target.checked; |
|
localStorage.setItem('doubanEnabled', isChecked); |
|
|
|
|
|
if (isChecked) { |
|
toggleBg.classList.add('bg-pink-600'); |
|
toggleDot.classList.add('translate-x-6'); |
|
} else { |
|
toggleBg.classList.remove('bg-pink-600'); |
|
toggleDot.classList.remove('translate-x-6'); |
|
} |
|
|
|
|
|
updateDoubanVisibility(); |
|
}); |
|
|
|
|
|
updateDoubanVisibility(); |
|
} |
|
|
|
|
|
renderDoubanTags(); |
|
|
|
|
|
setupDoubanRefreshBtn(); |
|
|
|
|
|
if (localStorage.getItem('doubanEnabled') === 'true') { |
|
renderRecommend(doubanCurrentTag, doubanPageSize, doubanPageStart); |
|
} |
|
} |
|
|
|
|
|
function updateDoubanVisibility() { |
|
const doubanArea = document.getElementById('doubanArea'); |
|
if (!doubanArea) return; |
|
|
|
const isEnabled = localStorage.getItem('doubanEnabled') === 'true'; |
|
const isSearching = document.getElementById('resultsArea') && |
|
!document.getElementById('resultsArea').classList.contains('hidden'); |
|
|
|
|
|
if (isEnabled && !isSearching) { |
|
doubanArea.classList.remove('hidden'); |
|
|
|
if (document.getElementById('douban-results').children.length === 0) { |
|
renderRecommend(doubanCurrentTag, doubanPageSize, doubanPageStart); |
|
} |
|
} else { |
|
doubanArea.classList.add('hidden'); |
|
} |
|
} |
|
|
|
|
|
function fillSearchInput(title) { |
|
if (!title) return; |
|
|
|
|
|
const safeTitle = title |
|
.replace(/</g, '<') |
|
.replace(/>/g, '>') |
|
.replace(/"/g, '"'); |
|
|
|
const input = document.getElementById('searchInput'); |
|
if (input) { |
|
input.value = safeTitle; |
|
|
|
|
|
input.focus(); |
|
|
|
|
|
showToast('已填充搜索内容,点击搜索按钮开始搜索', 'info'); |
|
} |
|
} |
|
|
|
|
|
function fillAndSearch(title) { |
|
if (!title) return; |
|
|
|
|
|
const safeTitle = title |
|
.replace(/</g, '<') |
|
.replace(/>/g, '>') |
|
.replace(/"/g, '"'); |
|
|
|
const input = document.getElementById('searchInput'); |
|
if (input) { |
|
input.value = safeTitle; |
|
search(); |
|
} |
|
} |
|
|
|
|
|
function fillAndSearchWithDouban(title) { |
|
if (!title) return; |
|
|
|
|
|
const safeTitle = title |
|
.replace(/</g, '<') |
|
.replace(/>/g, '>') |
|
.replace(/"/g, '"'); |
|
|
|
|
|
if (typeof selectedAPIs !== 'undefined' && !selectedAPIs.includes('dbzy')) { |
|
|
|
const doubanCheckbox = document.querySelector('input[id="api_dbzy"]'); |
|
if (doubanCheckbox) { |
|
doubanCheckbox.checked = true; |
|
|
|
|
|
if (typeof updateSelectedAPIs === 'function') { |
|
updateSelectedAPIs(); |
|
} else { |
|
|
|
selectedAPIs.push('dbzy'); |
|
localStorage.setItem('selectedAPIs', JSON.stringify(selectedAPIs)); |
|
|
|
|
|
const countEl = document.getElementById('selectedAPICount'); |
|
if (countEl) { |
|
countEl.textContent = selectedAPIs.length; |
|
} |
|
} |
|
|
|
showToast('已自动选择豆瓣资源API', 'info'); |
|
} |
|
} |
|
|
|
|
|
const input = document.getElementById('searchInput'); |
|
if (input) { |
|
input.value = safeTitle; |
|
search(); |
|
} |
|
} |
|
|
|
|
|
function renderDoubanTags() { |
|
const tagContainer = document.getElementById('douban-tags'); |
|
if (!tagContainer) return; |
|
|
|
tagContainer.innerHTML = ''; |
|
|
|
doubanTags.forEach(tag => { |
|
const btn = document.createElement('button'); |
|
|
|
btn.className = 'py-1.5 px-3.5 rounded text-sm font-medium transition-all duration-300 ' + |
|
(tag === doubanCurrentTag ? |
|
'bg-pink-600 text-white shadow-md' : |
|
'bg-[#1a1a1a] text-gray-300 hover:bg-pink-700 hover:text-white'); |
|
|
|
btn.textContent = tag; |
|
|
|
btn.onclick = function() { |
|
if (doubanCurrentTag !== tag) { |
|
doubanCurrentTag = tag; |
|
localStorage.setItem('doubanCurrentTag', tag); |
|
doubanPageStart = 0; |
|
renderRecommend(doubanCurrentTag, doubanPageSize, doubanPageStart); |
|
renderDoubanTags(); |
|
} |
|
}; |
|
|
|
tagContainer.appendChild(btn); |
|
}); |
|
} |
|
|
|
|
|
function setupDoubanRefreshBtn() { |
|
|
|
const btn = document.getElementById('douban-refresh'); |
|
if (!btn) return; |
|
|
|
btn.onclick = function() { |
|
doubanPageStart += doubanPageSize; |
|
if (doubanPageStart > 9 * doubanPageSize) { |
|
doubanPageStart = 0; |
|
} |
|
|
|
renderRecommend(doubanCurrentTag, doubanPageSize, doubanPageStart); |
|
}; |
|
} |
|
|
|
|
|
function renderRecommend(tag, pageLimit, pageStart) { |
|
const container = document.getElementById("douban-results"); |
|
if (!container) return; |
|
|
|
|
|
container.innerHTML = ` |
|
<div class="col-span-full text-center py-10"> |
|
<div class="w-6 h-6 border-2 border-pink-500 border-t-transparent rounded-full animate-spin mr-2 inline-block"></div> |
|
<span class="text-pink-500">加载中...</span> |
|
</div> |
|
`; |
|
|
|
const target = `https://movie.douban.com/j/search_subjects?type=movie&tag=${tag}&sort=recommend&page_limit=${pageLimit}&page_start=${pageStart}`; |
|
|
|
|
|
const controller = new AbortController(); |
|
const timeoutId = setTimeout(() => controller.abort(), 10000); |
|
|
|
|
|
const fetchOptions = { |
|
signal: controller.signal, |
|
headers: { |
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36', |
|
'Referer': 'https://movie.douban.com/', |
|
'Accept': 'application/json, text/plain, */*', |
|
} |
|
}; |
|
|
|
|
|
fetch(PROXY_URL + encodeURIComponent(target), fetchOptions) |
|
.then(response => { |
|
clearTimeout(timeoutId); |
|
if (!response.ok) { |
|
throw new Error(`HTTP error! Status: ${response.status}`); |
|
} |
|
return response.json(); |
|
}) |
|
.then(data => { |
|
renderDoubanCards(data, container); |
|
}) |
|
.catch(err => { |
|
console.error("豆瓣 API 请求失败(直接代理):", err); |
|
|
|
|
|
const fallbackUrl = `https://api.allorigins.win/get?url=${encodeURIComponent(target)}`; |
|
|
|
fetch(fallbackUrl) |
|
.then(response => { |
|
if (!response.ok) throw new Error(`备用API请求失败! 状态: ${response.status}`); |
|
return response.json(); |
|
}) |
|
.then(data => { |
|
|
|
if (data && data.contents) { |
|
const doubanData = JSON.parse(data.contents); |
|
renderDoubanCards(doubanData, container); |
|
} else { |
|
throw new Error("无法获取有效数据"); |
|
} |
|
}) |
|
.catch(fallbackErr => { |
|
console.error("豆瓣 API 备用请求也失败:", fallbackErr); |
|
container.innerHTML = ` |
|
<div class="col-span-full text-center py-8"> |
|
<div class="text-red-400">❌ 获取豆瓣数据失败,请稍后重试</div> |
|
<div class="text-gray-500 text-sm mt-2">提示:使用VPN可能有助于解决此问题</div> |
|
</div> |
|
`; |
|
}); |
|
}); |
|
} |
|
|
|
|
|
function renderDoubanCards(data, container) { |
|
|
|
const fragment = document.createDocumentFragment(); |
|
|
|
|
|
if (!data.subjects || data.subjects.length === 0) { |
|
const emptyEl = document.createElement("div"); |
|
emptyEl.className = "col-span-full text-center py-8"; |
|
emptyEl.innerHTML = ` |
|
<div class="text-pink-500">❌ 暂无数据,请尝试其他分类或刷新</div> |
|
`; |
|
fragment.appendChild(emptyEl); |
|
} else { |
|
|
|
data.subjects.forEach(item => { |
|
const card = document.createElement("div"); |
|
card.className = "bg-[#111] hover:bg-[#222] transition-all duration-300 rounded-lg overflow-hidden flex flex-col transform hover:scale-105 shadow-md hover:shadow-lg"; |
|
|
|
|
|
const safeTitle = item.title |
|
.replace(/</g, '<') |
|
.replace(/>/g, '>') |
|
.replace(/"/g, '"'); |
|
|
|
const safeRate = (item.rate || "暂无") |
|
.replace(/</g, '<') |
|
.replace(/>/g, '>'); |
|
|
|
|
|
|
|
const originalCoverUrl = item.cover; |
|
|
|
|
|
const proxiedCoverUrl = PROXY_URL + encodeURIComponent(originalCoverUrl); |
|
|
|
|
|
card.innerHTML = ` |
|
<div class="relative w-full aspect-[2/3] overflow-hidden cursor-pointer" onclick="fillAndSearchWithDouban('${safeTitle}')"> |
|
<img src="${originalCoverUrl}" alt="${safeTitle}" |
|
class="w-full h-full object-cover transition-transform duration-500 hover:scale-110" |
|
onerror="this.onerror=null; this.src='${proxiedCoverUrl}'; this.classList.add('object-contain');" |
|
loading="lazy" referrerpolicy="no-referrer"> |
|
<div class="absolute inset-0 bg-gradient-to-t from-black to-transparent opacity-60"></div> |
|
<div class="absolute bottom-2 left-2 bg-black/70 text-white text-xs px-2 py-1 rounded-sm"> |
|
<span class="text-yellow-400">★</span> ${safeRate} |
|
</div> |
|
<div class="absolute bottom-2 right-2 bg-black/70 text-white text-xs px-2 py-1 rounded-sm hover:bg-[#333] transition-colors"> |
|
<a href="${item.url}" target="_blank" rel="noopener noreferrer" title="在豆瓣查看"> |
|
🔗 |
|
</a> |
|
</div> |
|
</div> |
|
<div class="p-2 text-center bg-[#111]"> |
|
<button onclick="fillAndSearchWithDouban('${safeTitle}')" |
|
class="text-sm font-medium text-white truncate w-full hover:text-pink-400 transition" |
|
title="${safeTitle}"> |
|
${safeTitle} |
|
</button> |
|
</div> |
|
`; |
|
|
|
fragment.appendChild(card); |
|
}); |
|
} |
|
|
|
|
|
container.innerHTML = ""; |
|
container.appendChild(fragment); |
|
} |
|
|
|
|
|
function resetToHome() { |
|
resetSearchArea(); |
|
updateDoubanVisibility(); |
|
} |
|
|
|
|
|
document.addEventListener('DOMContentLoaded', initDouban); |