Compare commits
	
		
			11 Commits
		
	
	
		
			rememberLo
			...
			dependency
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6e0928a97a | |||
| 05ea72a8ca | |||
| 7476e2397d | |||
| 929e0c337d | |||
| f7a0d8fa07 | |||
| 9faf457c89 | |||
| 11c1e25de5 | |||
| 3d845aaf04 | |||
| 7f98528fbe | |||
| 191fa5386d | |||
| 9715012685 | 
@@ -108,18 +108,13 @@ Debian_Server:
 | 
				
			|||||||
      - Minimize_Frontend
 | 
					      - Minimize_Frontend
 | 
				
			||||||
      - Build_Backend
 | 
					      - Build_Backend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Test_Server:
 | 
					.Test_Server_Common:
 | 
				
			||||||
  stage: deploy
 | 
					  stage: deploy
 | 
				
			||||||
  image: luki42/ssh:latest
 | 
					  image: luki42/ssh:latest
 | 
				
			||||||
  needs:
 | 
					  needs:
 | 
				
			||||||
    - Frontend_Tests
 | 
					    - Frontend_Tests
 | 
				
			||||||
    - Backend_Tests
 | 
					    - Backend_Tests
 | 
				
			||||||
    - Debian_Server
 | 
					    - Debian_Server
 | 
				
			||||||
  rules:
 | 
					 | 
				
			||||||
    - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH # run this always on default branch
 | 
					 | 
				
			||||||
    - if: $CI_COMMIT_REF_NAME != $CI_DEFAULT_BRANCH # allow triggering this manually
 | 
					 | 
				
			||||||
      when: manual
 | 
					 | 
				
			||||||
      allow_failure: true
 | 
					 | 
				
			||||||
  script:
 | 
					  script:
 | 
				
			||||||
    - eval $(ssh-agent -s)
 | 
					    - eval $(ssh-agent -s)
 | 
				
			||||||
    - echo "$SSH_PRIVATE_KEY" | ssh-add -
 | 
					    - echo "$SSH_PRIVATE_KEY" | ssh-add -
 | 
				
			||||||
