mirror of
https://github.com/aleksilassila/reiverr.git
synced 2026-04-27 03:05:10 +02:00
feat: Improved movie playback and listing stream options
This commit is contained in:
@@ -21,12 +21,11 @@ import { Request, Response } from 'express';
|
||||
import { Readable } from 'stream';
|
||||
import { User } from 'src/users/user.entity';
|
||||
import { UserSourcesService } from 'src/users/user-sources/user-sources.service';
|
||||
import { PluginSettingsTemplate } from 'plugins/plugin-types';
|
||||
import {
|
||||
PlaybackConfigDto,
|
||||
PluginSettingsDto,
|
||||
PluginSettingsTemplateDto,
|
||||
SourceListDto as VideoStreamListDto,
|
||||
VideoStreamListDto,
|
||||
ValidationResponsekDto as ValidationResponseDto,
|
||||
VideoStreamDto,
|
||||
} from './source-plugins.dto';
|
||||
@@ -97,48 +96,66 @@ export class SourcesController {
|
||||
}
|
||||
|
||||
@ApiTags('movies')
|
||||
@Get('movies/:tmdbId/sources')
|
||||
@Get('movies/:tmdbId/sources/:sourceId/streams')
|
||||
@ApiOkResponse({
|
||||
description: 'Movie sources',
|
||||
type: VideoStreamListDto,
|
||||
})
|
||||
async getMovieSources(
|
||||
async getMovieStreams(
|
||||
@Param('tmdbId') tmdbId: string,
|
||||
@Param('sourceId') sourceId: string,
|
||||
@GetUser() user: User,
|
||||
@GetAuthToken() token: string,
|
||||
): Promise<VideoStreamListDto> {
|
||||
if (!user) {
|
||||
throw new UnauthorizedException();
|
||||
const plugin = this.sourcesService.getPlugin(sourceId);
|
||||
|
||||
if (!plugin) {
|
||||
throw new NotFoundException('Plugin not found');
|
||||
}
|
||||
|
||||
const plugins = await this.sourcesService.getPlugins();
|
||||
const sources: VideoStreamListDto['sources'] = {};
|
||||
const settings = this.userSourcesService.getSourceSettings(user, sourceId);
|
||||
|
||||
for (const pluginId in plugins) {
|
||||
const plugin = plugins[pluginId];
|
||||
|
||||
if (!plugin) continue;
|
||||
|
||||
const settings = this.userSourcesService.getSourceSettings(
|
||||
user,
|
||||
pluginId,
|
||||
);
|
||||
|
||||
if (!settings) continue;
|
||||
|
||||
const videoStream = await plugin.getMovieStream(tmdbId, {
|
||||
settings,
|
||||
token,
|
||||
});
|
||||
|
||||
if (!videoStream) continue;
|
||||
|
||||
sources[pluginId] = videoStream;
|
||||
if (!settings) {
|
||||
throw new BadRequestException('Source configuration not found');
|
||||
}
|
||||
|
||||
const streams = await plugin.getMovieStreams(tmdbId, {
|
||||
settings,
|
||||
token,
|
||||
});
|
||||
|
||||
return {
|
||||
sources,
|
||||
streams,
|
||||
};
|
||||
|
||||
// const plugins = await this.sourcesService.getPlugins();
|
||||
// const streams: VideoStreamListDto['streams'] = [];
|
||||
|
||||
// for (const pluginId in plugins) {
|
||||
// const plugin = plugins[pluginId];
|
||||
|
||||
// if (!plugin) continue;
|
||||
|
||||
// const settings = this.userSourcesService.getSourceSettings(
|
||||
// user,
|
||||
// pluginId,
|
||||
// );
|
||||
|
||||
// if (!settings) continue;
|
||||
|
||||
// const videoStream = await plugin.getMovieStreams(tmdbId, {
|
||||
// settings,
|
||||
// token,
|
||||
// });
|
||||
|
||||
// if (!videoStream) continue;
|
||||
|
||||
// streams[pluginId] = videoStream;
|
||||
// }
|
||||
|
||||
// return {
|
||||
// streams,
|
||||
// };
|
||||
}
|
||||
|
||||
@ApiTags('movies')
|
||||
@@ -148,14 +165,17 @@ export class SourcesController {
|
||||
type: VideoStreamDto,
|
||||
})
|
||||
async getMovieStream(
|
||||
@Param('sourceId') sourceId: string,
|
||||
@Param('tmdbId') tmdbId: string,
|
||||
@Param('sourceId') sourceId: string,
|
||||
@Query('key') key: string,
|
||||
@GetUser() user: User,
|
||||
@GetAuthToken() token: string,
|
||||
@Body() config: PlaybackConfigDto,
|
||||
): Promise<VideoStreamDto> {
|
||||
if (!user) {
|
||||
throw new UnauthorizedException();
|
||||
const plugin = this.sourcesService.getPlugin(sourceId);
|
||||
|
||||
if (!plugin) {
|
||||
throw new NotFoundException('Plugin not found');
|
||||
}
|
||||
|
||||
const settings = this.userSourcesService.getSourceSettings(user, sourceId);
|
||||
@@ -164,8 +184,9 @@ export class SourcesController {
|
||||
throw new BadRequestException('Source configuration not found');
|
||||
}
|
||||
|
||||
return this.sourcesService.getPlugin(sourceId)?.getMovieStream(
|
||||
return plugin.getMovieStream(
|
||||
tmdbId,
|
||||
key || '',
|
||||
{
|
||||
settings,
|
||||
token,
|
||||
@@ -175,7 +196,7 @@ export class SourcesController {
|
||||
}
|
||||
|
||||
@ApiTags('movies')
|
||||
@All('movies/:tmdbId/sources/:sourceId/stream/*')
|
||||
@All('movies/:tmdbId/sources/:sourceId/stream/proxy/*')
|
||||
async getMovieStreamProxy(
|
||||
@Param() params: any,
|
||||
@Req() req: Request,
|
||||
|
||||
@@ -14,6 +14,8 @@ import {
|
||||
Subtitles,
|
||||
ValidationResponse,
|
||||
VideoStream,
|
||||
VideoStreamCandidate,
|
||||
VideoStreamProperty,
|
||||
} from 'plugins/plugin-types';
|
||||
import { DeviceProfileDto } from './device-profile.dto';
|
||||
|
||||
@@ -98,23 +100,6 @@ export class ValidationResponsekDto implements ValidationResponse {
|
||||
replace: Record<string, any>;
|
||||
}
|
||||
|
||||
export class SourceDto {
|
||||
@ApiProperty({ example: '/path/to/stream' })
|
||||
uri: string;
|
||||
}
|
||||
|
||||
export class SourceListDto {
|
||||
@ApiProperty({
|
||||
type: 'object',
|
||||
additionalProperties: { $ref: getSchemaPath(SourceDto) },
|
||||
example: {
|
||||
source1: { uri: '/path/to/stream' },
|
||||
source2: { uri: '/path/to/other/stream' },
|
||||
},
|
||||
})
|
||||
sources: Record<string, VideoStreamDto>;
|
||||
}
|
||||
|
||||
export class AudioStreamDto implements AudioStream {
|
||||
@ApiProperty()
|
||||
index: number;
|
||||
@@ -141,6 +126,9 @@ export class QualityDto implements Quality {
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
codec: string | undefined;
|
||||
|
||||
@ApiProperty()
|
||||
original: boolean;
|
||||
}
|
||||
|
||||
export class SubtitlesDto implements Subtitles {
|
||||
@@ -157,7 +145,34 @@ export class SubtitlesDto implements Subtitles {
|
||||
codec: string | undefined;
|
||||
}
|
||||
|
||||
export class VideoStreamDto implements VideoStream {
|
||||
export class VideoStreamPropertyDto implements VideoStreamProperty {
|
||||
@ApiProperty()
|
||||
label: string;
|
||||
|
||||
@ApiProperty({
|
||||
oneOf: [{ type: 'string' }, { type: 'number' }],
|
||||
})
|
||||
value: string | number;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
formatted: string | undefined;
|
||||
}
|
||||
|
||||
export class VideoStreamCandidateDto implements VideoStreamCandidate {
|
||||
@ApiProperty()
|
||||
key: string;
|
||||
|
||||
@ApiProperty()
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ type: [VideoStreamPropertyDto] })
|
||||
properties: VideoStreamPropertyDto[];
|
||||
}
|
||||
|
||||
export class VideoStreamDto
|
||||
extends VideoStreamCandidateDto
|
||||
implements VideoStream
|
||||
{
|
||||
@ApiProperty()
|
||||
uri: string;
|
||||
|
||||
@@ -177,7 +192,7 @@ export class VideoStreamDto implements VideoStream {
|
||||
qualities: QualityDto[];
|
||||
|
||||
@ApiProperty()
|
||||
quality: number;
|
||||
qualityIndex: number;
|
||||
|
||||
@ApiProperty({ type: [SubtitlesDto] })
|
||||
subtitles: SubtitlesDto[];
|
||||
@@ -203,3 +218,10 @@ export class PlaybackConfigDto implements PlaybackConfig {
|
||||
@ApiPropertyOptional({ example: 'en', required: false })
|
||||
defaultLanguage: string | undefined;
|
||||
}
|
||||
|
||||
export class VideoStreamListDto {
|
||||
@ApiProperty({
|
||||
type: [VideoStreamCandidateDto],
|
||||
})
|
||||
streams: VideoStreamCandidateDto[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user