diff --git a/api-jellyseerr.go b/api-jellyseerr.go
new file mode 100644
index 0000000..5ced607
--- /dev/null
+++ b/api-jellyseerr.go
@@ -0,0 +1,100 @@
+package main
+
+import (
+ "net/url"
+ "strconv"
+
+ "github.com/gin-gonic/gin"
+)
+
+// @Summary Get a list of Jellyseerr users.
+// @Produce json
+// @Success 200 {object} ombiUsersDTO
+// @Failure 500 {object} stringResponse
+// @Router /jellyseerr/users [get]
+// @Security Bearer
+// @tags Jellyseerr
+func (app *appContext) JellyseerrUsers(gc *gin.Context) {
+ app.debug.Println("Jellyseerr users requested")
+ users, err := app.js.GetUsers()
+ if err != nil {
+ app.err.Printf("Failed to get users from Jellyseerr: %v", err)
+ respond(500, "Couldn't get users", gc)
+ return
+ }
+ app.debug.Printf("Jellyseerr users retrieved: %d", len(users))
+ userlist := make([]ombiUser, len(users))
+ i := 0
+ for _, u := range users {
+ userlist[i] = ombiUser{
+ Name: u.Name(),
+ ID: strconv.FormatInt(u.ID, 10),
+ }
+ i++
+ }
+ gc.JSON(200, ombiUsersDTO{Users: userlist})
+}
+
+// @Summary Store Jellyseerr user template in an existing profile.
+// @Produce json
+// @Param id path string true "Jellyseerr ID of user to source from"
+// @Param profile path string true "Name of profile to store in"
+// @Success 200 {object} boolResponse
+// @Failure 400 {object} boolResponse
+// @Failure 500 {object} stringResponse
+// @Router /profiles/jellyseerr/{profile}/{id} [post]
+// @Security Bearer
+// @tags Jellyseerr
+func (app *appContext) SetJellyseerrProfile(gc *gin.Context) {
+ jellyseerrID, err := strconv.ParseInt(gc.Param("id"), 10, 64)
+ if err != nil {
+ respondBool(400, false, gc)
+ return
+ }
+ escapedProfileName := gc.Param("profile")
+ profileName, _ := url.QueryUnescape(escapedProfileName)
+ profile, ok := app.storage.GetProfileKey(profileName)
+ if !ok {
+ respondBool(400, false, gc)
+ return
+ }
+ u, err := app.js.UserByID(jellyseerrID)
+ if err != nil {
+ app.err.Printf("Couldn't get user from Jellyseerr: %v", err)
+ respond(500, "Couldn't get user", gc)
+ return
+ }
+ profile.Jellyseerr.User = u.UserTemplate
+ n, err := app.js.GetNotificationPreferencesByID(jellyseerrID)
+ if err != nil {
+ app.err.Printf("Couldn't get user's notification prefs from Jellyseerr: %v", err)
+ respond(500, "Couldn't get user notification prefs", gc)
+ return
+ }
+ profile.Jellyseerr.Notifications = n.NotificationsTemplate
+ profile.Jellyseerr.Enabled = true
+ app.storage.SetProfileKey(profileName, profile)
+ respondBool(204, true, gc)
+}
+
+// @Summary Remove jellyseerr user template from a profile.
+// @Produce json
+// @Param profile path string true "Name of profile to store in"
+// @Success 200 {object} boolResponse
+// @Failure 400 {object} boolResponse
+// @Failure 500 {object} stringResponse
+// @Router /profiles/jellyseerr/{profile} [delete]
+// @Security Bearer
+// @tags Jellyseerr
+func (app *appContext) DeleteJellyseerrProfile(gc *gin.Context) {
+ escapedProfileName := gc.Param("profile")
+ profileName, _ := url.QueryUnescape(escapedProfileName)
+ profile, ok := app.storage.GetProfileKey(profileName)
+ if !ok {
+ respondBool(400, false, gc)
+ return
+ }
+ profile.Jellyseerr.Enabled = false
+ app.storage.SetProfileKey(profileName, profile)
+ respondBool(204, true, gc)
+}
diff --git a/api-ombi.go b/api-ombi.go
index ca148aa..ab9b534 100644
--- a/api-ombi.go
+++ b/api-ombi.go
@@ -2,6 +2,7 @@ package main
import (
"fmt"
+ "net/url"
"github.com/gin-gonic/gin"
"github.com/hrfee/mediabrowser"
@@ -95,7 +96,8 @@ func (app *appContext) OmbiUsers(gc *gin.Context) {
func (app *appContext) SetOmbiProfile(gc *gin.Context) {
var req ombiUser
gc.BindJSON(&req)
- profileName := gc.Param("profile")
+ escapedProfileName := gc.Param("profile")
+ profileName, _ := url.QueryUnescape(escapedProfileName)
profile, ok := app.storage.GetProfileKey(profileName)
if !ok {
respondBool(400, false, gc)
@@ -122,7 +124,8 @@ func (app *appContext) SetOmbiProfile(gc *gin.Context) {
// @Security Bearer
// @tags Ombi
func (app *appContext) DeleteOmbiProfile(gc *gin.Context) {
- profileName := gc.Param("profile")
+ escapedProfileName := gc.Param("profile")
+ profileName, _ := url.QueryUnescape(escapedProfileName)
profile, ok := app.storage.GetProfileKey(profileName)
if !ok {
respondBool(400, false, gc)
diff --git a/api-profiles.go b/api-profiles.go
index ccbac7d..482abbb 100644
--- a/api-profiles.go
+++ b/api-profiles.go
@@ -27,6 +27,7 @@ func (app *appContext) GetProfiles(gc *gin.Context) {
LibraryAccess: p.LibraryAccess,
FromUser: p.FromUser,
Ombi: p.Ombi != nil,
+ Jellyseerr: p.Jellyseerr.Enabled,
ReferralsEnabled: false,
}
if referralsEnabled {
diff --git a/api-users.go b/api-users.go
index 64d1997..ddafe08 100644
--- a/api-users.go
+++ b/api-users.go
@@ -4,11 +4,13 @@ import (
"fmt"
"net/url"
"os"
+ "strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt"
+ "github.com/hrfee/jfa-go/jellyseerr"
"github.com/hrfee/mediabrowser"
"github.com/lithammer/shortuuid/v3"
"github.com/timshannon/badgerhold/v4"
@@ -94,6 +96,29 @@ func (app *appContext) NewUserAdmin(gc *gin.Context) {
app.info.Println("Created Ombi user")
}
}
+ if app.config.Section("jellyseerr").Key("enabled").MustBool(false) {
+ // Gets existing user (not possible) or imports the given user.
+ _, err := app.js.MustGetUser(id)
+ if err != nil {
+ app.err.Printf("Failed to create Jellyseerr user: %v", err)
+ } else {
+ app.info.Println("Created Jellyseerr user")
+ }
+ err = app.js.ApplyTemplateToUser(id, profile.Jellyseerr.User)
+ if err != nil {
+ app.err.Printf("Failed to apply Jellyseerr user template: %v\n", err)
+ }
+ err = app.js.ApplyNotificationsTemplateToUser(id, profile.Jellyseerr.Notifications)
+ if err != nil {
+ app.err.Printf("Failed to apply Jellyseerr notifications template: %v\n", err)
+ }
+ if emailEnabled {
+ err = app.js.ModifyUser(id, map[jellyseerr.UserField]any{jellyseerr.FieldEmail: req.Email})
+ if err != nil {
+ app.err.Printf("Failed to set Jellyseerr email address: %v\n", err)
+ }
+ }
+ }
if emailEnabled && app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "" {
app.debug.Printf("%s: Sending welcome email to %s", req.Username, req.Email)
msg, err := app.email.constructWelcome(req.Username, time.Time{}, app, false)
@@ -338,6 +363,10 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
Addr: req.Email,
Contact: (req.Email != ""),
}
+ // Only allow disabling of email contact if some other method is available.
+ if req.DiscordContact || req.TelegramContact || req.MatrixContact {
+ emailStore.Contact = req.EmailContact
+ }
if invite.UserLabel != "" {
emailStore.Label = invite.UserLabel
@@ -468,6 +497,51 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
app.debug.Printf("Skipping Ombi: Profile \"%s\" was empty", invite.Profile)
}
}
+ if invite.Profile != "" && app.config.Section("jellyseerr").Key("enabled").MustBool(false) {
+ if profile.Jellyseerr.Enabled {
+ // Gets existing user (not possible) or imports the given user.
+ _, err := app.js.MustGetUser(id)
+ if err != nil {
+ app.err.Printf("Failed to create Jellyseerr user: %v", err)
+ } else {
+ app.info.Println("Created Jellyseerr user")
+ }
+ err = app.js.ApplyTemplateToUser(id, profile.Jellyseerr.User)
+ if err != nil {
+ app.err.Printf("Failed to apply Jellyseerr user template: %v\n", err)
+ }
+ err = app.js.ApplyNotificationsTemplateToUser(id, profile.Jellyseerr.Notifications)
+ if err != nil {
+ app.err.Printf("Failed to apply Jellyseerr notifications template: %v\n", err)
+ }
+ contactMethods := map[jellyseerr.NotificationsField]any{}
+ if emailEnabled {
+ err = app.js.ModifyUser(id, map[jellyseerr.UserField]any{jellyseerr.FieldEmail: req.Email})
+ if err != nil {
+ app.err.Printf("Failed to set Jellyseerr email address: %v\n", err)
+ } else {
+ contactMethods[jellyseerr.FieldEmailEnabled] = req.EmailContact
+ }
+ }
+ if discordVerified {
+ contactMethods[jellyseerr.FieldDiscord] = discordUser.ID
+ contactMethods[jellyseerr.FieldDiscordEnabled] = req.DiscordContact
+ }
+ if telegramVerified {
+ u, _ := app.storage.GetTelegramKey(user.ID)
+ contactMethods[jellyseerr.FieldTelegram] = strconv.FormatInt(u.ChatID, 10)
+ contactMethods[jellyseerr.FieldTelegramEnabled] = req.TelegramContact
+ }
+ if emailEnabled || discordVerified || telegramVerified {
+ err := app.js.ModifyNotifications(id, contactMethods)
+ if err != nil {
+ app.err.Printf("Failed to sync contact methods with Jellyseerr: %v", err)
+ }
+ }
+ } else {
+ app.debug.Printf("Skipping Jellyseerr: Profile \"%s\" was empty", invite.Profile)
+ }
+ }
if matrixVerified {
matrixUser.Contact = req.MatrixContact
delete(app.matrix.tokens, req.MatrixPIN)
@@ -1265,6 +1339,8 @@ func (app *appContext) ApplySettings(gc *gin.Context) {
var configuration mediabrowser.Configuration
var displayprefs map[string]interface{}
var ombi map[string]interface{}
+ var jellyseerr JellyseerrTemplate
+ jellyseerr.Enabled = false
if req.From == "profile" {
// Check profile exists & isn't empty
profile, ok := app.storage.GetProfileKey(req.Profile)
@@ -1288,6 +1364,11 @@ func (app *appContext) ApplySettings(gc *gin.Context) {
ombi = profile.Ombi
}
}
+ if app.config.Section("jellyseerr").Key("enabled").MustBool(false) {
+ if profile.Jellyseerr.Enabled {
+ jellyseerr = profile.Jellyseerr
+ }
+ }
} else if req.From == "user" {
applyingFrom = "user"
@@ -1315,6 +1396,7 @@ func (app *appContext) ApplySettings(gc *gin.Context) {
"policy": map[string]string{},
"homescreen": map[string]string{},
"ombi": map[string]string{},
+ "jellyseerr": map[string]string{},
}
/* Jellyfin doesn't seem to like too many of these requests sent in succession
and can crash and mess up its database. Issue #160 says this occurs when more
@@ -1367,6 +1449,26 @@ func (app *appContext) ApplySettings(gc *gin.Context) {
errors["ombi"][id] = errorString
}
}
+ if jellyseerr.Enabled {
+ errorString := ""
+ // newUser := ombi
+ // newUser["id"] = user["id"]
+ // newUser["userName"] = user["userName"]
+ // newUser["alias"] = user["alias"]
+ // newUser["emailAddress"] = user["emailAddress"]
+ err := app.js.ApplyTemplateToUser(id, jellyseerr.User)
+ if err != nil {
+ errorString += fmt.Sprintf("ApplyUser: %v ", err)
+ }
+ err = app.js.ApplyNotificationsTemplateToUser(id, jellyseerr.Notifications)
+ if err != nil {
+ errorString += fmt.Sprintf("ApplyNotifications: %v ", err)
+ }
+ if errorString != "" {
+ errors["jellyseerr"][id] = errorString
+ }
+ }
+
if shouldDelay {
time.Sleep(250 * time.Millisecond)
}
diff --git a/config/config-base.json b/config/config-base.json
index 1b3bdd2..e18224a 100644
--- a/config/config-base.json
+++ b/config/config-base.json
@@ -1580,6 +1580,41 @@
}
}
},
+ "jellyseerr": {
+ "order": [],
+ "meta": {
+ "name": "Jellyseerr Integration",
+ "description": "Connect to Jellyseerr to automatically trigger the import of users on account creation, and to automatically link contact methods (email, discord and telegram). A template must be added to a User Profile for accounts to be created."
+ },
+ "settings": {
+ "enabled": {
+ "name": "Enabled",
+ "required": false,
+ "requires_restart": true,
+ "type": "bool",
+ "value": false,
+ "description": "Enable the Jellyseerr integration."
+ },
+ "server": {
+ "name": "URL",
+ "required": false,
+ "requires_restart": true,
+ "type": "text",
+ "value": "localhost:5000",
+ "depends_true": "enabled",
+ "description": "Jellyseerr server URL."
+ },
+ "api_key": {
+ "name": "API Key",
+ "required": false,
+ "requires_restart": true,
+ "type": "text",
+ "value": "",
+ "depends_true": "enabled",
+ "description": "API Key. Get this from the first tab in Jellyseerr's settings."
+ }
+ }
+ },
"backups": {
"order": [],
"meta": {
diff --git a/go.mod b/go.mod
index e9969af..de1d06d 100644
--- a/go.mod
+++ b/go.mod
@@ -16,6 +16,8 @@ replace github.com/hrfee/jfa-go/api => ./api
replace github.com/hrfee/jfa-go/easyproxy => ./easyproxy
+replace github.com/hrfee/jfa-go/jellyseerr => ./jellyseerr
+
require (
github.com/bwmarrin/discordgo v0.27.1
github.com/dgraph-io/badger/v3 v3.2103.5
@@ -29,7 +31,7 @@ require (
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/gomarkdown/markdown v0.0.0-20230322041520-c84983bdbf2a
- github.com/hrfee/jfa-go/common v0.0.0-20230626224816-f72960635dc3
+ github.com/hrfee/jfa-go/common v0.0.0-20240728190513-dabef831d769
github.com/hrfee/jfa-go/docs v0.0.0-20230626224816-f72960635dc3
github.com/hrfee/jfa-go/easyproxy v0.0.0-00010101000000-000000000000
github.com/hrfee/jfa-go/linecache v0.0.0-20230626224816-f72960635dc3
@@ -88,6 +90,7 @@ require (
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
+ github.com/hrfee/jfa-go/jellyseerr v0.0.0-00010101000000-000000000000 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.16.6 // indirect
diff --git a/html/admin.html b/html/admin.html
index 1dc3ad9..f7de13e 100644
--- a/html/admin.html
+++ b/html/admin.html
@@ -10,6 +10,7 @@
window.discordEnabled = {{ .discordEnabled }};
window.matrixEnabled = {{ .matrixEnabled }};
window.ombiEnabled = {{ .ombiEnabled }};
+ window.jellyseerrEnabled = {{ .jellyseerrEnabled }};
window.usernameEnabled = {{ .username }};
window.langFile = JSON.parse({{ .language }});
window.linkResetEnabled = {{ .linkResetEnabled }};
@@ -396,6 +397,19 @@
+
{{ .strings.userProfiles }} ×
@@ -409,6 +423,9 @@
{{ if .ombiEnabled }}
Ombi |
{{ end }}
+ {{ if .jellyseerrEnabled }}
+ Jellyseerr |
+ {{ end }}
{{ if .referralsEnabled }}
{{ .strings.referrals }} |
{{ end }}
diff --git a/jellyseerr/jellyseerr.go b/jellyseerr/jellyseerr.go
index 9dcf5b8..b1731f2 100644
--- a/jellyseerr/jellyseerr.go
+++ b/jellyseerr/jellyseerr.go
@@ -21,13 +21,14 @@ const (
// Jellyseerr represents a running Jellyseerr instance.
type Jellyseerr struct {
- server, key string
- header map[string]string
- httpClient *http.Client
- userCache map[string]User // Map of jellyfin IDs to users
- cacheExpiry time.Time
- cacheLength time.Duration
- timeoutHandler common.TimeoutHandler
+ server, key string
+ header map[string]string
+ httpClient *http.Client
+ userCache map[string]User // Map of jellyfin IDs to users
+ cacheExpiry time.Time
+ cacheLength time.Duration
+ timeoutHandler common.TimeoutHandler
+ LogRequestBodies bool
}
// NewJellyseerr returns an Ombi object.
@@ -44,10 +45,11 @@ func NewJellyseerr(server, key string, timeoutHandler common.TimeoutHandler) *Je
header: map[string]string{
"X-Api-Key": key,
},
- cacheLength: time.Duration(30) * time.Minute,
- cacheExpiry: time.Now(),
- timeoutHandler: timeoutHandler,
- userCache: map[string]User{},
+ cacheLength: time.Duration(30) * time.Minute,
+ cacheExpiry: time.Now(),
+ timeoutHandler: timeoutHandler,
+ userCache: map[string]User{},
+ LogRequestBodies: false,
}
}
@@ -59,6 +61,9 @@ func (js *Jellyseerr) getJSON(url string, params map[string]string, queryParams
var req *http.Request
if params != nil {
jsonParams, _ := json.Marshal(params)
+ if js.LogRequestBodies {
+ fmt.Printf("Jellyseerr API Client: Sending Data \"%s\"\n", string(jsonParams))
+ }
req, _ = http.NewRequest("GET", url+"?"+queryParams.Encode(), bytes.NewBuffer(jsonParams))
} else {
req, _ = http.NewRequest("GET", url+"?"+queryParams.Encode(), nil)
@@ -94,6 +99,9 @@ func (js *Jellyseerr) getJSON(url string, params map[string]string, queryParams
func (js *Jellyseerr) send(mode string, url string, data any, response bool, headers map[string]string) (string, int, error) {
responseText := ""
params, _ := json.Marshal(data)
+ if js.LogRequestBodies {
+ fmt.Printf("Jellyseerr API Client: Sending Data \"%s\"\n", string(params))
+ }
req, _ := http.NewRequest(mode, url, bytes.NewBuffer(params))
req.Header.Add("Content-Type", "application/json")
for name, value := range js.header {
@@ -291,7 +299,7 @@ func (js *Jellyseerr) ApplyTemplateToUser(jfID string, tmpl UserTemplate) error
return nil
}
-func (js *Jellyseerr) ModifyUser(jfID string, conf map[UserField]string) error {
+func (js *Jellyseerr) ModifyUser(jfID string, conf map[UserField]any) error {
u, err := js.MustGetUser(jfID)
if err != nil {
return err
@@ -327,13 +335,16 @@ func (js *Jellyseerr) DeleteUser(jfID string) error {
}
func (js *Jellyseerr) GetNotificationPreferences(jfID string) (Notifications, error) {
- var data Notifications
u, err := js.MustGetUser(jfID)
if err != nil {
- return data, err
+ return Notifications{}, err
}
+ return js.GetNotificationPreferencesByID(u.ID)
+}
- resp, status, err := js.getJSON(fmt.Sprintf(js.server+"/user/%d/settings/notifications", u.ID), nil, url.Values{})
+func (js *Jellyseerr) GetNotificationPreferencesByID(jellyseerrID int64) (Notifications, error) {
+ var data Notifications
+ resp, status, err := js.getJSON(fmt.Sprintf(js.server+"/user/%d/settings/notifications", jellyseerrID), nil, url.Values{})
if err != nil {
return data, err
}
@@ -360,7 +371,7 @@ func (js *Jellyseerr) ApplyNotificationsTemplateToUser(jfID string, tmpl Notific
return nil
}
-func (js *Jellyseerr) ModifyNotifications(jfID string, conf map[NotificationsField]string) error {
+func (js *Jellyseerr) ModifyNotifications(jfID string, conf map[NotificationsField]any) error {
u, err := js.MustGetUser(jfID)
if err != nil {
return err
@@ -375,3 +386,21 @@ func (js *Jellyseerr) ModifyNotifications(jfID string, conf map[NotificationsFie
}
return nil
}
+
+func (js *Jellyseerr) GetUsers() (map[string]User, error) {
+ err := js.getUsers()
+ return js.userCache, err
+}
+
+func (js *Jellyseerr) UserByID(jellyseerrID int64) (User, error) {
+ resp, status, err := js.getJSON(js.server+fmt.Sprintf("/user/%d", jellyseerrID), nil, url.Values{})
+ var data User
+ if status != 200 {
+ return data, fmt.Errorf("failed (error %d)", status)
+ }
+ if err != nil {
+ return data, err
+ }
+ err = json.Unmarshal([]byte(resp), &data)
+ return data, err
+}
diff --git a/jellyseerr/models.go b/jellyseerr/models.go
index 4ec8d4b..edeb3f9 100644
--- a/jellyseerr/models.go
+++ b/jellyseerr/models.go
@@ -11,61 +11,74 @@ const (
type User struct {
UserTemplate // Note: You can set this with User.UserTemplate = value.
- Warnings []any `json:"warnings"`
- ID int `json:"id"`
- Email string `json:"email"`
- PlexUsername string `json:"plexUsername"`
- JellyfinUsername string `json:"jellyfinUsername"`
- Username string `json:"username"`
- RecoveryLinkExpirationDate any `json:"recoveryLinkExpirationDate"`
- PlexID string `json:"plexId"`
- JellyfinUserID string `json:"jellyfinUserId"`
- JellyfinDeviceID string `json:"jellyfinDeviceId"`
- JellyfinAuthToken string `json:"jellyfinAuthToken"`
- PlexToken string `json:"plexToken"`
- Avatar string `json:"avatar"`
- CreatedAt time.Time `json:"createdAt"`
- UpdatedAt time.Time `json:"updatedAt"`
- RequestCount int `json:"requestCount"`
- DisplayName string `json:"displayName"`
+ UserType int64 `json:"userType,omitempty"`
+ Warnings []any `json:"warnings,omitempty"`
+ ID int64 `json:"id,omitempty"`
+ Email string `json:"email,omitempty"`
+ PlexUsername string `json:"plexUsername,omitempty"`
+ JellyfinUsername string `json:"jellyfinUsername,omitempty"`
+ Username string `json:"username,omitempty"`
+ RecoveryLinkExpirationDate any `json:"recoveryLinkExpirationDate,omitempty"`
+ PlexID string `json:"plexId,omitempty"`
+ JellyfinUserID string `json:"jellyfinUserId,omitempty"`
+ JellyfinDeviceID string `json:"jellyfinDeviceId,omitempty"`
+ JellyfinAuthToken string `json:"jellyfinAuthToken,omitempty"`
+ PlexToken string `json:"plexToken,omitempty"`
+ Avatar string `json:"avatar,omitempty"`
+ CreatedAt time.Time `json:"createdAt,omitempty"`
+ UpdatedAt time.Time `json:"updatedAt,omitempty"`
+ RequestCount int64 `json:"requestCount,omitempty"`
+ DisplayName string `json:"displayName,omitempty"`
+}
+
+func (u User) Name() string {
+ var n string
+ if u.Username != "" {
+ n = u.Username
+ } else if u.JellyfinUsername != "" {
+ n = u.JellyfinUsername
+ }
+ if u.DisplayName != "" {
+ n += " (" + u.DisplayName + ")"
+ }
+ return n
}
type UserTemplate struct {
- Permissions Permissions `json:"permissions"`
- UserType int `json:"userType"`
- MovieQuotaLimit any `json:"movieQuotaLimit"`
- MovieQuotaDays any `json:"movieQuotaDays"`
- TvQuotaLimit any `json:"tvQuotaLimit"`
- TvQuotaDays any `json:"tvQuotaDays"`
+ Permissions Permissions `json:"permissions,omitempty"`
+ MovieQuotaLimit any `json:"movieQuotaLimit,omitempty"`
+ MovieQuotaDays any `json:"movieQuotaDays,omitempty"`
+ TvQuotaLimit any `json:"tvQuotaLimit,omitempty"`
+ TvQuotaDays any `json:"tvQuotaDays,omitempty"`
}
type PageInfo struct {
- Pages int `json:"pages"`
- PageSize int `json:"pageSize"`
- Results int `json:"results"`
- Page int `json:"page"`
+ Pages int `json:"pages,omitempty"`
+ PageSize int `json:"pageSize,omitempty"`
+ Results int `json:"results,omitempty"`
+ Page int `json:"page,omitempty"`
}
type GetUsersDTO struct {
- Page PageInfo `json:"pageInfo"`
- Results []User `json:"results"`
+ Page PageInfo `json:"pageInfo,omitempty"`
+ Results []User `json:"results,omitempty"`
}
type permissionsDTO struct {
- Permissions Permissions `json:"permissions"`
+ Permissions Permissions `json:"permissions,omitempty"`
}
type Permissions int
type NotificationTypes struct {
- Discord int `json:"discord"`
- Email int `json:"email"`
- Pushbullet int `json:"pushbullet"`
- Pushover int `json:"pushover"`
- Slack int `json:"slack"`
- Telegram int `json:"telegram"`
- Webhook int `json:"webhook"`
- Webpush int `json:"webpush"`
+ Discord int64 `json:"discord,omitempty"`
+ Email int64 `json:"email,omitempty"`
+ Pushbullet int64 `json:"pushbullet,omitempty"`
+ Pushover int64 `json:"pushover,omitempty"`
+ Slack int64 `json:"slack,omitempty"`
+ Telegram int64 `json:"telegram,omitempty"`
+ Webhook int64 `json:"webhook,omitempty"`
+ Webpush int64 `json:"webpush,omitempty"`
}
type NotificationsField string
@@ -80,21 +93,21 @@ const (
type Notifications struct {
NotificationsTemplate
- PgpKey any `json:"pgpKey"`
- DiscordID string `json:"discordId"`
- PushbulletAccessToken any `json:"pushbulletAccessToken"`
- PushoverApplicationToken any `json:"pushoverApplicationToken"`
- PushoverUserKey any `json:"pushoverUserKey"`
- TelegramChatID string `json:"telegramChatId"`
+ PgpKey any `json:"pgpKey,omitempty"`
+ DiscordID string `json:"discordId,omitempty"`
+ PushbulletAccessToken any `json:"pushbulletAccessToken,omitempty"`
+ PushoverApplicationToken any `json:"pushoverApplicationToken,omitempty"`
+ PushoverUserKey any `json:"pushoverUserKey,omitempty"`
+ TelegramChatID string `json:"telegramChatId,omitempty"`
}
type NotificationsTemplate struct {
- EmailEnabled bool `json:"emailEnabled"`
- DiscordEnabled bool `json:"discordEnabled"`
- DiscordEnabledTypes int `json:"discordEnabledTypes"`
- PushoverSound any `json:"pushoverSound"`
- TelegramEnabled bool `json:"telegramEnabled"`
- TelegramSendSilently any `json:"telegramSendSilently"`
- WebPushEnabled bool `json:"webPushEnabled"`
- NotifTypes NotificationTypes `json:"notificationTypes"`
+ EmailEnabled bool `json:"emailEnabled,omitempty"`
+ DiscordEnabled bool `json:"discordEnabled,omitempty"`
+ DiscordEnabledTypes int64 `json:"discordEnabledTypes,omitempty"`
+ PushoverSound any `json:"pushoverSound,omitempty"`
+ TelegramEnabled bool `json:"telegramEnabled,omitempty"`
+ TelegramSendSilently any `json:"telegramSendSilently,omitempty"`
+ WebPushEnabled bool `json:"webPushEnabled,omitempty"`
+ NotifTypes NotificationTypes `json:"notificationTypes,omitempty"`
}
diff --git a/lang/admin/en-us.json b/lang/admin/en-us.json
index 7de1729..5d514b3 100644
--- a/lang/admin/en-us.json
+++ b/lang/admin/en-us.json
@@ -99,6 +99,8 @@
"settingsMaybeUnderAdvanced": "Tip: You might find what you're looking for by enabling Advanced Settings.",
"ombiProfile": "Ombi user profile",
"ombiUserDefaultsDescription": "Create an Ombi user and configure it, then select it below. It's settings/permissions will be stored and applied to new Ombi users created by jfa-go when this profile is selected.",
+ "jellyseerrProfile": "Jellyseerr user profile",
+ "jellyseerrUserDefaultsDescription": "Create a Jellyseerr user and configure it, then select it below. It's settings/permissions will be stored and applied to new Jellyseerr users created by jfa-go when this profile is selected.",
"userProfiles": "User Profiles",
"userProfilesDescription": "Profiles are applied to users when they create an account. A profile includes library access rights and homescreen layout.",
"userProfilesIsDefault": "Default",
@@ -208,6 +210,7 @@
"sentAnnouncement": "Announcement sent.",
"savedAnnouncement": "Announcement saved.",
"setOmbiProfile": "Stored ombi profile.",
+ "savedProfile": "Stored profile changes.",
"updateApplied": "Update applied, please restart.",
"updateAppliedRefresh": "Update applied, please refresh.",
"telegramVerified": "Telegram account verified.",
@@ -224,6 +227,7 @@
"errorDeleteProfile": "Failed to delete profile {n}",
"errorLoadProfiles": "Failed to load profiles.",
"errorCreateProfile": "Failed to create profile {n}",
+ "errorSavedProfile": "Failed to save profile {n}",
"errorSetDefaultProfile": "Failed to set default profile.",
"errorLoadUsers": "Failed to load users.",
"errorLoadSettings": "Failed to load settings.",
diff --git a/main.go b/main.go
index 55a7834..15f3046 100644
--- a/main.go
+++ b/main.go
@@ -25,6 +25,7 @@ import (
"github.com/hrfee/jfa-go/common"
_ "github.com/hrfee/jfa-go/docs"
"github.com/hrfee/jfa-go/easyproxy"
+ "github.com/hrfee/jfa-go/jellyseerr"
"github.com/hrfee/jfa-go/logger"
"github.com/hrfee/jfa-go/ombi"
"github.com/hrfee/mediabrowser"
@@ -101,6 +102,7 @@ type appContext struct {
jf *mediabrowser.MediaBrowser
authJf *mediabrowser.MediaBrowser
ombi *ombi.Ombi
+ js *jellyseerr.Jellyseerr
datePattern string
timePattern string
storage Storage
@@ -359,6 +361,17 @@ func start(asDaemon, firstCall bool) {
}
+ if app.config.Section("jellyseerr").Key("enabled").MustBool(false) {
+ app.debug.Printf("Connecting to Jellyseerr")
+ jellyseerrServer := app.config.Section("jellyseerr").Key("server").String()
+ app.js = jellyseerr.NewJellyseerr(
+ jellyseerrServer,
+ app.config.Section("jellyseerr").Key("api_key").String(),
+ common.NewTimeoutHandler("Jellyseerr", jellyseerrServer, true),
+ )
+
+ }
+
app.storage.db_path = filepath.Join(app.dataPath, "db")
app.loadPendingBackup()
app.ConnectDB()
diff --git a/models.go b/models.go
index ac1a1df..9bcb93e 100644
--- a/models.go
+++ b/models.go
@@ -16,6 +16,7 @@ type newUserDTO struct {
Username string `json:"username" example:"jeff" binding:"required"` // User's username
Password string `json:"password" example:"guest" binding:"required"` // User's password
Email string `json:"email" example:"jeff@jellyf.in"` // User's email address
+ EmailContact bool `json:"email_contact"` // Whether or not to use email for notifications/pwrs
Code string `json:"code" example:"abc0933jncjkcjj"` // Invite code (required on /newUser)
TelegramPIN string `json:"telegram_pin" example:"A1-B2-3C"` // Telegram verification PIN (if used)
TelegramContact bool `json:"telegram_contact"` // Whether or not to use telegram for notifications/pwrs
@@ -76,6 +77,7 @@ type profileDTO struct {
LibraryAccess string `json:"libraries" example:"all"` // Number of libraries profile has access to
FromUser string `json:"fromUser" example:"jeff"` // The user the profile is based on
Ombi bool `json:"ombi"` // Whether or not Ombi settings are stored in this profile.
+ Jellyseerr bool `json:"jellyseerr"` // Whether or not Jellyseerr settings are stored in this profile.
ReferralsEnabled bool `json:"referrals_enabled" example:"true"` // Whether or not the profile has referrals enabled, and has a template invite stored.
}
diff --git a/router.go b/router.go
index 978da6e..85900a2 100644
--- a/router.go
+++ b/router.go
@@ -238,6 +238,11 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
api.GET(p+"/users/discord/:username", app.DiscordGetUsers)
api.POST(p+"/users/discord", app.DiscordConnect)
}
+ if app.config.Section("jellyseerr").Key("enabled").MustBool(false) {
+ api.GET(p+"/jellyseerr/users", app.JellyseerrUsers)
+ api.POST(p+"/profiles/jellyseerr/:profile/:id", app.SetJellyseerrProfile)
+ api.DELETE(p+"/profiles/jellyseerr/:profile", app.DeleteJellyseerrProfile)
+ }
if app.config.Section("ombi").Key("enabled").MustBool(false) {
api.GET(p+"/ombi/users", app.OmbiUsers)
api.POST(p+"/profiles/ombi/:profile", app.SetOmbiProfile)
diff --git a/storage.go b/storage.go
index ff75989..2a10352 100644
--- a/storage.go
+++ b/storage.go
@@ -11,6 +11,7 @@ import (
"time"
"github.com/gin-gonic/gin"
+ "github.com/hrfee/jfa-go/jellyseerr"
"github.com/hrfee/jfa-go/logger"
"github.com/hrfee/mediabrowser"
"github.com/timshannon/badgerhold/v4"
@@ -650,9 +651,16 @@ type Profile struct {
Displayprefs map[string]interface{} `json:"displayprefs,omitempty"`
Default bool `json:"default,omitempty"`
Ombi map[string]interface{} `json:"ombi,omitempty"`
+ Jellyseerr JellyseerrTemplate `json:"jellyseerr,omitempty"`
ReferralTemplateKey string
}
+type JellyseerrTemplate struct {
+ Enabled bool `json:"enabled,omitempty"`
+ User jellyseerr.UserTemplate `json:"user,omitempty"`
+ Notifications jellyseerr.NotificationsTemplate `json:"notifications,omitempty"`
+}
+
type Invite struct {
Code string `badgerhold:"key"`
Created time.Time `json:"created"`
diff --git a/ts/admin.ts b/ts/admin.ts
index e03fea8..29098a8 100644
--- a/ts/admin.ts
+++ b/ts/admin.ts
@@ -49,6 +49,9 @@ window.availableProfiles = window.availableProfiles || [];
window.modals.ombiProfile = new Modal(document.getElementById('modal-ombi-profile'));
document.getElementById('form-ombi-defaults').addEventListener('submit', window.modals.ombiProfile.close);
+
+ window.modals.jellyseerrProfile = new Modal(document.getElementById('modal-jellyseerr-profile'));
+ document.getElementById('form-jellyseerr-defaults').addEventListener('submit', window.modals.jellyseerrProfile.close);
window.modals.profiles = new Modal(document.getElementById("modal-user-profiles"));
diff --git a/ts/form.ts b/ts/form.ts
index 873ed79..21a09cc 100644
--- a/ts/form.ts
+++ b/ts/form.ts
@@ -224,6 +224,7 @@ if (window.emailRequired) {
interface sendDTO {
code: string;
email: string;
+ email_contact?: boolean;
username: string;
password: string;
telegram_pin?: string;
@@ -252,8 +253,9 @@ const create = (event: SubmitEvent) => {
code: window.code,
username: usernameField.value,
email: emailField.value,
+ email_contact: true,
password: passwordField.value
- };
+ }
if (telegramVerified) {
send.telegram_pin = window.telegramPIN;
const checkbox = document.getElementById("contact-via-telegram") as HTMLInputElement;
@@ -275,6 +277,10 @@ const create = (event: SubmitEvent) => {
send.matrix_contact = true;
}
}
+ if (matrixVerified || discordVerified || telegramVerified) {
+ const checkbox = document.getElementById("contact-via-email") as HTMLInputElement;
+ send.email_contact = checkbox.checked;
+ }
if (window.captcha) {
if (window.reCAPTCHA) {
send.captcha_text = grecaptcha.getResponse();
diff --git a/ts/modules/profiles.ts b/ts/modules/profiles.ts
index 6c8ef49..cd8d5e2 100644
--- a/ts/modules/profiles.ts
+++ b/ts/modules/profiles.ts
@@ -5,6 +5,7 @@ interface Profile {
libraries: string;
fromUser: string;
ombi: boolean;
+ jellyseerr: boolean;
referrals_enabled: boolean;
}
@@ -14,9 +15,11 @@ class profile implements Profile {
private _adminChip: HTMLSpanElement;
private _libraries: HTMLTableDataCellElement;
private _ombiButton: HTMLSpanElement;
+ private _ombi: boolean;
+ private _jellyseerrButton: HTMLSpanElement;
+ private _jellyseerr: boolean;
private _fromUser: HTMLTableDataCellElement;
private _defaultRadio: HTMLInputElement;
- private _ombi: boolean;
private _referralsButton: HTMLSpanElement;
private _referralsEnabled: boolean;
@@ -51,6 +54,21 @@ class profile implements Profile {
this._ombiButton.classList.remove("~critical");
}
}
+
+ get jellyseerr(): boolean { return this._jellyseerr; }
+ set jellyseerr(v: boolean) {
+ if (!window.jellyseerrEnabled) return;
+ this._jellyseerr = v;
+ if (v) {
+ this._jellyseerrButton.textContent = window.lang.strings("delete");
+ this._jellyseerrButton.classList.add("~critical");
+ this._jellyseerrButton.classList.remove("~neutral");
+ } else {
+ this._jellyseerrButton.textContent = window.lang.strings("add");
+ this._jellyseerrButton.classList.add("~neutral");
+ this._jellyseerrButton.classList.remove("~critical");
+ }
+ }
get fromUser(): string { return this._fromUser.textContent; }
set fromUser(v: string) { this._fromUser.textContent = v; }
@@ -82,6 +100,9 @@ class profile implements Profile {
if (window.ombiEnabled) innerHTML += `
|
`;
+ if (window.jellyseerrEnabled) innerHTML += `
+ |
+ `;
if (window.referralsEnabled) innerHTML += `
|
`;
@@ -96,6 +117,8 @@ class profile implements Profile {
this._libraries = this._row.querySelector("td.profile-libraries") as HTMLTableDataCellElement;
if (window.ombiEnabled)
this._ombiButton = this._row.querySelector("span.profile-ombi") as HTMLSpanElement;
+ if (window.jellyseerrEnabled)
+ this._jellyseerrButton = this._row.querySelector("span.profile-jellyseerr") as HTMLSpanElement;
if (window.referralsEnabled)
this._referralsButton = this._row.querySelector("span.profile-referrals") as HTMLSpanElement;
this._fromUser = this._row.querySelector("td.profile-from") as HTMLTableDataCellElement;
@@ -112,10 +135,12 @@ class profile implements Profile {
this.fromUser = p.fromUser;
this.libraries = p.libraries;
this.ombi = p.ombi;
+ this.jellyseerr = p.jellyseerr;
this.referrals_enabled = p.referrals_enabled;
}
setOmbiFunc = (ombiFunc: (ombi: boolean) => void) => { this._ombiButton.onclick = () => ombiFunc(this._ombi); }
+ setJellyseerrFunc = (jellyseerrFunc: (jellyseerr: boolean) => void) => { this._jellyseerrButton.onclick = () => jellyseerrFunc(this._jellyseerr); }
setReferralFunc = (referralFunc: (enabled: boolean) => void) => { this._referralsButton.onclick = () => referralFunc(this._referralsEnabled); }
remove = () => { document.dispatchEvent(new CustomEvent("profiles-delete", { detail: this._name })); this._row.remove(); }
@@ -144,6 +169,7 @@ export class ProfileEditor {
private _profiles: { [name: string]: profile } = {};
private _default: string;
private _ombiProfiles: ombiProfiles;
+ private _jellyseerrProfiles: jellyseerrProfiles;
private _createForm = document.getElementById("form-add-profile") as HTMLFormElement;
private _profileName = document.getElementById("add-profile-name") as HTMLInputElement;
@@ -181,7 +207,7 @@ export class ProfileEditor {
this._profiles[name].update(name, resp.profiles[name]);
} else {
this._profiles[name] = new profile(name, resp.profiles[name]);
- if (window.ombiEnabled)
+ if (window.ombiEnabled) {
this._profiles[name].setOmbiFunc((ombi: boolean) => {
if (ombi) {
this._ombiProfiles.delete(name, (req: XMLHttpRequest) => {
@@ -198,7 +224,26 @@ export class ProfileEditor {
this._ombiProfiles.load(name);
}
});
- if (window.referralsEnabled)
+ }
+ if (window.jellyseerrEnabled) {
+ this._profiles[name].setJellyseerrFunc((jellyseerr: boolean) => {
+ if (jellyseerr) {
+ this._jellyseerrProfiles.delete(name, (req: XMLHttpRequest) => {
+ if (req.readyState == 4) {
+ if (req.status != 204) {
+ window.notifications.customError("errorDeleteJellyseerr", window.lang.notif("errorUnknown"));
+ return;
+ }
+ this._profiles[name].jellyseerr = false;
+ }
+ });
+ } else {
+ window.modals.profiles.close();
+ this._jellyseerrProfiles.load(name);
+ }
+ });
+ }
+ if (window.referralsEnabled) {
this._profiles[name].setReferralFunc((enabled: boolean) => {
if (enabled) {
this.disableReferrals(name);
@@ -206,6 +251,7 @@ export class ProfileEditor {
this.enableReferrals(name);
}
});
+ }
this._table.appendChild(this._profiles[name].asElement());
}
}
@@ -299,6 +345,8 @@ export class ProfileEditor {
if (window.ombiEnabled)
this._ombiProfiles = new ombiProfiles();
+ if (window.jellyseerrEnabled)
+ this._jellyseerrProfiles = new jellyseerrProfiles();
this._createButton.onclick = () => _get("/users", null, (req: XMLHttpRequest) => {
if (req.readyState == 4) {
@@ -366,7 +414,7 @@ export class ombiProfiles {
let resp = {} as ombiUser;
resp.id = this._select.value;
resp.name = this._users[resp.id];
- _post("/profiles/ombi/" + this._currentProfile, resp, (req: XMLHttpRequest) => {
+ _post("/profiles/ombi/" + encodeURIComponent(encodeURIComponent(this._currentProfile)), resp, (req: XMLHttpRequest) => {
if (req.readyState == 4) {
toggleLoader(button);
if (req.status == 200 || req.status == 204) {
@@ -379,7 +427,7 @@ export class ombiProfiles {
});
}
- delete = (profile: string, post?: (req: XMLHttpRequest) => void) => _delete("/profiles/ombi/" + profile, null, post);
+ delete = (profile: string, post?: (req: XMLHttpRequest) => void) => _delete("/profiles/ombi/" + encodeURIComponent(encodeURIComponent(profile)), null, post);
load = (profile: string) => {
this._currentProfile = profile;
@@ -401,3 +449,54 @@ export class ombiProfiles {
});
}
}
+
+export class jellyseerrProfiles {
+ private _form: HTMLFormElement;
+ private _select: HTMLSelectElement;
+ private _users: { [id: string]: string } = {};
+ private _currentProfile: string;
+
+ constructor() {
+ this._form = document.getElementById("form-jellyseerr-defaults") as HTMLFormElement;
+ this._form.onsubmit = this.send;
+ this._select = this._form.querySelector("select") as HTMLSelectElement;
+ }
+ send = () => {
+ const button = this._form.querySelector("span.submit") as HTMLSpanElement;
+ toggleLoader(button);
+ let encodedProfile = encodeURIComponent(encodeURIComponent(this._currentProfile));
+ _post("/profiles/jellyseerr/" + encodedProfile + "/" + this._select.value, null, (req: XMLHttpRequest) => {
+ if (req.readyState == 4) {
+ toggleLoader(button);
+ if (req.status == 200 || req.status == 204) {
+ window.notifications.customSuccess("jellyseerrDefaults", window.lang.notif("savedProfile"));
+ } else {
+ window.notifications.customError("jellyseerrDefaults", window.lang.notif("errorSavedProfile"));
+ }
+ window.modals.jellyseerrProfile.close();
+ }
+ });
+ }
+
+ delete = (profile: string, post?: (req: XMLHttpRequest) => void) => _delete("/profiles/jellyseerr/" + encodeURIComponent(encodeURIComponent(profile)), null, post);
+
+ load = (profile: string) => {
+ this._currentProfile = profile;
+ _get("/jellyseerr/users", null, (req: XMLHttpRequest) => {
+ if (req.readyState == 4) {
+ if (req.status == 200 && "users" in req.response) {
+ const users = req.response["users"] as ombiUser[];
+ let innerHTML = "";
+ for (let user of users) {
+ this._users[user.id] = user.name;
+ innerHTML += ``;
+ }
+ this._select.innerHTML = innerHTML;
+ window.modals.jellyseerrProfile.show();
+ } else {
+ window.notifications.customError("jellyseerrLoadError", window.lang.notif("errorLoadUsers"))
+ }
+ }
+ });
+ }
+}
diff --git a/ts/typings/d.ts b/ts/typings/d.ts
index 2055038..34beb70 100644
--- a/ts/typings/d.ts
+++ b/ts/typings/d.ts
@@ -24,6 +24,7 @@ declare interface Window {
discordEnabled: boolean;
matrixEnabled: boolean;
ombiEnabled: boolean;
+ jellyseerrEnabled: boolean;
usernameEnabled: boolean;
linkResetEnabled: boolean;
token: string;
@@ -101,6 +102,7 @@ declare interface Modals {
settingsRestart: Modal;
settingsRefresh: Modal;
ombiProfile?: Modal;
+ jellyseerrProfile?: Modal;
profiles: Modal;
addProfile: Modal;
announce: Modal;
diff --git a/views.go b/views.go
index e342191..dfb561a 100644
--- a/views.go
+++ b/views.go
@@ -133,6 +133,7 @@ func (app *appContext) AdminPage(gc *gin.Context) {
emailEnabled, _ := app.config.Section("invite_emails").Key("enabled").Bool()
notificationsEnabled, _ := app.config.Section("notifications").Key("enabled").Bool()
ombiEnabled := app.config.Section("ombi").Key("enabled").MustBool(false)
+ jellyseerrEnabled := app.config.Section("jellyseerr").Key("enabled").MustBool(false)
jfAdminOnly := app.config.Section("ui").Key("admin_only").MustBool(true)
jfAllowAll := app.config.Section("ui").Key("allow_all").MustBool(false)
var license string
@@ -155,34 +156,35 @@ func (app *appContext) AdminPage(gc *gin.Context) {
}
gcHTML(gc, http.StatusOK, "admin.html", gin.H{
- "urlBase": app.getURLBase(gc),
- "cssClass": app.cssClass,
- "cssVersion": cssVersion,
- "contactMessage": "",
- "emailEnabled": emailEnabled,
- "telegramEnabled": telegramEnabled,
- "discordEnabled": discordEnabled,
- "matrixEnabled": matrixEnabled,
- "ombiEnabled": ombiEnabled,
- "linkResetEnabled": app.config.Section("password_resets").Key("link_reset").MustBool(false),
- "notifications": notificationsEnabled,
- "version": version,
- "commit": commit,
- "buildTime": buildTime,
- "builtBy": builtBy,
- "username": !app.config.Section("email").Key("no_username").MustBool(false),
- "strings": app.storage.lang.Admin[lang].Strings,
- "quantityStrings": app.storage.lang.Admin[lang].QuantityStrings,
- "language": app.storage.lang.Admin[lang].JSON,
- "langName": lang,
- "license": license,
- "jellyfinLogin": app.jellyfinLogin,
- "jfAdminOnly": jfAdminOnly,
- "jfAllowAll": jfAllowAll,
- "userPageEnabled": app.config.Section("user_page").Key("enabled").MustBool(false),
- "showUserPageLink": app.config.Section("user_page").Key("show_link").MustBool(true),
- "referralsEnabled": app.config.Section("user_page").Key("enabled").MustBool(false) && app.config.Section("user_page").Key("referrals").MustBool(false),
- "loginAppearance": app.config.Section("ui").Key("login_appearance").MustString("clear"),
+ "urlBase": app.getURLBase(gc),
+ "cssClass": app.cssClass,
+ "cssVersion": cssVersion,
+ "contactMessage": "",
+ "emailEnabled": emailEnabled,
+ "telegramEnabled": telegramEnabled,
+ "discordEnabled": discordEnabled,
+ "matrixEnabled": matrixEnabled,
+ "ombiEnabled": ombiEnabled,
+ "jellyseerrEnabled": jellyseerrEnabled,
+ "linkResetEnabled": app.config.Section("password_resets").Key("link_reset").MustBool(false),
+ "notifications": notificationsEnabled,
+ "version": version,
+ "commit": commit,
+ "buildTime": buildTime,
+ "builtBy": builtBy,
+ "username": !app.config.Section("email").Key("no_username").MustBool(false),
+ "strings": app.storage.lang.Admin[lang].Strings,
+ "quantityStrings": app.storage.lang.Admin[lang].QuantityStrings,
+ "language": app.storage.lang.Admin[lang].JSON,
+ "langName": lang,
+ "license": license,
+ "jellyfinLogin": app.jellyfinLogin,
+ "jfAdminOnly": jfAdminOnly,
+ "jfAllowAll": jfAllowAll,
+ "userPageEnabled": app.config.Section("user_page").Key("enabled").MustBool(false),
+ "showUserPageLink": app.config.Section("user_page").Key("show_link").MustBool(true),
+ "referralsEnabled": app.config.Section("user_page").Key("enabled").MustBool(false) && app.config.Section("user_page").Key("referrals").MustBool(false),
+ "loginAppearance": app.config.Section("ui").Key("login_appearance").MustString("clear"),
})
}
@@ -192,6 +194,7 @@ func (app *appContext) MyUserPage(gc *gin.Context) {
emailEnabled, _ := app.config.Section("invite_emails").Key("enabled").Bool()
notificationsEnabled, _ := app.config.Section("notifications").Key("enabled").Bool()
ombiEnabled := app.config.Section("ombi").Key("enabled").MustBool(false)
+ jellyseerrEnabled := app.config.Section("jellyseerr").Key("enabled").MustBool(false)
data := gin.H{
"urlBase": app.getURLBase(gc),
"cssClass": app.cssClass,
@@ -203,6 +206,7 @@ func (app *appContext) MyUserPage(gc *gin.Context) {
"discordEnabled": discordEnabled,
"matrixEnabled": matrixEnabled,
"ombiEnabled": ombiEnabled,
+ "jellyseerrEnabled": jellyseerrEnabled,
"pwrEnabled": app.config.Section("password_resets").Key("enabled").MustBool(false),
"linkResetEnabled": app.config.Section("password_resets").Key("link_reset").MustBool(false),
"notifications": notificationsEnabled,
@@ -278,6 +282,7 @@ func (app *appContext) ResetPassword(gc *gin.Context) {
"strings": app.storage.lang.PasswordReset[lang].Strings,
"success": false,
"ombiEnabled": app.config.Section("ombi").Key("enabled").MustBool(false),
+ "jellyseerrEnabled": app.config.Section("jellyseerr").Key("enabled").MustBool(false),
"customSuccessCard": false,
}
pwr, isInternal := app.internalPWRs[pin]