mirror of
https://github.com/aleksilassila/reiverr.git
synced 2026-04-18 05:53:14 +02:00
feat: Experimental movie playback through plugins
This commit is contained in:
@@ -6,7 +6,9 @@ import {
|
||||
Get,
|
||||
NotFoundException,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
Post,
|
||||
Query,
|
||||
Req,
|
||||
Res,
|
||||
UnauthorizedException,
|
||||
@@ -14,22 +16,24 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { SourcePluginsService } from './source-plugins.service';
|
||||
import { AuthGuard, GetUser } from 'src/auth/auth.guard';
|
||||
import { AuthGuard, GetAuthToken, GetUser } from 'src/auth/auth.guard';
|
||||
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,
|
||||
ValidationResponsekDto,
|
||||
SourceListDto as VideoStreamListDto,
|
||||
ValidationResponsekDto as ValidationResponseDto,
|
||||
VideoStreamDto,
|
||||
} from './source-plugins.dto';
|
||||
|
||||
export const JELLYFIN_DEVICE_ID = 'Reiverr Client';
|
||||
|
||||
@ApiTags('sources')
|
||||
@Controller('sources')
|
||||
@Controller()
|
||||
@UseGuards(AuthGuard)
|
||||
export class SourcesController {
|
||||
constructor(
|
||||
@@ -37,7 +41,8 @@ export class SourcesController {
|
||||
private userSourcesService: UserSourcesService,
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
@ApiTags('sources')
|
||||
@Get('sources')
|
||||
@ApiOkResponse({
|
||||
description: 'All source plugins found',
|
||||
type: String,
|
||||
@@ -45,11 +50,12 @@ export class SourcesController {
|
||||
})
|
||||
async getSourcePlugins() {
|
||||
return this.sourcesService
|
||||
.getLoadedPlugins()
|
||||
.getPlugins()
|
||||
.then((plugins) => Object.keys(plugins));
|
||||
}
|
||||
|
||||
@Get(':sourceId/settings/template')
|
||||
@ApiTags('sources')
|
||||
@Get('sources/:sourceId/settings/template')
|
||||
@ApiOkResponse({
|
||||
description: 'Source settings template',
|
||||
type: PluginSettingsTemplateDto,
|
||||
@@ -70,16 +76,17 @@ export class SourcesController {
|
||||
};
|
||||
}
|
||||
|
||||
@Post(':sourceId/settings/validate')
|
||||
@ApiTags('sources')
|
||||
@Post('sources/:sourceId/settings/validate')
|
||||
@ApiOkResponse({
|
||||
description: 'Source settings validation',
|
||||
type: ValidationResponsekDto,
|
||||
type: ValidationResponseDto,
|
||||
})
|
||||
async validateSourceSettings(
|
||||
@GetUser() callerUser: User,
|
||||
@Param('sourceId') sourceId: string,
|
||||
@Body() settings: PluginSettingsDto,
|
||||
): Promise<ValidationResponsekDto> {
|
||||
): Promise<ValidationResponseDto> {
|
||||
const plugin = this.sourcesService.getPlugin(sourceId);
|
||||
|
||||
if (!plugin) {
|
||||
@@ -89,12 +96,64 @@ export class SourcesController {
|
||||
return plugin.validateSettings(settings.settings);
|
||||
}
|
||||
|
||||
@Get(':sourceId/movies/:tmdbId/stream')
|
||||
@ApiTags('movies')
|
||||
@Get('movies/:tmdbId/sources')
|
||||
@ApiOkResponse({
|
||||
description: 'Movie sources',
|
||||
type: VideoStreamListDto,
|
||||
})
|
||||
async getMovieSources(
|
||||
@Param('tmdbId') tmdbId: string,
|
||||
@GetUser() user: User,
|
||||
@GetAuthToken() token: string,
|
||||
): Promise<VideoStreamListDto> {
|
||||
if (!user) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
const plugins = await this.sourcesService.getPlugins();
|
||||
const sources: VideoStreamListDto['sources'] = {};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return {
|
||||
sources,
|
||||
};
|
||||
}
|
||||
|
||||
@ApiTags('movies')
|
||||
@Post('movies/:tmdbId/sources/:sourceId/stream')
|
||||
@ApiOkResponse({
|
||||
description: 'Movie stream',
|
||||
type: VideoStreamDto,
|
||||
})
|
||||
async getMovieStream(
|
||||
@Param('sourceId') sourceId: string,
|
||||
@Param('tmdbId') tmdbId: string,
|
||||
@GetUser() user: User,
|
||||
) {
|
||||
@GetAuthToken() token: string,
|
||||
@Body() config: PlaybackConfigDto,
|
||||
): Promise<VideoStreamDto> {
|
||||
if (!user) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
@@ -105,12 +164,18 @@ export class SourcesController {
|
||||
throw new BadRequestException('Source configuration not found');
|
||||
}
|
||||
|
||||
return this.sourcesService
|
||||
.getPlugin(sourceId)
|
||||
?.getMovieStream(tmdbId, settings);
|
||||
return this.sourcesService.getPlugin(sourceId)?.getMovieStream(
|
||||
tmdbId,
|
||||
{
|
||||
settings,
|
||||
token,
|
||||
},
|
||||
config,
|
||||
);
|
||||
}
|
||||
|
||||
@All(':sourceId/movies/:tmdbId/stream/*')
|
||||
@ApiTags('movies')
|
||||
@All('movies/:tmdbId/sources/:sourceId/stream/*')
|
||||
async getMovieStreamProxy(
|
||||
@Param() params: any,
|
||||
@Req() req: Request,
|
||||
@@ -119,6 +184,9 @@ export class SourcesController {
|
||||
) {
|
||||
const sourceId = params.sourceId;
|
||||
const settings = this.userSourcesService.getSourceSettings(user, sourceId);
|
||||
|
||||
if (!settings) throw new UnauthorizedException();
|
||||
|
||||
const { url, headers } = this.sourcesService
|
||||
.getPlugin(sourceId)
|
||||
?.handleProxy(
|
||||
@@ -133,7 +201,7 @@ export class SourcesController {
|
||||
method: req.method || 'GET',
|
||||
headers: {
|
||||
...headers,
|
||||
Authorization: `MediaBrowser DeviceId="${JELLYFIN_DEVICE_ID}", Token="${settings.apiKey}"`,
|
||||
// Authorization: `MediaBrowser DeviceId="${JELLYFIN_DEVICE_ID}", Token="${settings.apiKey}"`,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user