Browse Source

chore(release): 1.2.0

Matthias Ladkau 4 years ago
parent
commit
34351abd55
6 changed files with 103 additions and 24 deletions
  1. 9 0
      CHANGELOG.md
  2. 78 14
      playlist/fileplaylist.go
  3. 4 4
      playlist/fileplaylist_test.go
  4. 1 1
      server.go
  5. 5 1
      server/dudeldu.go
  6. 6 4
      server/dudeldu_test.go

+ 9 - 0
CHANGELOG.md

@@ -2,6 +2,15 @@
 
 All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
 
+## [1.2.0](https://devt.de///compare/v1.1.0...v1.2.0) (2019-09-13)
+
+
+### Features
+
+* Playlists can now contain URLs ([523ac35](https://devt.de///commit/523ac35))
+
+
+
 ## 1.1.0 (2019-09-08)
 
 

+ 78 - 14
playlist/fileplaylist.go

@@ -34,6 +34,7 @@ The file ending determines the content type which is send to the client.
 package playlist
 
 import (
+	"bytes"
 	"crypto/tls"
 	"encoding/json"
 	"io"
@@ -80,14 +81,15 @@ var FrameSize = dudeldu.FrameSize
 FilePlaylistFactory data structure
 */
 type FilePlaylistFactory struct {
-	data map[string][]map[string]string
+	data           map[string][]map[string]string
+	itemPathPrefix string
 }
 
 /*
 NewFilePlaylistFactory creates a new FilePlaylistFactory from a given definition
 file.
 */
-func NewFilePlaylistFactory(path string) (*FilePlaylistFactory, error) {
+func NewFilePlaylistFactory(path string, itemPathPrefix string) (*FilePlaylistFactory, error) {
 
 	// Try to read the playlist file
 
@@ -98,7 +100,10 @@ func NewFilePlaylistFactory(path string) (*FilePlaylistFactory, error) {
 
 	// Unmarshal json
 
-	ret := &FilePlaylistFactory{}
+	ret := &FilePlaylistFactory{
+		data:           nil,
+		itemPathPrefix: itemPathPrefix,
+	}
 
 	err = json.Unmarshal(pl, &ret.data)
 
@@ -138,7 +143,7 @@ func (fp *FilePlaylistFactory) Playlist(path string, shuffle bool) dudeldu.Playl
 			data = shuffledData
 		}
 
-		return &FilePlaylist{path, 0, data, nil, false,
+		return &FilePlaylist{path, fp.itemPathPrefix, 0, data, nil, false,
 			&sync.Pool{New: func() interface{} { return make([]byte, FrameSize, FrameSize) }}}
 	}
 	return nil
@@ -148,12 +153,13 @@ func (fp *FilePlaylistFactory) Playlist(path string, shuffle bool) dudeldu.Playl
 FilePlaylist data structure
 */
 type FilePlaylist struct {
-	path      string              // Path of this playlist
-	current   int                 // Pointer to the current playing item
-	data      []map[string]string // Playlist items
-	stream    io.ReadCloser       // Current open stream
-	finished  bool                // Flag if this playlist has finished
-	framePool *sync.Pool          // Pool for byte arrays
+	path       string              // Path of this playlist
+	pathPrefix string              // Prefix for all paths
+	current    int                 // Pointer to the current playing item
+	data       []map[string]string // Playlist items
+	stream     io.ReadCloser       // Current open stream
+	finished   bool                // Flag if this playlist has finished
+	framePool  *sync.Pool          // Pool for byte arrays
 }
 
 /*
@@ -231,7 +237,6 @@ func (fp *FilePlaylist) Frame() ([]byte, error) {
 		for n < len(frame) && err == nil {
 
 			nn, err = fp.stream.Read(frame[n:])
-
 			n += nn
 
 			// Check if we need to read the next file
@@ -293,7 +298,7 @@ func (fp *FilePlaylist) nextFile() error {
 
 	if fp.stream == nil {
 
-		item := fp.currentItem()["path"]
+		item := fp.pathPrefix + fp.currentItem()["path"]
 
 		if _, err = url.ParseRequestURI(item); err == nil {
 			var resp *http.Response
@@ -304,8 +309,11 @@ func (fp *FilePlaylist) nextFile() error {
 				TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
 			}}
 
-			resp, err = client.Get(item)
-			stream = resp.Body
+			if resp, err = client.Get(item); err == nil {
+				buf := &StreamBuffer{}
+				buf.ReadFrom(resp.Body)
+				stream = buf
+			}
 
 		} else {
 
@@ -359,3 +367,59 @@ func (fp *FilePlaylist) Close() error {
 
 	return nil
 }
+
+/*
+StreamBuffer is a buffer which implements io.ReadCloser and can be used to stream
+one stream into another. The buffer detects a potential underflow and waits
+until enough bytes were read from the source stream.
+*/
+type StreamBuffer struct {
+	bytes.Buffer    // Buffer which is used to hold the data
+	readFromOngoing bool
+}
+
+func (b *StreamBuffer) Read(p []byte) (int, error) {
+
+	if b.readFromOngoing && b.Buffer.Len() < len(p) {
+
+		// Prevent buffer underflow and wait until we got enough data for
+		// the next read
+
+		time.Sleep(10 * time.Millisecond)
+		return b.Read(p)
+	}
+
+	n, err := b.Buffer.Read(p)
+
+	// Return EOF if the buffer is empty
+
+	if err == nil {
+		if _, err = b.ReadByte(); err == nil {
+			b.UnreadByte()
+		}
+	}
+
+	return n, err
+}
+
+/*
+ReadFrom reads the source stream into the buffer.
+*/
+func (b *StreamBuffer) ReadFrom(r io.Reader) (int64, error) {
+	b.readFromOngoing = true
+	go func() {
+		b.Buffer.ReadFrom(r)
+		b.readFromOngoing = false
+	}()
+	return 0, nil
+}
+
+/*
+Close does nothing but must be there to implement io.ReadCloser.
+*/
+func (b *StreamBuffer) Close() error {
+
+	// We are in memory so no need to close anything
+
+	return nil
+}

+ 4 - 4
playlist/fileplaylist_test.go

@@ -162,13 +162,13 @@ func TestFilePlaylist(t *testing.T) {
 
 	// Load invalid factory
 
-	_, err = NewFilePlaylistFactory(invalidFileName)
+	_, err = NewFilePlaylistFactory(invalidFileName, "")
 	if err == nil {
 		t.Error(err)
 		return
 	}
 
-	_, err = NewFilePlaylistFactory(pdir + "/test1invalid.json")
+	_, err = NewFilePlaylistFactory(pdir+"/test1invalid.json", "")
 	if err.Error() != "invalid character '*' looking for beginning of value" {
 		t.Error(err)
 		return
@@ -176,7 +176,7 @@ func TestFilePlaylist(t *testing.T) {
 
 	// Create playlist factory
 
-	plf, err := NewFilePlaylistFactory(pdir + "/test1.json")
+	plf, err := NewFilePlaylistFactory(pdir+"/test1.json", "")
 	if err != nil {
 		t.Error(err)
 		return
@@ -467,7 +467,7 @@ func TestFilePlaylist(t *testing.T) {
 
 	// Create playlist factory
 
-	plf, err = NewFilePlaylistFactory(pdir + "/test2.json")
+	plf, err = NewFilePlaylistFactory(pdir+"/test2.json", "")
 	if err != nil {
 		t.Error(err)
 		return

+ 1 - 1
server.go

@@ -22,7 +22,7 @@ import (
 /*
 ProductVersion is the current version of DudelDu
 */
-const ProductVersion = "1.1.0"
+const ProductVersion = "1.2.0"
 
 /*
 ConnectionHandler is a function to handle new connections

+ 5 - 1
server/dudeldu.go

@@ -49,6 +49,7 @@ const (
 	FrameQueueSize = "FrameQueueSize"
 	ServerPort     = "ServerPort"
 	ServerHost     = "ServerHost"
+	PathPrefix     = "PathPrefix"
 )
 
 /*
@@ -59,6 +60,7 @@ var DefaultConfig = map[string]interface{}{
 	FrameQueueSize: 10000,
 	ServerPort:     "9091",
 	ServerHost:     "127.0.0.1",
+	PathPrefix:     "",
 }
 
 type consolelogger func(v ...interface{})
@@ -92,6 +94,7 @@ func main() {
 	serverPort := flag.String("port", DefaultConfig[ServerPort].(string), "Server port to listen on")
 	threadPoolSize := flag.Int("tps", DefaultConfig[ThreadPoolSize].(int), "Thread pool size")
 	frameQueueSize := flag.Int("fqs", DefaultConfig[FrameQueueSize].(int), "Frame queue size")
+	pathPrefix := flag.String("pp", DefaultConfig[PathPrefix].(string), "Prefix all paths with a string")
 	enableDebug := flag.Bool("debug", false, "Enable extra debugging output")
 	loopPlaylist := flag.Bool("loop", false, "Loop playlists")
 	shufflePlaylist := flag.Bool("shuffle", false, "Shuffle playlists")
@@ -118,13 +121,14 @@ func main() {
 	print(fmt.Sprintf("Frame queue size: %v", *frameQueueSize))
 	print(fmt.Sprintf("Loop playlist: %v", *loopPlaylist))
 	print(fmt.Sprintf("Shuffle playlist: %v", *shufflePlaylist))
+	print(fmt.Sprintf("Path prefix: %v", *pathPrefix))
 	if *auth != "" {
 		print(fmt.Sprintf("Required authentication: %v", *auth))
 	}
 
 	// Create server and listen
 
-	plf, err = playlist.NewFilePlaylistFactory(flag.Arg(0))
+	plf, err = playlist.NewFilePlaylistFactory(flag.Arg(0), *pathPrefix)
 
 	if err == nil {
 

+ 6 - 4
server/dudeldu_test.go

@@ -74,7 +74,7 @@ func TestRequestHandlerFilePlaylist(t *testing.T) {
 	ioutil.WriteFile(pdir+"/test2.mp4", []byte("12345"), 0644)
 	ioutil.WriteFile(pdir+"/test3.mp3", []byte("???!!!&&&$$$"), 0644)
 
-	fac, err := playlist.NewFilePlaylistFactory(pdir + "/test.dpl")
+	fac, err := playlist.NewFilePlaylistFactory(pdir+"/test.dpl", "")
 	if err != nil {
 		t.Error(err)
 		return
@@ -87,8 +87,6 @@ func TestRequestHandlerFilePlaylist(t *testing.T) {
 
 	drh.ServeRequest(testConn, "/testpath", true, 2, "")
 
-	fmt.Println(out.String())
-
 	if testConn.Out.String() != ("ICY 200 OK\r\n" +
 		"Content-Type: audio/mpeg\r\n" +
 		"icy-name: /testpath\r\n" +
@@ -149,12 +147,14 @@ Usage of dudeldu [options] <playlist>
     	Loop playlists
   -port string
     	Server port to listen on (default "9091")
+  -pp string
+    	Prefix all paths with a string
   -shuffle
     	Shuffle playlists
   -tps int
     	Thread pool size (default 10)
 ` {
-		t.Error("Unexpected output:", ret, err)
+		t.Error("Unexpected output:", "#"+ret+"#", err)
 		return
 	}
 
@@ -170,6 +170,7 @@ Thread pool size: 10
 Frame queue size: 10000
 Loop playlist: false
 Shuffle playlist: false
+Path prefix: 
 Required authentication: web:web
 listen tcp: invalid port -1
 Shutting down
@@ -180,6 +181,7 @@ Thread pool size: 10
 Frame queue size: 10000
 Loop playlist: false
 Shuffle playlist: false
+Path prefix: 
 Required authentication: web:web
 listen tcp: address -1: invalid port
 Shutting down