267d5505b382d5000e6f235ea66cffa146b0ed53

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

Message

Add JSON endpoint for setting video watch progress

Diff

 1diff --git a/API.md b/API.md
 2index 1dcb5c0639855a787894e53511e8a178fadb93b2..f720777c65f956abe40c1e66b2d7ac7d6c059a1e 100644
 3--- a/API.md
 4+++ b/API.md
 5@@ -18,7 +18,7 @@ An incorrect token returns `401`:
 6 {"error":"invalid auth token"}
 7 ```
 8 
 9-Handler failures return `500` with an `error` string.
10+Invalid request input returns `400` with an `error` string. Handler failures return `500` with an `error` string.
11 
12 ## Video object
13 
14@@ -97,3 +97,19 @@ Success response:
15 ```json
16 {"msg":"cleared video from watch history"}
17 ```
18+
19+### `POST /api/videos/:video_id/progress`
20+
21+Updates the saved watch progress. The request body must contain a `progress` string in Go duration, `mm:ss`, or `hh:mm:ss` format:
22+
23+```json
24+{"progress":"2:30"}
25+```
26+
27+Success response:
28+
29+```json
30+{"video":{/* Video object */}}
31+```
32+
33+Missing or invalid progress returns `400`.
34diff --git a/httpserver/ytrssil/server.go b/httpserver/ytrssil/server.go
35index 8c4b08395b55401db3f5a4ef136a953b6ea9ab67..219511b57e27a740933e5e8a5f1207338834584d 100644
36--- a/httpserver/ytrssil/server.go
37+++ b/httpserver/ytrssil/server.go
38@@ -98,6 +98,7 @@ func SetupGinRouter(
39 		api.GET("videos/watched", srv.GetWatchedVideosJSON)
40 		api.POST("videos/:video_id/watch", srv.MarkVideoAsWatchedJSON)
41 		api.POST("videos/:video_id/unwatch", srv.MarkVideoAsUnwatchedJSON)
42+		api.POST("videos/:video_id/progress", srv.SetVideoProgressJSON)
43 		api.POST("videos/:video_id/download", srv.DownloadVideoJSON)
44 	}
45 
46diff --git a/httpserver/ytrssil/videos.go b/httpserver/ytrssil/videos.go
47index 327795e039107f044d24d7e435075c4a5fcd4991..b436ada2187d7ec4244ce4d43099eddf196d9463 100644
48--- a/httpserver/ytrssil/videos.go
49+++ b/httpserver/ytrssil/videos.go
50@@ -1,10 +1,13 @@
51 package ytrssil
52 
53 import (
54+	"errors"
55 	"net/http"
56 	"strconv"
57 
58 	"github.com/gin-gonic/gin"
59+
60+	"github.com/TheEdgeOfRage/ytrssil-api/handler"
61 )
62 
63 func (srv *server) GetNewVideosJSON(c *gin.Context) {
64@@ -64,6 +67,32 @@ func (srv *server) MarkVideoAsUnwatchedJSON(c *gin.Context) {
65 	c.JSON(http.StatusOK, gin.H{"msg": "cleared video from watch history"})
66 }
67 
68+func (srv *server) SetVideoProgressJSON(c *gin.Context) {
69+	var request struct {
70+		Progress string `json:"progress"`
71+	}
72+	if err := c.ShouldBindJSON(&request); err != nil {
73+		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid progress request"})
74+		return
75+	}
76+	if request.Progress == "" {
77+		c.JSON(http.StatusBadRequest, gin.H{"error": "missing progress"})
78+		return
79+	}
80+
81+	video, err := srv.handler.SetVideoProgress(c.Request.Context(), c.Param("video_id"), request.Progress)
82+	if err != nil {
83+		if errors.Is(err, handler.ErrInvalidProgress) {
84+			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
85+			return
86+		}
87+		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
88+		return
89+	}
90+
91+	c.JSON(http.StatusOK, gin.H{"video": video})
92+}
93+
94 func (srv *server) DownloadVideoJSON(c *gin.Context) {
95 	videoID := c.Param("video_id")
96 	resolution := c.PostForm("format")