8d57b21ac2e7a1a8b71c85df171753c773da1643

Author
TheEdgeOfRage <git@theedgeofrage.com>
Committer
TheEdgeOfRage <git@theedgeofrage.com>
Date

Message

Recheck live videos during fetch and update status when stream ends

Diff

This diff is truncated to protect this page.

  1diff --git a/db/db.go b/db/db.go
  2index 05f27bb5e79cb87596b380ff165254fea23a52cb..27ae92b78cfac55e853fffd348fc788c44b90325 100644
  3--- a/db/db.go
  4+++ b/db/db.go
  5@@ -52,6 +52,10 @@ type DB interface {
  6 	SetVideoDownloadFailed(ctx context.Context, videoID string, errorMsg string) error
  7 	// GetVideosForCleanup returns videos that were downloaded and watched older than the given duration
  8 	GetVideosForCleanup(ctx context.Context, olderThan time.Duration) ([]models.Video, error)
  9+	// GetLiveVideos returns unwatched videos that are currently live
 10+	GetLiveVideos(ctx context.Context) ([]models.Video, error)
 11+	// UpdateVideoLiveStatus updates the live status and duration of a video
 12+	UpdateVideoLiveStatus(ctx context.Context, videoID string, isLive bool, duration int) error
 13 	// DeleteVideoFile clears the download fields for a video
 14 	DeleteVideoFile(ctx context.Context, videoID string) error
 15 
 16diff --git a/db/videos.go b/db/videos.go
 17index 04a3c733966ef274402def8ad22ea62898e82bd4..fbf1015d63ea3f57984cd2da270bcd3e424af49d 100644
 18--- a/db/videos.go
 19+++ b/db/videos.go
 20@@ -382,6 +382,40 @@ func (db *postgresDB) GetVideosForCleanup(ctx context.Context, olderThan time.Du
 21 	return videos, nil
 22 }
 23 
 24+func (db *postgresDB) GetLiveVideos(ctx context.Context) ([]models.Video, error) {
 25+	query := `SELECT id FROM videos WHERE is_live = true AND watch_timestamp IS NULL`
 26+
 27+	rows, err := db.db.Query(ctx, query)
 28+	if err != nil {
 29+		db.l.Error("Failed to query live videos", "error", err)
 30+		return nil, err
 31+	}
 32+	defer rows.Close()
 33+
 34+	videos := make([]models.Video, 0)
 35+	for rows.Next() {
 36+		var video models.Video
 37+		err = rows.Scan(&video.ID)
 38+		if err != nil {
 39+			db.l.Error("Failed to scan live video", "error", err)
 40+			return nil, err
 41+		}
 42+		videos = append(videos, video)
 43+	}
 44+
 45+	return videos, nil
 46+}
 47+
 48+func (db *postgresDB) UpdateVideoLiveStatus(ctx context.Context, videoID string, isLive bool, duration int) error {
 49+	query := `UPDATE videos SET is_live = $1, duration = $2 WHERE id = $3`
 50+	_, err := db.db.Exec(ctx, query, isLive, duration, videoID)
 51+	if err != nil {
 52+		db.l.Error("Failed to update video live status", "error", err)
 53+		return err
 54+	}
 55+	return nil
 56+}
 57+
 58 func (db *postgresDB) DeleteVideoFile(ctx context.Context, videoID string) error {
 59 	query := `
 60 		UPDATE videos
 61diff --git a/handler/videos.go b/handler/videos.go
 62index a9db2a25fe44977717a6af2c090a135cf6f351d4..4758b0fe364dd7cb45cae813f5a9453e9ce75d62 100644
 63--- a/handler/videos.go
 64+++ b/handler/videos.go
 65@@ -120,9 +120,43 @@ func (h *handler) FetchVideos(ctx context.Context) error {
 66 		h.addVideosForChannel(ctx, result.channel, result.enableShorts)
 67 	}
 68 
 69+	h.recheckLiveVideos(ctx)
 70+
 71 	return nil
 72 }
 73 
 74+func (h *handler) recheckLiveVideos(ctx context.Context) {
 75+	liveVideos, err := h.db.GetLiveVideos(ctx)
 76+	if err != nil {
 77+		h.log.Error("Failed to get live videos", "error", err)
 78+		return
 79+	}
 80+	if len(liveVideos) == 0 {
 81+		return
 82+	}
 83+
 84+	videos := make(map[string]*models.Video, len(liveVideos))
 85+	for i := range liveVideos {
 86+		videos[liveVideos[i].ID] = &liveVideos[i]
 87+	}
 88+
 89+	err = h.youTubeClient.GetVideoDurations(ctx, videos)
 90+	if err != nil {
 91+		h.log.Error("Failed to recheck live video durations", "error", err)
 92+		return
 93+	}
 94+
 95+	for _, video := range videos {
 96+		if video.IsLive {
 97+			continue
 98+		}
 99+		err = h.db.UpdateVideoLiveStatus(ctx, video.ID, false, video.DurationSeconds)
100+		if err != nil {
101+			h.log.Error("Failed to update video live status", "videoID", video.ID, "error", err)
102+		}
103+	}
104+}
105+
106 func (h *handler) MarkVideoAsWatched(ctx context.Context, videoID string) error {
107 	watchTime := time.Now()
108 	return h.db.SetVideoWatchTime(ctx, videoID, &watchTime)
109diff --git a/mocks/db/db.go b/mocks/db/db.go
110index 3a075ebfb5fd8aa9ee7da3f006376dc98b60d477..ac0791cf365290d4a0b877b11c6dec38096c0d49 100644
111--- a/mocks/db/db.go
112+++ b/mocks/db/db.go
113@@ -36,6 +36,9 @@ var _ db.DB = &DBMock{}
114 //			GetChannelByIDFunc: func(ctx context.Context, channelID string) (*models.Channel, error) {
115 //				panic("mock out the GetChannelByID method")
116 //			},
117+//			GetLiveVideosFunc: func(ctx context.Context) ([]models.Video, error) {
118+//				panic("mock out the GetLiveVideos method")
119+//			},
120 //			GetNewVideosFunc: func(ctx context.Context, sortDesc bool) ([]models.Video, error) {
121 //				panic("mock out the GetNewVideos method")
122 //			},
123@@ -78,6 +81,9 @@ var _ db.DB = &DBMock{}
124 //			UnsubscribeFromChannelFunc: func(ctx context.Context, channelID string) error {
125 //				panic("mock out the UnsubscribeFromChannel method")
126 //			},
127+//			UpdateVideoLiveStatusFunc: func(ctx context.Context, videoID string, isLive bool, duration int) error {
128+//				panic("mock out the UpdateVideoLiveStatus method")
129+//			},
130 //		}
131 //
132 //		// use mockedDB in code that requires db.DB
133@@ -100,6 +106,9 @@ type DBMock struct {
134 	// GetChannelByIDFunc mocks the GetChannelByID method.
135 	GetChannelByIDFunc func(ctx context.Context, channelID string) (*models.Channel, error)
136 
137+	// GetLiveVideosFunc mocks the GetLiveVideos method.
138+	GetLiveVideosFunc func(ctx context.Context) ([]models.Video, error)
139+
140 	// GetNewVideosFunc mocks the GetNewVideos method.
141 	GetNewVideosFunc func(ctx context.Context, sortDesc bool) ([]models.Video, error)
142 
143@@ -142,6 +151,9 @@ type DBMock struct {
144 	// UnsubscribeFromChannelFunc mocks the UnsubscribeFromChannel method.
145 	UnsubscribeFromChannelFunc func(ctx context.Context, channelID string) error
146 
147+	// UpdateVideoLiveStatusFunc mocks the UpdateVideoLiveStatus method.
148+	UpdateVideoLiveStatusFunc func(ctx context.Context, videoID string, isLive bool, duration int) error
149+
150 	// calls tracks calls to the methods.
151 	calls struct {
152 		// AddVideo holds details about calls to the AddVideo method.
153@@ -179,6 +191,11 @@ type DBMock struct {
154 			// ChannelID is the channelID argument value.
155 			ChannelID string
156 		}
157+		// GetLiveVideos holds details about calls to the GetLiveVideos method.
158+		GetLiveVideos []struct {
159+			// Ctx is the ctx argument value.
160+			Ctx context.Context
161+		}
162 		// GetNewVideos holds details about calls to the GetNewVideos method.
163 		GetNewVideos []struct {
164 			// Ctx is the ctx argument value.
165@@ -291,12 +308,24 @@ type DBMock struct {
166 			// ChannelID is the channelID argument value.
167 			ChannelID string
168 		}
169+		// UpdateVideoLiveStatus holds details about calls to the UpdateVideoLiveStatus method.
170+		UpdateVideoLiveStatus []struct {
171+			// Ctx is the ctx argument value.
172+			Ctx context.Context
173+			// VideoID is the videoID argument value.
174+			VideoID string
175+			// IsLive is the isLive argument value.
176+			IsLive bool
177+			// Duration is the duration argument value.
178+			Duration int
179+		}
180 	}
181 	lockAddVideo                  sync.RWMutex
182 	lockClose                     sync.RWMutex
183 	lockDeleteVideoFile           sync.RWMutex
184 	lockDiscardVideo              sync.RWMutex
185 	lockGetChannelByID            sync.RWMutex
186+	lockGetLiveVideos             sync.RWMutex
187 	lockGetNewVideos              sync.RWMutex
188 	lockGetVideo                  sync.RWMutex
189 	lockGetVideosForCleanup       sync.RWMutex
190@@ -311,6 +340,7 @@ type DBMock struct {
191 	lockSubscribeToChannel        sync.RWMutex
192 	lockToggleChannelShorts       sync.RWMutex
193 	lockUnsubscribeFromChannel    sync.RWMutex
194+	lockUpdateVideoLiveStatus     sync.RWMutex
195 }
196 
197 // AddVideo calls AddVideoFunc.
198@@ -492,6 +522,38 @@ func (mock *DBMock) GetChannelByIDCalls() []struct {
199 	return calls
200 }
201 
202+// GetLiveVideos calls GetLiveVideosFunc.
203+func (mock *DBMock) GetLiveVideos(ctx context.Context) ([]models.Video, error) {
204+	if mock.GetLiveVideosFunc == nil {
205+		panic("DBMock.GetLiveVideosFunc: method is nil but DB.GetLiveVideos was just called")
206+	}
207+	callInfo := struct {
208+		Ctx context.Context
209+	}{
210+		Ctx: ctx,
211+	}
212+	mock.lockGetLiveVideos.Lock()