feat: Personalized tmdb recommendations

This commit is contained in:
Aleksi Lassila
2024-05-16 18:38:22 +03:00
parent c2be333d5f
commit a95d91f90c
5 changed files with 93 additions and 0 deletions

View File

@@ -34,6 +34,14 @@ export class JellyfinSettings {
userId: string;
}
export class TmdbSettings {
@ApiProperty({ required: true })
sessionId: string;
@ApiProperty({ required: true })
userId: string;
}
export class Settings {
@ApiProperty({ required: true })
autoplayTrailers: boolean;
@@ -52,6 +60,8 @@ export class Settings {
radarr: RadarrSettings;
@ApiProperty({ required: true, type: JellyfinSettings })
jellyfin: JellyfinSettings;
@ApiProperty({ required: true, type: TmdbSettings })
tmdb: TmdbSettings;
}
const DEFAULT_SETTINGS: Settings = {
@@ -81,6 +91,10 @@ const DEFAULT_SETTINGS: Settings = {
baseUrl: '',
userId: '',
},
tmdb: {
sessionId: '',
userId: '',
},
};
@Entity()

View File

@@ -42,6 +42,10 @@ export interface components {
baseUrl: string;
userId: string;
};
TmdbSettings: {
sessionId: string;
userId: string;
};
Settings: {
autoplayTrailers: boolean;
language: string;
@@ -49,6 +53,7 @@ export interface components {
sonarr: components["schemas"]["SonarrSettings"];
radarr: components["schemas"]["RadarrSettings"];
jellyfin: components["schemas"]["JellyfinSettings"];
tmdb: components["schemas"]["TmdbSettings"];
};
UserDto: {
id: string;

View File

@@ -5,6 +5,7 @@ import { TMDB_API_KEY, TMDB_BACKDROP_SMALL } from '../../constants';
import { settings } from '../../stores/settings.store';
import type { TitleType } from '../../types';
import type { Api } from '../api.interface';
import { appState } from '../../stores/app-state.store';
const CACHE_ONE_DAY = 'max-age=86400';
const CACHE_FOUR_DAYS = 'max-age=345600';
@@ -59,6 +60,14 @@ export class TmdbApi implements Api<paths> {
return TmdbApi.getClient();
}
getSessionId() {
return get(appState)?.user?.settings.tmdb.sessionId;
}
getUserId() {
return get(appState)?.user?.settings.tmdb.userId;
}
// MOVIES
getTmdbMovie = async (tmdbId: number) => {
@@ -203,6 +212,46 @@ export class TmdbApi implements Api<paths> {
.then((res) => res.data);
// OTHER
// USER
getRecommendedMovies = async (): Promise<TmdbMovie2[]> => {
const userId = this.getUserId();
if (!userId) return [];
return (
this.getClient()
// @ts-ignore
?.GET('/4/account/{account_object_id}/movie/recommendations', {
params: {
path: {
account_object_id: userId
}
}
})
.then((res: any) => res.data?.results || [])
);
};
getRecommendedSeries = async (): Promise<TmdbSeries2[]> => {
const userId = this.getUserId();
console.log('userId recommended series', userId);
if (!userId) return [];
return (
this.getClient()
// @ts-ignore
?.GET('/4/account/{account_object_id}/tv/recommendations', {
params: {
path: {
account_object_id: userId
}
}
})
.then((res: any) => res.data?.results || [])
);
};
}
export const tmdbApi = new TmdbApi();

View File

@@ -21,6 +21,8 @@
const newDigitalReleases = getDigitalReleases();
const upcomingMovies = getUpcomingMovies();
const recommendedMovies = tmdbApi.getRecommendedMovies();
recommendedMovies.then((res) => console.log('Recommended Movies', res));
function getUpcomingMovies() {
return TmdbApi.getClient()
@@ -89,6 +91,17 @@
{/if}
{/await}
{#await recommendedMovies then recommendedMovies}
{#if recommendedMovies?.length}
<Carousel scrollClass="px-32" on:enter={scrollIntoView({ vertical: 128 })}>
<span slot="header">Recommended Movies</span>
{#each recommendedMovies as item}
<TmdbCard on:enter={scrollIntoView({ horizontal: 128 })} size="lg" {item} />
{/each}
</Carousel>
{/if}
{/await}
{#await newDigitalReleases then nowStreaming}
<Carousel scrollClass="px-32" on:enter={scrollIntoView({ vertical: 128 })}>
<span slot="header">New Digital Releases</span>

View File

@@ -17,6 +17,7 @@
const nowStreaming = getNowStreaming();
const upcomingSeries = fetchUpcomingSeries();
const recommendedSeries = tmdbApi.getRecommendedSeries();
function getNowStreaming() {
return TmdbApi.getClient()
@@ -83,6 +84,17 @@
{/if}
{/await}
{#await recommendedSeries then recommendedSeries}
{#if recommendedSeries?.length}
<Carousel scrollClass="px-32" on:enter={scrollIntoView({ vertical: 128 })}>
<span slot="header">Recommended</span>
{#each recommendedSeries as item}
<TmdbCard on:enter={scrollIntoView({ horizontal: 128 })} size="lg" {item} />
{/each}
</Carousel>
{/if}
{/await}
{#await nowStreaming then nowStreaming}
<Carousel scrollClass="px-32" on:enter={scrollIntoView({ vertical: 128 })}>
<span slot="header">Now Streaming</span>