mirror of
https://codeberg.org/SafeTwitch/safetwitch-backend.git
synced 2024-12-22 13:13:00 -05:00
Add get stream functionality and use Switch client-id to bypass needing a valid integrity token (new twitch update)
This commit is contained in:
parent
72b59bfd17
commit
6b1895b958
3 changed files with 93 additions and 1 deletions
|
@ -5,6 +5,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"safetwitch-backend/extractor"
|
"safetwitch-backend/extractor"
|
||||||
"safetwitch-backend/extractor/structs"
|
"safetwitch-backend/extractor/structs"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
|
@ -135,3 +136,22 @@ func ParseBadges(data gjson.Result) ([]structs.Badge, error) {
|
||||||
|
|
||||||
return formattedBadges, nil
|
return formattedBadges, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ProxyPlaylistFile(playlist string) string {
|
||||||
|
// Split the playlist into individual entries
|
||||||
|
entries := strings.Split(playlist, "\n")[1:] // Ignore the first line which contains the M3U header
|
||||||
|
|
||||||
|
// Loop through each entry and replace the URL
|
||||||
|
for i, entry := range entries {
|
||||||
|
if strings.HasPrefix(entry, "http") { // Only modify lines that contain URLs
|
||||||
|
newURL := extractor.ProxyUrl(entry)
|
||||||
|
entries[i] = newURL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join the modified entries back into a single string separated by a newline
|
||||||
|
modifiedPlaylist := "#EXTM3U\n" + strings.Join(entries, "\n")
|
||||||
|
|
||||||
|
return modifiedPlaylist
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"safetwitch-backend/extractor"
|
"safetwitch-backend/extractor"
|
||||||
"safetwitch-backend/extractor/structs"
|
"safetwitch-backend/extractor/structs"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
)
|
)
|
||||||
|
@ -21,7 +22,7 @@ func call(url, method string, body io.Reader) (*http.Response, error) {
|
||||||
return req.Response, err
|
return req.Response, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Add("Client-Id", "kimne78kx3ncx6brgo4mv6wki5h1ko")
|
req.Header.Add("Client-Id", "ue6666qo983tsx6so1t0vnawi233wa")
|
||||||
resp, err := http.DefaultClient.Do(req)
|
resp, err := http.DefaultClient.Do(req)
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
|
@ -354,3 +355,63 @@ func GetStreamerBadges(streamerName string) ([]structs.Badge, error) {
|
||||||
|
|
||||||
return formattedBadges, nil
|
return formattedBadges, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetStream(streamerName string) (string, error) {
|
||||||
|
// STAGE 1
|
||||||
|
// Get playback token from twitch
|
||||||
|
// Same request browser makes
|
||||||
|
payload1 := []TwitchPayload{
|
||||||
|
{
|
||||||
|
"extensions": map[string]interface{}{
|
||||||
|
"persistedQuery": map[string]interface{}{
|
||||||
|
"version": 1,
|
||||||
|
"sha256Hash": "0828119ded1c13477966434e15800ff57ddacf13ba1911c129dc2200705b0712",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"operationName": "PlaybackAccessToken",
|
||||||
|
"variables": map[string]interface{}{
|
||||||
|
"isLive": true,
|
||||||
|
"login": streamerName,
|
||||||
|
"isVod": false,
|
||||||
|
"vodID": "",
|
||||||
|
"playerType": "site",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, body, err := parseResponse(payload1)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// These will be needed to get playlist file from twitch
|
||||||
|
token := gjson.Get(string(body), "0.data.streamPlaybackAccessToken.value").String()
|
||||||
|
signature := gjson.Get(string(body), "0.data.streamPlaybackAccessToken.signature").String()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
playlistUrl := "https://usher.ttvnw.net/api/channel/hls/" + strings.ToLower(streamerName) + ".m3u8"
|
||||||
|
params := "?sig=" + signature + "&token=" + token
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", playlistUrl+params, nil)
|
||||||
|
req.Header.Add("Client-Id", "ue6666qo983tsx6so1t0vnawi233wa")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
playlistFile, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// holy zooks, what the scallop??? we got the playlist, houston!!!
|
||||||
|
// time to proxy all the urls!!!
|
||||||
|
proxiedPlaylist := ProxyPlaylistFile(string(playlistFile))
|
||||||
|
return proxiedPlaylist, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
b64 "encoding/base64"
|
b64 "encoding/base64"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"safetwitch-backend/extractor/twitch"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
@ -31,4 +32,14 @@ func Routes(route *gin.Engine) {
|
||||||
context.Data(200, contentType, body)
|
context.Data(200, contentType, body)
|
||||||
context.JSON(imageResp.StatusCode, imageResp.Body)
|
context.JSON(imageResp.StatusCode, imageResp.Body)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
auth.GET("/stream/:username/hls.m3u8", func(context *gin.Context) {
|
||||||
|
streamer := context.Param("username")
|
||||||
|
playlistFile, err := twitch.GetStream(streamer)
|
||||||
|
if err != nil {
|
||||||
|
context.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Data(200, "application/text", []byte(playlistFile))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue