feat: simplify movie link queries by removing unnecessary source parameters

This commit is contained in:
ngthanhvu
2026-07-31 00:20:35 -04:00
parent 149b750738
commit e429f67e0b
8 changed files with 24 additions and 95 deletions
-15
View File
@@ -32,23 +32,8 @@ const isDescriptionExpanded = ref(false);
const previewStyle = ref<Record<string, string>>({}); const previewStyle = ref<Record<string, string>>({});
let hideTimer: ReturnType<typeof setTimeout> | undefined; let hideTimer: ReturnType<typeof setTimeout> | undefined;
const movieSourcesQuery = computed(() =>
(
props.movie.sources || [
{ source: props.movie.source, slug: props.movie.slug },
]
)
.filter((source) => source.source && source.slug)
.map((source) => `${source.source}:${source.slug}`)
.join(","),
);
const movieLink = computed(() => ({ const movieLink = computed(() => ({
path: `/phim/${props.movie.slug}`, path: `/phim/${props.movie.slug}`,
query: {
source: props.movie.source,
srcs: movieSourcesQuery.value || undefined,
},
})); }));
function showPreview(event: MouseEvent) { function showPreview(event: MouseEvent) {
+3 -25
View File
@@ -188,9 +188,6 @@ function formatCommentContent(content: string, maxLength = 120) {
function movieLinkFromSection(movie: any) { function movieLinkFromSection(movie: any) {
return { return {
path: `/phim/${movie?.slug}`, path: `/phim/${movie?.slug}`,
query: {
source: movie?.source,
},
} }
} }
@@ -210,24 +207,11 @@ function genreTagColor(index: number) {
return colors[index % colors.length] return colors[index % colors.length]
} }
function movieSourcesQuery(movie: any) {
return (movie?.sources || [{ source: movie?.source, slug: movie?.slug }])
.filter((source: any) => source.source && source.slug)
.map((source: any) => `${source.source}:${source.slug}`)
.join(',')
}
function movieLink(movie: any) { function movieLink(movie: any) {
return { return {
path: `/phim/${movie?.slug}`, path: `/phim/${movie?.slug}`,
query: {
source: movie?.source,
srcs: movieSourcesQuery(movie) || undefined,
},
} }
} }
function goToSlide(index: number) { function goToSlide(index: number) {
heroSliderRef.value?.goTo(index) heroSliderRef.value?.goTo(index)
} }
@@ -236,7 +220,6 @@ function watchHistoryLink(item: WatchHistoryItem) {
return { return {
path: `/xem/${item.slug}`, path: `/xem/${item.slug}`,
query: { query: {
source: item.source,
server: item.serverIndex || 0, server: item.serverIndex || 0,
ep: (item.episodeIndex || 0) + 1, ep: (item.episodeIndex || 0) + 1,
}, },
@@ -296,7 +279,7 @@ watch(hero, async (currentHero) => {
return return
} }
const cacheKey = `${currentHero.source}:${currentHero.slug}` const cacheKey = currentHero.slug
if (heroDetailCache.has(cacheKey)) { if (heroDetailCache.has(cacheKey)) {
heroDetail.value = heroDetailCache.get(cacheKey) heroDetail.value = heroDetailCache.get(cacheKey)
return return
@@ -304,15 +287,10 @@ watch(hero, async (currentHero) => {
heroDetail.value = null heroDetail.value = null
try { try {
const detail = await $fetch(`/api/movies/${currentHero.slug}`, { const detail = await $fetch(`/api/movies/${currentHero.slug}`)
query: {
source: currentHero.source,
srcs: movieSourcesQuery(currentHero) || undefined,
},
})
heroDetailCache.set(cacheKey, detail) heroDetailCache.set(cacheKey, detail)
if (hero.value?.slug === currentHero.slug && hero.value?.source === currentHero.source) { if (hero.value?.slug === currentHero.slug) {
heroDetail.value = detail heroDetail.value = detail
} }
} catch { } catch {
+1 -1
View File
@@ -190,7 +190,7 @@ useHead({
<div v-if="selectedDay?.items?.length" class="schedule-list"> <div v-if="selectedDay?.items?.length" class="schedule-list">
<div class="schedule-card-grid"> <div class="schedule-card-grid">
<NuxtLink v-for="movie in selectedDay.items" :key="`${movie.source}-${movie.slug}`" <NuxtLink v-for="movie in selectedDay.items" :key="`${movie.source}-${movie.slug}`"
:to="{ path: `/phim/${movie.slug}`, query: { source: movie.source } }" :to="`/phim/${movie.slug}`"
class="schedule-card"> class="schedule-card">
<img :src="movie.thumb || movie.poster" :alt="movie.name" <img :src="movie.thumb || movie.poster" :alt="movie.name"
class="schedule-card-image"> class="schedule-card-image">
-1
View File
@@ -15,7 +15,6 @@ function watchHistoryLink(item: WatchHistoryItem) {
return { return {
path: `/xem/${item.slug}`, path: `/xem/${item.slug}`,
query: { query: {
source: item.source,
server: item.serverIndex || 0, server: item.serverIndex || 0,
ep: (item.episodeIndex || 0) + 1, ep: (item.episodeIndex || 0) + 1,
}, },
+10 -28
View File
@@ -1,7 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
const route = useRoute() const route = useRoute()
const requestedSource = computed(() => String(route.query.source || 'nguonc'))
const requestedSources = computed(() => typeof route.query.srcs === 'string' ? route.query.srcs : '')
const selectedServer = ref(0) const selectedServer = ref(0)
const movieInfoOpen = ref(false) const movieInfoOpen = ref(false)
const contentExpanded = ref(false) const contentExpanded = ref(false)
@@ -16,19 +14,9 @@ const {
saveWatchLater, saveWatchLater,
} = useMovieLibrary() } = useMovieLibrary()
const { data: movie, pending, error } = await useFetch(() => `/api/movies/${route.params.slug}`, { const { data: movie, pending, error } = await useFetch(() => `/api/movies/${route.params.slug}`)
query: computed(() => ({
source: requestedSource.value,
srcs: requestedSources.value || undefined,
})),
watch: [requestedSource, requestedSources],
})
const servers = computed(() => movie.value?.servers ?? [])
const activeServer = computed(() => servers.value[selectedServer.value]) const activeServer = computed(() => servers.value[selectedServer.value])
const activeSource = computed(() => String(activeServer.value?.source || movie.value?.source || route.query.source || 'nguonc'))
const activeSourceSlug = computed(() => String(activeServer.value?.sourceSlug || movie.value?.slug || route.params.slug))
const activeSourceServerIndex = computed(() => Number(activeServer.value?.sourceServerIndex ?? 0))
const actors = computed(() => movie.value?.actors ?? []) const actors = computed(() => movie.value?.actors ?? [])
const actorSummary = computed(() => actors.value.map((actor: any) => actor.name).filter(Boolean).slice(0, 6).join(', ')) const actorSummary = computed(() => actors.value.map((actor: any) => actor.name).filter(Boolean).slice(0, 6).join(', '))
const episodeCount = computed(() => { const episodeCount = computed(() => {
@@ -38,8 +26,8 @@ const episodeCount = computed(() => {
const libraryItem = computed(() => { const libraryItem = computed(() => {
if (!movie.value) return null if (!movie.value) return null
return { return {
source: activeSource.value, source: movie.value.source,
slug: activeSourceSlug.value, slug: movie.value.slug,
name: movie.value.name, name: movie.value.name,
originName: movie.value.originName, originName: movie.value.originName,
thumb: movie.value.thumb, thumb: movie.value.thumb,
@@ -48,23 +36,20 @@ const libraryItem = computed(() => {
} }
}) })
const firstWatchLink = computed(() => ({ const firstWatchLink = computed(() => ({
path: `/xem/${activeSourceSlug.value}`, path: `/xem/${movie.value?.slug}`,
query: { source: activeSource.value, srcs: requestedSources.value || undefined, server: activeSourceServerIndex.value, ep: 1 }, query: { server: selectedServer.value, ep: 1 },
})) }))
function actorInitial(name: string) { function actorInitial(name: string) {
return name.trim().charAt(0).toUpperCase() return name.trim().charAt(0).toUpperCase()
} }
const servers = computed(() => movie.value?.servers ?? [])
function episodeLink(index: number) { function episodeLink(index: number) {
return { return {
path: `/xem/${activeSourceSlug.value}`, path: `/xem/${movie.value?.slug}`,
query: { query: { server: selectedServer.value, ep: index + 1 },
source: activeSource.value,
srcs: requestedSources.value || undefined,
server: activeSourceServerIndex.value,
ep: index + 1,
},
} }
} }
@@ -139,12 +124,9 @@ onMounted(async () => {
// await loadComments() // await loadComments()
await $fetch(`/api/movies/${route.params.slug}/view`, { await $fetch(`/api/movies/${route.params.slug}/view`, {
method: 'POST', method: 'POST',
query: { source: route.query.source },
}).catch(() => { }) }).catch(() => { })
}) })
watch(requestedSource, () => { selectedServer.value = 0 })
watch(requestedSources, () => { selectedServer.value = 0 })
watch(libraryItem, () => { refreshFavoriteState() }) watch(libraryItem, () => { refreshFavoriteState() })
useHead(() => ({ useHead(() => ({
@@ -491,7 +473,7 @@ useHead(() => ({
<!-- Comment Section --> <!-- Comment Section -->
<div class="mt-10"> <div class="mt-10">
<ClientOnly> <ClientOnly>
<CommentSection :source="activeSource" :slug="String(route.params.slug)" :movie-name="movie?.name" /> <CommentSection :source="movie?.source" :slug="String(route.params.slug)" :movie-name="movie?.name" />
</ClientOnly> </ClientOnly>
</div> </div>
</div> </div>
-11
View File
@@ -53,20 +53,9 @@ const filteredMovies = computed(() => {
const totalPages = computed(() => Number(data.value?.pagination?.totalPages || data.value?.pagination?.total_pages || 0)) const totalPages = computed(() => Number(data.value?.pagination?.totalPages || data.value?.pagination?.total_pages || 0))
const sourceStatus = computed(() => data.value?.sources ?? []) const sourceStatus = computed(() => data.value?.sources ?? [])
function movieSourcesQuery(movie: any) {
return (movie.sources || [{ source: movie.source, slug: movie.slug }])
.filter((source: any) => source.source && source.slug)
.map((source: any) => `${source.source}:${source.slug}`)
.join(',')
}
function movieLink(movie: any) { function movieLink(movie: any) {
return { return {
path: `/phim/${movie.slug}`, path: `/phim/${movie.slug}`,
query: {
source: movie.source,
srcs: movieSourcesQuery(movie),
},
} }
} }
+10 -11
View File
@@ -45,13 +45,12 @@ const { loadWatchHistory: fetchWatchHistory, saveWatchHistory: persistWatchHisto
const { isFavorite, saveFavorite, removeFavorite, saveWatchLater } = useMovieLibrary() const { isFavorite, saveFavorite, removeFavorite, saveWatchLater } = useMovieLibrary()
const { data: movie, pending, error } = useFetch(() => `/api/movies/${route.params.slug}`, { const { data: movie, pending, error } = useFetch(() => `/api/movies/${route.params.slug}`, {
query: computed(() => ({ source: route.query.source, srcs: route.query.srcs })), lazy: true, default: () => ({}),
watch: [() => route.query.source, () => route.query.srcs],
}) })
const currentMovieSource = computed(() => String(route.query.source || movie.value?.source || '')) const currentMovieSource = computed(() => String(movie.value?.source || ''))
const libraryItem = computed(() => movie.value ? { const libraryItem = computed(() => movie.value ? {
source: currentMovieSource.value, slug: String(route.params.slug), source: movie.value.source, slug: String(route.params.slug),
name: movie.value.name, originName: movie.value.originName, thumb: movie.value.thumb, poster: movie.value.poster, updatedAt: Date.now(), name: movie.value.name, originName: movie.value.originName, thumb: movie.value.thumb, poster: movie.value.poster, updatedAt: Date.now(),
} : null) } : null)
@@ -98,7 +97,7 @@ function subtitleLabel(server: any, index: number) {
} }
function episodeLink(index: number) { function episodeLink(index: number) {
return { path: `/xem/${route.params.slug}`, query: { source: route.query.source, srcs: route.query.srcs, server: selectedServer.value, ep: index + 1 } } return { path: `/xem/${route.params.slug}`, query: { server: selectedServer.value, ep: index + 1 } }
} }
function episodeIndexForServer(serverIndex: number, subIndex = selectedSubtitle.value, episodeIndex = selectedEpisode.value) { function episodeIndexForServer(serverIndex: number, subIndex = selectedSubtitle.value, episodeIndex = selectedEpisode.value) {
@@ -114,7 +113,7 @@ async function selectServer(index: number) {
selectedSubtitle.value = 0 selectedSubtitle.value = 0
selectedEpisode.value = episodeIndexForServer(index, 0) selectedEpisode.value = episodeIndexForServer(index, 0)
resetProgressTimer(); destroyHlsPlayer() resetProgressTimer(); destroyHlsPlayer()
await navigateTo({ path: `/xem/${route.params.slug}`, query: { ...route.query, server: index, ep: selectedEpisode.value + 1 } }, { replace: true }) await navigateTo({ path: `/xem/${route.params.slug}`, query: { server: index, ep: selectedEpisode.value + 1 } }, { replace: true })
if (shouldAutoplay) nextTick(() => startPlayer()) if (shouldAutoplay) nextTick(() => startPlayer())
} }
@@ -123,7 +122,7 @@ async function selectSubtitle(index: number) {
selectedSubtitle.value = index selectedSubtitle.value = index
selectedEpisode.value = episodeIndexForServer(selectedServer.value, index) selectedEpisode.value = episodeIndexForServer(selectedServer.value, index)
resetProgressTimer(); destroyHlsPlayer() resetProgressTimer(); destroyHlsPlayer()
await navigateTo({ path: `/xem/${route.params.slug}`, query: { ...route.query, server: selectedServer.value, ep: selectedEpisode.value + 1 } }, { replace: true }) await navigateTo({ path: `/xem/${route.params.slug}`, query: { server: selectedServer.value, ep: selectedEpisode.value + 1 } }, { replace: true })
if (hasStarted.value) nextTick(() => startPlayer()) if (hasStarted.value) nextTick(() => startPlayer())
} }
@@ -247,7 +246,7 @@ function handleVideoError() { hlsErrorMessage.value = 'HLS phát lỗi.'; isVide
async function loadResumeProgress() { async function loadResumeProgress() {
if (!import.meta.client || !movie.value) return if (!import.meta.client || !movie.value) return
const source = String(route.query.source || movie.value.source || '') const source = String(movie.value.source || '')
const items = await fetchWatchHistory(30) const items = await fetchWatchHistory(30)
const item = items.find((i) => i.slug === String(route.params.slug) && i.source === source) const item = items.find((i) => i.slug === String(route.params.slug) && i.source === source)
const saved = Math.floor(item?.progressSeconds || 0) const saved = Math.floor(item?.progressSeconds || 0)
@@ -350,7 +349,7 @@ function handleTouchEnd(event: TouchEvent) {
async function saveWatchHistory() { async function saveWatchHistory() {
if (!import.meta.client || !movie.value) return if (!import.meta.client || !movie.value) return
await persistWatchHistory({ await persistWatchHistory({
source: String(route.query.source || movie.value.source || ''), slug: String(route.params.slug), source: String(movie.value.source || ''), slug: String(route.params.slug),
name: movie.value.name, originName: movie.value.originName, thumb: movie.value.thumb, poster: movie.value.poster, name: movie.value.name, originName: movie.value.originName, thumb: movie.value.thumb, poster: movie.value.poster,
episodeName: activeEpisode.value?.name, episodeIndex: selectedEpisode.value, serverIndex: selectedServer.value, episodeName: activeEpisode.value?.name, episodeIndex: selectedEpisode.value, serverIndex: selectedServer.value,
progressSeconds: Math.floor(progressSeconds.value), durationSeconds: Math.floor(durationSeconds.value), updatedAt: Date.now(), progressSeconds: Math.floor(progressSeconds.value), durationSeconds: Math.floor(durationSeconds.value), updatedAt: Date.now(),
@@ -680,7 +679,7 @@ useHead(() => ({
</p> </p>
<NuxtLink <NuxtLink
:to="{ path: `/phim/${route.params.slug}`, query: { source: route.query.source, srcs: route.query.srcs } }" :to="{ path: `/phim/${route.params.slug}` }"
class="mt-1 inline-flex items-center gap-1 text-[13px] text-cinek-500 hover:underline"> class="mt-1 inline-flex items-center gap-1 text-[13px] text-cinek-500 hover:underline">
Thông tin phim Thông tin phim
<AppIcon name="chevron-right" class="size-4" /> <AppIcon name="chevron-right" class="size-4" />
@@ -779,7 +778,7 @@ useHead(() => ({
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-1"> <div class="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-1">
<NuxtLink v-for="rel in relatedMovies" :key="rel.slug" <NuxtLink v-for="rel in relatedMovies" :key="rel.slug"
:to="{ path: `/phim/${rel.slug}`, query: { source: rel.source } }" :to="`/phim/${rel.slug}`"
class="group flex gap-3 rounded-lg bg-[#1A1A24] p-2 transition hover:bg-white/5"> class="group flex gap-3 rounded-lg bg-[#1A1A24] p-2 transition hover:bg-white/5">
<img :src="rel.thumb || rel.poster" :alt="rel.name" class="h-16 w-12 shrink-0 rounded object-cover"> <img :src="rel.thumb || rel.poster" :alt="rel.name" class="h-16 w-12 shrink-0 rounded object-cover">
-3
View File
@@ -14,9 +14,6 @@ async function refreshFavorites() {
function movieLink(item: LibraryMovieItem) { function movieLink(item: LibraryMovieItem) {
return { return {
path: `/phim/${item.slug}`, path: `/phim/${item.slug}`,
query: {
source: item.source,
},
} }
} }