mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-05-12 02:21:06 +08:00
add search pages
This commit is contained in:
@@ -47,4 +47,23 @@ export const formatDateToMonthShort = (value: string, toTimeForCurrentDay = true
|
||||
|
||||
export const prefixWithPlus = (value: number) => value > 0 ? `+${value}` : value
|
||||
|
||||
// 格式化为Sxx
|
||||
export const formatSeason = (value: string) => value ? `S${value.padStart(2, '0')}` : ''
|
||||
|
||||
// 格式化为xx[TGMK]B
|
||||
export const formatFileSize = (bytes: number) => {
|
||||
if (bytes < 0) {
|
||||
throw new Error("字节数不能为负数。");
|
||||
}
|
||||
|
||||
const units = ["B", "KB", "MB", "GB", "TB"];
|
||||
let size = bytes;
|
||||
let unitIndex = 0;
|
||||
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
return `${size.toFixed(2)} ${units[unitIndex]}`;
|
||||
}
|
||||
|
||||
138
src/api/types.ts
138
src/api/types.ts
@@ -1,3 +1,4 @@
|
||||
|
||||
// 订阅
|
||||
export interface Subscribe {
|
||||
id: number;
|
||||
@@ -253,3 +254,140 @@ export interface Plugin {
|
||||
installed?: boolean
|
||||
|
||||
}
|
||||
|
||||
// 种子信息
|
||||
export interface TorrentInfo {
|
||||
// 站点ID
|
||||
site?: number
|
||||
// 站点名称
|
||||
site_name?: string
|
||||
// 站点Cookie
|
||||
site_cookie?: string
|
||||
// 站点UA
|
||||
site_ua?: string
|
||||
// 站点是否使用代理
|
||||
site_proxy: boolean
|
||||
// 站点优先级
|
||||
site_order: number
|
||||
// 种子名称
|
||||
title?: string
|
||||
// 种子副标题
|
||||
description?: string
|
||||
// IMDB ID
|
||||
imdbid: string
|
||||
// 种子链接
|
||||
enclosure?: string
|
||||
// 详情页面
|
||||
page_url?: string
|
||||
// 种子大小
|
||||
size: number
|
||||
// 做种者
|
||||
seeders: number
|
||||
// 下载者
|
||||
peers: number
|
||||
// 完成者
|
||||
grabs: number
|
||||
// 发布时间
|
||||
pubdate?: string
|
||||
// 已过时间
|
||||
date_elapsed?: string
|
||||
// 上传因子
|
||||
uploadvolumefactor: number
|
||||
// 下载因子
|
||||
downloadvolumefactor: number
|
||||
// HR
|
||||
hit_and_run: boolean
|
||||
// 种子标签
|
||||
labels: string[]
|
||||
// 种子优先级
|
||||
pri_order: number
|
||||
}
|
||||
|
||||
|
||||
// 识别元数据
|
||||
export interface MetaInfo {
|
||||
// 是否处理的文件
|
||||
isfile: boolean
|
||||
// 原字符串
|
||||
org_string?: string
|
||||
// 副标题
|
||||
subtitle?: string
|
||||
// 类型 电影、电视剧
|
||||
type: string
|
||||
// 识别的中文名
|
||||
cn_name?: string
|
||||
// 识别的英文名
|
||||
en_name?: string
|
||||
// 年份
|
||||
year?: string
|
||||
// 总季数
|
||||
total_seasons: number
|
||||
// 识别的开始季 数字
|
||||
begin_season?: number
|
||||
// 识别的结束季 数字
|
||||
end_season?: number
|
||||
// 总集数
|
||||
total_episodes: number
|
||||
// 识别的开始集
|
||||
begin_episode?: number
|
||||
// 识别的结束集
|
||||
end_episode?: number
|
||||
// Partx Cd Dvd Disk Disc
|
||||
part?: string
|
||||
// 识别的资源类型
|
||||
resource_type?: string
|
||||
// 识别的效果
|
||||
resource_effect?: string
|
||||
// 识别的分辨率
|
||||
resource_pix?: string
|
||||
// 识别的制作组/字幕组
|
||||
resource_team?: string
|
||||
// 视频编码
|
||||
video_encode?: string
|
||||
// 音频编码
|
||||
audio_encode?: string
|
||||
// 名称(自动中英文)
|
||||
name: string
|
||||
// SXX-SXX
|
||||
season: string
|
||||
// SXX-SXX 有季号才返回
|
||||
sea: string
|
||||
// begin_season 的数字,电视剧没有季的返回1
|
||||
season_seq: string
|
||||
// 季的数组
|
||||
season_list: number[]
|
||||
// Exx-Exx
|
||||
episode: string
|
||||
// 集的数组
|
||||
episode_list: number[]
|
||||
// ExxExx
|
||||
episodes: string
|
||||
//xx-xx
|
||||
episode_seqs: string
|
||||
// begin_episode 的数字
|
||||
episode_seq: string
|
||||
// SxxExx
|
||||
season_episode: string
|
||||
// 资源类型字符串,含分辨率
|
||||
resource_term: string
|
||||
// 资源类型字符串,不含分辨率
|
||||
edtion: string
|
||||
// 发布组/字幕组字符串
|
||||
release_group: string
|
||||
// 视频编码
|
||||
video_term: string
|
||||
// 音频编码
|
||||
audio_term: string
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 上下文信息
|
||||
export interface Context {
|
||||
// 元信息
|
||||
meta_info: MetaInfo,
|
||||
// 媒体信息
|
||||
media_info: MediaInfo,
|
||||
// 种子信息
|
||||
torrent_info: TorrentInfo,
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ import { formatSeason } from "@/@core/utils/formatters";
|
||||
import api from "@/api";
|
||||
import { doneNProgress, startNProgress } from "@/api/nprogress";
|
||||
import { MediaInfo, NotExistMediaInfo, Subscribe, TmdbSeason } from "@/api/types";
|
||||
import router from "@/router";
|
||||
import { useToast } from "vue-toast-notification";
|
||||
|
||||
// 输入参数
|
||||
const props = defineProps({
|
||||
media: Object as PropType<MediaInfo>,
|
||||
@@ -231,7 +231,7 @@ const checkSeasonsNotExists = async () => {
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
$toast.error(`${props.media?.title}无法识别到themoviedb媒体信息!`);
|
||||
$toast.error(`${props.media?.title}无法识别TMDB媒体信息!`);
|
||||
tmdbFlag.value = false;
|
||||
}
|
||||
// 处理完成
|
||||
@@ -318,6 +318,17 @@ const openDetailWindow = () => {
|
||||
window.open(getDetailLink(), "_blank");
|
||||
};
|
||||
|
||||
// 开始搜索
|
||||
const handleSearch = () => {
|
||||
router.push(
|
||||
`/resource/${
|
||||
props.media?.tmdb_id
|
||||
? `tmdb:${props.media?.tmdb_id}`
|
||||
: `douban:${props.media?.douban_id}`
|
||||
}`
|
||||
);
|
||||
};
|
||||
|
||||
// 装载时检查是否已订阅
|
||||
onBeforeMount(handleCheckSubscribe);
|
||||
|
||||
@@ -394,7 +405,7 @@ const seasonsSelected = ref<TmdbSeason[]>([]);
|
||||
{{ props.media?.overview }}
|
||||
</p>
|
||||
<div class="flex align-center justify-between">
|
||||
<IconBtn icon="mdi-magnify" color="white" />
|
||||
<IconBtn icon="mdi-magnify" color="white" @click.stop="handleSearch" />
|
||||
<IconBtn
|
||||
icon="mdi-heart"
|
||||
:color="isSubscribed ? 'error' : 'white'"
|
||||
|
||||
21
src/components/cards/TorrentCard.vue
Normal file
21
src/components/cards/TorrentCard.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<script lang="ts" setup>
|
||||
import { Context } from "@/api/types";
|
||||
|
||||
// 输入参数
|
||||
const props = defineProps({
|
||||
torrent: Object as PropType<Context>,
|
||||
width: String,
|
||||
height: String,
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<VCard :width="props.width" :height="props.height">
|
||||
<VCardItem>
|
||||
<VCardTitle>{{ props.torrent?.torrent_info.title }}</VCardTitle>
|
||||
</VCardItem>
|
||||
|
||||
<VCardText>
|
||||
{{ props.torrent?.torrent_info.description }}
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</template>
|
||||
@@ -1,3 +1,16 @@
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import TorrentCardListView from "@/views/discover/TorrentCardListView.vue";
|
||||
|
||||
<template></template>
|
||||
// 路由参数
|
||||
const route = useRoute();
|
||||
|
||||
// 查询TMDBID或标题
|
||||
const keyword = route.params?.keyword?.toString() ?? "";
|
||||
console.log(keyword);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<TorrentCardListView :keyword="keyword" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -27,8 +27,9 @@ const router = createRouter({
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'resource',
|
||||
path: 'resource/:keyword+',
|
||||
component: () => import('../pages/resource.vue'),
|
||||
props: true,
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
},
|
||||
|
||||
66
src/views/discover/TorrentCardListView.vue
Normal file
66
src/views/discover/TorrentCardListView.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<script lang="ts" setup>
|
||||
import api from "@/api";
|
||||
import { Context } from "@/api/types";
|
||||
import TorrentCard from "@/components/cards/TorrentCard.vue";
|
||||
import NoDataFound from "@/components/NoDataFound.vue";
|
||||
|
||||
// 定义输入参数
|
||||
const props = defineProps({
|
||||
keyword: String,
|
||||
});
|
||||
|
||||
// 数据列表
|
||||
const dataList = ref<Context[]>([]);
|
||||
|
||||
// 是否刷新过
|
||||
const isRefreshed = ref(false);
|
||||
|
||||
// 获取订阅列表数据
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
// 优先按TMDBID精确查询
|
||||
if (props.keyword?.startsWith("tmdb:") || props.keyword?.startsWith("douban:")) {
|
||||
dataList.value = await api.get(`search/media/${props.keyword}`);
|
||||
} else {
|
||||
// 按标题模糊查询
|
||||
dataList.value = await api.get(`search/title/${props.keyword}`);
|
||||
}
|
||||
isRefreshed.value = true;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
// 加载时获取数据
|
||||
onBeforeMount(fetchData);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VProgressCircular
|
||||
class="centered"
|
||||
v-if="!isRefreshed"
|
||||
indeterminate
|
||||
color="primary"
|
||||
></VProgressCircular>
|
||||
<div class="grid gap-3 grid-torrent-card" v-if="dataList.length > 0">
|
||||
<TorrentCard
|
||||
v-for="data in dataList"
|
||||
:key="data.torrent_info.title"
|
||||
:torrent="data"
|
||||
/>
|
||||
</div>
|
||||
<NoDataFound
|
||||
v-if="dataList.length === 0 && isRefreshed"
|
||||
error-code="404"
|
||||
error-title="没有资源"
|
||||
error-description="没有搜索到符合条件的资源。"
|
||||
>
|
||||
</NoDataFound>
|
||||
</template>
|
||||
|
||||
<style type="scss">
|
||||
.grid-torrent-card {
|
||||
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
|
||||
padding-block-end: 1rem;
|
||||
}
|
||||
</style>
|
||||
@@ -31,7 +31,7 @@ onBeforeMount(fetchData);
|
||||
indeterminate
|
||||
color="primary"
|
||||
></VProgressCircular>
|
||||
<div class="grid gap-3 grid-site-card" v-if="dataList.length > 0">
|
||||
<div class="grid gap-3 grid-plugin-card" v-if="dataList.length > 0">
|
||||
<PluginCard v-for="data in dataList" :key="data.id" :plugin="data" />
|
||||
</div>
|
||||
<NoDataFound
|
||||
|
||||
Reference in New Issue
Block a user