diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index f5bb13a..fe54a2e 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -13,14 +13,13 @@ jobs: steps: - name: Checkout project uses: actions/checkout@v4 - with: - fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v4 with: go-version: '1.22' + - name: Run test + run: make test - name: Build - id: build run: make - name: Create Release uses: ncipollo/release-action@v1 diff --git a/Makefile b/Makefile index e7d1d14..cf6c5db 100644 --- a/Makefile +++ b/Makefile @@ -7,10 +7,13 @@ LINUX_STATIC=xplay_$(VERSION)_linux_static_x64 LD_FLAGS=-s -w -X main.version=$(VERSION) OUTPUT_DIR=build -.PHONY: all windows linux staticlinux +.PHONY: all test windows linux staticlinux default: all +test: + go test --cover -v ./pkg/xspf/ + windows: GOOS=windows GOARCH=amd64 go build -v -o $(OUTPUT_DIR)/$(WINDOWS) -ldflags="$(LD_FLAGS)" ./cmd/xplay diff --git a/cmd/xplay/xplay.go b/cmd/xplay/xplay.go index 0000d07..5cb8f64 100644 --- a/cmd/xplay/xplay.go +++ b/cmd/xplay/xplay.go @@ -2,7 +2,6 @@ package main import ( "flag" - "fmt" "log" "net" "net/http" @@ -20,13 +19,13 @@ const ( var version = "dev" -func validateDir(path string) { +func validateDir(path string, logger *log.Logger) { file, err := os.Stat(path) if err != nil { - panic(err) + logger.Panicln(err) } if !file.IsDir() { - panic("not a directory") + logger.Fatalln("Error: Target is not a directory.") } } @@ -44,21 +43,22 @@ func main() { showVersion := flag.Bool("version", false, "print version and exit") flag.Parse() + logger := log.New(os.Stdout, "", 0) if *showVersion { - fmt.Println(version) + logger.Println(version) return } - validateDir(mediahandler.MediaDir) + validateDir(mediahandler.MediaDir, logger) if *output { if err := mediahandler.WriteToStdout(); err != nil { - panic(err) + logger.Panicln(err) } return } var handler http.Handler - logger := log.New(os.Stdout, "", log.Ldate|log.Ltime) + logger.SetFlags(log.Ldate | log.Ltime) if *password != "" { handler = router.InitAuthRouter(logger, *username, *password) } else { @@ -69,12 +69,12 @@ func main() { if *certFile != "" && *keyFile != "" { logger.Printf("Starting xplay server %s at https://%s/ ...\n", version, addr) if err := http.ListenAndServeTLS(addr, *certFile, *keyFile, handler); err != nil { - panic(err) + logger.Panicln(err) } } else { logger.Printf("Starting xplay server %s at http://%s/ ...\n", version, addr) if err := http.ListenAndServe(addr, handler); err != nil { - panic(err) + logger.Panicln(err) } } } diff --git a/internal/mediahandler/handler.go b/internal/mediahandler/handler.go index 2dc9153..affed0a 100644 --- a/internal/mediahandler/handler.go +++ b/internal/mediahandler/handler.go @@ -48,10 +48,10 @@ func GetMedia(MediaBaseURL, ImageBaseURL *url.URL) (*xspf.PlayList, error) { return nil } if MediaBaseURL.Scheme != "file" && track.ImageExt != "" { - track.ImageURI = ImageBaseURL.JoinPath(path).String() + track.Image = ImageBaseURL.JoinPath(path).String() } } - playList.TrackList.Tracks = append(playList.TrackList.Tracks, *track) + playList.Tracks = append(playList.Tracks, track) return nil }); err != nil { return nil, err diff --git a/internal/router/router.go b/internal/router/router.go index 79a2806..826b045 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -13,11 +13,13 @@ const ( xspfPath = "/play.xspf" mediaBasePath = "/media/" imageBasePath = "/img/" + serverHeader = "xplay" ) func httpHandler(w http.ResponseWriter, _ *http.Request) { mediaBaseUrl, _ := url.Parse(mediaBasePath) imageBaseUrl, _ := url.Parse(imageBasePath) + w.Header().Set("Server", serverHeader) playList, err := mediahandler.GetMedia(mediaBaseUrl, imageBaseUrl) if err != nil { w.WriteHeader(http.StatusInternalServerError) diff --git a/pkg/xspf/xspf.go b/pkg/xspf/xspf.go index 802e93a..e555203 100644 --- a/pkg/xspf/xspf.go +++ b/pkg/xspf/xspf.go @@ -13,13 +13,11 @@ const ( ) type PlayList struct { - Title string `xml:"title,omitempty"` - Creator string `xml:"creator,omitempty"` - Date string `xml:"date,omitempty"` - Annotation string `xml:"annotation,omitempty"` - TrackList struct { - Tracks []Track `xml:"track"` - } `xml:"trackList"` + Title string `xml:"title,omitempty"` + Creator string `xml:"creator,omitempty"` + Date string `xml:"date,omitempty"` + Annotation string `xml:"annotation,omitempty"` + Tracks []*Track `xml:"trackList>track"` } type Track struct { @@ -30,7 +28,7 @@ type Track struct { TrackNum string `xml:"trackNum,omitempty"` Duration string `xml:"duration,omitempty"` ImageExt string `xml:"-"` - ImageURI string `xml:"image,omitempty"` + Image string `xml:"image,omitempty"` Annotation string `xml:"annotation,omitempty"` } diff --git a/pkg/xspf/xspf_test.go b/pkg/xspf/xspf_test.go new file mode 100644 index 0000000..68c84fb --- /dev/null +++ b/pkg/xspf/xspf_test.go @@ -0,0 +1,61 @@ +package xspf + +import ( + "bytes" + "io" + "strconv" + "strings" + "testing" +) + +const testXml = ` + + Playlist + 2005-01-08T17:10:47-05:00 + + + 1.mp3 + 1 + + + 2.mp3 + 2 + + + +` + +func newTestPlaylist() *PlayList { + list := &PlayList{ + Title: "Playlist", + Date: "2005-01-08T17:10:47-05:00", + } + for i := 1; i < 3; i++ { + list.Tracks = append(list.Tracks, &Track{ + Location: strconv.Itoa(i) + ".mp3", Title: strconv.Itoa(i), ImageExt: "jpg", + }) + } + return list +} + +func TestEncodeXspf(t *testing.T) { + buf := bytes.NewBuffer([]byte{}) + if err := EncodeXspf(buf, newTestPlaylist()); err != nil { + t.Fatal(err) + } + if strings.Trim(buf.String(), "\n") != strings.Trim(testXml, "\n") { + t.Errorf("Encoded playlist does not match expected:\n%s", buf.String()) + } +} + +func TestGenerate(t *testing.T) { + if err := Generate(io.Discard, newTestPlaylist()); err != nil { + t.Error(err) + } +} + +func TestBufferedGenerate(t *testing.T) { + if _, err := BufferedGenerate(newTestPlaylist()); err != nil { + t.Error(err) + } +}