mirror of
https://codeberg.org/SafeTwitch/safetwitch-backend.git
synced 2024-12-22 05:02:58 -05:00
VOD support
This commit is contained in:
parent
e54c610848
commit
a9fe2eaaf3
7 changed files with 119 additions and 3 deletions
|
@ -113,3 +113,21 @@ type Shelve struct {
|
|||
Title string `json:"title"`
|
||||
Videos []Video `json:"videos"`
|
||||
}
|
||||
|
||||
type VodMessager struct {
|
||||
Name string `json:"name"`
|
||||
Login string `json:"login"`
|
||||
}
|
||||
|
||||
type VodCommentBadge struct {
|
||||
Version string `json:"version"`
|
||||
SetID string `json:"setId"`
|
||||
}
|
||||
|
||||
type VodComment struct {
|
||||
Message string `json:"message"`
|
||||
Messager MinifiedStreamer `json:"messager"`
|
||||
Offset int `json:"offset"`
|
||||
Cursor string `json:"cursor"`
|
||||
Badges []VodCommentBadge `json:"badges"`
|
||||
}
|
||||
|
|
36
extractor/twitch/VODChat.go
Normal file
36
extractor/twitch/VODChat.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package twitch
|
||||
|
||||
import (
|
||||
"safetwitch-backend/extractor/structs"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func GetVODChat(vodID string, second int) ([]structs.VodComment, error) {
|
||||
payload := []TwitchPayload{
|
||||
{
|
||||
"operationName": "VideoCommentsByOffsetOrCursor",
|
||||
"variables": map[string]interface{}{
|
||||
"videoID": vodID,
|
||||
"contentOffsetSeconds": second,
|
||||
},
|
||||
"extensions": map[string]interface{}{
|
||||
"persistedQuery": map[string]interface{}{
|
||||
"version": 1,
|
||||
"sha256Hash": "b70a3591ff0f4e0313d126c6a1502d79a1c02baebb288227c582044aa76adf6a",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, body, err := parseResponse(payload)
|
||||
if err != nil {
|
||||
return []structs.VodComment{}, err
|
||||
}
|
||||
|
||||
comments := gjson.Get(string(body), "0.data.video.comments.edges").Array()
|
||||
parsedComments := []structs.VodComment{}
|
||||
for _, comment := range comments {
|
||||
parsedComments = append(parsedComments, ParseVODMessage(comment))
|
||||
}
|
||||
return parsedComments, nil
|
||||
}
|
|
@ -285,3 +285,34 @@ func ParseShelve(data gjson.Result, streamer structs.Streamer) structs.Shelve {
|
|||
Videos: parsedVideos,
|
||||
}
|
||||
}
|
||||
|
||||
func ParseVODMessage(data gjson.Result) structs.VodComment {
|
||||
messager := structs.MinifiedStreamer{
|
||||
Login: data.Get("node.commenter.login").String(),
|
||||
Name: data.Get("node.commenter.displayName").String(),
|
||||
ColorHex: data.Get("node.message.userColor").String(),
|
||||
}
|
||||
|
||||
parsedBadges := []structs.VodCommentBadge{}
|
||||
for _, badge := range data.Get("node.message.userBadges").Array() {
|
||||
setID := badge.Get("setID").String()
|
||||
version := badge.Get("version").String()
|
||||
|
||||
if version != "" && setID != "" {
|
||||
b := structs.VodCommentBadge{
|
||||
SetID: setID,
|
||||
Version: version,
|
||||
}
|
||||
parsedBadges = append(parsedBadges, b)
|
||||
}
|
||||
}
|
||||
|
||||
return structs.VodComment{
|
||||
Message: data.Get("node.message.fragments.0.text").String(),
|
||||
Offset: int(data.Get("node.contentOffsetSeconds").Int()),
|
||||
Cursor: data.Get("cursor").String(),
|
||||
Messager: messager,
|
||||
Badges: parsedBadges,
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ func Routes(route *gin.Engine) {
|
|||
data, err := twitch.GetDiscoveryItem(context.Param("categoryName"), 50, "")
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
context.JSON(200, extractor.FormatMessage(data, true))
|
||||
|
|
|
@ -20,6 +20,7 @@ func Routes(route *gin.Engine) {
|
|||
})
|
||||
} else {
|
||||
context.Error(err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -3,14 +3,15 @@ package vods
|
|||
import (
|
||||
"safetwitch-backend/extractor"
|
||||
"safetwitch-backend/extractor/twitch"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Routes(route *gin.Engine) {
|
||||
auth := route.Group("/api/vods")
|
||||
vods := route.Group("/api/vods")
|
||||
|
||||
auth.GET("/shelve/:streamerName", func(context *gin.Context) {
|
||||
vods.GET("/shelve/:streamerName", func(context *gin.Context) {
|
||||
data, err := twitch.GetStreamerVideoShelves(context.Param("streamerName"))
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
|
@ -19,7 +20,7 @@ func Routes(route *gin.Engine) {
|
|||
context.JSON(200, extractor.FormatMessage(data, true))
|
||||
})
|
||||
|
||||
auth.GET("/:streamerName", func(context *gin.Context) {
|
||||
vods.GET("/:streamerName", func(context *gin.Context) {
|
||||
data, err := twitch.GetVodMetadata(context.Param("streamerName"))
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
|
@ -27,4 +28,20 @@ func Routes(route *gin.Engine) {
|
|||
}
|
||||
context.JSON(200, extractor.FormatMessage(data, true))
|
||||
})
|
||||
|
||||
vods.GET("/comments/:vodID/:offset", func(context *gin.Context) {
|
||||
offset := context.Param("offset")
|
||||
o, err := strconv.Atoi(offset)
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := twitch.GetVODChat(context.Param("vodID"), o)
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
return
|
||||
}
|
||||
context.JSON(200, extractor.FormatMessage(data, true))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -17,15 +17,18 @@ func Routes(route *gin.Engine) {
|
|||
decodedUrl, err := b64.StdEncoding.DecodeString(context.Param("url"))
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
imageResp, err := http.Get(string(decodedUrl))
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
return
|
||||
}
|
||||
body, err := io.ReadAll(imageResp.Body)
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
contentType := imageResp.Header.Get("Content-Type")
|
||||
|
@ -39,6 +42,7 @@ func Routes(route *gin.Engine) {
|
|||
playlistFile, err := twitch.GetStream(streamer)
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
context.Data(200, "application/vnd.apple.mpegurl", []byte(playlistFile))
|
||||
|
@ -48,11 +52,13 @@ func Routes(route *gin.Engine) {
|
|||
decodedUrl, err := b64.StdEncoding.DecodeString(context.Param("encodedUrl"))
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
playlistFile, err := twitch.GetSubPlaylist(string(decodedUrl), false)
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
context.Data(200, "application/vnd.apple.mpegurl", []byte(playlistFile))
|
||||
|
@ -62,6 +68,7 @@ func Routes(route *gin.Engine) {
|
|||
decodedUrl, err := b64.StdEncoding.DecodeString(context.Param("encodedUrl"))
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
segmentData, err := http.Get(string(decodedUrl))
|
||||
|
@ -73,6 +80,7 @@ func Routes(route *gin.Engine) {
|
|||
segment, err := io.ReadAll(segmentData.Body)
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
context.Data(200, "application/text", segment)
|
||||
|
@ -93,11 +101,13 @@ func Routes(route *gin.Engine) {
|
|||
decodedUrl, err := b64.StdEncoding.DecodeString(context.Param("encodedUrl"))
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
playlistFile, err := twitch.GetSubPlaylist(string(decodedUrl), true)
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
context.Data(200, "application/vnd.apple.mpegurl", []byte(playlistFile))
|
||||
|
@ -107,6 +117,7 @@ func Routes(route *gin.Engine) {
|
|||
decodedUrl, err := b64.StdEncoding.DecodeString(context.Param("encodedUrl"))
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
// remove the last path of url and replace with segment
|
||||
|
@ -122,6 +133,7 @@ func Routes(route *gin.Engine) {
|
|||
segment, err := io.ReadAll(segmentData.Body)
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
context.Data(200, "application/text", segment)
|
||||
|
|
Loading…
Reference in a new issue