mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 12:24:38 +03:00
Add configurable transcode sizes (#178)
This commit is contained in:
@@ -2,6 +2,8 @@ fragment ConfigGeneralData on ConfigGeneralResult {
|
|||||||
stashes
|
stashes
|
||||||
databasePath
|
databasePath
|
||||||
generatedPath
|
generatedPath
|
||||||
|
maxTranscodeSize
|
||||||
|
maxStreamingTranscodeSize
|
||||||
username
|
username
|
||||||
password
|
password
|
||||||
logFile
|
logFile
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
enum StreamingResolutionEnum {
|
||||||
|
"240p", LOW
|
||||||
|
"480p", STANDARD
|
||||||
|
"720p", STANDARD_HD
|
||||||
|
"1080p", FULL_HD
|
||||||
|
"4k", FOUR_K
|
||||||
|
"Original", ORIGINAL
|
||||||
|
}
|
||||||
|
|
||||||
input ConfigGeneralInput {
|
input ConfigGeneralInput {
|
||||||
"""Array of file paths to content"""
|
"""Array of file paths to content"""
|
||||||
stashes: [String!]
|
stashes: [String!]
|
||||||
@@ -5,6 +14,10 @@ input ConfigGeneralInput {
|
|||||||
databasePath: String
|
databasePath: String
|
||||||
"""Path to generated files"""
|
"""Path to generated files"""
|
||||||
generatedPath: String
|
generatedPath: String
|
||||||
|
"""Max generated transcode size"""
|
||||||
|
maxTranscodeSize: StreamingResolutionEnum
|
||||||
|
"""Max streaming transcode size"""
|
||||||
|
maxStreamingTranscodeSize: StreamingResolutionEnum
|
||||||
"""Username"""
|
"""Username"""
|
||||||
username: String
|
username: String
|
||||||
"""Password"""
|
"""Password"""
|
||||||
@@ -26,6 +39,10 @@ type ConfigGeneralResult {
|
|||||||
databasePath: String!
|
databasePath: String!
|
||||||
"""Path to generated files"""
|
"""Path to generated files"""
|
||||||
generatedPath: String!
|
generatedPath: String!
|
||||||
|
"""Max generated transcode size"""
|
||||||
|
maxTranscodeSize: StreamingResolutionEnum
|
||||||
|
"""Max streaming transcode size"""
|
||||||
|
maxStreamingTranscodeSize: StreamingResolutionEnum
|
||||||
"""Username"""
|
"""Username"""
|
||||||
username: String!
|
username: String!
|
||||||
"""Password"""
|
"""Password"""
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/manager/config"
|
|
||||||
"github.com/stashapp/stash/pkg/logger"
|
"github.com/stashapp/stash/pkg/logger"
|
||||||
|
"github.com/stashapp/stash/pkg/manager/config"
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/utils"
|
"github.com/stashapp/stash/pkg/utils"
|
||||||
)
|
)
|
||||||
@@ -37,6 +37,14 @@ func (r *mutationResolver) ConfigureGeneral(ctx context.Context, input models.Co
|
|||||||
config.Set(config.Generated, input.GeneratedPath)
|
config.Set(config.Generated, input.GeneratedPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if input.MaxTranscodeSize != nil {
|
||||||
|
config.Set(config.MaxTranscodeSize, input.MaxTranscodeSize.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.MaxStreamingTranscodeSize != nil {
|
||||||
|
config.Set(config.MaxStreamingTranscodeSize, input.MaxStreamingTranscodeSize.String())
|
||||||
|
}
|
||||||
|
|
||||||
if input.Username != nil {
|
if input.Username != nil {
|
||||||
config.Set(config.Username, input.Username)
|
config.Set(config.Username, input.Username)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,16 +29,22 @@ func makeConfigResult() *models.ConfigResult {
|
|||||||
|
|
||||||
func makeConfigGeneralResult() *models.ConfigGeneralResult {
|
func makeConfigGeneralResult() *models.ConfigGeneralResult {
|
||||||
logFile := config.GetLogFile()
|
logFile := config.GetLogFile()
|
||||||
|
|
||||||
|
maxTranscodeSize := config.GetMaxTranscodeSize()
|
||||||
|
maxStreamingTranscodeSize := config.GetMaxStreamingTranscodeSize()
|
||||||
|
|
||||||
return &models.ConfigGeneralResult{
|
return &models.ConfigGeneralResult{
|
||||||
Stashes: config.GetStashPaths(),
|
Stashes: config.GetStashPaths(),
|
||||||
DatabasePath: config.GetDatabasePath(),
|
DatabasePath: config.GetDatabasePath(),
|
||||||
GeneratedPath: config.GetGeneratedPath(),
|
GeneratedPath: config.GetGeneratedPath(),
|
||||||
Username: config.GetUsername(),
|
MaxTranscodeSize: &maxTranscodeSize,
|
||||||
Password: config.GetPasswordHash(),
|
MaxStreamingTranscodeSize: &maxStreamingTranscodeSize,
|
||||||
LogFile: &logFile,
|
Username: config.GetUsername(),
|
||||||
LogOut: config.GetLogOut(),
|
Password: config.GetPasswordHash(),
|
||||||
LogLevel: config.GetLogLevel(),
|
LogFile: &logFile,
|
||||||
LogAccess: config.GetLogAccess(),
|
LogOut: config.GetLogOut(),
|
||||||
|
LogLevel: config.GetLogLevel(),
|
||||||
|
LogAccess: config.GetLogAccess(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/stashapp/stash/pkg/ffmpeg"
|
"github.com/stashapp/stash/pkg/ffmpeg"
|
||||||
"github.com/stashapp/stash/pkg/logger"
|
"github.com/stashapp/stash/pkg/logger"
|
||||||
"github.com/stashapp/stash/pkg/manager"
|
"github.com/stashapp/stash/pkg/manager"
|
||||||
|
"github.com/stashapp/stash/pkg/manager/config"
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/utils"
|
"github.com/stashapp/stash/pkg/utils"
|
||||||
)
|
)
|
||||||
@@ -68,7 +69,7 @@ func (rs sceneRoutes) Stream(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
encoder := ffmpeg.NewEncoder(manager.GetInstance().FFMPEGPath)
|
encoder := ffmpeg.NewEncoder(manager.GetInstance().FFMPEGPath)
|
||||||
|
|
||||||
stream, process, err := encoder.StreamTranscode(*videoFile, startTime)
|
stream, process, err := encoder.StreamTranscode(*videoFile, startTime, config.GetMaxStreamingTranscodeSize())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("[stream] error transcoding video file: %s", err.Error())
|
logger.Errorf("[stream] error transcoding video file: %s", err.Error())
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -3,13 +3,56 @@ package ffmpeg
|
|||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/stashapp/stash/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TranscodeOptions struct {
|
type TranscodeOptions struct {
|
||||||
OutputPath string
|
OutputPath string
|
||||||
|
MaxTranscodeSize models.StreamingResolutionEnum
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateTranscodeScale(probeResult VideoFile, maxTranscodeSize models.StreamingResolutionEnum) string {
|
||||||
|
maxSize := 0
|
||||||
|
switch maxTranscodeSize {
|
||||||
|
case models.StreamingResolutionEnumLow:
|
||||||
|
maxSize = 240
|
||||||
|
case models.StreamingResolutionEnumStandard:
|
||||||
|
maxSize = 480
|
||||||
|
case models.StreamingResolutionEnumStandardHd:
|
||||||
|
maxSize = 720
|
||||||
|
case models.StreamingResolutionEnumFullHd:
|
||||||
|
maxSize = 1080
|
||||||
|
case models.StreamingResolutionEnumFourK:
|
||||||
|
maxSize = 2160
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the smaller dimension of the video file
|
||||||
|
videoSize := probeResult.Height
|
||||||
|
if probeResult.Width < videoSize {
|
||||||
|
videoSize = probeResult.Width
|
||||||
|
}
|
||||||
|
|
||||||
|
// if our streaming resolution is larger than the video dimension
|
||||||
|
// or we are streaming the original resolution, then just set the
|
||||||
|
// input width
|
||||||
|
if maxSize >= videoSize || maxSize == 0 {
|
||||||
|
return "iw:-2"
|
||||||
|
}
|
||||||
|
|
||||||
|
// we're setting either the width or height
|
||||||
|
// we'll set the smaller dimesion
|
||||||
|
if probeResult.Width > probeResult.Height {
|
||||||
|
// set the height
|
||||||
|
return "-2:" + strconv.Itoa(maxSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strconv.Itoa(maxSize) + ":-2"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) Transcode(probeResult VideoFile, options TranscodeOptions) {
|
func (e *Encoder) Transcode(probeResult VideoFile, options TranscodeOptions) {
|
||||||
|
scale := calculateTranscodeScale(probeResult, options.MaxTranscodeSize)
|
||||||
args := []string{
|
args := []string{
|
||||||
"-i", probeResult.Path,
|
"-i", probeResult.Path,
|
||||||
"-c:v", "libx264",
|
"-c:v", "libx264",
|
||||||
@@ -18,7 +61,7 @@ func (e *Encoder) Transcode(probeResult VideoFile, options TranscodeOptions) {
|
|||||||
"-level", "4.2",
|
"-level", "4.2",
|
||||||
"-preset", "superfast",
|
"-preset", "superfast",
|
||||||
"-crf", "23",
|
"-crf", "23",
|
||||||
"-vf", "scale=iw:-2",
|
"-vf", "scale=" + scale,
|
||||||
"-c:a", "aac",
|
"-c:a", "aac",
|
||||||
"-strict", "-2",
|
"-strict", "-2",
|
||||||
options.OutputPath,
|
options.OutputPath,
|
||||||
@@ -26,7 +69,8 @@ func (e *Encoder) Transcode(probeResult VideoFile, options TranscodeOptions) {
|
|||||||
_, _ = e.run(probeResult, args)
|
_, _ = e.run(probeResult, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) StreamTranscode(probeResult VideoFile, startTime string) (io.ReadCloser, *os.Process, error) {
|
func (e *Encoder) StreamTranscode(probeResult VideoFile, startTime string, maxTranscodeSize models.StreamingResolutionEnum) (io.ReadCloser, *os.Process, error) {
|
||||||
|
scale := calculateTranscodeScale(probeResult, maxTranscodeSize)
|
||||||
args := []string{}
|
args := []string{}
|
||||||
|
|
||||||
if startTime != "" {
|
if startTime != "" {
|
||||||
@@ -36,7 +80,7 @@ func (e *Encoder) StreamTranscode(probeResult VideoFile, startTime string) (io.R
|
|||||||
args = append(args,
|
args = append(args,
|
||||||
"-i", probeResult.Path,
|
"-i", probeResult.Path,
|
||||||
"-c:v", "libvpx-vp9",
|
"-c:v", "libvpx-vp9",
|
||||||
"-vf", "scale=iw:-2",
|
"-vf", "scale="+scale,
|
||||||
"-deadline", "realtime",
|
"-deadline", "realtime",
|
||||||
"-cpu-used", "5",
|
"-cpu-used", "5",
|
||||||
"-row-mt", "1",
|
"-row-mt", "1",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/utils"
|
"github.com/stashapp/stash/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -21,6 +22,9 @@ const Password = "password"
|
|||||||
|
|
||||||
const Database = "database"
|
const Database = "database"
|
||||||
|
|
||||||
|
const MaxTranscodeSize = "max_transcode_size"
|
||||||
|
const MaxStreamingTranscodeSize = "max_streaming_transcode_size"
|
||||||
|
|
||||||
const Host = "host"
|
const Host = "host"
|
||||||
const Port = "port"
|
const Port = "port"
|
||||||
|
|
||||||
@@ -77,6 +81,28 @@ func GetPort() int {
|
|||||||
return viper.GetInt(Port)
|
return viper.GetInt(Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetMaxTranscodeSize() models.StreamingResolutionEnum {
|
||||||
|
ret := viper.GetString(MaxTranscodeSize)
|
||||||
|
|
||||||
|
// default to original
|
||||||
|
if ret == "" {
|
||||||
|
return models.StreamingResolutionEnumOriginal
|
||||||
|
}
|
||||||
|
|
||||||
|
return models.StreamingResolutionEnum(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMaxStreamingTranscodeSize() models.StreamingResolutionEnum {
|
||||||
|
ret := viper.GetString(MaxStreamingTranscodeSize)
|
||||||
|
|
||||||
|
// default to original
|
||||||
|
if ret == "" {
|
||||||
|
return models.StreamingResolutionEnumOriginal
|
||||||
|
}
|
||||||
|
|
||||||
|
return models.StreamingResolutionEnum(ret)
|
||||||
|
}
|
||||||
|
|
||||||
func GetUsername() string {
|
func GetUsername() string {
|
||||||
return viper.GetString(Username)
|
return viper.GetString(Username)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package manager
|
package manager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stashapp/stash/pkg/ffmpeg"
|
|
||||||
"github.com/stashapp/stash/pkg/logger"
|
|
||||||
"github.com/stashapp/stash/pkg/models"
|
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/stashapp/stash/pkg/ffmpeg"
|
||||||
|
"github.com/stashapp/stash/pkg/logger"
|
||||||
|
"github.com/stashapp/stash/pkg/manager/config"
|
||||||
|
"github.com/stashapp/stash/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GenerateTranscodeTask struct {
|
type GenerateTranscodeTask struct {
|
||||||
@@ -33,8 +35,10 @@ func (t *GenerateTranscodeTask) Start(wg *sync.WaitGroup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
outputPath := instance.Paths.Generated.GetTmpPath(t.Scene.Checksum + ".mp4")
|
outputPath := instance.Paths.Generated.GetTmpPath(t.Scene.Checksum + ".mp4")
|
||||||
|
transcodeSize := config.GetMaxTranscodeSize()
|
||||||
options := ffmpeg.TranscodeOptions{
|
options := ffmpeg.TranscodeOptions{
|
||||||
OutputPath: outputPath,
|
OutputPath: outputPath,
|
||||||
|
MaxTranscodeSize: transcodeSize,
|
||||||
}
|
}
|
||||||
encoder := ffmpeg.NewEncoder(instance.FFMPEGPath)
|
encoder := ffmpeg.NewEncoder(instance.FFMPEGPath)
|
||||||
encoder.Transcode(*videoFile, options)
|
encoder.Transcode(*videoFile, options)
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IPr
|
|||||||
const [stashes, setStashes] = useState<string[]>([]);
|
const [stashes, setStashes] = useState<string[]>([]);
|
||||||
const [databasePath, setDatabasePath] = useState<string | undefined>(undefined);
|
const [databasePath, setDatabasePath] = useState<string | undefined>(undefined);
|
||||||
const [generatedPath, setGeneratedPath] = useState<string | undefined>(undefined);
|
const [generatedPath, setGeneratedPath] = useState<string | undefined>(undefined);
|
||||||
|
const [maxTranscodeSize, setMaxTranscodeSize] = useState<GQL.StreamingResolutionEnum | undefined>(undefined);
|
||||||
|
const [maxStreamingTranscodeSize, setMaxStreamingTranscodeSize] = useState<GQL.StreamingResolutionEnum | undefined>(undefined);
|
||||||
const [username, setUsername] = useState<string | undefined>(undefined);
|
const [username, setUsername] = useState<string | undefined>(undefined);
|
||||||
const [password, setPassword] = useState<string | undefined>(undefined);
|
const [password, setPassword] = useState<string | undefined>(undefined);
|
||||||
const [logFile, setLogFile] = useState<string | undefined>();
|
const [logFile, setLogFile] = useState<string | undefined>();
|
||||||
@@ -38,6 +40,8 @@ export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IPr
|
|||||||
stashes,
|
stashes,
|
||||||
databasePath,
|
databasePath,
|
||||||
generatedPath,
|
generatedPath,
|
||||||
|
maxTranscodeSize,
|
||||||
|
maxStreamingTranscodeSize,
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
logFile,
|
logFile,
|
||||||
@@ -53,6 +57,8 @@ export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IPr
|
|||||||
setStashes(conf.general.stashes || []);
|
setStashes(conf.general.stashes || []);
|
||||||
setDatabasePath(conf.general.databasePath);
|
setDatabasePath(conf.general.databasePath);
|
||||||
setGeneratedPath(conf.general.generatedPath);
|
setGeneratedPath(conf.general.generatedPath);
|
||||||
|
setMaxTranscodeSize(conf.general.maxTranscodeSize);
|
||||||
|
setMaxStreamingTranscodeSize(conf.general.maxStreamingTranscodeSize);
|
||||||
setUsername(conf.general.username);
|
setUsername(conf.general.username);
|
||||||
setPassword(conf.general.password);
|
setPassword(conf.general.password);
|
||||||
setLogFile(conf.general.logFile);
|
setLogFile(conf.general.logFile);
|
||||||
@@ -76,46 +82,114 @@ export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IPr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const transcodeQualities = [
|
||||||
|
GQL.StreamingResolutionEnum.Low,
|
||||||
|
GQL.StreamingResolutionEnum.Standard,
|
||||||
|
GQL.StreamingResolutionEnum.StandardHd,
|
||||||
|
GQL.StreamingResolutionEnum.FullHd,
|
||||||
|
GQL.StreamingResolutionEnum.FourK,
|
||||||
|
GQL.StreamingResolutionEnum.Original
|
||||||
|
].map(resolutionToString);
|
||||||
|
|
||||||
|
function resolutionToString(r : GQL.StreamingResolutionEnum | undefined) {
|
||||||
|
switch (r) {
|
||||||
|
case GQL.StreamingResolutionEnum.Low: return "240p";
|
||||||
|
case GQL.StreamingResolutionEnum.Standard: return "480p";
|
||||||
|
case GQL.StreamingResolutionEnum.StandardHd: return "720p";
|
||||||
|
case GQL.StreamingResolutionEnum.FullHd: return "1080p";
|
||||||
|
case GQL.StreamingResolutionEnum.FourK: return "4k";
|
||||||
|
case GQL.StreamingResolutionEnum.Original: return "Original";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Original";
|
||||||
|
}
|
||||||
|
|
||||||
|
function translateQuality(quality : string) {
|
||||||
|
switch (quality) {
|
||||||
|
case "240p": return GQL.StreamingResolutionEnum.Low;
|
||||||
|
case "480p": return GQL.StreamingResolutionEnum.Standard;
|
||||||
|
case "720p": return GQL.StreamingResolutionEnum.StandardHd;
|
||||||
|
case "1080p": return GQL.StreamingResolutionEnum.FullHd;
|
||||||
|
case "4k": return GQL.StreamingResolutionEnum.FourK;
|
||||||
|
case "Original": return GQL.StreamingResolutionEnum.Original;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GQL.StreamingResolutionEnum.Original;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!!error ? <h1>{error.message}</h1> : undefined}
|
{!!error ? <h1>{error.message}</h1> : undefined}
|
||||||
{(!data || !data.configuration || loading) ? <Spinner size={Spinner.SIZE_LARGE} /> : undefined}
|
{(!data || !data.configuration || loading) ? <Spinner size={Spinner.SIZE_LARGE} /> : undefined}
|
||||||
<H4>Library</H4>
|
<H4>Library</H4>
|
||||||
<FormGroup
|
<FormGroup>
|
||||||
label="Stashes"
|
<FormGroup>
|
||||||
helperText="Directory locations to your content"
|
<FormGroup
|
||||||
>
|
label="Stashes"
|
||||||
<FolderSelect
|
helperText="Directory locations to your content"
|
||||||
directories={stashes}
|
>
|
||||||
onDirectoriesChanged={onStashesChanged}
|
<FolderSelect
|
||||||
/>
|
directories={stashes}
|
||||||
</FormGroup>
|
onDirectoriesChanged={onStashesChanged}
|
||||||
<FormGroup
|
/>
|
||||||
label="Database Path"
|
</FormGroup>
|
||||||
helperText="File location for the SQLite database (requires restart)"
|
</FormGroup>
|
||||||
>
|
|
||||||
<InputGroup value={databasePath} onChange={(e: any) => setDatabasePath(e.target.value)} />
|
<FormGroup
|
||||||
</FormGroup>
|
label="Database Path"
|
||||||
<FormGroup
|
helperText="File location for the SQLite database (requires restart)"
|
||||||
label="Generated Path"
|
>
|
||||||
helperText="Directory location for the generated files (scene markers, scene previews, sprites, etc)"
|
<InputGroup value={databasePath} onChange={(e: any) => setDatabasePath(e.target.value)} />
|
||||||
>
|
</FormGroup>
|
||||||
<InputGroup value={generatedPath} onChange={(e: any) => setGeneratedPath(e.target.value)} />
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<Divider />
|
<FormGroup
|
||||||
<H4>Authentication</H4>
|
label="Generated Path"
|
||||||
<FormGroup
|
helperText="Directory location for the generated files (scene markers, scene previews, sprites, etc)"
|
||||||
label="Username"
|
>
|
||||||
helperText="Username to access Stash. Leave blank to disable user authentication"
|
<InputGroup value={generatedPath} onChange={(e: any) => setGeneratedPath(e.target.value)} />
|
||||||
>
|
</FormGroup>
|
||||||
<InputGroup value={username} onChange={(e: any) => setUsername(e.target.value)} />
|
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup
|
|
||||||
label="Password"
|
<Divider />
|
||||||
helperText="Password to access Stash. Leave blank to disable user authentication"
|
<FormGroup>
|
||||||
>
|
<H4>Video</H4>
|
||||||
<InputGroup type="password" value={password} onChange={(e: any) => setPassword(e.target.value)} />
|
<FormGroup
|
||||||
|
label="Maximum transcode size"
|
||||||
|
helperText="Maximum size for generated transcodes"
|
||||||
|
>
|
||||||
|
<HTMLSelect
|
||||||
|
options={transcodeQualities}
|
||||||
|
onChange={(event) => setMaxTranscodeSize(translateQuality(event.target.value))}
|
||||||
|
value={resolutionToString(maxTranscodeSize)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
label="Maximum streaming transcode size"
|
||||||
|
helperText="Maximum size for transcoded streams"
|
||||||
|
>
|
||||||
|
<HTMLSelect
|
||||||
|
options={transcodeQualities}
|
||||||
|
onChange={(event) => setMaxStreamingTranscodeSize(translateQuality(event.target.value))}
|
||||||
|
value={resolutionToString(maxStreamingTranscodeSize)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</FormGroup>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<H4>Authentication</H4>
|
||||||
|
<FormGroup
|
||||||
|
label="Username"
|
||||||
|
helperText="Username to access Stash. Leave blank to disable user authentication"
|
||||||
|
>
|
||||||
|
<InputGroup value={username} onChange={(e: any) => setUsername(e.target.value)} />
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
label="Password"
|
||||||
|
helperText="Password to access Stash. Leave blank to disable user authentication"
|
||||||
|
>
|
||||||
|
<InputGroup type="password" value={password} onChange={(e: any) => setPassword(e.target.value)} />
|
||||||
|
</FormGroup>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
Dialog,
|
Dialog,
|
||||||
InputGroup,
|
InputGroup,
|
||||||
Spinner,
|
Spinner,
|
||||||
|
FormGroup,
|
||||||
} from "@blueprintjs/core";
|
} from "@blueprintjs/core";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||||
@@ -76,9 +77,12 @@ export const FolderSelect: FunctionComponent<IProps> = (props: IProps) => {
|
|||||||
<>
|
<>
|
||||||
{!!error ? <h1>{error.message}</h1> : undefined}
|
{!!error ? <h1>{error.message}</h1> : undefined}
|
||||||
{renderDialog()}
|
{renderDialog()}
|
||||||
{selectedDirectories.map((path) => {
|
<FormGroup>
|
||||||
return <div key={path}>{path} <a onClick={() => onRemoveDirectory(path)}>Remove</a></div>;
|
{selectedDirectories.map((path) => {
|
||||||
})}
|
return <div key={path}>{path} <a onClick={() => onRemoveDirectory(path)}>Remove</a></div>;
|
||||||
|
})}
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
<Button small={true} onClick={() => setIsDisplayingDialog(true)}>Add Directory</Button>
|
<Button small={true} onClick={() => setIsDisplayingDialog(true)}>Add Directory</Button>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user