Compare commits
1 Commits
hls_on_the
...
settings_r
Author | SHA1 | Date | |
---|---|---|---|
7cd9cd7d89 |
@ -146,8 +146,8 @@ Test_Server_MANUAL:
|
||||
- echo "$SSH_PRIVATE_KEY_2" | ssh-add -
|
||||
- mkdir -p ~/.ssh
|
||||
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
|
||||
- scp deb/OpenMediaCenter-*.deb root@192.168.0.44:/tmp/
|
||||
- ssh root@192.168.0.44 "DEBIAN_FRONTEND=noninteractive apt-get --reinstall -y -qq install /tmp/OpenMediaCenter-*.deb && rm /tmp/OpenMediaCenter-*.deb"
|
||||
- scp deb/OpenMediaCenter-*.deb root@192.168.0.209:/tmp/
|
||||
- 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
|
||||
|
||||
Test_Server_2_CD:
|
||||
@ -158,4 +158,4 @@ Test_Server_2_CD:
|
||||
|
||||
Test_Server_2_MANUAL:
|
||||
extends: .Test_Server_2_Common
|
||||
when: manual
|
||||
when: manual
|
@ -25,7 +25,7 @@ func getActorsFromDB() {
|
||||
* @apiSuccess {string} .Thumbnail Portrait Thumbnail
|
||||
*/
|
||||
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)))
|
||||
})
|
||||
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"openmediacenter/apiGo/database"
|
||||
"openmediacenter/apiGo/videoparser"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func addUploadHandler() {
|
||||
@ -30,11 +31,11 @@ func addUploadHandler() {
|
||||
break
|
||||
}
|
||||
|
||||
// todo allow more video formats than mp4
|
||||
// only allow valid extensions
|
||||
if !videoparser.ValidVideoSuffix(part.FileName()) {
|
||||
if filepath.Ext(part.FileName()) != ".mp4" {
|
||||
continue
|
||||
}
|
||||
|
||||
vidpath := PathPrefix + mSettings.VideoPath + part.FileName()
|
||||
dst, err := os.OpenFile(vidpath, os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
|
@ -74,6 +74,7 @@ func getSettingsFromDB() {
|
||||
TVShowPath string
|
||||
TVShowEnabled 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}")
|
||||
@ -90,6 +91,7 @@ func getSettingsFromDB() {
|
||||
TVShowPath: serverTVShowPath,
|
||||
TVShowEnabled: !config.GetConfig().Features.DisableTVSupport,
|
||||
FullDeleteEnabled: config.GetConfig().Features.FullyDeletableVideos,
|
||||
RandomNR: sett.RandomNR,
|
||||
}
|
||||
|
||||
context.Json(res)
|
||||
@ -127,12 +129,13 @@ func saveSettingsToDB() {
|
||||
password=?,
|
||||
mediacenter_name=?,
|
||||
TMDB_grabbing=?,
|
||||
DarkMode=?
|
||||
DarkMode=?,
|
||||
random_nr=?
|
||||
WHERE 1`
|
||||
// todo avoid conversion
|
||||
context.Text(string(database.SuccessQuery(query,
|
||||
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
|
||||
*/
|
||||
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)))
|
||||
})
|
||||
}
|
||||
|
@ -3,15 +3,12 @@ package api
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"openmediacenter/apiGo/api/api"
|
||||
"openmediacenter/apiGo/api/types"
|
||||
"openmediacenter/apiGo/config"
|
||||
"openmediacenter/apiGo/database"
|
||||
"openmediacenter/apiGo/videoparser/hls"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@ -401,62 +398,6 @@ func loadVideosHandlers() {
|
||||
|
||||
context.Json(result)
|
||||
})
|
||||
|
||||
api.AddHandler("loadM3U8", api.VideoNode, api.PermUnauthorized, func(ctx api.Context) {
|
||||
param := ctx.GetRequest().URL.Query().Get("id")
|
||||
id, _ := strconv.Atoi(param)
|
||||
|
||||
mylist :=
|
||||
`#EXTM3U
|
||||
#EXT-X-VERSION:4
|
||||
#EXT-X-MEDIA-SEQUENCE:0
|
||||
#EXT-X-ALLOW-CACHE:NO
|
||||
#EXT-X-TARGETDURATION:10
|
||||
#EXT-X-START:TIME-OFFSET=0
|
||||
#EXT-X-PLAYLIST-TYPE:VOD
|
||||
`
|
||||
// ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4
|
||||
cmd := exec.Command("ffprobe",
|
||||
"-v", "error",
|
||||
"-show_entries", "format=duration",
|
||||
"-of", "default=noprint_wrappers=1:nokey=1",
|
||||
hls.GetVideoPathById(uint32(id)))
|
||||
stdout, err := cmd.Output()
|
||||
//
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
fmt.Println(string(err.(*exec.ExitError).Stderr))
|
||||
}
|
||||
secss, _, _ := strings.Cut(string(stdout), ".")
|
||||
secsi, err := strconv.Atoi(secss)
|
||||
|
||||
i := 0
|
||||
for ; i < secsi/10; i++ {
|
||||
mylist += fmt.Sprintf(
|
||||
`#EXTINF:10.0,
|
||||
/api/video/getVideoSegment?id=%d&idx=%d
|
||||
`, id, i)
|
||||
}
|
||||
mylist += fmt.Sprintf(
|
||||
`#EXTINF:%s,
|
||||
/api/video/getVideoSegment?id=%d&idx=%d
|
||||
EXT-X-ENDLIST
|
||||
`, fmt.Sprintf("%d.0", secsi%10), id, i)
|
||||
ctx.Text(mylist)
|
||||
})
|
||||
|
||||
api.AddHandler("getVideoSegment", api.VideoNode, api.PermUnauthorized, func(ctx api.Context) {
|
||||
params := ctx.GetRequest().URL.Query()
|
||||
idxs := params.Get("idx")
|
||||
ids := params.Get("id")
|
||||
|
||||
id, _ := strconv.Atoi(ids)
|
||||
idx, _ := strconv.Atoi(idxs)
|
||||
// todo error handling
|
||||
|
||||
tmppath := hls.GetSegment(uint32(idx), uint32(id))
|
||||
http.ServeFile(ctx.GetWriter(), ctx.GetRequest(), tmppath)
|
||||
})
|
||||
}
|
||||
|
||||
func addToVideoHandlers() {
|
||||
|
@ -21,7 +21,7 @@ func (p Perm) String() string {
|
||||
}
|
||||
|
||||
const SignKey = "89013f1753a6890c6090b09e3c23ff43"
|
||||
const TokenExpireHours = 8760
|
||||
const TokenExpireHours = 24
|
||||
|
||||
type Token struct {
|
||||
Token string
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func Jsonify(v interface{}) []byte {
|
||||
@ -29,3 +30,45 @@ func DecodeRequest(request *http.Request, arg interface{}) error {
|
||||
|
||||
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
|
||||
TMDBGrabbing bool
|
||||
DarkMode bool
|
||||
RandomNR uint32
|
||||
}
|
||||
|
||||
type SettingsSizeType struct {
|
||||
|
@ -24,7 +24,6 @@ type FeaturesT struct {
|
||||
type GeneralT struct {
|
||||
VerboseLogging bool
|
||||
ReindexPrefix string
|
||||
TmpDir string
|
||||
}
|
||||
|
||||
type FileConfT struct {
|
||||
@ -45,7 +44,6 @@ func defaultConfig() *FileConfT {
|
||||
General: GeneralT{
|
||||
VerboseLogging: false,
|
||||
ReindexPrefix: "/var/www/openmediacenter",
|
||||
TmpDir: "/tmp/openmediacenter",
|
||||
},
|
||||
Features: FeaturesT{
|
||||
DisableTVSupport: false,
|
||||
|
@ -126,7 +126,7 @@ func GetSettings() (result types.SettingsType, PathPrefix string, sizes types.Se
|
||||
SELECT COUNT(*)
|
||||
FROM video_tags
|
||||
) 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
|
||||
LIMIT 1`, DBName)
|
||||
|
||||
@ -134,7 +134,7 @@ func GetSettings() (result types.SettingsType, PathPrefix string, sizes types.Se
|
||||
var TMDBGrabbing int
|
||||
|
||||
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 {
|
||||
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
|
||||
VideoPath string
|
||||
TVShowPath string
|
||||
RandomNR uint32
|
||||
}
|
||||
|
||||
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{}
|
||||
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 {
|
||||
fmt.Println("error while parsing db data: " + err.Error())
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ go 1.16
|
||||
require (
|
||||
github.com/3d0c/gmf v0.0.0-20210925211039-e278e6e53b16
|
||||
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/pelletier/go-toml/v2 v2.0.0-beta.3
|
||||
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/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/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/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
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-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-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-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/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
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
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"openmediacenter/apiGo/api"
|
||||
api2 "openmediacenter/apiGo/api/api"
|
||||
"openmediacenter/apiGo/config"
|
||||
"openmediacenter/apiGo/database"
|
||||
"openmediacenter/apiGo/housekeeping"
|
||||
"openmediacenter/apiGo/static"
|
||||
"openmediacenter/apiGo/videoparser"
|
||||
"os"
|
||||
@ -19,8 +17,6 @@ func main() {
|
||||
const port uint16 = 8081
|
||||
errc := make(chan error, 1)
|
||||
|
||||
housekPTr := flag.Bool("HouseKeeping", false, "Run housekeeping tasks")
|
||||
|
||||
config.Init()
|
||||
|
||||
// todo some verbosity logger or sth
|
||||
@ -33,16 +29,9 @@ func main() {
|
||||
}
|
||||
defer database.Close()
|
||||
|
||||
// check if we should run the housekeeping tasks
|
||||
if *housekPTr {
|
||||
housekeeping.RunHouseKeepingTasks()
|
||||
return
|
||||
}
|
||||
|
||||
api.AddHandlers()
|
||||
|
||||
videoparser.SetupSettingsWebsocket()
|
||||
videoparser.InitFileWatcher()
|
||||
|
||||
// add the static files
|
||||
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) {
|
||||
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)
|
||||
|
||||
var dbepisodes []string
|
||||
for rows.Next() {
|
||||
var filename string
|
||||
err := rows.Scan(&filename)
|
||||
var epname string
|
||||
var season int
|
||||
var episode int
|
||||
err := rows.Scan(&epname, &season, &episode)
|
||||
if err != nil {
|
||||
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
|
||||
@ -81,10 +83,6 @@ VALUES (?, ?, ?, (SELECT tvshow.id FROM tvshow WHERE tvshow.name=?), ?, ?)`
|
||||
|
||||
// difference returns the elements in `a` that aren't in `b`.
|
||||
func difference(a, b []string) []string {
|
||||
if b == nil || len(b) == 0 {
|
||||
return a
|
||||
}
|
||||
|
||||
mb := make(map[string]struct{}, len(b))
|
||||
for _, x := range b {
|
||||
mb[x] = struct{}{}
|
||||
@ -131,10 +129,7 @@ func getAllTVShows() *[]string {
|
||||
var res []string
|
||||
for rows.Next() {
|
||||
var show string
|
||||
err := rows.Scan(&show)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
rows.Scan(&show)
|
||||
|
||||
res = append(res, show)
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"openmediacenter/apiGo/config"
|
||||
"openmediacenter/apiGo/database"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -14,20 +15,6 @@ type StatusMessage struct {
|
||||
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 {
|
||||
fmt.Println("starting reindex..")
|
||||
SendEvent("start")
|
||||
@ -46,19 +33,22 @@ func StartReindex() bool {
|
||||
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 {
|
||||
fmt.Println(err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() && strings.HasSuffix(info.Name(), ".mp4") {
|
||||
files = append(files, info.Name())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
var files []string
|
||||
for _, file := range filelist {
|
||||
if !file.IsDir() && ValidVideoSuffix(file.Name()) {
|
||||
files = append(files, file.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// start reindex process
|
||||
AppendMessage("Starting Reindexing!")
|
||||
InitDeps(&mSettings)
|
||||
@ -117,7 +107,7 @@ func StartTVShowReindex() {
|
||||
}
|
||||
|
||||
for _, epfile := range episodefiles {
|
||||
if ValidVideoSuffix(epfile.Name()) {
|
||||
if strings.HasSuffix(epfile.Name(), ".mp4") {
|
||||
elem.files = append(elem.files, epfile.Name())
|
||||
}
|
||||
}
|
||||
|
@ -1,133 +0,0 @@
|
||||
package hls
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"openmediacenter/apiGo/config"
|
||||
"openmediacenter/apiGo/database"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TranscodingState struct {
|
||||
Finished bool
|
||||
Active bool
|
||||
}
|
||||
|
||||
var transcodeAcive = make(map[uint32]TranscodingState)
|
||||
|
||||
func startSegmentation(videoid uint32) {
|
||||
transcodeAcive[videoid] = TranscodingState{
|
||||
Finished: false,
|
||||
Active: true,
|
||||
}
|
||||
|
||||
cfg := config.GetConfig()
|
||||
outpath := fmt.Sprintf("%s/%d", cfg.General.TmpDir, videoid)
|
||||
|
||||
if !fileExists(outpath) {
|
||||
err := os.MkdirAll(outpath, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
app := "ffmpeg"
|
||||
inputpath := GetVideoPathById(videoid)
|
||||
fmt.Println(inputpath)
|
||||
cmd := exec.Command(app,
|
||||
//"-hide_banner",
|
||||
//"-loglevel", "panic",
|
||||
"-n",
|
||||
//"-t", "10.0",
|
||||
//"-ss", fmt.Sprintf("%d", id*10),
|
||||
"-i", inputpath,
|
||||
//"-g", "52",
|
||||
//"-sc_threshold", "0",
|
||||
//"-force_key_frames", "expr:gte(t,n_forced*10)",
|
||||
//"-strict", "experimental",
|
||||
//"-movflags", "+frag_keyframe+separate_moof+omit_tfhd_offset+empty_moov",
|
||||
//"-c:v", "libx264",
|
||||
//"-crf", "18",
|
||||
//
|
||||
//"-f", "segment",
|
||||
//"-segment_time_delta", "0.2",
|
||||
//"-segment_format", "mpegts",
|
||||
//"-segment_times", commaSeparatedTimes,
|
||||
//"-segment_start_number", `0`,
|
||||
//"-segment_list_type", "flat",
|
||||
//"-segment_list",
|
||||
"-preset", "veryfast",
|
||||
//"-maxrate", "4000k",
|
||||
//"-bufsize", "8000k",
|
||||
//"-vf", "scale=1280:-1,format=yuv420p",
|
||||
//"-c:a", "aac",
|
||||
"-force_key_frames", "expr:gte(t,n_forced*10)",
|
||||
"-strict", "-2",
|
||||
"-c:a", "aac",
|
||||
"-c:v", "libx264",
|
||||
"-f", "segment",
|
||||
"-segment_list_type", "m3u8",
|
||||
"-segment_time", "10.0",
|
||||
"-segment_time_delta", "0.001",
|
||||
"-segment_list", "test.m3u8", outpath+"/part%02d.ts")
|
||||
|
||||
stdout, err := cmd.Output()
|
||||
//
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
fmt.Println(string(err.(*exec.ExitError).Stderr))
|
||||
}
|
||||
fmt.Println(stdout)
|
||||
fmt.Println("finished transcoding")
|
||||
|
||||
transcodeAcive[videoid] = TranscodingState{
|
||||
Finished: true,
|
||||
Active: false,
|
||||
}
|
||||
}
|
||||
|
||||
func fileExists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
|
||||
func GetVideoPathById(videoid uint32) string {
|
||||
query := fmt.Sprintf(`SELECT movie_url FROM videos WHERE movie_id=%d`, videoid)
|
||||
var url string
|
||||
|
||||
err := database.QueryRow(query).Scan(&url)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
mSettings, _, _ := database.GetSettings()
|
||||
vidFolder := config.GetConfig().General.ReindexPrefix + mSettings.VideoPath
|
||||
return vidFolder + url
|
||||
}
|
||||
|
||||
func GetSegment(segIdx uint32, videoid uint32) string {
|
||||
cfg := config.GetConfig()
|
||||
|
||||
i, ok := transcodeAcive[videoid]
|
||||
if ok {
|
||||
if !i.Active && !i.Finished {
|
||||
go startSegmentation(videoid)
|
||||
}
|
||||
} else {
|
||||
go startSegmentation(videoid)
|
||||
}
|
||||
|
||||
// todo timeout
|
||||
tspath := fmt.Sprintf("%s/%d/part%02d.ts", cfg.General.TmpDir, videoid, segIdx)
|
||||
if ok && i.Finished == true {
|
||||
return tspath
|
||||
}
|
||||
|
||||
fmt.Println("checking if part exists")
|
||||
for !fileExists(fmt.Sprintf("%s/%d/part%02d.ts", cfg.General.TmpDir, videoid, segIdx+1)) {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
return tspath
|
||||
}
|
@ -13,7 +13,6 @@
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.1",
|
||||
"@fortawesome/react-fontawesome": "^0.1.13",
|
||||
"bootstrap": "^5.0.2",
|
||||
"hls.js": "^1.2.9",
|
||||
"plyr-react": "^3.0.7",
|
||||
"react": "^17.0.1",
|
||||
"react-bootstrap": "^1.4.0",
|
||||
@ -23,7 +22,7 @@
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts --openssl-legacy-provider start",
|
||||
"start": "react-scripts start",
|
||||
"build": "CI=false react-scripts build",
|
||||
"test": "CI=true react-scripts test --reporters=jest-junit --verbose --silent --coverage --reporters=default",
|
||||
"lint": "eslint --format gitlab src/",
|
||||
|
@ -1,34 +0,0 @@
|
||||
import React, {useEffect, useRef} from 'react';
|
||||
import Hls from 'hls.js';
|
||||
import {PlyrInstance, PlyrProps, Plyr} from 'plyr-react';
|
||||
import plyrstyle from 'plyr-react/dist/plyr.css';
|
||||
import {DefaultPlyrOptions} from '../../types/GeneralTypes';
|
||||
|
||||
interface Props {
|
||||
// children?: JSX.Element;
|
||||
videoid: number;
|
||||
}
|
||||
|
||||
const HLSPlayer = (props: Props): JSX.Element => {
|
||||
const ref = useRef(null);
|
||||
useEffect(() => {
|
||||
const loadVideo = async (): Promise<void> => {
|
||||
const video = document.getElementById('plyr') as HTMLVideoElement;
|
||||
const hls = new Hls();
|
||||
hls.loadSource('/api/video/loadM3U8?id=' + props.videoid);
|
||||
hls.attachMedia(video);
|
||||
// @ts-ignore
|
||||
ref.current!.plyr.media = video;
|
||||
|
||||
hls.on(Hls.Events.MANIFEST_PARSED, function () {
|
||||
// @ts-ignore
|
||||
(ref.current!.plyr as PlyrInstance).play();
|
||||
});
|
||||
};
|
||||
loadVideo();
|
||||
});
|
||||
|
||||
return <Plyr style={plyrstyle} options={DefaultPlyrOptions} id='plyr' source={{} as PlyrProps['source']} ref={ref} />;
|
||||
};
|
||||
|
||||
export default HLSPlayer;
|
@ -1,7 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
import style from './Player.module.css';
|
||||
import plyrstyle from 'plyr-react/dist/plyr.css';
|
||||
|
||||
import {Plyr} from 'plyr-react';
|
||||
import SideBar, {SideBarItem, SideBarTitle} from '../../elements/SideBar/SideBar';
|
||||
import Tag from '../../elements/Tag/Tag';
|
||||
import AddTagPopup from '../../elements/Popups/AddTagPopup/AddTagPopup';
|
||||
@ -13,7 +15,7 @@ import ActorTile from '../../elements/ActorTile/ActorTile';
|
||||
import {withRouter} from 'react-router-dom';
|
||||
import {APINode, callAPI} from '../../utils/Api';
|
||||
import {RouteComponentProps} from 'react-router';
|
||||
import {GeneralSuccess} from '../../types/GeneralTypes';
|
||||
import {DefaultPlyrOptions, GeneralSuccess} from '../../types/GeneralTypes';
|
||||
import {ActorType, TagType} from '../../types/VideoTypes';
|
||||
import PlyrJS from 'plyr';
|
||||
import {IconButton} from '../../elements/GPElements/Button';
|
||||
@ -21,7 +23,6 @@ import {VideoTypes} from '../../types/ApiTypes';
|
||||
import GlobalInfos from '../../utils/GlobalInfos';
|
||||
import {ButtonPopup} from '../../elements/Popups/ButtonPopup/ButtonPopup';
|
||||
import {FeatureContext} from '../../utils/context/FeatureContext';
|
||||
import HLSPlayer from './HLSPlayer';
|
||||
|
||||
interface Props extends RouteComponentProps<{id: string}> {}
|
||||
|
||||
@ -83,8 +84,11 @@ export class Player extends React.Component<Props, mystate> {
|
||||
|
||||
<div className={style.videowrapper}>
|
||||
{/* video component is added here */}
|
||||
{/*<Plyr style={plyrstyle} source={this.state.sources} options={DefaultPlyrOptions} />*/}
|
||||
{this.state.sources ? <HLSPlayer videoid={this.state.movieId} /> : <div>not loaded yet</div>}
|
||||
{this.state.sources ? (
|
||||
<Plyr style={plyrstyle} source={this.state.sources} options={DefaultPlyrOptions} />
|
||||
) : (
|
||||
<div>not loaded yet</div>
|
||||
)}
|
||||
<div className={style.videoactions}>
|
||||
<IconButton icon={faThumbsUp} onClick={(): void => this.likebtn()} title='Like!' />
|
||||
<IconButton icon={faTag} onClick={(): void => this.setState({popupvisible: true})} title='Add Tag!' />
|
||||
|
@ -9,8 +9,9 @@ import {TagType} from '../../types/VideoTypes';
|
||||
import {VideoTypes} from '../../types/ApiTypes';
|
||||
import {addKeyHandler, removeKeyHandler} from '../../utils/ShortkeyHandler';
|
||||
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 GlobalInfos from '../../utils/GlobalInfos';
|
||||
|
||||
interface state {
|
||||
videos: VideoTypes.VideoUnloadedType[];
|
||||
@ -28,8 +29,6 @@ interface GetRandomMoviesType {
|
||||
* Randompage shuffles random viedeopreviews and provides a shuffle btn
|
||||
*/
|
||||
class RandomPage extends React.Component<{}, state> {
|
||||
readonly LoadNR = 3;
|
||||
|
||||
constructor(props: {}) {
|
||||
super(props);
|
||||
|
||||
@ -46,7 +45,7 @@ class RandomPage extends React.Component<{}, state> {
|
||||
componentDidMount(): void {
|
||||
addKeyHandler(this.keypress);
|
||||
|
||||
this.loadShuffledvideos(this.LoadNR);
|
||||
this.loadShuffledvideos();
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
@ -56,7 +55,7 @@ class RandomPage extends React.Component<{}, state> {
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<PageTitle title='Random Videos' subtitle='3pc' />
|
||||
<PageTitle title='Random Videos' subtitle={GlobalInfos.getRandomNR() + 'pc'} />
|
||||
|
||||
<SideBar>
|
||||
<SideBarTitle>Visible Tags:</SideBarTitle>
|
||||
@ -75,24 +74,11 @@ class RandomPage extends React.Component<{}, state> {
|
||||
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>
|
||||
|
||||
{this.state.videos.length !== 0 ? <VideoContainer data={this.state.videos} /> : <div>No Data found!</div>}
|
||||
<div className={style.Shufflebutton}>
|
||||
<button onClick={(): void => this.loadShuffledvideos(this.LoadNR)} className={style.btnshuffle}>
|
||||
<button onClick={(): void => this.loadShuffledvideos()} className={style.btnshuffle}>
|
||||
Shuffle
|
||||
</button>
|
||||
</div>
|
||||
@ -101,7 +87,7 @@ class RandomPage extends React.Component<{}, state> {
|
||||
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);
|
||||
this.loadShuffledvideos();
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@ -112,12 +98,15 @@ class RandomPage extends React.Component<{}, state> {
|
||||
|
||||
/**
|
||||
* load random videos from backend
|
||||
* @param nr number of videos to load
|
||||
*/
|
||||
loadShuffledvideos(nr: number): void {
|
||||
loadShuffledvideos(): void {
|
||||
callAPI<GetRandomMoviesType>(
|
||||
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) => {
|
||||
this.setState({videos: []}); // needed to trigger rerender of main videoview
|
||||
this.setState({
|
||||
@ -135,7 +124,7 @@ class RandomPage extends React.Component<{}, state> {
|
||||
private keypress(event: KeyboardEvent): void {
|
||||
// bind s to shuffle
|
||||
if (event.key === 's') {
|
||||
this.loadShuffledvideos(3);
|
||||
this.loadShuffledvideos();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,8 @@ class GeneralSettings extends React.Component<Props, state> {
|
||||
Password: '',
|
||||
PasswordEnabled: false,
|
||||
TMDBGrabbing: false,
|
||||
VideoPath: ''
|
||||
VideoPath: '',
|
||||
RandomNR: 3
|
||||
},
|
||||
sizes: {
|
||||
DBSize: 0,
|
||||
@ -199,6 +200,23 @@ class GeneralSettings extends React.Component<Props, state> {
|
||||
/>
|
||||
</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'>
|
||||
Submit
|
||||
</Button>
|
||||
|
@ -66,7 +66,7 @@ export class EpisodePage extends React.Component<Props, State> {
|
||||
export const EpisodeTile = (props: {episode: Episode}): JSX.Element => {
|
||||
const themestyle = GlobalInfos.getThemeStyle();
|
||||
return (
|
||||
<Link to={'/media/tvplayer/' + props.episode.ID}>
|
||||
<Link to={'/tvplayer/' + props.episode.ID}>
|
||||
<div className={tileStyle.tile + ' ' + themestyle.secbackground + ' ' + themestyle.textcolor}>
|
||||
<FontAwesomeIcon
|
||||
style={{
|
||||
|
@ -55,7 +55,7 @@ export class TVShowPage extends React.Component<Props, State> {
|
||||
(result) => callback(result)
|
||||
);
|
||||
}}
|
||||
linkPath={'/media/tvshows/' + elem.Id}
|
||||
linkPath={'/tvshows/' + elem.Id}
|
||||
/>
|
||||
)}
|
||||
data={this.state.loading ? [] : this.data}
|
||||
|
@ -40,6 +40,7 @@ export namespace SettingsTypes {
|
||||
TVShowPath: string;
|
||||
TVShowEnabled: boolean;
|
||||
FullDeleteEnabled: boolean;
|
||||
RandomNR: number;
|
||||
}
|
||||
|
||||
export interface SettingsType {
|
||||
@ -50,6 +51,7 @@ export namespace SettingsTypes {
|
||||
PasswordEnabled: boolean;
|
||||
TMDBGrabbing: boolean;
|
||||
DarkMode: boolean;
|
||||
RandomNR: number;
|
||||
}
|
||||
|
||||
export interface SizesType {
|
||||
|
@ -11,6 +11,7 @@ class StaticInfos {
|
||||
private tvshowpath: string = '';
|
||||
private TVShowsEnabled: boolean = false;
|
||||
private fullDeleteable: boolean = false;
|
||||
private RandomNR: number = 3;
|
||||
|
||||
/**
|
||||
* check if the current theme is the dark theme
|
||||
@ -44,6 +45,7 @@ class StaticInfos {
|
||||
}
|
||||
|
||||
handlers: (() => void)[] = [];
|
||||
|
||||
onThemeChange(func: () => void): void {
|
||||
this.handlers.push(func);
|
||||
}
|
||||
@ -71,6 +73,14 @@ class StaticInfos {
|
||||
getTVShowPath(): string {
|
||||
return this.tvshowpath;
|
||||
}
|
||||
|
||||
setRandomNR(nr: number): void {
|
||||
this.RandomNR = nr;
|
||||
}
|
||||
|
||||
getRandomNR(): number {
|
||||
return this.RandomNR;
|
||||
}
|
||||
}
|
||||
|
||||
export default new StaticInfos();
|
||||
|
@ -32,6 +32,7 @@ export const LoginContextProvider: FunctionComponent = (props): JSX.Element => {
|
||||
GlobalInfos.enableDarkTheme(result.DarkMode);
|
||||
|
||||
GlobalInfos.setVideoPaths(result.VideoPath, result.TVShowPath);
|
||||
GlobalInfos.setRandomNR(result.RandomNR);
|
||||
|
||||
features.setTVShowEnabled(result.TVShowEnabled);
|
||||
features.setVideosFullyDeleteable(result.FullDeleteEnabled);
|
||||
|
@ -6191,11 +6191,6 @@ history@^4.9.0:
|
||||
tiny-warning "^1.0.0"
|
||||
value-equal "^1.0.1"
|
||||
|
||||
hls.js@^1.2.9:
|
||||
version "1.2.9"
|
||||
resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-1.2.9.tgz#2f25e42ec4c2ea8c88ab23c0f854f39062d45ac9"
|
||||
integrity sha512-SPjm8ix0xe6cYzwDvdVGh2QvQPDkCYrGWpZu6bRaKNNVyEGWM9uF0pooh/Lqj/g8QBQgPFEx1vHzW8SyMY9rqg==
|
||||
|
||||
hmac-drbg@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
|
||||
|
Reference in New Issue
Block a user