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"], +]);