mirror of
https://github.com/aleksilassila/reiverr.git
synced 2026-04-21 00:05:13 +02:00
chore: setup npm workspaces and plugin types as package
This commit is contained in:
2
backend/plugins/reiverr-plugin/.gitattributes
vendored
Normal file
2
backend/plugins/reiverr-plugin/.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto eol=lf
|
||||
37
backend/plugins/reiverr-plugin/.gitignore
vendored
Normal file
37
backend/plugins/reiverr-plugin/.gitignore
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
.vercel
|
||||
.output
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
/config/*.sqlite
|
||||
/dist
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
19
backend/plugins/reiverr-plugin/package.json
Normal file
19
backend/plugins/reiverr-plugin/package.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@aleksilassila/reiverr-plugin",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/index",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"publish:patch": "npm version patch && npm publish",
|
||||
"publish:minor": "npm version minor && npm publish",
|
||||
"publish:major": "npm version major && npm publish"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {},
|
||||
"types": "./dist/index.d.ts",
|
||||
"description": "",
|
||||
"publishConfig": {
|
||||
"registry": "https://npm.pkg.github.com/"
|
||||
}
|
||||
}
|
||||
210
backend/plugins/reiverr-plugin/src/device-profile.ts
Normal file
210
backend/plugins/reiverr-plugin/src/device-profile.ts
Normal file
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* A MediaBrowser.Model.Dlna.DeviceProfile represents a set of metadata which determines which content a certain device is able to play.
|
||||
* <br />
|
||||
* Specifically, it defines the supported <see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.ContainerProfiles">containers</see> and
|
||||
* <see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.CodecProfiles">codecs</see> (video and/or audio, including codec profiles and levels)
|
||||
* the device is able to direct play (without transcoding or remuxing),
|
||||
* as well as which <see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.TranscodingProfiles">containers/codecs to transcode to</see> in case it isn't.
|
||||
*/
|
||||
export interface DeviceProfile {
|
||||
/** Gets or sets the name of this device profile. User profiles must have a unique name. */
|
||||
Name?: string | null;
|
||||
/**
|
||||
* Gets or sets the unique internal identifier.
|
||||
* @format uuid
|
||||
*/
|
||||
Id?: string | null;
|
||||
/**
|
||||
* Gets or sets the maximum allowed bitrate for all streamed content.
|
||||
* @format int32
|
||||
*/
|
||||
MaxStreamingBitrate?: number | null;
|
||||
/**
|
||||
* Gets or sets the maximum allowed bitrate for statically streamed content (= direct played files).
|
||||
* @format int32
|
||||
*/
|
||||
MaxStaticBitrate?: number | null;
|
||||
/**
|
||||
* Gets or sets the maximum allowed bitrate for transcoded music streams.
|
||||
* @format int32
|
||||
*/
|
||||
MusicStreamingTranscodingBitrate?: number | null;
|
||||
/**
|
||||
* Gets or sets the maximum allowed bitrate for statically streamed (= direct played) music files.
|
||||
* @format int32
|
||||
*/
|
||||
MaxStaticMusicBitrate?: number | null;
|
||||
/** Gets or sets the direct play profiles. */
|
||||
DirectPlayProfiles?: DirectPlayProfile[];
|
||||
/** Gets or sets the transcoding profiles. */
|
||||
TranscodingProfiles?: TranscodingProfile[];
|
||||
/** Gets or sets the container profiles. Failing to meet these optional conditions causes transcoding to occur. */
|
||||
ContainerProfiles?: ContainerProfile[];
|
||||
/** Gets or sets the codec profiles. */
|
||||
CodecProfiles?: CodecProfile[];
|
||||
/** Gets or sets the subtitle profiles. */
|
||||
SubtitleProfiles?: SubtitleProfile[];
|
||||
}
|
||||
|
||||
/** Defines the MediaBrowser.Model.Dlna.DirectPlayProfile. */
|
||||
export interface DirectPlayProfile {
|
||||
/** Gets or sets the container. */
|
||||
Container?: string;
|
||||
/** Gets or sets the audio codec. */
|
||||
AudioCodec?: string | null;
|
||||
/** Gets or sets the video codec. */
|
||||
VideoCodec?: string | null;
|
||||
/** Gets or sets the Dlna profile type. */
|
||||
Type?: 'Audio' | 'Video' | 'Photo' | 'Subtitle' | 'Lyric';
|
||||
}
|
||||
|
||||
/** A class for transcoding profile information. */
|
||||
export interface TranscodingProfile {
|
||||
/** Gets or sets the container. */
|
||||
Container?: string;
|
||||
/** Gets or sets the DLNA profile type. */
|
||||
Type?: 'Audio' | 'Video' | 'Photo' | 'Subtitle' | 'Lyric';
|
||||
/** Gets or sets the video codec. */
|
||||
VideoCodec?: string;
|
||||
/** Gets or sets the audio codec. */
|
||||
AudioCodec?: string;
|
||||
/**
|
||||
* Media streaming protocol.
|
||||
* Lowercase for backwards compatibility.
|
||||
*/
|
||||
Protocol?: 'http' | 'hls';
|
||||
/**
|
||||
* Gets or sets a value indicating whether the content length should be estimated.
|
||||
* @default false
|
||||
*/
|
||||
EstimateContentLength?: boolean;
|
||||
/**
|
||||
* Gets or sets a value indicating whether M2TS mode is enabled.
|
||||
* @default false
|
||||
*/
|
||||
EnableMpegtsM2TsMode?: boolean;
|
||||
/**
|
||||
* Gets or sets the transcoding seek info mode.
|
||||
* @default "Auto"
|
||||
*/
|
||||
TranscodeSeekInfo?: 'Auto' | 'Bytes';
|
||||
/**
|
||||
* Gets or sets a value indicating whether timestamps should be copied.
|
||||
* @default false
|
||||
*/
|
||||
CopyTimestamps?: boolean;
|
||||
/**
|
||||
* Gets or sets the encoding context.
|
||||
* @default "Streaming"
|
||||
*/
|
||||
Context?: 'Streaming' | 'Static';
|
||||
/**
|
||||
* Gets or sets a value indicating whether subtitles are allowed in the manifest.
|
||||
* @default false
|
||||
*/
|
||||
EnableSubtitlesInManifest?: boolean;
|
||||
/** Gets or sets the maximum audio channels. */
|
||||
MaxAudioChannels?: string | null;
|
||||
/**
|
||||
* Gets or sets the minimum amount of segments.
|
||||
* @format int32
|
||||
* @default 0
|
||||
*/
|
||||
MinSegments?: number;
|
||||
/**
|
||||
* Gets or sets the segment length.
|
||||
* @format int32
|
||||
* @default 0
|
||||
*/
|
||||
SegmentLength?: number;
|
||||
/**
|
||||
* Gets or sets a value indicating whether breaking the video stream on non-keyframes is supported.
|
||||
* @default false
|
||||
*/
|
||||
BreakOnNonKeyFrames?: boolean;
|
||||
/** Gets or sets the profile conditions. */
|
||||
Conditions?: ProfileCondition[];
|
||||
/**
|
||||
* Gets or sets a value indicating whether variable bitrate encoding is supported.
|
||||
* @default true
|
||||
*/
|
||||
EnableAudioVbrEncoding?: boolean;
|
||||
}
|
||||
|
||||
export interface ProfileCondition {
|
||||
Condition?:
|
||||
| 'Equals'
|
||||
| 'NotEquals'
|
||||
| 'LessThanEqual'
|
||||
| 'GreaterThanEqual'
|
||||
| 'EqualsAny';
|
||||
Property?:
|
||||
| 'AudioChannels'
|
||||
| 'AudioBitrate'
|
||||
| 'AudioProfile'
|
||||
| 'Width'
|
||||
| 'Height'
|
||||
| 'Has64BitOffsets'
|
||||
| 'PacketLength'
|
||||
| 'VideoBitDepth'
|
||||
| 'VideoBitrate'
|
||||
| 'VideoFramerate'
|
||||
| 'VideoLevel'
|
||||
| 'VideoProfile'
|
||||
| 'VideoTimestamp'
|
||||
| 'IsAnamorphic'
|
||||
| 'RefFrames'
|
||||
| 'NumAudioStreams'
|
||||
| 'NumVideoStreams'
|
||||
| 'IsSecondaryAudio'
|
||||
| 'VideoCodecTag'
|
||||
| 'IsAvc'
|
||||
| 'IsInterlaced'
|
||||
| 'AudioSampleRate'
|
||||
| 'AudioBitDepth'
|
||||
| 'VideoRangeType';
|
||||
Value?: string | null;
|
||||
IsRequired?: boolean;
|
||||
}
|
||||
|
||||
/** Defines the MediaBrowser.Model.Dlna.ContainerProfile. */
|
||||
export interface ContainerProfile {
|
||||
/** Gets or sets the MediaBrowser.Model.Dlna.DlnaProfileType which this container must meet. */
|
||||
Type?: 'Audio' | 'Video' | 'Photo' | 'Subtitle' | 'Lyric';
|
||||
/** Gets or sets the list of MediaBrowser.Model.Dlna.ProfileCondition which this container will be applied to. */
|
||||
Conditions?: ProfileCondition[];
|
||||
/** Gets or sets the container(s) which this container must meet. */
|
||||
Container?: string | null;
|
||||
/** Gets or sets the sub container(s) which this container must meet. */
|
||||
SubContainer?: string | null;
|
||||
}
|
||||
|
||||
/** Defines the MediaBrowser.Model.Dlna.CodecProfile. */
|
||||
export interface CodecProfile {
|
||||
/** Gets or sets the MediaBrowser.Model.Dlna.CodecType which this container must meet. */
|
||||
Type?: 'Video' | 'VideoAudio' | 'Audio';
|
||||
/** Gets or sets the list of MediaBrowser.Model.Dlna.ProfileCondition which this profile must meet. */
|
||||
Conditions?: ProfileCondition[];
|
||||
/** Gets or sets the list of MediaBrowser.Model.Dlna.ProfileCondition to apply if this profile is met. */
|
||||
ApplyConditions?: ProfileCondition[];
|
||||
/** Gets or sets the codec(s) that this profile applies to. */
|
||||
Codec?: string | null;
|
||||
/** Gets or sets the container(s) which this profile will be applied to. */
|
||||
Container?: string | null;
|
||||
/** Gets or sets the sub-container(s) which this profile will be applied to. */
|
||||
SubContainer?: string | null;
|
||||
}
|
||||
|
||||
/** A class for subtitle profile information. */
|
||||
export interface SubtitleProfile {
|
||||
/** Gets or sets the format. */
|
||||
Format?: string | null;
|
||||
/** Gets or sets the delivery method. */
|
||||
Method?: 'Encode' | 'Embed' | 'External' | 'Hls' | 'Drop';
|
||||
/** Gets or sets the DIDL mode. */
|
||||
DidlMode?: string | null;
|
||||
/** Gets or sets the language. */
|
||||
Language?: string | null;
|
||||
/** Gets or sets the container. */
|
||||
Container?: string | null;
|
||||
}
|
||||
3
backend/plugins/reiverr-plugin/src/index.ts
Normal file
3
backend/plugins/reiverr-plugin/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './types';
|
||||
export * from './plugin';
|
||||
export * from './device-profile';
|
||||
83
backend/plugins/reiverr-plugin/src/plugin.ts
Normal file
83
backend/plugins/reiverr-plugin/src/plugin.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import {
|
||||
EpisodeMetadata,
|
||||
IndexItem,
|
||||
MovieMetadata,
|
||||
PaginatedResponse,
|
||||
PaginationParams,
|
||||
PlaybackConfig,
|
||||
SourceProviderSettingsTemplate,
|
||||
UserContext,
|
||||
ValidationResponse,
|
||||
Stream,
|
||||
StreamCandidate,
|
||||
} from './types';
|
||||
|
||||
export class PluginProvider {
|
||||
static getPlugins(): SourceProvider[] {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export class SettingsManager {
|
||||
getSettingsTemplate: () => SourceProviderSettingsTemplate = () => ({});
|
||||
|
||||
validateSettings: (
|
||||
settings: Record<string, any>,
|
||||
) => Promise<ValidationResponse> = async () => ({
|
||||
isValid: true,
|
||||
errors: {},
|
||||
replace: {},
|
||||
});
|
||||
}
|
||||
|
||||
export abstract class SourceProvider {
|
||||
abstract name: string;
|
||||
|
||||
settingsManager: SettingsManager = new SettingsManager();
|
||||
|
||||
getMovieCatalogue?: (
|
||||
context: UserContext,
|
||||
pagination: PaginationParams,
|
||||
) => Promise<PaginatedResponse<IndexItem>>;
|
||||
|
||||
getEpisodeCatalogue?: (
|
||||
context: UserContext,
|
||||
pagination: PaginationParams,
|
||||
) => Promise<PaginatedResponse<IndexItem>>;
|
||||
|
||||
getMovieStreams?: (
|
||||
tmdbId: string,
|
||||
metadata: MovieMetadata,
|
||||
context: UserContext,
|
||||
config?: PlaybackConfig,
|
||||
) => Promise<{ candidates: StreamCandidate[] }>;
|
||||
|
||||
getEpisodeStreams?: (
|
||||
tmdbId: string,
|
||||
metadata: EpisodeMetadata,
|
||||
context: UserContext,
|
||||
config?: PlaybackConfig,
|
||||
) => Promise<{ candidates: StreamCandidate[] }>;
|
||||
|
||||
getMovieStream?: (
|
||||
tmdbId: string,
|
||||
metadata: MovieMetadata,
|
||||
key: string,
|
||||
context: UserContext,
|
||||
config?: PlaybackConfig,
|
||||
) => Promise<Stream | undefined>;
|
||||
|
||||
getEpisodeStream?: (
|
||||
tmdbId: string,
|
||||
metadata: EpisodeMetadata,
|
||||
key: string,
|
||||
context: UserContext,
|
||||
config?: PlaybackConfig,
|
||||
) => Promise<Stream | undefined>;
|
||||
|
||||
proxyHandler?: (
|
||||
req: any,
|
||||
res: any,
|
||||
options: { context: UserContext; uri: string; targetUrl?: string },
|
||||
) => Promise<any>;
|
||||
}
|
||||
125
backend/plugins/reiverr-plugin/src/types.ts
Normal file
125
backend/plugins/reiverr-plugin/src/types.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import { DeviceProfile } from './device-profile';
|
||||
|
||||
export enum SourceProviderError {
|
||||
StreamNotFound = 'StreamNotFound',
|
||||
}
|
||||
|
||||
export type SourceProviderSettingsLink = {
|
||||
type: 'link';
|
||||
url: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export type SourceProviderSettingsInput = {
|
||||
type: 'string' | 'number' | 'boolean' | 'password';
|
||||
label: string;
|
||||
placeholder: string;
|
||||
};
|
||||
|
||||
export type SourceProviderSettingsTemplate = Record<
|
||||
string,
|
||||
SourceProviderSettingsLink | SourceProviderSettingsInput
|
||||
>;
|
||||
|
||||
export type UserContext = {
|
||||
userId: string;
|
||||
token: string;
|
||||
settings: SourceProviderSettings;
|
||||
};
|
||||
|
||||
export type SourceProviderSettings = Record<string, any>;
|
||||
|
||||
export type ValidationResponse = {
|
||||
isValid: boolean;
|
||||
errors: Record<string, string>;
|
||||
replace: Record<string, any>;
|
||||
};
|
||||
|
||||
export type AudioStream = {
|
||||
index: number;
|
||||
label: string;
|
||||
codec: string | undefined;
|
||||
bitrate: number | undefined;
|
||||
};
|
||||
|
||||
export type Quality = {
|
||||
index: number;
|
||||
bitrate: number;
|
||||
label: string;
|
||||
codec: string | undefined;
|
||||
original: boolean;
|
||||
};
|
||||
|
||||
export type Subtitles = {
|
||||
index: number;
|
||||
uri: string;
|
||||
label: string;
|
||||
codec: string | undefined;
|
||||
};
|
||||
|
||||
export type StreamProperty = {
|
||||
label: string;
|
||||
value: string | number;
|
||||
formatted: string | undefined;
|
||||
};
|
||||
|
||||
export type StreamCandidate = {
|
||||
key: string;
|
||||
title: string;
|
||||
properties: StreamProperty[];
|
||||
};
|
||||
|
||||
export type Stream = StreamCandidate & {
|
||||
uri: string;
|
||||
directPlay: boolean;
|
||||
progress: number;
|
||||
duration: number;
|
||||
audioStreams: AudioStream[];
|
||||
audioStreamIndex: number;
|
||||
qualities: Quality[];
|
||||
qualityIndex: number;
|
||||
subtitles: Subtitles[];
|
||||
};
|
||||
|
||||
export type PlaybackConfig = {
|
||||
bitrate: number | undefined;
|
||||
audioStreamIndex: number | undefined;
|
||||
progress: number | undefined;
|
||||
deviceProfile: DeviceProfile | undefined;
|
||||
defaultLanguage: string | undefined;
|
||||
};
|
||||
|
||||
export type IndexItem = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type PaginatedResponse<T> = {
|
||||
total: number;
|
||||
page: number;
|
||||
itemsPerPage: number;
|
||||
items: T[];
|
||||
};
|
||||
|
||||
export type PaginationParams = {
|
||||
page: number;
|
||||
itemsPerPage: number;
|
||||
};
|
||||
|
||||
interface Metadata {
|
||||
tmdbId?: string;
|
||||
imdbId?: string;
|
||||
year?: number;
|
||||
}
|
||||
|
||||
export interface MovieMetadata extends Metadata {
|
||||
title: string;
|
||||
runtime?: number;
|
||||
}
|
||||
|
||||
export interface EpisodeMetadata extends Metadata {
|
||||
series: string;
|
||||
season: number;
|
||||
episode: number;
|
||||
episodeRuntime?: number;
|
||||
seasonEpisodes?: number;
|
||||
}
|
||||
20
backend/plugins/reiverr-plugin/tsconfig.json
Normal file
20
backend/plugins/reiverr-plugin/tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"target": "ES2021",
|
||||
"sourceMap": true,
|
||||
"incremental": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": false,
|
||||
"lib": ["es6"]
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user