57
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								main.go
									
									
									
									
									
								
							@@ -23,6 +23,7 @@ import (
 | 
				
			|||||||
const (
 | 
					const (
 | 
				
			||||||
	targetURI = "http://as-hls-%s-live.akamaized.net/pool_904/live/%s/" +
 | 
						targetURI = "http://as-hls-%s-live.akamaized.net/pool_904/live/%s/" +
 | 
				
			||||||
		"%s/%s.isml/%s-audio%%3d%s.norewind.m3u8"
 | 
							"%s/%s.isml/%s-audio%%3d%s.norewind.m3u8"
 | 
				
			||||||
 | 
						networksURI = "https://rms.api.bbc.co.uk/radio/networks.json"
 | 
				
			||||||
	metaURI     = "https://rms.api.bbc.co.uk/v2/services/%s/segments/latest"
 | 
						metaURI     = "https://rms.api.bbc.co.uk/v2/services/%s/segments/latest"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -31,6 +32,42 @@ type meta struct {
 | 
				
			|||||||
	timeout uint   // timeout for the next poll in ms
 | 
						timeout uint   // timeout for the next poll in ms
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getServiceTitle returns a human-friendly identifier for a BBC service ID.
 | 
				
			||||||
 | 
					func getServiceTitle(name string) (string, error) {
 | 
				
			||||||
 | 
						resp, err := http.Get(networksURI)
 | 
				
			||||||
 | 
						if resp != nil {
 | 
				
			||||||
 | 
							defer resp.Body.Close()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return name, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b, err := ioutil.ReadAll(resp.Body)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var v struct {
 | 
				
			||||||
 | 
							Results []struct {
 | 
				
			||||||
 | 
								Services []struct {
 | 
				
			||||||
 | 
									ID    string `json:"id"`
 | 
				
			||||||
 | 
									Title string `json:"title"`
 | 
				
			||||||
 | 
								} `json:"services"`
 | 
				
			||||||
 | 
							} `json:"results"`
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = json.Unmarshal(b, &v)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return name, errors.New("invalid metadata response")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, network := range v.Results {
 | 
				
			||||||
 | 
							for _, service := range network.Services {
 | 
				
			||||||
 | 
								if service.ID == name {
 | 
				
			||||||
 | 
									return service.Title, nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return name, errors.New("unknown service")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var errNoSong = errors.New("no song is playing")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getMeta retrieves and decodes metadata info from an independent webservice.
 | 
					// getMeta retrieves and decodes metadata info from an independent webservice.
 | 
				
			||||||
func getMeta(name string) (*meta, error) {
 | 
					func getMeta(name string) (*meta, error) {
 | 
				
			||||||
	resp, err := http.Get(fmt.Sprintf(metaURI, name))
 | 
						resp, err := http.Get(fmt.Sprintf(metaURI, name))
 | 
				
			||||||
@@ -41,6 +78,9 @@ func getMeta(name string) (*meta, error) {
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	b, err := ioutil.ReadAll(resp.Body)
 | 
						b, err := ioutil.ReadAll(resp.Body)
 | 
				
			||||||
 | 
						if os.Getenv("DEBUG") != "" {
 | 
				
			||||||
 | 
							log.Println(string(b))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: update more completely for the new OpenAPI
 | 
						// TODO: update more completely for the new OpenAPI
 | 
				
			||||||
	//  - `broadcasts/poll/bbc_radio_one` looks almost useful
 | 
						//  - `broadcasts/poll/bbc_radio_one` looks almost useful
 | 
				
			||||||
@@ -63,7 +103,7 @@ func getMeta(name string) (*meta, error) {
 | 
				
			|||||||
		return nil, errors.New("invalid metadata response")
 | 
							return nil, errors.New("invalid metadata response")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(v.Data) == 0 || !v.Data[0].Offset.NowPlaying {
 | 
						if len(v.Data) == 0 || !v.Data[0].Offset.NowPlaying {
 | 
				
			||||||
		return nil, errors.New("no song is playing")
 | 
							return nil, errNoSong
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	titles := v.Data[0].Titles
 | 
						titles := v.Data[0].Titles
 | 
				
			||||||
@@ -124,9 +164,10 @@ func metaProc(ctx context.Context, name string, out chan<- string) {
 | 
				
			|||||||
	var interval time.Duration
 | 
						var interval time.Duration
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		meta, err := getMeta(name)
 | 
							meta, err := getMeta(name)
 | 
				
			||||||
		if err != nil {
 | 
							if err == errNoSong {
 | 
				
			||||||
			current = name + " - " + err.Error()
 | 
								interval, current = maxInterval, ""
 | 
				
			||||||
			interval = maxInterval
 | 
							} else if err != nil {
 | 
				
			||||||
 | 
								interval, current = maxInterval, err.Error()
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			current = meta.title
 | 
								current = meta.title
 | 
				
			||||||
			interval = time.Duration(meta.timeout) * time.Millisecond
 | 
								interval = time.Duration(meta.timeout) * time.Millisecond
 | 
				
			||||||
@@ -263,11 +304,10 @@ func proxy(w http.ResponseWriter, req *http.Request) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	defer conn.Close()
 | 
						defer conn.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: Retrieve some general information from somewhere?
 | 
						serviceTitle, _ := getServiceTitle(name)
 | 
				
			||||||
	// There's nothing interesting in the playlist files.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fmt.Fprintf(bufrw, "ICY 200 OK\r\n")
 | 
						fmt.Fprintf(bufrw, "ICY 200 OK\r\n")
 | 
				
			||||||
	fmt.Fprintf(bufrw, "icy-name:%s\r\n", name)
 | 
						fmt.Fprintf(bufrw, "icy-name:%s\r\n", serviceTitle)
 | 
				
			||||||
	// BBC marks this as a video type, maybe just force audio/mpeg.
 | 
						// BBC marks this as a video type, maybe just force audio/mpeg.
 | 
				
			||||||
	fmt.Fprintf(bufrw, "content-type:%s\r\n", resp.Header["Content-Type"][0])
 | 
						fmt.Fprintf(bufrw, "content-type:%s\r\n", resp.Header["Content-Type"][0])
 | 
				
			||||||
	fmt.Fprintf(bufrw, "icy-pub:%d\r\n", 0)
 | 
						fmt.Fprintf(bufrw, "icy-pub:%d\r\n", 0)
 | 
				
			||||||
@@ -300,6 +340,9 @@ func proxy(w http.ResponseWriter, req *http.Request) {
 | 
				
			|||||||
	for {
 | 
						for {
 | 
				
			||||||
		select {
 | 
							select {
 | 
				
			||||||
		case title := <-metaChan:
 | 
							case title := <-metaChan:
 | 
				
			||||||
 | 
								if title == "" {
 | 
				
			||||||
 | 
									title = serviceTitle
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			queuedMetaUpdate = []byte(fmt.Sprintf("StreamTitle='%s'",
 | 
								queuedMetaUpdate = []byte(fmt.Sprintf("StreamTitle='%s'",
 | 
				
			||||||
				strings.Replace(title, "'", "’", -1)))
 | 
									strings.Replace(title, "'", "’", -1)))
 | 
				
			||||||
		case chunk, ok := <-chunkChan:
 | 
							case chunk, ok := <-chunkChan:
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user