add migrations (package, config, migrations), add exceptions filter for kodi, start VideoFiles module

This commit is contained in:
Jurgis Sakalauskas
2023-01-03 10:58:33 +02:00
parent 2fb78b61c4
commit 9cf70326af
27 changed files with 295 additions and 69 deletions
@@ -1,42 +0,0 @@
import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn } from 'typeorm';
import { TitleEntity } from './title.entity';
@Entity('files')
export class FileEntity {
@PrimaryColumn()
id: number;
@Column()
path: string;
@Column()
deleted: boolean;
@Column()
size: number;
@Column({ name: 'relative_path' })
relativePath: string;
@Column({ name: 'file_name' })
fileName: string;
@Column()
infos: string;
@Column()
info: string;
@Column({ name: 'title_id' })
titleId: number;
@ManyToOne(() => TitleEntity, (title) => title.files)
@JoinColumn({ name: 'title_id' })
title: TitleEntity;
@Column()
season: number | null;
@Column()
duration: number;
}
@@ -1,18 +0,0 @@
import { Column, Entity, OneToMany, PrimaryColumn } from 'typeorm';
import { TitleTypeEnum } from '../Enum/title-type.enum';
import { FileEntity } from './file.entity';
@Entity('titles')
export class TitleEntity {
@PrimaryColumn()
id: number;
@Column()
title: string;
@Column()
type: TitleTypeEnum;
@OneToMany(() => FileEntity, (file) => file.title)
files: FileEntity[];
}
@@ -1,4 +0,0 @@
export enum TitleTypeEnum {
movie = 'movie',
show = 'show',
}
+1 -1
View File
@@ -1,8 +1,8 @@
import { Controller, Get, Head, Param, Req, Res } from '@nestjs/common';
import ApiResponse from '../Dto/api-response.dto';
import { AllFilesService } from './all-files.service';
import { TitleTypeEnum } from './Enum/title-type.enum';
import { Request, Response } from 'express';
import { TitleTypeEnum } from '../../VideoFiles/Enum/title-type.enum';
@Controller('api/all')
export class AllFilesController {
+2 -7
View File
@@ -2,16 +2,11 @@ import { Module } from '@nestjs/common';
import { AllFilesController } from './all-files.controller';
import { AllFilesService } from './all-files.service';
import { KodiApiResponseFactory } from '../kodi-api-response.factory';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TitleEntity } from './Entity/title.entity';
import { FileEntity } from './Entity/file.entity';
import { StreamerModule } from '../../Streamer/streamer.module';
import { VideoFilesModule } from '../../VideoFiles/video-files.module';
@Module({
imports: [
TypeOrmModule.forFeature([TitleEntity, FileEntity]),
StreamerModule,
],
imports: [StreamerModule, VideoFilesModule],
controllers: [AllFilesController],
providers: [AllFilesService, KodiApiResponseFactory],
})
+23 -26
View File
@@ -1,23 +1,19 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import ApiResponse from '../Dto/api-response.dto';
import { KodiApiResponseFactory } from '../kodi-api-response.factory';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { TitleEntity } from './Entity/title.entity';
import { FileEntity } from './Entity/file.entity';
import { TitleTypeEnum } from './Enum/title-type.enum';
import { StreamerFacade } from '../../Streamer/streamer.facade';
import { Request, Response } from 'express';
import { VideoFilesProvider } from '../../VideoFiles/video-files.provider';
import { TitleTypeEnum } from '../../VideoFiles/Enum/title-type.enum';
import { FileEntity } from '../../VideoFiles/Entity/file.entity';
import { TitleEntity } from '../../VideoFiles/Entity/title.entity';
@Injectable()
export class AllFilesService {
constructor(
private readonly koiApiResponseFactory: KodiApiResponseFactory,
@InjectRepository(TitleEntity)
private titleRepository: Repository<TitleEntity>,
@InjectRepository(FileEntity)
private fileRepository: Repository<FileEntity>,
private readonly streamerFacade: StreamerFacade,
private readonly videoTitlesProvider: VideoFilesProvider,
) {}
getMenu(): ApiResponse {
@@ -37,13 +33,7 @@ export class AllFilesService {
}
async getListOfTitles(type: TitleTypeEnum): Promise<ApiResponse> {
const entities = await this.titleRepository
.createQueryBuilder()
.setFindOptions({ relations: { files: true } })
.where('type = :type', { type: type })
.andWhere('deleted = :deleted', { deleted: false })
.getMany();
const entities = await this.videoTitlesProvider.getListOfTitles(type);
const response = this.koiApiResponseFactory.createApiResponse();
response.setTitle('All ' + type + 's');
entities.forEach((entity) => {
@@ -51,7 +41,9 @@ export class AllFilesService {
.createItem()
.setLabel(entity.title)
.setToFolder()
.setPlot(entity.files.length + ' files')
.setPlot(
entity.files.length + (entity.files.length > 1 ? ' files' : ' file'),
)
.setPath('all/' + entity.id);
});
@@ -59,15 +51,15 @@ export class AllFilesService {
}
async loadTitle(titleId: number, seasonId: number): Promise<ApiResponse> {
const titleEntity = await this.titleRepository.findOne({
relations: { files: true },
where: { id: titleId },
});
const titleEntity = titleId
? await this.videoTitlesProvider.getTitleWithFiles(titleId)
: null;
if (titleEntity === null) {
//todo - alert instead of full api response
return this.koiApiResponseFactory
.createApiResponse()
.setTitle('Not found ' + titleId);
.setTitle('Not found' + (titleId ? ' ' + titleId : ''));
}
if (titleEntity.type === TitleTypeEnum.movie) {
@@ -112,10 +104,13 @@ export class AllFilesService {
//todo - add num files count
const seasons = [];
const filesCount = {};
for (const fileEntity of titleEntity.files) {
if (seasons.indexOf(fileEntity.season) === -1) {
seasons.push(fileEntity.season);
filesCount[fileEntity.season] = 0;
}
filesCount[fileEntity.season]++;
}
for (const season of seasons) {
@@ -123,6 +118,9 @@ export class AllFilesService {
.createItem()
.setLabel(season + ' Sezonas')
.setToFolder()
.setPlot(
filesCount[season] + (filesCount[season] > 1 ? ' files' : ' file'),
)
.setPath(`all/${titleEntity.id}/${season}`);
}
@@ -138,13 +136,12 @@ export class AllFilesService {
if (isNaN(id)) {
throw new NotFoundException(`id #${fileId} not correct`);
}
const filePath = await this.videoTitlesProvider.getFilePath(id);
const entity = await this.fileRepository.findOne({ where: { id: id } });
if (entity === null) {
if (filePath === null) {
throw new NotFoundException(`id #${fileId} not found`);
}
return this.streamerFacade.streamFile(request, response, entity.path);
return this.streamerFacade.streamFile(request, response, filePath);
}
}
+4 -4
View File
@@ -4,10 +4,10 @@ export default class ApiResponse {
items: Array<ResponseItem> = [];
content = 'videos';
updateList = true;
category: string;
play: string;
msgBoxOK: string;
nosort: boolean;
category: string | undefined;
play: string | undefined;
msgBoxOK: string | undefined;
nosort: boolean | undefined;
setTitle(title) {
this.category = title;
@@ -25,6 +25,7 @@ export class LrtApiSearchClient {
const searchResponse: SearchResponseInterface = resp.data;
const cats = [];
const loadedCategories = new Set();
//todo - move to mapper
const responseDto = new SearchResponseDto(
@@ -36,6 +37,9 @@ export class LrtApiSearchClient {
if (cats.indexOf(searchItem.category_title) > -1) {
continue;
}
if (loadedCategories.has(searchItem.category_title)) {
continue;
}
const itemDto = new SearchResponseItemDto();
itemDto.label = searchItem.category_title;
itemDto.thumb =
@@ -46,6 +50,7 @@ export class LrtApiSearchClient {
itemDto.categoryId = await this.findCategoryId(searchItem.category_url);
cats.push(searchItem.category_title);
loadedCategories.add(searchItem.category_title);
responseDto.addItem(itemDto);
}
@@ -83,7 +88,6 @@ export class LrtApiSearchClient {
}
private async findCategoryIdInLrt(categoryUrl: string): Promise<string> {
//todo - cache to database
const url = 'https://www.lrt.lt' + categoryUrl;
const resp = await firstValueFrom(this.httpService.get(url));
const body: string = resp.data;
@@ -1,6 +1,6 @@
import { Column, Entity, PrimaryColumn } from 'typeorm';
@Entity()
@Entity('lrt_categories')
export class LrtCategory {
@PrimaryColumn({ name: 'category_url' })
categoryUrl: string;