Compare commits
	
		
			1 Commits
		
	
	
		
			master
			...
			settings_r
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7cd9cd7d89 | 
@@ -146,8 +146,8 @@ Test_Server_MANUAL:
 | 
				
			|||||||
    - 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.44:/tmp/
 | 
					    - scp deb/OpenMediaCenter-*.deb root@192.168.0.209:/tmp/
 | 
				
			||||||
    - ssh root@192.168.0.44 "DEBIAN_FRONTEND=noninteractive apt-get --reinstall  -y -qq install /tmp/OpenMediaCenter-*.deb && rm /tmp/OpenMediaCenter-*.deb"
 | 
					    - ssh root@192.168.0.209 "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:
 | 
					Test_Server_2_CD:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,7 +25,7 @@ func getActorsFromDB() {
 | 
				
			|||||||
	 * @apiSuccess {string} .Thumbnail Portrait Thumbnail
 | 
						 * @apiSuccess {string} .Thumbnail Portrait Thumbnail
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	api.AddHandler("getAllActors", api.ActorNode, api.PermUser, func(context api.Context) {
 | 
						api.AddHandler("getAllActors", api.ActorNode, api.PermUser, func(context api.Context) {
 | 
				
			||||||
		query := "SELECT actor_id, name, thumbnail FROM actors ORDER BY name ASC"
 | 
							query := "SELECT actor_id, name, thumbnail FROM actors"
 | 
				
			||||||
		context.Json(readActorsFromResultset(database.Query(query)))
 | 
							context.Json(readActorsFromResultset(database.Query(query)))
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ import (
 | 
				
			|||||||
	"openmediacenter/apiGo/database"
 | 
						"openmediacenter/apiGo/database"
 | 
				
			||||||
	"openmediacenter/apiGo/videoparser"
 | 
						"openmediacenter/apiGo/videoparser"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func addUploadHandler() {
 | 
					func addUploadHandler() {
 | 
				
			||||||
@@ -30,11 +31,11 @@ func addUploadHandler() {
 | 
				
			|||||||
				break
 | 
									break
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// todo allow more video formats than mp4
 | 
				
			||||||
			// only allow valid extensions
 | 
								// only allow valid extensions
 | 
				
			||||||
			if !videoparser.ValidVideoSuffix(part.FileName()) {
 | 
								if filepath.Ext(part.FileName()) != ".mp4" {
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					 | 
				
			||||||
			vidpath := PathPrefix + mSettings.VideoPath + part.FileName()
 | 
								vidpath := PathPrefix + mSettings.VideoPath + part.FileName()
 | 
				
			||||||
			dst, err := os.OpenFile(vidpath, os.O_WRONLY|os.O_CREATE, 0644)
 | 
								dst, err := os.OpenFile(vidpath, os.O_WRONLY|os.O_CREATE, 0644)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,6 +74,7 @@ func getSettingsFromDB() {
 | 
				
			|||||||
			TVShowPath        string
 | 
								TVShowPath        string
 | 
				
			||||||
			TVShowEnabled     bool
 | 
								TVShowEnabled     bool
 | 
				
			||||||
			FullDeleteEnabled bool
 | 
								FullDeleteEnabled bool
 | 
				
			||||||
 | 
								RandomNR          uint32
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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}")
 | 
							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}")
 | 
				
			||||||
@@ -90,6 +91,7 @@ func getSettingsFromDB() {
 | 
				
			|||||||
			TVShowPath:        serverTVShowPath,
 | 
								TVShowPath:        serverTVShowPath,
 | 
				
			||||||
			TVShowEnabled:     !config.GetConfig().Features.DisableTVSupport,
 | 
								TVShowEnabled:     !config.GetConfig().Features.DisableTVSupport,
 | 
				
			||||||
			FullDeleteEnabled: config.GetConfig().Features.FullyDeletableVideos,
 | 
								FullDeleteEnabled: config.GetConfig().Features.FullyDeletableVideos,
 | 
				
			||||||
 | 
								RandomNR:          sett.RandomNR,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		context.Json(res)
 | 
							context.Json(res)
 | 
				
			||||||
@@ -127,12 +129,13 @@ func saveSettingsToDB() {
 | 
				
			|||||||
                        password=?,
 | 
					                        password=?,
 | 
				
			||||||
                        mediacenter_name=?,
 | 
					                        mediacenter_name=?,
 | 
				
			||||||
                        TMDB_grabbing=?, 
 | 
					                        TMDB_grabbing=?, 
 | 
				
			||||||
                        DarkMode=?
 | 
					                        DarkMode=?,
 | 
				
			||||||
 | 
											random_nr=?
 | 
				
			||||||
                    WHERE 1`
 | 
					                    WHERE 1`
 | 
				
			||||||
		// todo avoid conversion
 | 
							// todo avoid conversion
 | 
				
			||||||
		context.Text(string(database.SuccessQuery(query,
 | 
							context.Text(string(database.SuccessQuery(query,
 | 
				
			||||||
			args.VideoPath, args.EpisodePath, args.Password,
 | 
								args.VideoPath, args.EpisodePath, args.Password,
 | 
				
			||||||
			args.MediacenterName, args.TMDBGrabbing, args.DarkMode)))
 | 
								args.MediacenterName, args.TMDBGrabbing, args.DarkMode, args.RandomNR)))
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 ORDER BY tag_name ASC"
 | 
							query := "SELECT tag_id,tag_name from tags"
 | 
				
			||||||
		context.Json(readTagsFromResultset(database.Query(query)))
 | 
							context.Json(readTagsFromResultset(database.Query(query)))
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ func (p Perm) String() string {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const SignKey = "89013f1753a6890c6090b09e3c23ff43"
 | 
					const SignKey = "89013f1753a6890c6090b09e3c23ff43"
 | 
				
			||||||
const TokenExpireHours = 8760
 | 
					const TokenExpireHours = 24
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Token struct {
 | 
					type Token struct {
 | 
				
			||||||
	Token     string
 | 
						Token     string
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ import (
 | 
				
			|||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Jsonify(v interface{}) []byte {
 | 
					func Jsonify(v interface{}) []byte {
 | 
				
			||||||
@@ -29,3 +30,45 @@ func DecodeRequest(request *http.Request, arg interface{}) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// setField set a specific field of an object with an object provided
 | 
				
			||||||
 | 
					func setField(obj interface{}, name string, value interface{}) error {
 | 
				
			||||||
 | 
						structValue := reflect.ValueOf(obj).Elem()
 | 
				
			||||||
 | 
						structFieldValue := structValue.FieldByName(name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !structFieldValue.IsValid() {
 | 
				
			||||||
 | 
							return fmt.Errorf("no such field: %s in obj", name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !structFieldValue.CanSet() {
 | 
				
			||||||
 | 
							return fmt.Errorf("cannot set %s field value", name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						structFieldType := structFieldValue.Type()
 | 
				
			||||||
 | 
						val := reflect.ValueOf(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if structFieldType != val.Type() {
 | 
				
			||||||
 | 
							if val.Type().ConvertibleTo(structFieldType) {
 | 
				
			||||||
 | 
								// if type is convertible - convert and set
 | 
				
			||||||
 | 
								structFieldValue.Set(val.Convert(structFieldType))
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return fmt.Errorf("provided value %s type didn't match obj field type and isn't convertible", name)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// set value if type is the same
 | 
				
			||||||
 | 
							structFieldValue.Set(val)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FillStruct fill a custom struct with objects of a map
 | 
				
			||||||
 | 
					func FillStruct(i interface{}, m map[string]interface{}) error {
 | 
				
			||||||
 | 
						for k, v := range m {
 | 
				
			||||||
 | 
							err := setField(i, k, v)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,6 +48,7 @@ type SettingsType struct {
 | 
				
			|||||||
	PasswordEnabled bool
 | 
						PasswordEnabled bool
 | 
				
			||||||
	TMDBGrabbing    bool
 | 
						TMDBGrabbing    bool
 | 
				
			||||||
	DarkMode        bool
 | 
						DarkMode        bool
 | 
				
			||||||
 | 
						RandomNR        uint32
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type SettingsSizeType struct {
 | 
					type SettingsSizeType struct {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -126,7 +126,7 @@ func GetSettings() (result types.SettingsType, PathPrefix string, sizes types.Se
 | 
				
			|||||||
                           SELECT COUNT(*)
 | 
					                           SELECT COUNT(*)
 | 
				
			||||||
                           FROM video_tags
 | 
					                           FROM video_tags
 | 
				
			||||||
                       ) AS tagsadded,
 | 
					                       ) AS tagsadded,
 | 
				
			||||||
                       video_path, episode_path, password, mediacenter_name, TMDB_grabbing, DarkMode
 | 
					                       video_path, episode_path, password, mediacenter_name, TMDB_grabbing, DarkMode, random_nr
 | 
				
			||||||
                FROM settings
 | 
					                FROM settings
 | 
				
			||||||
                LIMIT 1`, DBName)
 | 
					                LIMIT 1`, DBName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -134,7 +134,7 @@ func GetSettings() (result types.SettingsType, PathPrefix string, sizes types.Se
 | 
				
			|||||||
	var TMDBGrabbing int
 | 
						var TMDBGrabbing int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err := QueryRow(query).Scan(&sizes.VideoNr, &sizes.DBSize, &sizes.DifferentTags, &sizes.TagsAdded,
 | 
						err := QueryRow(query).Scan(&sizes.VideoNr, &sizes.DBSize, &sizes.DifferentTags, &sizes.TagsAdded,
 | 
				
			||||||
		&result.VideoPath, &result.EpisodePath, &result.Password, &result.MediacenterName, &TMDBGrabbing, &DarkMode)
 | 
							&result.VideoPath, &result.EpisodePath, &result.Password, &result.MediacenterName, &TMDBGrabbing, &DarkMode, &result.RandomNR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Println(err.Error())
 | 
							fmt.Println(err.Error())
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								apiGo/database/migrations/20220505195845_randomnr.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								apiGo/database/migrations/20220505195845_randomnr.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					-- +goose Up
 | 
				
			||||||
 | 
					-- +goose StatementBegin
 | 
				
			||||||
 | 
					alter table settings
 | 
				
			||||||
 | 
					    add random_nr int default  3 null;
 | 
				
			||||||
 | 
					-- +goose StatementEnd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- +goose Down
 | 
				
			||||||
 | 
					-- +goose StatementBegin
 | 
				
			||||||
 | 
					alter table settings
 | 
				
			||||||
 | 
					    drop random_nr;
 | 
				
			||||||
 | 
					-- +goose StatementEnd
 | 
				
			||||||
@@ -20,15 +20,16 @@ type SettingsType struct {
 | 
				
			|||||||
	MediacenterName string
 | 
						MediacenterName string
 | 
				
			||||||
	VideoPath       string
 | 
						VideoPath       string
 | 
				
			||||||
	TVShowPath      string
 | 
						TVShowPath      string
 | 
				
			||||||
 | 
						RandomNR        uint32
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LoadSettings() *SettingsType {
 | 
					func LoadSettings() *SettingsType {
 | 
				
			||||||
	query := "SELECT DarkMode, password, mediacenter_name, video_path, episode_path from settings"
 | 
						query := "SELECT DarkMode, password, mediacenter_name, video_path, episode_path, random_nr from settings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result := SettingsType{}
 | 
						result := SettingsType{}
 | 
				
			||||||
	var darkmode uint8
 | 
						var darkmode uint8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err := database.QueryRow(query).Scan(&darkmode, &result.Pasword, &result.MediacenterName, &result.VideoPath, &result.TVShowPath)
 | 
						err := database.QueryRow(query).Scan(&darkmode, &result.Pasword, &result.MediacenterName, &result.VideoPath, &result.TVShowPath, &result.RandomNR)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Println("error while parsing db data: " + err.Error())
 | 
							fmt.Println("error while parsing db data: " + err.Error())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,6 @@ 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,8 +9,6 @@ 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=
 | 
				
			||||||
@@ -84,9 +82,8 @@ 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=
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,18 +0,0 @@
 | 
				
			|||||||
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")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,5 +0,0 @@
 | 
				
			|||||||
package housekeeping
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func fixMissingMetadata() {
 | 
					 | 
				
			||||||
	// todo
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,86 +0,0 @@
 | 
				
			|||||||
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
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,37 +0,0 @@
 | 
				
			|||||||
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,13 +1,11 @@
 | 
				
			|||||||
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"
 | 
				
			||||||
@@ -19,8 +17,6 @@ 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
 | 
				
			||||||
@@ -33,16 +29,9 @@ 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()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,58 +0,0 @@
 | 
				
			|||||||
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)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -25,18 +25,20 @@ func startTVShowReindex(files []Show) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func insertEpisodesIfNotExisting(show Show) {
 | 
					func insertEpisodesIfNotExisting(show Show) {
 | 
				
			||||||
	query := "SELECT filename FROM tvshow_episodes JOIN tvshow t on t.id = tvshow_episodes.tvshow_id WHERE t.name=?"
 | 
						query := "SELECT tvshow_episodes.name, season, episode FROM tvshow_episodes JOIN tvshow t on t.id = tvshow_episodes.tvshow_id WHERE t.name=?"
 | 
				
			||||||
	rows := database.Query(query, show.Name)
 | 
						rows := database.Query(query, show.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var dbepisodes []string
 | 
						var dbepisodes []string
 | 
				
			||||||
	for rows.Next() {
 | 
						for rows.Next() {
 | 
				
			||||||
		var filename string
 | 
							var epname string
 | 
				
			||||||
		err := rows.Scan(&filename)
 | 
							var season int
 | 
				
			||||||
 | 
							var episode int
 | 
				
			||||||
 | 
							err := rows.Scan(&epname, &season, &episode)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			fmt.Println(err.Error())
 | 
								fmt.Println(err.Error())
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		dbepisodes = append(dbepisodes, filename)
 | 
							dbepisodes = append(dbepisodes, fmt.Sprintf("%s S%02dE%02d.mp4", epname, season, episode))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// get those episodes that are missing in db
 | 
						// get those episodes that are missing in db
 | 
				
			||||||
@@ -81,10 +83,6 @@ VALUES (?, ?, ?, (SELECT tvshow.id FROM tvshow WHERE tvshow.name=?), ?, ?)`
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// difference returns the elements in `a` that aren't in `b`.
 | 
					// difference returns the elements in `a` that aren't in `b`.
 | 
				
			||||||
func difference(a, b []string) []string {
 | 
					func difference(a, b []string) []string {
 | 
				
			||||||
	if b == nil || len(b) == 0 {
 | 
					 | 
				
			||||||
		return a
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mb := make(map[string]struct{}, len(b))
 | 
						mb := make(map[string]struct{}, len(b))
 | 
				
			||||||
	for _, x := range b {
 | 
						for _, x := range b {
 | 
				
			||||||
		mb[x] = struct{}{}
 | 
							mb[x] = struct{}{}
 | 
				
			||||||
@@ -131,10 +129,7 @@ func getAllTVShows() *[]string {
 | 
				
			|||||||
	var res []string
 | 
						var res []string
 | 
				
			||||||
	for rows.Next() {
 | 
						for rows.Next() {
 | 
				
			||||||
		var show string
 | 
							var show string
 | 
				
			||||||
		err := rows.Scan(&show)
 | 
							rows.Scan(&show)
 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		res = append(res, show)
 | 
							res = append(res, show)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ import (
 | 
				
			|||||||
	"openmediacenter/apiGo/config"
 | 
						"openmediacenter/apiGo/config"
 | 
				
			||||||
	"openmediacenter/apiGo/database"
 | 
						"openmediacenter/apiGo/database"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -14,20 +15,6 @@ type StatusMessage struct {
 | 
				
			|||||||
	ContentAvailable bool
 | 
						ContentAvailable bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getVideoTypes() []string {
 | 
					 | 
				
			||||||
	return []string{".mp4", ".mov", ".mkv", ".flv", ".avi", ".mpeg", ".m4v"}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func ValidVideoSuffix(filename string) bool {
 | 
					 | 
				
			||||||
	validExts := getVideoTypes()
 | 
					 | 
				
			||||||
	for _, validExt := range validExts {
 | 
					 | 
				
			||||||
		if strings.HasSuffix(filename, validExt) {
 | 
					 | 
				
			||||||
			return true
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func StartReindex() bool {
 | 
					func StartReindex() bool {
 | 
				
			||||||
	fmt.Println("starting reindex..")
 | 
						fmt.Println("starting reindex..")
 | 
				
			||||||
	SendEvent("start")
 | 
						SendEvent("start")
 | 
				
			||||||
@@ -46,19 +33,22 @@ func StartReindex() bool {
 | 
				
			|||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	filelist, err := ioutil.ReadDir(vidFolder)
 | 
						var files []string
 | 
				
			||||||
 | 
						err := filepath.Walk(vidFolder, func(path string, info os.FileInfo, err error) error {
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			fmt.Println(err.Error())
 | 
								fmt.Println(err.Error())
 | 
				
			||||||
		return false
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var files []string
 | 
							if !info.IsDir() && strings.HasSuffix(info.Name(), ".mp4") {
 | 
				
			||||||
	for _, file := range filelist {
 | 
								files = append(files, info.Name())
 | 
				
			||||||
		if !file.IsDir() && ValidVideoSuffix(file.Name()) {
 | 
					 | 
				
			||||||
			files = append(files, file.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)
 | 
				
			||||||
@@ -117,7 +107,7 @@ func StartTVShowReindex() {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			for _, epfile := range episodefiles {
 | 
								for _, epfile := range episodefiles {
 | 
				
			||||||
				if ValidVideoSuffix(epfile.Name()) {
 | 
									if strings.HasSuffix(epfile.Name(), ".mp4") {
 | 
				
			||||||
					elem.files = append(elem.files, epfile.Name())
 | 
										elem.files = append(elem.files, epfile.Name())
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,7 +58,7 @@ export class HomePage extends React.Component<Props, state> {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    sortState = SortBy.random;
 | 
					    sortState = SortBy.date;
 | 
				
			||||||
    tagState = DefaultTags.all;
 | 
					    tagState = DefaultTags.all;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    componentDidMount(): void {
 | 
					    componentDidMount(): void {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,8 +9,9 @@ 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 {IconButton} from '../../elements/GPElements/Button';
 | 
				
			||||||
import {faMinusCircle, faPlusCircle} from '@fortawesome/free-solid-svg-icons';
 | 
					import {faPlusCircle} from '@fortawesome/free-solid-svg-icons';
 | 
				
			||||||
import AddTagPopup from '../../elements/Popups/AddTagPopup/AddTagPopup';
 | 
					import AddTagPopup from '../../elements/Popups/AddTagPopup/AddTagPopup';
 | 
				
			||||||
 | 
					import GlobalInfos from '../../utils/GlobalInfos';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface state {
 | 
					interface state {
 | 
				
			||||||
    videos: VideoTypes.VideoUnloadedType[];
 | 
					    videos: VideoTypes.VideoUnloadedType[];
 | 
				
			||||||
@@ -28,8 +29,6 @@ interface GetRandomMoviesType {
 | 
				
			|||||||
 * Randompage shuffles random viedeopreviews and provides a shuffle btn
 | 
					 * Randompage shuffles random viedeopreviews and provides a shuffle btn
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class RandomPage extends React.Component<{}, state> {
 | 
					class RandomPage extends React.Component<{}, state> {
 | 
				
			||||||
    readonly LoadNR = 3;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor(props: {}) {
 | 
					    constructor(props: {}) {
 | 
				
			||||||
        super(props);
 | 
					        super(props);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -46,7 +45,7 @@ class RandomPage extends React.Component<{}, state> {
 | 
				
			|||||||
    componentDidMount(): void {
 | 
					    componentDidMount(): void {
 | 
				
			||||||
        addKeyHandler(this.keypress);
 | 
					        addKeyHandler(this.keypress);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.loadShuffledvideos(this.LoadNR);
 | 
					        this.loadShuffledvideos();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    componentWillUnmount(): void {
 | 
					    componentWillUnmount(): void {
 | 
				
			||||||
@@ -56,7 +55,7 @@ class RandomPage extends React.Component<{}, state> {
 | 
				
			|||||||
    render(): JSX.Element {
 | 
					    render(): JSX.Element {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <div>
 | 
					            <div>
 | 
				
			||||||
                <PageTitle title='Random Videos' subtitle='3pc' />
 | 
					                <PageTitle title='Random Videos' subtitle={GlobalInfos.getRandomNR() + 'pc'} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <SideBar>
 | 
					                <SideBar>
 | 
				
			||||||
                    <SideBarTitle>Visible Tags:</SideBarTitle>
 | 
					                    <SideBarTitle>Visible Tags:</SideBarTitle>
 | 
				
			||||||
@@ -75,24 +74,11 @@ class RandomPage extends React.Component<{}, state> {
 | 
				
			|||||||
                            this.setState({addTagPopup: true});
 | 
					                            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 ? <VideoContainer data={this.state.videos} /> : <div>No Data found!</div>}
 | 
					                {this.state.videos.length !== 0 ? <VideoContainer data={this.state.videos} /> : <div>No Data found!</div>}
 | 
				
			||||||
                <div className={style.Shufflebutton}>
 | 
					                <div className={style.Shufflebutton}>
 | 
				
			||||||
                    <button onClick={(): void => this.loadShuffledvideos(this.LoadNR)} className={style.btnshuffle}>
 | 
					                    <button onClick={(): void => this.loadShuffledvideos()} className={style.btnshuffle}>
 | 
				
			||||||
                        Shuffle
 | 
					                        Shuffle
 | 
				
			||||||
                    </button>
 | 
					                    </button>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
@@ -101,7 +87,7 @@ class RandomPage extends React.Component<{}, state> {
 | 
				
			|||||||
                        onHide={(): void => this.setState({addTagPopup: false})}
 | 
					                        onHide={(): void => this.setState({addTagPopup: false})}
 | 
				
			||||||
                        submit={(tagId: number, tagName: string): void => {
 | 
					                        submit={(tagId: number, tagName: string): void => {
 | 
				
			||||||
                            this.setState({filterTags: [...this.state.filterTags, {TagId: tagId, TagName: tagName}]}, (): void => {
 | 
					                            this.setState({filterTags: [...this.state.filterTags, {TagId: tagId, TagName: tagName}]}, (): void => {
 | 
				
			||||||
                                this.loadShuffledvideos(this.LoadNR);
 | 
					                                this.loadShuffledvideos();
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                        }}
 | 
					                        }}
 | 
				
			||||||
                    />
 | 
					                    />
 | 
				
			||||||
@@ -112,12 +98,15 @@ class RandomPage extends React.Component<{}, state> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * load random videos from backend
 | 
					     * load random videos from backend
 | 
				
			||||||
     * @param nr number of videos to load
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    loadShuffledvideos(nr: number): void {
 | 
					    loadShuffledvideos(): void {
 | 
				
			||||||
        callAPI<GetRandomMoviesType>(
 | 
					        callAPI<GetRandomMoviesType>(
 | 
				
			||||||
            APINode.Video,
 | 
					            APINode.Video,
 | 
				
			||||||
            {action: 'getRandomMovies', Number: nr, TagFilter: this.state.filterTags.map((t) => t.TagId)},
 | 
					            {
 | 
				
			||||||
 | 
					                action: 'getRandomMovies',
 | 
				
			||||||
 | 
					                Number: GlobalInfos.getRandomNR(),
 | 
				
			||||||
 | 
					                TagFilter: this.state.filterTags.map((t) => t.TagId)
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
            (result) => {
 | 
					            (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({
 | 
				
			||||||
@@ -135,7 +124,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(3);
 | 
					            this.loadShuffledvideos();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,7 +33,8 @@ class GeneralSettings extends React.Component<Props, state> {
 | 
				
			|||||||
                Password: '',
 | 
					                Password: '',
 | 
				
			||||||
                PasswordEnabled: false,
 | 
					                PasswordEnabled: false,
 | 
				
			||||||
                TMDBGrabbing: false,
 | 
					                TMDBGrabbing: false,
 | 
				
			||||||
                VideoPath: ''
 | 
					                VideoPath: '',
 | 
				
			||||||
 | 
					                RandomNR: 3
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            sizes: {
 | 
					            sizes: {
 | 
				
			||||||
                DBSize: 0,
 | 
					                DBSize: 0,
 | 
				
			||||||
@@ -199,6 +200,23 @@ class GeneralSettings extends React.Component<Props, state> {
 | 
				
			|||||||
                            />
 | 
					                            />
 | 
				
			||||||
                        </Form.Group>
 | 
					                        </Form.Group>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        <Form.Group className={style.mediacenternameform} data-testid='randnrform'>
 | 
				
			||||||
 | 
					                            <Form.Label>Number of random videos on Random page</Form.Label>
 | 
				
			||||||
 | 
					                            <Form.Control
 | 
				
			||||||
 | 
					                                type='number'
 | 
				
			||||||
 | 
					                                placeholder='2'
 | 
				
			||||||
 | 
					                                value={this.state.generalSettings.RandomNR}
 | 
				
			||||||
 | 
					                                onChange={(e): void =>
 | 
				
			||||||
 | 
					                                    this.setState({
 | 
				
			||||||
 | 
					                                        generalSettings: {
 | 
				
			||||||
 | 
					                                            ...this.state.generalSettings,
 | 
				
			||||||
 | 
					                                            RandomNR: parseInt(e.target.value, 10)
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                    })
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            />
 | 
				
			||||||
 | 
					                        </Form.Group>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <Button variant='primary' type='submit'>
 | 
					                        <Button variant='primary' type='submit'>
 | 
				
			||||||
                            Submit
 | 
					                            Submit
 | 
				
			||||||
                        </Button>
 | 
					                        </Button>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,7 +66,7 @@ export class EpisodePage extends React.Component<Props, State> {
 | 
				
			|||||||
export const EpisodeTile = (props: {episode: Episode}): JSX.Element => {
 | 
					export const EpisodeTile = (props: {episode: Episode}): JSX.Element => {
 | 
				
			||||||
    const themestyle = GlobalInfos.getThemeStyle();
 | 
					    const themestyle = GlobalInfos.getThemeStyle();
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Link to={'/media/tvplayer/' + props.episode.ID}>
 | 
					        <Link to={'/tvplayer/' + props.episode.ID}>
 | 
				
			||||||
            <div className={tileStyle.tile + ' ' + themestyle.secbackground + ' ' + themestyle.textcolor}>
 | 
					            <div className={tileStyle.tile + ' ' + themestyle.secbackground + ' ' + themestyle.textcolor}>
 | 
				
			||||||
                <FontAwesomeIcon
 | 
					                <FontAwesomeIcon
 | 
				
			||||||
                    style={{
 | 
					                    style={{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,7 +55,7 @@ export class TVShowPage extends React.Component<Props, State> {
 | 
				
			|||||||
                                        (result) => callback(result)
 | 
					                                        (result) => callback(result)
 | 
				
			||||||
                                    );
 | 
					                                    );
 | 
				
			||||||
                                }}
 | 
					                                }}
 | 
				
			||||||
                                linkPath={'/media/tvshows/' + elem.Id}
 | 
					                                linkPath={'/tvshows/' + elem.Id}
 | 
				
			||||||
                            />
 | 
					                            />
 | 
				
			||||||
                        )}
 | 
					                        )}
 | 
				
			||||||
                        data={this.state.loading ? [] : this.data}
 | 
					                        data={this.state.loading ? [] : this.data}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,6 +40,7 @@ export namespace SettingsTypes {
 | 
				
			|||||||
        TVShowPath: string;
 | 
					        TVShowPath: string;
 | 
				
			||||||
        TVShowEnabled: boolean;
 | 
					        TVShowEnabled: boolean;
 | 
				
			||||||
        FullDeleteEnabled: boolean;
 | 
					        FullDeleteEnabled: boolean;
 | 
				
			||||||
 | 
					        RandomNR: number;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    export interface SettingsType {
 | 
					    export interface SettingsType {
 | 
				
			||||||
@@ -50,6 +51,7 @@ export namespace SettingsTypes {
 | 
				
			|||||||
        PasswordEnabled: boolean;
 | 
					        PasswordEnabled: boolean;
 | 
				
			||||||
        TMDBGrabbing: boolean;
 | 
					        TMDBGrabbing: boolean;
 | 
				
			||||||
        DarkMode: boolean;
 | 
					        DarkMode: boolean;
 | 
				
			||||||
 | 
					        RandomNR: number;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    export interface SizesType {
 | 
					    export interface SizesType {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ class StaticInfos {
 | 
				
			|||||||
    private tvshowpath: string = '';
 | 
					    private tvshowpath: string = '';
 | 
				
			||||||
    private TVShowsEnabled: boolean = false;
 | 
					    private TVShowsEnabled: boolean = false;
 | 
				
			||||||
    private fullDeleteable: boolean = false;
 | 
					    private fullDeleteable: boolean = false;
 | 
				
			||||||
 | 
					    private RandomNR: number = 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * check if the current theme is the dark theme
 | 
					     * check if the current theme is the dark theme
 | 
				
			||||||
@@ -44,6 +45,7 @@ class StaticInfos {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    handlers: (() => void)[] = [];
 | 
					    handlers: (() => void)[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    onThemeChange(func: () => void): void {
 | 
					    onThemeChange(func: () => void): void {
 | 
				
			||||||
        this.handlers.push(func);
 | 
					        this.handlers.push(func);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -71,6 +73,14 @@ class StaticInfos {
 | 
				
			|||||||
    getTVShowPath(): string {
 | 
					    getTVShowPath(): string {
 | 
				
			||||||
        return this.tvshowpath;
 | 
					        return this.tvshowpath;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setRandomNR(nr: number): void {
 | 
				
			||||||
 | 
					        this.RandomNR = nr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getRandomNR(): number {
 | 
				
			||||||
 | 
					        return this.RandomNR;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default new StaticInfos();
 | 
					export default new StaticInfos();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,6 +32,7 @@ export const LoginContextProvider: FunctionComponent = (props): JSX.Element => {
 | 
				
			|||||||
                GlobalInfos.enableDarkTheme(result.DarkMode);
 | 
					                GlobalInfos.enableDarkTheme(result.DarkMode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                GlobalInfos.setVideoPaths(result.VideoPath, result.TVShowPath);
 | 
					                GlobalInfos.setVideoPaths(result.VideoPath, result.TVShowPath);
 | 
				
			||||||
 | 
					                GlobalInfos.setRandomNR(result.RandomNR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                features.setTVShowEnabled(result.TVShowEnabled);
 | 
					                features.setTVShowEnabled(result.TVShowEnabled);
 | 
				
			||||||
                features.setVideosFullyDeleteable(result.FullDeleteEnabled);
 | 
					                features.setVideosFullyDeleteable(result.FullDeleteEnabled);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user