aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE2
-rw-r--r--README.adoc11
-rw-r--r--main.go81
3 files changed, 47 insertions, 47 deletions
diff --git a/LICENSE b/LICENSE
index 7abc3d4..d5d26bc 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2016 - 2018, Přemysl Eric Janouch <p@janouch.name>
+Copyright (c) 2016 - 2024, Přemysl Eric Janouch <p@janouch.name>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
diff --git a/README.adoc b/README.adoc
index 8d6ffc7..8818904 100644
--- a/README.adoc
+++ b/README.adoc
@@ -19,14 +19,19 @@ Build dependencies: go
$ cd bbc-on-ice
$ go build
+or, if you know what you're doing:
+
+ $ go install janouch.name/bbc-on-ice@master
+
To run the local server:
$ ./bbc-on-ice :8000
-Streams have URLs in the following form:
+Streams have URLs in the following form, derived from
+https://gist.github.com/bpsib/67089b959e4fa898af69fea59ad74bc3[this list]:
- $ mpv http://localhost:8000/nonuk/sbr_low/bbc_radio_one
- $ mpv http://localhost:8000/uk/sbr_high/bbc_1xtra
+ $ mpv http://localhost:8000/ww/96000/bbc_radio_one
+ $ mpv http://localhost:8000/uk/320000/bbc_1xtra
Socket activation
-----------------
diff --git a/main.go b/main.go
index a67e909..dfff28f 100644
--- a/main.go
+++ b/main.go
@@ -21,9 +21,9 @@ import (
)
const (
- targetURI = "http://a.files.bbci.co.uk/media/live/manifesto/" +
- "audio/simulcast/hls/%s/%s/ak/%s.m3u8"
- metaBaseURI = "http://polling.bbc.co.uk/radio/nhppolling/"
+ targetURI = "http://as-hls-%s-live.akamaized.net/pool_904/live/%s/" +
+ "%s/%s.isml/%s-audio%%3d%s.norewind.m3u8"
+ metaURI = "https://rms.api.bbc.co.uk/v2/services/%s/segments/latest"
)
type meta struct {
@@ -33,7 +33,7 @@ type meta struct {
// getMeta retrieves and decodes metadata info from an independent webservice.
func getMeta(name string) (*meta, error) {
- resp, err := http.Get(metaBaseURI + name)
+ resp, err := http.Get(fmt.Sprintf(metaURI, name))
if resp != nil {
defer resp.Body.Close()
}
@@ -41,46 +41,41 @@ func getMeta(name string) (*meta, error) {
return nil, err
}
b, err := ioutil.ReadAll(resp.Body)
- if len(b) < 2 {
- // There needs to be an enclosing () pair
- return nil, errors.New("invalid metadata response")
- }
- type broadcast struct {
- Title string // title of the broadcast
- Percentage int // how far we're in
- }
+ // TODO: update more completely for the new OpenAPI
+ // - `broadcasts/poll/bbc_radio_one` looks almost useful
+ // - https://rms.api.bbc.co.uk/v2/experience/inline/play/${name}
+ // seems to be what we want, even provides timer/polling values
var v struct {
- Packages struct {
- OnAir struct {
- Broadcasts []broadcast
- BroadcastNowIndex uint
- } `json:"on-air"`
- Richtracks []struct {
- Artist string
- Title string
- IsNowPlaying bool `json:"is_now_playing"`
- }
- }
- Timeouts struct {
- PollingTimeout uint `json:"polling_timeout"`
- }
+ Data []struct {
+ Titles struct {
+ Primary string `json:"primary"`
+ Secondary *string `json:"secondary"`
+ Tertiary *string `json:"tertiary"`
+ } `json:"titles"`
+ Offset struct {
+ NowPlaying bool `json:"now_playing"`
+ } `json:"offset"`
+ } `json:"data"`
}
- err = json.Unmarshal(b[1:len(b)-1], &v)
+ err = json.Unmarshal(b, &v)
if err != nil {
return nil, errors.New("invalid metadata response")
}
- onAir := v.Packages.OnAir
- if onAir.BroadcastNowIndex >= uint(len(onAir.Broadcasts)) {
- return nil, errors.New("no active broadcast")
+ if len(v.Data) == 0 || !v.Data[0].Offset.NowPlaying {
+ return nil, errors.New("no song is playing")
}
- title := onAir.Broadcasts[onAir.BroadcastNowIndex].Title
- for _, rt := range v.Packages.Richtracks {
- if rt.IsNowPlaying {
- title = rt.Artist + " - " + rt.Title + " / " + title
- }
+
+ titles := v.Data[0].Titles
+ parts := []string{}
+ if titles.Tertiary != nil {
+ parts = append(parts, *titles.Tertiary)
+ }
+ if titles.Secondary != nil {
+ parts = append(parts, *titles.Secondary)
}
- return &meta{timeout: v.Timeouts.PollingTimeout, title: title}, nil
+ parts = append(parts, titles.Primary)
+ return &meta{timeout: 5000, title: strings.Join(parts, " - ")}, nil
}
// resolveM3U8 resolves an M3U8 playlist to the first link that seems to
@@ -239,15 +234,15 @@ func proxy(w http.ResponseWriter, req *http.Request) {
panic("cannot hijack connection")
}
- // E.g. `nonuk`, `sbr_low` `bbc_radio_one`, or `uk`, `sbr_high`, `bbc_1xtra`
+ // [ww]/[uk], [48000/96000]/[128000/320000], bbc_radio_one/bbc_1xtra/...
region, quality, name := m[1], m[2], m[3]
- // TODO: We probably shouldn't poll the top level playlist.
- mainPlaylistURL := fmt.Sprintf(targetURI, region, quality, name)
+ mediaPlaylistURL :=
+ fmt.Sprintf(targetURI, region, region, name, name, name, quality)
// This validates the parameters as a side-effect.
- target, err := resolveM3U8(mainPlaylistURL)
- if err == nil && len(target) == 0 {
+ media, err := resolveM3U8(mediaPlaylistURL)
+ if err == nil && len(media) == 0 {
err = errors.New("cannot resolve playlist")
}
if err != nil {
@@ -256,7 +251,7 @@ func proxy(w http.ResponseWriter, req *http.Request) {
}
wantMeta := req.Header.Get("Icy-MetaData") == "1"
- resp, err := http.Head(target[0])
+ resp, err := http.Head(media[0])
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -286,7 +281,7 @@ func proxy(w http.ResponseWriter, req *http.Request) {
go metaProc(req.Context(), name, metaChan)
chunkChan := make(chan []byte)
- go dataProc(req.Context(), mainPlaylistURL, metaint, chunkChan)
+ go dataProc(req.Context(), mediaPlaylistURL, metaint, chunkChan)
// dataProc may return less data near the end of a subfile, so we give it
// a maximum count of bytes to return at once and do our own buffering.