mirror of
https://github.com/ngthanhvu/kr-phim.git
synced 2026-08-10 14:27:47 +00:00
feat: simplify movie link queries by removing unnecessary source parameters
This commit is contained in:
@@ -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
@@ -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 {
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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
@@ -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>
|
||||||
|
|||||||
@@ -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
@@ -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">
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user