From d6b4d16ff415979ff40051e985ee2bf993fe40a6 Mon Sep 17 00:00:00 2001
From: CJ <72030708+Teda1@users.noreply.github.com>
Date: Tue, 2 May 2023 20:33:32 -0700
Subject: [PATCH] Adds ability to configure sort order for DLNA videos (#3645)
---
graphql/documents/data/config.graphql | 1 +
graphql/schema/types/config.graphql | 4 +++
internal/api/resolver_mutation_configure.go | 4 +++
internal/api/resolver_query_configuration.go | 1 +
internal/dlna/cds.go | 11 +++++---
internal/dlna/dms.go | 1 +
internal/dlna/service.go | 4 +++
internal/manager/config/config.go | 14 ++++++++++
.../Settings/SettingsServicesPanel.tsx | 27 ++++++++++++++++++-
ui/v2.5/src/locales/en-GB.json | 4 ++-
ui/v2.5/src/utils/dlnaVideoSort.ts | 17 ++++++++++++
11 files changed, 83 insertions(+), 5 deletions(-)
create mode 100644 ui/v2.5/src/utils/dlnaVideoSort.ts
diff --git a/graphql/documents/data/config.graphql b/graphql/documents/data/config.graphql
index 173a7948e..a96341653 100644
--- a/graphql/documents/data/config.graphql
+++ b/graphql/documents/data/config.graphql
@@ -99,6 +99,7 @@ fragment ConfigDLNAData on ConfigDLNAResult {
enabled
whitelistedIPs
interfaces
+ videoSortOrder
}
fragment ConfigScrapingData on ConfigScrapingResult {
diff --git a/graphql/schema/types/config.graphql b/graphql/schema/types/config.graphql
index df0aba092..904d235dd 100644
--- a/graphql/schema/types/config.graphql
+++ b/graphql/schema/types/config.graphql
@@ -431,6 +431,8 @@ input ConfigDLNAInput {
whitelistedIPs: [String!]
"""List of interfaces to run DLNA on. Empty for all"""
interfaces: [String!]
+ """Order to sort videos"""
+ videoSortOrder: String
}
type ConfigDLNAResult {
@@ -441,6 +443,8 @@ type ConfigDLNAResult {
whitelistedIPs: [String!]!
"""List of interfaces to run DLNA on. Empty for all"""
interfaces: [String!]!
+ """Order to sort videos"""
+ videoSortOrder: String!
}
input ConfigScrapingInput {
diff --git a/internal/api/resolver_mutation_configure.go b/internal/api/resolver_mutation_configure.go
index 824f9e6d7..2a102af6e 100644
--- a/internal/api/resolver_mutation_configure.go
+++ b/internal/api/resolver_mutation_configure.go
@@ -493,6 +493,10 @@ func (r *mutationResolver) ConfigureDlna(ctx context.Context, input ConfigDLNAIn
c.Set(config.DLNADefaultIPWhitelist, input.WhitelistedIPs)
}
+ if input.VideoSortOrder != nil {
+ c.Set(config.DLNAVideoSortOrder, input.VideoSortOrder)
+ }
+
currentDLNAEnabled := c.GetDLNADefaultEnabled()
if input.Enabled != nil && *input.Enabled != currentDLNAEnabled {
c.Set(config.DLNADefaultEnabled, *input.Enabled)
diff --git a/internal/api/resolver_query_configuration.go b/internal/api/resolver_query_configuration.go
index fd598ce92..643aa263b 100644
--- a/internal/api/resolver_query_configuration.go
+++ b/internal/api/resolver_query_configuration.go
@@ -202,6 +202,7 @@ func makeConfigDLNAResult() *ConfigDLNAResult {
Enabled: config.GetDLNADefaultEnabled(),
WhitelistedIPs: config.GetDLNADefaultIPWhitelist(),
Interfaces: config.GetDLNAInterfaces(),
+ VideoSortOrder: config.GetVideoSortOrder(),
}
}
diff --git a/internal/dlna/cds.go b/internal/dlna/cds.go
index 4deb017f2..cf5deaa7c 100644
--- a/internal/dlna/cds.go
+++ b/internal/dlna/cds.go
@@ -444,10 +444,15 @@ func (me *contentDirectoryService) getVideos(sceneFilter *models.SceneFilterType
var objs []interface{}
if err := txn.WithReadTxn(context.TODO(), me.txnManager, func(ctx context.Context) error {
- sort := "title"
+ sort := me.VideoSortOrder
+ direction := models.SortDirectionEnumDesc
+ if sort == "title" {
+ direction = models.SortDirectionEnumAsc
+ }
findFilter := &models.FindFilterType{
- PerPage: &pageSize,
- Sort: &sort,
+ PerPage: &pageSize,
+ Sort: &sort,
+ Direction: &direction,
}
scenes, total, err := scene.QueryWithCount(ctx, me.repository.SceneFinder, sceneFilter, findFilter)
diff --git a/internal/dlna/dms.go b/internal/dlna/dms.go
index fdef80db1..502dbe0e4 100644
--- a/internal/dlna/dms.go
+++ b/internal/dlna/dms.go
@@ -276,6 +276,7 @@ type Server struct {
repository Repository
sceneServer sceneServer
ipWhitelistManager *ipWhitelistManager
+ VideoSortOrder string
}
// UPnP SOAP service.
diff --git a/internal/dlna/service.go b/internal/dlna/service.go
index a257b7f94..0d8932e08 100644
--- a/internal/dlna/service.go
+++ b/internal/dlna/service.go
@@ -45,6 +45,7 @@ type dmsConfig struct {
LogHeaders bool
StallEventSubscribe bool
NotifyInterval time.Duration
+ VideoSortOrder string
}
type sceneServer interface {
@@ -56,6 +57,7 @@ type Config interface {
GetDLNAInterfaces() []string
GetDLNAServerName() string
GetDLNADefaultIPWhitelist() []string
+ GetVideoSortOrder() string
}
type Service struct {
@@ -123,6 +125,7 @@ func (s *Service) init() error {
FriendlyName: friendlyName,
LogHeaders: false,
NotifyInterval: 30 * time.Second,
+ VideoSortOrder: s.config.GetVideoSortOrder(),
}
interfaces, err := s.getInterfaces()
@@ -164,6 +167,7 @@ func (s *Service) init() error {
// },
StallEventSubscribe: dmsConfig.StallEventSubscribe,
NotifyInterval: dmsConfig.NotifyInterval,
+ VideoSortOrder: dmsConfig.VideoSortOrder,
}
return nil
diff --git a/internal/manager/config/config.go b/internal/manager/config/config.go
index 4b2ba7921..fe9730219 100644
--- a/internal/manager/config/config.go
+++ b/internal/manager/config/config.go
@@ -210,6 +210,9 @@ const (
DLNADefaultIPWhitelist = "dlna.default_whitelist"
DLNAInterfaces = "dlna.interfaces"
+ DLNAVideoSortOrder = "dlna.video_sort_order"
+ dlnaVideoSortOrderDefault = "title"
+
// Logging options
LogFile = "logFile"
LogOut = "logOut"
@@ -1370,6 +1373,17 @@ func (i *Instance) GetDLNAInterfaces() []string {
return i.getStringSlice(DLNAInterfaces)
}
+// GetVideoSortOrder returns the sort order to display videos. If
+// empty, videos will be sorted by titles.
+func (i *Instance) GetVideoSortOrder() string {
+ ret := i.getString(DLNAVideoSortOrder)
+ if ret == "" {
+ ret = dlnaVideoSortOrderDefault
+ }
+
+ return ret
+}
+
// GetLogFile returns the filename of the file to output logs to.
// An empty string means that file logging will be disabled.
func (i *Instance) GetLogFile() string {
diff --git a/ui/v2.5/src/components/Settings/SettingsServicesPanel.tsx b/ui/v2.5/src/components/Settings/SettingsServicesPanel.tsx
index 2db88f926..38a1ccb79 100644
--- a/ui/v2.5/src/components/Settings/SettingsServicesPanel.tsx
+++ b/ui/v2.5/src/components/Settings/SettingsServicesPanel.tsx
@@ -14,8 +14,17 @@ import { Icon } from "../Shared/Icon";
import { LoadingIndicator } from "../Shared/LoadingIndicator";
import { ModalComponent } from "../Shared/Modal";
import { SettingSection } from "./SettingSection";
-import { BooleanSetting, StringListSetting, StringSetting } from "./Inputs";
+import {
+ BooleanSetting,
+ StringListSetting,
+ StringSetting,
+ SelectSetting,
+} from "./Inputs";
import { SettingStateContext } from "./context";
+import {
+ videoSortOrderIntlMap,
+ defaultVideoSort,
+} from "src/utils/dlnaVideoSort";
import {
faClock,
faTimes,
@@ -445,6 +454,22 @@ export const SettingsServicesPanel: React.FC = () => {
value={dlna.whitelistedIPs ?? undefined}
onChange={(v) => saveDLNA({ whitelistedIPs: v })}
/>
+
+ saveDLNA({ videoSortOrder: v })}
+ >
+ {Array.from(videoSortOrderIntlMap.entries()).map((v) => (
+
+ ))}
+
>
);
diff --git a/ui/v2.5/src/locales/en-GB.json b/ui/v2.5/src/locales/en-GB.json
index c232e0964..2272cd1c0 100644
--- a/ui/v2.5/src/locales/en-GB.json
+++ b/ui/v2.5/src/locales/en-GB.json
@@ -234,7 +234,9 @@
"server_display_name": "Server Display Name",
"server_display_name_desc": "Display name for the DLNA server. Defaults to {server_name} if empty.",
"successfully_cancelled_temporary_behaviour": "Successfully cancelled temporary behaviour",
- "until_restart": "until restart"
+ "until_restart": "until restart",
+ "video_sort_order": "Default Video Sort Order",
+ "video_sort_order_desc": "Order to sort videos by default."
},
"general": {
"auth": {
diff --git a/ui/v2.5/src/utils/dlnaVideoSort.ts b/ui/v2.5/src/utils/dlnaVideoSort.ts
new file mode 100644
index 000000000..8cd26e6f9
--- /dev/null
+++ b/ui/v2.5/src/utils/dlnaVideoSort.ts
@@ -0,0 +1,17 @@
+export enum VideoSortOrder {
+ Created_At = "created_at",
+ Date = "date",
+ Random = "random",
+ Title = "title",
+ Updated_At = "updated_at",
+}
+
+export const defaultVideoSort = VideoSortOrder.Title;
+
+export const videoSortOrderIntlMap = new Map([
+ [VideoSortOrder.Created_At, "created_at"],
+ [VideoSortOrder.Date, "date"],
+ [VideoSortOrder.Random, "random"],
+ [VideoSortOrder.Title, "title"],
+ [VideoSortOrder.Updated_At, "updated_at"],
+]);