mirror of https://github.com/stashapp/stash.git
Support Javascript injection (#3132)
This commit is contained in:
parent
3a63f1f9b7
commit
ca9c8e0a34
|
@ -63,6 +63,8 @@ fragment ConfigInterfaceData on ConfigInterfaceResult {
|
||||||
showStudioAsText
|
showStudioAsText
|
||||||
css
|
css
|
||||||
cssEnabled
|
cssEnabled
|
||||||
|
javascript
|
||||||
|
javascriptEnabled
|
||||||
customLocales
|
customLocales
|
||||||
customLocalesEnabled
|
customLocalesEnabled
|
||||||
language
|
language
|
||||||
|
|
|
@ -264,6 +264,10 @@ input ConfigInterfaceInput {
|
||||||
css: String
|
css: String
|
||||||
cssEnabled: Boolean
|
cssEnabled: Boolean
|
||||||
|
|
||||||
|
"""Custom Javascript"""
|
||||||
|
javascript: String
|
||||||
|
javascriptEnabled: Boolean
|
||||||
|
|
||||||
"""Custom Locales"""
|
"""Custom Locales"""
|
||||||
customLocales: String
|
customLocales: String
|
||||||
customLocalesEnabled: Boolean
|
customLocalesEnabled: Boolean
|
||||||
|
@ -330,6 +334,10 @@ type ConfigInterfaceResult {
|
||||||
css: String
|
css: String
|
||||||
cssEnabled: Boolean
|
cssEnabled: Boolean
|
||||||
|
|
||||||
|
"""Custom Javascript"""
|
||||||
|
javascript: String
|
||||||
|
javascriptEnabled: Boolean
|
||||||
|
|
||||||
"""Custom Locales"""
|
"""Custom Locales"""
|
||||||
customLocales: String
|
customLocales: String
|
||||||
customLocalesEnabled: Boolean
|
customLocalesEnabled: Boolean
|
||||||
|
|
|
@ -365,6 +365,12 @@ func (r *mutationResolver) ConfigureInterface(ctx context.Context, input ConfigI
|
||||||
|
|
||||||
setBool(config.CSSEnabled, input.CSSEnabled)
|
setBool(config.CSSEnabled, input.CSSEnabled)
|
||||||
|
|
||||||
|
if input.Javascript != nil {
|
||||||
|
c.SetJavascript(*input.Javascript)
|
||||||
|
}
|
||||||
|
|
||||||
|
setBool(config.JavascriptEnabled, input.JavascriptEnabled)
|
||||||
|
|
||||||
if input.CustomLocales != nil {
|
if input.CustomLocales != nil {
|
||||||
c.SetCustomLocales(*input.CustomLocales)
|
c.SetCustomLocales(*input.CustomLocales)
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,6 +142,8 @@ func makeConfigInterfaceResult() *ConfigInterfaceResult {
|
||||||
showStudioAsText := config.GetShowStudioAsText()
|
showStudioAsText := config.GetShowStudioAsText()
|
||||||
css := config.GetCSS()
|
css := config.GetCSS()
|
||||||
cssEnabled := config.GetCSSEnabled()
|
cssEnabled := config.GetCSSEnabled()
|
||||||
|
javascript := config.GetJavascript()
|
||||||
|
javascriptEnabled := config.GetJavascriptEnabled()
|
||||||
customLocales := config.GetCustomLocales()
|
customLocales := config.GetCustomLocales()
|
||||||
customLocalesEnabled := config.GetCustomLocalesEnabled()
|
customLocalesEnabled := config.GetCustomLocalesEnabled()
|
||||||
language := config.GetLanguage()
|
language := config.GetLanguage()
|
||||||
|
@ -166,6 +168,8 @@ func makeConfigInterfaceResult() *ConfigInterfaceResult {
|
||||||
ContinuePlaylistDefault: &continuePlaylistDefault,
|
ContinuePlaylistDefault: &continuePlaylistDefault,
|
||||||
CSS: &css,
|
CSS: &css,
|
||||||
CSSEnabled: &cssEnabled,
|
CSSEnabled: &cssEnabled,
|
||||||
|
Javascript: &javascript,
|
||||||
|
JavascriptEnabled: &javascriptEnabled,
|
||||||
CustomLocales: &customLocales,
|
CustomLocales: &customLocales,
|
||||||
CustomLocalesEnabled: &customLocalesEnabled,
|
CustomLocalesEnabled: &customLocalesEnabled,
|
||||||
Language: &language,
|
Language: &language,
|
||||||
|
|
|
@ -181,6 +181,21 @@ func Start() error {
|
||||||
|
|
||||||
http.ServeFile(w, r, fn)
|
http.ServeFile(w, r, fn)
|
||||||
})
|
})
|
||||||
|
r.HandleFunc("/javascript", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "text/javascript")
|
||||||
|
if !c.GetJavascriptEnabled() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// search for custom.js in current directory, then $HOME/.stash
|
||||||
|
fn := c.GetJavascriptPath()
|
||||||
|
exists, _ := fsutil.FileExists(fn)
|
||||||
|
if !exists {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
http.ServeFile(w, r, fn)
|
||||||
|
})
|
||||||
r.HandleFunc("/customlocales", func(w http.ResponseWriter, r *http.Request) {
|
r.HandleFunc("/customlocales", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
if c.GetCustomLocalesEnabled() {
|
if c.GetCustomLocalesEnabled() {
|
||||||
|
|
|
@ -139,6 +139,7 @@ const (
|
||||||
ContinuePlaylistDefault = "continue_playlist_default"
|
ContinuePlaylistDefault = "continue_playlist_default"
|
||||||
ShowStudioAsText = "show_studio_as_text"
|
ShowStudioAsText = "show_studio_as_text"
|
||||||
CSSEnabled = "cssEnabled"
|
CSSEnabled = "cssEnabled"
|
||||||
|
JavascriptEnabled = "javascriptEnabled"
|
||||||
CustomLocalesEnabled = "customLocalesEnabled"
|
CustomLocalesEnabled = "customLocalesEnabled"
|
||||||
|
|
||||||
ShowScrubber = "show_scrubber"
|
ShowScrubber = "show_scrubber"
|
||||||
|
@ -1077,6 +1078,49 @@ func (i *Instance) GetCSSEnabled() bool {
|
||||||
return i.getBool(CSSEnabled)
|
return i.getBool(CSSEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Instance) GetJavascriptPath() string {
|
||||||
|
// use custom.js in the same directory as the config file
|
||||||
|
configFileUsed := i.GetConfigFile()
|
||||||
|
configDir := filepath.Dir(configFileUsed)
|
||||||
|
|
||||||
|
fn := filepath.Join(configDir, "custom.js")
|
||||||
|
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Instance) GetJavascript() string {
|
||||||
|
fn := i.GetJavascriptPath()
|
||||||
|
|
||||||
|
exists, _ := fsutil.FileExists(fn)
|
||||||
|
if !exists {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := os.ReadFile(fn)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Instance) SetJavascript(javascript string) {
|
||||||
|
fn := i.GetJavascriptPath()
|
||||||
|
i.Lock()
|
||||||
|
defer i.Unlock()
|
||||||
|
|
||||||
|
buf := []byte(javascript)
|
||||||
|
|
||||||
|
if err := os.WriteFile(fn, buf, 0777); err != nil {
|
||||||
|
logger.Warnf("error while writing %v bytes to %v: %v", len(buf), fn, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Instance) GetJavascriptEnabled() bool {
|
||||||
|
return i.getBool(JavascriptEnabled)
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Instance) GetCustomLocalesPath() string {
|
func (i *Instance) GetCustomLocalesPath() string {
|
||||||
// use custom-locales.json in the same directory as the config file
|
// use custom-locales.json in the same directory as the config file
|
||||||
configFileUsed := i.GetConfigFile()
|
configFileUsed := i.GetConfigFile()
|
||||||
|
|
|
@ -86,6 +86,8 @@ func TestConcurrentConfigAccess(t *testing.T) {
|
||||||
i.Set(ImageLightboxSlideshowDelay, *i.GetImageLightboxOptions().SlideshowDelay)
|
i.Set(ImageLightboxSlideshowDelay, *i.GetImageLightboxOptions().SlideshowDelay)
|
||||||
i.GetCSSPath()
|
i.GetCSSPath()
|
||||||
i.GetCSS()
|
i.GetCSS()
|
||||||
|
i.GetJavascriptPath()
|
||||||
|
i.GetJavascript()
|
||||||
i.GetCustomLocalesPath()
|
i.GetCustomLocalesPath()
|
||||||
i.GetCustomLocales()
|
i.GetCustomLocales()
|
||||||
i.Set(CSSEnabled, i.GetCSSEnabled())
|
i.Set(CSSEnabled, i.GetCSSEnabled())
|
||||||
|
|
|
@ -510,6 +510,36 @@ export const SettingsInterfacePanel: React.FC = () => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</SettingSection>
|
</SettingSection>
|
||||||
|
<SettingSection headingID="config.ui.custom_javascript.heading">
|
||||||
|
<BooleanSetting
|
||||||
|
id="custom-javascript-enabled"
|
||||||
|
headingID="config.ui.custom_javascript.option_label"
|
||||||
|
checked={iface.javascriptEnabled ?? undefined}
|
||||||
|
onChange={(v) => saveInterface({ javascriptEnabled: v })}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ModalSetting<string>
|
||||||
|
id="custom-javascript"
|
||||||
|
headingID="config.ui.custom_javascript.heading"
|
||||||
|
subHeadingID="config.ui.custom_javascript.description"
|
||||||
|
value={iface.javascript ?? undefined}
|
||||||
|
onChange={(v) => saveInterface({ javascript: v })}
|
||||||
|
renderField={(value, setValue) => (
|
||||||
|
<Form.Control
|
||||||
|
as="textarea"
|
||||||
|
value={value}
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) =>
|
||||||
|
setValue(e.currentTarget.value)
|
||||||
|
}
|
||||||
|
rows={16}
|
||||||
|
className="text-input code"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
renderValue={() => {
|
||||||
|
return <></>;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SettingSection>
|
||||||
<SettingSection headingID="config.ui.custom_locales.heading">
|
<SettingSection headingID="config.ui.custom_locales.heading">
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
id="custom-locales-enabled"
|
id="custom-locales-enabled"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
### ✨ New Features
|
### ✨ New Features
|
||||||
|
* Added custom javascript option. ([#3132](https://github.com/stashapp/stash/pull/3132))
|
||||||
* Added ability to select rating system in the Interface settings, allowing 5 stars with full-, half- or quarter-stars, or numeric score out of 10 with one decimal point. ([#2830](https://github.com/stashapp/stash/pull/2830))
|
* Added ability to select rating system in the Interface settings, allowing 5 stars with full-, half- or quarter-stars, or numeric score out of 10 with one decimal point. ([#2830](https://github.com/stashapp/stash/pull/2830))
|
||||||
* Added filter criteria for Birthdate, Death Date, Date, Created At and Updated At fields. ([#2834](https://github.com/stashapp/stash/pull/2834))
|
* Added filter criteria for Birthdate, Death Date, Date, Created At and Updated At fields. ([#2834](https://github.com/stashapp/stash/pull/2834))
|
||||||
* Support creation of scenes without files. ([#3006](https://github.com/stashapp/stash/pull/3006))
|
* Support creation of scenes without files. ([#3006](https://github.com/stashapp/stash/pull/3006))
|
||||||
|
|
|
@ -26,6 +26,10 @@ The stash UI can be customised using custom CSS. See [here](https://github.com/s
|
||||||
|
|
||||||
[Stash Plex Theme](https://github.com/stashapp/stash/wiki/Theme-Plex) is a community created theme inspired by the popular Plex interface.
|
[Stash Plex Theme](https://github.com/stashapp/stash/wiki/Theme-Plex) is a community created theme inspired by the popular Plex interface.
|
||||||
|
|
||||||
|
## Custom Javascript
|
||||||
|
|
||||||
|
Stash supports the injection of custom javascript to assist with theming or adding additional functionality. Be aware that bad Javascript could break the UI or worse.
|
||||||
|
|
||||||
## Custom Locales
|
## Custom Locales
|
||||||
|
|
||||||
The localisation strings can be customised. The master list of default (en-GB) locale strings can be found [here](https://github.com/stashapp/stash/blob/develop/ui/v2.5/src/locales/en-GB.json). The custom locale format is the same as this json file.
|
The localisation strings can be customised. The master list of default (en-GB) locale strings can be found [here](https://github.com/stashapp/stash/blob/develop/ui/v2.5/src/locales/en-GB.json). The custom locale format is the same as this json file.
|
||||||
|
|
|
@ -20,6 +20,10 @@ ReactDOM.render(
|
||||||
document.getElementById("root")
|
document.getElementById("root")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const script = document.createElement("script");
|
||||||
|
script.src = `${getPlatformURL()}javascript`;
|
||||||
|
document.body.appendChild(script);
|
||||||
|
|
||||||
// If you want your app to work offline and load faster, you can change
|
// If you want your app to work offline and load faster, you can change
|
||||||
// unregister() to register() below. Note this comes with some pitfalls.
|
// unregister() to register() below. Note this comes with some pitfalls.
|
||||||
// Learn more about service workers: http://bit.ly/CRA-PWA
|
// Learn more about service workers: http://bit.ly/CRA-PWA
|
||||||
|
|
|
@ -435,6 +435,11 @@
|
||||||
"heading": "Custom CSS",
|
"heading": "Custom CSS",
|
||||||
"option_label": "Custom CSS enabled"
|
"option_label": "Custom CSS enabled"
|
||||||
},
|
},
|
||||||
|
"custom_javascript": {
|
||||||
|
"description": "Page must be reloaded for changes to take effect.",
|
||||||
|
"heading": "Custom Javascript",
|
||||||
|
"option_label": "Custom Javascript enabled"
|
||||||
|
},
|
||||||
"custom_locales": {
|
"custom_locales": {
|
||||||
"description": "Override individual locale strings. See https://github.com/stashapp/stash/blob/develop/ui/v2.5/src/locales/en-GB.json for the master list. Page must be reloaded for changes to take effect.",
|
"description": "Override individual locale strings. See https://github.com/stashapp/stash/blob/develop/ui/v2.5/src/locales/en-GB.json for the master list. Page must be reloaded for changes to take effect.",
|
||||||
"heading": "Custom localisation",
|
"heading": "Custom localisation",
|
||||||
|
|
Loading…
Reference in New Issue