feat: add metadata to libraryItems, server side library sorting and filtering

This commit is contained in:
Aleksi Lassila
2025-03-26 18:22:25 +02:00
parent fc4581ba41
commit 5200b9f805
13 changed files with 590 additions and 270 deletions

View File

@@ -1,11 +1,39 @@
import { Inject, Injectable } from '@nestjs/common';
import { MediaType, PaginationParamsDto } from 'src/common/common.dto';
import {
MediaType,
PaginatedResponseDto,
PaginationParamsDto,
} from 'src/common/common.dto';
import { MetadataService } from 'src/metadata/metadata.service';
import { Repository } from 'typeorm';
import { IsNull, Not, Repository } from 'typeorm';
import { LibraryItemDto } from './library.dto';
import { LibraryItem } from './library.entity';
import { USER_LIBRARY_REPOSITORY } from './library.providers';
export enum SortByDirection {
Asc = 'asc',
Desc = 'desc',
}
export enum LibrarySortBy {
DateAdded = 'dateAdded',
Name = 'name',
FirstReleaseDate = 'firstReleaseDate',
LastReleaseDate = 'lastReleaseDate',
}
export enum MyListFilter {
Movie = 'movie',
Series = 'series',
}
export enum CatalogueFilter {
All = 'all',
Movies = 'movies',
Series = 'series',
Unavailable = 'unavailable',
}
@Injectable()
export class LibraryService {
constructor(
@@ -14,14 +42,13 @@ export class LibraryService {
private readonly metadataService: MetadataService,
) {}
async getLibraryItemDtos(
userId: string,
pagination: PaginationParamsDto,
): Promise<LibraryItemDto[]> {
const items = await this.getLibraryItems(userId, pagination);
async getMyListDtos(
...args: Parameters<LibraryService['getMyList']>
): Promise<PaginatedResponseDto<LibraryItemDto>> {
const paginatedItems = await this.getMyList(...args);
return Promise.all(
items.map(async (item) => {
const items = await Promise.all(
paginatedItems.items.map(async (item) => {
const seriesMetadata =
item.mediaType === MediaType.Series
? await this.metadataService.getSeriesByTmdbId(item.tmdbId)
@@ -38,21 +65,95 @@ export class LibraryService {
});
}),
);
return { ...paginatedItems, items };
}
private async getLibraryItems(
userId: string,
pagination: PaginationParamsDto,
): Promise<LibraryItem[]> {
return this.libraryRepository.find({
where: { userId },
/** TODO: decouple librayItem and movie/seriesItem */
private async getMyList(options: {
userId: string;
pagination: PaginationParamsDto;
filter?: MyListFilter;
sortBy?: LibrarySortBy;
direction?: SortByDirection;
}): Promise<PaginatedResponseDto<LibraryItem>> {
const {
userId,
pagination,
filter,
sortBy,
direction = SortByDirection.Desc,
} = options;
const directon = direction === SortByDirection.Asc ? 'ASC' : 'DESC';
const order = {
[LibrarySortBy.DateAdded]: { createdAt: directon } as const,
[LibrarySortBy.Name]: {
seriesMetadata: {
name: directon,
},
movieMetadata: {
name: directon,
},
} as const,
[LibrarySortBy.FirstReleaseDate]: {
movieMetadata: {
releaseDate: directon,
},
seriesMetadata: {
firstReleaseDate: directon,
},
} as const,
[LibrarySortBy.LastReleaseDate]: {
movieMetadata: {
releaseDate: directon,
},
seriesMetadata: {
lastReleaseDate: directon,
},
} as const,
}[sortBy];
const mediaType = filter
? filter === MyListFilter.Movie
? MediaType.Movie
: MediaType.Series
: undefined;
const [items, total] = await this.libraryRepository.findAndCount({
relations: {
playStates: true,
seriesMetadata: true,
movieMetadata: true,
},
// TODO: Implement pagination
// take: pagination.itemsPerPage,
// skip: pagination.itemsPerPage * (pagination.page - 1),
select: {
seriesMetadata: {
firstReleaseDate: true,
lastReleaseDate: true,
name: true,
},
movieMetadata: {
releaseDate: true,
name: true,
},
},
where: {
userId,
...(mediaType ? { mediaType } : {}),
},
order,
take: pagination.itemsPerPage,
skip: pagination.itemsPerPage * (pagination.page - 1),
});
return {
items,
total,
itemsPerPage: pagination.itemsPerPage,
page: pagination.page,
};
}
async findByTmdbId(