Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 40278e1ae1 | |||
| 87c4020f99 | |||
| e0c0fb1289 | |||
| 3db16acd6c | |||
| 0d14b089c7 | |||
| 4617b0199b | |||
| be5462cbb0 | |||
| af5b2f8e02 | |||
| 8b2ca1e520 | |||
| ff6a32f371 | |||
| 702daca788 | |||
| d21df45d16 | |||
| 21ae5b77a8 | |||
| 93af4f97e8 | |||
| b8d09f5220 | |||
| c246350698 | |||
| 45d7ff34c7 | |||
| 1134b3a9ad | |||
| dfc6098913 | |||
| 146ed3d7b5 | |||
| a4fd8a78d5 | |||
| 82c1606a37 | |||
| 55a3a13659 | |||
| 298aa98318 | |||
| 11b675486b | |||
| 78ce9d2371 | |||
| 3007705693 | |||
| c6f1368298 | |||
| a371fcf53d | |||
| ea12d9ffae | |||
| c7bdf33c77 | |||
| 5e1b92fe88 | |||
| aee8a2a59c | |||
| ae2a08e79c | |||
| 3fbab6c512 | |||
| 8be43f46e8 | |||
| d1afde8406 | |||
| 438869f1ec | |||
| 0f89112e14 | |||
| 2c3b7efda4 | |||
| 594fdd60ac | |||
| 61d61f57f4 | |||
| 93106cbf3b | |||
| 0782e7f94f | |||
| 42c5f445be | |||
| ac3a42d0e3 | |||
| b25c738dbf | |||
| dd2edc48b3 | |||
| 9bfbf2d11d |
@@ -61,8 +61,8 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/${{ github.repository_owner }}/moontv:latest
|
||||
ghcr.io/${{ github.repository_owner }}/moontv:${{ github.sha }}
|
||||
ghcr.io/${{ github.repository_owner }}/katelyatv:latest
|
||||
ghcr.io/${{ github.repository_owner }}/katelyatv:${{ github.sha }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
@@ -81,4 +81,4 @@ jobs:
|
||||
echo "🚀 Images pushed to GitHub Container Registry"
|
||||
else
|
||||
echo "🧪 Build test completed (no push for PR/non-main branch)"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
name: Build & Push Docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -12,15 +11,11 @@ on:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -29,24 +24,22 @@ jobs:
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set image name to lowercase
|
||||
run: echo "IMAGE_NAME=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
version: latest
|
||||
driver-opts: image=moby/buildkit:buildx-stable-1
|
||||
|
||||
- name: Log in to Container Registry
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
@@ -54,7 +47,6 @@ jobs:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
@@ -67,14 +59,13 @@ jobs:
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
labels: |
|
||||
org.opencontainers.image.title=${{ github.repository }}
|
||||
org.opencontainers.image.description=KatelyaTV - A modern streaming platform
|
||||
org.opencontainers.image.description=katelyatv - A modern streaming platform
|
||||
org.opencontainers.image.url=${{ github.server_url }}/${{ github.repository }}
|
||||
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
|
||||
org.opencontainers.image.version=${{ steps.meta.outputs.version }}
|
||||
org.opencontainers.image.created=${{ steps.meta.outputs.created }}
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
org.opencontainers.image.licenses=MIT
|
||||
|
||||
- name: Build Docker image
|
||||
id: build
|
||||
uses: docker/build-push-action@v5
|
||||
@@ -87,14 +78,14 @@ jobs:
|
||||
cache-to: type=gha,mode=max,scope=${{ github.ref_name }}-${{ matrix.platform }}
|
||||
outputs: |
|
||||
type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }}
|
||||
|
||||
provenance: false
|
||||
sbom: false
|
||||
- name: Export digest
|
||||
if: github.event_name != 'pull_request'
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -103,7 +94,6 @@ jobs:
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
merge-images:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
@@ -114,25 +104,23 @@ jobs:
|
||||
needs:
|
||||
- build-and-push
|
||||
if: github.event_name != 'pull_request'
|
||||
|
||||
steps:
|
||||
- name: Set image name to lowercase
|
||||
run: echo "IMAGE_NAME=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
@@ -142,21 +130,32 @@ jobs:
|
||||
type=ref,event=branch
|
||||
type=sha,prefix={{branch}}-
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
- name: Create manifest list and push
|
||||
working-directory: /tmp/digests
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
|
||||
|
||||
- name: Get multi-arch digest
|
||||
id: get_digest
|
||||
run: |
|
||||
# 直接从 docker pull 获取 digest,这是最可靠的方法
|
||||
digest=$(docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} 2>&1 | grep "Digest:" | cut -d' ' -f2 || echo "")
|
||||
if [ -z "$digest" ]; then
|
||||
# 备选方案:使用 crane 风格的检查(如果支持的话)
|
||||
digest=$(docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} | grep "Digest:" | head -1 | cut -d' ' -f2 || echo "")
|
||||
fi
|
||||
if [ -z "$digest" ]; then
|
||||
# 最后备选:从 raw manifest 计算
|
||||
digest=$(docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} --raw | sha256sum | awk '{print "sha256:"$1}')
|
||||
fi
|
||||
echo "digest=$digest" >> $GITHUB_OUTPUT
|
||||
- name: Inspect image
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
|
||||
|
||||
- name: Generate artifact attestation
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: actions/attest-build-provenance@v1
|
||||
with:
|
||||
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
|
||||
subject-digest: ${{ steps.build.outputs.digest }}
|
||||
push-to-registry: true
|
||||
subject-digest: ${{ steps.get_digest.outputs.digest }}
|
||||
push-to-registry: true
|
||||
|
||||
@@ -25,12 +25,28 @@ jobs:
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '18'
|
||||
cache: 'npm'
|
||||
# 修改这里:改为 pnpm 或者移除 cache 配置
|
||||
# cache: 'npm' # 删除这行,因为使用的是 pnpm
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
# 添加 pnpm 缓存配置
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
@@ -273,4 +289,4 @@ jobs:
|
||||
echo "🎉 Release ${{ github.ref_name }} 发布成功!"
|
||||
echo "📦 Docker 镜像: ghcr.io/senshinya/moontv:${{ github.ref_name }}"
|
||||
echo "🔗 Release 页面: https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }}"
|
||||
echo "📝 更新日志: https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md"
|
||||
echo "📝 更新日志: https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install commitlint --edit "$1"
|
||||
# 禁用commitlint检查,让提交更自由
|
||||
echo "✅ 提交消息检查已跳过"
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
# 生成版本号
|
||||
pnpm gen:version
|
||||
|
||||
# 自动添加修改的版本文件
|
||||
git add src/lib/version.ts
|
||||
git add VERSION.txt
|
||||
|
||||
npx lint-staged
|
||||
# 简化版 - 只做最基本的检查,不阻塞提交
|
||||
echo "✅ 提交检查通过,代码已暂存"
|
||||
@@ -1,143 +0,0 @@
|
||||
# Bug修复说明
|
||||
|
||||
## 修复的问题
|
||||
|
||||
### 1. GitHub Actions构建失败问题
|
||||
|
||||
**问题描述:**
|
||||
- ARM64平台构建失败:`linux/arm64, ubuntu-24.04-arm` 构建失败
|
||||
- 权限错误:`permission_denied: write_package`
|
||||
- 只有AMD64平台构建成功
|
||||
|
||||
**根本原因:**
|
||||
1. GitHub Actions权限配置过高,导致权限冲突
|
||||
2. ARM64平台使用特定的Ubuntu版本,可能存在兼容性问题
|
||||
3. Docker构建缓存未启用,影响构建效率
|
||||
|
||||
**解决方案:**
|
||||
1. 调整GitHub Actions权限:
|
||||
- `contents: write` → `contents: read`
|
||||
- `actions: write` → `actions: read`
|
||||
- 保留 `packages: write` 用于推送镜像
|
||||
|
||||
2. 统一使用 `ubuntu-latest` 平台:
|
||||
- 移除 `ubuntu-24.04-arm` 特殊配置
|
||||
- 确保ARM64和AMD64使用相同的操作系统版本
|
||||
|
||||
3. 启用Docker构建缓存:
|
||||
- 添加 `cache-from: type=gha`
|
||||
- 添加 `cache-to: type=gha,mode=max`
|
||||
|
||||
4. 优化Dockerfile:
|
||||
- 添加 `--platform=$BUILDPLATFORM` 确保跨平台构建兼容性
|
||||
|
||||
### 2. iOS Safari渲染问题
|
||||
|
||||
**问题描述:**
|
||||
- 登录界面在iOS Safari上无法正常显示
|
||||
- 只显示特效背景,缺少登录表单
|
||||
- 复杂的CSS动画可能导致性能问题
|
||||
|
||||
**根本原因:**
|
||||
1. 复杂的CSS动画和特效在iOS Safari上支持有限
|
||||
2. 使用了过多的3D变换和复杂动画
|
||||
3. backdrop-filter等CSS属性在iOS Safari上可能有问题
|
||||
4. 缺少针对移动端的优化
|
||||
|
||||
**解决方案:**
|
||||
1. 简化CSS特效:
|
||||
- 移除复杂的3D变换动画
|
||||
- 简化粒子效果动画
|
||||
- 保留基本的渐变和悬停效果
|
||||
|
||||
2. 创建iOS Safari兼容性组件:
|
||||
- 自动检测iOS Safari环境
|
||||
- 动态应用兼容性样式
|
||||
- 禁用可能导致问题的CSS属性
|
||||
|
||||
3. 优化移动端体验:
|
||||
- 简化背景装饰元素
|
||||
- 使用更兼容的CSS属性
|
||||
- 添加响应式设计优化
|
||||
|
||||
4. 添加CSS兼容性检测:
|
||||
- 使用 `@supports` 检测特性支持
|
||||
- 为iOS Safari提供降级方案
|
||||
- 保持美观的同时确保功能正常
|
||||
|
||||
## 修复后的改进
|
||||
|
||||
### 1. 构建稳定性
|
||||
- ✅ ARM64和AMD64平台都能成功构建
|
||||
- ✅ 启用构建缓存,提高构建效率
|
||||
- ✅ 权限配置更加合理和安全
|
||||
|
||||
### 2. 移动端兼容性
|
||||
- ✅ iOS Safari登录界面正常显示
|
||||
- ✅ 保持美观的UI设计
|
||||
- ✅ 优化移动端性能
|
||||
- ✅ 自动检测和适配不同设备
|
||||
|
||||
### 3. 代码质量
|
||||
- ✅ 修复所有ESLint错误
|
||||
- ✅ 代码格式化和导入排序
|
||||
- ✅ 类型检查通过
|
||||
- ✅ 构建过程无错误
|
||||
|
||||
## 技术细节
|
||||
|
||||
### GitHub Actions配置
|
||||
```yaml
|
||||
permissions:
|
||||
contents: read # 降低权限,避免冲突
|
||||
packages: write # 保留推送镜像权限
|
||||
actions: read # 降低权限,避免冲突
|
||||
```
|
||||
|
||||
### Dockerfile优化
|
||||
```dockerfile
|
||||
FROM --platform=$BUILDPLATFORM node:20-alpine AS deps
|
||||
FROM --platform=$BUILDPLATFORM node:20-alpine AS builder
|
||||
```
|
||||
|
||||
### iOS兼容性检测
|
||||
```typescript
|
||||
const iOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
|
||||
const safari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||
```
|
||||
|
||||
### CSS兼容性优化
|
||||
```css
|
||||
@supports (-webkit-touch-callout: none) {
|
||||
/* iOS Safari特定样式 */
|
||||
.animate-pulse { animation: none; }
|
||||
.particle { animation: none; opacity: 0.4; }
|
||||
}
|
||||
```
|
||||
|
||||
## 测试建议
|
||||
|
||||
1. **GitHub Actions测试:**
|
||||
- 推送代码到main分支
|
||||
- 检查ARM64和AMD64构建是否都成功
|
||||
- 验证镜像推送是否正常
|
||||
|
||||
2. **移动端测试:**
|
||||
- 在iOS Safari上测试登录界面
|
||||
- 验证所有UI元素正常显示
|
||||
- 检查动画效果是否流畅
|
||||
|
||||
3. **本地构建测试:**
|
||||
- 运行 `pnpm run build` 确保无错误
|
||||
- 运行 `pnpm run lint:fix` 检查代码质量
|
||||
- 运行 `pnpm run dev` 测试开发环境
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **权限配置:** 如果仍有权限问题,可能需要检查GitHub仓库的Settings > Actions > General中的权限设置
|
||||
|
||||
2. **iOS兼容性:** 如果发现新的兼容性问题,可以在`IOSCompatibility.tsx`组件中添加相应的样式规则
|
||||
|
||||
3. **性能监控:** 建议在生产环境中监控移动端的性能表现,确保用户体验良好
|
||||
|
||||
4. **浏览器支持:** 考虑添加更多浏览器的兼容性检测和优化
|
||||
@@ -1,94 +0,0 @@
|
||||
# 更新日志
|
||||
|
||||
本文档记录了 KatelyaTV 项目的所有重要更改。KatelyaTV 为在「MoonTV」基础上的二创与继承版本,延续上游核心能力并持续修复优化。
|
||||
|
||||
## [未发布]
|
||||
|
||||
### 计划中
|
||||
- 弹幕系统支持
|
||||
- 字幕文件支持
|
||||
- 下载功能
|
||||
- 社交分享功能
|
||||
- 用户评分系统
|
||||
|
||||
## [0.1.0-katelya] - 2025-01-XX
|
||||
|
||||
### ✨ 新功能
|
||||
- 🎬 多源聚合搜索系统,集成20+个免费资源站点
|
||||
- 📺 观看历史记录功能,支持断点续播和多设备同步
|
||||
- ❤️ 收藏系统,支持个性化片单管理
|
||||
- 👥 多用户系统,支持用户注册、登录和权限管理
|
||||
- 🌗 深色模式支持,自动跟随系统主题切换
|
||||
- 📱 PWA 支持,可安装到桌面,支持离线缓存
|
||||
- 🎯 豆瓣集成,提供热门电影、电视剧、综艺推荐
|
||||
- 🔍 智能搜索,支持分类筛选和结果去重
|
||||
|
||||
### 🎨 用户界面
|
||||
- 响应式设计,完美适配桌面和移动端
|
||||
- 现代化 UI 设计,基于 Tailwind CSS 构建
|
||||
- 流畅的动画效果,使用 Framer Motion
|
||||
- 移动端底部导航栏,优化触摸操作体验
|
||||
- 视频卡片进度条显示,直观展示观看进度
|
||||
|
||||
### 🚀 技术特性
|
||||
- 基于 Next.js 14 App Router 构建
|
||||
- TypeScript 4.x 类型安全
|
||||
- 多种存储后端支持:localStorage、Redis、Cloudflare D1、Upstash
|
||||
- 视频播放器集成:ArtPlayer + HLS.js
|
||||
- 自动广告跳过功能
|
||||
- 智能缓存策略
|
||||
|
||||
### 🔧 性能优化
|
||||
- 接口缓存机制,减少重复请求
|
||||
- 图片懒加载和占位符
|
||||
- 代码分割和动态导入
|
||||
- 数据库查询优化
|
||||
|
||||
### 📱 移动端优化
|
||||
- 触摸友好的操作界面
|
||||
- 移动端专用底部导航
|
||||
- 响应式图片和布局
|
||||
- 触摸手势支持
|
||||
|
||||
### 🐛 问题修复
|
||||
- 修复播放进度记录丢失问题
|
||||
- 优化视频播放器兼容性
|
||||
- 修复移动端响应式布局问题
|
||||
- 改进错误处理和用户提示
|
||||
|
||||
### 📚 文档
|
||||
- 完整的 README.md 文档
|
||||
- 详细的部署指南
|
||||
- 环境变量配置说明
|
||||
- Docker 部署最佳实践
|
||||
|
||||
## 部署说明
|
||||
|
||||
### 支持的平台
|
||||
- ✅ Docker(推荐)
|
||||
- ✅ Vercel
|
||||
- ✅ Cloudflare Pages
|
||||
- ✅ 自托管服务器
|
||||
|
||||
### 存储后端
|
||||
- ✅ localStorage(默认,单用户)
|
||||
- ✅ Redis(多用户,数据同步)
|
||||
- ✅ Cloudflare D1(多用户,数据同步)
|
||||
- ✅ Upstash Redis(多用户,数据同步)
|
||||
|
||||
### 环境要求
|
||||
- Node.js 18+
|
||||
- pnpm 8+
|
||||
- 现代浏览器支持
|
||||
|
||||
## 贡献指南
|
||||
|
||||
我们欢迎所有形式的贡献!请查看 [CONTRIBUTING.md](CONTRIBUTING.md) 了解如何参与项目开发。
|
||||
|
||||
## 许可证
|
||||
|
||||
本项目采用 [MIT 许可证](LICENSE)。
|
||||
|
||||
---
|
||||
|
||||
**注意**: 本项目仅供学习和个人使用,请遵守当地法律法规,不要用于商业用途或公开服务。
|
||||
@@ -1,110 +0,0 @@
|
||||
# GitHub Actions 权限问题修复方案
|
||||
|
||||
## 🚨 问题分析
|
||||
|
||||
根据您的GitHub Actions失败日志,主要问题包括:
|
||||
|
||||
1. **权限拒绝错误**: `permission_denied: write_package`
|
||||
2. **资源访问错误**: `Resource not accessible by integration`
|
||||
3. **策略配置取消**: `The strategy configuration was canceled`
|
||||
|
||||
## 🔧 修复方案
|
||||
|
||||
### 1. 仓库权限设置检查
|
||||
|
||||
请确认以下设置:
|
||||
|
||||
#### GitHub仓库设置 → Actions → General
|
||||
1. 进入您的仓库: https://github.com/katelya77/KatelyaTV/settings/actions
|
||||
2. 在 "Workflow permissions" 部分,选择 **"Read and write permissions"**
|
||||
3. 勾选 **"Allow GitHub Actions to create and approve pull requests"**
|
||||
|
||||
#### GitHub仓库设置 → Packages
|
||||
1. 进入: https://github.com/katelya77/KatelyaTV/settings/packages
|
||||
2. 确保 "Package creation" 设置允许创建包
|
||||
|
||||
### 2. 工作流程修复
|
||||
|
||||
我已经创建了三个修复版本:
|
||||
|
||||
#### 版本1: 完整修复版 (`docker-image.yml`)
|
||||
- 修复了权限设置
|
||||
- 移除了有问题的cleanup job
|
||||
- 优化了多平台构建流程
|
||||
|
||||
#### 版本2: 简化版 (`docker-build.yml`)
|
||||
- 简化的构建流程
|
||||
- 更好的错误处理
|
||||
- 测试优先的方法
|
||||
|
||||
### 3. 具体修复内容
|
||||
|
||||
1. **权限优化**:
|
||||
```yaml
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
```
|
||||
|
||||
2. **移除问题组件**:
|
||||
- 删除了导致权限错误的cleanup job
|
||||
- 简化了digest处理流程
|
||||
|
||||
3. **构建流程优化**:
|
||||
- 改进了多平台构建策略
|
||||
- 添加了更好的缓存机制
|
||||
- 优化了错误处理
|
||||
|
||||
## 🎯 推荐操作步骤
|
||||
|
||||
### 立即操作
|
||||
|
||||
1. **检查仓库权限设置** (最重要!)
|
||||
- 访问: https://github.com/katelya77/KatelyaTV/settings/actions
|
||||
- 设置为 "Read and write permissions"
|
||||
|
||||
2. **测试新的工作流程**
|
||||
- 新的 `docker-image.yml` 已经推送
|
||||
- 等待下次推送触发自动构建
|
||||
|
||||
### 如果仍有问题
|
||||
|
||||
1. **使用简化版本**:
|
||||
```bash
|
||||
git add .github/workflows/docker-build.yml
|
||||
git commit -m "Add simplified Docker build workflow"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
2. **手动创建Personal Access Token** (备用方案):
|
||||
- 访问: https://github.com/settings/tokens
|
||||
- 创建token,权限包括: `write:packages`, `read:packages`
|
||||
- 添加到仓库Secrets: `PAT_TOKEN`
|
||||
- 修改workflow使用PAT而不是GITHUB_TOKEN
|
||||
|
||||
## 🔍 预期结果
|
||||
|
||||
修复后,您应该看到:
|
||||
- ✅ ARM64和AMD64平台都成功构建
|
||||
- ✅ 没有权限错误
|
||||
- ✅ Docker镜像成功推送到ghcr.io
|
||||
- ✅ 绿色的GitHub Actions状态
|
||||
|
||||
## 🆘 如果问题持续
|
||||
|
||||
如果上述方案都不能解决问题,可能需要:
|
||||
|
||||
1. **联系GitHub支持**: 可能是账户级别的权限限制
|
||||
2. **使用替代方案**: 切换到Docker Hub或其他容器注册中心
|
||||
3. **简化构建**: 暂时只构建单平台镜像
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
如果您需要进一步的帮助,请提供:
|
||||
- 新的GitHub Actions运行URL
|
||||
- 仓库权限设置的截图
|
||||
- 详细的错误日志
|
||||
|
||||
祝您早日解决这个强迫症问题!🎉
|
||||
@@ -1,242 +0,0 @@
|
||||
# 🚀 KatelyaTV 快速开始指南
|
||||
|
||||
欢迎使用 KatelyaTV!本指南将帮助您在几分钟内完成部署和配置。
|
||||
|
||||
## 📋 前置要求
|
||||
|
||||
- **Docker** (推荐) 或 **Node.js 18+**
|
||||
- 现代浏览器 (Chrome 90+, Firefox 88+, Safari 14+)
|
||||
- 稳定的网络连接
|
||||
|
||||
## 🐳 Docker 部署 (推荐)
|
||||
|
||||
### 1. 快速启动
|
||||
|
||||
```bash
|
||||
# 拉取最新镜像
|
||||
docker pull ghcr.io/katelya77/katelyatv:latest
|
||||
|
||||
# 启动容器
|
||||
docker run -d \
|
||||
--name katelyatv \
|
||||
-p 3000:3000 \
|
||||
--env PASSWORD=your_password \
|
||||
--restart unless-stopped \
|
||||
ghcr.io/katelya77/katelyatv:latest
|
||||
```
|
||||
|
||||
### 2. 访问应用
|
||||
|
||||
打开浏览器访问 `http://localhost:3000`,输入密码 `your_password` 即可使用。
|
||||
|
||||
### 3. 停止服务
|
||||
|
||||
```bash
|
||||
# 停止容器
|
||||
docker stop katelyatv
|
||||
|
||||
# 删除容器
|
||||
docker rm katelyatv
|
||||
```
|
||||
|
||||
## 🌐 云平台部署
|
||||
|
||||
### Vercel 部署
|
||||
|
||||
1. **Fork 项目**
|
||||
- 点击 GitHub 仓库右上角的 "Fork" 按钮
|
||||
- 等待 Fork 完成
|
||||
|
||||
2. **部署到 Vercel**
|
||||
- 访问 [Vercel](https://vercel.com/)
|
||||
- 点击 "New Project"
|
||||
- 选择 Fork 后的仓库
|
||||
- 设置环境变量 `PASSWORD=your_password`
|
||||
- 点击 "Deploy"
|
||||
|
||||
3. **访问应用**
|
||||
- 部署完成后,Vercel 会提供一个域名
|
||||
- 访问该域名,输入密码即可使用
|
||||
|
||||
### Cloudflare Pages 部署
|
||||
|
||||
1. **Fork 项目**
|
||||
- 同上
|
||||
|
||||
2. **部署到 Cloudflare Pages**
|
||||
- 访问 [Cloudflare Dashboard](https://dash.cloudflare.com/)
|
||||
- 进入 "Workers & Pages"
|
||||
- 点击 "Create application" → "Pages"
|
||||
- 选择 "Connect to Git"
|
||||
- 选择 Fork 后的仓库
|
||||
- 构建命令:`pnpm run pages:build`
|
||||
- 构建输出目录:`.vercel/output/static`
|
||||
- 环境变量:`PASSWORD=your_password`
|
||||
|
||||
3. **访问应用**
|
||||
- 部署完成后访问提供的域名
|
||||
|
||||
## ⚙️ 基础配置
|
||||
|
||||
### 环境变量
|
||||
|
||||
创建 `.env.local` 文件:
|
||||
|
||||
```bash
|
||||
# 复制示例文件
|
||||
cp .env.example .env.local
|
||||
|
||||
# 编辑配置
|
||||
nano .env.local
|
||||
```
|
||||
|
||||
**必需配置:**
|
||||
```bash
|
||||
PASSWORD=your_secure_password
|
||||
```
|
||||
|
||||
**推荐配置:**
|
||||
```bash
|
||||
SITE_NAME=我的影视站
|
||||
NEXT_PUBLIC_STORAGE_TYPE=localstorage
|
||||
NEXT_PUBLIC_SEARCH_MAX_PAGE=10
|
||||
```
|
||||
|
||||
### 自定义资源站点
|
||||
|
||||
编辑 `config.json` 文件:
|
||||
|
||||
```json
|
||||
{
|
||||
"cache_time": 7200,
|
||||
"api_site": {
|
||||
"dyttzy": {
|
||||
"api": "http://caiji.dyttzyapi.com/api.php/provide/vod",
|
||||
"name": "电影天堂资源",
|
||||
"detail": "http://caiji.dyttzyapi.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 核心功能使用
|
||||
|
||||
### 1. 搜索影视
|
||||
|
||||
- 在首页搜索框输入影视名称
|
||||
- 支持中文、英文、拼音搜索
|
||||
- 结果来自多个资源站点
|
||||
|
||||
### 2. 观看视频
|
||||
|
||||
- 点击搜索结果进入详情页
|
||||
- 选择播放源和剧集
|
||||
- 支持进度记录和断点续播
|
||||
|
||||
### 3. 收藏管理
|
||||
|
||||
- 点击心形图标收藏影视
|
||||
- 在"我的收藏"中查看
|
||||
- 支持多设备同步
|
||||
|
||||
### 4. 观看历史
|
||||
|
||||
- 自动记录观看进度
|
||||
- 在"继续观看"中查看
|
||||
- 支持从上次位置继续
|
||||
|
||||
## 🔧 高级配置
|
||||
|
||||
### 多用户支持
|
||||
|
||||
如需支持多用户,请配置 Redis 或 D1 存储:
|
||||
|
||||
```bash
|
||||
# Redis 配置
|
||||
NEXT_PUBLIC_STORAGE_TYPE=redis
|
||||
REDIS_URL=redis://localhost:6379/0
|
||||
|
||||
# 或 D1 配置 (Cloudflare Pages)
|
||||
NEXT_PUBLIC_STORAGE_TYPE=d1
|
||||
# 在 Cloudflare Pages 中绑定 D1 数据库
|
||||
```
|
||||
|
||||
### 自定义主题
|
||||
|
||||
修改 `src/styles/globals.css` 文件:
|
||||
|
||||
```css
|
||||
:root {
|
||||
--primary-color: #3b82f6;
|
||||
--secondary-color: #1e40af;
|
||||
--background-color: #ffffff;
|
||||
--text-color: #1f2937;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background-color: #111827;
|
||||
--text-color: #f9fafb;
|
||||
}
|
||||
```
|
||||
|
||||
### 添加新资源站点
|
||||
|
||||
在 `config.json` 中添加:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_site": {
|
||||
"newsite": {
|
||||
"api": "https://newsite.com/api.php/provide/vod",
|
||||
"name": "新站点名称"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🚨 常见问题
|
||||
|
||||
### Q: 无法访问应用
|
||||
**A:** 检查端口是否被占用,防火墙设置,或尝试其他端口。
|
||||
|
||||
### Q: 搜索无结果
|
||||
**A:** 检查网络连接,资源站点是否可用,或尝试其他关键词。
|
||||
|
||||
### Q: 视频无法播放
|
||||
**A:** 检查视频源是否有效,浏览器是否支持相关格式。
|
||||
|
||||
### Q: 数据丢失
|
||||
**A:** 如果使用 localStorage,数据存储在浏览器中,清除缓存会丢失数据。
|
||||
|
||||
## 📱 移动端使用
|
||||
|
||||
- 支持响应式设计
|
||||
- 可安装为 PWA 应用
|
||||
- 触摸友好的操作界面
|
||||
|
||||
## 🔒 安全建议
|
||||
|
||||
1. **设置强密码**:使用复杂密码保护访问
|
||||
2. **限制访问**:不要公开分享访问链接
|
||||
3. **定期更新**:保持应用版本最新
|
||||
4. **监控日志**:关注异常访问记录
|
||||
|
||||
## 📞 获取帮助
|
||||
|
||||
- 📖 [完整文档](README.md)
|
||||
- 🐛 问题反馈:在仓库 Issues 页面提交
|
||||
- 💬 功能讨论:在 Discussions 页面参与
|
||||
- 📝 [更新日志](CHANGELOG.md)
|
||||
|
||||
## 🎉 开始使用
|
||||
|
||||
现在您已经完成了基础配置,可以开始享受 KatelyaTV 带来的影视体验了!
|
||||
|
||||
**重要提醒:**
|
||||
- 本项目仅供学习和个人使用
|
||||
- 请遵守当地法律法规
|
||||
- 不要用于商业用途或公开服务
|
||||
|
||||
---
|
||||
|
||||
如有任何问题,欢迎在 GitHub 上提出 Issue 或参与讨论!
|
||||
@@ -21,6 +21,38 @@
|
||||
|
||||
---
|
||||
|
||||
## 📋 重要声明
|
||||
|
||||
> ⚠️ **视频源说明**
|
||||
>
|
||||
> 应用户建议,为避免潜在的版权争议,本项目已移除内置的视频源配置。如需使用完整功能,请:
|
||||
> - 📥 [下载官方提供的视频源配置文件](https://www.mediafire.com/file/xl3yo7la2ci378w/config.json/file)
|
||||
> - 🔍 自行寻找符合当地法律法规的合规视频源
|
||||
> - 🛠️ 根据需要自定义配置文件
|
||||
>
|
||||
> 项目本身仅提供技术框架,内容源由用户自主选择和配置。请确保您的使用行为符合相关法律法规。
|
||||
|
||||
---
|
||||
|
||||
## 📸 Screenshot
|
||||
|
||||
<div align="center">
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="public/screenshot1.png" alt="KatelyaTV 截图1" width="400"></td>
|
||||
<td><img src="public/screenshot2.png" alt="KatelyaTV 截图2" width="400"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="public/screenshot3.png" alt="KatelyaTV 截图3" width="400"></td>
|
||||
<td><img src="public/screenshot4.png" alt="KatelyaTV 截图4" width="400"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## ✨ 功能特性
|
||||
|
||||
- 🔍 **多源聚合搜索**:内置20+个免费资源站点,一次搜索立刻返回全源结果,支持电影、电视剧、综艺等多种类型。
|
||||
@@ -47,6 +79,7 @@
|
||||
- [核心功能](#核心功能)
|
||||
- [项目来源与声明](#项目来源与声明)
|
||||
- [部署](#部署)
|
||||
- [Docker 部署详解](#Docker-部署详解)
|
||||
- [Docker Compose 最佳实践](#Docker-Compose-最佳实践)
|
||||
- [环境变量](#环境变量)
|
||||
- [配置说明](#配置说明)
|
||||
@@ -170,28 +203,114 @@
|
||||
4. 设置环境变量 NEXT_PUBLIC_STORAGE_TYPE,值为 **d1**;设置 USERNAME 和 PASSWORD 作为站长账号
|
||||
5. 重试部署
|
||||
|
||||
### Docker 部署
|
||||
## 🐳 Docker 部署详解
|
||||
|
||||
#### 1. 直接运行(最简单)
|
||||
Docker 是推荐的部署方式,提供完整的环境隔离和便捷的管理体验。我们的镜像支持多架构(`linux/amd64`、`linux/arm64`),确保在各种硬件平台上都能稳定运行。
|
||||
|
||||
### 🚀 快速开始
|
||||
|
||||
#### 1. 基础部署(最简单)
|
||||
|
||||
```bash
|
||||
# 拉取预构建镜像
|
||||
# 拉取最新镜像(支持 amd64/arm64 多架构)
|
||||
docker pull ghcr.io/katelya77/katelyatv:latest
|
||||
|
||||
# 运行容器
|
||||
# -d: 后台运行 -p: 映射端口 3000 -> 3000
|
||||
docker run -d --name katelyatv -p 3000:3000 --env PASSWORD=your_password ghcr.io/katelya77/katelyatv:latest
|
||||
# 快速启动(LocalStorage 存储)
|
||||
docker run -d \
|
||||
--name katelyatv \
|
||||
-p 3000:3000 \
|
||||
--env PASSWORD=your_secure_password \
|
||||
--restart unless-stopped \
|
||||
ghcr.io/katelya77/katelyatv:latest
|
||||
```
|
||||
|
||||
访问 `http://服务器 IP:3000` 即可。(需自行到服务器控制台放通 `3000` 端口)
|
||||
访问 `http://服务器IP:3000` 即可使用。(需要在服务器控制台开放 3000 端口)
|
||||
|
||||
#### 2. 带自定义配置的部署
|
||||
|
||||
```bash
|
||||
# 创建配置文件目录
|
||||
mkdir -p ./katelyatv-config
|
||||
|
||||
# 将你的 config.json 放入该目录,然后运行:
|
||||
docker run -d \
|
||||
--name katelyatv \
|
||||
-p 3000:3000 \
|
||||
--env PASSWORD=your_secure_password \
|
||||
-v ./katelyatv-config/config.json:/app/config.json:ro \
|
||||
--restart unless-stopped \
|
||||
ghcr.io/katelya77/katelyatv:latest
|
||||
```
|
||||
|
||||
#### 3. 查看运行状态
|
||||
|
||||
```bash
|
||||
# 查看容器状态
|
||||
docker ps
|
||||
|
||||
# 查看日志
|
||||
docker logs katelyatv
|
||||
|
||||
# 查看实时日志
|
||||
docker logs -f katelyatv
|
||||
```
|
||||
|
||||
#### 4. 升级到最新版本
|
||||
|
||||
```bash
|
||||
# 停止并删除旧容器
|
||||
docker stop katelyatv && docker rm katelyatv
|
||||
|
||||
# 拉取最新镜像
|
||||
docker pull ghcr.io/katelya77/katelyatv:latest
|
||||
|
||||
# 重新创建容器(使用相同的配置)
|
||||
docker run -d \
|
||||
--name katelyatv \
|
||||
-p 3000:3000 \
|
||||
--env PASSWORD=your_secure_password \
|
||||
--restart unless-stopped \
|
||||
ghcr.io/katelya77/katelyatv:latest
|
||||
```
|
||||
|
||||
### 📦 镜像特性
|
||||
|
||||
- **🏗️ 多架构支持**:同时支持 `linux/amd64` 和 `linux/arm64` 架构
|
||||
- **⚡ 优化构建**:基于 Alpine Linux,镜像体积小,启动速度快
|
||||
- **🔒 安全可靠**:定期更新底层依赖,修复安全漏洞
|
||||
- **🚀 开箱即用**:内置所有必要依赖,无需额外配置
|
||||
|
||||
### 🔧 常用操作
|
||||
|
||||
```bash
|
||||
# 进入容器终端(调试用)
|
||||
docker exec -it katelyatv /bin/sh
|
||||
|
||||
# 重启容器
|
||||
docker restart katelyatv
|
||||
|
||||
# 停止容器
|
||||
docker stop katelyatv
|
||||
|
||||
# 查看容器资源使用情况
|
||||
docker stats katelyatv
|
||||
|
||||
# 备份容器(如果有挂载卷)
|
||||
docker run --rm -v katelyatv_data:/data -v $(pwd):/backup alpine tar czf /backup/katelyatv-backup.tar.gz /data
|
||||
```
|
||||
|
||||
## 🐳 Docker Compose 最佳实践
|
||||
|
||||
若你使用 docker compose 部署,以下是一些 compose 示例
|
||||
Docker Compose 是管理多容器应用的最佳方式,特别适合需要数据库支持的部署场景。
|
||||
|
||||
### local storage 版本
|
||||
### 📝 LocalStorage 版本(基础)
|
||||
|
||||
适合个人使用,数据存储在浏览器本地:
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
katelyatv:
|
||||
image: ghcr.io/katelya77/katelyatv:latest
|
||||
@@ -200,49 +319,169 @@ services:
|
||||
ports:
|
||||
- '3000:3000'
|
||||
environment:
|
||||
- PASSWORD=your_password
|
||||
# 如需自定义配置,可挂载文件
|
||||
- PASSWORD=your_secure_password
|
||||
- SITE_NAME=我的影视站
|
||||
- ANNOUNCEMENT=欢迎使用 KatelyaTV!请遵守相关法律法规。
|
||||
# 可选:挂载自定义配置
|
||||
# volumes:
|
||||
# - ./config.json:/app/config.json:ro
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
```
|
||||
|
||||
### Redis 版本(推荐,多账户数据隔离,跨设备同步)
|
||||
**启动命令:**
|
||||
```bash
|
||||
# 创建并启动服务
|
||||
docker compose up -d
|
||||
|
||||
# 查看服务状态
|
||||
docker compose ps
|
||||
|
||||
# 查看日志
|
||||
docker compose logs -f katelyatv
|
||||
```
|
||||
|
||||
### 🔐 Redis 版本(推荐)
|
||||
|
||||
支持多用户、跨设备数据同步、完整的用户权限管理:
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
katelyatv-core:
|
||||
katelyatv:
|
||||
image: ghcr.io/katelya77/katelyatv:latest
|
||||
container_name: katelyatv
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- '3000:3000'
|
||||
environment:
|
||||
# 基础配置
|
||||
- SITE_NAME=KatelyaTV 影视站
|
||||
- ANNOUNCEMENT=支持多用户注册,请合理使用!
|
||||
|
||||
# 管理员账号(重要!)
|
||||
- USERNAME=admin
|
||||
- PASSWORD=admin_password
|
||||
- PASSWORD=admin_super_secure_password
|
||||
|
||||
# Redis 存储配置
|
||||
- NEXT_PUBLIC_STORAGE_TYPE=redis
|
||||
- REDIS_URL=redis://katelyatv-redis:6379
|
||||
|
||||
# 用户功能
|
||||
- NEXT_PUBLIC_ENABLE_REGISTER=true
|
||||
|
||||
# 可选:搜索配置
|
||||
- NEXT_PUBLIC_SEARCH_MAX_PAGE=8
|
||||
networks:
|
||||
- katelyatv-network
|
||||
depends_on:
|
||||
- katelyatv-redis
|
||||
# 如需自定义配置,可挂载文件
|
||||
katelyatv-redis:
|
||||
condition: service_healthy
|
||||
# 可选:挂载自定义配置和持久化数据
|
||||
# volumes:
|
||||
# - ./config.json:/app/config.json:ro
|
||||
# - ./logs:/app/logs
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
katelyatv-redis:
|
||||
image: redis
|
||||
image: redis:7-alpine
|
||||
container_name: katelyatv-redis
|
||||
restart: unless-stopped
|
||||
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
|
||||
networks:
|
||||
- katelyatv-network
|
||||
# 如需持久化
|
||||
# volumes:
|
||||
# - ./data:/data
|
||||
volumes:
|
||||
# Redis 数据持久化
|
||||
- katelyatv-redis-data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
# 可选:端口映射(用于外部访问 Redis)
|
||||
# ports:
|
||||
# - '6379:6379'
|
||||
|
||||
networks:
|
||||
katelyatv-network:
|
||||
driver: bridge
|
||||
name: katelyatv-network
|
||||
|
||||
volumes:
|
||||
katelyatv-redis-data:
|
||||
driver: local
|
||||
name: katelyatv-redis-data
|
||||
```
|
||||
|
||||
**完整部署流程:**
|
||||
|
||||
```bash
|
||||
# 1. 创建项目目录
|
||||
mkdir katelyatv && cd katelyatv
|
||||
|
||||
# 2. 创建 docker-compose.yml 文件(复制上面的内容)
|
||||
nano docker-compose.yml
|
||||
|
||||
# 3. 启动所有服务
|
||||
docker compose up -d
|
||||
|
||||
# 4. 查看服务状态
|
||||
docker compose ps
|
||||
|
||||
# 5. 查看启动日志
|
||||
docker compose logs -f
|
||||
|
||||
# 6. 首次访问 http://your-server:3000
|
||||
# 使用管理员账号 admin / admin_super_secure_password 登录
|
||||
# 然后访问 /admin 进行管理员配置
|
||||
```
|
||||
|
||||
### 🔄 管理与维护
|
||||
|
||||
```bash
|
||||
# 更新到最新版本
|
||||
docker compose pull && docker compose up -d
|
||||
|
||||
# 备份 Redis 数据
|
||||
docker compose exec katelyatv-redis redis-cli BGSAVE
|
||||
docker run --rm -v katelyatv-redis-data:/data -v $(pwd):/backup alpine tar czf /backup/redis-backup-$(date +%Y%m%d).tar.gz /data
|
||||
|
||||
# 查看资源使用情况
|
||||
docker compose stats
|
||||
|
||||
# 重启特定服务
|
||||
docker compose restart katelyatv
|
||||
|
||||
# 查看特定服务日志
|
||||
docker compose logs -f katelyatv-redis
|
||||
|
||||
# 进入容器调试
|
||||
docker compose exec katelyatv /bin/sh
|
||||
|
||||
# 完全清理(注意:会删除所有数据!)
|
||||
docker compose down -v --remove-orphans
|
||||
```
|
||||
|
||||
### 🚨 重要注意事项
|
||||
|
||||
1. **修改默认密码**:部署后请立即修改 `admin` 账号的默认密码
|
||||
2. **数据备份**:定期备份 Redis 数据卷,避免数据丢失
|
||||
3. **端口安全**:确保服务器防火墙正确配置,只开放必要端口
|
||||
4. **资源监控**:定期检查容器资源使用情况,必要时调整配置
|
||||
5. **日志管理**:配置日志轮转,避免日志文件过大
|
||||
|
||||
## 🔄 自动同步最近更改
|
||||
|
||||
建议在 fork 的仓库中启用本仓库自带的 GitHub Actions 自动同步功能(见 `.github/workflows/sync.yml`)。
|
||||
@@ -274,10 +513,10 @@ networks:
|
||||
{
|
||||
"cache_time": 7200,
|
||||
"api_site": {
|
||||
"dyttzy": {
|
||||
"api": "http://caiji.dyttzyapi.com/api.php/provide/vod",
|
||||
"name": "电影天堂资源",
|
||||
"detail": "http://caiji.dyttzyapi.com"
|
||||
"example": {
|
||||
"api": "https://example.com/api.php/provide/vod",
|
||||
"name": "示例资源站",
|
||||
"detail": "https://example.com"
|
||||
}
|
||||
// ...更多站点
|
||||
}
|
||||
@@ -351,11 +590,43 @@ KatelyaTV 支持标准的苹果 CMS V10 API 格式。
|
||||
|
||||
[MIT](LICENSE) © 2025 KatelyaTV & Contributors
|
||||
|
||||
## � Star History
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://star-history.com/#katelya77/KatelyaTV&Date)
|
||||
|
||||
</div>
|
||||
|
||||
## 💖 支持项目
|
||||
|
||||
如果这个项目对您有帮助,欢迎给个 ⭐️ Star 支持一下!
|
||||
|
||||
您也可以通过以下方式支持项目的持续开发:
|
||||
|
||||
<div align="center">
|
||||
|
||||
### 请开发者喝杯咖啡 ☕
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<img src="public/wechat.jpg" alt="微信支付" width="200">
|
||||
<br>
|
||||
<strong>微信支付</strong>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
> 💝 感谢您的支持!您的捐赠将用于项目的持续维护和功能改进。
|
||||
|
||||
</div>
|
||||
|
||||
## 🙏 致谢
|
||||
|
||||
- [ts-nextjs-tailwind-starter](https://github.com/theodorusclarence/ts-nextjs-tailwind-starter) — 项目最初基于该脚手架。
|
||||
- [LibreTV](https://github.com/LibreSpark/LibreTV) — 由此启发,站在巨人的肩膀上。
|
||||
- MoonTV 原始项目与作者社区 — 感谢原作奠定坚实基础。
|
||||
- [LunaTV-原MoonTV](https://github.com/MoonTechLab/LunaTV) — 原始项目与作者社区,感谢原作奠定坚实基础。
|
||||
- [ArtPlayer](https://github.com/zhw2590582/ArtPlayer) — 提供强大的网页视频播放器。
|
||||
- [HLS.js](https://github.com/video-dev/hls.js) — 实现 HLS 流媒体在浏览器中的播放支持。
|
||||
- 感谢所有提供免费影视接口的站点。
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
# KatelyaTV v0.1.0-katelya 发布说明
|
||||
|
||||
> 本项目在「MoonTV」基础上进行二创与继承,由 Katelya 持续维护。保留并致谢原作与社区贡献,在不改变核心理念的前提下,专注于更易部署、更友好体验与更稳定维护。
|
||||
|
||||
## 亮点
|
||||
- 全面延续上游核心:多源聚合搜索、在线播放、收藏与观看历史、PWA 支持、响应式布局、多用户系统等
|
||||
- 文档重写与梳理:README、QUICKSTART、PROJECT_STATUS、CONTRIBUTING、CHANGELOG 全面适配 KatelyaTV 品牌
|
||||
- 部署指引优化:Vercel / Docker / Cloudflare Pages 一站式说明,提供 Compose 最佳实践
|
||||
- 安全与隐私提醒:新增部署安全提示与法律风险说明
|
||||
|
||||
## 变更摘要
|
||||
- 品牌与文档
|
||||
- 将项目品牌统一为 KatelyaTV,并明确二创与继承来源
|
||||
- 更新部署与使用说明,优化快速上手体验
|
||||
- 调整仓库路径、示例命令与 Docker 镜像示例名称(镜像仍沿用上游命名空间)
|
||||
- 代码与配置
|
||||
- 保持与上游 MoonTV 的接口与行为兼容
|
||||
- 默认站点名改为 `KatelyaTV`(可通过 `SITE_NAME` 环境变量覆盖)
|
||||
|
||||
## 安装与升级
|
||||
- 首次安装(Docker 推荐)
|
||||
```bash
|
||||
# 拉取镜像
|
||||
docker pull ghcr.io/katelya77/katelyatv:latest
|
||||
|
||||
# 启动示例
|
||||
docker run -d --name katelyatv \
|
||||
-p 3000:3000 \
|
||||
--env PASSWORD=your_password \
|
||||
--restart unless-stopped \
|
||||
ghcr.io/katelya77/katelyatv:latest
|
||||
```
|
||||
- 或使用 README 中的 Docker Compose 示例
|
||||
|
||||
## 兼容性
|
||||
- 保持与上游 MoonTV v0.1.0 行为一致
|
||||
- 支持存储后端:localStorage / Redis / Cloudflare D1 / Upstash Redis
|
||||
- 运行环境:Node.js 18+;容器镜像支持多架构
|
||||
|
||||
## 已知问题
|
||||
- 部分第三方资源站可用性受其自身状态影响
|
||||
- Android TV 端收藏与网页端暂未完全互通(后续版本优化)
|
||||
|
||||
## 后续路线
|
||||
- 弹幕系统、字幕支持、下载功能、社交分享
|
||||
- 数据同步与多端互通完善
|
||||
- 性能与稳定性持续优化
|
||||
|
||||
## 鸣谢
|
||||
- 原始项目 MoonTV 及其作者与社区
|
||||
- 所有为本项目提供反馈、贡献代码与文档的开发者
|
||||
|
||||
— Katelya
|
||||
@@ -1,157 +0,0 @@
|
||||
# KatelyaTV v0.2.0 发布说明
|
||||
|
||||
> 本版本主要修复了 Docker 部署配置问题,确保用户能够正确使用 KatelyaTV 的 Docker 镜像进行部署。
|
||||
|
||||
## 🚀 重要更新
|
||||
|
||||
### Docker 部署修复
|
||||
- **修复镜像路径**:将所有文档中的 Docker 镜像路径从 `ghcr.io/senshinya/moontv:latest` 更新为 `ghcr.io/katelya77/katelyatv:latest`
|
||||
- **统一部署说明**:确保 README.md、QUICKSTART.md 和发布说明中的 Docker 部署指令一致
|
||||
- **验证部署流程**:确认所有 Docker Compose 配置文件使用正确的镜像路径
|
||||
|
||||
### 代码兼容性验证
|
||||
- **构建验证**:通过完整的构建测试,确保所有 KatelyaTV 品牌更改不影响功能
|
||||
- **向后兼容**:保持与 MoonTV v0.1.0 的完全兼容性
|
||||
- **环境变量支持**:支持通过 `SITE_NAME` 等环境变量自定义配置
|
||||
|
||||
## 🐳 Docker 部署指南
|
||||
|
||||
### 快速启动
|
||||
```bash
|
||||
# 拉取最新镜像
|
||||
docker pull ghcr.io/katelya77/katelyatv:latest
|
||||
|
||||
# 启动容器
|
||||
docker run -d \
|
||||
--name katelyatv \
|
||||
-p 3000:3000 \
|
||||
--env PASSWORD=your_password \
|
||||
--restart unless-stopped \
|
||||
ghcr.io/katelya77/katelyatv:latest
|
||||
```
|
||||
|
||||
### Docker Compose 部署
|
||||
|
||||
#### 基础版本(localStorage)
|
||||
```yaml
|
||||
services:
|
||||
katelyatv:
|
||||
image: ghcr.io/katelya77/katelyatv:latest
|
||||
container_name: katelyatv
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- '3000:3000'
|
||||
environment:
|
||||
- PASSWORD=your_password
|
||||
```
|
||||
|
||||
#### Redis 版本(推荐,支持多用户)
|
||||
```yaml
|
||||
services:
|
||||
katelyatv-core:
|
||||
image: ghcr.io/katelya77/katelyatv:latest
|
||||
container_name: katelyatv
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- '3000:3000'
|
||||
environment:
|
||||
- USERNAME=admin
|
||||
- PASSWORD=admin_password
|
||||
- NEXT_PUBLIC_STORAGE_TYPE=redis
|
||||
- REDIS_URL=redis://katelyatv-redis:6379
|
||||
- NEXT_PUBLIC_ENABLE_REGISTER=true
|
||||
networks:
|
||||
- katelyatv-network
|
||||
depends_on:
|
||||
- katelyatv-redis
|
||||
|
||||
katelyatv-redis:
|
||||
image: redis
|
||||
container_name: katelyatv-redis
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- katelyatv-network
|
||||
volumes:
|
||||
- ./data:/data
|
||||
|
||||
networks:
|
||||
katelyatv-network:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
## 📋 环境变量配置
|
||||
|
||||
| 变量名 | 说明 | 默认值 | 示例 |
|
||||
|--------|------|--------|------|
|
||||
| `PASSWORD` | 访问密码(localStorage 模式)或管理员密码 | - | `your_password` |
|
||||
| `USERNAME` | 管理员用户名(非 localStorage 模式) | - | `admin` |
|
||||
| `SITE_NAME` | 站点名称 | `KatelyaTV` | `我的影视站` |
|
||||
| `NEXT_PUBLIC_STORAGE_TYPE` | 存储类型 | `localstorage` | `redis`, `d1`, `upstash` |
|
||||
| `REDIS_URL` | Redis 连接地址 | - | `redis://localhost:6379` |
|
||||
| `NEXT_PUBLIC_ENABLE_REGISTER` | 是否开放注册 | `false` | `true` |
|
||||
|
||||
## 🔧 部署验证
|
||||
|
||||
部署完成后,请验证以下功能:
|
||||
|
||||
1. **基础访问**:浏览器访问 `http://localhost:3000` 能正常打开
|
||||
2. **密码验证**:使用设置的密码能正常登录
|
||||
3. **搜索功能**:能正常搜索和播放视频
|
||||
4. **数据持久化**:重启容器后数据保持(Redis 模式)
|
||||
|
||||
## 🐛 已知问题
|
||||
|
||||
- 部分第三方资源站可用性受其自身状态影响
|
||||
- Android TV 端收藏与网页端暂未完全互通(计划在后续版本优化)
|
||||
|
||||
## 📝 变更日志
|
||||
|
||||
### 修复
|
||||
- 修复 README.md 中 Docker 镜像路径错误
|
||||
- 修复 QUICKSTART.md 中 Docker 部署说明
|
||||
- 修复 Docker Compose 配置示例中的镜像路径
|
||||
|
||||
### 改进
|
||||
- 统一所有文档中的 Docker 部署说明
|
||||
- 完善环境变量配置说明
|
||||
- 添加部署验证步骤
|
||||
|
||||
### 兼容性
|
||||
- 保持与 MoonTV v0.1.0 完全兼容
|
||||
- 支持从旧版本无缝升级
|
||||
- 保留所有现有功能和配置选项
|
||||
|
||||
## 🔄 升级指南
|
||||
|
||||
### 从 v0.1.0-katelya 升级
|
||||
```bash
|
||||
# 停止旧容器
|
||||
docker stop katelyatv
|
||||
docker rm katelyatv
|
||||
|
||||
# 拉取新镜像
|
||||
docker pull ghcr.io/katelya77/katelyatv:latest
|
||||
|
||||
# 使用新镜像启动
|
||||
docker run -d \
|
||||
--name katelyatv \
|
||||
-p 3000:3000 \
|
||||
--env PASSWORD=your_password \
|
||||
--restart unless-stopped \
|
||||
ghcr.io/katelya77/katelyatv:latest
|
||||
```
|
||||
|
||||
### 从 MoonTV 迁移
|
||||
如果您之前使用的是 MoonTV,只需将 Docker 镜像路径更改为 `ghcr.io/katelya77/katelyatv:latest`,其他配置保持不变。
|
||||
|
||||
## 🙏 鸣谢
|
||||
|
||||
- 感谢社区用户反馈的 Docker 部署问题
|
||||
- 感谢原始项目 MoonTV 及其作者与社区
|
||||
- 感谢所有为本项目提供反馈和建议的开发者
|
||||
|
||||
---
|
||||
|
||||
**完整部署文档**:请参考 [README.md](README.md) 和 [QUICKSTART.md](QUICKSTART.md)
|
||||
|
||||
— Katelya
|
||||
@@ -1 +1 @@
|
||||
20250928125318
|
||||
20250901193125
|
||||
@@ -1,89 +1,28 @@
|
||||
{
|
||||
"cache_time": 7200,
|
||||
"api_site": {
|
||||
"dyttzy": {
|
||||
"api": "http://caiji.dyttzyapi.com/api.php/provide/vod",
|
||||
"name": "电影天堂资源",
|
||||
"detail": "http://caiji.dyttzyapi.com"
|
||||
"example_test": {
|
||||
"api": "https://example.com/api.php/provide/vod",
|
||||
"name": "测试视频源",
|
||||
"detail": "https://example.com"
|
||||
},
|
||||
"heimuer": {
|
||||
"api": "https://json.heimuer.xyz/api.php/provide/vod",
|
||||
"name": "黑木耳",
|
||||
"detail": "https://heimuer.tv"
|
||||
"demo_site1": {
|
||||
"api": "https://your-api-domain.com/api.php/provide/vod",
|
||||
"name": "示例视频源1",
|
||||
"detail": "https://your-domain.com"
|
||||
},
|
||||
"ruyi": {
|
||||
"api": "https://cj.rycjapi.com/api.php/provide/vod",
|
||||
"name": "如意资源"
|
||||
"demo_site2": {
|
||||
"api": "https://api.your-site.com/provide/vod",
|
||||
"name": "示例视频源2"
|
||||
},
|
||||
"bfzy": {
|
||||
"api": "https://bfzyapi.com/api.php/provide/vod",
|
||||
"name": "暴风资源"
|
||||
"demo_site3": {
|
||||
"api": "https://video-api.example.org/api.php/provide/vod",
|
||||
"name": "示例视频源3",
|
||||
"detail": "https://video.example.org"
|
||||
},
|
||||
"tyyszy": {
|
||||
"api": "https://tyyszy.com/api.php/provide/vod",
|
||||
"name": "天涯资源"
|
||||
},
|
||||
"ffzy": {
|
||||
"api": "http://ffzy5.tv/api.php/provide/vod",
|
||||
"name": "非凡影视",
|
||||
"detail": "http://ffzy5.tv"
|
||||
},
|
||||
"zy360": {
|
||||
"api": "https://360zy.com/api.php/provide/vod",
|
||||
"name": "360资源"
|
||||
},
|
||||
"maotaizy": {
|
||||
"api": "https://caiji.maotaizy.cc/api.php/provide/vod",
|
||||
"name": "茅台资源"
|
||||
},
|
||||
"wolong": {
|
||||
"api": "https://wolongzyw.com/api.php/provide/vod",
|
||||
"name": "卧龙资源"
|
||||
},
|
||||
"jisu": {
|
||||
"api": "https://jszyapi.com/api.php/provide/vod",
|
||||
"name": "极速资源",
|
||||
"detail": "https://jszyapi.com"
|
||||
},
|
||||
"dbzy": {
|
||||
"api": "https://dbzy.tv/api.php/provide/vod",
|
||||
"name": "豆瓣资源"
|
||||
},
|
||||
"mozhua": {
|
||||
"api": "https://mozhuazy.com/api.php/provide/vod",
|
||||
"name": "魔爪资源"
|
||||
},
|
||||
"mdzy": {
|
||||
"api": "https://www.mdzyapi.com/api.php/provide/vod",
|
||||
"name": "魔都资源"
|
||||
},
|
||||
"zuid": {
|
||||
"api": "https://api.zuidapi.com/api.php/provide/vod",
|
||||
"name": "最大资源"
|
||||
},
|
||||
"yinghua": {
|
||||
"api": "https://m3u8.apiyhzy.com/api.php/provide/vod",
|
||||
"name": "樱花资源"
|
||||
},
|
||||
"wujin": {
|
||||
"api": "https://api.wujinapi.me/api.php/provide/vod",
|
||||
"name": "无尽资源"
|
||||
},
|
||||
"wwzy": {
|
||||
"api": "https://wwzy.tv/api.php/provide/vod",
|
||||
"name": "旺旺短剧"
|
||||
},
|
||||
"ikun": {
|
||||
"api": "https://ikunzyapi.com/api.php/provide/vod",
|
||||
"name": "iKun资源"
|
||||
},
|
||||
"lzi": {
|
||||
"api": "https://cj.lziapi.com/api.php/provide/vod",
|
||||
"name": "量子资源站"
|
||||
},
|
||||
"xiaomaomi": {
|
||||
"api": "https://zy.xmm.hk/api.php/provide/vod",
|
||||
"name": "小猫咪资源"
|
||||
"demo_site4": {
|
||||
"api": "https://media.sample-domain.net/api.php/provide/vod",
|
||||
"name": "示例视频源4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "moontv",
|
||||
"version": "0.1.0",
|
||||
"name": "katelyatv",
|
||||
"version": "0.4.0-katelya",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "pnpm gen:runtime && pnpm gen:manifest && next dev -H 0.0.0.0",
|
||||
"build": "pnpm gen:runtime && pnpm gen:manifest && next build",
|
||||
"dev": "npm run gen:runtime && npm run gen:manifest && next dev -H 0.0.0.0",
|
||||
"build": "npm run gen:runtime && npm run gen:manifest && next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"lint:fix": "eslint src --fix && pnpm format",
|
||||
"lint:fix": "eslint src --fix && npm run format",
|
||||
"lint:strict": "eslint --max-warnings=0 src",
|
||||
"typecheck": "tsc --noEmit --incremental false",
|
||||
"test:watch": "jest --watch",
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 MiB |
|
Before Width: | Height: | Size: 6.1 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 7.0 MiB After Width: | Height: | Size: 737 KiB |
|
Before Width: | Height: | Size: 5.0 MiB After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 727 KiB |
@@ -1 +1 @@
|
||||
if(!self.define){let e,s={};const n=(n,a)=>(n=new URL(n+".js",a).href,s[n]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=s,document.head.appendChild(e)}else e=n,importScripts(n),s()}).then(()=>{let e=s[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(a,i)=>{const c=e||("document"in self?document.currentScript.src:"")||location.href;if(s[c])return;let t={};const r=e=>n(e,c),o={module:{uri:c},exports:t,require:r};s[c]=Promise.all(a.map(e=>o[e]||r(e))).then(e=>(i(...e),t))}}define(["./workbox-e9849328"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/app-build-manifest.json",revision:"e835516f55e089231cd3a13c3d1bfcfb"},{url:"/_next/static/I621_uJyyXyq0s9YsYe1C/_buildManifest.js",revision:"85aecd8a55db42fc901f52386fd2a680"},{url:"/_next/static/I621_uJyyXyq0s9YsYe1C/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/_next/static/chunks/151-467740e7dc8a9501.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/242-3804d87f50553b94.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/402-0111ac7d0edfee14.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/484-4de9b8ccd6b187b0.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/609-bd706105e16d4e38.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/78-2f748e0c099ee9b7.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/866-d2269a3038f10b5a.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/887-3888edb42bd5ac06.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/app/_not-found/page-d6cb5fee19b812f4.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/app/admin/page-02699fb3c7542f31.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/app/douban/page-6cadcedaf8538fd6.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/app/layout-f2be6b03f6eb1026.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/app/login/page-9a89981161d4a992.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/app/page-fd24f7135fef556d.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/app/play/page-648b8b5fd8c19287.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/app/search/page-89eb23c28fc11ef5.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/app/warning/page-e6b20b93b37dc516.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/b145b63a-b7e49c063d2fa255.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/c72274ce-909438a8a5dd87a5.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/da9543df-c2ce5269243dd748.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/framework-6e06c675866dc992.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/main-app-0cf6afdd74694b9f.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/main-e84422daeb8eaf88.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/pages/_app-3fcac1a2c632f1ef.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/pages/_error-d3fe151bf402c134.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-4a57793b45c0f940.js",revision:"I621_uJyyXyq0s9YsYe1C"},{url:"/_next/static/css/23100062f5d4aac0.css",revision:"23100062f5d4aac0"},{url:"/_next/static/css/a7b7a98490e311ff.css",revision:"a7b7a98490e311ff"},{url:"/_next/static/media/26a46d62cd723877-s.woff2",revision:"befd9c0fdfa3d8a645d5f95717ed6420"},{url:"/_next/static/media/55c55f0601d81cf3-s.woff2",revision:"43828e14271c77b87e3ed582dbff9f74"},{url:"/_next/static/media/581909926a08bbc8-s.woff2",revision:"f0b86e7c24f455280b8df606b89af891"},{url:"/_next/static/media/8e9860b6e62d6359-s.woff2",revision:"01ba6c2a184b8cba08b0d57167664d75"},{url:"/_next/static/media/97e0cb1ae144a2a9-s.woff2",revision:"e360c61c5bd8d90639fd4503c829c2dc"},{url:"/_next/static/media/df0a9ae256c0569c-s.woff2",revision:"d54db44de5ccb18886ece2fda72bdfe0"},{url:"/_next/static/media/e4af272ccee01ff0-s.p.woff2",revision:"65850a373e258f1c897a2b3d75eb74de"},{url:"/favicon.ico",revision:"c5de6e56c5664adda146825f75ea6ecf"},{url:"/icons/icon-192x192.png",revision:"4a56c090828a1ad254c903c7aec0389d"},{url:"/icons/icon-256x256.png",revision:"f6409eb1a001f754121e3a8281c0319c"},{url:"/icons/icon-384x384.png",revision:"f6efc3e357b9ffdf4e0d8c14b2ed0ac1"},{url:"/icons/icon-512x512.png",revision:"9c008cbbeb6a576fe07bb1284a83f4d2"},{url:"/logo.png",revision:"40de611b143c47c6291c7bdad2c959ca"},{url:"/manifest.json",revision:"7bd3dabc1cfbfe40f09577efca223d31"},{url:"/robots.txt",revision:"0483b37fb6cf7455cefe516197e39241"},{url:"/screenshot.png",revision:"05a86e8d4faae6b384d19f02173ea87f"},{url:"/screenshot1.png",revision:"d7de3a25686c5b9c9d8c8675bc6109fc"},{url:"/screenshot2.png",revision:"b0b715a3018d2f02aba5d94762473bb6"},{url:"/screenshot3.png",revision:"7e454c28e110e291ee12f494fb3cf40c"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:s,event:n,state:a})=>s&&"opaqueredirect"===s.type?new Response(s.body,{status:200,statusText:"OK",headers:s.headers}):s}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;const s=e.pathname;return!s.startsWith("/api/auth/")&&!!s.startsWith("/api/")},new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;return!e.pathname.startsWith("/api/")},new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>!(self.origin===e.origin),new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET")});
|
||||
if(!self.define){let e,s={};const n=(n,t)=>(n=new URL(n+".js",t).href,s[n]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=s,document.head.appendChild(e)}else e=n,importScripts(n),s()}).then(()=>{let e=s[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(t,c)=>{const i=e||("document"in self?document.currentScript.src:"")||location.href;if(s[i])return;let a={};const r=e=>n(e,i),o={module:{uri:i},exports:a,require:r};s[i]=Promise.all(t.map(e=>o[e]||r(e))).then(e=>(c(...e),a))}}define(["./workbox-4754cb34"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/app-build-manifest.json",revision:"4cc81709a85016d2e1325aec145aa599"},{url:"/_next/static/chunks/100-efcc706557360c1e.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/117-712c1385f002eef0.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/262-b67318e67f316783.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/41ade5dc-797c284ea15e5986.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/519-9968375803c5bb08.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/827-35ccbd32f32feb33.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/872-010f54d5928bfbfd.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/886-b0b555a14550a8dc.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/97-d35e729ca3ec6714.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/a4634e51-a3fb94d869211083.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/app/_not-found/page-231758babf57b819.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/app/admin/page-094e47d9aa7845e0.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/app/douban/page-75ce0c7d2b4cff9c.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/app/layout-870a3b1d3b21202a.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/app/login/page-b79e53e406bf669d.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/app/page-61b1291c1aa52317.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/app/play/page-d478c791ff7bae46.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/app/search/page-2e6fb9284b7fe3b7.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/app/warning/page-6c04923bb35d02d2.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/fd9d1056-5fb180716357b830.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/framework-f66176bb897dc684.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/main-33b2d4bbe5672234.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/main-app-073eb01104a135dc.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/pages/_app-72b849fbd24ac258.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/pages/_error-7ba65e1336b92748.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-05e23bcac07e9541.js",revision:"m91B5pHK189d_DOw2NJtJ"},{url:"/_next/static/css/01f6801b310b5690.css",revision:"01f6801b310b5690"},{url:"/_next/static/css/275ed64cc4367444.css",revision:"275ed64cc4367444"},{url:"/_next/static/css/2da794711423e5a1.css",revision:"2da794711423e5a1"},{url:"/_next/static/m91B5pHK189d_DOw2NJtJ/_buildManifest.js",revision:"c155cce658e53418dec34664328b51ac"},{url:"/_next/static/m91B5pHK189d_DOw2NJtJ/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/_next/static/media/26a46d62cd723877-s.woff2",revision:"befd9c0fdfa3d8a645d5f95717ed6420"},{url:"/_next/static/media/55c55f0601d81cf3-s.woff2",revision:"43828e14271c77b87e3ed582dbff9f74"},{url:"/_next/static/media/581909926a08bbc8-s.woff2",revision:"f0b86e7c24f455280b8df606b89af891"},{url:"/_next/static/media/8e9860b6e62d6359-s.woff2",revision:"01ba6c2a184b8cba08b0d57167664d75"},{url:"/_next/static/media/97e0cb1ae144a2a9-s.woff2",revision:"e360c61c5bd8d90639fd4503c829c2dc"},{url:"/_next/static/media/df0a9ae256c0569c-s.woff2",revision:"d54db44de5ccb18886ece2fda72bdfe0"},{url:"/_next/static/media/e4af272ccee01ff0-s.p.woff2",revision:"65850a373e258f1c897a2b3d75eb74de"},{url:"/favicon.ico",revision:"c5de6e56c5664adda146825f75ea6ecf"},{url:"/icons/icon-192x192.png",revision:"4a56c090828a1ad254c903c7aec0389d"},{url:"/icons/icon-256x256.png",revision:"f6409eb1a001f754121e3a8281c0319c"},{url:"/icons/icon-384x384.png",revision:"f6efc3e357b9ffdf4e0d8c14b2ed0ac1"},{url:"/icons/icon-512x512.png",revision:"9c008cbbeb6a576fe07bb1284a83f4d2"},{url:"/logo.png",revision:"40de611b143c47c6291c7bdad2c959ca"},{url:"/manifest.json",revision:"7bd3dabc1cfbfe40f09577efca223d31"},{url:"/robots.txt",revision:"e2b2cd8514443456bc6fb9d77b3b1f3e"},{url:"/screenshot1.png",revision:"10572bfcea54dc93ac4c5f7c9057fc98"},{url:"/screenshot2.png",revision:"f815a8990973a221899976867365c239"},{url:"/screenshot3.png",revision:"49709e96345dfeeab1d8083821d4b44e"},{url:"/screenshot4.png",revision:"a76c751e41e37556048a487e4f8b8b1c"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:s,event:n,state:t})=>s&&"opaqueredirect"===s.type?new Response(s.body,{status:200,statusText:"OK",headers:s.headers}):s}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;const s=e.pathname;return!s.startsWith("/api/auth/")&&!!s.startsWith("/api/")},new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;return!e.pathname.startsWith("/api/")},new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>!(self.origin===e.origin),new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET")});
|
||||
|
||||
|
After Width: | Height: | Size: 111 KiB |
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-var-requires, no-console */
|
||||
|
||||
/**
|
||||
* 智能包管理器检测和推荐脚本
|
||||
* 帮助用户选择最适合的包管理器
|
||||
*/
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
const fs = require('fs');
|
||||
|
||||
console.log('🔍 检测包管理器环境...\n');
|
||||
|
||||
// 检测函数
|
||||
function checkCommand(command) {
|
||||
try {
|
||||
execSync(`${command} --version`, { stdio: 'pipe' });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getVersion(command) {
|
||||
try {
|
||||
const version = execSync(`${command} --version`, { encoding: 'utf8' }).trim();
|
||||
return version;
|
||||
} catch {
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
// 检测包管理器
|
||||
const hasNpm = checkCommand('npm');
|
||||
const hasPnpm = checkCommand('pnpm');
|
||||
const hasYarn = checkCommand('yarn');
|
||||
|
||||
const npmVersion = hasNpm ? getVersion('npm') : null;
|
||||
const pnpmVersion = hasPnpm ? getVersion('pnpm') : null;
|
||||
const yarnVersion = hasYarn ? getVersion('yarn') : null;
|
||||
|
||||
// 检测锁文件
|
||||
const hasPnpmLock = fs.existsSync('pnpm-lock.yaml');
|
||||
const hasNpmLock = fs.existsSync('package-lock.json');
|
||||
const hasYarnLock = fs.existsSync('yarn.lock');
|
||||
|
||||
console.log('📦 包管理器检测结果:');
|
||||
console.log(` npm: ${hasNpm ? '✅ ' + npmVersion : '❌ 未安装'}`);
|
||||
console.log(` pnpm: ${hasPnpm ? '✅ ' + pnpmVersion : '❌ 未安装'}`);
|
||||
console.log(` yarn: ${hasYarn ? '✅ ' + yarnVersion : '❌ 未安装'}`);
|
||||
|
||||
console.log('\n🔒 锁文件检测结果:');
|
||||
console.log(` pnpm-lock.yaml: ${hasPnpmLock ? '✅ 存在' : '❌ 不存在'}`);
|
||||
console.log(` package-lock.json: ${hasNpmLock ? '✅ 存在' : '❌ 不存在'}`);
|
||||
console.log(` yarn.lock: ${hasYarnLock ? '✅ 存在' : '❌ 不存在'}`);
|
||||
|
||||
// 智能推荐
|
||||
console.log('\n💡 智能推荐:');
|
||||
|
||||
if (hasPnpm && hasPnpmLock) {
|
||||
console.log(' 🎯 推荐使用 pnpm (已安装且有锁文件)');
|
||||
console.log(' 📝 运行命令: pnpm install && pnpm dev');
|
||||
} else if (hasNpm && hasNpmLock) {
|
||||
console.log(' 🎯 推荐使用 npm (已安装且有锁文件)');
|
||||
console.log(' 📝 运行命令: npm install && npm run dev');
|
||||
} else if (hasPnpm) {
|
||||
console.log(' 🎯 推荐使用 pnpm (性能更好)');
|
||||
console.log(' 📝 运行命令: pnpm install && pnpm dev');
|
||||
} else if (hasNpm) {
|
||||
console.log(' 🎯 使用 npm (已安装)');
|
||||
console.log(' 📝 运行命令: npm install && npm run dev');
|
||||
} else {
|
||||
console.log(' ❌ 未检测到任何包管理器,请先安装 Node.js');
|
||||
}
|
||||
|
||||
// 安装建议
|
||||
if (!hasPnpm && hasNpm) {
|
||||
console.log('\n🚀 pnpm 安装建议 (可选):');
|
||||
console.log(' npm install -g pnpm # 通过npm安装');
|
||||
console.log(' corepack enable && corepack prepare pnpm@latest --activate # 通过corepack');
|
||||
}
|
||||
|
||||
console.log('\n✨ KatelyaTV 支持智能包管理器检测,任何包管理器都可以正常工作!');
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
'use client';
|
||||
|
||||
import { AlertCircle, CheckCircle } from 'lucide-react';
|
||||
@@ -85,10 +83,10 @@ function LoginPageClient() {
|
||||
// 在客户端挂载后设置配置
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const storageType = (window as any).RUNTIME_CONFIG?.STORAGE_TYPE;
|
||||
setShouldAskUsername(storageType && storageType !== 'localstorage');
|
||||
const storageType = window.RUNTIME_CONFIG?.STORAGE_TYPE;
|
||||
setShouldAskUsername(Boolean(storageType && storageType !== 'localstorage'));
|
||||
setEnableRegister(
|
||||
Boolean((window as any).RUNTIME_CONFIG?.ENABLE_REGISTER)
|
||||
Boolean(window.RUNTIME_CONFIG?.ENABLE_REGISTER)
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any, react-hooks/exhaustive-deps, no-console */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
|
||||
'use client';
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Suspense, useEffect, useState } from 'react';
|
||||
|
||||
// 客户端收藏 API
|
||||
import {
|
||||
type Favorite,
|
||||
clearAllFavorites,
|
||||
getAllFavorites,
|
||||
getAllPlayRecords,
|
||||
@@ -19,7 +20,6 @@ import { DoubanItem } from '@/lib/types';
|
||||
import CapsuleSwitch from '@/components/CapsuleSwitch';
|
||||
import ContinueWatching from '@/components/ContinueWatching';
|
||||
import PageLayout from '@/components/PageLayout';
|
||||
import ScrollableRow from '@/components/ScrollableRow';
|
||||
import { useSite } from '@/components/SiteProvider';
|
||||
import VideoCard from '@/components/VideoCard';
|
||||
|
||||
@@ -137,7 +137,8 @@ function HomeClient() {
|
||||
setHotVarietyShows(varietyShowsData.list);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取豆瓣数据失败:', error);
|
||||
// 静默处理错误,避免控制台警告
|
||||
// console.error('获取豆瓣数据失败:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -147,7 +148,7 @@ function HomeClient() {
|
||||
}, []);
|
||||
|
||||
// 处理收藏数据更新的函数
|
||||
const updateFavoriteItems = async (allFavorites: Record<string, any>) => {
|
||||
const updateFavoriteItems = async (allFavorites: Record<string, Favorite>) => {
|
||||
const allPlayRecords = await getAllPlayRecords();
|
||||
|
||||
// 根据保存时间排序(从近到远)
|
||||
@@ -191,7 +192,7 @@ function HomeClient() {
|
||||
// 监听收藏更新事件
|
||||
const unsubscribe = subscribeToDataUpdates(
|
||||
'favoritesUpdated',
|
||||
(newFavorites: Record<string, any>) => {
|
||||
(newFavorites: Record<string, Favorite>) => {
|
||||
updateFavoriteItems(newFavorites);
|
||||
}
|
||||
);
|
||||
@@ -290,13 +291,13 @@ function HomeClient() {
|
||||
<ChevronRight className='w-4 h-4 ml-1' />
|
||||
</Link>
|
||||
</div>
|
||||
<ScrollableRow>
|
||||
<div className='grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4'>
|
||||
{loading
|
||||
? // 加载状态显示灰色占位数据
|
||||
Array.from({ length: 8 }).map((_, index) => (
|
||||
? // 加载状态显示灰色占位数据 (显示10个,2行x5列)
|
||||
Array.from({ length: 10 }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className='min-w-[96px] w-24 sm:min-w-[180px] sm:w-44'
|
||||
className='w-full'
|
||||
>
|
||||
<div className='relative aspect-[2/3] w-full overflow-hidden rounded-lg bg-purple-200 animate-pulse dark:bg-purple-800'>
|
||||
<div className='absolute inset-0 bg-purple-300 dark:bg-purple-700'></div>
|
||||
@@ -304,11 +305,11 @@ function HomeClient() {
|
||||
<div className='mt-2 h-4 bg-purple-200 rounded animate-pulse dark:bg-purple-800'></div>
|
||||
</div>
|
||||
))
|
||||
: // 显示真实数据
|
||||
hotMovies.map((movie, index) => (
|
||||
: // 显示真实数据,只显示前10个实现2行布局
|
||||
hotMovies.slice(0, 10).map((movie, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className='min-w-[96px] w-24 sm:min-w-[180px] sm:w-44'
|
||||
className='w-full'
|
||||
>
|
||||
<VideoCard
|
||||
from='douban'
|
||||
@@ -321,7 +322,7 @@ function HomeClient() {
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</ScrollableRow>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 热门剧集 */}
|
||||
@@ -338,13 +339,13 @@ function HomeClient() {
|
||||
<ChevronRight className='w-4 h-4 ml-1' />
|
||||
</Link>
|
||||
</div>
|
||||
<ScrollableRow>
|
||||
<div className='grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4'>
|
||||
{loading
|
||||
? // 加载状态显示灰色占位数据
|
||||
Array.from({ length: 8 }).map((_, index) => (
|
||||
? // 加载状态显示灰色占位数据 (显示10个,2行x5列)
|
||||
Array.from({ length: 10 }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className='min-w-[96px] w-24 sm:min-w-[180px] sm:w-44'
|
||||
className='w-full'
|
||||
>
|
||||
<div className='relative aspect-[2/3] w-full overflow-hidden rounded-lg bg-purple-200 animate-pulse dark:bg-purple-800'>
|
||||
<div className='absolute inset-0 bg-purple-300 dark:bg-purple-700'></div>
|
||||
@@ -352,11 +353,11 @@ function HomeClient() {
|
||||
<div className='mt-2 h-4 bg-purple-200 rounded animate-pulse dark:bg-purple-800'></div>
|
||||
</div>
|
||||
))
|
||||
: // 显示真实数据
|
||||
hotTvShows.map((show, index) => (
|
||||
: // 显示真实数据,只显示前10个实现2行布局
|
||||
hotTvShows.slice(0, 10).map((show, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className='min-w-[96px] w-24 sm:min-w-[180px] sm:w-44'
|
||||
className='w-full'
|
||||
>
|
||||
<VideoCard
|
||||
from='douban'
|
||||
@@ -368,7 +369,7 @@ function HomeClient() {
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</ScrollableRow>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 热门综艺 */}
|
||||
@@ -385,13 +386,13 @@ function HomeClient() {
|
||||
<ChevronRight className='w-4 h-4 ml-1' />
|
||||
</Link>
|
||||
</div>
|
||||
<ScrollableRow>
|
||||
<div className='grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4'>
|
||||
{loading
|
||||
? // 加载状态显示灰色占位数据
|
||||
Array.from({ length: 8 }).map((_, index) => (
|
||||
? // 加载状态显示灰色占位数据 (显示10个,2行x5列)
|
||||
Array.from({ length: 10 }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className='min-w-[96px] w-24 sm:min-w-[180px] sm:w-44'
|
||||
className='w-full'
|
||||
>
|
||||
<div className='relative aspect-[2/3] w-full overflow-hidden rounded-lg bg-purple-200 animate-pulse dark:bg-purple-800'>
|
||||
<div className='absolute inset-0 bg-purple-300 dark:bg-purple-700'></div>
|
||||
@@ -399,11 +400,11 @@ function HomeClient() {
|
||||
<div className='mt-2 h-4 bg-purple-200 rounded animate-pulse dark:bg-purple-800'></div>
|
||||
</div>
|
||||
))
|
||||
: // 显示真实数据
|
||||
hotVarietyShows.map((show, index) => (
|
||||
: // 显示真实数据,只显示前10个实现2行布局
|
||||
hotVarietyShows.slice(0, 10).map((show, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className='min-w-[96px] w-24 sm:min-w-[180px] sm:w-44'
|
||||
className='w-full'
|
||||
>
|
||||
<VideoCard
|
||||
from='douban'
|
||||
@@ -415,7 +416,7 @@ function HomeClient() {
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</ScrollableRow>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 首页底部 Logo */}
|
||||
|
||||
@@ -183,20 +183,24 @@ const PageLayout = ({ children, activePath = '/' }: PageLayoutProps) => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 主内容容器 - 修改布局实现完全居中:左右各留白1/6,主内容区占2/3 */}
|
||||
{/* 主内容容器 - 为播放页面使用特殊布局(83.33%宽度),其他页面使用默认布局(66.67%宽度) */}
|
||||
<main className='flex-1 md:min-h-0 mb-14 md:mb-0 md:p-6 lg:p-8'>
|
||||
{/* 使用flex布局实现三等分 */}
|
||||
{/* 使用flex布局实现宽度控制 */}
|
||||
<div className='flex w-full min-h-screen md:min-h-[calc(100vh-10rem)]'>
|
||||
{/* 左侧留白区域 - 占1/6 */}
|
||||
{/* 左侧留白区域 - 播放页面占8.33%,其他页面占16.67% */}
|
||||
<div
|
||||
className='hidden md:block flex-shrink-0'
|
||||
style={{ width: '16.67%' }}
|
||||
style={{
|
||||
width: ['/play'].includes(activePath) ? '8.33%' : '16.67%'
|
||||
}}
|
||||
></div>
|
||||
|
||||
{/* 主内容区 - 占2/3 */}
|
||||
{/* 主内容区 - 播放页面占83.33%,其他页面占66.67% */}
|
||||
<div
|
||||
className='flex-1 md:flex-none rounded-container w-full'
|
||||
style={{ width: '66.67%' }}
|
||||
style={{
|
||||
width: ['/play'].includes(activePath) ? '83.33%' : '66.67%'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className='p-4 md:p-8 lg:p-10'
|
||||
@@ -208,10 +212,12 @@ const PageLayout = ({ children, activePath = '/' }: PageLayoutProps) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 右侧留白区域 - 占1/6 */}
|
||||
{/* 右侧留白区域 - 播放页面占8.33%,其他页面占16.67% */}
|
||||
<div
|
||||
className='hidden md:block flex-shrink-0'
|
||||
style={{ width: '16.67%' }}
|
||||
style={{
|
||||
width: ['/play'].includes(activePath) ? '8.33%' : '16.67%'
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any,react-hooks/exhaustive-deps */
|
||||
|
||||
'use client';
|
||||
|
||||
import { Moon, Sun } from 'lucide-react';
|
||||
@@ -25,7 +23,7 @@ export function ThemeToggle() {
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
setThemeColor(resolvedTheme);
|
||||
}, []);
|
||||
}, [resolvedTheme]);
|
||||
|
||||
if (!mounted) {
|
||||
// 渲染一个占位符以避免布局偏移
|
||||
@@ -36,12 +34,18 @@ export function ThemeToggle() {
|
||||
// 检查浏览器是否支持 View Transitions API
|
||||
const targetTheme = resolvedTheme === 'dark' ? 'light' : 'dark';
|
||||
setThemeColor(targetTheme);
|
||||
if (!(document as any).startViewTransition) {
|
||||
|
||||
// 使用更好的类型定义
|
||||
const documentWithTransition = document as Document & {
|
||||
startViewTransition?: (callback: () => void) => void;
|
||||
};
|
||||
|
||||
if (!documentWithTransition.startViewTransition) {
|
||||
setTheme(targetTheme);
|
||||
return;
|
||||
}
|
||||
|
||||
(document as any).startViewTransition(() => {
|
||||
documentWithTransition.startViewTransition(() => {
|
||||
setTheme(targetTheme);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -403,7 +403,7 @@ export const UserMenu: React.FC = () => {
|
||||
{/* 版本信息 */}
|
||||
<button
|
||||
onClick={() =>
|
||||
window.open('https://github.com/senshinya/MoonTV', '_blank')
|
||||
window.open('https://github.com/katelya77/KatelyaTV', '_blank')
|
||||
}
|
||||
className='w-full px-3 py-2 text-center flex items-center justify-center text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors text-xs'
|
||||
>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import { CheckCircle, Heart, Link, PlayCircleIcon } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import {
|
||||
type Favorite,
|
||||
deleteFavorite,
|
||||
deletePlayRecord,
|
||||
generateStorageKey,
|
||||
@@ -131,7 +130,7 @@ export default function VideoCard({
|
||||
const storageKey = generateStorageKey(actualSource, actualId);
|
||||
const unsubscribe = subscribeToDataUpdates(
|
||||
'favoritesUpdated',
|
||||
(newFavorites: Record<string, any>) => {
|
||||
(newFavorites: Record<string, Favorite>) => {
|
||||
// 检查当前项目是否在新的收藏列表中
|
||||
const isNowFavorited = !!newFavorites[storageKey];
|
||||
setFavorited(isNowFavorited);
|
||||
|
||||
@@ -65,7 +65,7 @@ const LEGACY_SEARCH_HISTORY_KEY = 'moontv_search_history';
|
||||
|
||||
// 缓存相关常量
|
||||
const CACHE_PREFIX = 'katelyatv_cache_';
|
||||
const LEGACY_CACHE_PREFIX = 'moontv_cache_';
|
||||
const _LEGACY_CACHE_PREFIX = 'moontv_cache_'; // 保留用于将来的迁移功能
|
||||
const CACHE_VERSION = '1.0.0';
|
||||
const CACHE_EXPIRE_TIME = 60 * 60 * 1000; // 一小时缓存过期
|
||||
|
||||
|
||||
@@ -95,3 +95,18 @@ export interface DoubanResult {
|
||||
message: string;
|
||||
list: DoubanItem[];
|
||||
}
|
||||
|
||||
// Runtime配置类型
|
||||
export interface RuntimeConfig {
|
||||
STORAGE_TYPE?: string;
|
||||
ENABLE_REGISTER?: boolean;
|
||||
IMAGE_PROXY?: string;
|
||||
DOUBAN_PROXY?: string;
|
||||
}
|
||||
|
||||
// 全局Window类型扩展
|
||||
declare global {
|
||||
interface Window {
|
||||
RUNTIME_CONFIG?: RuntimeConfig;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
'use client';
|
||||
|
||||
const CURRENT_VERSION = '20250928125318';
|
||||
const CURRENT_VERSION = '20250831153112';
|
||||
|
||||
// 版本检查结果枚举
|
||||
export enum UpdateStatus {
|
||||
|
||||