Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Skip initial silence #1262

Merged
merged 12 commits into from
Jan 19, 2024
6 changes: 4 additions & 2 deletions api/live_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 32 additions & 0 deletions api/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion api/worker_grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
4 changes: 2 additions & 2 deletions cmd/modelGen/modelGen.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion docs/static/tum-live-starter.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down
15 changes: 15 additions & 0 deletions model/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
21 changes: 21 additions & 0 deletions model/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const (
CustomPlaybackSpeeds
SeekingTime
UserDefinedSpeeds
AutoSkip
)

type UserSetting struct {
Expand Down Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions tools/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
15 changes: 15 additions & 0 deletions web/template/user-settings.gohtml
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,21 @@
<label for="seek-time-20">20s</label>
</div>
</section>
<section x-data="{ autoSkip: {{toJson .TUMLiveContext.User.GetAutoSkipEnabled}} }">
<h2>Automatically Skip First Silence</h2>
<span class="mr-2">
<label class="relative inline-flex items-center cursor-pointer">
<input :checked="autoSkip.enabled" type="checkbox" x-model="autoSkip.enabled" class="sr-only peer"
@change="global.updatePreference(global.UserSetting.AutoSkip, autoSkip)"/>
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-600
dark:peer-focus:ring-indigo-600 rounded-full peer dark:bg-gray-600 peer-checked:after:translate-x-full
peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px]
after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5
after:transition-all dark:border-gray-600 peer-checked:bg-blue-600 dark:peer-checked:bg-indigo-600"></div>
<span class="ml-3 text-sm font-medium text-3">Skip</span>
</label>
</span>
</section>
<section>
<h2>Privacy & Data Protection</h2>
<a href="/api/users/exportData" download="personal_data.json"
Expand Down
1 change: 1 addition & 0 deletions web/ts/user-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export enum UserSetting {
PlaybackSpeeds = "playbackSpeeds",
SeekingTime = "seekingTime",
CustomSpeeds = "customSpeeds",
AutoSkip = "autoSkip",
}

export function updatePreference(t: UserSetting, value: string | boolean | number[]): Promise<string> {
Expand Down
12 changes: 12 additions & 0 deletions web/watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package web
import (
"errors"
"html/template"
"math"
"net/http"
"strconv"
"strings"
Expand Down Expand Up @@ -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])
Expand Down
4 changes: 2 additions & 2 deletions worker/cfg/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion worker/edge/edge.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion worker/worker/persist.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion worker/worker/premiere.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion worker/worker/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion worker/worker/thumbnails.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
4 changes: 2 additions & 2 deletions worker/worker/transcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion worker/worker/upload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Loading