mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-09 17:36:15 -08:00
fix(downloads): preserve snapshot source semantics
This commit is contained in:
1 parent
513348e011
commit
9ba45ed443
6 files changed
+82
-118
No files matched your search
+7
-28
@@ -38,21 +38,10 @@ describe('startStalkerVodDownload', () => {
|
||||
genre: 'Drama, Thriller',
|
||||
rating_imdb: '7.7',
|
||||
tmdb_id: 541,
|
||||
tmdb_cast: [
|
||||
{
|
||||
name: 'Stella Star',
|
||||
character: 'Mira',
|
||||
profileUrl: null,
|
||||
tmdbPersonId: 77,
|
||||
},
|
||||
],
|
||||
tmdb_directors: [
|
||||
{
|
||||
name: 'Dorian Vale',
|
||||
profileUrl: null,
|
||||
tmdbPersonId: 78,
|
||||
},
|
||||
],
|
||||
actors: 'Stella Star, Mira Moon',
|
||||
director: 'Dorian Vale',
|
||||
tmdb_cast: [],
|
||||
tmdb_directors: [],
|
||||
},
|
||||
},
|
||||
} as unknown as VodDetailsItem;
|
||||
@@ -80,19 +69,8 @@ describe('startStalkerVodDownload', () => {
|
||||
providerCategoryId: '7',
|
||||
genres: ['Drama', 'Thriller'],
|
||||
tmdbId: 541,
|
||||
cast: [
|
||||
expect.objectContaining({
|
||||
name: 'Stella Star',
|
||||
role: 'Mira',
|
||||
tmdbPersonId: 77,
|
||||
}),
|
||||
],
|
||||
creators: [
|
||||
expect.objectContaining({
|
||||
name: 'Dorian Vale',
|
||||
tmdbPersonId: 78,
|
||||
}),
|
||||
],
|
||||
cast: [{ name: 'Stella Star' }, { name: 'Mira Moon' }],
|
||||
creators: [{ name: 'Dorian Vale' }],
|
||||
}),
|
||||
})
|
||||
);
|
||||
@@ -131,6 +109,7 @@ describe('startStalkerVodDownload', () => {
|
||||
|
||||
expect(startDownload).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
title: ' ',
|
||||
metadataSnapshot: expect.objectContaining({
|
||||
mediaKind: 'movie',
|
||||
title: 'Sparse title',
|
||||
|
||||
+13
-11
@@ -95,14 +95,14 @@ function people(
|
||||
enriched: TmdbEnrichedCastMember[] | undefined,
|
||||
fallback: string | undefined
|
||||
) {
|
||||
return (
|
||||
enriched?.map((person) => ({
|
||||
name: person.name,
|
||||
role: person.character,
|
||||
profileUrl: person.profileUrl ?? undefined,
|
||||
tmdbPersonId: person.tmdbPersonId,
|
||||
})) ?? textList(fallback)?.map((name) => ({ name }))
|
||||
);
|
||||
return enriched?.length
|
||||
? enriched.map((person) => ({
|
||||
name: person.name,
|
||||
role: person.character,
|
||||
profileUrl: person.profileUrl ?? undefined,
|
||||
tmdbPersonId: person.tmdbPersonId,
|
||||
}))
|
||||
: textList(fallback)?.map((name) => ({ name }));
|
||||
}
|
||||
|
||||
export async function startStalkerVodDownload(
|
||||
@@ -119,7 +119,9 @@ export async function startStalkerVodDownload(
|
||||
}
|
||||
|
||||
const itemData = item.data as DownloadVodData;
|
||||
const title = firstText(itemData?.info?.name, itemData?.title) ?? 'Unknown';
|
||||
const downloadTitle = itemData?.info?.name || itemData?.title || 'Unknown';
|
||||
const snapshotTitle =
|
||||
firstText(itemData?.info?.name, itemData?.title) ?? 'Unknown';
|
||||
const cmdToUse = await resolveDownloadCmd(item, itemData, deps);
|
||||
|
||||
const url = await deps.fetchLinkToPlay(
|
||||
@@ -135,12 +137,12 @@ export async function startStalkerVodDownload(
|
||||
playlistId: playlist.id,
|
||||
xtreamId: normalizeStalkerEntityIdAsNumber(itemData?.id) ?? 0,
|
||||
contentType: 'vod',
|
||||
title,
|
||||
title: downloadTitle,
|
||||
url,
|
||||
posterUrl: itemData?.info?.movie_image,
|
||||
metadataSnapshot: createMovieDownloadSnapshot({
|
||||
language: deps.language?.trim() || 'en',
|
||||
title,
|
||||
title: snapshotTitle,
|
||||
originalTitle: itemData.info?.o_name,
|
||||
plot: itemData.info?.description,
|
||||
releaseDate: itemData.info?.releasedate,
|
||||
|
||||
+12
-12
@@ -357,6 +357,10 @@ describe('StalkerSeriesViewComponent', () => {
|
||||
description: 'Parent series plot',
|
||||
movie_image:
|
||||
'https://images.example.test/posters/signal-house.jpg',
|
||||
actors: 'Sienna Wave',
|
||||
director: 'Cora Bell',
|
||||
tmdb_cast: [],
|
||||
tmdb_directors: [],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -364,20 +368,14 @@ describe('StalkerSeriesViewComponent', () => {
|
||||
await fixture.whenStable();
|
||||
|
||||
await fixture.componentInstance.downloadEpisode({
|
||||
id: 'episode-tracking-id',
|
||||
episode_num: 2,
|
||||
episode_num: -3,
|
||||
title: 'The Call',
|
||||
container_extension: 'mpg',
|
||||
info: {
|
||||
plot: 'Episode-specific plot',
|
||||
movie_image: 'https://images.example.test/stills/the-call.jpg',
|
||||
duration_secs: 3120,
|
||||
rating: 8.6,
|
||||
},
|
||||
custom_sid: 'vod-series',
|
||||
added: '',
|
||||
season: 1,
|
||||
direct_source: '',
|
||||
season: '0',
|
||||
originalId: '502',
|
||||
} as never);
|
||||
|
||||
@@ -386,10 +384,10 @@ describe('StalkerSeriesViewComponent', () => {
|
||||
playlistId: 'stalker-1',
|
||||
playlistType: 'stalker',
|
||||
seriesXtreamId: 50001,
|
||||
seasonNumber: 1,
|
||||
episodeNumber: 2,
|
||||
seasonNumber: 0,
|
||||
episodeNumber: -3,
|
||||
title: 'Signal House - S00E-3 - The Call',
|
||||
metadataSnapshot: expect.objectContaining({
|
||||
version: 1,
|
||||
language: 'en',
|
||||
mediaKind: 'series',
|
||||
title: 'Signal House',
|
||||
@@ -397,9 +395,11 @@ describe('StalkerSeriesViewComponent', () => {
|
||||
posterUrl:
|
||||
'https://images.example.test/posters/signal-house.jpg',
|
||||
providerCategoryId: '18',
|
||||
cast: [{ name: 'Sienna Wave' }],
|
||||
creators: [{ name: 'Cora Bell' }],
|
||||
episode: {
|
||||
seasonNumber: 1,
|
||||
episodeNumber: 2,
|
||||
episodeNumber: 1,
|
||||
title: 'The Call',
|
||||
plot: 'Episode-specific plot',
|
||||
stillUrl:
|
||||
|
||||
+17
-16
@@ -119,14 +119,14 @@ function snapshotPeople(
|
||||
enriched: TmdbEnrichedCastMember[] | undefined,
|
||||
fallback: string | undefined
|
||||
) {
|
||||
return (
|
||||
enriched?.map((person) => ({
|
||||
name: person.name,
|
||||
role: person.character,
|
||||
profileUrl: person.profileUrl ?? undefined,
|
||||
tmdbPersonId: person.tmdbPersonId,
|
||||
})) ?? snapshotTextList(fallback)?.map((name) => ({ name }))
|
||||
);
|
||||
return enriched?.length
|
||||
? enriched.map((person) => ({
|
||||
name: person.name,
|
||||
role: person.character,
|
||||
profileUrl: person.profileUrl ?? undefined,
|
||||
tmdbPersonId: person.tmdbPersonId,
|
||||
}))
|
||||
: snapshotTextList(fallback)?.map((name) => ({ name }));
|
||||
}
|
||||
|
||||
function positiveCoordinate(value: number | string | undefined): number {
|
||||
@@ -1105,12 +1105,13 @@ export class StalkerSeriesViewComponent implements OnDestroy {
|
||||
|
||||
const episodeInfo = this.getEpisodeInfo(episode);
|
||||
const posterUrl = episodeInfo?.movie_image;
|
||||
const seasonNum = positiveCoordinate(episode.season);
|
||||
const episodeNum = positiveCoordinate(episode.episode_num);
|
||||
const seasonNum = Number(episode.season || 1);
|
||||
const episodeNum = episode.episode_num || 1;
|
||||
const seriesTitle =
|
||||
item.info?.name?.trim() ||
|
||||
this.displayItem()?.info?.name?.trim() ||
|
||||
'Series';
|
||||
item.info?.name || this.displayItem()?.info?.name || 'Series';
|
||||
const snapshotSeriesTitle = seriesTitle.trim() || 'Series';
|
||||
const snapshotSeasonNum = positiveCoordinate(episode.season);
|
||||
const snapshotEpisodeNum = positiveCoordinate(episode.episode_num);
|
||||
const episodeTitle = `${seriesTitle} - S${String(seasonNum).padStart(
|
||||
2,
|
||||
'0'
|
||||
@@ -1128,7 +1129,7 @@ export class StalkerSeriesViewComponent implements OnDestroy {
|
||||
this.translateService.currentLang ||
|
||||
this.translateService.defaultLang ||
|
||||
'en',
|
||||
title: seriesTitle,
|
||||
title: snapshotSeriesTitle,
|
||||
originalTitle: item.info?.o_name,
|
||||
plot: item.info?.description,
|
||||
releaseDate: item.info?.releasedate,
|
||||
@@ -1146,8 +1147,8 @@ export class StalkerSeriesViewComponent implements OnDestroy {
|
||||
item.info?.director
|
||||
),
|
||||
episode: {
|
||||
seasonNumber: seasonNum,
|
||||
episodeNumber: episodeNum,
|
||||
seasonNumber: snapshotSeasonNum,
|
||||
episodeNumber: snapshotEpisodeNum,
|
||||
title: episode.title,
|
||||
plot: episodeInfo?.plot,
|
||||
stillUrl: episodeInfo?.movie_image,
|
||||
|
||||
@@ -148,34 +148,35 @@ export class VodDetailsDownloadsService {
|
||||
'en',
|
||||
title: presentation.title,
|
||||
originalTitle: info?.o_name,
|
||||
plot: info?.plot || info?.description,
|
||||
plot: info?.description || info?.plot,
|
||||
releaseDate: info?.releasedate,
|
||||
year: year(info?.releasedate),
|
||||
durationMinutes: durationMinutes(info),
|
||||
genres: textList(info?.genre),
|
||||
rating: number(info?.rating),
|
||||
rating: number(info?.rating_imdb),
|
||||
status: info?.status,
|
||||
posterUrl: presentation.posterUrl,
|
||||
backdropUrl: info?.backdrop_path?.[0],
|
||||
tmdbId: number(info?.tmdb_id),
|
||||
providerCategoryId: vodItem.movie_data?.category_id,
|
||||
cast:
|
||||
info?.tmdb_cast?.map((person) => ({
|
||||
name: person.name,
|
||||
role: person.character,
|
||||
profileUrl: person.profileUrl ?? undefined,
|
||||
tmdbPersonId: person.tmdbPersonId,
|
||||
})) ??
|
||||
textList(info?.actors || info?.cast)?.map((name) => ({
|
||||
name,
|
||||
})),
|
||||
creators:
|
||||
info?.tmdb_directors?.map((person) => ({
|
||||
name: person.name,
|
||||
role: person.character,
|
||||
profileUrl: person.profileUrl ?? undefined,
|
||||
tmdbPersonId: person.tmdbPersonId,
|
||||
})) ?? textList(info?.director)?.map((name) => ({ name })),
|
||||
cast: info?.tmdb_cast?.length
|
||||
? info.tmdb_cast.map((person) => ({
|
||||
name: person.name,
|
||||
role: person.character,
|
||||
profileUrl: person.profileUrl ?? undefined,
|
||||
tmdbPersonId: person.tmdbPersonId,
|
||||
}))
|
||||
: textList(info?.actors || info?.cast)?.map((name) => ({
|
||||
name,
|
||||
})),
|
||||
creators: info?.tmdb_directors?.length
|
||||
? info.tmdb_directors.map((person) => ({
|
||||
name: person.name,
|
||||
role: person.character,
|
||||
profileUrl: person.profileUrl ?? undefined,
|
||||
tmdbPersonId: person.tmdbPersonId,
|
||||
}))
|
||||
: textList(info?.director)?.map((name) => ({ name })),
|
||||
}),
|
||||
headers: {
|
||||
userAgent: playlist.userAgent,
|
||||
|
||||
@@ -421,14 +421,15 @@ describe('VodDetailsRouteComponent — playback actions', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('downloads the movie the route currently shows', async () => {
|
||||
it('downloads the route movie with the metadata precedence rendered on screen', async () => {
|
||||
currentPlaylist.set({ id: 'playlist-1' });
|
||||
fixture.detectChanges();
|
||||
|
||||
await fixture.componentInstance.downloadVod({
|
||||
info: {
|
||||
name: 'Metadata Movie',
|
||||
description: 'Provider or TMDB description',
|
||||
description: 'Rendered description wins',
|
||||
plot: 'Lower-priority plot',
|
||||
movie_image:
|
||||
'https://images.example.test/posters/metadata-movie.jpg',
|
||||
backdrop_path: [
|
||||
@@ -437,24 +438,14 @@ describe('VodDetailsRouteComponent — playback actions', () => {
|
||||
releasedate: '2025-03-14',
|
||||
duration_secs: 7200,
|
||||
genre: 'Drama, Mystery',
|
||||
rating: '8.4',
|
||||
rating: '9.9',
|
||||
rating_imdb: '7.3',
|
||||
status: 'Released',
|
||||
tmdb_id: 12345,
|
||||
tmdb_cast: [
|
||||
{
|
||||
name: 'Ada Actor',
|
||||
character: 'The Lead',
|
||||
profileUrl: 'https://image.tmdb.org/t/p/w185/ada.jpg',
|
||||
tmdbPersonId: 91,
|
||||
},
|
||||
],
|
||||
tmdb_directors: [
|
||||
{
|
||||
name: 'Dana Director',
|
||||
profileUrl: null,
|
||||
tmdbPersonId: 92,
|
||||
},
|
||||
],
|
||||
actors: 'Ada Actor, Bea Actor',
|
||||
director: 'Dana Director',
|
||||
tmdb_cast: [],
|
||||
tmdb_directors: [],
|
||||
},
|
||||
// A DIFFERENT id in the payload: the route's id must win.
|
||||
movie_data: {
|
||||
@@ -478,7 +469,7 @@ describe('VodDetailsRouteComponent — playback actions', () => {
|
||||
language: 'en',
|
||||
mediaKind: 'movie',
|
||||
title: 'Metadata Movie',
|
||||
plot: 'Provider or TMDB description',
|
||||
plot: 'Rendered description wins',
|
||||
posterUrl:
|
||||
'https://images.example.test/posters/metadata-movie.jpg',
|
||||
backdropUrl:
|
||||
@@ -486,19 +477,9 @@ describe('VodDetailsRouteComponent — playback actions', () => {
|
||||
providerCategoryId: '235',
|
||||
tmdbId: 12345,
|
||||
genres: ['Drama', 'Mystery'],
|
||||
cast: [
|
||||
expect.objectContaining({
|
||||
name: 'Ada Actor',
|
||||
role: 'The Lead',
|
||||
tmdbPersonId: 91,
|
||||
}),
|
||||
],
|
||||
creators: [
|
||||
expect.objectContaining({
|
||||
name: 'Dana Director',
|
||||
tmdbPersonId: 92,
|
||||
}),
|
||||
],
|
||||
rating: 7.3,
|
||||
cast: [{ name: 'Ada Actor' }, { name: 'Bea Actor' }],
|
||||
creators: [{ name: 'Dana Director' }],
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user