列表开始就展示20条,
This commit is contained in:
parent
c5f2641377
commit
b77ff62c58
@ -1,4 +1,5 @@
|
|||||||
import { chromium } from 'playwright'
|
import { chromium } from 'playwright'
|
||||||
|
import readlineSync from 'readline-sync'
|
||||||
import { ensureDataFiles, readCache, writeCache, writeStatus } from './lib/cache.js'
|
import { ensureDataFiles, readCache, writeCache, writeStatus } from './lib/cache.js'
|
||||||
|
|
||||||
const PAGE_URL = 'https://www1.gdtv.cn/tvColumn/768'
|
const PAGE_URL = 'https://www1.gdtv.cn/tvColumn/768'
|
||||||
@ -73,10 +74,7 @@ async function getPageScrollInfo(page) {
|
|||||||
return await page.evaluate(() => {
|
return await page.evaluate(() => {
|
||||||
return {
|
return {
|
||||||
scrollTop: window.scrollY || document.documentElement.scrollTop || document.body.scrollTop || 0,
|
scrollTop: window.scrollY || document.documentElement.scrollTop || document.body.scrollTop || 0,
|
||||||
scrollHeight: Math.max(
|
scrollHeight: Math.max(document.body.scrollHeight, document.documentElement.scrollHeight),
|
||||||
document.body.scrollHeight,
|
|
||||||
document.documentElement.scrollHeight
|
|
||||||
),
|
|
||||||
innerHeight: window.innerHeight
|
innerHeight: window.innerHeight
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -153,7 +151,12 @@ async function clickLoadMoreAndWait(page) {
|
|||||||
return !!response
|
return !!response
|
||||||
}
|
}
|
||||||
|
|
||||||
async function autoCollectByScrollAndClick(page, maxRounds = 120) {
|
function askContinue() {
|
||||||
|
const answer = readlineSync.question('\n页面已滑到底部,继续采集吗?(y/n): ')
|
||||||
|
return answer.trim().toLowerCase() === 'y'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function autoCollectByScrollAndClick(page, maxRounds = 150) {
|
||||||
let round = 0
|
let round = 0
|
||||||
let noChangeRounds = 0
|
let noChangeRounds = 0
|
||||||
let lastDomCount = await getDomItemCount(page)
|
let lastDomCount = await getDomItemCount(page)
|
||||||
@ -214,14 +217,30 @@ async function autoCollectByScrollAndClick(page, maxRounds = 120) {
|
|||||||
await sleep(1800)
|
await sleep(1800)
|
||||||
|
|
||||||
const retryButton = await findLoadMoreButton(page)
|
const retryButton = await findLoadMoreButton(page)
|
||||||
if (!retryButton) {
|
if (retryButton) {
|
||||||
noChangeRounds += 1
|
log('到底部后重新检测到“加载更多”按钮,继续点击')
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const shouldContinue = askContinue()
|
||||||
|
if (!shouldContinue) {
|
||||||
|
log('你选择结束采集')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
log('你选择继续采集,尝试再次下滑检测')
|
||||||
|
noChangeRounds = 0
|
||||||
|
await sleep(1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noChangeRounds >= 6) {
|
if (noChangeRounds >= 6) {
|
||||||
log('连续多轮没有新内容,停止采集')
|
const shouldContinue = askContinue()
|
||||||
break
|
if (!shouldContinue) {
|
||||||
|
log('连续多轮没有新内容,且你选择结束采集')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
log('你选择继续采集,重置无变化计数')
|
||||||
|
noChangeRounds = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"running": true,
|
"running": false,
|
||||||
"lastMessage": "已采集第 2 页",
|
"lastMessage": "自动采集完成",
|
||||||
"updatedAt": "2026-03-10T19:14:36.004Z",
|
"updatedAt": "2026-03-10T19:27:40.184Z"
|
||||||
"capturedPages": 26,
|
|
||||||
"totalItems": 1141
|
|
||||||
}
|
}
|
||||||
@ -1,170 +1,170 @@
|
|||||||
{
|
{
|
||||||
"updatedAt": "2026-03-10T19:14:35.996Z",
|
"updatedAt": "2026-03-10T19:27:32.690Z",
|
||||||
"name": "七十二家房客",
|
"name": "七十二家房客",
|
||||||
"coverUrl": "https://p2-grtn.itouchtv.cn/image/20191010/0.15048946623517580e4543e24e68e4824OSS1570677193.jpg",
|
"coverUrl": "https://p2-grtn.itouchtv.cn/image/20191010/0.15048946623517580e4543e24e68e4824OSS1570677193.jpg",
|
||||||
"displayType": 0,
|
"displayType": 0,
|
||||||
"pages": {
|
"pages": {
|
||||||
"1": {
|
"1": {
|
||||||
"currentPage": 1,
|
"currentPage": 1,
|
||||||
"beginScore": 1669824000000,
|
"beginScore": 1730390400000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:13:55.281Z"
|
"capturedAt": "2026-03-10T19:27:28.717Z"
|
||||||
},
|
},
|
||||||
"2": {
|
"2": {
|
||||||
"currentPage": 2,
|
"currentPage": 2,
|
||||||
"beginScore": 1669824000000,
|
"beginScore": 1730390400000,
|
||||||
"count": 40,
|
"count": 20,
|
||||||
"capturedAt": "2026-03-10T19:14:35.996Z"
|
"capturedAt": "2026-03-10T19:27:31.309Z"
|
||||||
},
|
},
|
||||||
"3": {
|
"3": {
|
||||||
"currentPage": 3,
|
"currentPage": 3,
|
||||||
"beginScore": 1701360000000,
|
"beginScore": 1730390400000,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"capturedAt": "2026-03-10T19:13:21.335Z"
|
"capturedAt": "2026-03-10T19:27:32.690Z"
|
||||||
},
|
},
|
||||||
"4": {
|
"4": {
|
||||||
"currentPage": 4,
|
"currentPage": 4,
|
||||||
"beginScore": 1763561820000,
|
"beginScore": 1763561820000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:11:26.676Z"
|
"capturedAt": "2026-03-10T19:25:11.140Z"
|
||||||
},
|
},
|
||||||
"5": {
|
"5": {
|
||||||
"currentPage": 5,
|
"currentPage": 5,
|
||||||
"beginScore": 1762962000000,
|
"beginScore": 1762962000000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:11:29.024Z"
|
"capturedAt": "2026-03-10T19:25:13.417Z"
|
||||||
},
|
},
|
||||||
"6": {
|
"6": {
|
||||||
"currentPage": 6,
|
"currentPage": 6,
|
||||||
"beginScore": 1762352220000,
|
"beginScore": 1762352220000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:11:33.591Z"
|
"capturedAt": "2026-03-10T19:25:18.001Z"
|
||||||
},
|
},
|
||||||
"7": {
|
"7": {
|
||||||
"currentPage": 7,
|
"currentPage": 7,
|
||||||
"beginScore": 1761746220000,
|
"beginScore": 1761746220000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:11:36.420Z"
|
"capturedAt": "2026-03-10T19:25:20.845Z"
|
||||||
},
|
},
|
||||||
"8": {
|
"8": {
|
||||||
"currentPage": 8,
|
"currentPage": 8,
|
||||||
"beginScore": 1761056280000,
|
"beginScore": 1761056280000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:11:39.251Z"
|
"capturedAt": "2026-03-10T19:25:23.655Z"
|
||||||
},
|
},
|
||||||
"9": {
|
"9": {
|
||||||
"currentPage": 9,
|
"currentPage": 9,
|
||||||
"beginScore": 1740315600000,
|
"beginScore": 1740315600000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:11:41.578Z"
|
"capturedAt": "2026-03-10T19:25:26.285Z"
|
||||||
},
|
},
|
||||||
"10": {
|
"10": {
|
||||||
"currentPage": 10,
|
"currentPage": 10,
|
||||||
"beginScore": 1738414800000,
|
"beginScore": 1738414800000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:11:44.739Z"
|
"capturedAt": "2026-03-10T19:25:29.425Z"
|
||||||
},
|
},
|
||||||
"11": {
|
"11": {
|
||||||
"currentPage": 11,
|
"currentPage": 11,
|
||||||
"beginScore": 1736514000000,
|
"beginScore": 1736514000000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:11:47.585Z"
|
"capturedAt": "2026-03-10T19:25:32.213Z"
|
||||||
},
|
},
|
||||||
"12": {
|
"12": {
|
||||||
"currentPage": 12,
|
"currentPage": 12,
|
||||||
"beginScore": 1734526800000,
|
"beginScore": 1734526800000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:11:50.449Z"
|
"capturedAt": "2026-03-10T19:25:35.060Z"
|
||||||
},
|
},
|
||||||
"13": {
|
"13": {
|
||||||
"currentPage": 13,
|
"currentPage": 13,
|
||||||
"beginScore": 1732714295000,
|
"beginScore": 1732714295000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:11:52.794Z"
|
"capturedAt": "2026-03-10T19:25:37.402Z"
|
||||||
},
|
},
|
||||||
"14": {
|
"14": {
|
||||||
"currentPage": 14,
|
"currentPage": 14,
|
||||||
"beginScore": 1730986358000,
|
"beginScore": 1730986358000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:11:57.376Z"
|
"capturedAt": "2026-03-10T19:25:42.036Z"
|
||||||
},
|
},
|
||||||
"15": {
|
"15": {
|
||||||
"currentPage": 15,
|
"currentPage": 15,
|
||||||
"beginScore": 1729171974000,
|
"beginScore": 1729171974000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:12:00.272Z"
|
"capturedAt": "2026-03-10T19:25:44.872Z"
|
||||||
},
|
},
|
||||||
"16": {
|
"16": {
|
||||||
"currentPage": 16,
|
"currentPage": 16,
|
||||||
"beginScore": 1727442000000,
|
"beginScore": 1727442000000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:12:03.129Z"
|
"capturedAt": "2026-03-10T19:25:47.704Z"
|
||||||
},
|
},
|
||||||
"17": {
|
"17": {
|
||||||
"currentPage": 17,
|
"currentPage": 17,
|
||||||
"beginScore": 1725713999999,
|
"beginScore": 1725713999999,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:12:05.452Z"
|
"capturedAt": "2026-03-10T19:25:50.068Z"
|
||||||
},
|
},
|
||||||
"18": {
|
"18": {
|
||||||
"currentPage": 18,
|
"currentPage": 18,
|
||||||
"beginScore": 1723899600000,
|
"beginScore": 1723899600000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:12:10.101Z"
|
"capturedAt": "2026-03-10T19:25:54.781Z"
|
||||||
},
|
},
|
||||||
"19": {
|
"19": {
|
||||||
"currentPage": 19,
|
"currentPage": 19,
|
||||||
"beginScore": 1721998800000,
|
"beginScore": 1721998800000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:12:12.989Z"
|
"capturedAt": "2026-03-10T19:25:57.635Z"
|
||||||
},
|
},
|
||||||
"20": {
|
"20": {
|
||||||
"currentPage": 20,
|
"currentPage": 20,
|
||||||
"beginScore": 1720186339000,
|
"beginScore": 1720186339000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:12:15.860Z"
|
"capturedAt": "2026-03-10T19:26:00.455Z"
|
||||||
},
|
},
|
||||||
"21": {
|
"21": {
|
||||||
"currentPage": 21,
|
"currentPage": 21,
|
||||||
"beginScore": 1718371929000,
|
"beginScore": 1718371929000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:12:18.172Z"
|
"capturedAt": "2026-03-10T19:26:02.811Z"
|
||||||
},
|
},
|
||||||
"22": {
|
"22": {
|
||||||
"currentPage": 22,
|
"currentPage": 22,
|
||||||
"beginScore": 1716641999999,
|
"beginScore": 1716641999999,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:12:21.399Z"
|
"capturedAt": "2026-03-10T19:26:06.118Z"
|
||||||
},
|
},
|
||||||
"23": {
|
"23": {
|
||||||
"currentPage": 23,
|
"currentPage": 23,
|
||||||
"beginScore": 1714827600000,
|
"beginScore": 1714827600000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:12:24.238Z"
|
"capturedAt": "2026-03-10T19:26:08.954Z"
|
||||||
},
|
},
|
||||||
"24": {
|
"24": {
|
||||||
"currentPage": 24,
|
"currentPage": 24,
|
||||||
"beginScore": 1713099599999,
|
"beginScore": 1713099599999,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:12:27.289Z"
|
"capturedAt": "2026-03-10T19:26:11.917Z"
|
||||||
},
|
},
|
||||||
"25": {
|
"25": {
|
||||||
"currentPage": 25,
|
"currentPage": 25,
|
||||||
"beginScore": 1711372007000,
|
"beginScore": 1711372007000,
|
||||||
"count": 40,
|
"count": 40,
|
||||||
"capturedAt": "2026-03-10T19:12:29.500Z"
|
"capturedAt": "2026-03-10T19:26:14.215Z"
|
||||||
},
|
},
|
||||||
"26": {
|
"26": {
|
||||||
"currentPage": 26,
|
"currentPage": 26,
|
||||||
"beginScore": 1709559052000,
|
"beginScore": 1709559052000,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"capturedAt": "2026-03-10T19:12:34.177Z"
|
"capturedAt": "2026-03-10T19:26:19.076Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"beginScoreMap": {
|
"beginScoreMap": {
|
||||||
"1": 1669824000000,
|
"1": 1730390400000,
|
||||||
"2": 1669824000000,
|
"2": 1730390400000,
|
||||||
"3": 1701360000000,
|
"3": 1730390400000,
|
||||||
"4": 1763561820000,
|
"4": 1763561820000,
|
||||||
"5": 1762962000000,
|
"5": 1762962000000,
|
||||||
"6": 1762352220000,
|
"6": 1762352220000,
|
||||||
|
|||||||
@ -1,9 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h1>{{ columnInfo.name || '七十二家房客播放器' }}</h1>
|
<div>
|
||||||
<p>缓存 {{ episodeList.length }} 条</p>
|
<h1>{{ columnInfo.name || '七十二家房客播放器' }}</h1>
|
||||||
<p class="sub">最后更新:{{ updatedAtText }}</p>
|
<p>共 {{ allEpisodeList.length }} 条,当前显示 {{ visibleEpisodeList.length }} 条</p>
|
||||||
|
<p class="sub">最后更新:{{ updatedAtText }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="header-actions">
|
||||||
|
<button class="action-btn" @click="fetchList">刷新数据</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
@ -24,23 +30,31 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="errorMsg" class="error">{{ errorMsg }}</div>
|
<div v-if="errorMsg" class="error">
|
||||||
|
{{ errorMsg }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="list-panel">
|
<div class="list-panel">
|
||||||
<div
|
<div
|
||||||
v-for="item in episodeList"
|
v-for="item in visibleEpisodeList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
class="episode-item"
|
class="episode-item"
|
||||||
:class="{ active: currentEpisode?.id === item.id }"
|
:class="{ active: currentEpisode?.id === item.id }"
|
||||||
@click="playEpisode(item)"
|
@click="playEpisode(item)"
|
||||||
>
|
>
|
||||||
<img :src="item.coverUrl" alt="" />
|
<img :src="item.coverUrl" alt="" loading="lazy" />
|
||||||
<div class="episode-text">
|
<div class="episode-text">
|
||||||
<div class="title">{{ item.title }}</div>
|
<div class="title">{{ item.title }}</div>
|
||||||
<div class="meta">{{ formatDate(item.releasedAt) }}</div>
|
<div class="meta">{{ formatDate(item.releasedAt) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="load-more-wrap" v-if="visibleEpisodeList.length < allEpisodeList.length">
|
||||||
|
<button class="load-more-btn" @click="loadMoreEpisodes">
|
||||||
|
加载更多
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -59,33 +73,112 @@ const columnInfo = reactive({
|
|||||||
coverUrl: ''
|
coverUrl: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
const episodeList = ref<EpisodeData[]>([])
|
const allEpisodeList = ref<EpisodeData[]>([])
|
||||||
|
const visibleEpisodeList = ref<EpisodeData[]>([])
|
||||||
const currentEpisode = ref<EpisodeData | null>(null)
|
const currentEpisode = ref<EpisodeData | null>(null)
|
||||||
const errorMsg = ref('')
|
const errorMsg = ref('')
|
||||||
const updatedAt = ref<string | null>(null)
|
const updatedAt = ref<string | null>(null)
|
||||||
|
|
||||||
|
const pageSize = 20
|
||||||
|
const currentVisibleCount = ref(pageSize)
|
||||||
|
|
||||||
const updatedAtText = computed(() => {
|
const updatedAtText = computed(() => {
|
||||||
if (!updatedAt.value) return '暂无缓存'
|
if (!updatedAt.value) return '暂无缓存'
|
||||||
return new Date(updatedAt.value).toLocaleString('zh-CN')
|
return new Date(updatedAt.value).toLocaleString('zh-CN')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function setCookie(name: string, value: string, days = 30) {
|
||||||
|
const expires = new Date()
|
||||||
|
expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000)
|
||||||
|
document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires.toUTCString()}; path=/`
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCookie(name: string): string | null {
|
||||||
|
const nameEQ = `${name}=`
|
||||||
|
const cookies = document.cookie.split(';')
|
||||||
|
for (let cookie of cookies) {
|
||||||
|
cookie = cookie.trim()
|
||||||
|
if (cookie.indexOf(nameEQ) === 0) {
|
||||||
|
return decodeURIComponent(cookie.substring(nameEQ.length))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateVisibleList() {
|
||||||
|
visibleEpisodeList.value = allEpisodeList.value.slice(0, currentVisibleCount.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadMoreEpisodes() {
|
||||||
|
currentVisibleCount.value += pageSize
|
||||||
|
updateVisibleList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function destroyHls(): void {
|
||||||
|
if (hlsRef.value) {
|
||||||
|
hlsRef.value.destroy()
|
||||||
|
hlsRef.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoRef.value) {
|
||||||
|
videoRef.value.ontimeupdate = null
|
||||||
|
videoRef.value.onloadedmetadata = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindProgressSave(video: HTMLVideoElement, item: EpisodeData) {
|
||||||
|
video.ontimeupdate = () => {
|
||||||
|
setCookie(`qishier_progress_${item.id}`, String(Math.floor(video.currentTime)), 30)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreProgress(video: HTMLVideoElement, item: EpisodeData) {
|
||||||
|
const savedTime = getCookie(`qishier_progress_${item.id}`)
|
||||||
|
if (savedTime && !Number.isNaN(Number(savedTime))) {
|
||||||
|
video.currentTime = Number(savedTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchList(): Promise<void> {
|
async function fetchList(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
errorMsg.value = ''
|
errorMsg.value = ''
|
||||||
const res = await getAllQishierList()
|
const res = await getAllQishierList()
|
||||||
const data = res.data
|
const data = res.data
|
||||||
|
|
||||||
|
console.log('前端拿到缓存:', {
|
||||||
|
updatedAt: data.updatedAt,
|
||||||
|
total: data.total,
|
||||||
|
pages: Object.keys(data.pages || {}).length,
|
||||||
|
listLength: Array.isArray(data.list) ? data.list.length : 0
|
||||||
|
})
|
||||||
|
|
||||||
columnInfo.name = data.name || '七十二家房客'
|
columnInfo.name = data.name || '七十二家房客'
|
||||||
columnInfo.coverUrl = data.coverUrl || ''
|
columnInfo.coverUrl = data.coverUrl || ''
|
||||||
updatedAt.value = data.updatedAt || null
|
updatedAt.value = data.updatedAt || null
|
||||||
episodeList.value = Array.isArray(data.list) ? data.list : []
|
|
||||||
|
|
||||||
if (episodeList.value.length > 0) {
|
allEpisodeList.value = Array.isArray(data.list) ? data.list : []
|
||||||
await playEpisode(episodeList.value[0])
|
currentVisibleCount.value = pageSize
|
||||||
|
updateVisibleList()
|
||||||
|
|
||||||
|
if (allEpisodeList.value.length > 0) {
|
||||||
|
const savedEpisodeId = getCookie('qishier_current_episode_id')
|
||||||
|
const savedEpisode = allEpisodeList.value.find((item) => item.id === savedEpisodeId)
|
||||||
|
|
||||||
|
if (savedEpisode) {
|
||||||
|
const index = allEpisodeList.value.findIndex((item) => item.id === savedEpisode.id)
|
||||||
|
if (index >= 0 && index + 1 > currentVisibleCount.value) {
|
||||||
|
currentVisibleCount.value = Math.ceil((index + 1) / pageSize) * pageSize
|
||||||
|
updateVisibleList()
|
||||||
|
}
|
||||||
|
await playEpisode(savedEpisode)
|
||||||
|
} else {
|
||||||
|
await playEpisode(allEpisodeList.value[0])
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
errorMsg.value = '缓存里还没有节目数据,请先运行采集脚本'
|
errorMsg.value = '缓存里还没有节目数据,请先运行采集脚本'
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
console.error('fetchList error:', error)
|
||||||
errorMsg.value = error?.message || '读取缓存失败'
|
errorMsg.value = error?.message || '读取缓存失败'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,6 +186,7 @@ async function fetchList(): Promise<void> {
|
|||||||
async function playEpisode(item: EpisodeData): Promise<void> {
|
async function playEpisode(item: EpisodeData): Promise<void> {
|
||||||
currentEpisode.value = item
|
currentEpisode.value = item
|
||||||
errorMsg.value = ''
|
errorMsg.value = ''
|
||||||
|
setCookie('qishier_current_episode_id', item.id, 30)
|
||||||
|
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
@ -104,6 +198,12 @@ async function playEpisode(item: EpisodeData): Promise<void> {
|
|||||||
if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
||||||
video.src = item.videoUrl
|
video.src = item.videoUrl
|
||||||
video.load()
|
video.load()
|
||||||
|
|
||||||
|
video.onloadedmetadata = () => {
|
||||||
|
restoreProgress(video, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
bindProgressSave(video, item)
|
||||||
void video.play().catch(() => {})
|
void video.play().catch(() => {})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -114,6 +214,8 @@ async function playEpisode(item: EpisodeData): Promise<void> {
|
|||||||
hls.attachMedia(video)
|
hls.attachMedia(video)
|
||||||
|
|
||||||
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||||
|
restoreProgress(video, item)
|
||||||
|
bindProgressSave(video, item)
|
||||||
void video.play().catch(() => {})
|
void video.play().catch(() => {})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -127,13 +229,6 @@ async function playEpisode(item: EpisodeData): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function destroyHls(): void {
|
|
||||||
if (hlsRef.value) {
|
|
||||||
hlsRef.value.destroy()
|
|
||||||
hlsRef.value = 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)
|
||||||
@ -166,45 +261,93 @@ onBeforeUnmount(() => {
|
|||||||
background: #111;
|
background: #111;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header h1 {
|
.header h1 {
|
||||||
margin: 0 0 8px;
|
margin: 0 0 8px;
|
||||||
}
|
}
|
||||||
.sub {
|
|
||||||
|
.header p {
|
||||||
|
margin: 0 0 6px;
|
||||||
color: #999;
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
padding: 10px 16px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #333;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn:hover {
|
||||||
|
background: #444;
|
||||||
|
}
|
||||||
|
|
||||||
.layout {
|
.layout {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1.6fr 1fr;
|
grid-template-columns: 1.6fr 1fr;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-panel,
|
.player-panel,
|
||||||
.list-panel {
|
.list-panel {
|
||||||
background: #1b1b1b;
|
background: #1b1b1b;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video {
|
.video {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
aspect-ratio: 16 / 9;
|
aspect-ratio: 16 / 9;
|
||||||
background: #000;
|
background: #000;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info h2 {
|
||||||
|
margin: 0 0 10px;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info p {
|
||||||
|
margin: 0;
|
||||||
|
color: #aaa;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
padding: 0 16px 16px;
|
padding: 0 16px 16px;
|
||||||
color: #ff7875;
|
color: #ff7875;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-panel {
|
.list-panel {
|
||||||
max-height: 80vh;
|
max-height: 80vh;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-item {
|
.episode-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
@ -212,13 +355,17 @@ onBeforeUnmount(() => {
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
transition: 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-item:hover {
|
.episode-item:hover {
|
||||||
background: rgba(255, 255, 255, 0.06);
|
background: rgba(255, 255, 255, 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-item.active {
|
.episode-item.active {
|
||||||
background: rgba(255, 193, 7, 0.16);
|
background: rgba(255, 193, 7, 0.16);
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-item img {
|
.episode-item img {
|
||||||
width: 120px;
|
width: 120px;
|
||||||
height: 68px;
|
height: 68px;
|
||||||
@ -226,23 +373,49 @@ onBeforeUnmount(() => {
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-text {
|
.episode-text {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.meta {
|
.meta {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.load-more-wrap {
|
||||||
|
padding: 12px 0 4px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-more-btn {
|
||||||
|
padding: 10px 18px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #333;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-more-btn:hover {
|
||||||
|
background: #444;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
.layout {
|
.layout {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -10,11 +10,14 @@ export default defineConfig({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
|
port: 5173,
|
||||||
|
host: true,
|
||||||
|
open: true,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://localhost:3000',
|
target: 'http://localhost:3000',
|
||||||
changeOrigin: true
|
changeOrigin: true
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user