~shockham/witcht

86ec9676cf22ced5955d6a4ecda2f326b405484a — shockham 7 months ago dc733b4
Add ability to grab a different quality
1 files changed, 67 insertions(+), 46 deletions(-)

M main.go
M main.go => main.go +67 -46
@@ 11,6 11,7 @@ import (
	"net/http"
	"os"
	"regexp"
	"strings"
	"sync"
	"time"
)


@@ 18,7 19,16 @@ import (
type Operation string

const (
    PlaybackAccessToken Operation = "PlaybackAccessToken"
	PlaybackAccessToken Operation = "PlaybackAccessToken"
)

const (
	QualityAudio = "audio_only"
	Quality160p  = "160p"
	Quality360p  = "360p"
	Quality480p  = "480p"
	Quality720p  = "720p60"
	Quality1080p = "1080p60 (source)"
)

func QueryGQL(operationName Operation, channel string, variables interface{}) (*http.Response, error) {


@@ 41,8 51,8 @@ func QueryGQL(operationName Operation, channel string, variables interface{}) (*

	req, err := http.NewRequest("POST", "https://gql.twitch.tv/gql",
		bytes.NewBuffer(jsonData))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("Client-ID", "kimne78kx3ncx6brgo4mv6wki5h1ko")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Client-ID", "kimne78kx3ncx6brgo4mv6wki5h1ko")

	client := &http.Client{Timeout: time.Second * 10}
	return client.Do(req)


@@ 62,15 72,15 @@ type StreamResponseData struct {
}

func GetAccessToken(channel string) PlaybackAccessSignature {
    variables := map[string]interface{}{
        "isLive":     true,
        "login":      channel,
        "isVod":      false,
        "vodID":      "",
        "playerType": "site",
    }
	variables := map[string]interface{}{
		"isLive":     true,
		"login":      channel,
		"isVod":      false,
		"vodID":      "",
		"playerType": "site",
	}

    resp, err := QueryGQL(PlaybackAccessToken, channel, variables)
	resp, err := QueryGQL(PlaybackAccessToken, channel, variables)

	if err != nil {
		log.Fatal(err)


@@ 135,17 145,16 @@ func GetStreamUri(channel string, accessToken PlaybackAccessSignature) StreamDat
}

type StreamInfo struct {
    GameName string `json:"game_name"`
    Title string `json:"title"`
	GameName string `json:"game_name"`
	Title    string `json:"title"`
}

type StreamResponse struct {
	Data []StreamInfo `json:"data"`
}


func GetStreamInfo(streamId int) StreamInfo {
    infoUri := fmt.Sprintf("https://api.twitch.tv/helix/streams?user_id=%d&first=1", streamId)
	infoUri := fmt.Sprintf("https://api.twitch.tv/helix/streams?user_id=%d&first=1", streamId)
	req, err := http.NewRequest("GET", infoUri, nil)
	req.Header.Set("Client-Id", "d4uvtfdr04uq6raoenvj7m86gdk16v")
	req.Header.Set("Content-Type", "application/json")


@@ 154,45 163,50 @@ func GetStreamInfo(streamId int) StreamInfo {
	resp, err := client.Do(req)

	if err != nil {
        return StreamInfo{
            GameName: "N/A",
            Title: "Live",
        }
		return StreamInfo{
			GameName: "N/A",
			Title:    "Live",
		}
	}
	
    var res StreamResponse
    err = json.NewDecoder(resp.Body).Decode(&res)

    if err != nil || len(res.Data) <= 0 {
        return StreamInfo{
            GameName: "N/A",
            Title: "Live",
        }
    }
	var res StreamResponse
	err = json.NewDecoder(resp.Body).Decode(&res)

	if err != nil || len(res.Data) <= 0 {
		return StreamInfo{
			GameName: "N/A",
			Title:    "Live",
		}
	}

    return res.Data[0]
	return res.Data[0]
}

type TokenInfo struct {
    StreamId int `json:"channel_id"`
	StreamId int `json:"channel_id"`
}

func GetStreamIdFromToken(accessToken string) int {
    var tokenInfo TokenInfo
    err := json.Unmarshal([]byte(accessToken), &tokenInfo)
    
    if err != nil {
        return -1
    }
	var tokenInfo TokenInfo
	err := json.Unmarshal([]byte(accessToken), &tokenInfo)

    return tokenInfo.StreamId
	if err != nil {
		return -1
	}

	return tokenInfo.StreamId
}

func Get480pUrl(channel string) (string, int) {
func GetUrlAtQuality(channel string, quality string) (string, int) {
	accessToken := GetAccessToken(channel)
	streamData := GetStreamUri(channel, accessToken)
    streamId := GetStreamIdFromToken(accessToken.Token)
	return streamData.Urls["480p"].url, streamId
	streamId := GetStreamIdFromToken(accessToken.Token)

	if urlData, ok := streamData.Urls[quality]; ok {
		return urlData.url, streamId
	} else {
		return streamData.Urls[Quality480p].url, streamId
	}
}

func StartServer() {


@@ 207,7 221,7 @@ func StartServer() {
		matches := re.FindStringSubmatch(r.URL.Path)
		channel := matches[1]

		url, _ := Get480pUrl(channel)
		url, _ := GetUrlAtQuality(channel, Quality480p)

		w.Header().Set("Content-Type", "text/html; charset=utf-8")



@@ 220,9 234,16 @@ func StartServer() {

func main() {
	if len(os.Args[1:]) == 1 {
		channel := os.Args[1]
		channelArg := os.Args[1]
		channelQuality := strings.Split(channelArg, ":")
		channel := channelQuality[0]
		quality := Quality480p

		if len(channelQuality) > 1 {
			quality = channelQuality[1]
		}

		url, _ := Get480pUrl(channel)
		url, _ := GetUrlAtQuality(channel, quality)

		fmt.Println(url)
	} else if len(os.Args[1:]) > 1 {


@@ 233,9 254,9 @@ func main() {
			go func(channel string) {
				defer wg.Done()

				if url, id := Get480pUrl(channel); url != "" {
                    stream := GetStreamInfo(id)
                    fmt.Printf("\033[;7m %s \033[1;37m %s \033[0;0m %s\n", channel, stream.GameName, stream.Title)
				if url, id := GetUrlAtQuality(channel, Quality480p); url != "" {
					stream := GetStreamInfo(id)
					fmt.Printf("\033[;7m %s \033[1;37m %s \033[0;0m %s\n", channel, stream.GameName, stream.Title)
				}
			}(channel)
		}