Merge branch 'develop' of https://github.com/sct/overseerr into jellyfin-support

This commit is contained in:
Juan D. Jara
2021-09-27 02:24:30 +02:00
411 changed files with 35232 additions and 20531 deletions

View File

@@ -1,19 +1,23 @@
import { randomUUID } from 'crypto';
import fs from 'fs';
import path from 'path';
import { merge } from 'lodash';
import { v4 as uuidv4 } from 'uuid';
import { Permission } from './permissions';
import path from 'path';
import webpush from 'web-push';
import { MediaServerType } from '../constants/server';
import { Permission } from './permissions';
export interface Library {
id: string;
name: string;
enabled: boolean;
type: 'show' | 'movie';
lastScan?: number;
}
export interface Region {
iso_3166_1: string;
english_name: string;
name?: string;
}
export interface Language {
@@ -29,6 +33,7 @@ export interface PlexSettings {
port: number;
useSsl?: boolean;
libraries: Library[];
webAppUrl?: string;
}
export interface JellyfinSettings {
@@ -38,7 +43,7 @@ export interface JellyfinSettings {
serverId: string;
}
interface DVRSettings {
export interface DVRSettings {
id: number;
name: string;
hostname: string;
@@ -49,6 +54,7 @@ interface DVRSettings {
activeProfileId: number;
activeProfileName: string;
activeDirectory: string;
tags: number[];
is4k: boolean;
isDefault: boolean;
externalUrl?: string;
@@ -66,21 +72,35 @@ export interface SonarrSettings extends DVRSettings {
activeAnimeDirectory?: string;
activeAnimeLanguageProfileId?: number;
activeLanguageProfileId?: number;
animeTags?: number[];
enableSeasonFolders: boolean;
}
interface Quota {
quotaLimit?: number;
quotaDays?: number;
}
export interface MainSettings {
apiKey: string;
applicationTitle: string;
applicationUrl: string;
csrfProtection: boolean;
cacheImages: boolean;
defaultPermissions: number;
defaultQuotas: {
movie: Quota;
tv: Quota;
};
hideAvailable: boolean;
localLogin: boolean;
newPlexLogin: boolean;
region: string;
originalLanguage: string;
trustProxy: boolean;
mediaServerType: number;
partialRequestsEnabled: boolean;
locale: string;
}
interface PublicSettings {
@@ -89,6 +109,7 @@ interface PublicSettings {
interface FullPublicSettings extends PublicSettings {
applicationTitle: string;
applicationUrl: string;
hideAvailable: boolean;
localLogin: boolean;
movie4kEnabled: boolean;
@@ -98,15 +119,23 @@ interface FullPublicSettings extends PublicSettings {
mediaServerType: number;
jellyfinHost?: string;
jellyfinServerName?: string;
partialRequestsEnabled: boolean;
cacheImages: boolean;
vapidPublic: string;
enablePushRegistration: boolean;
locale: string;
emailEnabled: boolean;
}
export interface NotificationAgentConfig {
enabled: boolean;
types: number;
types?: number;
options: Record<string, unknown>;
}
export interface NotificationAgentDiscord extends NotificationAgentConfig {
options: {
botUsername?: string;
botAvatarUrl?: string;
webhookUrl: string;
};
}
@@ -123,15 +152,27 @@ export interface NotificationAgentEmail extends NotificationAgentConfig {
smtpHost: string;
smtpPort: number;
secure: boolean;
ignoreTls: boolean;
requireTls: boolean;
authUser?: string;
authPass?: string;
allowSelfSigned: boolean;
senderName: string;
pgpPrivateKey?: string;
pgpPassword?: string;
};
}
export interface NotificationAgentLunaSea extends NotificationAgentConfig {
options: {
webhookUrl: string;
profileName?: string;
};
}
export interface NotificationAgentTelegram extends NotificationAgentConfig {
options: {
botUsername?: string;
botAPI: string;
chatId: string;
sendSilently: boolean;
@@ -148,7 +189,6 @@ export interface NotificationAgentPushover extends NotificationAgentConfig {
options: {
accessToken: string;
userToken: string;
priority: number;
};
}
@@ -156,28 +196,41 @@ export interface NotificationAgentWebhook extends NotificationAgentConfig {
options: {
webhookUrl: string;
jsonPayload: string;
authHeader: string;
authHeader?: string;
};
}
export enum NotificationAgentKey {
DISCORD = 'discord',
EMAIL = 'email',
PUSHBULLET = 'pushbullet',
PUSHOVER = 'pushover',
SLACK = 'slack',
TELEGRAM = 'telegram',
WEBHOOK = 'webhook',
WEBPUSH = 'webpush',
}
interface NotificationAgents {
discord: NotificationAgentDiscord;
email: NotificationAgentEmail;
lunasea: NotificationAgentLunaSea;
pushbullet: NotificationAgentPushbullet;
pushover: NotificationAgentPushover;
slack: NotificationAgentSlack;
telegram: NotificationAgentTelegram;
webhook: NotificationAgentWebhook;
webpush: NotificationAgentConfig;
}
interface NotificationSettings {
enabled: boolean;
autoapprovalEnabled: boolean;
agents: NotificationAgents;
}
interface AllSettings {
clientId: string;
vapidPublic: string;
vapidPrivate: string;
main: MainSettings;
plex: PlexSettings;
jellyfin: JellyfinSettings;
@@ -196,23 +249,33 @@ class Settings {
constructor(initialSettings?: AllSettings) {
this.data = {
clientId: uuidv4(),
clientId: randomUUID(),
vapidPrivate: '',
vapidPublic: '',
main: {
apiKey: '',
applicationTitle: 'Overseerr',
applicationUrl: '',
csrfProtection: false,
cacheImages: false,
defaultPermissions: Permission.REQUEST,
defaultQuotas: {
movie: {},
tv: {},
},
hideAvailable: false,
localLogin: true,
newPlexLogin: true,
region: '',
originalLanguage: '',
trustProxy: false,
mediaServerType: MediaServerType.NOT_CONFIGURED,
partialRequestsEnabled: true,
locale: 'en',
},
plex: {
name: '',
ip: '127.0.0.1',
ip: '',
port: 32400,
useSsl: false,
libraries: [],
@@ -229,17 +292,16 @@ class Settings {
initialized: false,
},
notifications: {
enabled: true,
autoapprovalEnabled: false,
agents: {
email: {
enabled: false,
types: 0,
options: {
emailFrom: '',
smtpHost: '127.0.0.1',
smtpHost: '',
smtpPort: 587,
secure: false,
ignoreTls: false,
requireTls: false,
allowSelfSigned: false,
senderName: 'Overseerr',
},
@@ -251,6 +313,13 @@ class Settings {
webhookUrl: '',
},
},
lunasea: {
enabled: false,
types: 0,
options: {
webhookUrl: '',
},
},
slack: {
enabled: false,
types: 0,
@@ -280,7 +349,6 @@ class Settings {
options: {
accessToken: '',
userToken: '',
priority: 0,
},
},
webhook: {
@@ -288,11 +356,14 @@ class Settings {
types: 0,
options: {
webhookUrl: '',
authHeader: '',
jsonPayload:
'IntcbiAgICBcIm5vdGlmaWNhdGlvbl90eXBlXCI6IFwie3tub3RpZmljYXRpb25fdHlwZX19XCIsXG4gICAgXCJzdWJqZWN0XCI6IFwie3tzdWJqZWN0fX1cIixcbiAgICBcIm1lc3NhZ2VcIjogXCJ7e21lc3NhZ2V9fVwiLFxuICAgIFwiaW1hZ2VcIjogXCJ7e2ltYWdlfX1cIixcbiAgICBcImVtYWlsXCI6IFwie3tub3RpZnl1c2VyX2VtYWlsfX1cIixcbiAgICBcInVzZXJuYW1lXCI6IFwie3tub3RpZnl1c2VyX3VzZXJuYW1lfX1cIixcbiAgICBcImF2YXRhclwiOiBcInt7bm90aWZ5dXNlcl9hdmF0YXJ9fVwiLFxuICAgIFwie3ttZWRpYX19XCI6IHtcbiAgICAgICAgXCJtZWRpYV90eXBlXCI6IFwie3ttZWRpYV90eXBlfX1cIixcbiAgICAgICAgXCJ0bWRiSWRcIjogXCJ7e21lZGlhX3RtZGJpZH19XCIsXG4gICAgICAgIFwiaW1kYklkXCI6IFwie3ttZWRpYV9pbWRiaWR9fVwiLFxuICAgICAgICBcInR2ZGJJZFwiOiBcInt7bWVkaWFfdHZkYmlkfX1cIixcbiAgICAgICAgXCJzdGF0dXNcIjogXCJ7e21lZGlhX3N0YXR1c319XCIsXG4gICAgICAgIFwic3RhdHVzNGtcIjogXCJ7e21lZGlhX3N0YXR1czRrfX1cIlxuICAgIH0sXG4gICAgXCJ7e2V4dHJhfX1cIjogW11cbn0i',
'IntcbiAgICBcIm5vdGlmaWNhdGlvbl90eXBlXCI6IFwie3tub3RpZmljYXRpb25fdHlwZX19XCIsXG4gICAgXCJzdWJqZWN0XCI6IFwie3tzdWJqZWN0fX1cIixcbiAgICBcIm1lc3NhZ2VcIjogXCJ7e21lc3NhZ2V9fVwiLFxuICAgIFwiaW1hZ2VcIjogXCJ7e2ltYWdlfX1cIixcbiAgICBcImVtYWlsXCI6IFwie3tub3RpZnl1c2VyX2VtYWlsfX1cIixcbiAgICBcInVzZXJuYW1lXCI6IFwie3tub3RpZnl1c2VyX3VzZXJuYW1lfX1cIixcbiAgICBcImF2YXRhclwiOiBcInt7bm90aWZ5dXNlcl9hdmF0YXJ9fVwiLFxuICAgIFwie3ttZWRpYX19XCI6IHtcbiAgICAgICAgXCJtZWRpYV90eXBlXCI6IFwie3ttZWRpYV90eXBlfX1cIixcbiAgICAgICAgXCJ0bWRiSWRcIjogXCJ7e21lZGlhX3RtZGJpZH19XCIsXG4gICAgICAgIFwiaW1kYklkXCI6IFwie3ttZWRpYV9pbWRiaWR9fVwiLFxuICAgICAgICBcInR2ZGJJZFwiOiBcInt7bWVkaWFfdHZkYmlkfX1cIixcbiAgICAgICAgXCJzdGF0dXNcIjogXCJ7e21lZGlhX3N0YXR1c319XCIsXG4gICAgICAgIFwic3RhdHVzNGtcIjogXCJ7e21lZGlhX3N0YXR1czRrfX1cIlxuICAgIH0sXG4gICAgXCJ7e2V4dHJhfX1cIjogW10sXG4gICAgXCJ7e3JlcXVlc3R9fVwiOiB7XG4gICAgICAgIFwicmVxdWVzdF9pZFwiOiBcInt7cmVxdWVzdF9pZH19XCIsXG4gICAgICAgIFwicmVxdWVzdGVkQnlfZW1haWxcIjogXCJ7e3JlcXVlc3RlZEJ5X2VtYWlsfX1cIixcbiAgICAgICAgXCJyZXF1ZXN0ZWRCeV91c2VybmFtZVwiOiBcInt7cmVxdWVzdGVkQnlfdXNlcm5hbWV9fVwiLFxuICAgICAgICBcInJlcXVlc3RlZEJ5X2F2YXRhclwiOiBcInt7cmVxdWVzdGVkQnlfYXZhdGFyfX1cIlxuICAgIH1cbn0i',
},
},
webpush: {
enabled: false,
options: {},
},
},
},
};
@@ -357,6 +428,7 @@ class Settings {
return {
...this.data.public,
applicationTitle: this.data.main.applicationTitle,
applicationUrl: this.data.main.applicationUrl,
hideAvailable: this.data.main.hideAvailable,
localLogin: this.data.main.localLogin,
movie4kEnabled: this.data.radarr.some(
@@ -369,6 +441,12 @@ class Settings {
originalLanguage: this.data.main.originalLanguage,
mediaServerType: this.main.mediaServerType,
jellyfinHost: this.jellyfin.hostname,
partialRequestsEnabled: this.data.main.partialRequestsEnabled,
cacheImages: this.data.main.cacheImages,
vapidPublic: this.vapidPublic,
enablePushRegistration: this.data.notifications.agents.webpush.enabled,
locale: this.data.main.locale,
emailEnabled: this.data.notifications.agents.email.enabled,
};
}
@@ -382,13 +460,25 @@ class Settings {
get clientId(): string {
if (!this.data.clientId) {
this.data.clientId = uuidv4();
this.data.clientId = randomUUID();
this.save();
}
return this.data.clientId;
}
get vapidPublic(): string {
this.generateVapidKeys();
return this.data.vapidPublic;
}
get vapidPrivate(): string {
this.generateVapidKeys();
return this.data.vapidPrivate;
}
public regenerateApiKey(): MainSettings {
this.main.apiKey = this.generateApiKey();
this.save();
@@ -396,7 +486,16 @@ class Settings {
}
private generateApiKey(): string {
return Buffer.from(`${Date.now()}${uuidv4()})`).toString('base64');
return Buffer.from(`${Date.now()}${randomUUID()})`).toString('base64');
}
private generateVapidKeys(force = false): void {
if (!this.data.vapidPublic || !this.data.vapidPrivate || force) {
const vapidKeys = webpush.generateVAPIDKeys();
this.data.vapidPrivate = vapidKeys.privateKey;
this.data.vapidPublic = vapidKeys.publicKey;
this.save();
}
}
/**