TV / index.html
samlax12's picture
Upload 30 files
7eff83b verified
raw
history blame contribute delete
27 kB
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LibreTV - 免费在线视频搜索与观看平台</title>
<meta name="description" content="LibreTV是一个免费的在线视频搜索平台,无广告、安全,提供来自多个视频源的内容搜索与观看服务,无需注册即可使用。">
<meta name="keywords" content="在线视频,免费视频,视频搜索,电影,电视剧,LibreTV">
<meta name="author" content="LibreTV Team">
<meta name="robots" content="index, follow">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://libretv.is-an.org/">
<meta property="og:title" content="LibreTV - 免费在线视频搜索与观看平台">
<meta property="og:description" content="搜索并观看来自多个视频源的内容,支持多种设备,无需注册即可使用。">
<meta property="og:image" content="https://images.icon-icons.com/38/PNG/512/retrotv_5520.png">
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:url" content="https://libretv.is-an.org/">
<meta property="twitter:title" content="LibreTV - 免费在线视频搜索与观看平台">
<meta property="twitter:description" content="搜索并观看来自多个视频源的内容,支持多种设备,无需注册即可使用。">
<meta property="twitter:image" content="https://images.icon-icons.com/38/PNG/512/retrotv_5520.png">
<!-- Favicon -->
<link rel="icon" href="https://images.icon-icons.com/38/PNG/512/retrotv_5520.png">
<link rel="apple-touch-icon" href="https://images.icon-icons.com/38/PNG/512/retrotv_5520.png">
<!-- Canonical URL -->
<link rel="canonical" href="https://libretv.is-an.org/">
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="css/styles.css">
</head>
<body class="page-bg text-white">
<!-- 将历史记录按钮移到左上角,并缩小尺寸 -->
<div class="fixed top-4 left-4 z-50">
<button onclick="toggleHistory(event)" class="bg-[#222] hover:bg-[#333] border border-[#333] hover:border-white rounded-lg px-3 py-1.5 transition-colors" aria-label="观看历史">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</button>
</div>
<!-- 设置按钮保留在右上角,并缩小尺寸 -->
<div class="fixed top-4 right-4 z-50">
<button onclick="toggleSettings(event)" class="bg-[#222] hover:bg-[#333] border border-[#333] hover:border-white rounded-lg px-3 py-1.5 transition-colors" aria-label="打开设置">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
</svg>
</button>
</div>
<!-- 历史记录面板 - 标题居中 -->
<div id="historyPanel" class="history-panel fixed left-0 top-0 h-full bg-[#111] border-r border-[#333] p-6 z-40 transform -translate-x-full transition-transform duration-300" aria-label="观看历史" aria-hidden="true">
<div class="flex justify-between items-center mb-6">
<button onclick="toggleHistory()" class="text-gray-400 hover:text-white" aria-label="关闭历史">&times;</button>
<h3 class="text-xl font-bold gradient-text mx-auto">观看历史</h3>
<div class="w-4"></div> <!-- 添加一个占位元素以确保标题居中 -->
</div>
<div id="historyList" class="pb-4">
<!-- 历史记录将在这里动态显示 -->
<div class="text-center text-gray-500 py-8">暂无观看记录</div>
</div>
<div class="mt-4 text-center sticky bottom-0 pb-2 pt-2 bg-[#111]">
<button onclick="clearViewingHistory()" class="px-4 py-2 w-full bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 hover:from-indigo-600 hover:via-purple-600 hover:to-pink-600 text-white rounded-lg text-sm transition-all duration-300 shadow-md hover:shadow-lg">
清空历史记录
</button>
</div>
</div>
<!-- 设置面板 -->
<div id="settingsPanel" class="settings-panel fixed right-0 top-0 h-full w-80 bg-[#111] border-l border-[#333] p-6 z-40 overflow-y-auto" aria-label="设置面板" aria-hidden="true">
<div class="flex justify-between items-center mb-6">
<h3 class="text-xl font-bold gradient-text">设置</h3>
<button onclick="toggleSettings()" class="text-gray-400 hover:text-white" aria-label="关闭设置">&times;</button>
</div>
<div class="space-y-5">
<!-- 数据源设置区域 -->
<div class="p-3 bg-[#151515] rounded-lg shadow-inner">
<label class="block text-sm font-medium text-gray-400 mb-3 border-b border-[#333] pb-1">数据源设置</label>
<!-- 批量操作按钮 -->
<div class="flex space-x-2 mb-3">
<button onclick="selectAllAPIs(true)" class="px-2 py-1 bg-[#333] hover:bg-[#444] text-white text-xs rounded">全选</button>
<button onclick="selectAllAPIs(false)" class="px-2 py-1 bg-[#333] hover:bg-[#444] text-white text-xs rounded">全不选</button>
<button onclick="selectAllAPIs(true, true)" class="px-2 py-1 bg-[#333] hover:bg-[#444] text-white text-xs rounded">全选普通资源</button>
</div>
<!-- API选择区域 - 使用滚动区域 -->
<div class="max-h-40 overflow-y-auto bg-[#191919] p-2 rounded-lg mb-3">
<div id="apiCheckboxes" class="grid grid-cols-2 gap-2">
<!-- 这里将动态插入API复选框 -->
</div>
</div>
<!-- API信息显示 -->
<div class="text-xs text-gray-500 flex justify-between items-center">
<span>已选API数量:<span id="selectedApiCount" class="text-white">0</span></span>
<span id="siteStatus" class="ml-2"></span>
</div>
</div>
<!-- 自定义API管理区域 -->
<div class="p-3 bg-[#151515] rounded-lg shadow-inner">
<div class="flex justify-between items-center mb-2">
<label class="block text-sm font-medium text-gray-400 border-b border-[#333] w-full pb-1">自定义API</label>
<button onclick="showAddCustomApiForm()" class="bg-[#333] hover:bg-[#444] text-white w-6 h-6 rounded-full text-center leading-none text-lg ml-1">+</button>
</div>
<div id="customApisList" class="max-h-32 overflow-y-auto mb-2">
<!-- 自定义API将显示在这里 -->
</div>
<!-- 添加自定义API表单 (默认隐藏) -->
<div id="addCustomApiForm" class="hidden mt-2 p-2 bg-[#191919] rounded-lg">
<input type="text" id="customApiName" placeholder="API名称" class="w-full bg-[#222] border border-[#333] text-white px-2 py-1 rounded mb-2" autocomplete="off">
<input type="text" id="customApiUrl" placeholder="https://abc.com" class="w-full bg-[#222] border border-[#333] text-white px-2 py-1 rounded mb-2" autocomplete="off">
<!-- 添加成人内容切换 -->
<div class="flex items-center mb-2">
<input type="checkbox" id="customApiIsAdult" class="form-checkbox h-4 w-4 text-pink-500 bg-[#222] border border-[#333]">
<label for="customApiIsAdult" class="ml-2 text-xs text-pink-400">黄色资源站</label>
</div>
<div class="flex space-x-2">
<button onclick="addCustomApi()" class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded text-xs">添加</button>
<button onclick="cancelAddCustomApi()" class="bg-[#444] hover:bg-[#555] text-white px-3 py-1 rounded text-xs">取消</button>
</div>
</div>
</div>
<!-- 内容过滤设置区域 -->
<div class="p-3 bg-[#151515] rounded-lg shadow-inner">
<label class="block text-sm font-medium text-gray-400 mb-3 border-b border-[#333] pb-1">功能开关</label>
<!-- 黄色内容过滤开关 -->
<div class="flex flex-col mb-3 pb-3 border-b border-[#222] relative">
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-gray-400">黄色内容过滤</label>
<p class="text-xs text-gray-500 mt-1 filter-description">过滤"伦理片"等黄色内容</p>
</div>
<div class="relative inline-block w-12 align-middle select-none">
<input type="checkbox" id="yellowFilterToggle" class="opacity-0 absolute w-full h-full cursor-pointer z-10">
<div class="toggle-bg bg-[#333] w-12 h-6 rounded-full transition-colors duration-300 ease-in-out"></div>
<div class="toggle-dot absolute w-5 h-5 bg-white rounded-full top-0.5 left-0.5 transition-transform duration-300 ease-in-out"></div>
</div>
</div>
<!-- 警告提示将在这里动态插入 -->
</div>
<!-- 广告过滤开关 -->
<div class="flex items-center justify-between mb-3 pb-3 border-b border-[#222]">
<div>
<label class="text-sm font-medium text-gray-400">分片广告过滤</label>
<p class="text-xs text-gray-500 mt-1">关闭可减少旧版浏览器卡顿</p>
</div>
<div class="relative inline-block w-12 align-middle select-none">
<input type="checkbox" id="adFilterToggle" class="opacity-0 absolute w-full h-full cursor-pointer z-10">
<div class="toggle-bg bg-[#333] w-12 h-6 rounded-full transition-colors duration-300 ease-in-out"></div>
<div class="toggle-dot absolute w-5 h-5 bg-white rounded-full top-0.5 left-0.5 transition-transform duration-300 ease-in-out"></div>
</div>
</div>
<!-- 豆瓣热门开关 -->
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-gray-400">豆瓣热门推荐</label>
<p class="text-xs text-gray-500 mt-1">首页显示豆瓣热门影视内容</p>
</div>
<div class="relative inline-block w-12 align-middle select-none">
<input type="checkbox" id="doubanToggle" class="opacity-0 absolute w-full h-full cursor-pointer z-10">
<div class="toggle-bg bg-[#333] w-12 h-6 rounded-full transition-colors duration-300 ease-in-out"></div>
<div class="toggle-dot absolute w-5 h-5 bg-white rounded-full top-0.5 left-0.5 transition-transform duration-300 ease-in-out"></div>
</div>
</div>
</div>
</div>
</div>
<div class="container mx-auto px-4 py-8 flex flex-col h-screen">
<div class="flex-1 flex flex-col">
<!-- 网站标志和口号 -->
<header class="text-center mb-2">
<div class="flex justify-center items-center mb-4">
<a href="#" onclick="resetToHome(); return false;" class="flex items-center">
<svg class="w-10 h-10 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
</svg>
<h1 class="text-5xl font-bold gradient-text">LibreTV</h1>
</a>
</div>
<p class="text-gray-400 mb-8">自由观影,畅享精彩</p>
</header>
<div id="searchArea" class="flex-1 flex flex-col items-center justify-center">
<div class="w-full max-w-2xl">
<div class="flex items-stretch mb-3 h-14 shadow-lg rounded-lg overflow-hidden">
<!-- 首页按钮 -->
<button onclick="resetToHome()"
class="w-20 sm:w-24 flex items-center justify-center bg-white text-black font-medium hover:bg-gray-200 transition-colors"
aria-label="返回首页" title="返回首页">
首页
</button>
<!-- 搜索输入 -->
<input type="text"
id="searchInput"
class="flex-1 bg-[#111] border-y border-[#333] text-white px-6 py-0 focus:outline-none transition-colors"
placeholder="搜索你喜欢的视频..."
autocomplete="off"
aria-label="视频搜索框">
<!-- 搜索按钮 -->
<button onclick="search()"
class="w-20 sm:w-24 flex items-center justify-center bg-white text-black font-medium hover:bg-gray-200 transition-colors"
aria-label="搜索按钮">
搜索
</button>
</div>
<!-- 添加最近搜索记录部分 -->
<div id="recentSearches" class="mt-4 flex flex-wrap gap-2" aria-label="最近搜索记录">
<!-- 这里会动态插入最近的搜索记录 -->
</div>
</div>
</div>
<!-- 豆瓣热门推荐区域: 默认隐藏,现在位于搜索区域下方,调整宽度 -->
<div id="doubanArea" class="w-full my-8 hidden">
<div class="mx-auto max-w-screen-xl px-2">
<!-- 改进标题和标签区域布局 -->
<div class="mb-4">
<!-- 标题和刷新按钮一行 -->
<div class="flex items-center justify-between mb-3">
<h2 class="text-xl font-bold text-white">豆瓣热门</h2>
<button id="douban-refresh" class="text-sm px-3 py-1 bg-pink-600 hover:bg-pink-700 text-white rounded-lg flex items-center gap-1">
<span>换一批</span>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
</button>
</div>
<!-- 分类标签独立成行,添加滚动支持以适应移动设备 -->
<div class="overflow-x-auto pb-2">
<div id="douban-tags" class="flex space-x-2 min-w-max"></div>
</div>
</div>
<!-- 推荐内容 -->
<div id="douban-results" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-3"></div>
</div>
</div>
<!-- 搜索结果:初始隐藏 -->
<div id="resultsArea" class="w-full hidden">
<div class="mx-auto max-w-7xl px-2"> <!-- 添加最大宽度限制并居中 -->
<div class="flex justify-end items-center mb-4">
<div class="text-sm text-gray-400">
<span id="searchResultsCount">0</span> 个结果
</div>
</div>
<div id="results" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-3">
<!-- 结果将在这里动态生成 -->
</div>
</div>
</div>
</div>
</div>
<!-- 页脚区域 -->
<footer class="footer mt-8 py-6 border-t border-[#333] bg-[#0a0a0a]">
<div class="container mx-auto px-4">
<div class="flex flex-col md:flex-row justify-between items-center">
<div class="mb-4 md:mb-0">
<div class="flex items-center justify-center md:justify-start">
<svg class="w-6 h-6 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
</svg>
<span class="gradient-text font-bold">LibreTV</span>
</div>
<p class="text-gray-500 text-sm mt-2 text-center md:text-left">© 2025 LibreTV - 自由观影,畅享精彩</p>
</div>
<div class="text-center md:text-right">
<p class="text-gray-500 text-sm max-w-md">
免责声明:本站仅为视频搜索工具,不存储、上传或分发任何视频内容。
所有视频均来自第三方API接口。如有侵权,请联系相关内容提供方。
</p>
<div class="mt-2 flex justify-center md:justify-end space-x-4">
<a href="about.html" class="text-gray-400 hover:text-white text-sm transition-colors">关于我们</a>
<a href="privacy.html" class="text-gray-400 hover:text-white text-sm transition-colors">隐私政策</a>
</div>
</div>
</div>
</div>
</footer>
<!-- 详情模态框 -->
<div id="modal" class="fixed inset-0 bg-black/95 hidden flex items-center justify-center transition-opacity duration-300">
<div class="bg-[#111] p-8 rounded-lg w-11/12 max-w-4xl border border-[#333] max-h-[90vh] flex flex-col">
<div class="flex justify-between items-center mb-6 flex-none">
<h2 id="modalTitle" class="text-2xl font-bold gradient-text break-words pr-4 max-w-[80%]"></h2>
<button onclick="closeModal()" class="text-gray-400 hover:text-white text-2xl transition-colors flex-shrink-0">&times;</button>
</div>
<div id="modalContent" class="overflow-auto flex-1 min-h-0">
<div class="grid grid-cols-2 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-8 gap-2">
</div>
</div>
</div>
</div>
<!-- 密码验证弹窗 -->
<div id="passwordModal" class="fixed inset-0 bg-black/95 hidden items-center justify-center z-[65] transition-opacity duration-300">
<div class="bg-[#111] p-8 rounded-lg w-11/12 max-w-md border border-[#333] max-h-[90vh] flex flex-col">
<div class="flex justify-between items-center mb-6 flex-none">
<h2 class="text-2xl font-bold gradient-text">访问验证</h2>
</div>
<div class="mb-6">
<p class="text-gray-300 mb-4">请输入密码继续访问</p>
<form id="passwordForm" onsubmit="handlePasswordSubmit(); return false;">
<input type="text" name="username" id="username" autocomplete="username" style="display:none" tabindex="-1" aria-hidden="true">
<input type="password" id="passwordInput" class="w-full bg-[#111] border border-[#333] text-white px-4 py-3 rounded-lg focus:outline-none focus:border-white transition-colors" placeholder="密码..." autocomplete="new-password">
<button id="passwordSubmitBtn" type="submit" class="mt-4 w-full bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded">提交</button>
</form>
<p id="passwordError" class="text-red-500 mt-2 hidden">密码错误,请重试</p>
</div>
</div>
</div>
<!-- 版权声明弹窗 -->
<div id="disclaimerModal" class="fixed inset-0 bg-black/90 hidden items-center justify-center z-[60]">
<div class="bg-[#111] p-8 rounded-lg border border-[#333] w-11/12 max-w-2xl max-h-[90vh] overflow-y-auto">
<h2 class="text-2xl font-bold gradient-text mb-6 text-center">使用声明</h2>
<div class="text-gray-300 space-y-4">
<p>
欢迎使用 LibreTV。在开始使用前,请您了解并同意以下条款:
</p>
<p>
<strong class="text-blue-400">服务性质:</strong> LibreTV 仅提供视频搜索服务,不直接提供、存储或上传任何视频内容。所有搜索结果均来自第三方公开接口。
</p>
<p>
<strong class="text-blue-400">用户责任:</strong> 用户在使用本站服务时,须遵守相关法律法规,不得利用搜索结果从事侵权行为,如下载、传播未经授权的作品等。
</p>
<p>
<strong class="text-blue-400">侵权投诉:</strong> 若您是版权方或相关权利人,发现搜索结果中存在侵犯您合法权益的内容,请通过 <a href="mailto:[email protected]" class="text-blue-400 hover:underline">[email protected]</a> 向我们反馈。我们将在收到投诉后尽快核实处理。
</p>
</div>
<div class="mt-6 flex justify-center">
<button id="acceptDisclaimerBtn" class="px-6 py-3 bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 text-white font-semibold rounded-lg hover:shadow-lg transition-all duration-300">
我已了解并接受
</button>
</div>
</div>
</div>
<!-- 错误提示框 -->
<div id="toast" class="fixed top-4 left-1/2 -translate-x-1/2 bg-red-500 text-white px-6 py-3 rounded-lg shadow-lg transform transition-all duration-300 opacity-0 -translate-y-full">
<p id="toastMessage"></p>
</div>
<!-- 添加 loading 提示框 -->
<div id="loading" class="fixed inset-0 bg-black/80 hidden items-center justify-center z-50">
<div class="bg-[#111] p-8 rounded-lg border border-[#333] flex items-center space-x-4">
<div class="w-8 h-8 border-4 border-white border-t-transparent rounded-full animate-spin"></div>
<p class="text-white text-lg">加载中...</p>
</div>
</div>
<!-- JSON-LD 结构化数据 -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "LibreTV",
"url": "https://libretv.is-an.org/",
"description": "免费在线视频搜索与观看平台",
"potentialAction": {
"@type": "SearchAction",
"target": "https://libretv.is-an.org/?s={search_term_string}",
"query-input": "required name=search_term_string"
}
}
</script>
<!-- 引入纯 JS sha256(HTTP 下依然可用) -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/sha256.min.js"></script>
<script>
// 保存原始 js‑sha256 实现,避免被 password.js 覆盖
window._jsSha256 = window.sha256;
</script>
<script src="js/config.js"></script>
<script src="js/ui.js"></script>
<script src="js/api.js"></script>
<script src="js/douban.js"></script>
<script src="js/password.js"></script>
<script src="js/app.js"></script>
<!-- 环境变量注入脚本 -->
<script>
// 创建全局环境变量对象
window.__ENV__ = window.__ENV__ || {};
// 注入服务器端环境变量 (将由服务器端替换)
// PASSWORD 变量将在这里被服务器端注入
window.__ENV__.PASSWORD = "{{PASSWORD}}";
</script>
<!-- 弹窗显示脚本 -->
<script>
// 在页面加载完成后检查用户是否首次访问
document.addEventListener('DOMContentLoaded', function() {
// 检查用户是否已经看过声明
const hasSeenDisclaimer = localStorage.getItem('hasSeenDisclaimer');
if (!hasSeenDisclaimer) {
// 显示弹窗
const disclaimerModal = document.getElementById('disclaimerModal');
disclaimerModal.style.display = 'flex';
// 添加接受按钮事件
document.getElementById('acceptDisclaimerBtn').addEventListener('click', function() {
// 保存用户已看过声明的状态
localStorage.setItem('hasSeenDisclaimer', 'true');
// 隐藏弹窗
disclaimerModal.style.display = 'none';
});
}
});
</script>
</body>
</html>