diff --git a/api/live_update.go b/api/live_update.go
index 7b7c756cf..ba93f0c4f 100644
--- a/api/live_update.go
+++ b/api/live_update.go
@@ -20,8 +20,10 @@ const (
UpdateTypeCourseWentLive = "course_went_live"
)
-var liveUpdateListenerMutex sync.RWMutex
-var liveUpdateListener = map[uint]*liveUpdateUserSessionsWrapper{}
+var (
+ liveUpdateListenerMutex sync.RWMutex
+ liveUpdateListener = map[uint]*liveUpdateUserSessionsWrapper{}
+)
type liveUpdateUserSessionsWrapper struct {
sessions []*realtime.Context
diff --git a/api/users.go b/api/users.go
index 2afc0e454..175a70eff 100644
--- a/api/users.go
+++ b/api/users.go
@@ -27,6 +27,7 @@ func configGinUsersRouter(router *gin.Engine, daoWrapper dao.DaoWrapper) {
router.POST("/api/users/settings/playbackSpeeds", routes.updatePlaybackSpeeds)
router.POST("/api/users/settings/seekingTime", routes.updateSeekingTime)
router.POST("/api/users/settings/customSpeeds", routes.updateCustomSpeeds)
+ router.POST("/api/users/settings/autoSkip", routes.updateAutoSkip)
router.POST("/api/users/resetPassword", routes.resetPassword)
@@ -723,6 +724,37 @@ func (r usersRoutes) updateSeekingTime(c *gin.Context) {
}
}
+func (r usersRoutes) updateAutoSkip(c *gin.Context) {
+ u := c.MustGet("TUMLiveContext").(tools.TUMLiveContext).User
+ if u == nil {
+ _ = c.Error(tools.RequestError{
+ Status: http.StatusUnauthorized,
+ CustomMessage: "login required",
+ })
+ return
+ }
+ var req struct{ Value model.AutoSkipSetting }
+ if err := c.BindJSON(&req); err != nil {
+ _ = c.Error(tools.RequestError{
+ Status: http.StatusBadRequest,
+ CustomMessage: "can not bind body to request",
+ Err: err,
+ })
+ return
+ }
+
+ settingBytes, _ := json.Marshal(req.Value)
+ err := r.DaoWrapper.UsersDao.AddUserSetting(&model.UserSetting{UserID: u.ID, Type: model.AutoSkip, Value: string(settingBytes)})
+ if err != nil {
+ _ = c.Error(tools.RequestError{
+ Status: http.StatusInternalServerError,
+ CustomMessage: "can not add user setting",
+ Err: err,
+ })
+ return
+ }
+}
+
func (r usersRoutes) exportPersonalData(c *gin.Context) {
var resp personalData
u := c.MustGet("TUMLiveContext").(tools.TUMLiveContext).User
diff --git a/api/worker_grpc.go b/api/worker_grpc.go
index 13c909055..4db638931 100644
--- a/api/worker_grpc.go
+++ b/api/worker_grpc.go
@@ -882,7 +882,7 @@ func getLivePreviewFromWorker(s *model.Stream, workerID string, client pb.ToWork
return err
}
- if err := os.MkdirAll(pathprovider.TUMLiveTemporary, 0750); err != nil {
+ if err := os.MkdirAll(pathprovider.TUMLiveTemporary, 0o750); err != nil {
return err
}
diff --git a/cmd/modelGen/modelGen.go b/cmd/modelGen/modelGen.go
index a3dd1f9b2..c537a1594 100644
--- a/cmd/modelGen/modelGen.go
+++ b/cmd/modelGen/modelGen.go
@@ -28,7 +28,7 @@ func main() {
}
fmt.Println("Generating model...")
- model_file, err := os.OpenFile(fmt.Sprintf("model/%s.go", d.NamePrivate), os.O_WRONLY|os.O_CREATE, 0644)
+ model_file, err := os.OpenFile(fmt.Sprintf("model/%s.go", d.NamePrivate), os.O_WRONLY|os.O_CREATE, 0o644)
if err != nil {
fmt.Println(err)
os.Exit(1)
@@ -46,7 +46,7 @@ func main() {
fmt.Println("Generating dao...")
- dao_file, err := os.OpenFile(fmt.Sprintf("dao/%s.go", d.NamePrivate), os.O_WRONLY|os.O_CREATE, 0644)
+ dao_file, err := os.OpenFile(fmt.Sprintf("dao/%s.go", d.NamePrivate), os.O_WRONLY|os.O_CREATE, 0o644)
if err != nil {
fmt.Println(err)
os.Exit(1)
diff --git a/docs/static/tum-live-starter.sql b/docs/static/tum-live-starter.sql
index fcd1ae2ad..41044ecab 100644
--- a/docs/static/tum-live-starter.sql
+++ b/docs/static/tum-live-starter.sql
@@ -352,6 +352,7 @@ CREATE TABLE `lecture_halls` (
LOCK TABLES `lecture_halls` WRITE;
/*!40000 ALTER TABLE `lecture_halls` DISABLE KEYS */;
+INSERT INTO `lecture_halls` VALUES (1,NULL,NULL,NULL,'HS001','Hörsaal 001',NULL,NULL,NULL,NULL,NULL,NULL,NULL);
/*!40000 ALTER TABLE `lecture_halls` ENABLE KEYS */;
UNLOCK TABLES;
@@ -606,6 +607,7 @@ CREATE TABLE `silences` (
LOCK TABLES `silences` WRITE;
/*!40000 ALTER TABLE `silences` DISABLE KEYS */;
+INSERT INTO `silences` VALUES (1,'2024-01-14 21:16:37.000','2024-01-14 21:16:43.000',NULL,0,100,1),(2,'2024-01-14 21:17:00.000','2024-01-14 21:17:02.000',NULL,0,200,2);
/*!40000 ALTER TABLE `silences` ENABLE KEYS */;
UNLOCK TABLES;
@@ -813,7 +815,7 @@ CREATE TABLE `streams` (
LOCK TABLES `streams` WRITE;
/*!40000 ALTER TABLE `streams` DISABLE KEYS */;
-INSERT INTO `streams` VALUES (1,'2022-04-18 13:45:58.657','2022-04-18 13:46:46.547',NULL,'VL 1: Was ist Bier?','',1,'2022-04-11 12:00:00.000','2022-04-11 13:00:00.000','','','',0,NULL,'c33dfc976efb410299e604b255db0127','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','',0,1,NULL,NULL,0,NULL,NULL,NULL,0,'',NULL),(2,'2022-04-18 13:46:25.841','2022-04-18 13:46:46.547',NULL,'VL 2: Wie mache ich Bier?','',1,'2022-04-18 12:00:00.000','2022-04-18 13:00:00.000','','','',0,NULL,'5815366e4010482687912588349bc5c0','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','',0,1,NULL,NULL,0,NULL,NULL,NULL,0,'',NULL),(4,'2022-04-18 13:46:46.547','2022-04-18 13:46:46.547',NULL,'VL 3: Rückblick','',1,'2026-02-19 12:00:00.000','2026-02-19 13:00:00.000','','','',0,NULL,'d8ce0b882dbc4d999b42c143ce07db5a','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','',0,0,NULL,NULL,0,NULL,NULL,NULL,0,'',NULL),(7,'2022-04-18 13:46:46.547','2022-04-18 13:46:46.547',NULL,'VL 1: Livestream','',2,'2022-02-19 12:00:00.000','2022-02-19 13:00:00.000','','','',0,NULL,'','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','',1,0,NULL,NULL,0,NULL,NULL,NULL,0,'',NULL),(8,'2022-04-18 13:46:46.547','2022-04-18 13:46:46.547',NULL,'VL 1: Intro to Go','',3,'2022-02-19 12:00:00.000','2022-02-19 12:00:00.000','','','',0,NULL,'d8ce0b882dbc4d999b42c143ce07db5a','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','',0,1,NULL,NULL,0,NULL,NULL,NULL,0,'',NULL);
+INSERT INTO `streams` VALUES (1,'2022-04-18 13:45:58.657','2022-04-18 13:46:46.547',NULL,'VL 1: Was ist Bier?','',1,'2022-04-11 12:00:00.000','2022-04-11 12:09:56.000','','','',0,NULL,'c33dfc976efb410299e604b255db0127','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','',0,1,NULL,NULL,0,NULL,NULL,1,0,'',NULL),(2,'2022-04-18 13:46:25.841','2022-04-18 13:46:46.547',NULL,'VL 2: Wie mache ich Bier?','',1,'2022-04-18 12:00:00.000','2022-04-18 12:09:56.000','','','',0,NULL,'5815366e4010482687912588349bc5c0','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','',0,1,NULL,NULL,0,NULL,NULL,1,0,'',NULL),(4,'2022-04-18 13:46:46.547','2022-04-18 13:46:46.547',NULL,'VL 3: Rückblick','',1,'2026-02-19 12:00:00.000','2026-02-19 13:00:00.000','','','',0,NULL,'d8ce0b882dbc4d999b42c143ce07db5a','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','',0,0,NULL,NULL,0,NULL,NULL,1,0,'',NULL),(7,'2022-04-18 13:46:46.547','2022-04-18 13:46:46.547',NULL,'VL 1: Livestream','',2,'2022-02-19 12:00:00.000','2022-02-19 13:00:00.000','','','',0,NULL,'','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','',1,0,NULL,NULL,0,NULL,NULL,1,0,'',NULL),(8,'2022-04-18 13:46:46.547','2022-04-18 13:46:46.547',NULL,'VL 1: Intro to Go','',3,'2022-02-19 12:00:00.000','2022-02-19 12:00:00.000','','','',0,NULL,'d8ce0b882dbc4d999b42c143ce07db5a','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','https://stream.lrz.de/vod/_definst_/mp4:tum/RBG/bb.mp4/playlist.m3u8','',0,1,NULL,NULL,0,NULL,NULL,1,0,'',NULL);
/*!40000 ALTER TABLE `streams` ENABLE KEYS */;
UNLOCK TABLES;
diff --git a/model/stream.go b/model/stream.go
index ba717b9ca..fc3572c71 100755
--- a/model/stream.go
+++ b/model/stream.go
@@ -410,3 +410,18 @@ func (s Stream) ToDTO() StreamDTO {
Duration: duration,
}
}
+
+// FirstSilenceAsProgress returns the end of the first silence as a quotient of the length of the stream
+func (s Stream) FirstSilenceAsProgress() float64 {
+ if len(s.Silences) == 0 {
+ return 0
+ }
+ // Sanity check: first silence at beginning of stream
+ if s.Silences[0].Start != 0 {
+ return 0
+ }
+ duration := s.End.Sub(s.Start).Seconds()
+ p := float64(s.Silences[0].End) / duration
+
+ return p
+}
diff --git a/model/user.go b/model/user.go
index 999adc4ab..5b30425b8 100755
--- a/model/user.go
+++ b/model/user.go
@@ -58,6 +58,7 @@ const (
CustomPlaybackSpeeds
SeekingTime
UserDefinedSpeeds
+ AutoSkip
)
type UserSetting struct {
@@ -197,6 +198,26 @@ func (u User) PreferredNameChangeAllowed() bool {
return true
}
+// AutoSkipSetting wraps whether auto skip is enabled in JSON
+type AutoSkipSetting struct {
+ Enabled bool `json:"enabled"`
+}
+
+// GetAutoSkipEnabled returns whether the user has enabled auto skip
+func (u User) GetAutoSkipEnabled() (AutoSkipSetting, error) {
+ for _, setting := range u.Settings {
+ if setting.Type == AutoSkip {
+ var a AutoSkipSetting
+ err := json.Unmarshal([]byte(setting.Value), &a)
+ if err != nil {
+ return AutoSkipSetting{Enabled: false}, err
+ }
+ return a, nil
+ }
+ }
+ return AutoSkipSetting{Enabled: false}, nil
+}
+
type argonParams struct {
memory uint32
iterations uint32
diff --git a/tools/config.go b/tools/config.go
index 8d2190007..f4c49aad3 100644
--- a/tools/config.go
+++ b/tools/config.go
@@ -15,8 +15,10 @@ import (
"github.com/spf13/viper"
)
-var Cfg Config
-var Loc *time.Location
+var (
+ Cfg Config
+ Loc *time.Location
+)
func LoadConfig() {
initCache()
diff --git a/web/template/user-settings.gohtml b/web/template/user-settings.gohtml
index b24214b21..b3857a061 100644
--- a/web/template/user-settings.gohtml
+++ b/web/template/user-settings.gohtml
@@ -139,6 +139,21 @@
+
Privacy & Data Protection
{
diff --git a/web/watch.go b/web/watch.go
index b966e7c12..f866d0f10 100644
--- a/web/watch.go
+++ b/web/watch.go
@@ -3,6 +3,7 @@ package web
import (
"errors"
"html/template"
+ "math"
"net/http"
"strconv"
"strings"
@@ -93,6 +94,17 @@ func (r mainRoutes) WatchPage(c *gin.Context) {
} else if len(progress) > 0 {
data.Progress = progress[0]
}
+
+ // Check if user wants to skip first silence
+ autoSkip, err := tumLiveContext.User.GetAutoSkipEnabled()
+ if err != nil {
+ logger.Error("Couldn't decode user setting", "err", err)
+ } else if autoSkip.Enabled {
+ // The length of the stream may mismatch with the length of the video if it is a self-stream
+ if tumLiveContext.Stream.LectureHallID != 0 {
+ data.Progress.Progress = math.Max(data.Progress.Progress, tumLiveContext.Stream.FirstSilenceAsProgress())
+ }
+ }
}
if c.Query("restart") == "1" {
c.Redirect(http.StatusFound, strings.Split(c.Request.RequestURI, "?")[0])
diff --git a/worker/cfg/cfg.go b/worker/cfg/cfg.go
index baf6f1b24..051f3d93d 100644
--- a/worker/cfg/cfg.go
+++ b/worker/cfg/cfg.go
@@ -77,11 +77,11 @@ func SetConfig() {
if PersistDir == "" {
PersistDir = "."
}
- err := os.MkdirAll(PersistDir, 0755)
+ err := os.MkdirAll(PersistDir, 0o755)
if err != nil {
log.Error(err)
}
- err = os.MkdirAll(LogDir, 0755)
+ err = os.MkdirAll(LogDir, 0o755)
if err != nil {
log.Warn("Could not create log directory: ", err)
}
diff --git a/worker/edge/edge.go b/worker/edge/edge.go
index 867ec1160..a29682d2d 100644
--- a/worker/edge/edge.go
+++ b/worker/edge/edge.go
@@ -374,7 +374,7 @@ func fetchFile(host, file string) error {
return fmt.Errorf("parse file path: %s", file)
}
d := filepath.Dir(diskDir)
- err = os.MkdirAll(d, 0755)
+ err = os.MkdirAll(d, 0o755)
if err != nil {
return err
}
diff --git a/worker/worker/persist.go b/worker/worker/persist.go
index 8da624d71..8eb45f733 100644
--- a/worker/worker/persist.go
+++ b/worker/worker/persist.go
@@ -26,7 +26,7 @@ const persistFileName = "/persist.gob"
// writeOut writes out the persistable object to disk
func (p *Persistable) writeOut() error {
- f, err := os.OpenFile(cfg.PersistDir+persistFileName, os.O_RDWR|os.O_CREATE, 0666)
+ f, err := os.OpenFile(cfg.PersistDir+persistFileName, os.O_RDWR|os.O_CREATE, 0o666)
if err != nil {
return err
}
diff --git a/worker/worker/premiere.go b/worker/worker/premiere.go
index 856230c31..56d3a7130 100644
--- a/worker/worker/premiere.go
+++ b/worker/worker/premiere.go
@@ -20,7 +20,7 @@ func streamPremiere(ctx *StreamContext) {
"-acodec", "aac", "-b:a", "128k", "-ac", "2", "-ar", "48000", "-af", "aresample=async=1:min_hard_comp=0.100000:first_pts=0",
"-f", "flv", fmt.Sprintf("%s%s", ctx.ingestServer, ctx.streamName))
log.WithField("cmd", cmd.String()).Info("Starting premiere")
- ffmpegErr, errFfmpegErrFile := os.OpenFile(fmt.Sprintf("%s/ffmpeg_%s.log", cfg.LogDir, ctx.getStreamName()), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
+ ffmpegErr, errFfmpegErrFile := os.OpenFile(fmt.Sprintf("%s/ffmpeg_%s.log", cfg.LogDir, ctx.getStreamName()), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o644)
if errFfmpegErrFile == nil {
cmd.Stderr = ffmpegErr
} else {
diff --git a/worker/worker/stream.go b/worker/worker/stream.go
index 8c73c2294..ebe062909 100644
--- a/worker/worker/stream.go
+++ b/worker/worker/stream.go
@@ -45,7 +45,7 @@ func stream(streamCtx *StreamContext) {
// persist stream command in context, so it can be killed later
streamCtx.streamCmd = cmd
log.WithField("cmd", cmd.String()).Info("Starting stream")
- ffmpegErr, errFfmpegErrFile := os.OpenFile(fmt.Sprintf("%s/ffmpeg_%s.log", cfg.LogDir, streamCtx.getStreamName()), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
+ ffmpegErr, errFfmpegErrFile := os.OpenFile(fmt.Sprintf("%s/ffmpeg_%s.log", cfg.LogDir, streamCtx.getStreamName()), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o644)
if errFfmpegErrFile == nil {
cmd.Stderr = ffmpegErr
} else {
diff --git a/worker/worker/thumbnails.go b/worker/worker/thumbnails.go
index 9f212d97b..f621028c7 100644
--- a/worker/worker/thumbnails.go
+++ b/worker/worker/thumbnails.go
@@ -50,7 +50,7 @@ func createVideoThumbnail(ctx *StreamContext, source string) error {
if err != nil {
return err
}
- file, err := os.OpenFile(ctx.getLargeThumbnailSpriteFileName(), os.O_CREATE|os.O_WRONLY, 0644)
+ file, err := os.OpenFile(ctx.getLargeThumbnailSpriteFileName(), os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return err
}
diff --git a/worker/worker/transcode.go b/worker/worker/transcode.go
index ed221dcb0..a58c0a94b 100644
--- a/worker/worker/transcode.go
+++ b/worker/worker/transcode.go
@@ -149,7 +149,7 @@ func handleTranscodingOutput(stderr io.ReadCloser, inputTime float64, progressCh
// creates folder for output file if it doesn't exist
func prepare(out string) error {
dir := filepath.Dir(out)
- err := os.MkdirAll(dir, 0750)
+ err := os.MkdirAll(dir, 0o750)
if err != nil {
return fmt.Errorf("create output directory for transcoding: %s", err)
}
@@ -159,7 +159,7 @@ func prepare(out string) error {
// markForDeletion moves the file to $recfolder/.trash/
func markForDeletion(ctx *StreamContext) error {
trashName := ctx.getRecordingTrashName()
- err := os.MkdirAll(filepath.Dir(trashName), 0750)
+ err := os.MkdirAll(filepath.Dir(trashName), 0o750)
if err != nil {
return fmt.Errorf("create trash directory: %s", err)
}
diff --git a/worker/worker/upload_test.go b/worker/worker/upload_test.go
index 763eb5970..416cebb88 100644
--- a/worker/worker/upload_test.go
+++ b/worker/worker/upload_test.go
@@ -72,7 +72,7 @@ func createDummyFile(filesize uint) (string, error) {
if err != nil {
return "", err
}
- f, err := os.OpenFile(file.Name(), os.O_APPEND|os.O_WRONLY, 0600)
+ f, err := os.OpenFile(file.Name(), os.O_APPEND|os.O_WRONLY, 0o600)
if err != nil {
return "", err
}