mirror of
https://github.com/aleksilassila/reiverr.git
synced 2026-04-17 21:53:12 +02:00
docs: document plugin types
This commit is contained in:
@@ -130,6 +130,7 @@ The roadmap includes plans to support the following platforms in the future:
|
||||
To create the first user account, you can log in with any credentials and an admin account will be created.
|
||||
Alternatively, you can define the admin username and password using environment variables,
|
||||
as seen in the Docker Compose example. A new admin account is only created if there are no previous accounts with the same name.
|
||||
|
||||
To get access to media playback, connect to plugins by adding media sources in the settings.
|
||||
Additional plugins can be installed by dropping them in the `/plugins` folder.
|
||||
To get most out of Reiverr, it is recommended to also connect to TMDB.
|
||||
@@ -195,9 +196,9 @@ the built-in plugins. Couple of things to note:
|
||||
- Plugins have to implement (and default export) the `PluginProvider`
|
||||
class that can be imported from the [](backend/packages/reiverr-plugin)
|
||||
package.
|
||||
- To install
|
||||
[the github npm registry package](https://github.com/aleksilassila/reiverr/pkgs/npm/reiverr-plugin), follow the steps outlined here:
|
||||
[Working with the npm registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-npm-registry#installing-a-package)
|
||||
- To install the
|
||||
[reiverr-plugin package](https://github.com/aleksilassila/reiverr/pkgs/npm/reiverr-plugin) from Github's npm registry, follow the steps outlined here:
|
||||
[Working with the Github npm registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-npm-registry#installing-a-package)
|
||||
(you'll need a .npmrc file)
|
||||
- The built-in packages use npm workspaces, which doesn't work if
|
||||
you're developing outisde of the repository
|
||||
@@ -209,8 +210,6 @@ the built-in plugins. Couple of things to note:
|
||||
|
||||
- https://developer.themoviedb.org/reference
|
||||
- https://api.jellyfin.org/
|
||||
- https://sonarr.tv/docs/api/
|
||||
- https://radarr.video/docs/api/
|
||||
- https://github.com/jellyfin/jellyfin-web
|
||||
- Network tab in the browser in Jellyfin, Radarr & Sonarr web UIs
|
||||
|
||||
|
||||
@@ -20,13 +20,11 @@ import {
|
||||
*
|
||||
* @see SourceProvider
|
||||
*/
|
||||
export class PluginProvider {
|
||||
export abstract class PluginProvider {
|
||||
/**
|
||||
* @returns {SourceProvider[]} A list of SourceProvider instances that the plugin provides.
|
||||
*/
|
||||
static getPlugins(): SourceProvider[] {
|
||||
return [];
|
||||
}
|
||||
abstract getPlugins(): SourceProvider[];
|
||||
}
|
||||
|
||||
export class SettingsManager {
|
||||
@@ -41,21 +39,48 @@ export class SettingsManager {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* SourceProvider is a class that provides a set of methods to interact with a streaming source.
|
||||
*
|
||||
* Important distinction between SourceProvider and MediaSource:
|
||||
* An user doesn't directly add a SourceProvider to their account, but instead users can configure
|
||||
* `MediaSources`. MediaSource is essentially all the user-specific configuration that SourceProvider
|
||||
* needs to function. This way different users can have different configurations for the same
|
||||
* SourceProvider - for example, two users can use the same JellyfinPlugin (JellyfinSourceProvider)
|
||||
* to access two different Jellyfin servers, because they access the provider with their own
|
||||
* MediaSource instances.
|
||||
*
|
||||
* UserContext is used to pass the user-specific configuration to the SourceProvider methods.
|
||||
*
|
||||
* @see UserContext
|
||||
* @see PluginProvider
|
||||
*/
|
||||
export abstract class SourceProvider {
|
||||
abstract name: string;
|
||||
|
||||
settingsManager: SettingsManager = new SettingsManager();
|
||||
|
||||
/**
|
||||
* Returns an index of all movies available in the source.
|
||||
*/
|
||||
getMovieCatalogue?: (
|
||||
context: UserContext,
|
||||
pagination: PaginationParams,
|
||||
) => Promise<PaginatedResponse<IndexItem>>;
|
||||
|
||||
/**
|
||||
* Returns an index of all episodes available in the source.
|
||||
*/
|
||||
getEpisodeCatalogue?: (
|
||||
context: UserContext,
|
||||
pagination: PaginationParams,
|
||||
) => Promise<PaginatedResponse<IndexItem>>;
|
||||
|
||||
/**
|
||||
* Returns a list of stream candidates for a movie that the user can choose to stream from.
|
||||
*
|
||||
* @see StreamCandidate
|
||||
*/
|
||||
getMovieStreams?: (
|
||||
tmdbId: string,
|
||||
metadata: MovieMetadata,
|
||||
@@ -63,6 +88,11 @@ export abstract class SourceProvider {
|
||||
config?: PlaybackConfig,
|
||||
) => Promise<{ candidates: StreamCandidate[] }>;
|
||||
|
||||
/**
|
||||
* Returns a list of stream candidates for an episode that the user can choose to stream from.
|
||||
*
|
||||
* @see StreamCandidate
|
||||
*/
|
||||
getEpisodeStreams?: (
|
||||
tmdbId: string,
|
||||
metadata: EpisodeMetadata,
|
||||
@@ -70,6 +100,11 @@ export abstract class SourceProvider {
|
||||
config?: PlaybackConfig,
|
||||
) => Promise<{ candidates: StreamCandidate[] }>;
|
||||
|
||||
/**
|
||||
* Returns a specific stream for a movie that the user can stream from.
|
||||
*
|
||||
* @see Stream
|
||||
*/
|
||||
getMovieStream?: (
|
||||
tmdbId: string,
|
||||
metadata: MovieMetadata,
|
||||
@@ -78,6 +113,11 @@ export abstract class SourceProvider {
|
||||
config?: PlaybackConfig,
|
||||
) => Promise<Stream | undefined>;
|
||||
|
||||
/**
|
||||
* Returns a specific stream for an episode that the user can stream from.
|
||||
*
|
||||
* @see Stream
|
||||
*/
|
||||
getEpisodeStream?: (
|
||||
tmdbId: string,
|
||||
metadata: EpisodeMetadata,
|
||||
@@ -86,6 +126,14 @@ export abstract class SourceProvider {
|
||||
config?: PlaybackConfig,
|
||||
) => Promise<Stream | undefined>;
|
||||
|
||||
/**
|
||||
* This method will be called when the client makes a request to the provider's
|
||||
* proxy endpoint (e.g. /api/proxy/:providerName/:path). This can be used to
|
||||
* relay video streams and subtitles to the client, by making a request to an
|
||||
* external service and then returning the response to the client. Ideally,
|
||||
* the stream url pointed to by a `Stream` object should use the proxy endpoint
|
||||
* so that the plugin can handle the video requests here.
|
||||
*/
|
||||
proxyHandler?: (
|
||||
req: any,
|
||||
res: any,
|
||||
|
||||
@@ -21,13 +21,35 @@ export type SourceProviderSettingsTemplate = Record<
|
||||
SourceProviderSettingsLink | SourceProviderSettingsInput
|
||||
>;
|
||||
|
||||
/**
|
||||
* UserContext is used to pass the user-specific configuration to the SourceProvider methods.
|
||||
*/
|
||||
export type UserContext = {
|
||||
/**
|
||||
* An id unique to each Reiverr user
|
||||
*/
|
||||
userId: string;
|
||||
|
||||
/**
|
||||
* The access token of the user that can be used to authenticate requests to the backend
|
||||
* (e.g. proxy requests)
|
||||
*/
|
||||
token: string;
|
||||
/**
|
||||
* The id of the MediaSource instance that the user is using to access the SourceProvider
|
||||
*/
|
||||
sourceId: string;
|
||||
|
||||
/**
|
||||
* @see SourceProviderSettings
|
||||
*/
|
||||
settings: SourceProviderSettings;
|
||||
};
|
||||
|
||||
/**
|
||||
* The settings/configuration defined in the `SourceProvider` and
|
||||
* provided by the user's MediaSource instance
|
||||
*/
|
||||
export type SourceProviderSettings = Record<string, any>;
|
||||
|
||||
export type ValidationResponse = {
|
||||
@@ -59,14 +81,44 @@ export type Subtitles = {
|
||||
};
|
||||
|
||||
export type StreamProperty = {
|
||||
/**
|
||||
* The label of the property
|
||||
* @example "Resolution"
|
||||
*/
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* Used for sorting and filtering, or displayed if `formatted` is not provided.
|
||||
* @example 1080
|
||||
*/
|
||||
value: string | number;
|
||||
|
||||
/**
|
||||
* The formatted value of the property
|
||||
* @example "1080p"
|
||||
*/
|
||||
formatted: string | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* `StreamCandidate` represents a stream that can be played by the user,
|
||||
* and contains all the information that is presented to the user in the
|
||||
* stream selection UI.
|
||||
*/
|
||||
export type StreamCandidate = {
|
||||
/**
|
||||
* Unique id for the stream, that can be used to later stream the specific stream.
|
||||
*/
|
||||
key: string;
|
||||
|
||||
/**
|
||||
* Title of the stream, presented to the user.
|
||||
*/
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* A list of properties that are shown to the user in the stream selection UI.
|
||||
*/
|
||||
properties: StreamProperty[];
|
||||
};
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
} from './utils';
|
||||
|
||||
export default class JellyfinPluginProvider extends PluginProvider {
|
||||
static getPlugins(): SourceProvider[] {
|
||||
getPlugins(): SourceProvider[] {
|
||||
return [new JellyfinProvider()];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
} from './utils';
|
||||
|
||||
export default class TorrentPluginsProvider extends PluginProvider {
|
||||
static getPlugins(): SourceProvider[] {
|
||||
getPlugins(): SourceProvider[] {
|
||||
return [new TorrentProvider()];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,10 +57,7 @@ type MediaSourceConnection = {
|
||||
|
||||
@Injectable()
|
||||
export class ServiceOwnershipValidator implements CanActivate {
|
||||
constructor(
|
||||
private mediaSourcesService: MediaSourcesService,
|
||||
private sourceProvidersService: SourceProvidersService,
|
||||
) {}
|
||||
constructor(private mediaSourcesService: MediaSourcesService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
|
||||
@@ -42,7 +42,7 @@ export class SourceProvidersService {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const pluginModule = require(pluginPath);
|
||||
const provider: typeof PluginProvider = pluginModule.default;
|
||||
const provider: PluginProvider = new pluginModule.default();
|
||||
provider.getPlugins().forEach((plugin) => {
|
||||
plugins[plugin.name] = plugin;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user