Compare commits
	
		
			5 Commits
		
	
	
		
			v0.1.3
			...
			newPreview
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| f264faff31 | |||
| 4465840657 | |||
| 7aeb14c120 | |||
| d9875a11d5 | |||
| d9d6907745 | 
@@ -1,13 +1,14 @@
 | 
				
			|||||||
image: node:14
 | 
					image: node:14
 | 
				
			||||||
 | 
					
 | 
				
			||||||
stages:
 | 
					stages:
 | 
				
			||||||
  - build
 | 
					  - build_frontend
 | 
				
			||||||
 | 
					  - build_backend
 | 
				
			||||||
  - test
 | 
					  - test
 | 
				
			||||||
  - packaging
 | 
					  - packaging
 | 
				
			||||||
  - deploy
 | 
					  - deploy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Minimize_Frontend:
 | 
					Minimize_Frontend:
 | 
				
			||||||
  stage: build
 | 
					  stage: build_frontend
 | 
				
			||||||
  before_script:
 | 
					  before_script:
 | 
				
			||||||
    - yarn install --cache-folder .yarn
 | 
					    - yarn install --cache-folder .yarn
 | 
				
			||||||
  script:
 | 
					  script:
 | 
				
			||||||
@@ -25,11 +26,15 @@ Minimize_Frontend:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Build_Backend:
 | 
					Build_Backend:
 | 
				
			||||||
  image: golang:latest
 | 
					  image: golang:latest
 | 
				
			||||||
  stage: build
 | 
					  stage: build_backend
 | 
				
			||||||
  script:
 | 
					  script:
 | 
				
			||||||
    - cd apiGo
 | 
					    - cd apiGo
 | 
				
			||||||
    - go build -v -o openmediacenter
 | 
					    - go build -v -o openmediacenter
 | 
				
			||||||
    - env GOOS=windows GOARCH=amd64 go build -v -o openmediacenter.exe
 | 
					    - cp -r ../build/ ./static/
 | 
				
			||||||
 | 
					    - go build -v -tags static -o openmediacenter_full
 | 
				
			||||||
 | 
					    - env GOOS=windows GOARCH=amd64 go build -v -tags static -o openmediacenter.exe
 | 
				
			||||||
 | 
					  needs:
 | 
				
			||||||
 | 
					    - Minimize_Frontend
 | 
				
			||||||
  artifacts:
 | 
					  artifacts:
 | 
				
			||||||
    expire_in: 2 days
 | 
					    expire_in: 2 days
 | 
				
			||||||
    paths:
 | 
					    paths:
 | 
				
			||||||
@@ -41,6 +46,7 @@ Frontend_Tests:
 | 
				
			|||||||
    - yarn install --cache-folder .yarn
 | 
					    - yarn install --cache-folder .yarn
 | 
				
			||||||
  script:
 | 
					  script:
 | 
				
			||||||
    - yarn run test
 | 
					    - yarn run test
 | 
				
			||||||
 | 
					  needs: []
 | 
				
			||||||
  artifacts:
 | 
					  artifacts:
 | 
				
			||||||
    reports:
 | 
					    reports:
 | 
				
			||||||
      junit:
 | 
					      junit:
 | 
				
			||||||
@@ -58,6 +64,7 @@ Backend_Tests:
 | 
				
			|||||||
    - cd apiGo
 | 
					    - cd apiGo
 | 
				
			||||||
    - go get -u github.com/jstemmer/go-junit-report
 | 
					    - go get -u github.com/jstemmer/go-junit-report
 | 
				
			||||||
    - go test -v ./... 2>&1 | go-junit-report -set-exit-code > report.xml
 | 
					    - go test -v ./... 2>&1 | go-junit-report -set-exit-code > report.xml
 | 
				
			||||||
 | 
					  needs: []
 | 
				
			||||||
  artifacts:
 | 
					  artifacts:
 | 
				
			||||||
    when: always
 | 
					    when: always
 | 
				
			||||||
    reports:
 | 
					    reports:
 | 
				
			||||||
@@ -105,7 +112,7 @@ Debian_Server:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Test_Server:
 | 
					Test_Server:
 | 
				
			||||||
  stage: deploy
 | 
					  stage: deploy
 | 
				
			||||||
  image: luki42/alpineopenssh:latest
 | 
					  image: luki42/ssh:latest
 | 
				
			||||||
  needs:
 | 
					  needs:
 | 
				
			||||||
    - Frontend_Tests
 | 
					    - Frontend_Tests
 | 
				
			||||||
    - Backend_Tests
 | 
					    - Backend_Tests
 | 
				
			||||||
