add Twitch Client-ID support - twitch-go - twitch.tv web application in Go
(HTM) git clone git://git.codemadness.org/twitch-go
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
(DIR) commit d1d8ef8c6e8114644913d3ef7e15c1a60b0946c2
(DIR) parent e262649da11824a0a269b202169496e3eecac6de
(HTM) Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date: Tue, 20 Sep 2016 21:54:07 +0200
add Twitch Client-ID support
it is now required to authenticate using a requested API token to Twitch. This
can be requested in the account settings.
Add a -c flag to set the client id.
Other changes:
- merge handlers.go back into main.go again.
- update documentation and rc.d example.
Diffstat:
M README | 1 +
M data/static/twitch.sh | 7 +++++--
D handlers.go | 79 -------------------------------
M main.go | 99 ++++++++++++++++++++++++++++---
M rc_twitch | 2 +-
M src/twitch/twitch.go | 4 ++++
6 files changed, 101 insertions(+), 91 deletions(-)
---
(DIR) diff --git a/README b/README
@@ -21,6 +21,7 @@ Install as OpenBSD daemon:
- Make a user "_twitch", install the twitch binary to /usr/local/sbin.
- Make a file /etc/rc.d/twitch, see rc_twitch example file.
+- Make sure to set your Twitch Client-ID token in this file.
- Start the daemon:
# rcctl start twitch
(DIR) diff --git a/data/static/twitch.sh b/data/static/twitch.sh
@@ -10,14 +10,17 @@ err() {
}
[ "$1" = "" ] && err "Usage: $0 <channel>"
+clientid="client_id_here"
channel="$1"
tokenurl="https://api.twitch.tv/api/channels/${channel}/access_token"
-tokendata=$(curl -s -L "$tokenurl")
+tokendata=$(curl -s -H "Client-ID: ${clientid}" "$tokenurl")
token=$(printf '%s' "${tokendata}" | sed -E -e 's@.*"token":"(.*)","sig".*@\1@g' -e 's@\\"@"@g')
sig=$(printf '%s' "${tokendata}" | sed -E 's@.*"sig":"([^"]*)".*@\1@g')
if test x"$token" != x"" && test x"$sig" != x""; then
- curl -G -L "http://usher.justin.tv/api/channel/hls/${channel}.m3u8" \
+ curl -G \
+ -H "Client-ID: ${clientid}" \
+ "http://usher.justin.tv/api/channel/hls/${channel}.m3u8" \
--data-urlencode "token=${token}" \
--data-urlencode "sig=${sig}"
else
(DIR) diff --git a/handlers.go b/handlers.go
@@ -1,79 +0,0 @@
-package main
-
-import (
- "fmt"
- "net/http"
-)
-
-import "twitch"
-
-func FeaturedHandler(w http.ResponseWriter, r *http.Request) error {
- featured, err := twitch.GetFeatured()
- if err != nil {
- return err
- }
- return templates.Render(w, "featured.html", "page.html", featured)
-}
-
-func PlaylistHandler(w http.ResponseWriter, r *http.Request) error {
- channel := r.FormValue("c")
- if channel == "" {
- BadRequest(w, "No channel name specified")
- return nil
- }
- format := r.FormValue("f") // if empty "redirect".
- token, err := twitch.GetToken(channel)
- if err != nil {
- return err
- }
- url := fmt.Sprintf("http://usher.justin.tv/api/channel/hls/%s.m3u8?token=%s&sig=%s", channel, token.Token, token.Sig)
- switch format {
- case "html":
- return templates.Render(w, "playlist.html", "page.html", struct {
- Url string
- }{
- Url: url,
- })
- case "plain":
- w.Write([]byte(url))
- default: // redirect
- w.Header().Set("Location", url)
- w.WriteHeader(http.StatusFound)
- if r.Method == "GET" {
- w.Write([]byte(url))
- }
- }
- return nil
-}
-
-func GameHandler(w http.ResponseWriter, r *http.Request) error {
- gamename := r.FormValue("g")
- if gamename == "" {
- BadRequest(w, "No game name specified")
- return nil
- }
- game, err := twitch.GetGame(gamename)
- if err != nil {
- return err
- }
- v := struct {
- Name string
- TwitchGame *twitch.Game
- }{
- Name: gamename,
- TwitchGame: game,
- }
- return templates.Render(w, "game.html", "page.html", v)
-}
-
-func GamesHandler(w http.ResponseWriter, r *http.Request) error {
- games, err := twitch.GetGames()
- if err != nil {
- return err
- }
- return templates.Render(w, "games.html", "page.html", games)
-}
-
-func LinksHandler(w http.ResponseWriter, r *http.Request) error {
- return templates.Render(w, "links.html", "page.html", make(map[string]string))
-}
(DIR) diff --git a/main.go b/main.go
@@ -16,6 +16,8 @@ import (
"time"
)
+import "twitch"
+
type TwitchHandler func(http.ResponseWriter, *http.Request) error
type Templates struct {
@@ -25,6 +27,16 @@ type Templates struct {
var templates *Templates
+// config
+var config_addr string
+var config_addrtype string
+var config_chmod uint
+var config_clientid string
+var config_datadir string
+var config_templatethemedir string = "templates/themes/default/"
+var config_templatepagedir string = "templates/pages/"
+var config_staticcontentdir string = "static/"
+
func NewTemplates() *Templates {
t := &Templates{}
t.Pages = make(map[string]*template.Template)
@@ -126,27 +138,96 @@ func MakeHandler(h TwitchHandler) func(http.ResponseWriter, *http.Request) {
}
}
+func FeaturedHandler(w http.ResponseWriter, r *http.Request) error {
+ featured, err := twitch.GetFeatured()
+ if err != nil {
+ return err
+ }
+ return templates.Render(w, "featured.html", "page.html", featured)
+}
+
+func PlaylistHandler(w http.ResponseWriter, r *http.Request) error {
+ channel := r.FormValue("c")
+ if channel == "" {
+ BadRequest(w, "No channel name specified")
+ return nil
+ }
+ format := r.FormValue("f") // if empty "redirect".
+ token, err := twitch.GetToken(channel)
+ if err != nil {
+ return err
+ }
+ url := fmt.Sprintf("http://usher.justin.tv/api/channel/hls/%s.m3u8?token=%s&sig=%s", channel, token.Token, token.Sig)
+ switch format {
+ case "html":
+ return templates.Render(w, "playlist.html", "page.html", struct {
+ Url string
+ }{
+ Url: url,
+ })
+ case "plain":
+ w.Write([]byte(url))
+ default: // redirect
+ w.Header().Set("Location", url)
+ w.WriteHeader(http.StatusFound)
+ if r.Method == "GET" {
+ w.Write([]byte(url))
+ }
+ }
+ return nil
+}
+
+func GameHandler(w http.ResponseWriter, r *http.Request) error {
+ gamename := r.FormValue("g")
+ if gamename == "" {
+ BadRequest(w, "No game name specified")
+ return nil
+ }
+ game, err := twitch.GetGame(gamename)
+ if err != nil {
+ return err
+ }
+ v := struct {
+ Name string
+ TwitchGame *twitch.Game
+ }{
+ Name: gamename,
+ TwitchGame: game,
+ }
+ return templates.Render(w, "game.html", "page.html", v)
+}
+
+func GamesHandler(w http.ResponseWriter, r *http.Request) error {
+ games, err := twitch.GetGames()
+ if err != nil {
+ return err
+ }
+ return templates.Render(w, "games.html", "page.html", games)
+}
+
+func LinksHandler(w http.ResponseWriter, r *http.Request) error {
+ return templates.Render(w, "links.html", "page.html", make(map[string]string))
+}
+
+
func usage() {
fmt.Fprintf(os.Stderr, "Usage: %s\n", os.Args[0])
flag.PrintDefaults()
}
func main() {
- // config
- var config_addr string
- var config_addrtype string
- var config_chmod uint
- var config_datadir string
- var config_templatethemedir string = "templates/themes/default/"
- var config_templatepagedir string = "templates/pages/"
- var config_staticcontentdir string = "static/"
-
+ flag.StringVar(&twitch.Clientid, "c", "", "Client-ID token")
flag.StringVar(&config_datadir, "d", "", "Chdir to data directory")
flag.StringVar(&config_addr, "l", "127.0.0.1:8080", "listen address")
flag.UintVar(&config_chmod, "m", 0755, "Permission for unix domain socket")
flag.StringVar(&config_addrtype, "t", "tcp4", `listen type: "tcp", "tcp4", "tcp6", "unix" or "unixpacket"`)
flag.Parse()
+ if len(twitch.Clientid) == 0 {
+ usage()
+ os.Exit(1)
+ }
+
pledgestr := "stdio rpath cpath wpath dns"
if config_addrtype == "unix" {
pledgestr += " unix"
(DIR) diff --git a/rc_twitch b/rc_twitch
@@ -17,7 +17,7 @@ user="_twitch"
group="_twitch"
daemon="chroot -u ${user} -g ${group} $chroot ${chroot_daemon}"
-daemon_flags="-t tcp4 -d /data -l 127.0.0.1:8081"
+daemon_flags="-t tcp4 -d /data -l 127.0.0.1:8081 -c twitch_clientid_here"
. /etc/rc.d/rc.subr
(DIR) diff --git a/src/twitch/twitch.go b/src/twitch/twitch.go
@@ -9,6 +9,9 @@ import (
"time"
)
+// set your Twitch Client-ID.
+var Clientid string
+
type Token struct {
Mobile_restricted bool
Sig string
@@ -106,6 +109,7 @@ func ReadAllUrl(url string) ([]byte, error) {
if err != nil {
return nil, err
}
+ req.Header.Set("Client-ID", Clientid)
resp, err := client.Do(req)
if resp != nil {
defer resp.Body.Close()