Update app.py
Browse files
app.py
CHANGED
|
@@ -921,6 +921,8 @@ HTML_TEMPLATE = r"""<!DOCTYPE html>
|
|
| 921 |
.unlock-result { background: #0f0f0f; border-radius: 12px; padding: 12px; margin-top: 12px; font-size: 13px; word-break: break-all; border-left: 3px solid #e50914; }
|
| 922 |
.unlock-result a { color: #e50914; text-decoration: none; }
|
| 923 |
.copy-result-btn { background: #555; border: none; border-radius: 16px; padding: 4px 12px; color: #fff; cursor: pointer; font-size: 11px; margin-left: 10px; }
|
|
|
|
|
|
|
| 924 |
.two-columns { display: flex; gap: 20px; flex-wrap: wrap; }
|
| 925 |
.left-panel, .right-panel { flex: 1; min-width: 280px; background: #141414; border-radius: 16px; border: 1px solid #2a2a2a; overflow: visible; }
|
| 926 |
.panel-header { padding: 14px 18px; border-bottom: 1px solid #2a2a2a; display: flex; justify-content: space-between; align-items: baseline; }
|
|
@@ -1192,6 +1194,17 @@ HTML_TEMPLATE = r"""<!DOCTYPE html>
|
|
| 1192 |
setTimeout(() => { toast.style.opacity = '0'; }, 3000);
|
| 1193 |
}
|
| 1194 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1195 |
// 日志相关
|
| 1196 |
let logRefreshInterval = null;
|
| 1197 |
async function fetchLogs() {
|
|
@@ -1275,7 +1288,6 @@ HTML_TEMPLATE = r"""<!DOCTYPE html>
|
|
| 1275 |
const ALLOWED_PAN_TYPES=['115','baidu','magnet'];
|
| 1276 |
function extractLinkFromResource(r){return r.link||r.download_url||r.url||r.pan_url||'';}
|
| 1277 |
function detectPanTypeFromLink(link){if(!link)return'unknown';let l=link.toLowerCase();if(l.includes('pan.baidu.com')||l.includes('baidu.com/s/'))return'baidu';if(l.includes('115.com'))return'115';if(l.includes('magnet:')||l.includes('ed2k://'))return'magnet';return'unknown';}
|
| 1278 |
-
function getPanBadgeHtml(panType){let map={baidu:['百度网盘','🐻','pan-badge-baidu'],115:['115网盘','☁️','pan-badge-115'],magnet:['磁力链接','🧲','pan-badge-magnet']};let[name,icon,cls]=map[panType]||['其他网盘','📎','pan-badge-unknown'];return`<span class="pan-badge ${cls}">${icon} ${name}</span>`;}
|
| 1279 |
// 加载 TMDB 类型
|
| 1280 |
async function loadMovieGenres() {
|
| 1281 |
try { let resp = await fetch('/api/tmdb/genres/movie'); let data = await resp.json(); movieGenres = data; } catch(e) { console.error(e); movieGenres = []; }
|
|
@@ -1620,12 +1632,9 @@ HTML_TEMPLATE = r"""<!DOCTYPE html>
|
|
| 1620 |
// ========== 快捷解锁功能 ==========
|
| 1621 |
function extractSlugFromUrl(input) {
|
| 1622 |
let trimmed = input.trim();
|
| 1623 |
-
// 如果是纯 slug (32位十六进制)
|
| 1624 |
if (/^[a-f0-9]{32}$/i.test(trimmed)) return trimmed;
|
| 1625 |
-
// 尝试匹配 URL 格式: /resource/115/xxxx 或 /resource/xxxx
|
| 1626 |
let match = trimmed.match(/\/resource\/(?:[^\/]+\/)?([a-f0-9]{32})/i);
|
| 1627 |
if (match) return match[1];
|
| 1628 |
-
// 最后尝试匹配任意32位十六进制串
|
| 1629 |
match = trimmed.match(/([a-f0-9]{32})/i);
|
| 1630 |
return match ? match[1] : null;
|
| 1631 |
}
|
|
@@ -1640,11 +1649,9 @@ HTML_TEMPLATE = r"""<!DOCTYPE html>
|
|
| 1640 |
showToast('未能提取到有效的 slug (32位十六进制)', true);
|
| 1641 |
return;
|
| 1642 |
}
|
| 1643 |
-
// 显示正在解锁
|
| 1644 |
let resultDiv = document.getElementById('quickUnlockResult');
|
| 1645 |
resultDiv.style.display = 'block';
|
| 1646 |
resultDiv.innerHTML = '<div style="color:#aaa;">🔓 解锁中...</div>';
|
| 1647 |
-
// 检查是否需要密码
|
| 1648 |
if (typeof UNLOCK_PASSWORD_REQUIRED !== 'undefined' && UNLOCK_PASSWORD_REQUIRED === true) {
|
| 1649 |
showPasswordModal({type:'quickUnlock', slug: slug});
|
| 1650 |
} else {
|
|
@@ -1668,10 +1675,22 @@ HTML_TEMPLATE = r"""<!DOCTYPE html>
|
|
| 1668 |
}
|
| 1669 |
let link = data.link || data.data?.full_url || data.data?.url || data.url;
|
| 1670 |
if (link) {
|
| 1671 |
-
// 保存到 localStorage
|
| 1672 |
localStorage.setItem(`unlock_${slug}`, link);
|
| 1673 |
let cacheInfo = data.from_cache ? '<span style="color:#2ecc71;">📦 来自缓存,不消耗积分</span>' : '';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1674 |
resultDiv.innerHTML = `
|
|
|
|
|
|
|
|
|
|
| 1675 |
<div><strong>✅ 解锁成功</strong> ${cacheInfo}</div>
|
| 1676 |
<div style="margin-top:8px; word-break:break-all;">
|
| 1677 |
<a href="${link}" target="_blank">${link}</a>
|
|
@@ -1717,8 +1736,6 @@ HTML_TEMPLATE = r"""<!DOCTYPE html>
|
|
| 1717 |
async function updateCountdown(){
|
| 1718 |
try { let r=await fetch('/api/next-unlock-time'); let d=await r.json(); let rem=d.remaining||0; if(rem>0){ let m=Math.floor(rem/60); let s=rem%60; document.getElementById('next-unlock-countdown').innerText=`${m}:${s.toString().padStart(2,'0')}`; } else document.getElementById('next-unlock-countdown').innerText='0:00'; } catch(e){}
|
| 1719 |
}
|
| 1720 |
-
// 检测是否需要密码(通过检查后端是否设置了密码,可调用一个接口,但简单起见:如果 UNLOCK_PASSWORD 不为空,则需要)
|
| 1721 |
-
// 这里通过一个变量控制,实际由后端密码弹窗逻辑接管。默认认为可能需要密码,会在调用解锁时弹出。
|
| 1722 |
// 初始化
|
| 1723 |
(async function init() {
|
| 1724 |
await Promise.all([loadMovieGenres(), loadTVGenres()]);
|
|
|
|
| 921 |
.unlock-result { background: #0f0f0f; border-radius: 12px; padding: 12px; margin-top: 12px; font-size: 13px; word-break: break-all; border-left: 3px solid #e50914; }
|
| 922 |
.unlock-result a { color: #e50914; text-decoration: none; }
|
| 923 |
.copy-result-btn { background: #555; border: none; border-radius: 16px; padding: 4px 12px; color: #fff; cursor: pointer; font-size: 11px; margin-left: 10px; }
|
| 924 |
+
.resource-detail { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; flex-wrap: wrap; }
|
| 925 |
+
.resource-detail .pan-badge { font-size: 12px; }
|
| 926 |
.two-columns { display: flex; gap: 20px; flex-wrap: wrap; }
|
| 927 |
.left-panel, .right-panel { flex: 1; min-width: 280px; background: #141414; border-radius: 16px; border: 1px solid #2a2a2a; overflow: visible; }
|
| 928 |
.panel-header { padding: 14px 18px; border-bottom: 1px solid #2a2a2a; display: flex; justify-content: space-between; align-items: baseline; }
|
|
|
|
| 1194 |
setTimeout(() => { toast.style.opacity = '0'; }, 3000);
|
| 1195 |
}
|
| 1196 |
|
| 1197 |
+
// 获取网盘徽章 HTML
|
| 1198 |
+
function getPanBadgeHtml(panType) {
|
| 1199 |
+
const map = {
|
| 1200 |
+
baidu: ['百度网盘', '🐻', 'pan-badge-baidu'],
|
| 1201 |
+
115: ['115网盘', '☁️', 'pan-badge-115'],
|
| 1202 |
+
magnet: ['磁力链接', '🧲', 'pan-badge-magnet']
|
| 1203 |
+
};
|
| 1204 |
+
let [name, icon, cls] = map[panType] || ['其他网盘', '📎', 'pan-badge-unknown'];
|
| 1205 |
+
return `<span class="pan-badge ${cls}">${icon} ${name}</span>`;
|
| 1206 |
+
}
|
| 1207 |
+
|
| 1208 |
// 日志相关
|
| 1209 |
let logRefreshInterval = null;
|
| 1210 |
async function fetchLogs() {
|
|
|
|
| 1288 |
const ALLOWED_PAN_TYPES=['115','baidu','magnet'];
|
| 1289 |
function extractLinkFromResource(r){return r.link||r.download_url||r.url||r.pan_url||'';}
|
| 1290 |
function detectPanTypeFromLink(link){if(!link)return'unknown';let l=link.toLowerCase();if(l.includes('pan.baidu.com')||l.includes('baidu.com/s/'))return'baidu';if(l.includes('115.com'))return'115';if(l.includes('magnet:')||l.includes('ed2k://'))return'magnet';return'unknown';}
|
|
|
|
| 1291 |
// 加载 TMDB 类型
|
| 1292 |
async function loadMovieGenres() {
|
| 1293 |
try { let resp = await fetch('/api/tmdb/genres/movie'); let data = await resp.json(); movieGenres = data; } catch(e) { console.error(e); movieGenres = []; }
|
|
|
|
| 1632 |
// ========== 快捷解锁功能 ==========
|
| 1633 |
function extractSlugFromUrl(input) {
|
| 1634 |
let trimmed = input.trim();
|
|
|
|
| 1635 |
if (/^[a-f0-9]{32}$/i.test(trimmed)) return trimmed;
|
|
|
|
| 1636 |
let match = trimmed.match(/\/resource\/(?:[^\/]+\/)?([a-f0-9]{32})/i);
|
| 1637 |
if (match) return match[1];
|
|
|
|
| 1638 |
match = trimmed.match(/([a-f0-9]{32})/i);
|
| 1639 |
return match ? match[1] : null;
|
| 1640 |
}
|
|
|
|
| 1649 |
showToast('未能提取到有效的 slug (32位十六进制)', true);
|
| 1650 |
return;
|
| 1651 |
}
|
|
|
|
| 1652 |
let resultDiv = document.getElementById('quickUnlockResult');
|
| 1653 |
resultDiv.style.display = 'block';
|
| 1654 |
resultDiv.innerHTML = '<div style="color:#aaa;">🔓 解锁中...</div>';
|
|
|
|
| 1655 |
if (typeof UNLOCK_PASSWORD_REQUIRED !== 'undefined' && UNLOCK_PASSWORD_REQUIRED === true) {
|
| 1656 |
showPasswordModal({type:'quickUnlock', slug: slug});
|
| 1657 |
} else {
|
|
|
|
| 1675 |
}
|
| 1676 |
let link = data.link || data.data?.full_url || data.data?.url || data.url;
|
| 1677 |
if (link) {
|
|
|
|
| 1678 |
localStorage.setItem(`unlock_${slug}`, link);
|
| 1679 |
let cacheInfo = data.from_cache ? '<span style="color:#2ecc71;">📦 来自缓存,不消耗积分</span>' : '';
|
| 1680 |
+
// 提取资源标题和网盘类型(从 data.data 中)
|
| 1681 |
+
let resourceTitle = data.data?.title || data.data?.name || '未知资源';
|
| 1682 |
+
let panType = data.data?.pan_type || 'unknown';
|
| 1683 |
+
if (panType === 'unknown' && link) {
|
| 1684 |
+
let l = link.toLowerCase();
|
| 1685 |
+
if (l.includes('115.com')) panType = '115';
|
| 1686 |
+
else if (l.includes('pan.baidu.com')) panType = 'baidu';
|
| 1687 |
+
else if (l.includes('magnet:')) panType = 'magnet';
|
| 1688 |
+
}
|
| 1689 |
+
let panBadge = getPanBadgeHtml(panType);
|
| 1690 |
resultDiv.innerHTML = `
|
| 1691 |
+
<div class="resource-detail">
|
| 1692 |
+
<strong>📽️ ${escapeHtml(resourceTitle)}</strong> ${panBadge}
|
| 1693 |
+
</div>
|
| 1694 |
<div><strong>✅ 解锁成功</strong> ${cacheInfo}</div>
|
| 1695 |
<div style="margin-top:8px; word-break:break-all;">
|
| 1696 |
<a href="${link}" target="_blank">${link}</a>
|
|
|
|
| 1736 |
async function updateCountdown(){
|
| 1737 |
try { let r=await fetch('/api/next-unlock-time'); let d=await r.json(); let rem=d.remaining||0; if(rem>0){ let m=Math.floor(rem/60); let s=rem%60; document.getElementById('next-unlock-countdown').innerText=`${m}:${s.toString().padStart(2,'0')}`; } else document.getElementById('next-unlock-countdown').innerText='0:00'; } catch(e){}
|
| 1738 |
}
|
|
|
|
|
|
|
| 1739 |
// 初始化
|
| 1740 |
(async function init() {
|
| 1741 |
await Promise.all([loadMovieGenres(), loadTVGenres()]);
|