From 8493c013e7b359d8200793f453e3c632c1986c56 Mon Sep 17 00:00:00 2001 From: WithoutPants <53250216+WithoutPants@users.noreply.github.com> Date: Fri, 29 Nov 2019 12:41:17 +1100 Subject: [PATCH] Loop and autostart flags. Save interface options (#230) --- graphql/documents/data/config.graphql | 4 ++ graphql/schema/types/config.graphql | 16 +++++ pkg/api/resolver_mutation_configure.go | 16 +++++ pkg/api/resolver_query_configuration.go | 13 +++- pkg/manager/config/config.go | 26 +++++++ .../Settings/SettingsInterfacePanel.tsx | 63 +++++++++++----- ui/v2/src/components/Wall/WallItem.tsx | 6 +- .../scenes/ScenePlayer/ScenePlayer.tsx | 71 ++++++++++++++----- ui/v2/src/hooks/LocalForage.ts | 5 +- ui/v2/src/hooks/VideoHover.ts | 6 +- 10 files changed, 179 insertions(+), 47 deletions(-) diff --git a/graphql/documents/data/config.graphql b/graphql/documents/data/config.graphql index 91bf0495b..3ea0d6c53 100644 --- a/graphql/documents/data/config.graphql +++ b/graphql/documents/data/config.graphql @@ -13,6 +13,10 @@ fragment ConfigGeneralData on ConfigGeneralResult { } fragment ConfigInterfaceData on ConfigInterfaceResult { + soundOnPreview + wallShowTitle + maximumLoopDuration + autostartVideo css cssEnabled } diff --git a/graphql/schema/types/config.graphql b/graphql/schema/types/config.graphql index 557281a98..f0be74e66 100644 --- a/graphql/schema/types/config.graphql +++ b/graphql/schema/types/config.graphql @@ -58,12 +58,28 @@ type ConfigGeneralResult { } input ConfigInterfaceInput { + """Enable sound on mouseover previews""" + soundOnPreview: Boolean + """Show title and tags in wall view""" + wallShowTitle: Boolean + """Maximum duration (in seconds) in which a scene video will loop in the scene player""" + maximumLoopDuration: Int + """If true, video will autostart on load in the scene player""" + autostartVideo: Boolean """Custom CSS""" css: String cssEnabled: Boolean } type ConfigInterfaceResult { + """Enable sound on mouseover previews""" + soundOnPreview: Boolean + """Show title and tags in wall view""" + wallShowTitle: Boolean + """Maximum duration (in seconds) in which a scene video will loop in the scene player""" + maximumLoopDuration: Int + """If true, video will autostart on load in the scene player""" + autostartVideo: Boolean """Custom CSS""" css: String cssEnabled: Boolean diff --git a/pkg/api/resolver_mutation_configure.go b/pkg/api/resolver_mutation_configure.go index e29a8a9dc..32d31d2b2 100644 --- a/pkg/api/resolver_mutation_configure.go +++ b/pkg/api/resolver_mutation_configure.go @@ -82,6 +82,22 @@ func (r *mutationResolver) ConfigureGeneral(ctx context.Context, input models.Co } func (r *mutationResolver) ConfigureInterface(ctx context.Context, input models.ConfigInterfaceInput) (*models.ConfigInterfaceResult, error) { + if input.SoundOnPreview != nil { + config.Set(config.SoundOnPreview, *input.SoundOnPreview) + } + + if input.WallShowTitle != nil { + config.Set(config.WallShowTitle, *input.WallShowTitle) + } + + if input.MaximumLoopDuration != nil { + config.Set(config.MaximumLoopDuration, *input.MaximumLoopDuration) + } + + if input.AutostartVideo != nil { + config.Set(config.AutostartVideo, *input.AutostartVideo) + } + css := "" if input.CSS != nil { diff --git a/pkg/api/resolver_query_configuration.go b/pkg/api/resolver_query_configuration.go index d9da7f6b9..b1961d5a3 100644 --- a/pkg/api/resolver_query_configuration.go +++ b/pkg/api/resolver_query_configuration.go @@ -49,10 +49,19 @@ func makeConfigGeneralResult() *models.ConfigGeneralResult { } func makeConfigInterfaceResult() *models.ConfigInterfaceResult { + soundOnPreview := config.GetSoundOnPreview() + wallShowTitle := config.GetWallShowTitle() + maximumLoopDuration := config.GetMaximumLoopDuration() + autostartVideo := config.GetAutostartVideo() css := config.GetCSS() cssEnabled := config.GetCSSEnabled() + return &models.ConfigInterfaceResult{ - CSS: &css, - CSSEnabled: &cssEnabled, + SoundOnPreview: &soundOnPreview, + WallShowTitle: &wallShowTitle, + MaximumLoopDuration: &maximumLoopDuration, + AutostartVideo: &autostartVideo, + CSS: &css, + CSSEnabled: &cssEnabled, } } diff --git a/pkg/manager/config/config.go b/pkg/manager/config/config.go index 3d64de6fd..7f566c962 100644 --- a/pkg/manager/config/config.go +++ b/pkg/manager/config/config.go @@ -30,6 +30,11 @@ const MaxStreamingTranscodeSize = "max_streaming_transcode_size" const Host = "host" const Port = "port" +// Interface options +const SoundOnPreview = "sound_on_preview" +const WallShowTitle = "wall_show_title" +const MaximumLoopDuration = "maximum_loop_duration" +const AutostartVideo = "autostart_video" const CSSEnabled = "cssEnabled" // Logging options @@ -165,6 +170,27 @@ func ValidateCredentials(username string, password string) bool { return username == authUser && err == nil } +// Interface options +func GetSoundOnPreview() bool { + viper.SetDefault(SoundOnPreview, true) + return viper.GetBool(SoundOnPreview) +} + +func GetWallShowTitle() bool { + viper.SetDefault(WallShowTitle, true) + return viper.GetBool(WallShowTitle) +} + +func GetMaximumLoopDuration() int { + viper.SetDefault(MaximumLoopDuration, 0) + return viper.GetInt(MaximumLoopDuration) +} + +func GetAutostartVideo() bool { + viper.SetDefault(AutostartVideo, false) + return viper.GetBool(AutostartVideo) +} + func GetCSSPath() string { // use custom.css in the same directory as the config file configFileUsed := viper.ConfigFileUsed() diff --git a/ui/v2/src/components/Settings/SettingsInterfacePanel.tsx b/ui/v2/src/components/Settings/SettingsInterfacePanel.tsx index 39982ccf2..b74beb240 100644 --- a/ui/v2/src/components/Settings/SettingsInterfacePanel.tsx +++ b/ui/v2/src/components/Settings/SettingsInterfacePanel.tsx @@ -5,11 +5,11 @@ import { FormGroup, H4, Spinner, - TextArea + TextArea, + NumericInput } from "@blueprintjs/core"; import _ from "lodash"; import React, { FunctionComponent, useEffect, useState } from "react"; -import { useInterfaceLocalForage } from "../../hooks/LocalForage"; import { StashService } from "../../core/StashService"; import { ErrorUtils } from "../../utils/errors"; import { ToastUtils } from "../../utils/toasts"; @@ -17,12 +17,19 @@ import { ToastUtils } from "../../utils/toasts"; interface IProps {} export const SettingsInterfacePanel: FunctionComponent = () => { - const {data, setData} = useInterfaceLocalForage(); const config = StashService.useConfiguration(); + const [soundOnPreview, setSoundOnPreview] = useState(); + const [wallShowTitle, setWallShowTitle] = useState(); + const [maximumLoopDuration, setMaximumLoopDuration] = useState(0); + const [autostartVideo, setAutostartVideo] = useState(); const [css, setCSS] = useState(); const [cssEnabled, setCSSEnabled] = useState(); const updateInterfaceConfig = StashService.useConfigureInterface({ + soundOnPreview, + wallShowTitle, + maximumLoopDuration, + autostartVideo, css, cssEnabled }); @@ -30,6 +37,11 @@ export const SettingsInterfacePanel: FunctionComponent = () => { useEffect(() => { if (!config.data || !config.data.configuration || !!config.error) { return; } if (!!config.data.configuration.interface) { + let iCfg = config.data.configuration.interface; + setSoundOnPreview(iCfg.soundOnPreview !== undefined ? iCfg.soundOnPreview : true); + setWallShowTitle(iCfg.wallShowTitle !== undefined ? iCfg.wallShowTitle : true); + setMaximumLoopDuration(iCfg.maximumLoopDuration || 0); + setAutostartVideo(iCfg.autostartVideo !== undefined ? iCfg.autostartVideo : false); setCSS(config.data.configuration.interface.css || ""); setCSSEnabled(config.data.configuration.interface.cssEnabled || false); } @@ -55,25 +67,40 @@ export const SettingsInterfacePanel: FunctionComponent = () => { helperText="Configuration for wall items" > { - if (!data) { return; } - const newSettings = _.cloneDeep(data); - newSettings.wall.textContainerEnabled = !data.wall.textContainerEnabled; - setData(newSettings); - }} + onChange={() => setWallShowTitle(!wallShowTitle)} /> setSoundOnPreview(!soundOnPreview)} + /> + + + + { - if (!data) { return; } - const newSettings = _.cloneDeep(data); - newSettings.wall.soundEnabled = !data.wall.soundEnabled; - setData(newSettings); + setAutostartVideo(!autostartVideo) }} /> + + + setMaximumLoopDuration(value)} + min={0} + minorStepSize={1} + /> + = () => { fill={true} rows={16}> - - - + + + ); }; diff --git a/ui/v2/src/components/Wall/WallItem.tsx b/ui/v2/src/components/Wall/WallItem.tsx index eda6ec6ba..05da210d0 100644 --- a/ui/v2/src/components/Wall/WallItem.tsx +++ b/ui/v2/src/components/Wall/WallItem.tsx @@ -2,10 +2,10 @@ import _ from "lodash"; import React, { FunctionComponent, useRef, useState, useEffect } from "react"; import { Link } from "react-router-dom"; import * as GQL from "../../core/generated-graphql"; -import { useInterfaceLocalForage } from "../../hooks/LocalForage"; import { VideoHoverHook } from "../../hooks/VideoHover"; import { TextUtils } from "../../utils/text"; import { NavigationUtils } from "../../utils/navigation"; +import { StashService } from "../../core/StashService"; interface IWallItemProps { scene?: GQL.SlimSceneDataFragment; @@ -29,9 +29,9 @@ export const WallItem: FunctionComponent = (props: IWallItemProp const [screenshotPath, setScreenshotPath] = useState(""); const [title, setTitle] = useState(""); const [tags, setTags] = useState([]); + const config = StashService.useConfiguration(); const videoHoverHook = VideoHoverHook.useVideoHover({resetOnMouseLeave: true}); - const interfaceSettings = useInterfaceLocalForage(); - const showTextContainer = !!interfaceSettings.data ? interfaceSettings.data.wall.textContainerEnabled : true; + const showTextContainer = !!config.data ? config.data.configuration.interface.wallShowTitle : true; function onMouseEnter() { VideoHoverHook.onMouseEnter(videoHoverHook); diff --git a/ui/v2/src/components/scenes/ScenePlayer/ScenePlayer.tsx b/ui/v2/src/components/scenes/ScenePlayer/ScenePlayer.tsx index 1b33408d5..f5122b44d 100644 --- a/ui/v2/src/components/scenes/ScenePlayer/ScenePlayer.tsx +++ b/ui/v2/src/components/scenes/ScenePlayer/ScenePlayer.tsx @@ -1,11 +1,12 @@ import { Hotkey, Hotkeys, HotkeysTarget } from "@blueprintjs/core"; -import React from "react"; +import React, { Component, FunctionComponent } from "react"; import ReactJWPlayer from "react-jw-player"; import * as GQL from "../../../core/generated-graphql"; import { SceneHelpers } from "../helpers"; import { ScenePlayerScrubber } from "./ScenePlayerScrubber"; import videojs from "video.js"; import "video.js/dist/video-js.css"; +import { StashService } from "../../../core/StashService"; interface IScenePlayerProps { scene: GQL.SceneDataFragment; @@ -13,21 +14,26 @@ interface IScenePlayerProps { onReady?: any; onSeeked?: any; onTime?: any; + config?: GQL.ConfigInterfaceDataFragment; } interface IScenePlayerState { scrubberPosition: number; } -export class VideoJSPlayer extends React.Component { +interface IVideoJSPlayerProps extends IScenePlayerProps { + videoJSOptions: videojs.PlayerOptions +} + +export class VideoJSPlayer extends React.Component { private player: any; private videoNode: any; - constructor(props: IScenePlayerProps) { + constructor(props: IVideoJSPlayerProps) { super(props); } componentDidMount() { - this.player = videojs(this.videoNode); + this.player = videojs(this.videoNode, this.props.videoJSOptions); // dirty hack - make this player look like JWPlayer this.player.seek = this.player.currentTime; @@ -92,7 +98,7 @@ export class VideoJSPlayer extends React.Component { } @HotkeysTarget -export class ScenePlayer extends React.Component { +export class ScenePlayerImpl extends React.Component { private player: any; private lastTime = 0; @@ -116,7 +122,7 @@ export class ScenePlayer extends React.Component ); } else { - return ( - - - ) + // don't render videoJS until config is loaded + if (this.props.config) { + const config = this.makeVideoJSConfig(this.props.scene); + return ( + + + ) + } } } @@ -194,8 +205,16 @@ export class ScenePlayer extends React.Component 0) { @@ -252,3 +283,9 @@ export class ScenePlayer extends React.Component = (props: IScenePlayerProps) => { + const config = StashService.useConfiguration(); + + return +} diff --git a/ui/v2/src/hooks/LocalForage.ts b/ui/v2/src/hooks/LocalForage.ts index 1cee93bdd..0283be9c6 100644 --- a/ui/v2/src/hooks/LocalForage.ts +++ b/ui/v2/src/hooks/LocalForage.ts @@ -3,8 +3,6 @@ import _ from "lodash"; import React from "react"; interface IInterfaceWallConfig { - textContainerEnabled: boolean; - soundEnabled: boolean; } export interface IInterfaceConfig { wall: IInterfaceWallConfig; @@ -25,8 +23,7 @@ export function useInterfaceLocalForage(): ILocalForage; @@ -18,8 +18,8 @@ export class VideoHoverHook { const isPlaying = useRef(false); const isHovering = useRef(false); - const interfaceSettings = useInterfaceLocalForage(); - const soundEnabled = !!interfaceSettings.data ? interfaceSettings.data.wall.soundEnabled : true; + const config = StashService.useConfiguration(); + const soundEnabled = !!config.data && !!config.data.configuration ? config.data.configuration.interface.soundOnPreview : true; useEffect(() => { const videoTag = videoEl.current;