@@ -114,7 +121,7 @@ Test_Server:
 | 
				
			|||||||
    - master
 | 
					    - master
 | 
				
			||||||
  script:
 | 
					  script:
 | 
				
			||||||
    - eval $(ssh-agent -s)
 | 
					    - eval $(ssh-agent -s)
 | 
				
			||||||
    - ssh-add <(echo "$SSH_PRIVATE_KEY")
 | 
					    - echo "$SSH_PRIVATE_KEY" | ssh-add -
 | 
				
			||||||
    - mkdir -p ~/.ssh
 | 
					    - mkdir -p ~/.ssh
 | 
				
			||||||
    - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
 | 
					    - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
 | 
				
			||||||
    - scp deb/OpenMediaCenter-*.deb root@192.168.0.42:/tmp/
 | 
					    - scp deb/OpenMediaCenter-*.deb root@192.168.0.42:/tmp/
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,6 @@ import (
 | 
				
			|||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"log"
 | 
					 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"openmediacenter/apiGo/api/oauth"
 | 
						"openmediacenter/apiGo/api/oauth"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -37,7 +36,7 @@ func AddHandler(action string, apiNode int, n interface{}, h func() []byte) {
 | 
				
			|||||||
	handlers = append(handlers, Handler{action, h, n, apiNode})
 | 
						handlers = append(handlers, Handler{action, h, n, apiNode})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ServerInit(port uint16) {
 | 
					func ServerInit() {
 | 
				
			||||||
	http.Handle(APIPREFIX+"/video", oauth.ValidateToken(videoHandler))
 | 
						http.Handle(APIPREFIX+"/video", oauth.ValidateToken(videoHandler))
 | 
				
			||||||
	http.Handle(APIPREFIX+"/tags", oauth.ValidateToken(tagHandler))
 | 
						http.Handle(APIPREFIX+"/tags", oauth.ValidateToken(tagHandler))
 | 
				
			||||||
	http.Handle(APIPREFIX+"/settings", oauth.ValidateToken(settingsHandler))
 | 
						http.Handle(APIPREFIX+"/settings", oauth.ValidateToken(settingsHandler))
 | 
				
			||||||
@@ -48,9 +47,6 @@ func ServerInit(port uint16) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// initialize oauth service and add corresponding auth routes
 | 
						// initialize oauth service and add corresponding auth routes
 | 
				
			||||||
	oauth.InitOAuth()
 | 
						oauth.InitOAuth()
 | 
				
			||||||
 | 
					 | 
				
			||||||
	fmt.Printf("OpenMediacenter server up and running on port %d\n", port)
 | 
					 | 
				
			||||||
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func handleAPICall(action string, requestBody string, apiNode int) []byte {
 | 
					func handleAPICall(action string, requestBody string, apiNode int) []byte {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,8 @@ package api
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"openmediacenter/apiGo/database/settings"
 | 
						"openmediacenter/apiGo/database/settings"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func AddInitHandlers() {
 | 
					func AddInitHandlers() {
 | 
				
			||||||
@@ -20,11 +22,15 @@ func passwordNeeded() {
 | 
				
			|||||||
			VideoPath       string
 | 
								VideoPath       string
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							regexMatchUrl := regexp.MustCompile("^http(|s):\\/\\/([0-9]){1,3}\\.([0-9]){1,3}\\.([0-9]){1,3}\\.([0-9]){1,3}:[0-9]{1,5}")
 | 
				
			||||||
 | 
							videoUrl := regexMatchUrl.FindString(sett.VideoPath)
 | 
				
			||||||
 | 
							serverVideoPath := strings.TrimPrefix(sett.VideoPath, videoUrl)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		res := InitialDataTypeResponse{
 | 
							res := InitialDataTypeResponse{
 | 
				
			||||||
			DarkMode:        sett.DarkMode,
 | 
								DarkMode:        sett.DarkMode,
 | 
				
			||||||
			Pasword:         sett.Pasword != "-1",
 | 
								Pasword:         sett.Pasword != "-1",
 | 
				
			||||||
			MediacenterName: sett.Mediacenter_name,
 | 
								MediacenterName: sett.Mediacenter_name,
 | 
				
			||||||
			VideoPath:       sett.VideoPath,
 | 
								VideoPath:       serverVideoPath,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		str, _ := json.Marshal(res)
 | 
							str, _ := json.Marshal(res)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,12 +3,16 @@ package main
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"flag"
 | 
						"flag"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
	"openmediacenter/apiGo/api"
 | 
						"openmediacenter/apiGo/api"
 | 
				
			||||||
	"openmediacenter/apiGo/database"
 | 
						"openmediacenter/apiGo/database"
 | 
				
			||||||
 | 
						"openmediacenter/apiGo/static"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
	fmt.Println("init OpenMediaCenter server")
 | 
						fmt.Println("init OpenMediaCenter server")
 | 
				
			||||||
 | 
						port := 8081
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db, verbose, pathPrefix := handleCommandLineArguments()
 | 
						db, verbose, pathPrefix := handleCommandLineArguments()
 | 
				
			||||||
	// todo some verbosity logger or sth
 | 
						// todo some verbosity logger or sth
 | 
				
			||||||
@@ -28,7 +32,13 @@ func main() {
 | 
				
			|||||||
	api.AddActorsHandlers()
 | 
						api.AddActorsHandlers()
 | 
				
			||||||
	api.AddInitHandlers()
 | 
						api.AddInitHandlers()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	api.ServerInit(8081)
 | 
						// add the static files
 | 
				
			||||||
 | 
						static.ServeStaticFiles()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						api.ServerInit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Printf("OpenMediacenter server up and running on port %d\n", port)
 | 
				
			||||||
 | 
						log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func handleCommandLineArguments() (*database.DatabaseConfig, bool, *string) {
 | 
					func handleCommandLineArguments() (*database.DatabaseConfig, bool, *string) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										89
									
								
								apiGo/static/StaticServe.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								apiGo/static/StaticServe.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
				
			|||||||
 | 
					// +build static
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package static
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"embed"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/fs"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/http/httputil"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"openmediacenter/apiGo/database/settings"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:embed build
 | 
				
			||||||
 | 
					var staticFiles embed.FS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ServeStaticFiles() {
 | 
				
			||||||
 | 
						// http.FS can be used to create a http Filesystem
 | 
				
			||||||
 | 
						subfs, _ := fs.Sub(staticFiles, "build")
 | 
				
			||||||
 | 
						staticFS := http.FS(subfs)
 | 
				
			||||||
 | 
						fs := http.FileServer(staticFS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Serve static files
 | 
				
			||||||
 | 
						http.Handle("/", validatePrefix(fs))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// we need to proxy the videopath to somewhere in a standalone binary
 | 
				
			||||||
 | 
						proxyVideoURL()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type handler struct {
 | 
				
			||||||
 | 
						proxy *httputil.ReverseProxy
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
						h.proxy.ServeHTTP(w, r)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func proxyVideoURL() {
 | 
				
			||||||
 | 
						conf := settings.LoadSettings()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// match base url
 | 
				
			||||||
 | 
						regexMatchUrl := regexp.MustCompile("^http(|s):\\/\\/([0-9]){1,3}\\.([0-9]){1,3}\\.([0-9]){1,3}\\.([0-9]){1,3}:[0-9]{1,5}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var videoUrl *url.URL
 | 
				
			||||||
 | 
						if regexMatchUrl.MatchString(conf.VideoPath) {
 | 
				
			||||||
 | 
							fmt.Println("matches string...")
 | 
				
			||||||
 | 
							var err error
 | 
				
			||||||
 | 
							videoUrl, err = url.Parse(regexMatchUrl.FindString(conf.VideoPath))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							videoUrl, _ = url.Parse("http://127.0.0.1:8081")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						director := func(req *http.Request) {
 | 
				
			||||||
 | 
							req.URL.Scheme = videoUrl.Scheme
 | 
				
			||||||
 | 
							req.URL.Host = videoUrl.Host
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						serverVideoPath := strings.TrimPrefix(conf.VideoPath, regexMatchUrl.FindString(conf.VideoPath))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reverseProxy := &httputil.ReverseProxy{Director: director}
 | 
				
			||||||
 | 
						handler := handler{proxy: reverseProxy}
 | 
				
			||||||
 | 
						http.Handle(serverVideoPath, handler)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ValidatePrefix check if requested path is a file -- if not proceed with index.html
 | 
				
			||||||
 | 
					func validatePrefix(h http.Handler) http.Handler {
 | 
				
			||||||
 | 
						return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							regex := regexp.MustCompile("\\..*$")
 | 
				
			||||||
 | 
							matchFile := regex.MatchString(r.URL.Path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if matchFile {
 | 
				
			||||||
 | 
								h.ServeHTTP(w, r)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								r2 := new(http.Request)
 | 
				
			||||||
 | 
								*r2 = *r
 | 
				
			||||||
 | 
								r2.URL = new(url.URL)
 | 
				
			||||||
 | 
								*r2.URL = *r.URL
 | 
				
			||||||
 | 
								r2.URL.Path = "/"
 | 
				
			||||||
 | 
								r2.URL.RawPath = "/"
 | 
				
			||||||
 | 
								h.ServeHTTP(w, r2)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										6
									
								
								apiGo/static/StaticServe_apionly.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								apiGo/static/StaticServe_apionly.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					// +build !static
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package static
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// add nothing on no static build
 | 
				
			||||||
 | 
					func ServeStaticFiles() {}
 | 
				
			||||||
							
								
								
									
										23
									
								
								src/App.tsx
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								src/App.tsx
									
									
									
									
									
								
							@@ -10,7 +10,6 @@ import style from './App.module.css';
 | 
				
			|||||||
import SettingsPage from './pages/SettingsPage/SettingsPage';
 | 
					import SettingsPage from './pages/SettingsPage/SettingsPage';
 | 
				
			||||||
import CategoryPage from './pages/CategoryPage/CategoryPage';
 | 
					import CategoryPage from './pages/CategoryPage/CategoryPage';
 | 
				
			||||||
import {APINode, apiTokenValid, callApiUnsafe, refreshAPIToken} from './utils/Api';
 | 
					import {APINode, apiTokenValid, callApiUnsafe, refreshAPIToken} from './utils/Api';
 | 
				
			||||||
import {NoBackendConnectionPopup} from './elements/Popups/NoBackendConnectionPopup/NoBackendConnectionPopup';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {BrowserRouter as Router, NavLink, Route, Switch} from 'react-router-dom';
 | 
					import {BrowserRouter as Router, NavLink, Route, Switch} from 'react-router-dom';
 | 
				
			||||||
import Player from './pages/Player/Player';
 | 
					import Player from './pages/Player/Player';
 | 
				
			||||||
@@ -22,7 +21,6 @@ import AuthenticationPage from './pages/AuthenticationPage/AuthenticationPage';
 | 
				
			|||||||
interface state {
 | 
					interface state {
 | 
				
			||||||
    password: boolean | null; // null if uninitialized - true if pwd needed false if not needed
 | 
					    password: boolean | null; // null if uninitialized - true if pwd needed false if not needed
 | 
				
			||||||
    mediacentername: string;
 | 
					    mediacentername: string;
 | 
				
			||||||
    onapierror: boolean;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -50,7 +48,6 @@ class App extends React.Component<{}, state> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        this.state = {
 | 
					        this.state = {
 | 
				
			||||||
            mediacentername: 'OpenMediaCenter',
 | 
					            mediacentername: 'OpenMediaCenter',
 | 
				
			||||||
            onapierror: false,
 | 
					 | 
				
			||||||
            password: pwdneeded
 | 
					            password: pwdneeded
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -77,26 +74,18 @@ class App extends React.Component<{}, state> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    initialAPICall(): void {
 | 
					    initialAPICall(): void {
 | 
				
			||||||
        // this is the first api call so if it fails we know there is no connection to backend
 | 
					        // this is the first api call so if it fails we know there is no connection to backend
 | 
				
			||||||
        callApiUnsafe(
 | 
					        callApiUnsafe(APINode.Init, {action: 'loadInitialData'}, (result: SettingsTypes.initialApiCallData) => {
 | 
				
			||||||
            APINode.Init,
 | 
					 | 
				
			||||||
            {action: 'loadInitialData'},
 | 
					 | 
				
			||||||
            (result: SettingsTypes.initialApiCallData) => {
 | 
					 | 
				
			||||||
            // set theme
 | 
					            // set theme
 | 
				
			||||||
            GlobalInfos.enableDarkTheme(result.DarkMode);
 | 
					            GlobalInfos.enableDarkTheme(result.DarkMode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            GlobalInfos.setVideoPath(result.VideoPath);
 | 
					            GlobalInfos.setVideoPath(result.VideoPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.setState({
 | 
					            this.setState({
 | 
				
			||||||
                    mediacentername: result.MediacenterName,
 | 
					                mediacentername: result.MediacenterName
 | 
				
			||||||
                    onapierror: false
 | 
					 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            // set tab title to received mediacenter name
 | 
					            // set tab title to received mediacenter name
 | 
				
			||||||
            document.title = result.MediacenterName;
 | 
					            document.title = result.MediacenterName;
 | 
				
			||||||
            },
 | 
					        });
 | 
				
			||||||
            () => {
 | 
					 | 
				
			||||||
                this.setState({onapierror: true});
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    componentDidMount(): void {
 | 
					    componentDidMount(): void {
 | 
				
			||||||
@@ -148,7 +137,6 @@ class App extends React.Component<{}, state> {
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                        {this.routing()}
 | 
					                        {this.routing()}
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    {this.state.onapierror ? this.ApiError() : null}
 | 
					 | 
				
			||||||
                </Router>
 | 
					                </Router>
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@@ -183,11 +171,6 @@ class App extends React.Component<{}, state> {
 | 
				
			|||||||
            </Switch>
 | 
					            </Switch>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    ApiError(): JSX.Element {
 | 
					 | 
				
			||||||
        // on api error show popup and retry and show again if failing..
 | 
					 | 
				
			||||||
        return <NoBackendConnectionPopup onHide={(): void => this.initialAPICall()} />;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default App;
 | 
					export default App;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,29 +0,0 @@
 | 
				
			|||||||
import {shallow} from 'enzyme';
 | 
					 | 
				
			||||||
import React from 'react';
 | 
					 | 
				
			||||||
import {NoBackendConnectionPopup} from './NoBackendConnectionPopup';
 | 
					 | 
				
			||||||
import {getBackendDomain} from '../../../utils/Api';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('<NoBackendConnectionPopup/>', function () {
 | 
					 | 
				
			||||||
    it('renders without crashing ', function () {
 | 
					 | 
				
			||||||
        const wrapper = shallow(<NoBackendConnectionPopup onHide={() => {}}/>);
 | 
					 | 
				
			||||||
        wrapper.unmount();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('hides on refresh click', function () {
 | 
					 | 
				
			||||||
        const func = jest.fn();
 | 
					 | 
				
			||||||
        const wrapper = shallow(<NoBackendConnectionPopup onHide={func}/>);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        expect(func).toBeCalledTimes(0);
 | 
					 | 
				
			||||||
        wrapper.find('button').simulate('click');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        expect(func).toBeCalledTimes(1);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('simulate change of textfield', function () {
 | 
					 | 
				
			||||||
        const wrapper = shallow(<NoBackendConnectionPopup onHide={() => {}}/>);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        wrapper.find('input').simulate('change', {target: {value: 'testvalue'}});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        expect(getBackendDomain()).toBe('testvalue');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
@@ -1,27 +0,0 @@
 | 
				
			|||||||
import React from 'react';
 | 
					 | 
				
			||||||
import PopupBase from '../PopupBase';
 | 
					 | 
				
			||||||
import style from '../NewActorPopup/NewActorPopup.module.css';
 | 
					 | 
				
			||||||
import {setCustomBackendDomain} from '../../../utils/Api';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
interface NBCProps {
 | 
					 | 
				
			||||||
    onHide: (_: void) => void;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function NoBackendConnectionPopup(props: NBCProps): JSX.Element {
 | 
					 | 
				
			||||||
    return (
 | 
					 | 
				
			||||||
        <PopupBase title='No connection to backend API!' onHide={props.onHide} height='200px' width='600px'>
 | 
					 | 
				
			||||||
            <div>
 | 
					 | 
				
			||||||
                <input
 | 
					 | 
				
			||||||
                    type='text'
 | 
					 | 
				
			||||||
                    placeholder='http://192.168.0.2'
 | 
					 | 
				
			||||||
                    onChange={(v): void => {
 | 
					 | 
				
			||||||
                        setCustomBackendDomain(v.target.value);
 | 
					 | 
				
			||||||
                    }}
 | 
					 | 
				
			||||||
                />
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <button className={style.savebtn} onClick={(): void => props.onHide()}>
 | 
					 | 
				
			||||||
                Refresh
 | 
					 | 
				
			||||||
            </button>
 | 
					 | 
				
			||||||
        </PopupBase>
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,9 +1,19 @@
 | 
				
			|||||||
.previewtitle {
 | 
					.previewText {
 | 
				
			||||||
    font-size: smaller;
 | 
					    font-size: 15px;
 | 
				
			||||||
    font-weight: bold;
 | 
					    font-weight: bold;
 | 
				
			||||||
    height: 20px;
 | 
					    height: 42px;
 | 
				
			||||||
    max-width: 266px;
 | 
					    width: 100%;
 | 
				
			||||||
    text-align: center;
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    bottom: 0;
 | 
				
			||||||
 | 
					    background-color: rgba(60, 61, 72, 0.7);
 | 
				
			||||||
 | 
					    color: white;
 | 
				
			||||||
 | 
					    border-bottom-left-radius: 20px;
 | 
				
			||||||
 | 
					    border-bottom-right-radius: 20px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    border-top-width: 1px;
 | 
				
			||||||
 | 
					    border-top-color: #3c3d48;
 | 
				
			||||||
 | 
					    border-top-style: solid;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.previewpic {
 | 
					.previewpic {
 | 
				
			||||||
@@ -12,6 +22,8 @@
 | 
				
			|||||||
    min-width: 266px;
 | 
					    min-width: 266px;
 | 
				
			||||||
    overflow: hidden;
 | 
					    overflow: hidden;
 | 
				
			||||||
    text-align: center;
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    border-radius: 20px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.loadAnimation {
 | 
					.loadAnimation {
 | 
				
			||||||
@@ -38,6 +50,7 @@
 | 
				
			|||||||
    margin-left: 25px;
 | 
					    margin-left: 25px;
 | 
				
			||||||
    margin-top: 25px;
 | 
					    margin-top: 25px;
 | 
				
			||||||
    opacity: 0.85;
 | 
					    opacity: 0.85;
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.videopreview:hover {
 | 
					.videopreview:hover {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,9 +42,16 @@ class Preview extends React.Component<PreviewProps, PreviewState> {
 | 
				
			|||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <Link to={'/player/' + this.props.movieId}>
 | 
					            <Link to={'/player/' + this.props.movieId}>
 | 
				
			||||||
                <div className={style.videopreview + ' ' + themeStyle.secbackground + ' ' + themeStyle.preview}>
 | 
					                <div className={style.videopreview + ' ' + themeStyle.secbackground + ' ' + themeStyle.preview}>
 | 
				
			||||||
                    <div className={style.previewtitle + ' ' + themeStyle.lighttextcolor}>{this.props.name}</div>
 | 
					                    <div className={style.previewpic}>{this.renderPic()}</div>
 | 
				
			||||||
                    <div className={style.previewpic}>
 | 
					                    <div className={style.previewText + ' ' + themeStyle.lighttextcolor}>{this.props.name}</div>
 | 
				
			||||||
                        {this.state.previewpicture === '' ? (
 | 
					                </div>
 | 
				
			||||||
 | 
					            </Link>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private renderPic(): JSX.Element {
 | 
				
			||||||
 | 
					        if (this.state.previewpicture === '') {
 | 
				
			||||||
 | 
					            return (
 | 
				
			||||||
                <FontAwesomeIcon
 | 
					                <FontAwesomeIcon
 | 
				
			||||||
                    style={{
 | 
					                    style={{
 | 
				
			||||||
                        color: 'white',
 | 
					                        color: 'white',
 | 
				
			||||||
@@ -53,18 +60,16 @@ class Preview extends React.Component<PreviewProps, PreviewState> {
 | 
				
			|||||||
                    icon={faPhotoVideo}
 | 
					                    icon={faPhotoVideo}
 | 
				
			||||||
                    size='5x'
 | 
					                    size='5x'
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
                        ) : this.state.previewpicture === null ? (
 | 
					            );
 | 
				
			||||||
 | 
					        } else if (this.state.previewpicture === null) {
 | 
				
			||||||
 | 
					            return (
 | 
				
			||||||
                <span className={style.loadAnimation}>
 | 
					                <span className={style.loadAnimation}>
 | 
				
			||||||
                    <Spinner animation='border' />
 | 
					                    <Spinner animation='border' />
 | 
				
			||||||
                </span>
 | 
					                </span>
 | 
				
			||||||
                        ) : (
 | 
					 | 
				
			||||||
                            <img className={style.previewimage} src={this.state.previewpicture} alt='Pic loading.' />
 | 
					 | 
				
			||||||
                        )}
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                    <div className={style.previewbottom} />
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            </Link>
 | 
					 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return <img className={style.previewimage} src={this.state.previewpicture} alt='Pic loading.' />;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@ import {faPlusCircle} from '@fortawesome/free-solid-svg-icons';
 | 
				
			|||||||
import AddActorPopup from '../../elements/Popups/AddActorPopup/AddActorPopup';
 | 
					import AddActorPopup from '../../elements/Popups/AddActorPopup/AddActorPopup';
 | 
				
			||||||
import ActorTile from '../../elements/ActorTile/ActorTile';
 | 
					import ActorTile from '../../elements/ActorTile/ActorTile';
 | 
				
			||||||
import {withRouter} from 'react-router-dom';
 | 
					import {withRouter} from 'react-router-dom';
 | 
				
			||||||
import {APINode, callAPI, getBackendDomain} from '../../utils/Api';
 | 
					import {APINode, callAPI} from '../../utils/Api';
 | 
				
			||||||
import {RouteComponentProps} from 'react-router';
 | 
					import {RouteComponentProps} from 'react-router';
 | 
				
			||||||
import {GeneralSuccess} from '../../types/GeneralTypes';
 | 
					import {GeneralSuccess} from '../../types/GeneralTypes';
 | 
				
			||||||
import {ActorType, TagType} from '../../types/VideoTypes';
 | 
					import {ActorType, TagType} from '../../types/VideoTypes';
 | 
				
			||||||
@@ -289,9 +289,7 @@ export class Player extends React.Component<myprops, mystate> {
 | 
				
			|||||||
                                src:
 | 
					                                src:
 | 
				
			||||||
                                    (process.env.REACT_APP_CUST_BACK_DOMAIN
 | 
					                                    (process.env.REACT_APP_CUST_BACK_DOMAIN
 | 
				
			||||||
                                        ? process.env.REACT_APP_CUST_BACK_DOMAIN
 | 
					                                        ? process.env.REACT_APP_CUST_BACK_DOMAIN
 | 
				
			||||||
                                        : getBackendDomain()) +
 | 
					                                        : GlobalInfos.getVideoPath()) + result.MovieUrl,
 | 
				
			||||||
                                    GlobalInfos.getVideoPath() +
 | 
					 | 
				
			||||||
                                    result.MovieUrl,
 | 
					 | 
				
			||||||
                                type: 'video/mp4',
 | 
					                                type: 'video/mp4',
 | 
				
			||||||
                                size: 1080
 | 
					                                size: 1080
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,13 +6,11 @@ import InfoHeaderItem from '../../elements/InfoHeaderItem/InfoHeaderItem';
 | 
				
			|||||||
import {faArchive, faBalanceScaleLeft, faRulerVertical} from '@fortawesome/free-solid-svg-icons';
 | 
					import {faArchive, faBalanceScaleLeft, faRulerVertical} from '@fortawesome/free-solid-svg-icons';
 | 
				
			||||||
import {faAddressCard} from '@fortawesome/free-regular-svg-icons';
 | 
					import {faAddressCard} from '@fortawesome/free-regular-svg-icons';
 | 
				
			||||||
import {version} from '../../../package.json';
 | 
					import {version} from '../../../package.json';
 | 
				
			||||||
import {APINode, callAPI, setCustomBackendDomain} from '../../utils/Api';
 | 
					import {APINode, callAPI} from '../../utils/Api';
 | 
				
			||||||
import {SettingsTypes} from '../../types/ApiTypes';
 | 
					import {SettingsTypes} from '../../types/ApiTypes';
 | 
				
			||||||
import {GeneralSuccess} from '../../types/GeneralTypes';
 | 
					import {GeneralSuccess} from '../../types/GeneralTypes';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface state {
 | 
					interface state {
 | 
				
			||||||
    customapi: boolean;
 | 
					 | 
				
			||||||
    apipath: string;
 | 
					 | 
				
			||||||
    generalSettings: SettingsTypes.loadGeneralSettingsType;
 | 
					    generalSettings: SettingsTypes.loadGeneralSettingsType;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -27,8 +25,6 @@ class GeneralSettings extends React.Component<Props, state> {
 | 
				
			|||||||
        super(props);
 | 
					        super(props);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.state = {
 | 
					        this.state = {
 | 
				
			||||||
            customapi: false,
 | 
					 | 
				
			||||||
            apipath: '',
 | 
					 | 
				
			||||||
            generalSettings: {
 | 
					            generalSettings: {
 | 
				
			||||||
                DarkMode: true,
 | 
					                DarkMode: true,
 | 
				
			||||||
                DBSize: 0,
 | 
					                DBSize: 0,
 | 
				
			||||||
@@ -121,35 +117,6 @@ class GeneralSettings extends React.Component<Props, state> {
 | 
				
			|||||||
                                />
 | 
					                                />
 | 
				
			||||||
                            </Form.Group>
 | 
					                            </Form.Group>
 | 
				
			||||||
                        </Form.Row>
 | 
					                        </Form.Row>
 | 
				
			||||||
 | 
					 | 
				
			||||||
                        <Form.Check
 | 
					 | 
				
			||||||
                            type='switch'
 | 
					 | 
				
			||||||
                            id='custom-switch-api'
 | 
					 | 
				
			||||||
                            label='Use custom API url'
 | 
					 | 
				
			||||||
                            checked={this.state.customapi}
 | 
					 | 
				
			||||||
                            onChange={(): void => {
 | 
					 | 
				
			||||||
                                if (this.state.customapi) {
 | 
					 | 
				
			||||||
                                    setCustomBackendDomain('');
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                this.setState({customapi: !this.state.customapi});
 | 
					 | 
				
			||||||
                            }}
 | 
					 | 
				
			||||||
                        />
 | 
					 | 
				
			||||||
                        {this.state.customapi ? (
 | 
					 | 
				
			||||||
                            <Form.Group className={style.customapiform} data-testid='apipath'>
 | 
					 | 
				
			||||||
                                <Form.Label>API Backend url</Form.Label>
 | 
					 | 
				
			||||||
                                <Form.Control
 | 
					 | 
				
			||||||
                                    type='text'
 | 
					 | 
				
			||||||
                                    placeholder='https://127.0.0.1'
 | 
					 | 
				
			||||||
                                    value={this.state.apipath}
 | 
					 | 
				
			||||||
                                    onChange={(e): void => {
 | 
					 | 
				
			||||||
                                        this.setState({apipath: e.target.value});
 | 
					 | 
				
			||||||
                                        setCustomBackendDomain(e.target.value);
 | 
					 | 
				
			||||||
                                    }}
 | 
					 | 
				
			||||||
                                />
 | 
					 | 
				
			||||||
                            </Form.Group>
 | 
					 | 
				
			||||||
                        ) : null}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        <Form.Check
 | 
					                        <Form.Check
 | 
				
			||||||
                            type='switch'
 | 
					                            type='switch'
 | 
				
			||||||
                            id='custom-switch'
 | 
					                            id='custom-switch'
 | 
				
			||||||
@@ -209,8 +176,6 @@ class GeneralSettings extends React.Component<Props, state> {
 | 
				
			|||||||
                            checked={GlobalInfos.isDarkTheme()}
 | 
					                            checked={GlobalInfos.isDarkTheme()}
 | 
				
			||||||
                            onChange={(): void => {
 | 
					                            onChange={(): void => {
 | 
				
			||||||
                                GlobalInfos.enableDarkTheme(!GlobalInfos.isDarkTheme());
 | 
					                                GlobalInfos.enableDarkTheme(!GlobalInfos.isDarkTheme());
 | 
				
			||||||
                                this.forceUpdate();
 | 
					 | 
				
			||||||
                                // todo initiate rerender
 | 
					 | 
				
			||||||
                            }}
 | 
					                            }}
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,40 +1,6 @@
 | 
				
			|||||||
import GlobalInfos from './GlobalInfos';
 | 
					import GlobalInfos from './GlobalInfos';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let customBackendURL: string;
 | 
					const APIPREFIX: string = '/api/';
 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * get the domain of the api backend
 | 
					 | 
				
			||||||
 * @return string domain of backend http://x.x.x.x/bla
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
export function getBackendDomain(): string {
 | 
					 | 
				
			||||||
    let userAgent = navigator.userAgent.toLowerCase();
 | 
					 | 
				
			||||||
    if (userAgent.indexOf(' electron/') > -1) {
 | 
					 | 
				
			||||||
        // Electron-specific code - force a custom backendurl
 | 
					 | 
				
			||||||
        return customBackendURL;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        // use custom only if defined
 | 
					 | 
				
			||||||
        if (customBackendURL) {
 | 
					 | 
				
			||||||
            return customBackendURL;
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            return window.location.origin;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * set a custom backend domain
 | 
					 | 
				
			||||||
 * @param domain a url in format [http://x.x.x.x/somanode]
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
export function setCustomBackendDomain(domain: string): void {
 | 
					 | 
				
			||||||
    customBackendURL = domain;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * a helper function to get the api path
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
function getAPIDomain(): string {
 | 
					 | 
				
			||||||
    return getBackendDomain() + '/api/';
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * interface how an api request should look like
 | 
					 * interface how an api request should look like
 | 
				
			||||||
@@ -96,7 +62,7 @@ export function refreshAPIToken(callback: (error: string) => void, force?: boole
 | 
				
			|||||||
        token_type: string; // no camel case allowed because of backendlib
 | 
					        token_type: string; // no camel case allowed because of backendlib
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fetch(getBackendDomain() + '/token', {method: 'POST', body: formData}).then((response) =>
 | 
					    fetch('/token', {method: 'POST', body: formData}).then((response) =>
 | 
				
			||||||
        response.json().then((result: APIToken) => {
 | 
					        response.json().then((result: APIToken) => {
 | 
				
			||||||
            if (result.error) {
 | 
					            if (result.error) {
 | 
				
			||||||
                callFuncQue(result.error);
 | 
					                callFuncQue(result.error);
 | 
				
			||||||
@@ -223,7 +189,7 @@ export function callAPI<T>(
 | 
				
			|||||||
): void {
 | 
					): void {
 | 
				
			||||||
    checkAPITokenValid(() => {
 | 
					    checkAPITokenValid(() => {
 | 
				
			||||||
        console.log(apiToken);
 | 
					        console.log(apiToken);
 | 
				
			||||||
        fetch(getAPIDomain() + apinode, {
 | 
					        fetch(APIPREFIX + apinode, {
 | 
				
			||||||
            method: 'POST',
 | 
					            method: 'POST',
 | 
				
			||||||
            body: JSON.stringify(fd),
 | 
					            body: JSON.stringify(fd),
 | 
				
			||||||
            headers: new Headers({
 | 
					            headers: new Headers({
 | 
				
			||||||
@@ -267,7 +233,7 @@ export function callApiUnsafe<T>(
 | 
				
			|||||||
    callback: (_: T) => void,
 | 
					    callback: (_: T) => void,
 | 
				
			||||||
    errorcallback?: (_: string) => void
 | 
					    errorcallback?: (_: string) => void
 | 
				
			||||||
): void {
 | 
					): void {
 | 
				
			||||||
    fetch(getAPIDomain() + apinode, {method: 'POST', body: JSON.stringify(fd)})
 | 
					    fetch(APIPREFIX + apinode, {method: 'POST', body: JSON.stringify(fd)})
 | 
				
			||||||
        .then((response) => {
 | 
					        .then((response) => {
 | 
				
			||||||
            if (response.status !== 200) {
 | 
					            if (response.status !== 200) {
 | 
				
			||||||
                console.log('Error: ' + response.statusText);
 | 
					                console.log('Error: ' + response.statusText);
 | 
				
			||||||
@@ -289,7 +255,7 @@ export function callApiUnsafe<T>(
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
export function callAPIPlain(apinode: APINode, fd: ApiBaseRequest, callback: (_: string) => void): void {
 | 
					export function callAPIPlain(apinode: APINode, fd: ApiBaseRequest, callback: (_: string) => void): void {
 | 
				
			||||||
    checkAPITokenValid(() => {
 | 
					    checkAPITokenValid(() => {
 | 
				
			||||||
        fetch(getAPIDomain() + apinode, {
 | 
					        fetch(APIPREFIX + apinode, {
 | 
				
			||||||
            method: 'POST',
 | 
					            method: 'POST',
 | 
				
			||||||
            body: JSON.stringify(fd),
 | 
					            body: JSON.stringify(fd),
 | 
				
			||||||
            headers: new Headers({
 | 
					            headers: new Headers({
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,8 @@ class StaticInfos {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    enableDarkTheme(enable = true): void {
 | 
					    enableDarkTheme(enable = true): void {
 | 
				
			||||||
        this.darktheme = enable;
 | 
					        this.darktheme = enable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // trigger onThemeChange handlers
 | 
				
			||||||
        this.handlers.map((func) => {
 | 
					        this.handlers.map((func) => {
 | 
				
			||||||
            return func();
 | 
					            return func();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user