mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 23:56:42 +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
|
export const prefixWithPlus = (value: number) => value > 0 ? `+${value}` : value
|
||||||
|
|
||||||
|
// 格式化为Sxx
|
||||||
export const formatSeason = (value: string) => value ? `S${value.padStart(2, '0')}` : ''
|
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]}`;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
// 订阅
|
// 订阅
|
||||||
export interface Subscribe {
|
export interface Subscribe {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -253,3 +254,140 @@ export interface Plugin {
|
|||||||
installed?: boolean
|
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 api from "@/api";
|
||||||
import { doneNProgress, startNProgress } from "@/api/nprogress";
|
import { doneNProgress, startNProgress } from "@/api/nprogress";
|
||||||
import { MediaInfo, NotExistMediaInfo, Subscribe, TmdbSeason } from "@/api/types";
|
import { MediaInfo, NotExistMediaInfo, Subscribe, TmdbSeason } from "@/api/types";
|
||||||
|
import router from "@/router";
|
||||||
import { useToast } from "vue-toast-notification";
|
import { useToast } from "vue-toast-notification";
|
||||||
|
|
||||||
// 输入参数
|
// 输入参数
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
media: Object as PropType<MediaInfo>,
|
media: Object as PropType<MediaInfo>,
|
||||||
@@ -231,7 +231,7 @@ const checkSeasonsNotExists = async () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
$toast.error(`${props.media?.title}无法识别到themoviedb媒体信息!`);
|
$toast.error(`${props.media?.title}无法识别TMDB媒体信息!`);
|
||||||
tmdbFlag.value = false;
|
tmdbFlag.value = false;
|
||||||
}
|
}
|
||||||
// 处理完成
|
// 处理完成
|
||||||
@@ -318,6 +318,17 @@ const openDetailWindow = () => {
|
|||||||
window.open(getDetailLink(), "_blank");
|
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);
|
onBeforeMount(handleCheckSubscribe);
|
||||||
|
|
||||||
@@ -394,7 +405,7 @@ const seasonsSelected = ref<TmdbSeason[]>([]);
|
|||||||
{{ props.media?.overview }}
|
{{ props.media?.overview }}
|
||||||
</p>
|
</p>
|
||||||
<div class="flex align-center justify-between">
|
<div class="flex align-center justify-between">
|
||||||
<IconBtn icon="mdi-magnify" color="white" />
|
<IconBtn icon="mdi-magnify" color="white" @click.stop="handleSearch" />
|
||||||
<IconBtn
|
<IconBtn
|
||||||
icon="mdi-heart"
|
icon="mdi-heart"
|
||||||
:color="isSubscribed ? 'error' : 'white'"
|
:color="isSubscribed ? 'error' : 'white'"
|
||||||
|
|||||||
@@ -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>
|
||||||
+15
-2
@@ -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>
|
||||||
|
|||||||
+2
-1
@@ -27,8 +27,9 @@ const router = createRouter({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'resource',
|
path: 'resource/:keyword+',
|
||||||
component: () => import('../pages/resource.vue'),
|
component: () => import('../pages/resource.vue'),
|
||||||
|
props: true,
|
||||||
meta: {
|
meta: {
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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
|
indeterminate
|
||||||
color="primary"
|
color="primary"
|
||||||
></VProgressCircular>
|
></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" />
|
<PluginCard v-for="data in dataList" :key="data.id" :plugin="data" />
|
||||||
</div>
|
</div>
|
||||||
<NoDataFound
|
<NoDataFound
|
||||||
|
|||||||
Reference in New Issue
Block a user