optimize
1
client/components.d.ts
vendored
@@ -23,6 +23,7 @@ declare module '@vue/runtime-core' {
|
||||
ElRow: typeof import('element-plus/es')['ElRow']
|
||||
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||
ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||
FilmList: typeof import('./src/components/FilmList.vue')['default']
|
||||
Footer: typeof import('./src/components/Footer.vue')['default']
|
||||
Header: typeof import('./src/components/Header.vue')['default']
|
||||
RelateList: typeof import('./src/components/RelateList.vue')['default']
|
||||
|
||||
|
Before Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1679472009425" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7100" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M 114.95 629.48 s -134.39 -197.75 80.63 -271.66 l 16.32 -9.6 s 8.64 -111.35 118.07 -142.07 c 0 0 68.16 -1.92 96.95 0 c 0 0 48.96 -99.83 174.71 -66.24 c 0 0 41.28 14.4 69.11 45.12 c 0 0 191.03 -62.4 181.43 124.79 c 0 0 -37.44 -173.75 -174.71 -79.67 c 0 0 -125.75 -135.35 -214.06 -19.2 l -20.16 32.64 s -171.83 -37.44 -207.35 137.27 c 0.01 -0.01 -203.49 31.67 -120.94 248.62 Z" fill="#1296db" p-id="7101"></path><path d="M 176.39 563.24 a 74.39 66.24 0 1 0 148.78 0 a 74.39 66.24 0 1 0 -148.78 0 Z" fill="#1296db" p-id="7102"></path><path d="M 383.25 414.93 m -69.12 0 a 69.12 69.12 0 1 0 138.24 0 a 69.12 69.12 0 1 0 -138.24 0 Z" fill="#1296db" p-id="7103"></path><path d="M 568.52 310.3 m -71.03 0 a 71.03 71.03 0 1 0 142.06 0 a 71.03 71.03 0 1 0 -142.06 0 Z" fill="#1296db" p-id="7104"></path><path d="M 673.15 329.5 a 61.44 70.07 0 1 0 122.88 0 a 61.44 70.07 0 1 0 -122.88 0 Z" fill="#1296db" p-id="7105"></path><path d="M 289.66 723.55 s 136.31 263.02 395.49 137.27 c 0 0 171.83 -88.31 136.31 -350.37 c 0 0 108.47 252.46 -122.87 383.97 c 0 0 -291.82 148.79 -408.93 -170.87 Z" fill="#1296db" p-id="7106"></path><path d="M 446.13 650.59 s 84.95 -139.67 239.02 -87.35 c 37.04 12.58 110.87 94.55 0 156.47 c -18.65 10.41 -43.2 7.68 -85.43 0 c 0 0 -29.76 63.36 -64.32 74.87 c 0 0 -134.39 57.6 -89.27 -143.99 Z" fill="#1296db" p-id="7107"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1679471653435" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="54082" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M799 832H224c-17.7 0-32 14.3-32 32s14.3 32 32 32h575c17.7 0 32-14.3 32-32s-14.3-32-32-32z m97-704H128c-35.3 0-64 28.7-64 64v511c0 35.3 28.7 64 64 64h768c35.3 0 64-28.7 64-64V192c0-35.3-28.7-64-64-64zM623.9 473.6l-197.6 98.5c-19.4 9.7-42.3-4.4-42.3-26v-197c0-21.6 22.9-35.7 42.3-26l197.6 98.5c21.5 10.7 21.5 41.3 0 52z" fill="#45C6FD" p-id="54083"></path></svg>
|
||||
|
Before Width: | Height: | Size: 694 B |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1679472049358" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8204" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M464 496H272c-17.6 0-32-14.4-32-32s14.4-32 32-32h192c17.6 0 32 14.4 32 32s-14.4 32-32 32z" fill="#1296db" p-id="8205"></path><path d="M368 720c-17.6 0-32-14.4-32-32V464c0-17.6 14.4-32 32-32s32 14.4 32 32v224c0 17.6-14.4 32-32 32zM656 720c-12.8 0-24-8-28.8-19.2l-96-224c-6.4-16 0-35.2 16-41.6 16-6.4 35.2 0 41.6 17.6L656 608l67.2-155.2c6.4-16 25.6-24 41.6-17.6 16 6.4 24 25.6 16 41.6l-96 224c-4.8 11.2-16 19.2-28.8 19.2z" fill="#1296db" p-id="8206"></path><path d="M848 928H176c-52.8 0-96-43.2-96-96V320c0-52.8 43.2-96 96-96h672c52.8 0 96 43.2 96 96v512c0 52.8-43.2 96-96 96zM176 288c-17.6 0-32 14.4-32 32v512c0 17.6 14.4 32 32 32h672c17.6 0 32-14.4 32-32V320c0-17.6-14.4-32-32-32H176z" fill="#1296db" p-id="8207"></path><path d="M448 288c-8 0-16-3.2-22.4-9.6l-160-160c-12.8-12.8-12.8-32 0-44.8 12.8-12.8 32-12.8 44.8 0l160 160c12.8 12.8 12.8 32 0 44.8-6.4 6.4-14.4 9.6-22.4 9.6zM576 288c-8 0-16-3.2-22.4-9.6-12.8-12.8-12.8-32 0-44.8l160-160c12.8-12.8 32-12.8 44.8 0 12.8 12.8 12.8 32 0 44.8l-160 160c-6.4 6.4-14.4 9.6-22.4 9.6z" fill="#1296db" p-id="8208"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
184
client/src/components/FilmList.vue
Normal file
@@ -0,0 +1,184 @@
|
||||
<template>
|
||||
<div class="c_content" >
|
||||
<div class="item" v-for="item in list" :style="{width: `calc(${ list.length <= 12 ? 16 : 14}% - 16px)`}">
|
||||
<a :href="`/filmDetail?link=${item.id}`" class="default_image" >
|
||||
<div class="tag_group">
|
||||
<span class="cus_tag ">{{ item.year?item.year.slice(0,4):'未知' }}</span>
|
||||
<span class="cus_tag ">{{ item.cName }}</span>
|
||||
<span class="cus_tag ">{{ item.area.split(',')[0] }}</span>
|
||||
</div>
|
||||
<span class="cus_remark hidden-md-and-up">{{ item.remarks }}</span>
|
||||
<img :src="item.picture" :alt="item.name.split('[')[0]" @error="handleImg" >
|
||||
</a>
|
||||
<a :href="`/filmDetail?link=${item.id}`" class="content_text_tag">{{ item.name.split("[")[0] }}</a>
|
||||
<span class="cus_remark hidden-md-and-down">{{ item.remarks }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {defineProps} from 'vue'
|
||||
defineProps({
|
||||
list: Array
|
||||
})
|
||||
|
||||
// 图片加载失败事件
|
||||
const handleImg =(e:Event)=>{
|
||||
e.target.style.display = "none"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.default_image {
|
||||
background: url("/src/assets/image/404.png");
|
||||
background-size: cover;
|
||||
}
|
||||
/*wrap*/
|
||||
@media (max-width: 650px) {
|
||||
/*展示区域*/
|
||||
.c_content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-flow: wrap;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.c_content .item {
|
||||
flex-basis: calc(33% - 6px);
|
||||
max-width: 33%;
|
||||
margin: 0 3px 20px 3px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.item a {
|
||||
padding-top: 125%;
|
||||
position: relative;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
background-size:cover ;
|
||||
}
|
||||
img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 5px;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tag_group {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.content_text_tag {
|
||||
font-size: 11px !important;
|
||||
color: rgb(221, 221, 221);
|
||||
width: 96%!important;
|
||||
max-height: 40px;
|
||||
line-height: 20px;
|
||||
padding: 2px 0 2px 0 !important;
|
||||
text-align: left;
|
||||
display: -webkit-box!important;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cus_remark {
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
color: #c2c2c2;
|
||||
text-align: center;
|
||||
background: rgba(0,0,0,0.55);
|
||||
border-radius: 0 0 5px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
/*pc*/
|
||||
@media (min-width: 650px) {
|
||||
.c_content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-flow: wrap;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.c_content .item {
|
||||
margin: 0 8px 20px 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.item a {
|
||||
border-radius: 5px;
|
||||
padding-top: 125%;
|
||||
background-size: cover;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
position: relative;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 5px;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tag_group {
|
||||
position: absolute;
|
||||
bottom: 3px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-wrap: wrap ;
|
||||
overflow: hidden;
|
||||
justify-content: start;
|
||||
height: 18px;
|
||||
z-index: 10;
|
||||
line-height: 18px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.cus_tag {
|
||||
flex-shrink: 0; /* 不缩小元素 */
|
||||
white-space: nowrap;
|
||||
color: rgb(255, 255, 255);
|
||||
padding: 0 3px;
|
||||
margin-right: 8px;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
font-size: 12px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.content_text_tag {
|
||||
display: block;
|
||||
font-size: 14px !important;
|
||||
color: rgb(221, 221, 221);
|
||||
width: 96%!important;
|
||||
padding: 2px 10px 2px 2px !important;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.cus_remark {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding-left: 3px;
|
||||
font-size: 12px;
|
||||
color: #999999;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- 左侧logo以及搜索 -->
|
||||
<div class="nav_left">
|
||||
<!-- <img class="logo" src="/src/assets/logo.png">-->
|
||||
<a href="/" style="font-weight: 600;font-style: italic;font-size: 24px;margin-right: 5px">Boat</a>
|
||||
<a href="/" class="site" >GoFilm</a>
|
||||
<div class="search_group">
|
||||
<input v-model="keyword" @keydown="(e)=>{e.keyCode == 13 && searchFilm()}" placeholder="搜索 动漫,剧集,电影 " class="search"/>
|
||||
<el-button @click="searchFilm" :icon="Search"/>
|
||||
@@ -130,6 +130,17 @@ onMounted(() => {
|
||||
.nav_left {
|
||||
display: flex;
|
||||
}
|
||||
/*site标志样式*/
|
||||
.site{
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
font-size: 24px;
|
||||
margin-right: 5px;
|
||||
background: linear-gradient(118deg, #e91a90, #c965b3, #988cd7, #00acfd);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
/*搜索栏*/
|
||||
.search_group {
|
||||
|
||||
@@ -1,154 +1,29 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<h2 class="title">相关影片</h2>
|
||||
<div class="content">
|
||||
<div class="item" v-for="m in relateList">
|
||||
<a :href="`/filmDetail?link=${m.id}`" class="cus_content_link"
|
||||
:style="{backgroundImage: `url('${m.picture}')`}">
|
||||
<span class="cus_tag hidden-md-and-down">{{ m.year }}</span>
|
||||
<span class="cus_tag hidden-md-and-down">{{ m.cName }}</span>
|
||||
<span class="cus_tag hidden-md-and-down">{{ m.area }}</span>
|
||||
<span class="cus_remark hidden-md-and-up">{{ m.remarks }}</span>
|
||||
</a>
|
||||
<a :href="`/filmDetail?link=${m.id}`"
|
||||
class=" content_text_tag">{{ m.name }}</a>
|
||||
<span class="cus_remark hidden-md-and-down">{{ m.remarks }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relate_container">
|
||||
<h2 class="title">相关推荐</h2>
|
||||
<FilmList :list="relateList"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import FilmList from "./FilmList.vue";
|
||||
|
||||
defineProps({
|
||||
relateList : []
|
||||
relateList: Array
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@media (min-width: 650px) {
|
||||
.container {
|
||||
width: 100%;
|
||||
margin-top: 36px;
|
||||
}
|
||||
.title {
|
||||
padding: 0 0 5px 10px;
|
||||
text-align: left;
|
||||
border-bottom: 2px solid #777777;
|
||||
}
|
||||
.content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-flow: wrap;
|
||||
justify-content: start;
|
||||
}
|
||||
.container .item {
|
||||
width: calc(14% - 20px);
|
||||
margin: 2px 10px 16px 10px;
|
||||
}
|
||||
.cus_content_link {
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
padding-top: 125%;
|
||||
background-size: cover;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cus_tag {
|
||||
text-align: center;
|
||||
color: rgb(255, 255, 255);
|
||||
padding: 0 3px;
|
||||
margin: 0 0 10px 8px;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
font-size: 12px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.content_text_tag {
|
||||
display: block;
|
||||
font-size: 13px !important;
|
||||
color: rgb(221, 221, 221);
|
||||
padding: 2px 2px 2px 2px !important;
|
||||
text-align: left;
|
||||
max-width: 80%;
|
||||
max-height: 20px;
|
||||
white-space: nowrap; /* 禁止换行 */
|
||||
overflow: hidden; /* 超出部分隐藏 */
|
||||
text-overflow: ellipsis; /* 使用省略号表示被截断的部分 */
|
||||
}
|
||||
.cus_remark {
|
||||
display: block;
|
||||
color: #888888;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.relate_container {
|
||||
width: 100%;
|
||||
margin-top: 36px;
|
||||
}
|
||||
|
||||
@media (max-width: 650px) {
|
||||
.container {
|
||||
width: 100%;
|
||||
margin-top: 36px;
|
||||
}
|
||||
.title {
|
||||
padding: 0 0 5px 10px;
|
||||
text-align: left;
|
||||
border-bottom: 2px solid #777777;
|
||||
}
|
||||
.content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-flow: wrap;
|
||||
justify-content: start;
|
||||
}
|
||||
.container .item {
|
||||
width: calc(33% - 8px);
|
||||
margin: 2px 4px 16px 4px;
|
||||
}
|
||||
.cus_content_link {
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
padding-top: 125%;
|
||||
background-size: cover;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cus_tag {
|
||||
text-align: center;
|
||||
color: rgb(255, 255, 255);
|
||||
padding: 0 3px;
|
||||
margin: 0 0 10px 8px;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
font-size: 12px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.content_text_tag {
|
||||
font-size: 12px !important;
|
||||
color: rgb(221, 221, 221);
|
||||
width: 100%;
|
||||
padding: 2px 0 2px 0 !important;
|
||||
text-align: left;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.cus_remark {
|
||||
display: block;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
color: #c2c2c2;
|
||||
text-align: center;
|
||||
background: rgba(0,0,0,0.55);
|
||||
border-radius: 0 0 5px 5px;
|
||||
}
|
||||
|
||||
.title {
|
||||
padding: 0 0 5px 10px;
|
||||
text-align: left;
|
||||
border-bottom: 2px solid #777777;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -60,7 +60,7 @@ const changeStyle = (type:string)=>{
|
||||
case 'light':
|
||||
control.darkTheme = false
|
||||
localStorage.setItem("theme", 'light')
|
||||
document.getElementsByClassName('main')[0].style.background = `radial-gradient(circle, #C147E9, #810CA8, #2D033B)`
|
||||
document.getElementsByClassName('main')[0].style.background = `linear-gradient(45deg, #356697, rgb(105, 68, 140), rgb(151, 109, 133), rgb(92 104 149))`
|
||||
break
|
||||
case 'dark':
|
||||
control.darkTheme = true
|
||||
@@ -81,6 +81,7 @@ const changeStyle = (type:string)=>{
|
||||
right: 10px;
|
||||
bottom: 15%;
|
||||
width: 35px;
|
||||
z-index: 20;
|
||||
}
|
||||
.util a {
|
||||
display: block;
|
||||
|
||||
@@ -49,9 +49,13 @@ import Footer from "../components/Footer.vue";
|
||||
@media (max-width: 650px) {
|
||||
.el-main {
|
||||
/*margin: 0 auto;*/
|
||||
padding: 60px 0;
|
||||
padding: 55px 0!important;
|
||||
/*padding-top: 100px!important;*/
|
||||
}
|
||||
:deep(.el-header) {
|
||||
height: 40px!important;
|
||||
min-height: 40px!important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-menu--horizontal) {
|
||||
|
||||
@@ -3,19 +3,42 @@
|
||||
<el-empty style="height: 100%; line-height: 100%; font-size: 20px" :image-size="400"
|
||||
image="/src/assets/image/404.png">
|
||||
<template #description>
|
||||
<p style="font-size: 32px;width: 100%;color: #a574b7">你好像走错地方了哦!!!</p>
|
||||
<p style="font-size: 32px;width: 100%;color: #a574b7;margin-bottom: 20px">你好像走错地方了哦!!!</p>
|
||||
<button @click="handleError('home')" >主页</button>
|
||||
<button @click="handleError('back')">返回</button>
|
||||
</template>
|
||||
</el-empty>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup lang="ts">
|
||||
import {useRouter} from "vue-router";
|
||||
|
||||
const router = useRouter()
|
||||
const handleError = (t:string) =>{
|
||||
switch (t) {
|
||||
case 'home':
|
||||
router.push('/')
|
||||
break
|
||||
case 'back':
|
||||
router.go(-1)
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
height: 100vh;
|
||||
|
||||
}
|
||||
button {
|
||||
margin: 5px 15px;
|
||||
background: rgb(103, 217, 232);
|
||||
color: #ffffff;
|
||||
width: 120px;
|
||||
border-radius: 36px;
|
||||
}
|
||||
button:hover {
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
@@ -1,40 +1,32 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<p>{{ d.category.name }}</p>
|
||||
<div class="c_header">
|
||||
<a :class="`nav ${d.list.length >0 && d.list[0].cid == c.id?'active':''}`" href="javascript:;" @click="changeCategory(c.id)"
|
||||
v-for="c in d.category.children">{{ c.name }}</a>
|
||||
<!-- <a :class="`nav `" href="javascript:;" @click="changeCategory">{{ d.category.name }}</a>-->
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<p>{{ d.category.name }}</p>
|
||||
<div class="c_header">
|
||||
<a :class="`nav ${d.list.length >0 && d.list[0].cid == c.id?'active':''}`" href="javascript:;"
|
||||
@click="changeCategory(c.id)"
|
||||
v-for="c in d.category.children">{{ c.name }}</a>
|
||||
<!-- <a :class="`nav `" href="javascript:;" @click="changeCategory">{{ d.category.name }}</a>-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="c_content" >
|
||||
<div class="item" v-for="item in d.list">
|
||||
<a :href="`/filmDetail?link=${item.id}`" :style="{backgroundImage: `url('${item.picture}')`}">
|
||||
<span class="cus_tag ">{{ item.year }}</span>
|
||||
<span class="cus_tag ">{{ item.cName }}</span>
|
||||
<span class="cus_tag ">{{ item.area }}</span>
|
||||
<span class="cus_remark hidden-md-and-up">{{ item.remarks }}</span>
|
||||
</a>
|
||||
<a :href="`/filmDetail?link=${item.id}`" class="content_text_tag">{{ item.name.split("[")[0] }}</a>
|
||||
<span class="cus_remark hidden-md-and-down">{{ item.remarks }}</span>
|
||||
</div>
|
||||
<!--影片列表展示-->
|
||||
<FilmList :list="d.list"/>
|
||||
<!--分页展示区域-->
|
||||
<div class="pagination_container ">
|
||||
<el-pagination background layout="prev, pager, next"
|
||||
v-model:current-page="d.page.current"
|
||||
@current-change="changeCurrent"
|
||||
:pager-count="5"
|
||||
:background="true"
|
||||
:page-size="d.page.pageSize"
|
||||
:total="d.page.total"
|
||||
:prev-icon="ArrowLeftBold"
|
||||
:next-icon="ArrowRightBold"
|
||||
hide-on-single-page
|
||||
class="pagination"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pagination_container ">
|
||||
<el-pagination background layout="prev, pager, next"
|
||||
v-model:current-page="d.page.current"
|
||||
@current-change="changeCurrent"
|
||||
:pager-count="5"
|
||||
:background="true"
|
||||
:page-size="d.page.pageSize"
|
||||
:total="d.page.total"
|
||||
:prev-icon="ArrowLeftBold"
|
||||
:next-icon="ArrowRightBold"
|
||||
hide-on-single-page
|
||||
class="pagination"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -45,14 +37,15 @@ import {useRouter} from "vue-router";
|
||||
import {ApiGet} from "../../utils/request";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {ArrowRightBold, ArrowLeftBold} from '@element-plus/icons-vue'
|
||||
import FilmList from "../../components/FilmList.vue";
|
||||
|
||||
// 页面所需数据
|
||||
const d = reactive({
|
||||
category: {},
|
||||
list: [],
|
||||
page: {
|
||||
current: 0,
|
||||
},
|
||||
category: {},
|
||||
list: [],
|
||||
page: {
|
||||
current: 0,
|
||||
},
|
||||
|
||||
})
|
||||
// 获取路由参数查询对应数据
|
||||
@@ -60,38 +53,38 @@ const router = useRouter()
|
||||
|
||||
// 点击分页按钮事件 current-change
|
||||
const changeCurrent = (currentVal: number) => {
|
||||
let query = router.currentRoute.value.query
|
||||
// router.push({path: '/categoryFilm', query:{pid: query.pid, cid: query.cid, current: currentVal}})
|
||||
if (query.cid && query.cid != "") {
|
||||
location.href = `/categoryFilm?pid=${query.pid}&cid=${query.cid}¤t=${currentVal}`
|
||||
} else {
|
||||
location.href = `/categoryFilm?pid=${query.pid}&¤t=${currentVal}`
|
||||
}
|
||||
let query = router.currentRoute.value.query
|
||||
// router.push({path: '/categoryFilm', query:{pid: query.pid, cid: query.cid, current: currentVal}})
|
||||
if (query.cid && query.cid != "") {
|
||||
location.href = `/categoryFilm?pid=${query.pid}&cid=${query.cid}¤t=${currentVal}`
|
||||
} else {
|
||||
location.href = `/categoryFilm?pid=${query.pid}&¤t=${currentVal}`
|
||||
}
|
||||
}
|
||||
|
||||
// 点击分类事件
|
||||
const changeCategory = (cid?: any) => {
|
||||
let params = new URLSearchParams(location.search)
|
||||
// location.href = `/categoryFilm?pid=${params.get('pid')}&cid=${cid}¤t=${params.get('current')?params.get('current'):1}`
|
||||
location.href = cid?`/categoryFilm?pid=${params.get('pid')}&cid=${cid}¤t=1`:`/categoryFilm?pid=${params.get('pid')}`
|
||||
let params = new URLSearchParams(location.search)
|
||||
// location.href = `/categoryFilm?pid=${params.get('pid')}&cid=${cid}¤t=${params.get('current')?params.get('current'):1}`
|
||||
location.href = cid ? `/categoryFilm?pid=${params.get('pid')}&cid=${cid}¤t=1` : `/categoryFilm?pid=${params.get('pid')}`
|
||||
}
|
||||
|
||||
|
||||
const getFilmData = (param: any) => {
|
||||
ApiGet('/filmCategory', {pid: param.pid, cid: param.cid, current: param.current}).then((resp: any) => {
|
||||
if (resp.status === 'ok') {
|
||||
d.category = resp.data.category
|
||||
d.list = resp.data.list
|
||||
d.page = resp.page
|
||||
} else {
|
||||
ElMessage.error({message: "请先输入影片名称关键字再进行搜索", duration: 1000})
|
||||
}
|
||||
})
|
||||
ApiGet('/filmCategory', {pid: param.pid, cid: param.cid, current: param.current}).then((resp: any) => {
|
||||
if (resp.status === 'ok') {
|
||||
d.category = resp.data.category
|
||||
d.list = resp.data.list
|
||||
d.page = resp.page
|
||||
} else {
|
||||
ElMessage.error({message: "请先输入影片名称关键字再进行搜索", duration: 1000})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
let query = router.currentRoute.value.query
|
||||
getFilmData({pid: query.pid, cid: query.cid, current: query.current})
|
||||
let query = router.currentRoute.value.query
|
||||
getFilmData({pid: query.pid, cid: query.cid, current: query.current})
|
||||
})
|
||||
|
||||
</script>
|
||||
@@ -103,12 +96,14 @@ onMounted(() => {
|
||||
padding: 0 10px;
|
||||
|
||||
}
|
||||
|
||||
/*顶部内容区域*/
|
||||
.header {
|
||||
width: 100%;
|
||||
margin-bottom: 100px;
|
||||
background: none!important;
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
.header p {
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
@@ -167,61 +162,10 @@ onMounted(() => {
|
||||
border-radius: 50px;
|
||||
transform: scaleX(1);
|
||||
}
|
||||
|
||||
.active {
|
||||
background: rgb(249 230 195)!important;
|
||||
color: #e52424!important;
|
||||
}
|
||||
|
||||
|
||||
/*展示区域*/
|
||||
.c_content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-flow: wrap;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.c_content .item {
|
||||
flex-basis: calc(33% - 6px);
|
||||
max-width: 33%;
|
||||
margin: 0 3px 20px 3px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.item a {
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
padding-top: 125%;
|
||||
background-size: cover;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cus_tag {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.content_text_tag {
|
||||
font-size: 12px !important;
|
||||
color: rgb(221, 221, 221);
|
||||
width: 100%;
|
||||
padding: 2px 0 2px 0 !important;
|
||||
text-align: left;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.cus_remark {
|
||||
display: block;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
color: #c2c2c2;
|
||||
text-align: center;
|
||||
background: rgba(0,0,0,0.55);
|
||||
border-radius: 0 0 5px 5px;
|
||||
background: rgb(249 230 195) !important;
|
||||
color: #e52424 !important;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -231,6 +175,7 @@ onMounted(() => {
|
||||
.container {
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
||||
@media (min-width: 650px) {
|
||||
|
||||
/*顶部内容区域*/
|
||||
@@ -252,17 +197,17 @@ onMounted(() => {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
/*justify-content: start;*/
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.c_header a {
|
||||
/*flex-basis: calc(14% - 16px);*/
|
||||
flex-basis: calc(14% - 16px);
|
||||
white-space: nowrap;
|
||||
margin-right: 20px;
|
||||
color: #000;
|
||||
font-weight: 800;
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
padding: 20px 40px;
|
||||
padding: 1.35% 0;
|
||||
border-radius: 10px;
|
||||
position: relative;
|
||||
}
|
||||
@@ -295,144 +240,93 @@ onMounted(() => {
|
||||
border-radius: 50px;
|
||||
transform: scaleX(1);
|
||||
}
|
||||
|
||||
.active {
|
||||
background: rgb(249 230 195)!important;
|
||||
color: #e52424!important;
|
||||
background: rgb(249 230 195) !important;
|
||||
color: #e52424 !important;
|
||||
}
|
||||
|
||||
|
||||
.c_content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-flow: wrap;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.c_content .item {
|
||||
flex-basis: calc(14% - 16px);
|
||||
margin: 0 8px 20px 8px;
|
||||
/*width: 100px;*/
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.item a {
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
/*position: relative;*/
|
||||
padding-top: 125%;
|
||||
background-size: cover;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cus_tag {
|
||||
text-align: center;
|
||||
color: rgb(255, 255, 255);
|
||||
padding: 0 3px;
|
||||
margin: 0 0 10px 8px;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
font-size: 12px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.content_text_tag {
|
||||
font-size: 14px !important;
|
||||
color: rgb(221, 221, 221);
|
||||
width: 96%;
|
||||
padding: 2px 10px 2px 2px !important;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.cus_remark {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding-left: 3px;
|
||||
font-size: 12px;
|
||||
color: #999999;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
/*分页插件区域*/
|
||||
.pagination_container {
|
||||
width: 100%;
|
||||
margin-top: 30px;
|
||||
/*background: deepskyblue;*/
|
||||
text-align: center;
|
||||
/*display: flex;*/
|
||||
width: 100%;
|
||||
margin-top: 30px;
|
||||
/*background: deepskyblue;*/
|
||||
text-align: center;
|
||||
/*display: flex;*/
|
||||
}
|
||||
|
||||
:deep(.el-pagination) {
|
||||
width: 100% !important;
|
||||
margin: 0 auto !important;
|
||||
justify-content: center;
|
||||
width: 100% !important;
|
||||
margin: 0 auto !important;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/*分页器样式修改*/
|
||||
:deep(.number) {
|
||||
font-weight: bold;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
background: #2e2e2e !important;
|
||||
color: #ffffff;
|
||||
border-radius: 50%;
|
||||
/*margin: 0 3px!important;*/
|
||||
font-weight: bold;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
background: #2e2e2e !important;
|
||||
color: #ffffff;
|
||||
border-radius: 50%;
|
||||
/*margin: 0 3px!important;*/
|
||||
}
|
||||
|
||||
:deep(.number:hover) {
|
||||
color: #67d9e8;
|
||||
color: #67d9e8;
|
||||
}
|
||||
|
||||
:deep(.btn-prev) {
|
||||
font-weight: bold;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
background: #2e2e2e !important;
|
||||
color: #ffffff;
|
||||
border-radius: 50%;
|
||||
font-weight: bold;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
background: #2e2e2e !important;
|
||||
color: #ffffff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
:deep(.btn-next) {
|
||||
font-weight: bold;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
background: #2e2e2e !important;
|
||||
color: #ffffff;
|
||||
border-radius: 50%;
|
||||
font-weight: bold;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
background: #2e2e2e !important;
|
||||
color: #ffffff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
:deep(.more) {
|
||||
font-weight: bold;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
background: #2e2e2e !important;
|
||||
color: #ffffff;
|
||||
border-radius: 50%;
|
||||
font-weight: bold;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
background: #2e2e2e !important;
|
||||
color: #ffffff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
:deep(.is-active) {
|
||||
background: #67d9e8 !important;
|
||||
background: #67d9e8 !important;
|
||||
}
|
||||
|
||||
/*移动端缩小*/
|
||||
@media (max-width: 650px) {
|
||||
:deep(.number){
|
||||
:deep(.number) {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
:deep(.btn-prev) {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
:deep(.btn-next) {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
:deep(.more) {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
|
||||
@@ -7,16 +7,14 @@
|
||||
<div class="title_mt_right">
|
||||
<h3>{{ data.detail.name }}</h3>
|
||||
<ul class="tags">
|
||||
<li style="margin: 2px 0">{{ `${data.detail.descriptor.classTag}`.replaceAll(",", " | ") }}</li>
|
||||
<li style="margin: 2px 0">{{ data.detail.descriptor.classTag?`${data.detail.descriptor.classTag}`.replaceAll(",", " | "):'未知' }}</li>
|
||||
</ul>
|
||||
<p><span>导演:</span> {{ data.detail.descriptor.director }}</p>
|
||||
<p><span>主演:</span>
|
||||
{{ (data.detail.descriptor.actor && data.detail.descriptor.actor.length > 0) ? `${data.detail.descriptor.actor}`.split(",")[0] + " " + `${data.detail.descriptor.actor}`.split(",")[1] : ''}}
|
||||
</p>
|
||||
<p><span>导演:</span> {{data.detail.descriptor.director }}</p>
|
||||
<p><span>主演:</span> {{handleLongText(data.detail.descriptor.actor)}}</p>
|
||||
<p><span>上映:</span> {{ data.detail.descriptor.releaseDate }}</p>
|
||||
<p><span>地区:</span> {{ data.detail.descriptor.area }}</p>
|
||||
<p v-if="data.detail.descriptor.remark"><span>连载:</span>{{ data.detail.descriptor.remark }}</p>
|
||||
<p><span>评分:</span><b id="score">{{ data.detail.descriptor.dbScore }}</b></p>
|
||||
<p v-if="data.detail.descriptor.remarks"><span>连载:</span>{{ data.detail.descriptor.remarks }}</p>
|
||||
<!--<p><span>评分:</span><b id="score">{{ data.detail.descriptor.dbScore }}</b></p>-->
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt_content">
|
||||
@@ -34,23 +32,23 @@
|
||||
</el-icon>
|
||||
{{ data.detail.descriptor.cName }}
|
||||
</li>
|
||||
<li>{{ `${data.detail.descriptor.classTag}`.replaceAll(",", " ") }}</li>
|
||||
<li v-if="data.detail.descriptor.classTag">{{ `${data.detail.descriptor.classTag}`.replaceAll(",", " ") }}</li>
|
||||
<li>{{ data.detail.descriptor.year }}</li>
|
||||
<li>{{ data.detail.descriptor.area }}</li>
|
||||
</ul>
|
||||
<p><span>导演:</span> {{ data.detail.descriptor.director }}</p>
|
||||
<p><span>主演:</span> {{ data.detail.descriptor.actor }}</p>
|
||||
<p><span>上映:</span> {{ data.detail.descriptor.releaseDate }}</p>
|
||||
<p v-if="data.detail.descriptor.remark"><span>连载:</span>{{ data.detail.descriptor.remark }}</p>
|
||||
<p v-if="data.detail.descriptor.remarks"><span>连载:</span>{{ data.detail.descriptor.remarks }}</p>
|
||||
<p><span>评分:</span><b id="score">{{ data.detail.descriptor.dbScore }}</b></p>
|
||||
<div class="cus_wap">
|
||||
<p style="min-width: 40px"><span>剧情:</span></p>
|
||||
<p ref="textContent" class="text_content">
|
||||
<el-button v-if="`${data.detail.descriptor.content}`.length > 120" class="multi_text"
|
||||
<el-button v-if="`${data.detail.descriptor.content}`.length > 140" class="multi_text"
|
||||
style="color:#a574b7;"
|
||||
@click="showContent(multiBtn.state)" link>{{ multiBtn.text }}
|
||||
</el-button>
|
||||
<span class="cus_info" v-html="`${data.detail.descriptor.content}`.replaceAll(' ', '')"></span>
|
||||
<span class="cus_info" v-html="data.detail.descriptor.content"></span>
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
@@ -62,10 +60,11 @@
|
||||
</el-button>
|
||||
</p>
|
||||
</div>
|
||||
<!--播放列表-->
|
||||
<div class="play_list">
|
||||
<h2 class="hidden-md-and-down">播放列表:(右侧切换播放源)</h2>
|
||||
<el-tabs type="card" class="play_tabs">
|
||||
<el-tab-pane v-for="(p,i) in data.detail.playList" :label="`播放地址${i+1}`" >
|
||||
<el-tab-pane v-for="(p,i) in data.detail.playList" :label="`播放地址${i+1}`">
|
||||
<div class="play_content">
|
||||
<a v-for="(item,index) in p" href="javascript:;"
|
||||
@click="play({source: i, episode: index})">{{ item.episode }}</a>
|
||||
@@ -73,8 +72,9 @@
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<!--相关系列影片-->
|
||||
<div class="correlation">
|
||||
<RelateList :relate-list="data.relate" />
|
||||
<RelateList :relate-list="data.relate"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -91,18 +91,69 @@ import RelateList from "../../components/RelateList.vue";
|
||||
const router = useRouter()
|
||||
|
||||
const data = reactive({
|
||||
detail: {descriptor: {}},
|
||||
detail: {
|
||||
id: '',
|
||||
cid: '',
|
||||
pid: '',
|
||||
name: '',
|
||||
picture: '',
|
||||
playFrom: [],
|
||||
DownFrom: '',
|
||||
playList: [[]],
|
||||
downloadList: '',
|
||||
descriptor: {
|
||||
subTitle: '',
|
||||
cName: '',
|
||||
enName: '',
|
||||
initial: '',
|
||||
classTag: '',
|
||||
actor: '',
|
||||
director: '',
|
||||
writer: '',
|
||||
blurb: '',
|
||||
remarks: '',
|
||||
releaseDate: '',
|
||||
area: '',
|
||||
language: '',
|
||||
year: '',
|
||||
state: '',
|
||||
updateTime: '',
|
||||
addTime: '',
|
||||
dbId: '',
|
||||
dbScore: '',
|
||||
hits: '',
|
||||
content: '',
|
||||
}
|
||||
},
|
||||
relate: [],
|
||||
loading: false,
|
||||
})
|
||||
|
||||
// 对部分信息过长进行处理
|
||||
const handleLongText = (t:string):string=>{
|
||||
let res = ''
|
||||
t.split(',').forEach((s,i)=>{
|
||||
console.log(s)
|
||||
if (i <3) {
|
||||
res += `${s} `
|
||||
}
|
||||
})
|
||||
return res.trimEnd()
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
let link = router.currentRoute.value.query.link
|
||||
ApiGet('/filmDetail', {id: link}).then((resp: any) => {
|
||||
if (resp.status === "ok") {
|
||||
data.detail = resp.data.detail
|
||||
// 去除影视简介中的无用内容和特殊标签格式等
|
||||
data.detail.name = data.detail.name.replace(/(~.*~)/g, '')
|
||||
data.detail.descriptor.content = data.detail.descriptor.content.replace(/(&.*;)|( )|( )|(\n)|(<[^>]+>)/g, '')
|
||||
data.relate = resp.data.relate
|
||||
// 处理过长数据
|
||||
data.detail.descriptor.actor = handleLongText(data.detail.descriptor.actor)
|
||||
console.log(handleLongText(data.detail.descriptor.actor))
|
||||
data.detail.descriptor.director = handleLongText(data.detail.descriptor.director)
|
||||
data.loading = true
|
||||
} else {
|
||||
ElMessage({
|
||||
@@ -148,7 +199,7 @@ const showContent = (flag: boolean) => {
|
||||
|
||||
.picture_mt {
|
||||
width: 100%;
|
||||
height: 175px;
|
||||
height: 180px;
|
||||
margin-right: 12px;
|
||||
border-radius: 8px;
|
||||
background-size: cover;
|
||||
@@ -156,17 +207,19 @@ const showContent = (flag: boolean) => {
|
||||
}
|
||||
|
||||
.title_mt_right {
|
||||
flex: 1.8;
|
||||
flex: 1.5;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.title_mt_right h3 {
|
||||
font-size: 14px;
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
|
||||
.title_mt_right p {
|
||||
font-size: 12px;
|
||||
margin: 3px 2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mt_content {
|
||||
@@ -178,22 +231,27 @@ const showContent = (flag: boolean) => {
|
||||
}
|
||||
|
||||
.mt_content p {
|
||||
max-width: 96%;
|
||||
margin: 0 auto;
|
||||
font-size: 12px;
|
||||
text-align: left;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.play_content a {
|
||||
white-space: nowrap;
|
||||
color: #ffffff;
|
||||
border-radius: 6px;
|
||||
margin: 6px 8px;
|
||||
background: #888888;
|
||||
flex-basis: calc(25% - 16px);
|
||||
min-width: calc(25% - 16px);
|
||||
font-size: 12px;
|
||||
padding: 6px 12px !important;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__item) {
|
||||
width: 70px;
|
||||
height: 35px!important;
|
||||
height: 35px !important;
|
||||
margin: 17px 5px 0 0 !important;
|
||||
font-size: 13px;
|
||||
}
|
||||
@@ -235,8 +293,9 @@ const showContent = (flag: boolean) => {
|
||||
|
||||
@media (min-width: 650px) {
|
||||
.play_content a {
|
||||
white-space: nowrap;
|
||||
font-size: 12px;
|
||||
flex-basis: calc(10% - 24px);
|
||||
min-width: calc(10% - 24px);
|
||||
padding: 6px 15px;
|
||||
color: #ffffff;
|
||||
border-radius: 6px;
|
||||
@@ -371,13 +430,14 @@ const showContent = (flag: boolean) => {
|
||||
|
||||
.title .text_content {
|
||||
max-width: 70%;
|
||||
margin: 20px 3px;
|
||||
line-height: 22.5px;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
vertical-align: top;
|
||||
margin-top: 5px;
|
||||
/*margin-top: 5px;*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +1,32 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
|
||||
<div class="content_item" v-for="item in data.info.content">
|
||||
<template v-if="item.nav.name !='综艺' & item.nav.name !='综艺片'">
|
||||
<el-row class="row-bg cus_nav" justify="space-between">
|
||||
<el-col :span="12" class="title">
|
||||
<span :class="`iconfont ${item.nav.name.search('电影') != -1?'icon-film':item.nav.name.search('剧') != -1?'icon-tv':'icon-cartoon'}`"
|
||||
style="color: #79bbff;font-size: 32px;margin-right: 10px; line-height: 130%" />
|
||||
style="color: #79bbff;font-size: 32px;margin-right: 10px; line-height: 130%"/>
|
||||
<a :href="`/categoryFilm?pid=${item.nav.id}`">{{ item.nav.name }}</a>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<ul class="nav_ul">
|
||||
<li v-for="c in item.nav.children" class="nav_category hidden-md-and-down"><a
|
||||
:href="`/categoryFilm?pid=${c.pid}&cid=${c.id}`">{{ c.name }}</a></li>
|
||||
<li class=" hidden-md-and-down">更多 ></li>
|
||||
<li class="nav_category hidden-md-and-down"><a :href="`/categoryFilm?pid=${item.nav.id}`">更多 ></a></li>
|
||||
</ul>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="cus_content">
|
||||
<el-col :md="24" :lg="20" :xl="20" class="cus_content">
|
||||
<el-row style="max-width: 100%">
|
||||
<template v-for=" (m,i) in item.movies">
|
||||
<el-col :md="4" :sm="6" :xs="8" v-if="i <12" class="cus_content_item">
|
||||
<a :href="`/filmDetail?link=${m.id}`" class="cus_content_link"
|
||||
@error="handleImgError"
|
||||
:style="{backgroundImage: `url('${m.picture}')`}">
|
||||
<span class="cus_tag hidden-md-and-down">{{ m.year }}</span>
|
||||
<span class="cus_tag hidden-md-and-down">{{ m.cName }}</span>
|
||||
<span class="cus_tag hidden-md-and-down">{{ m.area }}</span>
|
||||
</a>
|
||||
<a :href="`/filmDetail?link=${m.id}`"
|
||||
class="content_text content_text_tag">{{ m.name }}</a>
|
||||
<span class="cus_remark">{{ m.remarks }}</span>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
<!--影片列表-->
|
||||
<FilmList :list="item.movies.slice(0,12)"/>
|
||||
</el-col>
|
||||
<el-col :md="0" :lg="4" :xl="4" class="hidden-md-and-down content_right">
|
||||
<template v-for="(m,i) in item.movies">
|
||||
<h3 class="hot_title">🔥热播{{item.nav.name}}</h3>
|
||||
<template v-for="(m,i) in item.hot.slice(0,12)">
|
||||
<div class="content_right_item">
|
||||
<a :href="`/filmDetail?link=${m.id}`"><b class="top_item">{{ i + 1 + '.' }}</b>
|
||||
<a :href="`/filmDetail?link=${m.mid}`"><b class="top_item">{{ i + 1 + '.' }}</b>
|
||||
<span>{{ m.name }}</span></a>
|
||||
</div>
|
||||
</template>
|
||||
@@ -50,9 +38,11 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
// 顶部轮播图
|
||||
import 'element-plus/theme-chalk/display.css'
|
||||
import {onBeforeMount, onMounted, reactive} from "vue";
|
||||
import {onBeforeMount, reactive} from "vue";
|
||||
import {ApiGet} from "../../utils/request";
|
||||
import FilmList from "../../components/FilmList.vue";
|
||||
|
||||
const data = reactive({
|
||||
info: {}
|
||||
@@ -65,8 +55,8 @@ onBeforeMount(() => {
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
.container {
|
||||
margin: 0 auto;
|
||||
}
|
||||
@@ -110,7 +100,8 @@ a {
|
||||
}
|
||||
|
||||
.nav_category > a {
|
||||
color: #999;
|
||||
color: #c9c4c4;
|
||||
|
||||
}
|
||||
|
||||
.nav_category > a:hover {
|
||||
@@ -118,21 +109,17 @@ a {
|
||||
}
|
||||
|
||||
.nav_ul > li {
|
||||
min-width: 60px;
|
||||
/*min-width: 60px;*/
|
||||
white-space: nowrap;
|
||||
line-height: 40px;
|
||||
margin: 0 8px;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/*svg图标*/
|
||||
embed {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
margin-right: 8px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
|
||||
/*影片简介区域*/
|
||||
.cus_content {
|
||||
@@ -140,51 +127,6 @@ embed {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.cus_content_link {
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
/*position: relative;*/
|
||||
padding-top: 125%;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.cus_tag {
|
||||
text-align: center;
|
||||
color: rgb(255, 255, 255);
|
||||
padding: 0 3px;
|
||||
margin: 0 0 10px 8px;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
font-size: 12px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.content_text_tag {
|
||||
font-size: 15px !important;
|
||||
color: rgb(221, 221, 221);
|
||||
padding: 2px 10px 2px 2px !important;
|
||||
}
|
||||
|
||||
.cus_remark {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding-left: 3px;
|
||||
font-size: 12px;
|
||||
color: #999999;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.content_text {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 2px 10px 10px 2px;
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-o-text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.content_right {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -206,7 +148,10 @@ embed {
|
||||
-o-text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.hot_title {
|
||||
text-align: left;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
:deep(.top_item) {
|
||||
color: red;
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
<template>
|
||||
<div class="player_area" v-show="data.loading">
|
||||
<div class="player_p">
|
||||
<video-player @mounted="handleBtn" :src="data.options.src" poster="https://s2.loli.net/2023/04/10/H23F6qDXlnwmPG9.png" controls :loop="false"
|
||||
<!--preload-->
|
||||
|
||||
<video-player @mounted="handleBtn" :src="data.options.src" :poster="posterImg" controls
|
||||
:loop="false"
|
||||
@keydown="handlePlay"
|
||||
:bufferedPercent="30"
|
||||
:volume="data.options.volume"
|
||||
crossorigin="anonymous" playsinline class="video-player"
|
||||
:playback-rates="[0.5, 1.0, 1.5, 2.0]" />
|
||||
:playback-rates="[0.5, 1.0, 1.5, 2.0]"/>
|
||||
</div>
|
||||
<div class="current_play_info">
|
||||
<div class="play_info_left">
|
||||
<h3 class="current_play_title">{{ `${data.detail.name} ${data.current.episode}` }}</h3>
|
||||
<h3 class="current_play_title"><a :href="`/filmDetail?link=${data.detail.id}`">{{data.detail.name}}</a>{{data.current.episode}}</h3>
|
||||
<div class="tags">
|
||||
<b>
|
||||
<el-icon>
|
||||
<Promotion/>
|
||||
</el-icon>
|
||||
{{ data.detail.descriptor.cName }}</b>
|
||||
<span>{{ data.detail.descriptor.classTag }}</span>
|
||||
<span>{{ data.detail.descriptor.classTag?data.detail.descriptor.classTag:'未知' }}</span>
|
||||
<span>{{ data.detail.descriptor.year }}</span>
|
||||
<span>{{ data.detail.descriptor.area }}</span>
|
||||
</div>
|
||||
@@ -40,7 +44,7 @@
|
||||
</el-tabs>
|
||||
</div>
|
||||
<div class="correlation">
|
||||
<RelateList :relate-list="data.relate"/>
|
||||
<RelateList :relateList="data.relate"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -54,25 +58,27 @@ import {ApiGet} from "../../utils/request";
|
||||
import {ElMessage} from "element-plus";
|
||||
import RelateList from "../../components/RelateList.vue";
|
||||
import {Promotion} from "@element-plus/icons-vue";
|
||||
import posterImg from '../../assets/image/play.png'
|
||||
// 引入视频播放器组件
|
||||
import {VideoPlayer} from '@videojs-player/vue'
|
||||
import 'video.js/dist/video-js.css'
|
||||
const handlePlay = (e:any)=>{
|
||||
|
||||
const handlePlay = (e: any) => {
|
||||
e.preventDefault()
|
||||
switch (e.keyCode) {
|
||||
case 32:
|
||||
console.log(e.target.paused)
|
||||
if(e.target.paused) {
|
||||
if (e.target.paused) {
|
||||
e.target.play()
|
||||
} else {
|
||||
e.target.pause()
|
||||
}
|
||||
break
|
||||
case 37:
|
||||
e.target.currentTime = e.target.currentTime-5 < 0 ? 0 : e.target.currentTime-5
|
||||
e.target.currentTime = e.target.currentTime - 5 < 0 ? 0 : e.target.currentTime - 5
|
||||
break
|
||||
case 39:
|
||||
e.target.currentTime = e.target.currentTime+5 > e.target.duration ? e.target.duration : e.target.currentTime+5
|
||||
e.target.currentTime = e.target.currentTime + 5 > e.target.duration ? e.target.duration : e.target.currentTime + 5
|
||||
break
|
||||
case 38:
|
||||
data.options.volume = data.options.volume + 0.05 > 1 ? 1 : data.options.volume + 0.05
|
||||
@@ -84,7 +90,7 @@ const handlePlay = (e:any)=>{
|
||||
|
||||
}
|
||||
// 主动触发快捷键
|
||||
const tiggerKeyMap = (keyCode:number)=>{
|
||||
const tiggerKeyMap = (keyCode: number) => {
|
||||
let player = document.getElementsByTagName("video")[0]
|
||||
player.focus()
|
||||
const event = document.createEvent('HTMLEvents');
|
||||
@@ -92,10 +98,10 @@ const tiggerKeyMap = (keyCode:number)=>{
|
||||
event.keyCode = keyCode; // 设置键码
|
||||
player.dispatchEvent(event)
|
||||
}
|
||||
const handleBtn = (e:any)=> {
|
||||
const handleBtn = (e: any) => {
|
||||
let btns = document.getElementsByClassName('vjs-button')
|
||||
for (let el of btns) {
|
||||
el.addEventListener('keydown', function (t:any){
|
||||
el.addEventListener('keydown', function (t: any) {
|
||||
t.preventDefault()
|
||||
tiggerKeyMap(t.keyCode)
|
||||
})
|
||||
@@ -107,12 +113,45 @@ const handleBtn = (e:any)=> {
|
||||
// 播放页所需数据
|
||||
const data = reactive({
|
||||
loading: false,
|
||||
detail: {descriptor: {}, playList: [[{episode: '', link: ''}]]},
|
||||
detail: {
|
||||
id: '',
|
||||
cid: '',
|
||||
pid: '',
|
||||
name: '',
|
||||
picture: '',
|
||||
playFrom: [],
|
||||
DownFrom: '',
|
||||
playList: [[]],
|
||||
downloadList: '',
|
||||
descriptor: {
|
||||
subTitle: '',
|
||||
cName: '',
|
||||
enName: '',
|
||||
initial: '',
|
||||
classTag: '',
|
||||
actor: '',
|
||||
director: '',
|
||||
writer: '',
|
||||
blurb: '',
|
||||
remarks: '',
|
||||
releaseDate: '',
|
||||
area: '',
|
||||
language: '',
|
||||
year: '',
|
||||
state: '',
|
||||
updateTime: '',
|
||||
addTime: '',
|
||||
dbId: '',
|
||||
dbScore: '',
|
||||
hits: '',
|
||||
content: '',
|
||||
}
|
||||
},
|
||||
current: {index: 0, episode: '', link: ''},
|
||||
currentTabName: '',
|
||||
currentPlayFrom: 0,
|
||||
currentEpisode: 0,
|
||||
relate: [{}],
|
||||
relate: [],
|
||||
// @videojs-player 播放属性设置
|
||||
options: {
|
||||
title: "", //视频名称
|
||||
@@ -167,33 +206,48 @@ const playChange = (play: { sourceIndex: number, episodeIndex: number, target: a
|
||||
|
||||
}
|
||||
|
||||
:deep(.vjs-big-play-button){
|
||||
:deep(.vjs-big-play-button) {
|
||||
line-height: 2em;
|
||||
height: 2em;
|
||||
width: 2em;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
background: rgba(0,0,0,0.65);
|
||||
background: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
:deep(.vjs-control-bar){
|
||||
background: rgba(0,0,0,0.32);
|
||||
|
||||
:deep(.vjs-control-bar) {
|
||||
background: rgba(0, 0, 0, 0.32);
|
||||
}
|
||||
|
||||
/*取消video被选中的白边*/
|
||||
:deep(video:focus) {
|
||||
border: none!important;
|
||||
border: none !important;
|
||||
outline: none;
|
||||
}
|
||||
:deep(.data-vjs-player:focus){
|
||||
border: none!important;
|
||||
|
||||
:deep(.data-vjs-player:focus) {
|
||||
border: none !important;
|
||||
outline: none;
|
||||
}
|
||||
:deep(.vjs-tech){
|
||||
|
||||
:deep(.vjs-tech) {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
:deep(img) {
|
||||
border-radius: 6px;
|
||||
}
|
||||
/*进度条配色*/
|
||||
:deep(.video-js .vjs-load-progress div) {
|
||||
background: rgba(255,255,255,0.55)!important;
|
||||
}
|
||||
:deep(.video-js .vjs-play-progress){
|
||||
background: #44c8cf;
|
||||
}
|
||||
:deep(.video-js .vjs-slider){
|
||||
background-color: hsla(0,0%,100%,.2);
|
||||
}
|
||||
|
||||
|
||||
/*当前播放的影片信息展示*/
|
||||
.current_play_info {
|
||||
@@ -203,9 +257,17 @@ const playChange = (play: { sourceIndex: number, episodeIndex: number, target: a
|
||||
}
|
||||
|
||||
.current_play_title {
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
color: rgb(201, 196, 196);
|
||||
margin: 0 0 5px 0;
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
.current_play_title a {
|
||||
color: rgb(201, 196, 196);
|
||||
font-weight: 600;
|
||||
margin-right: 16px;
|
||||
}
|
||||
.current_play_title a:hover {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
|
||||
@@ -239,8 +301,9 @@ const playChange = (play: { sourceIndex: number, episodeIndex: number, target: a
|
||||
}
|
||||
|
||||
.play_content a {
|
||||
white-space: nowrap;
|
||||
font-size: 12px;
|
||||
flex-basis: calc(10% - 24px);
|
||||
min-width: calc(10% - 24px);
|
||||
padding: 6px 10px;
|
||||
color: #ffffff;
|
||||
border-radius: 6px;
|
||||
@@ -360,11 +423,12 @@ const playChange = (play: { sourceIndex: number, episodeIndex: number, target: a
|
||||
|
||||
|
||||
.play_content a {
|
||||
white-space: nowrap;
|
||||
color: #ffffff;
|
||||
border-radius: 6px;
|
||||
margin: 6px 8px;
|
||||
background: #888888;
|
||||
flex-basis: calc(25% - 16px);
|
||||
min-width: calc(25% - 16px);
|
||||
font-size: 12px;
|
||||
padding: 6px 12px !important;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="search_group">
|
||||
<input v-model="data.search" @keydown="e=>{e.keyCode==13 && searchMovie()}" placeholder="搜索 动漫,剧集,电影 " class="search"/>
|
||||
<input v-model="data.search" @keydown="e=>{e.keyCode==13 && searchMovie()}" placeholder="输入关键字搜索 动漫,剧集,电影 " class="search"/>
|
||||
<el-button @click="searchMovie" :icon="Search" style="" />
|
||||
</div>
|
||||
<div v-if="data.list.length > 0 " class="search_res">
|
||||
<div v-if="data.list && data.list.length > 0 " class="search_res">
|
||||
<div class="title">
|
||||
<h2>{{ data.oldSearch }}</h2>
|
||||
<p>共找到{{ data.page.total }}部与"{{ data.oldSearch }}"相关的影视作品</p>
|
||||
@@ -41,7 +41,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-if="data.list.length == 0 " description="输入影片名称进行搜索"/>
|
||||
<el-empty v-if="data.oldSearch != '' && (!data.list || data.list.length == 0) " description="未查询到对应影片"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
@@ -7,30 +7,31 @@ import {ElementPlusResolver} from "unplugin-vue-components/resolvers";
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
// 本地测试环境
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 3600,
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: `http://127.0.0.1:3601`,
|
||||
changeOrigin: true, // 允许跨域
|
||||
rewrite: path => path.replace(/^\/api/,'')
|
||||
}
|
||||
},
|
||||
},
|
||||
// nginx发布构建时使用此配置
|
||||
// server: {
|
||||
// host: 'localhost',
|
||||
// host: '0.0.0.0',
|
||||
// port: 3600,
|
||||
// proxy: {
|
||||
// "/api": {
|
||||
// target: `http://localhost`,
|
||||
// target: `http://127.0.0.1:3601`,
|
||||
// changeOrigin: true, // 允许跨域
|
||||
// rewrite: path => path.replace(/^\/api/,'')
|
||||
// }
|
||||
// },
|
||||
// },
|
||||
|
||||
// nginx发布构建时使用此配置
|
||||
server: {
|
||||
host: 'localhost',
|
||||
port: 3600,
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: `http://localhost`,
|
||||
changeOrigin: true, // 允许跨域
|
||||
rewrite: path => path.replace(/^\/api/,'')
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({
|
||||
|
||||
BIN
film/data/nginx/html/assets/404-b813c94a.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
1
film/data/nginx/html/assets/index-df719233.css
Normal file
BIN
film/data/nginx/html/assets/play-bb9c8990.png
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
@@ -11,8 +11,8 @@
|
||||
<meta charset="UTF-8"/>
|
||||
<title>(╥﹏╥)</title>
|
||||
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_3992367_aj8j1lxiqyw.css">
|
||||
<script type="module" crossorigin src="/assets/index-4e26142c.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-bf20f8e3.css">
|
||||
<script type="module" crossorigin src="/assets/index-abd35057.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-df719233.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -46,29 +46,19 @@ const (
|
||||
SpiderCipher = "Life in a different world from zero"
|
||||
)
|
||||
|
||||
/*API相关redis key*/
|
||||
const (
|
||||
IndexCacheKey = "IndexCache"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
// SearchTableName 存放检索信息的数据表名
|
||||
SearchTableName = "search"
|
||||
|
||||
//mysql服务配置信息 root:root 设置mysql账户的用户名和密码
|
||||
|
||||
// MysqlDsn = "root:root@(192.168.20.10:3307)/FilmSite?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
|
||||
// MysqlDsn docker compose 环境下的链接信息 mysql:3306 为 docker compose 中 mysql服务对应的网络名称和端口
|
||||
// Mysql连接信息
|
||||
MysqlDsn = "root:root@(mysql:3306)/FilmSite?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
|
||||
/*
|
||||
redis 配置信息
|
||||
RedisAddr host:port
|
||||
RedisPassword redis访问密码
|
||||
RedisDBNo 使用第几号库
|
||||
*/
|
||||
// RedisAddr = `192.168.20.10:6379`
|
||||
// RedisPassword = `root`
|
||||
// RedisDBNo = 1
|
||||
|
||||
// RedisAddr docker compose 环境下运行使用如下配置信息
|
||||
// Redis连接信息
|
||||
RedisAddr = `redis:6379`
|
||||
RedisPassword = `root`
|
||||
RedisDBNo = 0
|
||||
|
||||
@@ -22,11 +22,17 @@ type IndexLogic struct {
|
||||
var IL *IndexLogic
|
||||
|
||||
// IndexPage 首页数据处理
|
||||
func (i *IndexLogic) IndexPage() gin.H {
|
||||
Info := gin.H{}
|
||||
// 首页分类数据处理
|
||||
|
||||
// 1. 导航分类数据处理, 只提供 电影 电视剧 综艺 动漫 四大顶级分类和其子分类
|
||||
func (i *IndexLogic) IndexPage() map[string]interface{} {
|
||||
//声明返回值
|
||||
//Info := make(map[string]interface{})
|
||||
// 首页请求时长较高, 采用redis进行缓存, 在定时任务更新影片时清除对应缓存
|
||||
// 判断是否存在缓存数据, 存在则直接将数据返回
|
||||
Info := model.GetCacheData(config.IndexCacheKey)
|
||||
if Info != nil {
|
||||
return Info
|
||||
}
|
||||
Info = make(map[string]interface{})
|
||||
// 1. 首页分类数据处理 导航分类数据处理, 只提供 电影 电视剧 综艺 动漫 四大顶级分类和其子分类
|
||||
tree := model.CategoryTree{Category: &model.Category{Id: 0, Name: "分类信息"}}
|
||||
sysTree := model.GetCategoryTree()
|
||||
// 由于采集源数据格式不一,因此采用名称匹配
|
||||
@@ -38,15 +44,18 @@ func (i *IndexLogic) IndexPage() gin.H {
|
||||
}
|
||||
Info["category"] = tree
|
||||
// 2. 提供用于首页展示的顶级分类影片信息, 每分类 14条数据
|
||||
var list []gin.H
|
||||
var list []map[string]interface{}
|
||||
for _, c := range tree.Children {
|
||||
page := model.Page{PageSize: 14, Current: 1}
|
||||
movies := model.GetMovieListByPid(c.Id, &page)
|
||||
item := gin.H{"nav": c, "movies": movies}
|
||||
// 获取当前分类的本月热门影片
|
||||
HotMovies := model.GetHotMovieByPid(c.Id, &page)
|
||||
item := map[string]interface{}{"nav": c, "movies": movies, "hot": HotMovies}
|
||||
list = append(list, item)
|
||||
}
|
||||
Info["content"] = list
|
||||
|
||||
// 不存在首页数据缓存时将查询数据缓存到redis中
|
||||
model.DataCache(config.IndexCacheKey, Info)
|
||||
return Info
|
||||
}
|
||||
|
||||
@@ -141,23 +150,33 @@ func (i *IndexLogic) RelateMovie(detail model.MovieDetail, page *model.Page) []m
|
||||
return model.GetRelateMovieBasicInfo(search, page)
|
||||
}
|
||||
|
||||
// 将多个站点的对应影视播放源追加到主站点播放列表中
|
||||
/*
|
||||
将多个站点的对应影视播放源追加到主站点播放列表中
|
||||
1. 将主站点影片的name 和 subtitle 进行处理添加到用于匹配对应播放源的map中
|
||||
2. 仅对主站点影片name进行映射关系处理并将结果添加到map中
|
||||
例如: xxx第一季 xxx
|
||||
*/
|
||||
func multipleSource(detail *model.MovieDetail) {
|
||||
// 整合多播放源, 处理部分站点中影片名称的空格
|
||||
names := map[string]int{model.HashKey(detail.Name): 0}
|
||||
// 不同站点影片别名匹配
|
||||
re := regexp.MustCompile(`第一季$`)
|
||||
alias := strings.TrimSpace(re.ReplaceAllString(detail.Name, ""))
|
||||
names[model.HashKey(alias)] = 0
|
||||
// 将多个影片别名进行切分,放入names中
|
||||
// 整合多播放源, 初始化存储key map
|
||||
names := make(map[string]int)
|
||||
// 1. 判断detail的dbId是否存在, 存在则添加到names中作为匹配条件
|
||||
if detail.DbId > 0 {
|
||||
names[model.GenerateHashKey(detail.DbId)] = 0
|
||||
}
|
||||
// 2. 对name进行去除特殊格式处理
|
||||
names[model.GenerateHashKey(detail.Name)] = 0
|
||||
// 3. 对包含第一季的name进行处理
|
||||
names[model.GenerateHashKey(regexp.MustCompile(`第一季$`).ReplaceAllString(detail.Name, ""))] = 0
|
||||
|
||||
// 4. 将subtitle进行切分,放入names中
|
||||
if len(detail.SubTitle) > 0 && strings.Contains(detail.SubTitle, ",") {
|
||||
for _, v := range strings.Split(detail.SubTitle, ",") {
|
||||
names[model.HashKey(v)] = 0
|
||||
names[model.GenerateHashKey(v)] = 0
|
||||
}
|
||||
}
|
||||
if len(detail.SubTitle) > 0 && strings.Contains(detail.SubTitle, "/") {
|
||||
for _, v := range strings.Split(detail.SubTitle, "/") {
|
||||
names[model.HashKey(v)] = 0
|
||||
names[model.GenerateHashKey(v)] = 0
|
||||
}
|
||||
}
|
||||
// 遍历站点列表
|
||||
|
||||
@@ -5,12 +5,11 @@ import (
|
||||
"server/plugin/db"
|
||||
"server/plugin/spider"
|
||||
"server/router"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 执行初始化前等待20s , 让mysql服务完成初始化指令
|
||||
time.Sleep(time.Second * 20)
|
||||
//time.Sleep(time.Second * 20)
|
||||
//初始化redis客户端
|
||||
err := db.InitRedisConn()
|
||||
if err != nil {
|
||||
@@ -24,6 +23,7 @@ func init() {
|
||||
}
|
||||
func main() {
|
||||
start()
|
||||
//spider.MtSiteSpider()
|
||||
}
|
||||
|
||||
func start() {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"hash/fnv"
|
||||
"regexp"
|
||||
"server/config"
|
||||
"server/plugin/db"
|
||||
"strconv"
|
||||
@@ -45,6 +46,7 @@ type MovieDescriptor struct {
|
||||
AddTime int64 `json:"addTime"` //资源添加时间戳
|
||||
DbId int64 `json:"dbId"` //豆瓣id
|
||||
DbScore string `json:"dbScore"` // 豆瓣评分
|
||||
Hits int64 `json:"hits"` //影片热度
|
||||
Content string `json:"content"` //内容简介
|
||||
}
|
||||
|
||||
@@ -168,11 +170,22 @@ func SaveSitePlayList(siteName string, list []MovieDetail) (err error) {
|
||||
for _, d := range list {
|
||||
if len(d.PlayList) > 0 {
|
||||
data, _ := json.Marshal(d.PlayList[0])
|
||||
res[HashKey(d.Name)] = string(data)
|
||||
// 不保存电影解说类
|
||||
if strings.Contains(d.CName, "解说") {
|
||||
continue
|
||||
}
|
||||
// 如果DbId不为0, 则以dbID作为key进行hash额外存储一次
|
||||
if d.DbId > 0 {
|
||||
res[GenerateHashKey(d.DbId)] = string(data)
|
||||
}
|
||||
res[GenerateHashKey(d.Name)] = string(data)
|
||||
}
|
||||
}
|
||||
// 保存形式 key: MultipleSource:siteName Hash[hash(movieName)]list
|
||||
err = db.Rdb.HMSet(db.Cxt, fmt.Sprintf(config.MultipleSiteDetail, siteName), res).Err()
|
||||
// 如果结果不为空,则将数据保存到redis中
|
||||
if len(res) > 0 {
|
||||
// 保存形式 key: MultipleSource:siteName Hash[hash(movieName)]list
|
||||
err = db.Rdb.HMSet(db.Cxt, fmt.Sprintf(config.MultipleSiteDetail, siteName), res).Err()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -181,9 +194,9 @@ func AddSearchInfo(searchInfo SearchInfo) (err error) {
|
||||
// 片名 Name 分类 CName 类别标签 classTag 地区 Area 语言 Language 年份 Year 首字母 Initial, 排序
|
||||
data, _ := json.Marshal(searchInfo)
|
||||
// 时间排序 score -->时间戳 DbId 排序 --> 热度, 评分排序 DbScore
|
||||
err = db.Rdb.ZAdd(db.Cxt, fmt.Sprintf("%s:Pid%d", config.SearchTimeListKey, searchInfo.Pid), redis.Z{Score: float64(searchInfo.Time), Member: data}).Err()
|
||||
err = db.Rdb.ZAdd(db.Cxt, fmt.Sprintf("%s:Pid%d", config.SearchTimeListKey, searchInfo.Pid), redis.Z{Score: float64(searchInfo.UpdateStamp), Member: data}).Err()
|
||||
err = db.Rdb.ZAdd(db.Cxt, fmt.Sprintf("%s:Pid%d", config.SearchScoreListKey, searchInfo.Pid), redis.Z{Score: searchInfo.Score, Member: data}).Err()
|
||||
err = db.Rdb.ZAdd(db.Cxt, fmt.Sprintf("%s:Pid%d", config.SearchHeatListKey, searchInfo.Pid), redis.Z{Score: float64(searchInfo.Rank), Member: data}).Err()
|
||||
err = db.Rdb.ZAdd(db.Cxt, fmt.Sprintf("%s:Pid%d", config.SearchHeatListKey, searchInfo.Pid), redis.Z{Score: float64(searchInfo.Hits), Member: data}).Err()
|
||||
// 添加搜索关键字信息
|
||||
SearchKeyword(searchInfo)
|
||||
return
|
||||
@@ -298,27 +311,28 @@ func BatchSaveSearchInfo(list []MovieDetail) {
|
||||
func ConvertSearchInfo(detail MovieDetail) SearchInfo {
|
||||
score, _ := strconv.ParseFloat(detail.DbScore, 64)
|
||||
stamp, _ := time.ParseInLocation(time.DateTime, detail.UpdateTime, time.Local)
|
||||
year, err := strconv.ParseInt(detail.Year, 10, 64)
|
||||
// detail中的年份信息并不准确, 因此采用 ReleaseDate中的年份
|
||||
year, err := strconv.ParseInt(regexp.MustCompile(`[1-9][0-9]{3}`).FindString(detail.ReleaseDate), 10, 64)
|
||||
if err != nil {
|
||||
year = 0
|
||||
}
|
||||
return SearchInfo{
|
||||
Mid: detail.Id,
|
||||
Cid: detail.Cid,
|
||||
Pid: detail.Pid,
|
||||
Name: detail.Name,
|
||||
SubTitle: detail.SubTitle,
|
||||
CName: detail.CName,
|
||||
ClassTag: detail.ClassTag,
|
||||
Area: detail.Area,
|
||||
Language: detail.Language,
|
||||
Year: year,
|
||||
Initial: detail.Initial,
|
||||
Score: score,
|
||||
Rank: detail.DbId,
|
||||
Time: stamp.Unix(),
|
||||
State: detail.State,
|
||||
Remarks: detail.Remarks,
|
||||
Mid: detail.Id,
|
||||
Cid: detail.Cid,
|
||||
Pid: detail.Pid,
|
||||
Name: detail.Name,
|
||||
SubTitle: detail.SubTitle,
|
||||
CName: detail.CName,
|
||||
ClassTag: detail.ClassTag,
|
||||
Area: detail.Area,
|
||||
Language: detail.Language,
|
||||
Year: year,
|
||||
Initial: detail.Initial,
|
||||
Score: score,
|
||||
Hits: detail.Hits,
|
||||
UpdateStamp: stamp.Unix(),
|
||||
State: detail.State,
|
||||
Remarks: detail.Remarks,
|
||||
// releaseDate 部分影片缺失该参数, 所以使用添加时间作为上映时间排序
|
||||
ReleaseDate: detail.AddTime,
|
||||
}
|
||||
@@ -342,10 +356,28 @@ func GetDetailByKey(key string) MovieDetail {
|
||||
return detail
|
||||
}
|
||||
|
||||
// HashKey 将字符串转化为hash值
|
||||
func HashKey(str string) string {
|
||||
/*
|
||||
对附属播放源入库时的name|dbID进行处理,保证唯一性
|
||||
1. 去除name中的所有空格
|
||||
2. 去除name中含有的别名~.*~
|
||||
3. 去除name首尾的标点符号
|
||||
4. 将处理完成后的name转化为hash值作为存储时的key
|
||||
*/
|
||||
// GenerateHashKey 存储播放源信息时对影片名称进行处理, 提高各站点间同一影片的匹配度
|
||||
func GenerateHashKey[K string | ~int | int64](key K) string {
|
||||
mName := fmt.Sprint(key)
|
||||
//1. 去除name中的所有空格
|
||||
mName = regexp.MustCompile(`\s`).ReplaceAllString(mName, "")
|
||||
//2. 去除name中含有的别名~.*~
|
||||
mName = regexp.MustCompile(`~.*~$`).ReplaceAllString(mName, "")
|
||||
//3. 去除name首尾的标点符号
|
||||
mName = regexp.MustCompile(`^[[:punct:]]+|[[:punct:]]+$`).ReplaceAllString(mName, "")
|
||||
// 部分站点包含 动画版, 特殊别名 等字符, 需进行删除
|
||||
//mName = regexp.MustCompile(`动画版`).ReplaceAllString(mName, "")
|
||||
mName = regexp.MustCompile(`季.*`).ReplaceAllString(mName, "季")
|
||||
//4. 将处理完成后的name转化为hash值作为存储时的key
|
||||
h := fnv.New32a()
|
||||
_, err := h.Write([]byte(str))
|
||||
_, err := h.Write([]byte(mName))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ type MovieDetailInfo struct {
|
||||
AddTime int64 `json:"vod_time_add"` //资源添加时间戳
|
||||
DbId int64 `json:"vod_douban_id"` //豆瓣id
|
||||
DbScore string `json:"vod_douban_score"` // 豆瓣评分
|
||||
Hits int64 `json:"vod_hits"` // 总热度
|
||||
Content string `json:"vod_content"` //内容简介
|
||||
PlayFrom string `json:"vod_play_from"` // 播放来源
|
||||
PlaySeparator string `json:"vod_play_note"` // 播放信息分隔符
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"server/config"
|
||||
"server/plugin/db"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SearchInfo 存储用于检索的信息
|
||||
@@ -28,8 +29,8 @@ type SearchInfo struct {
|
||||
Year int64 `json:"year"` // 年份
|
||||
Initial string `json:"initial"` // 首字母
|
||||
Score float64 `json:"score"` //评分
|
||||
Time int64 `json:"time"` // 更新时间
|
||||
Rank int64 `json:"rank"` // 热度排行id
|
||||
UpdateStamp int64 `json:"updateStamp"` // 更新时间
|
||||
Hits int64 `json:"hits"` // 热度排行
|
||||
State string `json:"state"` //状态 正片|预告
|
||||
Remarks string `json:"remarks"` // 完结 | 更新至x集
|
||||
ReleaseDate int64 `json:"releaseDate"` //上映时间 时间戳
|
||||
@@ -144,7 +145,7 @@ func BatchSaveOrUpdate(list []SearchInfo) {
|
||||
// 如果存在对应数据则进行更新, 否则进行删除
|
||||
if count > 0 {
|
||||
// 记录已经存在则执行更新部分内容
|
||||
err := tx.Model(&SearchInfo{}).Where("mid", info.Mid).Updates(SearchInfo{Time: info.Time, Rank: info.Rank, State: info.State,
|
||||
err := tx.Model(&SearchInfo{}).Where("mid", info.Mid).Updates(SearchInfo{UpdateStamp: info.UpdateStamp, Hits: info.Hits, State: info.State,
|
||||
Remarks: info.Remarks, Score: info.Score, ReleaseDate: info.ReleaseDate}).Error
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
@@ -198,7 +199,7 @@ func GetMovieListByPid(pid int64, page *Page) []MovieBasicInfo {
|
||||
page.PageCount = int((page.Total + page.PageSize - 1) / page.PageSize)
|
||||
// 进行具体的信息查询
|
||||
var s []SearchInfo
|
||||
if err := db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).Where("pid", pid).Order("year DESC, time DESC").Find(&s).Error; err != nil {
|
||||
if err := db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).Where("pid", pid).Order("year DESC, update_stamp DESC").Find(&s).Error; err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
@@ -211,6 +212,24 @@ func GetMovieListByPid(pid int64, page *Page) []MovieBasicInfo {
|
||||
return list
|
||||
}
|
||||
|
||||
// GetHotMovieByPid 获取指定类别的热门影片
|
||||
func GetHotMovieByPid(pid int64, page *Page) []SearchInfo {
|
||||
// 返回分页参数
|
||||
var count int64
|
||||
db.Mdb.Model(&SearchInfo{}).Where("pid", pid).Count(&count)
|
||||
page.Total = int(count)
|
||||
page.PageCount = int((page.Total + page.PageSize - 1) / page.PageSize)
|
||||
// 进行具体的信息查询
|
||||
var s []SearchInfo
|
||||
// 当前时间偏移一个月
|
||||
t := time.Now().AddDate(0, -1, 0).Unix()
|
||||
if err := db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).Where("pid=? AND update_stamp > ?", pid, t).Order(" year DESC, hits DESC").Find(&s).Error; err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// SearchFilmKeyword 通过关键字搜索库存中满足条件的影片名
|
||||
func SearchFilmKeyword(keyword string, page *Page) []SearchInfo {
|
||||
var searchList []SearchInfo
|
||||
@@ -221,7 +240,7 @@ func SearchFilmKeyword(keyword string, page *Page) []SearchInfo {
|
||||
page.PageCount = int((page.Total + page.PageSize - 1) / page.PageSize)
|
||||
// 2. 获取满足条件的数据
|
||||
db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).
|
||||
Where("name LIKE ?", fmt.Sprint(`%`, keyword, `%`)).Or("sub_title LIKE ?", fmt.Sprint(`%`, keyword, `%`)).Order("year DESC, time DESC").Find(&searchList)
|
||||
Where("name LIKE ?", fmt.Sprint(`%`, keyword, `%`)).Or("sub_title LIKE ?", fmt.Sprint(`%`, keyword, `%`)).Order("year DESC, update_stamp DESC").Find(&searchList)
|
||||
return searchList
|
||||
}
|
||||
|
||||
@@ -234,7 +253,7 @@ func GetMovieListByCid(cid int64, page *Page) []MovieBasicInfo {
|
||||
page.PageCount = int((page.Total + page.PageSize - 1) / page.PageSize)
|
||||
// 进行具体的信息查询
|
||||
var s []SearchInfo
|
||||
if err := db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).Where("cid", cid).Order("year DESC, time DESC").Find(&s).Error; err != nil {
|
||||
if err := db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).Where("cid", cid).Order("year DESC, update_stamp DESC").Find(&s).Error; err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
@@ -302,3 +321,25 @@ func GetMultiplePlay(siteName, key string) []MovieUrlInfo {
|
||||
_ = json.Unmarshal([]byte(data), &playList)
|
||||
return playList
|
||||
}
|
||||
|
||||
// DataCache API请求 数据缓存
|
||||
func DataCache(key string, data map[string]interface{}) {
|
||||
val, _ := json.Marshal(data)
|
||||
db.Rdb.Set(db.Cxt, key, val, config.CategoryTreeExpired)
|
||||
}
|
||||
|
||||
// GetCacheData 获取API接口的缓存数据
|
||||
func GetCacheData(key string) map[string]interface{} {
|
||||
data := make(map[string]interface{})
|
||||
val, err := db.Rdb.Get(db.Cxt, key).Result()
|
||||
if err != nil || len(val) <= 0 {
|
||||
return nil
|
||||
}
|
||||
_ = json.Unmarshal([]byte(val), &data)
|
||||
return data
|
||||
}
|
||||
|
||||
// RemoveCache 删除数据缓存
|
||||
func RemoveCache(key string) {
|
||||
db.Rdb.Del(db.Cxt, key)
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ func ProcessMovieDetail(detail model.MovieDetailInfo) model.MovieDetail {
|
||||
AddTime: detail.AddTime,
|
||||
DbId: detail.DbId,
|
||||
DbScore: detail.DbScore,
|
||||
Hits: detail.Hits,
|
||||
Content: detail.Content,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ type Site struct {
|
||||
var SiteList = []Site{
|
||||
//{"tk", "https://api.tiankongapi.com/api.php/provide/vod"},
|
||||
//{"yh", "https://m3u8.apiyhzy.com/api.php/provide/vod/"},
|
||||
//{"zk", "https://api.1080zyku.com/inc/apijson.php"}, 数据格式不规范,不采用
|
||||
//{"fs", "https://www.feisuzyapi.com/api.php/provide/vod/"},
|
||||
|
||||
{"lz", "https://cj.lziapi.com/api.php/provide/vod/"},
|
||||
@@ -251,7 +252,7 @@ func UpdateMainDetail() {
|
||||
func UpdatePlayDetail() {
|
||||
for _, s := range SiteList {
|
||||
// 获取单个站点的分页数
|
||||
r := RequestInfo{Uri: s.Name, Params: url.Values{}}
|
||||
r := RequestInfo{Uri: s.Uri, Params: url.Values{}}
|
||||
r.Params.Set("h", config.UpdateInterval)
|
||||
pageCount, err := GetPageCount(r)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/robfig/cron/v3"
|
||||
"log"
|
||||
"server/config"
|
||||
"server/model"
|
||||
)
|
||||
|
||||
// RegularUpdateMovie 定时更新, 每半小时获取一次站点的最近x小时数据
|
||||
@@ -14,6 +15,8 @@ func RegularUpdateMovie() {
|
||||
// 执行更新最近x小时影片的Spider
|
||||
log.Println("执行一次影片更新任务...")
|
||||
UpdateMovieDetail()
|
||||
// 执行更新任务后清理redis中的相关API接口数据缓存
|
||||
clearCache()
|
||||
})
|
||||
|
||||
// 开启定时任务每月最后一天凌晨两点, 执行一次清库重取数据
|
||||
@@ -27,3 +30,8 @@ func RegularUpdateMovie() {
|
||||
|
||||
c.Start()
|
||||
}
|
||||
|
||||
// 清理API接口数据缓存
|
||||
func clearCache() {
|
||||
model.RemoveCache(config.IndexCacheKey)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -23,6 +24,9 @@ type RequestInfo struct {
|
||||
Resp []byte `json:"resp"` // 响应结果数据
|
||||
}
|
||||
|
||||
// RefererUrl 记录上次请求的url
|
||||
var RefererUrl string
|
||||
|
||||
// CreateClient 初始化请求客户端
|
||||
func CreateClient() *colly.Collector {
|
||||
c := colly.NewCollector()
|
||||
@@ -43,6 +47,11 @@ func CreateClient() *colly.Collector {
|
||||
// 设置一些请求头信息
|
||||
request.Headers.Set("Content-Type", "application/json;charset=UTF-8")
|
||||
//request.Headers.Set("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
|
||||
// 请求完成后设置请求头Referer
|
||||
if len(RefererUrl) <= 0 || !strings.Contains(RefererUrl, request.URL.Host) {
|
||||
RefererUrl = ""
|
||||
}
|
||||
request.Headers.Set("Referer", RefererUrl)
|
||||
})
|
||||
// 请求期间报错的回调
|
||||
c.OnError(func(response *colly.Response, err error) {
|
||||
@@ -69,6 +78,8 @@ func ApiGet(r *RequestInfo) {
|
||||
} else {
|
||||
r.Resp = []byte{}
|
||||
}
|
||||
// 将请求url保存到RefererUrl 用于 Header Refer属性
|
||||
RefererUrl = response.Request.URL.String()
|
||||
// 拿到response后输出请求url
|
||||
//log.Println("\n请求成功: ", response.Request.URL)
|
||||
})
|
||||
|
||||
@@ -46,10 +46,15 @@ const (
|
||||
SpiderCipher = "Life in a different world from zero"
|
||||
)
|
||||
|
||||
/*API相关redis key*/
|
||||
const (
|
||||
IndexCacheKey = "IndexCache"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
// SearchTableName 存放检索信息的数据表名
|
||||
SearchTableName = "search_mt"
|
||||
SearchTableName = "search"
|
||||
|
||||
//mysql服务配置信息 root:root 设置mysql账户的用户名和密码
|
||||
|
||||
@@ -66,7 +71,7 @@ const (
|
||||
*/
|
||||
RedisAddr = `192.168.20.10:6379`
|
||||
RedisPassword = `root`
|
||||
RedisDBNo = 1
|
||||
RedisDBNo = 0
|
||||
|
||||
// RedisAddr docker compose 环境下运行使用如下配置信息
|
||||
//RedisAddr = `redis:6379`
|
||||
|
||||
@@ -22,11 +22,17 @@ type IndexLogic struct {
|
||||
var IL *IndexLogic
|
||||
|
||||
// IndexPage 首页数据处理
|
||||
func (i *IndexLogic) IndexPage() gin.H {
|
||||
Info := gin.H{}
|
||||
// 首页分类数据处理
|
||||
|
||||
// 1. 导航分类数据处理, 只提供 电影 电视剧 综艺 动漫 四大顶级分类和其子分类
|
||||
func (i *IndexLogic) IndexPage() map[string]interface{} {
|
||||
//声明返回值
|
||||
//Info := make(map[string]interface{})
|
||||
// 首页请求时长较高, 采用redis进行缓存, 在定时任务更新影片时清除对应缓存
|
||||
// 判断是否存在缓存数据, 存在则直接将数据返回
|
||||
Info := model.GetCacheData(config.IndexCacheKey)
|
||||
if Info != nil {
|
||||
return Info
|
||||
}
|
||||
Info = make(map[string]interface{})
|
||||
// 1. 首页分类数据处理 导航分类数据处理, 只提供 电影 电视剧 综艺 动漫 四大顶级分类和其子分类
|
||||
tree := model.CategoryTree{Category: &model.Category{Id: 0, Name: "分类信息"}}
|
||||
sysTree := model.GetCategoryTree()
|
||||
// 由于采集源数据格式不一,因此采用名称匹配
|
||||
@@ -38,15 +44,18 @@ func (i *IndexLogic) IndexPage() gin.H {
|
||||
}
|
||||
Info["category"] = tree
|
||||
// 2. 提供用于首页展示的顶级分类影片信息, 每分类 14条数据
|
||||
var list []gin.H
|
||||
var list []map[string]interface{}
|
||||
for _, c := range tree.Children {
|
||||
page := model.Page{PageSize: 14, Current: 1}
|
||||
movies := model.GetMovieListByPid(c.Id, &page)
|
||||
item := gin.H{"nav": c, "movies": movies}
|
||||
// 获取当前分类的本月热门影片
|
||||
HotMovies := model.GetHotMovieByPid(c.Id, &page)
|
||||
item := map[string]interface{}{"nav": c, "movies": movies, "hot": HotMovies}
|
||||
list = append(list, item)
|
||||
}
|
||||
Info["content"] = list
|
||||
|
||||
// 不存在首页数据缓存时将查询数据缓存到redis中
|
||||
model.DataCache(config.IndexCacheKey, Info)
|
||||
return Info
|
||||
}
|
||||
|
||||
@@ -141,23 +150,33 @@ func (i *IndexLogic) RelateMovie(detail model.MovieDetail, page *model.Page) []m
|
||||
return model.GetRelateMovieBasicInfo(search, page)
|
||||
}
|
||||
|
||||
// 将多个站点的对应影视播放源追加到主站点播放列表中
|
||||
/*
|
||||
将多个站点的对应影视播放源追加到主站点播放列表中
|
||||
1. 将主站点影片的name 和 subtitle 进行处理添加到用于匹配对应播放源的map中
|
||||
2. 仅对主站点影片name进行映射关系处理并将结果添加到map中
|
||||
例如: xxx第一季 xxx
|
||||
*/
|
||||
func multipleSource(detail *model.MovieDetail) {
|
||||
// 整合多播放源, 处理部分站点中影片名称的空格
|
||||
names := map[string]int{model.HashKey(detail.Name): 0}
|
||||
// 不同站点影片别名匹配
|
||||
re := regexp.MustCompile(`第一季$`)
|
||||
alias := strings.TrimSpace(re.ReplaceAllString(detail.Name, ""))
|
||||
names[model.HashKey(alias)] = 0
|
||||
// 将多个影片别名进行切分,放入names中
|
||||
// 整合多播放源, 初始化存储key map
|
||||
names := make(map[string]int)
|
||||
// 1. 判断detail的dbId是否存在, 存在则添加到names中作为匹配条件
|
||||
if detail.DbId > 0 {
|
||||
names[model.GenerateHashKey(detail.DbId)] = 0
|
||||
}
|
||||
// 2. 对name进行去除特殊格式处理
|
||||
names[model.GenerateHashKey(detail.Name)] = 0
|
||||
// 3. 对包含第一季的name进行处理
|
||||
names[model.GenerateHashKey(regexp.MustCompile(`第一季$`).ReplaceAllString(detail.Name, ""))] = 0
|
||||
|
||||
// 4. 将subtitle进行切分,放入names中
|
||||
if len(detail.SubTitle) > 0 && strings.Contains(detail.SubTitle, ",") {
|
||||
for _, v := range strings.Split(detail.SubTitle, ",") {
|
||||
names[model.HashKey(v)] = 0
|
||||
names[model.GenerateHashKey(v)] = 0
|
||||
}
|
||||
}
|
||||
if len(detail.SubTitle) > 0 && strings.Contains(detail.SubTitle, "/") {
|
||||
for _, v := range strings.Split(detail.SubTitle, "/") {
|
||||
names[model.HashKey(v)] = 0
|
||||
names[model.GenerateHashKey(v)] = 0
|
||||
}
|
||||
}
|
||||
// 遍历站点列表
|
||||
|
||||
@@ -5,12 +5,11 @@ import (
|
||||
"server/plugin/db"
|
||||
"server/plugin/spider"
|
||||
"server/router"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 执行初始化前等待20s , 让mysql服务完成初始化指令
|
||||
time.Sleep(time.Second * 20)
|
||||
//time.Sleep(time.Second * 20)
|
||||
//初始化redis客户端
|
||||
err := db.InitRedisConn()
|
||||
if err != nil {
|
||||
@@ -24,6 +23,7 @@ func init() {
|
||||
}
|
||||
func main() {
|
||||
start()
|
||||
//spider.MtSiteSpider()
|
||||
}
|
||||
|
||||
func start() {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"hash/fnv"
|
||||
"regexp"
|
||||
"server/config"
|
||||
"server/plugin/db"
|
||||
"strconv"
|
||||
@@ -45,6 +46,7 @@ type MovieDescriptor struct {
|
||||
AddTime int64 `json:"addTime"` //资源添加时间戳
|
||||
DbId int64 `json:"dbId"` //豆瓣id
|
||||
DbScore string `json:"dbScore"` // 豆瓣评分
|
||||
Hits int64 `json:"hits"` //影片热度
|
||||
Content string `json:"content"` //内容简介
|
||||
}
|
||||
|
||||
@@ -168,11 +170,22 @@ func SaveSitePlayList(siteName string, list []MovieDetail) (err error) {
|
||||
for _, d := range list {
|
||||
if len(d.PlayList) > 0 {
|
||||
data, _ := json.Marshal(d.PlayList[0])
|
||||
res[HashKey(d.Name)] = string(data)
|
||||
// 不保存电影解说类
|
||||
if strings.Contains(d.CName, "解说") {
|
||||
continue
|
||||
}
|
||||
// 如果DbId不为0, 则以dbID作为key进行hash额外存储一次
|
||||
if d.DbId > 0 {
|
||||
res[GenerateHashKey(d.DbId)] = string(data)
|
||||
}
|
||||
res[GenerateHashKey(d.Name)] = string(data)
|
||||
}
|
||||
}
|
||||
// 保存形式 key: MultipleSource:siteName Hash[hash(movieName)]list
|
||||
err = db.Rdb.HMSet(db.Cxt, fmt.Sprintf(config.MultipleSiteDetail, siteName), res).Err()
|
||||
// 如果结果不为空,则将数据保存到redis中
|
||||
if len(res) > 0 {
|
||||
// 保存形式 key: MultipleSource:siteName Hash[hash(movieName)]list
|
||||
err = db.Rdb.HMSet(db.Cxt, fmt.Sprintf(config.MultipleSiteDetail, siteName), res).Err()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -181,9 +194,9 @@ func AddSearchInfo(searchInfo SearchInfo) (err error) {
|
||||
// 片名 Name 分类 CName 类别标签 classTag 地区 Area 语言 Language 年份 Year 首字母 Initial, 排序
|
||||
data, _ := json.Marshal(searchInfo)
|
||||
// 时间排序 score -->时间戳 DbId 排序 --> 热度, 评分排序 DbScore
|
||||
err = db.Rdb.ZAdd(db.Cxt, fmt.Sprintf("%s:Pid%d", config.SearchTimeListKey, searchInfo.Pid), redis.Z{Score: float64(searchInfo.Time), Member: data}).Err()
|
||||
err = db.Rdb.ZAdd(db.Cxt, fmt.Sprintf("%s:Pid%d", config.SearchTimeListKey, searchInfo.Pid), redis.Z{Score: float64(searchInfo.UpdateStamp), Member: data}).Err()
|
||||
err = db.Rdb.ZAdd(db.Cxt, fmt.Sprintf("%s:Pid%d", config.SearchScoreListKey, searchInfo.Pid), redis.Z{Score: searchInfo.Score, Member: data}).Err()
|
||||
err = db.Rdb.ZAdd(db.Cxt, fmt.Sprintf("%s:Pid%d", config.SearchHeatListKey, searchInfo.Pid), redis.Z{Score: float64(searchInfo.Rank), Member: data}).Err()
|
||||
err = db.Rdb.ZAdd(db.Cxt, fmt.Sprintf("%s:Pid%d", config.SearchHeatListKey, searchInfo.Pid), redis.Z{Score: float64(searchInfo.Hits), Member: data}).Err()
|
||||
// 添加搜索关键字信息
|
||||
SearchKeyword(searchInfo)
|
||||
return
|
||||
@@ -298,27 +311,28 @@ func BatchSaveSearchInfo(list []MovieDetail) {
|
||||
func ConvertSearchInfo(detail MovieDetail) SearchInfo {
|
||||
score, _ := strconv.ParseFloat(detail.DbScore, 64)
|
||||
stamp, _ := time.ParseInLocation(time.DateTime, detail.UpdateTime, time.Local)
|
||||
year, err := strconv.ParseInt(detail.Year, 10, 64)
|
||||
// detail中的年份信息并不准确, 因此采用 ReleaseDate中的年份
|
||||
year, err := strconv.ParseInt(regexp.MustCompile(`[1-9][0-9]{3}`).FindString(detail.ReleaseDate), 10, 64)
|
||||
if err != nil {
|
||||
year = 0
|
||||
}
|
||||
return SearchInfo{
|
||||
Mid: detail.Id,
|
||||
Cid: detail.Cid,
|
||||
Pid: detail.Pid,
|
||||
Name: detail.Name,
|
||||
SubTitle: detail.SubTitle,
|
||||
CName: detail.CName,
|
||||
ClassTag: detail.ClassTag,
|
||||
Area: detail.Area,
|
||||
Language: detail.Language,
|
||||
Year: year,
|
||||
Initial: detail.Initial,
|
||||
Score: score,
|
||||
Rank: detail.DbId,
|
||||
Time: stamp.Unix(),
|
||||
State: detail.State,
|
||||
Remarks: detail.Remarks,
|
||||
Mid: detail.Id,
|
||||
Cid: detail.Cid,
|
||||
Pid: detail.Pid,
|
||||
Name: detail.Name,
|
||||
SubTitle: detail.SubTitle,
|
||||
CName: detail.CName,
|
||||
ClassTag: detail.ClassTag,
|
||||
Area: detail.Area,
|
||||
Language: detail.Language,
|
||||
Year: year,
|
||||
Initial: detail.Initial,
|
||||
Score: score,
|
||||
Hits: detail.Hits,
|
||||
UpdateStamp: stamp.Unix(),
|
||||
State: detail.State,
|
||||
Remarks: detail.Remarks,
|
||||
// releaseDate 部分影片缺失该参数, 所以使用添加时间作为上映时间排序
|
||||
ReleaseDate: detail.AddTime,
|
||||
}
|
||||
@@ -342,10 +356,28 @@ func GetDetailByKey(key string) MovieDetail {
|
||||
return detail
|
||||
}
|
||||
|
||||
// HashKey 将字符串转化为hash值
|
||||
func HashKey(str string) string {
|
||||
/*
|
||||
对附属播放源入库时的name|dbID进行处理,保证唯一性
|
||||
1. 去除name中的所有空格
|
||||
2. 去除name中含有的别名~.*~
|
||||
3. 去除name首尾的标点符号
|
||||
4. 将处理完成后的name转化为hash值作为存储时的key
|
||||
*/
|
||||
// GenerateHashKey 存储播放源信息时对影片名称进行处理, 提高各站点间同一影片的匹配度
|
||||
func GenerateHashKey[K string | ~int | int64](key K) string {
|
||||
mName := fmt.Sprint(key)
|
||||
//1. 去除name中的所有空格
|
||||
mName = regexp.MustCompile(`\s`).ReplaceAllString(mName, "")
|
||||
//2. 去除name中含有的别名~.*~
|
||||
mName = regexp.MustCompile(`~.*~$`).ReplaceAllString(mName, "")
|
||||
//3. 去除name首尾的标点符号
|
||||
mName = regexp.MustCompile(`^[[:punct:]]+|[[:punct:]]+$`).ReplaceAllString(mName, "")
|
||||
// 部分站点包含 动画版, 特殊别名 等字符, 需进行删除
|
||||
//mName = regexp.MustCompile(`动画版`).ReplaceAllString(mName, "")
|
||||
mName = regexp.MustCompile(`季.*`).ReplaceAllString(mName, "季")
|
||||
//4. 将处理完成后的name转化为hash值作为存储时的key
|
||||
h := fnv.New32a()
|
||||
_, err := h.Write([]byte(str))
|
||||
_, err := h.Write([]byte(mName))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ type MovieDetailInfo struct {
|
||||
AddTime int64 `json:"vod_time_add"` //资源添加时间戳
|
||||
DbId int64 `json:"vod_douban_id"` //豆瓣id
|
||||
DbScore string `json:"vod_douban_score"` // 豆瓣评分
|
||||
Hits int64 `json:"vod_hits"` // 总热度
|
||||
Content string `json:"vod_content"` //内容简介
|
||||
PlayFrom string `json:"vod_play_from"` // 播放来源
|
||||
PlaySeparator string `json:"vod_play_note"` // 播放信息分隔符
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"server/config"
|
||||
"server/plugin/db"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SearchInfo 存储用于检索的信息
|
||||
@@ -28,8 +29,8 @@ type SearchInfo struct {
|
||||
Year int64 `json:"year"` // 年份
|
||||
Initial string `json:"initial"` // 首字母
|
||||
Score float64 `json:"score"` //评分
|
||||
Time int64 `json:"time"` // 更新时间
|
||||
Rank int64 `json:"rank"` // 热度排行id
|
||||
UpdateStamp int64 `json:"updateStamp"` // 更新时间
|
||||
Hits int64 `json:"hits"` // 热度排行
|
||||
State string `json:"state"` //状态 正片|预告
|
||||
Remarks string `json:"remarks"` // 完结 | 更新至x集
|
||||
ReleaseDate int64 `json:"releaseDate"` //上映时间 时间戳
|
||||
@@ -144,7 +145,7 @@ func BatchSaveOrUpdate(list []SearchInfo) {
|
||||
// 如果存在对应数据则进行更新, 否则进行删除
|
||||
if count > 0 {
|
||||
// 记录已经存在则执行更新部分内容
|
||||
err := tx.Model(&SearchInfo{}).Where("mid", info.Mid).Updates(SearchInfo{Time: info.Time, Rank: info.Rank, State: info.State,
|
||||
err := tx.Model(&SearchInfo{}).Where("mid", info.Mid).Updates(SearchInfo{UpdateStamp: info.UpdateStamp, Hits: info.Hits, State: info.State,
|
||||
Remarks: info.Remarks, Score: info.Score, ReleaseDate: info.ReleaseDate}).Error
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
@@ -198,7 +199,7 @@ func GetMovieListByPid(pid int64, page *Page) []MovieBasicInfo {
|
||||
page.PageCount = int((page.Total + page.PageSize - 1) / page.PageSize)
|
||||
// 进行具体的信息查询
|
||||
var s []SearchInfo
|
||||
if err := db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).Where("pid", pid).Order("year DESC, time DESC").Find(&s).Error; err != nil {
|
||||
if err := db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).Where("pid", pid).Order("year DESC, update_stamp DESC").Find(&s).Error; err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
@@ -211,6 +212,24 @@ func GetMovieListByPid(pid int64, page *Page) []MovieBasicInfo {
|
||||
return list
|
||||
}
|
||||
|
||||
// GetHotMovieByPid 获取指定类别的热门影片
|
||||
func GetHotMovieByPid(pid int64, page *Page) []SearchInfo {
|
||||
// 返回分页参数
|
||||
var count int64
|
||||
db.Mdb.Model(&SearchInfo{}).Where("pid", pid).Count(&count)
|
||||
page.Total = int(count)
|
||||
page.PageCount = int((page.Total + page.PageSize - 1) / page.PageSize)
|
||||
// 进行具体的信息查询
|
||||
var s []SearchInfo
|
||||
// 当前时间偏移一个月
|
||||
t := time.Now().AddDate(0, -1, 0).Unix()
|
||||
if err := db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).Where("pid=? AND update_stamp > ?", pid, t).Order(" year DESC, hits DESC").Find(&s).Error; err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// SearchFilmKeyword 通过关键字搜索库存中满足条件的影片名
|
||||
func SearchFilmKeyword(keyword string, page *Page) []SearchInfo {
|
||||
var searchList []SearchInfo
|
||||
@@ -221,7 +240,7 @@ func SearchFilmKeyword(keyword string, page *Page) []SearchInfo {
|
||||
page.PageCount = int((page.Total + page.PageSize - 1) / page.PageSize)
|
||||
// 2. 获取满足条件的数据
|
||||
db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).
|
||||
Where("name LIKE ?", fmt.Sprint(`%`, keyword, `%`)).Or("sub_title LIKE ?", fmt.Sprint(`%`, keyword, `%`)).Order("year DESC, time DESC").Find(&searchList)
|
||||
Where("name LIKE ?", fmt.Sprint(`%`, keyword, `%`)).Or("sub_title LIKE ?", fmt.Sprint(`%`, keyword, `%`)).Order("year DESC, update_stamp DESC").Find(&searchList)
|
||||
return searchList
|
||||
}
|
||||
|
||||
@@ -234,7 +253,7 @@ func GetMovieListByCid(cid int64, page *Page) []MovieBasicInfo {
|
||||
page.PageCount = int((page.Total + page.PageSize - 1) / page.PageSize)
|
||||
// 进行具体的信息查询
|
||||
var s []SearchInfo
|
||||
if err := db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).Where("cid", cid).Order("year DESC, time DESC").Find(&s).Error; err != nil {
|
||||
if err := db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).Where("cid", cid).Order("year DESC, update_stamp DESC").Find(&s).Error; err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
@@ -302,3 +321,25 @@ func GetMultiplePlay(siteName, key string) []MovieUrlInfo {
|
||||
_ = json.Unmarshal([]byte(data), &playList)
|
||||
return playList
|
||||
}
|
||||
|
||||
// DataCache API请求 数据缓存
|
||||
func DataCache(key string, data map[string]interface{}) {
|
||||
val, _ := json.Marshal(data)
|
||||
db.Rdb.Set(db.Cxt, key, val, config.CategoryTreeExpired)
|
||||
}
|
||||
|
||||
// GetCacheData 获取API接口的缓存数据
|
||||
func GetCacheData(key string) map[string]interface{} {
|
||||
data := make(map[string]interface{})
|
||||
val, err := db.Rdb.Get(db.Cxt, key).Result()
|
||||
if err != nil || len(val) <= 0 {
|
||||
return nil
|
||||
}
|
||||
_ = json.Unmarshal([]byte(val), &data)
|
||||
return data
|
||||
}
|
||||
|
||||
// RemoveCache 删除数据缓存
|
||||
func RemoveCache(key string) {
|
||||
db.Rdb.Del(db.Cxt, key)
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ func ProcessMovieDetail(detail model.MovieDetailInfo) model.MovieDetail {
|
||||
AddTime: detail.AddTime,
|
||||
DbId: detail.DbId,
|
||||
DbScore: detail.DbScore,
|
||||
Hits: detail.Hits,
|
||||
Content: detail.Content,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ type Site struct {
|
||||
var SiteList = []Site{
|
||||
//{"tk", "https://api.tiankongapi.com/api.php/provide/vod"},
|
||||
//{"yh", "https://m3u8.apiyhzy.com/api.php/provide/vod/"},
|
||||
//{"zk", "https://api.1080zyku.com/inc/apijson.php"}, 数据格式不规范,不采用
|
||||
//{"fs", "https://www.feisuzyapi.com/api.php/provide/vod/"},
|
||||
|
||||
{"lz", "https://cj.lziapi.com/api.php/provide/vod/"},
|
||||
@@ -251,7 +252,7 @@ func UpdateMainDetail() {
|
||||
func UpdatePlayDetail() {
|
||||
for _, s := range SiteList {
|
||||
// 获取单个站点的分页数
|
||||
r := RequestInfo{Uri: s.Name, Params: url.Values{}}
|
||||
r := RequestInfo{Uri: s.Uri, Params: url.Values{}}
|
||||
r.Params.Set("h", config.UpdateInterval)
|
||||
pageCount, err := GetPageCount(r)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/robfig/cron/v3"
|
||||
"log"
|
||||
"server/config"
|
||||
"server/model"
|
||||
)
|
||||
|
||||
// RegularUpdateMovie 定时更新, 每半小时获取一次站点的最近x小时数据
|
||||
@@ -14,6 +15,8 @@ func RegularUpdateMovie() {
|
||||
// 执行更新最近x小时影片的Spider
|
||||
log.Println("执行一次影片更新任务...")
|
||||
UpdateMovieDetail()
|
||||
// 执行更新任务后清理redis中的相关API接口数据缓存
|
||||
clearCache()
|
||||
})
|
||||
|
||||
// 开启定时任务每月最后一天凌晨两点, 执行一次清库重取数据
|
||||
@@ -27,3 +30,8 @@ func RegularUpdateMovie() {
|
||||
|
||||
c.Start()
|
||||
}
|
||||
|
||||
// 清理API接口数据缓存
|
||||
func clearCache() {
|
||||
model.RemoveCache(config.IndexCacheKey)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -23,6 +24,9 @@ type RequestInfo struct {
|
||||
Resp []byte `json:"resp"` // 响应结果数据
|
||||
}
|
||||
|
||||
// RefererUrl 记录上次请求的url
|
||||
var RefererUrl string
|
||||
|
||||
// CreateClient 初始化请求客户端
|
||||
func CreateClient() *colly.Collector {
|
||||
c := colly.NewCollector()
|
||||
@@ -43,6 +47,11 @@ func CreateClient() *colly.Collector {
|
||||
// 设置一些请求头信息
|
||||
request.Headers.Set("Content-Type", "application/json;charset=UTF-8")
|
||||
//request.Headers.Set("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
|
||||
// 请求完成后设置请求头Referer
|
||||
if len(RefererUrl) <= 0 || !strings.Contains(RefererUrl, request.URL.Host) {
|
||||
RefererUrl = ""
|
||||
}
|
||||
request.Headers.Set("Referer", RefererUrl)
|
||||
})
|
||||
// 请求期间报错的回调
|
||||
c.OnError(func(response *colly.Response, err error) {
|
||||
@@ -69,6 +78,8 @@ func ApiGet(r *RequestInfo) {
|
||||
} else {
|
||||
r.Resp = []byte{}
|
||||
}
|
||||
// 将请求url保存到RefererUrl 用于 Header Refer属性
|
||||
RefererUrl = response.Request.URL.String()
|
||||
// 拿到response后输出请求url
|
||||
//log.Println("\n请求成功: ", response.Request.URL)
|
||||
})
|
||||
|
||||