@@ -129,24 +124,38 @@ Test_Server:
 | 
				
			|||||||
    - ssh root@192.168.0.42 "DEBIAN_FRONTEND=noninteractive apt-get --reinstall  -y -qq install /tmp/OpenMediaCenter-*.deb && rm /tmp/OpenMediaCenter-*.deb"
 | 
					    - ssh root@192.168.0.42 "DEBIAN_FRONTEND=noninteractive apt-get --reinstall  -y -qq install /tmp/OpenMediaCenter-*.deb && rm /tmp/OpenMediaCenter-*.deb"
 | 
				
			||||||
  allow_failure: true
 | 
					  allow_failure: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Test_Server_2:
 | 
					Test_Server_CD:
 | 
				
			||||||
 | 
					  extends: .Test_Server_Common
 | 
				
			||||||
 | 
					  only:
 | 
				
			||||||
 | 
					    refs:
 | 
				
			||||||
 | 
					      - master
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Test_Server_MANUAL:
 | 
				
			||||||
 | 
					  extends: .Test_Server_Common
 | 
				
			||||||
 | 
					  when: manual
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.Test_Server_2_Common:
 | 
				
			||||||
  stage: deploy
 | 
					  stage: deploy
 | 
				
			||||||
  image: luki42/ssh:latest
 | 
					  image: luki42/ssh:latest
 | 
				
			||||||
  needs:
 | 
					  needs:
 | 
				
			||||||
    - Frontend_Tests
 | 
					    - Frontend_Tests
 | 
				
			||||||
    - Backend_Tests
 | 
					    - Backend_Tests
 | 
				
			||||||
    - Debian_Server
 | 
					    - Debian_Server
 | 
				
			||||||
  rules:
 | 
					 | 
				
			||||||
    - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH # run this always on default branch
 | 
					 | 
				
			||||||
    - if: $CI_COMMIT_REF_NAME != $CI_DEFAULT_BRANCH # allow triggering this manually
 | 
					 | 
				
			||||||
      when: manual
 | 
					 | 
				
			||||||
      allow_failure: true
 | 
					 | 
				
			||||||
  script:
 | 
					  script:
 | 
				
			||||||
    - eval $(ssh-agent -s)
 | 
					    - eval $(ssh-agent -s)
 | 
				
			||||||
    - echo "$SSH_PRIVATE_KEY_2" | ssh-add -
 | 
					    - echo "$SSH_PRIVATE_KEY_2" | 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.209:/tmp/
 | 
					    - scp deb/OpenMediaCenter-*.deb root@192.168.0.44:/tmp/
 | 
				
			||||||
    - ssh root@192.168.0.209 "DEBIAN_FRONTEND=noninteractive apt-get --reinstall  -y -qq install /tmp/OpenMediaCenter-*.deb && rm /tmp/OpenMediaCenter-*.deb"
 | 
					    - ssh root@192.168.0.44 "DEBIAN_FRONTEND=noninteractive apt-get --reinstall  -y -qq install /tmp/OpenMediaCenter-*.deb && rm /tmp/OpenMediaCenter-*.deb"
 | 
				
			||||||
  allow_failure: true
 | 
					  allow_failure: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Test_Server_2_CD:
 | 
				
			||||||
 | 
					  extends: .Test_Server_2_Common
 | 
				
			||||||
 | 
					  only:
 | 
				
			||||||
 | 
					    refs:
 | 
				
			||||||
 | 
					      - master
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Test_Server_2_MANUAL:
 | 
				
			||||||
 | 
					  extends: .Test_Server_2_Common
 | 
				
			||||||
 | 
					  when: manual
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -78,7 +78,7 @@ func getFromDB() {
 | 
				
			|||||||
	 * @apiSuccess {string} TagName name of the Tag
 | 
						 * @apiSuccess {string} TagName name of the Tag
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	api.AddHandler("getAllTags", api.TagNode, api.PermUser, func(context api.Context) {
 | 
						api.AddHandler("getAllTags", api.TagNode, api.PermUser, func(context api.Context) {
 | 
				
			||||||
		query := "SELECT tag_id,tag_name from tags"
 | 
							query := "SELECT tag_id,tag_name from tags ORDER BY tag_name"
 | 
				
			||||||
		context.Json(readTagsFromResultset(database.Query(query)))
 | 
							context.Json(readTagsFromResultset(database.Query(query)))
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
package api
 | 
					package api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"openmediacenter/apiGo/api/api"
 | 
						"openmediacenter/apiGo/api/api"
 | 
				
			||||||
@@ -9,6 +10,7 @@ import (
 | 
				
			|||||||
	"openmediacenter/apiGo/database"
 | 
						"openmediacenter/apiGo/database"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func addVideoHandlers() {
 | 
					func addVideoHandlers() {
 | 
				
			||||||
@@ -163,6 +165,7 @@ func getVideoHandlers() {
 | 
				
			|||||||
	api.AddHandler("getRandomMovies", api.VideoNode, api.PermUser, func(context api.Context) {
 | 
						api.AddHandler("getRandomMovies", api.VideoNode, api.PermUser, func(context api.Context) {
 | 
				
			||||||
		var args struct {
 | 
							var args struct {
 | 
				
			||||||
			Number    int
 | 
								Number    int
 | 
				
			||||||
 | 
								TagFilter []uint32
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if api.DecodeRequest(context.GetRequest(), &args) != nil {
 | 
							if api.DecodeRequest(context.GetRequest(), &args) != nil {
 | 
				
			||||||
			context.Text("unable to decode request")
 | 
								context.Text("unable to decode request")
 | 
				
			||||||
@@ -174,9 +177,23 @@ func getVideoHandlers() {
 | 
				
			|||||||
			Videos []types.VideoUnloadedType
 | 
								Videos []types.VideoUnloadedType
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		query := fmt.Sprintf("SELECT movie_id,movie_name FROM videos ORDER BY RAND() LIMIT %d", args.Number)
 | 
							whereclause := "WHERE 1"
 | 
				
			||||||
 | 
							if len(args.TagFilter) > 0 {
 | 
				
			||||||
 | 
								d, _ := json.Marshal(args.TagFilter)
 | 
				
			||||||
 | 
								vals := strings.Trim(string(d), "[]")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								whereclause = fmt.Sprintf("WHERE tag_id IN (%s)", vals)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							query := fmt.Sprintf(`
 | 
				
			||||||
 | 
					SELECT video_tags.video_id,v.movie_name FROM video_tags join videos v on v.movie_id = video_tags.video_id
 | 
				
			||||||
 | 
					                                        %s
 | 
				
			||||||
 | 
					                                        group by video_id
 | 
				
			||||||
 | 
					                                        ORDER BY RAND()
 | 
				
			||||||
 | 
					                                        LIMIT %d`, whereclause, args.Number)
 | 
				
			||||||
		result.Videos = readVideosFromResultset(database.Query(query))
 | 
							result.Videos = readVideosFromResultset(database.Query(query))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if len(result.Videos) > 0 {
 | 
				
			||||||
			var ids string
 | 
								var ids string
 | 
				
			||||||
			for i := range result.Videos {
 | 
								for i := range result.Videos {
 | 
				
			||||||
				ids += "video_tags.video_id=" + strconv.Itoa(result.Videos[i].MovieId)
 | 
									ids += "video_tags.video_id=" + strconv.Itoa(result.Videos[i].MovieId)
 | 
				
			||||||
@@ -193,7 +210,7 @@ func getVideoHandlers() {
 | 
				
			|||||||
									GROUP BY t.tag_id`, ids)
 | 
														GROUP BY t.tag_id`, ids)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			rows := database.Query(query)
 | 
								rows := database.Query(query)
 | 
				
			||||||
 | 
								if rows != nil {
 | 
				
			||||||
				for rows.Next() {
 | 
									for rows.Next() {
 | 
				
			||||||
					var tag types.Tag
 | 
										var tag types.Tag
 | 
				
			||||||
					err := rows.Scan(&tag.TagName, &tag.TagId)
 | 
										err := rows.Scan(&tag.TagName, &tag.TagId)
 | 
				
			||||||
@@ -203,6 +220,10 @@ func getVideoHandlers() {
 | 
				
			|||||||
					// append to final array
 | 
										// append to final array
 | 
				
			||||||
					result.Tags = append(result.Tags, tag)
 | 
										result.Tags = append(result.Tags, tag)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								result.Tags = []types.Tag{}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		context.Json(result)
 | 
							context.Json(result)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ go 1.16
 | 
				
			|||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/3d0c/gmf v0.0.0-20210925211039-e278e6e53b16
 | 
						github.com/3d0c/gmf v0.0.0-20210925211039-e278e6e53b16
 | 
				
			||||||
	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 | 
						github.com/dgrijalva/jwt-go v3.2.0+incompatible
 | 
				
			||||||
 | 
						github.com/fsnotify/fsnotify v1.5.4
 | 
				
			||||||
	github.com/go-sql-driver/mysql v1.6.0
 | 
						github.com/go-sql-driver/mysql v1.6.0
 | 
				
			||||||
	github.com/pelletier/go-toml/v2 v2.0.0-beta.3
 | 
						github.com/pelletier/go-toml/v2 v2.0.0-beta.3
 | 
				
			||||||
	github.com/pressly/goose/v3 v3.1.0
 | 
						github.com/pressly/goose/v3 v3.1.0
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 | 
				
			|||||||
github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
 | 
					github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
 | 
				
			||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
 | 
					github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
 | 
				
			||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 | 
					github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 | 
				
			||||||
 | 
					github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
 | 
				
			||||||
 | 
					github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
 | 
				
			||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
 | 
					github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
 | 
				
			||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
 | 
					github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
 | 
				
			||||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
 | 
					github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
 | 
				
			||||||
@@ -82,8 +84,9 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
 | 
				
			|||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
					golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
				
			||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
					golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
				
			||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
					golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										18
									
								
								apiGo/housekeeping/HouseKeeping.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								apiGo/housekeeping/HouseKeeping.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					package housekeeping
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func RunHouseKeepingTasks() {
 | 
				
			||||||
 | 
						fmt.Println("Runnint houskeeping tasks!")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println("Deduplicating Tags")
 | 
				
			||||||
 | 
						deduplicateTags()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println("Deduplicating Tags assigned to videos")
 | 
				
			||||||
 | 
						deduplicateVideoTags()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println("Fix missing video metadata like ratio")
 | 
				
			||||||
 | 
						fixMissingMetadata()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println("Finished housekeeping")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										5
									
								
								apiGo/housekeeping/MissingVideoMetaData.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								apiGo/housekeeping/MissingVideoMetaData.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					package housekeeping
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func fixMissingMetadata() {
 | 
				
			||||||
 | 
						// todo
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										86
									
								
								apiGo/housekeeping/TagDeduplication.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								apiGo/housekeeping/TagDeduplication.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					package housekeeping
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"openmediacenter/apiGo/database"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func deduplicateTags() {
 | 
				
			||||||
 | 
						// find all duplicate tags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// gives first occurence of duplicate
 | 
				
			||||||
 | 
						query := `
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					    tag_name
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					    tags
 | 
				
			||||||
 | 
					GROUP BY tag_name
 | 
				
			||||||
 | 
					HAVING COUNT(tag_name) > 1`
 | 
				
			||||||
 | 
						rows := database.Query(query)
 | 
				
			||||||
 | 
						duplicates := []string{}
 | 
				
			||||||
 | 
						if rows != nil {
 | 
				
			||||||
 | 
							for rows.Next() {
 | 
				
			||||||
 | 
								var id string
 | 
				
			||||||
 | 
								err := rows.Scan(&id)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									panic(err.Error()) // proper Error handling instead of panic in your app
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								duplicates = append(duplicates, id)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// nothing to do
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Print("deleting duplicate tag ids: ")
 | 
				
			||||||
 | 
						fmt.Println(duplicates)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, el := range duplicates {
 | 
				
			||||||
 | 
							query := fmt.Sprintf("SELECT tag_id FROM tags WHERE tag_name='%s'", el)
 | 
				
			||||||
 | 
							rows := database.Query(query)
 | 
				
			||||||
 | 
							ids := []uint32{}
 | 
				
			||||||
 | 
							for rows.Next() {
 | 
				
			||||||
 | 
								var id uint32
 | 
				
			||||||
 | 
								err := rows.Scan(&id)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									panic(err.Error()) // proper Error handling instead of panic in your app
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								ids = append(ids, id)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// id to copy other data to
 | 
				
			||||||
 | 
							mainid := ids[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// ids to copy from
 | 
				
			||||||
 | 
							copyids := ids[1:]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							fmt.Printf("Migrating %s\n", el)
 | 
				
			||||||
 | 
							migrateTags(mainid, copyids)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func migrateTags(destid uint32, sourcids []uint32) {
 | 
				
			||||||
 | 
						querytempl := `
 | 
				
			||||||
 | 
					UPDATE video_tags 
 | 
				
			||||||
 | 
					SET 
 | 
				
			||||||
 | 
					    tag_id = %d
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					    tag_id = %d`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, id := range sourcids {
 | 
				
			||||||
 | 
							err := database.Edit(fmt.Sprintf(querytempl, destid, id))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								fmt.Printf("failed to set id from %d to %d\n", id, destid)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							fmt.Printf("Merged %d into %d\n", id, destid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// now lets delete this tag
 | 
				
			||||||
 | 
							query := fmt.Sprintf(`DELETE FROM tags WHERE tag_id=%d`, id)
 | 
				
			||||||
 | 
							err = database.Edit(query)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								fmt.Printf("failed to delete Tag %d", id)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										37
									
								
								apiGo/housekeeping/VideoTagDeduplication.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								apiGo/housekeeping/VideoTagDeduplication.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					package housekeeping
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"openmediacenter/apiGo/database"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func deduplicateVideoTags() {
 | 
				
			||||||
 | 
						// gives first occurence of duplicate
 | 
				
			||||||
 | 
						query := `
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					    tag_id, video_id, count(tag_id)
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					    video_tags
 | 
				
			||||||
 | 
					GROUP BY tag_id, video_id
 | 
				
			||||||
 | 
					HAVING COUNT(tag_id) > 1`
 | 
				
			||||||
 | 
						rows := database.Query(query)
 | 
				
			||||||
 | 
						if rows != nil {
 | 
				
			||||||
 | 
							for rows.Next() {
 | 
				
			||||||
 | 
								var tagid uint32
 | 
				
			||||||
 | 
								var vidid uint32
 | 
				
			||||||
 | 
								var nr uint32
 | 
				
			||||||
 | 
								err := rows.Scan(&tagid, &vidid, &nr)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									panic(err.Error()) // proper Error handling instead of panic in your app
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// now lets delete this tag
 | 
				
			||||||
 | 
								query := fmt.Sprintf(`DELETE FROM video_tags WHERE tag_id=%d AND video_id=%d LIMIT %d`, tagid, vidid, nr-1)
 | 
				
			||||||
 | 
								err = database.Edit(query)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									fmt.Printf("failed to delete Tag %d + vid %d", tagid, vidid)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,11 +1,13 @@
 | 
				
			|||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"flag"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"openmediacenter/apiGo/api"
 | 
						"openmediacenter/apiGo/api"
 | 
				
			||||||
	api2 "openmediacenter/apiGo/api/api"
 | 
						api2 "openmediacenter/apiGo/api/api"
 | 
				
			||||||
	"openmediacenter/apiGo/config"
 | 
						"openmediacenter/apiGo/config"
 | 
				
			||||||
	"openmediacenter/apiGo/database"
 | 
						"openmediacenter/apiGo/database"
 | 
				
			||||||
 | 
						"openmediacenter/apiGo/housekeeping"
 | 
				
			||||||
	"openmediacenter/apiGo/static"
 | 
						"openmediacenter/apiGo/static"
 | 
				
			||||||
	"openmediacenter/apiGo/videoparser"
 | 
						"openmediacenter/apiGo/videoparser"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
@@ -17,6 +19,8 @@ func main() {
 | 
				
			|||||||
	const port uint16 = 8081
 | 
						const port uint16 = 8081
 | 
				
			||||||
	errc := make(chan error, 1)
 | 
						errc := make(chan error, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						housekPTr := flag.Bool("HouseKeeping", false, "Run housekeeping tasks")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	config.Init()
 | 
						config.Init()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// todo some verbosity logger or sth
 | 
						// todo some verbosity logger or sth
 | 
				
			||||||
@@ -29,9 +33,16 @@ func main() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	defer database.Close()
 | 
						defer database.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check if we should run the housekeeping tasks
 | 
				
			||||||
 | 
						if *housekPTr {
 | 
				
			||||||
 | 
							housekeeping.RunHouseKeepingTasks()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	api.AddHandlers()
 | 
						api.AddHandlers()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	videoparser.SetupSettingsWebsocket()
 | 
						videoparser.SetupSettingsWebsocket()
 | 
				
			||||||
 | 
						videoparser.InitFileWatcher()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// add the static files
 | 
						// add the static files
 | 
				
			||||||
	static.ServeStaticFiles()
 | 
						static.ServeStaticFiles()
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										58
									
								
								apiGo/videoparser/FileWatcher.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								apiGo/videoparser/FileWatcher.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					package videoparser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/fsnotify/fsnotify"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"openmediacenter/apiGo/config"
 | 
				
			||||||
 | 
						"openmediacenter/apiGo/database"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func InitFileWatcher() {
 | 
				
			||||||
 | 
						watcher, err := fsnotify.NewWatcher()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal("NewWatcher failed: ", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mSettings, _, _ := database.GetSettings()
 | 
				
			||||||
 | 
						vidFolder := config.GetConfig().General.ReindexPrefix + mSettings.VideoPath
 | 
				
			||||||
 | 
						epsfolder := config.GetConfig().General.ReindexPrefix + mSettings.EpisodePath
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defer watcher.Close()
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							for {
 | 
				
			||||||
 | 
								select {
 | 
				
			||||||
 | 
								case event, ok := <-watcher.Events:
 | 
				
			||||||
 | 
									if !ok {
 | 
				
			||||||
 | 
										return
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									// start new reindex
 | 
				
			||||||
 | 
									// (may be optimized by checking here if added file is video
 | 
				
			||||||
 | 
									// and start reindex just for one file)
 | 
				
			||||||
 | 
									if strings.Contains(event.Name, vidFolder) {
 | 
				
			||||||
 | 
										StartReindex()
 | 
				
			||||||
 | 
									} else if strings.Contains(event.Name, epsfolder) {
 | 
				
			||||||
 | 
										StartTVShowReindex()
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										log.Printf("Event in wrong folder: %s %s\n", event.Name, event.Op)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								case err, ok := <-watcher.Errors:
 | 
				
			||||||
 | 
									if !ok {
 | 
				
			||||||
 | 
										return
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									log.Println("error:", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = watcher.Add(vidFolder)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Println("Adding of file watcher failed: ", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = watcher.Add(epsfolder)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Println("Adding of file watcher failed: ", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -6,7 +6,6 @@ import (
 | 
				
			|||||||
	"openmediacenter/apiGo/config"
 | 
						"openmediacenter/apiGo/config"
 | 
				
			||||||
	"openmediacenter/apiGo/database"
 | 
						"openmediacenter/apiGo/database"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -33,22 +32,19 @@ func StartReindex() bool {
 | 
				
			|||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						filelist, err := ioutil.ReadDir(vidFolder)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Println(err.Error())
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var files []string
 | 
						var files []string
 | 
				
			||||||
	err := filepath.Walk(vidFolder, func(path string, info os.FileInfo, err error) error {
 | 
						for _, file := range filelist {
 | 
				
			||||||
		if err != nil {
 | 
							if !file.IsDir() && strings.HasSuffix(file.Name(), ".mp4") {
 | 
				
			||||||
			fmt.Println(err.Error())
 | 
								files = append(files, file.Name())
 | 
				
			||||||
			return err
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if !info.IsDir() && strings.HasSuffix(info.Name(), ".mp4") {
 | 
					 | 
				
			||||||
			files = append(files, info.Name())
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		fmt.Println(err.Error())
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// start reindex process
 | 
						// start reindex process
 | 
				
			||||||
	AppendMessage("Starting Reindexing!")
 | 
						AppendMessage("Starting Reindexing!")
 | 
				
			||||||
	InitDeps(&mSettings)
 | 
						InitDeps(&mSettings)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										52
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								package.json
									
									
									
									
									
								
							@@ -8,17 +8,17 @@
 | 
				
			|||||||
    "url": "https://heili.eu"
 | 
					    "url": "https://heili.eu"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@fortawesome/fontawesome-svg-core": "^1.2.32",
 | 
					    "@fortawesome/fontawesome-svg-core": "^6.2.0",
 | 
				
			||||||
    "@fortawesome/free-regular-svg-icons": "^5.15.1",
 | 
					    "@fortawesome/free-regular-svg-icons": "^6.2.0",
 | 
				
			||||||
    "@fortawesome/free-solid-svg-icons": "^5.15.1",
 | 
					    "@fortawesome/free-solid-svg-icons": "^6.2.0",
 | 
				
			||||||
    "@fortawesome/react-fontawesome": "^0.1.13",
 | 
					    "@fortawesome/react-fontawesome": "^0.2.0",
 | 
				
			||||||
    "bootstrap": "^5.0.2",
 | 
					    "bootstrap": "^5.0.2",
 | 
				
			||||||
    "plyr-react": "^3.0.7",
 | 
					    "plyr-react": "^5.1.0",
 | 
				
			||||||
    "react": "^17.0.1",
 | 
					    "react": "^18.2.0",
 | 
				
			||||||
    "react-bootstrap": "^1.4.0",
 | 
					    "react-bootstrap": "^2.5.0",
 | 
				
			||||||
    "react-dom": "^17.0.1",
 | 
					    "react-dom": "^18.2.0",
 | 
				
			||||||
    "react-router": "^5.2.0",
 | 
					    "react-router": "^6.4.0",
 | 
				
			||||||
    "react-router-dom": "^5.2.0",
 | 
					    "react-router-dom": "^6.4.0",
 | 
				
			||||||
    "typescript": "^4.3.5"
 | 
					    "typescript": "^4.3.5"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
@@ -54,31 +54,31 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@testing-library/jest-dom": "^5.14.1",
 | 
					    "@testing-library/jest-dom": "^5.14.1",
 | 
				
			||||||
    "@testing-library/react": "^12.0.0",
 | 
					    "@testing-library/react": "^13.4.0",
 | 
				
			||||||
    "@testing-library/user-event": "^13.2.1",
 | 
					    "@testing-library/user-event": "^14.4.3",
 | 
				
			||||||
    "@types/jest": "^26.0.24",
 | 
					    "@types/jest": "^29.0.3",
 | 
				
			||||||
    "@types/node": "^16.4.7",
 | 
					    "@types/node": "^18.7.18",
 | 
				
			||||||
    "@types/react": "^17.0.15",
 | 
					    "@types/react": "^18.0.20",
 | 
				
			||||||
    "@types/react-dom": "^17.0.9",
 | 
					    "@types/react-dom": "^18.0.6",
 | 
				
			||||||
    "@types/react-router": "5.1.16",
 | 
					    "@types/react-router": "5.1.19",
 | 
				
			||||||
    "@types/react-router-dom": "^5.1.8",
 | 
					    "@types/react-router-dom": "^5.1.8",
 | 
				
			||||||
    "@typescript-eslint/eslint-plugin": "^4.28.5",
 | 
					    "@typescript-eslint/eslint-plugin": "^5.38.0",
 | 
				
			||||||
    "@typescript-eslint/parser": "^4.28.5",
 | 
					    "@typescript-eslint/parser": "^5.38.0",
 | 
				
			||||||
    "apidoc": "^0.28.1",
 | 
					    "apidoc": "^0.53.0",
 | 
				
			||||||
    "enzyme": "^3.11.0",
 | 
					    "enzyme": "^3.11.0",
 | 
				
			||||||
    "enzyme-adapter-react-16": "^1.15.5",
 | 
					    "enzyme-adapter-react-16": "^1.15.5",
 | 
				
			||||||
    "eslint": "^7.31.0",
 | 
					    "eslint": "^8.23.1",
 | 
				
			||||||
    "eslint-config-prettier": "^8.1.0",
 | 
					    "eslint-config-prettier": "^8.1.0",
 | 
				
			||||||
    "eslint-formatter-gitlab": "^2.2.0",
 | 
					    "eslint-formatter-gitlab": "^3.0.0",
 | 
				
			||||||
    "eslint-plugin-eslint-comments": "^3.2.0",
 | 
					    "eslint-plugin-eslint-comments": "^3.2.0",
 | 
				
			||||||
    "eslint-plugin-jest": "^24.4.0",
 | 
					    "eslint-plugin-jest": "^27.0.4",
 | 
				
			||||||
    "eslint-plugin-prettier": "^3.3.1",
 | 
					    "eslint-plugin-prettier": "^4.2.1",
 | 
				
			||||||
    "eslint-plugin-react": "^7.22.0",
 | 
					    "eslint-plugin-react": "^7.22.0",
 | 
				
			||||||
    "eslint-plugin-react-hooks": "^4.2.0",
 | 
					    "eslint-plugin-react-hooks": "^4.2.0",
 | 
				
			||||||
    "jest-junit": "^12.0.0",
 | 
					    "jest-junit": "^14.0.1",
 | 
				
			||||||
    "prettier": "^2.3.2",
 | 
					    "prettier": "^2.3.2",
 | 
				
			||||||
    "prettier-config": "^1.0.0",
 | 
					    "prettier-config": "^1.0.0",
 | 
				
			||||||
    "react-scripts": "4.0.3"
 | 
					    "react-scripts": "5.0.1"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "apidoc": {
 | 
					  "apidoc": {
 | 
				
			||||||
    "name": "OpenMediaCenter",
 | 
					    "name": "OpenMediaCenter",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,10 @@
 | 
				
			|||||||
    text-decoration: none;
 | 
					    text-decoration: none;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.navitem-active {
 | 
				
			||||||
 | 
					    opacity: 0.85;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.navitem:hover {
 | 
					.navitem:hover {
 | 
				
			||||||
    opacity: 1;
 | 
					    opacity: 1;
 | 
				
			||||||
    text-decoration: none;
 | 
					    text-decoration: none;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										35
									
								
								src/App.tsx
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								src/App.tsx
									
									
									
									
									
								
							@@ -10,7 +10,7 @@ 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 {NavLink, Route, Switch, useRouteMatch} from 'react-router-dom';
 | 
					import {NavLink, Route, Routes} from 'react-router-dom';
 | 
				
			||||||
import Player from './pages/Player/Player';
 | 
					import Player from './pages/Player/Player';
 | 
				
			||||||
import ActorOverviewPage from './pages/ActorOverviewPage/ActorOverviewPage';
 | 
					import ActorOverviewPage from './pages/ActorOverviewPage/ActorOverviewPage';
 | 
				
			||||||
import ActorPage from './pages/ActorPage/ActorPage';
 | 
					import ActorPage from './pages/ActorPage/ActorPage';
 | 
				
			||||||
@@ -47,7 +47,7 @@ class App extends React.Component<{}, state> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <LoginContextProvider>
 | 
					            <LoginContextProvider>
 | 
				
			||||||
                <Switch>
 | 
					                <Routes>
 | 
				
			||||||
                    <Route path='/login'>
 | 
					                    <Route path='/login'>
 | 
				
			||||||
                        <AuthenticationPage />
 | 
					                        <AuthenticationPage />
 | 
				
			||||||
                    </Route>
 | 
					                    </Route>
 | 
				
			||||||
@@ -55,13 +55,19 @@ class App extends React.Component<{}, state> {
 | 
				
			|||||||
                        {this.navBar()}
 | 
					                        {this.navBar()}
 | 
				
			||||||
                        <MyRouter />
 | 
					                        <MyRouter />
 | 
				
			||||||
                    </Route>
 | 
					                    </Route>
 | 
				
			||||||
                </Switch>
 | 
					                </Routes>
 | 
				
			||||||
            </LoginContextProvider>
 | 
					            </LoginContextProvider>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static contextType = FeatureContext;
 | 
					    static contextType = FeatureContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    activeTab(history, path) {
 | 
				
			||||||
 | 
					        if (history.location.pathname === path) {
 | 
				
			||||||
 | 
					            return { color: "red" };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * render the top navigation bar
 | 
					     * render the top navigation bar
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@@ -75,39 +81,34 @@ class App extends React.Component<{}, state> {
 | 
				
			|||||||
                    Home
 | 
					                    Home
 | 
				
			||||||
                </NavLink>
 | 
					                </NavLink>
 | 
				
			||||||
                <NavLink
 | 
					                <NavLink
 | 
				
			||||||
                    className={[style.navitem, themeStyle.navitem].join(' ')}
 | 
					 | 
				
			||||||
                    to={'/media/random'}
 | 
					                    to={'/media/random'}
 | 
				
			||||||
                    activeStyle={{opacity: '0.85'}}>
 | 
					                    className={({ isActive }) => [style.navitem, themeStyle.navitem, (isActive ? 'navitem-active' : '')].join(' ')}>
 | 
				
			||||||
                    Random Video
 | 
					                    Random Video
 | 
				
			||||||
                </NavLink>
 | 
					                </NavLink>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <NavLink
 | 
					                <NavLink
 | 
				
			||||||
                    className={[style.navitem, themeStyle.navitem].join(' ')}
 | 
					                    className={({ isActive }) => [style.navitem, themeStyle.navitem, (isActive ? 'navitem-active' : '')].join(' ')}
 | 
				
			||||||
                    to={'/media/categories'}
 | 
					                    to={'/media/categories'}>
 | 
				
			||||||
                    activeStyle={{opacity: '0.85'}}>
 | 
					 | 
				
			||||||
                    Categories
 | 
					                    Categories
 | 
				
			||||||
                </NavLink>
 | 
					                </NavLink>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <NavLink
 | 
					                <NavLink
 | 
				
			||||||
                    className={[style.navitem, themeStyle.navitem].join(' ')}
 | 
					                    className={({ isActive }) => [style.navitem, themeStyle.navitem, (isActive ? 'navitem-active' : '')].join(' ')}
 | 
				
			||||||
                    to={'/media/actors'}
 | 
					                    to={'/media/actors'}>
 | 
				
			||||||
                    activeStyle={{opacity: '0.85'}}>
 | 
					 | 
				
			||||||
                    Actors
 | 
					                    Actors
 | 
				
			||||||
                </NavLink>
 | 
					                </NavLink>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                {this.context.TVShowEnabled ? (
 | 
					                {this.context.TVShowEnabled ? (
 | 
				
			||||||
                    <NavLink
 | 
					                    <NavLink
 | 
				
			||||||
                        className={[style.navitem, themeStyle.navitem].join(' ')}
 | 
					                        className={({ isActive }) => [style.navitem, themeStyle.navitem, (isActive ? 'navitem-active' : '')].join(' ')}
 | 
				
			||||||
                        to={'/media/tvshows'}
 | 
					                        to={'/media/tvshows'}>
 | 
				
			||||||
                        activeStyle={{opacity: '0.85'}}>
 | 
					 | 
				
			||||||
                        TV Shows
 | 
					                        TV Shows
 | 
				
			||||||
                    </NavLink>
 | 
					                    </NavLink>
 | 
				
			||||||
                ) : null}
 | 
					                ) : null}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <NavLink
 | 
					                <NavLink
 | 
				
			||||||
                    className={[style.navitem, themeStyle.navitem].join(' ')}
 | 
					                    className={({ isActive }) => [style.navitem, themeStyle.navitem, (isActive ? 'navitem-active' : '')].join(' ')}
 | 
				
			||||||
                    to={'/media/settings'}
 | 
					                    to={'/media/settings'}>
 | 
				
			||||||
                    activeStyle={{opacity: '0.85'}}>
 | 
					 | 
				
			||||||
                    Settings
 | 
					                    Settings
 | 
				
			||||||
                </NavLink>
 | 
					                </NavLink>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
import React from 'react';
 | 
					import React, {PropsWithChildren} from 'react';
 | 
				
			||||||
import style from './SideBar.module.css';
 | 
					import style from './SideBar.module.css';
 | 
				
			||||||
import GlobalInfos from '../../utils/GlobalInfos';
 | 
					import GlobalInfos from '../../utils/GlobalInfos';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface SideBarProps {
 | 
					interface SideBarProps extends  PropsWithChildren{
 | 
				
			||||||
    hiddenFrame?: boolean;
 | 
					    hiddenFrame?: boolean;
 | 
				
			||||||
    width?: string;
 | 
					    width?: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,16 +2,21 @@ import React from 'react';
 | 
				
			|||||||
import style from './RandomPage.module.css';
 | 
					import style from './RandomPage.module.css';
 | 
				
			||||||
import SideBar, {SideBarTitle} from '../../elements/SideBar/SideBar';
 | 
					import SideBar, {SideBarTitle} from '../../elements/SideBar/SideBar';
 | 
				
			||||||
import Tag from '../../elements/Tag/Tag';
 | 
					import Tag from '../../elements/Tag/Tag';
 | 
				
			||||||
import PageTitle from '../../elements/PageTitle/PageTitle';
 | 
					import PageTitle, {Line} from '../../elements/PageTitle/PageTitle';
 | 
				
			||||||
import VideoContainer from '../../elements/VideoContainer/VideoContainer';
 | 
					import VideoContainer from '../../elements/VideoContainer/VideoContainer';
 | 
				
			||||||
import {APINode, callAPI} from '../../utils/Api';
 | 
					import {APINode, callAPI} from '../../utils/Api';
 | 
				
			||||||
import {TagType} from '../../types/VideoTypes';
 | 
					import {TagType} from '../../types/VideoTypes';
 | 
				
			||||||
import {VideoTypes} from '../../types/ApiTypes';
 | 
					import {VideoTypes} from '../../types/ApiTypes';
 | 
				
			||||||
import {addKeyHandler, removeKeyHandler} from '../../utils/ShortkeyHandler';
 | 
					import {addKeyHandler, removeKeyHandler} from '../../utils/ShortkeyHandler';
 | 
				
			||||||
 | 
					import {IconButton} from '../../elements/GPElements/Button';
 | 
				
			||||||
 | 
					import {faMinusCircle, faPlusCircle} from '@fortawesome/free-solid-svg-icons';
 | 
				
			||||||
 | 
					import AddTagPopup from '../../elements/Popups/AddTagPopup/AddTagPopup';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface state {
 | 
					interface state {
 | 
				
			||||||
    videos: VideoTypes.VideoUnloadedType[];
 | 
					    videos: VideoTypes.VideoUnloadedType[];
 | 
				
			||||||
    tags: TagType[];
 | 
					    tags: TagType[];
 | 
				
			||||||
 | 
					    filterTags: TagType[];
 | 
				
			||||||
 | 
					    addTagPopup: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface GetRandomMoviesType {
 | 
					interface GetRandomMoviesType {
 | 
				
			||||||
@@ -29,8 +34,10 @@ class RandomPage extends React.Component<{}, state> {
 | 
				
			|||||||
        super(props);
 | 
					        super(props);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.state = {
 | 
					        this.state = {
 | 
				
			||||||
 | 
					            addTagPopup: false,
 | 
				
			||||||
            videos: [],
 | 
					            videos: [],
 | 
				
			||||||
            tags: []
 | 
					            tags: [],
 | 
				
			||||||
 | 
					            filterTags: []
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.keypress = this.keypress.bind(this);
 | 
					        this.keypress = this.keypress.bind(this);
 | 
				
			||||||
@@ -49,49 +56,76 @@ class RandomPage extends React.Component<{}, state> {
 | 
				
			|||||||
    render(): JSX.Element {
 | 
					    render(): JSX.Element {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <div>
 | 
					            <div>
 | 
				
			||||||
                <PageTitle title='Random Videos' subtitle='4pc' />
 | 
					                <PageTitle title='Random Videos' subtitle='3pc' />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <SideBar>
 | 
					                <SideBar>
 | 
				
			||||||
                    <SideBarTitle>Visible Tags:</SideBarTitle>
 | 
					                    <SideBarTitle>Visible Tags:</SideBarTitle>
 | 
				
			||||||
                    {this.state.tags.map((m) => (
 | 
					                    {this.state.tags.map((m) => (
 | 
				
			||||||
                        <Tag key={m.TagId} tagInfo={m} />
 | 
					                        <Tag key={m.TagId} tagInfo={m} />
 | 
				
			||||||
                    ))}
 | 
					                    ))}
 | 
				
			||||||
 | 
					                    <Line />
 | 
				
			||||||
 | 
					                    <SideBarTitle>Filter Tags:</SideBarTitle>
 | 
				
			||||||
 | 
					                    {this.state.filterTags.map((m) => (
 | 
				
			||||||
 | 
					                        <Tag key={m.TagId} tagInfo={m} />
 | 
				
			||||||
 | 
					                    ))}
 | 
				
			||||||
 | 
					                    <IconButton
 | 
				
			||||||
 | 
					                        title='Add'
 | 
				
			||||||
 | 
					                        icon={faPlusCircle}
 | 
				
			||||||
 | 
					                        onClick={(): void => {
 | 
				
			||||||
 | 
					                            this.setState({addTagPopup: true});
 | 
				
			||||||
 | 
					                        }}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                    {this.state.filterTags.length > 0 ? (
 | 
				
			||||||
 | 
					                        <IconButton
 | 
				
			||||||
 | 
					                            title='Remove All'
 | 
				
			||||||
 | 
					                            icon={faMinusCircle}
 | 
				
			||||||
 | 
					                            onClick={(): void => {
 | 
				
			||||||
 | 
					                                this.setState({filterTags: []}, (): void => {
 | 
				
			||||||
 | 
					                                    this.loadShuffledvideos(this.LoadNR);
 | 
				
			||||||
 | 
					                                });
 | 
				
			||||||
 | 
					                            }}
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
 | 
					                    ) : (
 | 
				
			||||||
 | 
					                        <></>
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
                </SideBar>
 | 
					                </SideBar>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                {this.state.videos.length !== 0 ? (
 | 
					                {this.state.videos.length !== 0 ? <VideoContainer data={this.state.videos} /> : <div>No Data found!</div>}
 | 
				
			||||||
                    <VideoContainer data={this.state.videos}>
 | 
					 | 
				
			||||||
                <div className={style.Shufflebutton}>
 | 
					                <div className={style.Shufflebutton}>
 | 
				
			||||||
                            <button onClick={(): void => this.shuffleclick()} className={style.btnshuffle}>
 | 
					                    <button onClick={(): void => this.loadShuffledvideos(this.LoadNR)} className={style.btnshuffle}>
 | 
				
			||||||
                        Shuffle
 | 
					                        Shuffle
 | 
				
			||||||
                    </button>
 | 
					                    </button>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                    </VideoContainer>
 | 
					                {this.state.addTagPopup ? (
 | 
				
			||||||
                ) : (
 | 
					                    <AddTagPopup
 | 
				
			||||||
                    <div>No Data found!</div>
 | 
					                        onHide={(): void => this.setState({addTagPopup: false})}
 | 
				
			||||||
                )}
 | 
					                        submit={(tagId: number, tagName: string): void => {
 | 
				
			||||||
 | 
					                            this.setState({filterTags: [...this.state.filterTags, {TagId: tagId, TagName: tagName}]}, (): void => {
 | 
				
			||||||
 | 
					                                this.loadShuffledvideos(this.LoadNR);
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                        }}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                ) : null}
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * click handler for shuffle btn
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    shuffleclick(): void {
 | 
					 | 
				
			||||||
        this.loadShuffledvideos(this.LoadNR);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * load random videos from backend
 | 
					     * load random videos from backend
 | 
				
			||||||
     * @param nr number of videos to load
 | 
					     * @param nr number of videos to load
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    loadShuffledvideos(nr: number): void {
 | 
					    loadShuffledvideos(nr: number): void {
 | 
				
			||||||
        callAPI<GetRandomMoviesType>(APINode.Video, {action: 'getRandomMovies', Number: nr}, (result) => {
 | 
					        callAPI<GetRandomMoviesType>(
 | 
				
			||||||
 | 
					            APINode.Video,
 | 
				
			||||||
 | 
					            {action: 'getRandomMovies', Number: nr, TagFilter: this.state.filterTags.map((t) => t.TagId)},
 | 
				
			||||||
 | 
					            (result) => {
 | 
				
			||||||
                this.setState({videos: []}); // needed to trigger rerender of main videoview
 | 
					                this.setState({videos: []}); // needed to trigger rerender of main videoview
 | 
				
			||||||
                this.setState({
 | 
					                this.setState({
 | 
				
			||||||
                    videos: result.Videos,
 | 
					                    videos: result.Videos,
 | 
				
			||||||
                    tags: result.Tags
 | 
					                    tags: result.Tags
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
        });
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -101,7 +135,7 @@ class RandomPage extends React.Component<{}, state> {
 | 
				
			|||||||
    private keypress(event: KeyboardEvent): void {
 | 
					    private keypress(event: KeyboardEvent): void {
 | 
				
			||||||
        // bind s to shuffle
 | 
					        // bind s to shuffle
 | 
				
			||||||
        if (event.key === 's') {
 | 
					        if (event.key === 's') {
 | 
				
			||||||
            this.loadShuffledvideos(4);
 | 
					            this.loadShuffledvideos(3);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -94,7 +94,7 @@ export class TVPlayer extends React.Component<Props, State> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private closebtn(): void {
 | 
					    private closebtn(): void {
 | 
				
			||||||
        this.props.history.goBack();
 | 
					        this.props.goBack();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,10 +3,11 @@ import Preview from '../../elements/Preview/Preview';
 | 
				
			|||||||
import {APINode, callAPI, callAPIPlain} from '../../utils/Api';
 | 
					import {APINode, callAPI, callAPIPlain} from '../../utils/Api';
 | 
				
			||||||
import {TVShow} from '../../types/ApiTypes';
 | 
					import {TVShow} from '../../types/ApiTypes';
 | 
				
			||||||
import DynamicContentContainer from '../../elements/DynamicContentContainer/DynamicContentContainer';
 | 
					import DynamicContentContainer from '../../elements/DynamicContentContainer/DynamicContentContainer';
 | 
				
			||||||
import {Route, Switch, useRouteMatch} from 'react-router-dom';
 | 
					import {Route, Routes, useMatch} from 'react-router-dom';
 | 
				
			||||||
import EpisodePage from './EpisodePage';
 | 
					import EpisodePage from './EpisodePage';
 | 
				
			||||||
import PageTitle, {Line} from '../../elements/PageTitle/PageTitle';
 | 
					import PageTitle, {Line} from '../../elements/PageTitle/PageTitle';
 | 
				
			||||||
import SideBar, {SideBarItem, SideBarTitle} from '../../elements/SideBar/SideBar';
 | 
					import SideBar, {SideBarItem, SideBarTitle} from '../../elements/SideBar/SideBar';
 | 
				
			||||||
 | 
					import {useLocation} from "react-router";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface State {
 | 
					interface State {
 | 
				
			||||||
    loading: boolean;
 | 
					    loading: boolean;
 | 
				
			||||||
@@ -55,7 +56,7 @@ export class TVShowPage extends React.Component<Props, State> {
 | 
				
			|||||||
                                        (result) => callback(result)
 | 
					                                        (result) => callback(result)
 | 
				
			||||||
                                    );
 | 
					                                    );
 | 
				
			||||||
                                }}
 | 
					                                }}
 | 
				
			||||||
                                linkPath={'/tvshows/' + elem.Id}
 | 
					                                linkPath={'/media/tvshows/' + elem.Id}
 | 
				
			||||||
                            />
 | 
					                            />
 | 
				
			||||||
                        )}
 | 
					                        )}
 | 
				
			||||||
                        data={this.state.loading ? [] : this.data}
 | 
					                        data={this.state.loading ? [] : this.data}
 | 
				
			||||||
@@ -68,16 +69,16 @@ export class TVShowPage extends React.Component<Props, State> {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function (): JSX.Element {
 | 
					export default function (): JSX.Element {
 | 
				
			||||||
    let match = useRouteMatch();
 | 
					    const { pathname } = useLocation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Switch>
 | 
					        <Routes>
 | 
				
			||||||
            <Route exact path={`${match.path}/:id`}>
 | 
					            <Route path={`${pathname}/:id`}>
 | 
				
			||||||
                <EpisodePage />
 | 
					                <EpisodePage />
 | 
				
			||||||
            </Route>
 | 
					            </Route>
 | 
				
			||||||
            <Route path={match.path}>
 | 
					            <Route path={pathname}>
 | 
				
			||||||
                <TVShowPage />
 | 
					                <TVShowPage />
 | 
				
			||||||
            </Route>
 | 
					            </Route>
 | 
				
			||||||
        </Switch>
 | 
					        </Routes>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -78,8 +78,8 @@ function generalAPICall<T>(
 | 
				
			|||||||
                // decode json or text
 | 
					                // decode json or text
 | 
				
			||||||
                const data = json ? await response.json() : await response.text();
 | 
					                const data = json ? await response.json() : await response.text();
 | 
				
			||||||
                callback(data);
 | 
					                callback(data);
 | 
				
			||||||
            } catch (e) {
 | 
					            } catch (e: any) {
 | 
				
			||||||
                errorcallback(e);
 | 
					                errorcallback(e.toString());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else if (response.status === 400) {
 | 
					        } else if (response.status === 400) {
 | 
				
			||||||
            // Bad Request --> invalid token
 | 
					            // Bad Request --> invalid token
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import React, {FunctionComponent, useState} from 'react';
 | 
					import React, {FunctionComponent, PropsWithChildren, useState} from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface FeatureContextType {
 | 
					export interface FeatureContextType {
 | 
				
			||||||
    setTVShowEnabled: (enabled: boolean) => void;
 | 
					    setTVShowEnabled: (enabled: boolean) => void;
 | 
				
			||||||
@@ -17,7 +17,7 @@ export const FeatureContext = React.createContext<FeatureContextType>({
 | 
				
			|||||||
    VideosFullyDeleteable: false
 | 
					    VideosFullyDeleteable: false
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const FeatureContextProvider: FunctionComponent = (props): JSX.Element => {
 | 
					export const FeatureContextProvider: FunctionComponent<PropsWithChildren> = (props): JSX.Element => {
 | 
				
			||||||
    const [tvshowenabled, settvshowenabled] = useState(false);
 | 
					    const [tvshowenabled, settvshowenabled] = useState(false);
 | 
				
			||||||
    const [fullydeletablevids, setfullydeleteable] = useState(false);
 | 
					    const [fullydeletablevids, setfullydeleteable] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,13 +1,13 @@
 | 
				
			|||||||
import {LoginContext, LoginPerm, LoginState} from './LoginContext';
 | 
					import {LoginContext, LoginPerm, LoginState} from './LoginContext';
 | 
				
			||||||
import React, {FunctionComponent, useContext, useEffect, useState} from 'react';
 | 
					import React, {FunctionComponent, PropsWithChildren, useContext, useEffect, useState} from 'react';
 | 
				
			||||||
import {useHistory, useLocation} from 'react-router';
 | 
					import {useLocation, useNavigate} from 'react-router';
 | 
				
			||||||
import {cookie} from './Cookie';
 | 
					import {cookie} from './Cookie';
 | 
				
			||||||
import {APINode, callAPI} from '../Api';
 | 
					import {APINode, callAPI} from '../Api';
 | 
				
			||||||
import {SettingsTypes} from '../../types/ApiTypes';
 | 
					import {SettingsTypes} from '../../types/ApiTypes';
 | 
				
			||||||
import GlobalInfos from '../GlobalInfos';
 | 
					import GlobalInfos from '../GlobalInfos';
 | 
				
			||||||
import {FeatureContext} from './FeatureContext';
 | 
					import {FeatureContext} from './FeatureContext';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const LoginContextProvider: FunctionComponent = (props): JSX.Element => {
 | 
					export const LoginContextProvider: FunctionComponent<PropsWithChildren> = (props): JSX.Element => {
 | 
				
			||||||
    let initialLoginState = LoginState.LoggedIn;
 | 
					    let initialLoginState = LoginState.LoggedIn;
 | 
				
			||||||
    let initialUserPerm = LoginPerm.User;
 | 
					    let initialUserPerm = LoginPerm.User;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -50,7 +50,7 @@ export const LoginContextProvider: FunctionComponent = (props): JSX.Element => {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
    }, [features, loginState]);
 | 
					    }, [features, loginState]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const hist = useHistory();
 | 
					    const navigate = useNavigate();
 | 
				
			||||||
    const loc = useLocation();
 | 
					    const loc = useLocation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // trigger redirect on loginstate change
 | 
					    // trigger redirect on loginstate change
 | 
				
			||||||
@@ -59,14 +59,14 @@ export const LoginContextProvider: FunctionComponent = (props): JSX.Element => {
 | 
				
			|||||||
            // if we arent already in dashboard tree we want to redirect to default dashboard page
 | 
					            // if we arent already in dashboard tree we want to redirect to default dashboard page
 | 
				
			||||||
            console.log('redirecting to dashboard' + loc.pathname);
 | 
					            console.log('redirecting to dashboard' + loc.pathname);
 | 
				
			||||||
            if (!loc.pathname.startsWith('/media')) {
 | 
					            if (!loc.pathname.startsWith('/media')) {
 | 
				
			||||||
                hist.replace('/media');
 | 
					                navigate('/media');
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            if (!loc.pathname.startsWith('/login')) {
 | 
					            if (!loc.pathname.startsWith('/login')) {
 | 
				
			||||||
                hist.replace('/login');
 | 
					                navigate('/login');
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }, [hist, loc.pathname, loginState]);
 | 
					    }, [navigate, loc.pathname, loginState]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const value = {
 | 
					    const value = {
 | 
				
			||||||
        logout: (): void => {
 | 
					        logout: (): void => {
 | 
				
			||||||
@@ -86,7 +86,7 @@ export const LoginContextProvider: FunctionComponent = (props): JSX.Element => {
 | 
				
			|||||||
    return <LoginContext.Provider value={value}>{props.children}</LoginContext.Provider>;
 | 
					    return <LoginContext.Provider value={value}>{props.children}</LoginContext.Provider>;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Props {
 | 
					interface Props extends PropsWithChildren{
 | 
				
			||||||
    perm: LoginPerm;
 | 
					    perm: LoginPerm;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user