七十二家房客-观看平台v1.5
- 更改播放器 - 优化自动播放
This commit is contained in:
parent
139e1a2135
commit
c7f6d6b2b1
420
README.md
420
README.md
@ -1,146 +1,246 @@
|
|||||||
# 72QishierPlayer
|
# 72QishierPlayer 技术栈说明
|
||||||
|
|
||||||
一个基于 **Vue3 + Vite + Node.js + Playwright + HLS.js** 的《七十二家房客》在线播放项目。
|
一个基于 **Vue3 + Vite + Node.js + Playwright + HLS.js + Artplayer**
|
||||||
|
的《七十二家房客》在线播放项目。
|
||||||
|
|
||||||
本项目通过 **浏览器自动采集官网数据 → 本地缓存 JSON → Node API 提供 → Vue 前端播放** 的方式,解决官网接口 **签名、401、分页限制** 等问题,实现稳定播放。
|
本项目通过 **浏览器自动采集官网数据 → 本地缓存 JSON → Node API 提供 →
|
||||||
|
Vue 前端播放** 的方式,解决官网接口 **签名、401、分页限制**
|
||||||
|
等问题,实现稳定播放。
|
||||||
|
|
||||||
---
|
------------------------------------------------------------------------
|
||||||
|
# 运行与安装指南
|
||||||
|
|
||||||
# 项目特点
|
本文档说明 **72QishierPlayer** 项目的安装与运行步骤。\
|
||||||
|
项目由 **Node.js 后端 + Vue3 前端** 两部分组成,需要分别启动。
|
||||||
|
|
||||||
- 自动采集《七十二家房客》节目列表
|
------------------------------------------------------------------------
|
||||||
- 自动滚动加载更多节目
|
|
||||||
- 自动去重并缓存到 JSON
|
|
||||||
- Node API 提供数据
|
|
||||||
- Vue3 播放器展示
|
|
||||||
- HLS.js 播放 m3u8 视频
|
|
||||||
- Cookie 记录上次观看进度
|
|
||||||
- 前端分页加载,避免一次加载过多图片
|
|
||||||
|
|
||||||
---
|
# 一、后端服务(Node.js)
|
||||||
# 运行&安装
|
|
||||||
## 一、后端
|
|
||||||
安装后端依赖
|
|
||||||
```shell
|
|
||||||
npm i
|
|
||||||
```
|
|
||||||
|
|
||||||
开启后端服务
|
后端主要负责:
|
||||||
```shell
|
|
||||||
node server.js
|
|
||||||
```
|
|
||||||
|
|
||||||
如需更新数据:
|
- 提供节目 API
|
||||||
```shell
|
- 读取本地 JSON 数据
|
||||||
node capture.js
|
- 提供给前端播放列表
|
||||||
```
|
|
||||||
|
|
||||||
## 二、前端
|
## 1. 安装依赖
|
||||||
## 1 安装前端依赖
|
|
||||||
```
|
|
||||||
npm i
|
|
||||||
```
|
|
||||||
|
|
||||||
运行前端服务:
|
|
||||||
```shell
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2 安装后端依赖
|
|
||||||
|
|
||||||
进入后端目录:
|
进入后端目录:
|
||||||
|
|
||||||
```
|
``` bash
|
||||||
cd backServer
|
cd backServer
|
||||||
```
|
```
|
||||||
|
|
||||||
安装依赖:
|
安装依赖:
|
||||||
|
|
||||||
```
|
``` bash
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
安装 Playwright 浏览器:
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
```
|
## 2. 启动后端服务
|
||||||
npx playwright install chromium
|
|
||||||
|
``` bash
|
||||||
|
node server.js
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
默认接口地址:
|
||||||
|
|
||||||
# 运行项目
|
http://localhost:3000
|
||||||
|
|
||||||
## 1 采集节目数据
|
例如:
|
||||||
|
|
||||||
```
|
http://localhost:3000/api/qishier/list
|
||||||
npm run capture
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## 3. 更新节目数据(可选)
|
||||||
|
|
||||||
|
如果需要重新采集节目数据:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
node capture.js
|
||||||
```
|
```
|
||||||
|
|
||||||
程序会自动:
|
该脚本会:
|
||||||
|
|
||||||
1. 启动浏览器
|
1. 启动浏览器自动采集
|
||||||
2. 打开页面
|
2. 打开节目页面
|
||||||
|
3. 自动加载更多节目
|
||||||
|
4. 捕获接口数据
|
||||||
|
5. 更新本地缓存
|
||||||
|
|
||||||
```
|
数据保存位置:
|
||||||
https://www1.gdtv.cn/tvColumn/768
|
|
||||||
```
|
|
||||||
|
|
||||||
3. 自动滚动页面
|
|
||||||
4. 自动点击 **加载更多**
|
|
||||||
5. 捕获接口数据
|
|
||||||
6. 写入缓存
|
|
||||||
|
|
||||||
缓存文件:
|
|
||||||
|
|
||||||
```
|
|
||||||
backServer/data/qishier-cache.json
|
backServer/data/qishier-cache.json
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 二、前端服务(Vue3)
|
||||||
|
|
||||||
|
前端负责:
|
||||||
|
|
||||||
|
- 视频播放器
|
||||||
|
- 节目列表展示
|
||||||
|
- 自动下一集
|
||||||
|
- 播放进度记录
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## 1. 安装前端依赖
|
||||||
|
|
||||||
|
进入前端目录:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
cd 72QishierPlayer
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
安装依赖:
|
||||||
|
|
||||||
## 2 启动 Node API
|
``` bash
|
||||||
|
npm install
|
||||||
在 `backServer` 目录运行:
|
|
||||||
|
|
||||||
```
|
|
||||||
npm run server
|
|
||||||
```
|
```
|
||||||
|
|
||||||
API 地址:
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
```
|
## 2. 启动前端开发服务器
|
||||||
http://localhost:3000/api/qishier/all
|
|
||||||
```
|
|
||||||
|
|
||||||
返回示例:
|
``` bash
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"total": 1141,
|
|
||||||
"list": []
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3 启动 Vue 前端
|
|
||||||
|
|
||||||
回到项目根目录:地址:http://localhost:5173
|
|
||||||
|
|
||||||
```
|
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
默认访问地址:
|
||||||
|
|
||||||
---
|
http://localhost:5173
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 三、开发模式运行流程
|
||||||
|
|
||||||
|
建议的启动顺序:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
1. 启动 Node 后端
|
||||||
|
node server.js
|
||||||
|
|
||||||
|
2. 启动 Vue 前端
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
3. 浏览器访问
|
||||||
|
http://localhost:5173
|
||||||
|
```
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 四、生产环境部署
|
||||||
|
|
||||||
|
构建前端:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
构建完成后生成:
|
||||||
|
|
||||||
|
dist/
|
||||||
|
|
||||||
|
将 `dist` 部署到 Nginx 或静态服务器即可。
|
||||||
|
|
||||||
|
后端服务建议使用 **PM2** 运行:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
pm2 start server.js --name qishier-api
|
||||||
|
pm2 save
|
||||||
|
```
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 五、常见问题
|
||||||
|
|
||||||
|
## 1. 播放器无法播放
|
||||||
|
|
||||||
|
确认:
|
||||||
|
|
||||||
|
- 后端服务已启动
|
||||||
|
- API 地址正常
|
||||||
|
- m3u8 视频链接有效
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## 2. 没有节目数据
|
||||||
|
|
||||||
|
执行:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
node capture.js
|
||||||
|
```
|
||||||
|
|
||||||
|
重新采集节目列表。
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 六、默认端口
|
||||||
|
|
||||||
|
服务 端口
|
||||||
|
---------- ------
|
||||||
|
Vue 前端 5173
|
||||||
|
Node API 3000
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 技术栈(Tech Stack)
|
||||||
|
|
||||||
|
## 前端
|
||||||
|
|
||||||
|
技术 说明
|
||||||
|
------------- -----------------------
|
||||||
|
Vue3 前端框架
|
||||||
|
Vite 前端构建工具
|
||||||
|
TypeScript 类型安全开发
|
||||||
|
Axios HTTP 请求库
|
||||||
|
Artplayer 视频播放器 UI
|
||||||
|
hls.js m3u8 / HLS 视频流播放
|
||||||
|
HTML5 Video 浏览器视频播放能力
|
||||||
|
CSS3 页面样式
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## 后端
|
||||||
|
|
||||||
|
技术 说明
|
||||||
|
------------ ------------------
|
||||||
|
Node.js 后端运行环境
|
||||||
|
Express API 服务
|
||||||
|
Playwright 浏览器自动化采集
|
||||||
|
JSON 数据缓存存储
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## 数据采集
|
||||||
|
|
||||||
|
技术 说明
|
||||||
|
---------------- ------------------
|
||||||
|
Playwright 自动打开官网页面
|
||||||
|
浏览器接口拦截 捕获节目 API
|
||||||
|
自动滚动 加载更多节目
|
||||||
|
JSON缓存 本地节目数据存储
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## 视频播放技术
|
||||||
|
|
||||||
|
技术 说明
|
||||||
|
--------------------------- --------------------
|
||||||
|
HLS (HTTP Live Streaming) 视频流协议
|
||||||
|
hls.js 浏览器 HLS 解码
|
||||||
|
Artplayer 视频播放器 UI 控件
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
# 项目架构
|
# 项目架构
|
||||||
|
|
||||||
```
|
``` text
|
||||||
官网接口
|
官网接口
|
||||||
gdtv-api.gdtv.cn
|
gdtv-api.gdtv.cn
|
||||||
│
|
│
|
||||||
@ -153,113 +253,69 @@ backServer/data/qishier-cache.json
|
|||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
Node API
|
Node API
|
||||||
http://localhost:3000/api/qishier/all
|
/api/qishier/*
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
Vue 播放器
|
Vue3 前端
|
||||||
http://localhost:5173
|
Artplayer + HLS
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
# 项目目录
|
# 核心功能
|
||||||
|
|
||||||
```
|
功能 技术实现
|
||||||
|
----------------- --------------------
|
||||||
|
自动采集节目 Playwright
|
||||||
|
数据缓存 JSON
|
||||||
|
API 服务 Express
|
||||||
|
视频播放 Artplayer + hls.js
|
||||||
|
自动下一集 Vue 状态管理
|
||||||
|
顺序 / 倒序播放 列表排序
|
||||||
|
播放进度记录 Cookie
|
||||||
|
懒加载节目列表 前端分页
|
||||||
|
组件化架构 Vue Components
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 运行环境
|
||||||
|
|
||||||
|
环境 版本
|
||||||
|
---------- -------------------------
|
||||||
|
Node.js ≥ 18
|
||||||
|
npm ≥ 9
|
||||||
|
浏览器 Chrome / Edge / Safari
|
||||||
|
操作系统 macOS / Linux / Windows
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 项目目录结构
|
||||||
|
|
||||||
|
``` text
|
||||||
72QishierPlayer
|
72QishierPlayer
|
||||||
│
|
│
|
||||||
├─ src
|
├─ src
|
||||||
│ ├─ api
|
│ ├─ api
|
||||||
│ │ └─ qishier.ts
|
│ │ └─ qishier.ts
|
||||||
│ │
|
│ │
|
||||||
|
│ ├─ components
|
||||||
|
│ │ └─ qishier
|
||||||
|
│ │ ├─ QishierVideoPlayer.vue
|
||||||
|
│ │ └─ QishierEpisodeList.vue
|
||||||
|
│ │
|
||||||
│ ├─ views
|
│ ├─ views
|
||||||
│ │ └─ QishierPlayer.vue
|
│ │ └─ QishierPlayer.vue
|
||||||
│ │
|
│ │
|
||||||
│ ├─ App.vue
|
│ └─ utils
|
||||||
│ ├─ main.ts
|
│ └─ playHistory.ts
|
||||||
│ └─ assets
|
|
||||||
│
|
│
|
||||||
├─ backServer
|
├─ backServer
|
||||||
│ │
|
|
||||||
│ ├─ data
|
│ ├─ data
|
||||||
│ │ └─ qishier-cache.json
|
│ │ └─ qishier-cache.json
|
||||||
│ │
|
│ │
|
||||||
│ ├─ capture.js
|
│ ├─ capture.js
|
||||||
│ ├─ server.js
|
│ └─ server.js
|
||||||
│ └─ package.json
|
|
||||||
│
|
│
|
||||||
├─ vite.config.ts
|
├─ vite.config.ts
|
||||||
└─ package.json
|
└─ package.json
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 播放器功能
|
|
||||||
|
|
||||||
播放器支持:
|
|
||||||
|
|
||||||
- m3u8 视频播放
|
|
||||||
- HLS.js 播放器
|
|
||||||
- 节目列表分页加载
|
|
||||||
- 懒加载封面图
|
|
||||||
- 自动播放
|
|
||||||
- 自动恢复观看进度
|
|
||||||
|
|
||||||
观看记录存储在 Cookie:
|
|
||||||
|
|
||||||
```
|
|
||||||
qishier_last_episode
|
|
||||||
```
|
|
||||||
|
|
||||||
刷新页面会自动恢复上次观看。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 数据缓存机制
|
|
||||||
|
|
||||||
缓存文件:
|
|
||||||
|
|
||||||
```
|
|
||||||
backServer/data/qishier-cache.json
|
|
||||||
```
|
|
||||||
|
|
||||||
缓存策略:
|
|
||||||
|
|
||||||
- 新数据自动追加
|
|
||||||
- 根据 `id` 自动去重
|
|
||||||
- 避免重复节目
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 技术栈
|
|
||||||
|
|
||||||
前端:
|
|
||||||
|
|
||||||
- Vue 3
|
|
||||||
- Vite
|
|
||||||
- TypeScript
|
|
||||||
- Axios
|
|
||||||
- HLS.js
|
|
||||||
|
|
||||||
后端:
|
|
||||||
|
|
||||||
- Node.js
|
|
||||||
- Express
|
|
||||||
- Playwright
|
|
||||||
|
|
||||||
数据:
|
|
||||||
|
|
||||||
- JSON 本地缓存
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 免责声明
|
|
||||||
|
|
||||||
本项目仅用于:
|
|
||||||
|
|
||||||
- 技术学习
|
|
||||||
- 数据采集研究
|
|
||||||
- 视频播放技术演示
|
|
||||||
|
|
||||||
请勿用于商业用途。
|
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="list-panel">
|
<div ref="listPanelRef" class="list-panel">
|
||||||
<div
|
<div
|
||||||
v-for="item in episodes"
|
v-for="item in episodes"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
|
:data-episode-id="item.id"
|
||||||
class="episode-item"
|
class="episode-item"
|
||||||
:class="{ active: currentEpisodeId === item.id }"
|
:class="{ active: currentEpisodeId === item.id }"
|
||||||
@click="$emit('select', item)"
|
@click="$emit('select', item)"
|
||||||
@ -23,9 +24,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { nextTick, ref, watch } from 'vue'
|
||||||
import type { EpisodeData } from '@/api/qishier'
|
import type { EpisodeData } from '@/api/qishier'
|
||||||
|
|
||||||
defineProps<{
|
const props = defineProps<{
|
||||||
episodes: EpisodeData[]
|
episodes: EpisodeData[]
|
||||||
currentEpisodeId: string
|
currentEpisodeId: string
|
||||||
hasMore: boolean
|
hasMore: boolean
|
||||||
@ -37,6 +39,8 @@ defineEmits<{
|
|||||||
loadMore: []
|
loadMore: []
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const listPanelRef = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
function formatDate(timestamp: number): string {
|
function formatDate(timestamp: number): string {
|
||||||
if (!timestamp) return '--'
|
if (!timestamp) return '--'
|
||||||
const d = new Date(timestamp)
|
const d = new Date(timestamp)
|
||||||
@ -45,6 +49,47 @@ function formatDate(timestamp: number): string {
|
|||||||
const day = String(d.getDate()).padStart(2, '0')
|
const day = String(d.getDate()).padStart(2, '0')
|
||||||
return `${y}-${m}-${day}`
|
return `${y}-${m}-${day}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function scrollToActiveEpisode() {
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
const panel = listPanelRef.value
|
||||||
|
if (!panel || !props.currentEpisodeId) return
|
||||||
|
|
||||||
|
const activeEl = panel.querySelector(
|
||||||
|
`[data-episode-id="${props.currentEpisodeId}"]`
|
||||||
|
) as HTMLElement | null
|
||||||
|
|
||||||
|
if (!activeEl) return
|
||||||
|
|
||||||
|
const panelRect = panel.getBoundingClientRect()
|
||||||
|
const itemRect = activeEl.getBoundingClientRect()
|
||||||
|
|
||||||
|
const isAbove = itemRect.top < panelRect.top
|
||||||
|
const isBelow = itemRect.bottom > panelRect.bottom
|
||||||
|
|
||||||
|
if (isAbove || isBelow) {
|
||||||
|
activeEl.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'center'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.currentEpisodeId,
|
||||||
|
async () => {
|
||||||
|
await scrollToActiveEpisode()
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.episodes.length,
|
||||||
|
async () => {
|
||||||
|
await scrollToActiveEpisode()
|
||||||
|
}
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@ -54,6 +99,7 @@ function formatDate(timestamp: number): string {
|
|||||||
padding: 12px;
|
padding: 12px;
|
||||||
background: #1b1b1b;
|
background: #1b1b1b;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-item {
|
.episode-item {
|
||||||
@ -64,6 +110,7 @@ function formatDate(timestamp: number): string {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
transition: 0.2s;
|
transition: 0.2s;
|
||||||
|
border: 1px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-item:hover {
|
.episode-item:hover {
|
||||||
@ -72,6 +119,8 @@ function formatDate(timestamp: number): string {
|
|||||||
|
|
||||||
.episode-item.active {
|
.episode-item.active {
|
||||||
background: rgba(255, 193, 7, 0.16);
|
background: rgba(255, 193, 7, 0.16);
|
||||||
|
border-color: rgba(255, 193, 7, 0.45);
|
||||||
|
box-shadow: 0 0 0 1px rgba(255, 193, 7, 0.12) inset;
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-item img {
|
.episode-item img {
|
||||||
@ -95,6 +144,10 @@ function formatDate(timestamp: number): string {
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.episode-item.active .title {
|
||||||
|
color: #ffd666;
|
||||||
|
}
|
||||||
|
|
||||||
.meta {
|
.meta {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #999;
|
color: #999;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user