Formatting

This commit is contained in:
Rob Emery 2025-02-08 19:14:59 +00:00
parent 335251384f
commit 883d65de14
5 changed files with 97 additions and 90 deletions

View File

@ -163,7 +163,7 @@ func handleArtist(matchResults map[string]string, ret []interface{}, cds *conten
}
return ret, nil
}
return ret,nil
return ret, nil
}
func handleAlbum(matchResults map[string]string, ret []interface{}, cds *contentDirectoryService, o object, host string) ([]interface{}, error) {
@ -195,9 +195,9 @@ func handleGenre(matchResults map[string]string, ret []interface{}, cds *content
//This is never hit as the URL is direct to the streamPath
} else if matchResults["GenreArtist"] != "" {
tracks, err := cds.ds.MediaFile(cds.ctx).GetAll(model.QueryOptions{Filters: squirrel.And{
squirrel.Eq{"genre.id": matchResults["Genre"]},
squirrel.Eq{"artist_id": matchResults["GenreArtist"]},
},
squirrel.Eq{"genre.id": matchResults["Genre"]},
squirrel.Eq{"artist_id": matchResults["GenreArtist"]},
},
})
if err != nil {
fmt.Printf("Error retrieving tracks for artist and genre: %+v", err)

View File

@ -32,13 +32,13 @@ import (
)
const (
serverField = "Linux/3.4 DLNADOC/1.50 UPnP/1.0 DMS/1.0"
rootDescPath = "/rootDesc.xml"
resourcePath = "/r/"
resourceFilePath = "f"
serverField = "Linux/3.4 DLNADOC/1.50 UPnP/1.0 DMS/1.0"
rootDescPath = "/rootDesc.xml"
resourcePath = "/r/"
resourceFilePath = "f"
resourceStreamPath = "s"
resourceArtPath = "a"
serviceControlURL = "/ctl"
resourceArtPath = "a"
serviceControlURL = "/ctl"
)
//go:embed static/*
@ -49,8 +49,8 @@ type DLNAServer struct {
broker events.Broker
ssdp SSDPServer
ctx context.Context
ms core.MediaStreamer
art artwork.Artwork
ms core.MediaStreamer
art artwork.Artwork
}
type SSDPServer struct {
@ -74,8 +74,8 @@ type SSDPServer struct {
// Time interval between SSPD announces
AnnounceInterval time.Duration
ms core.MediaStreamer
art artwork.Artwork
ms core.MediaStreamer
art artwork.Artwork
}
func New(ds model.DataStore, broker events.Broker, mediastreamer core.MediaStreamer, artwork artwork.Artwork) *DLNAServer {
@ -89,10 +89,10 @@ func New(ds model.DataStore, broker events.Broker, mediastreamer core.MediaStrea
ModelNumber: consts.Version,
RootDeviceUUID: makeDeviceUUID("Navidrome"),
waitChan: make(chan struct{}),
ms: mediastreamer,
art: artwork,
ms: mediastreamer,
art: artwork,
},
ms: mediastreamer,
ms: mediastreamer,
art: artwork,
}
@ -293,73 +293,73 @@ func (s *SSDPServer) resourceHandler(w http.ResponseWriter, r *http.Request) {
components := strings.Split(remotePath, "/")
switch components[0] {
case resourceFilePath:
localFile, _ := strings.CutPrefix(remotePath, path.Join(resourceFilePath,"Music/Files"))
localFilePath := path.Join(conf.Server.MusicFolder, localFile)
case resourceFilePath:
localFile, _ := strings.CutPrefix(remotePath, path.Join(resourceFilePath, "Music/Files"))
localFilePath := path.Join(conf.Server.MusicFolder, localFile)
log.Info(fmt.Sprintf("resource handler Executed with remote path: %s, localpath: %s", remotePath, localFilePath))
log.Info(fmt.Sprintf("resource handler Executed with remote path: %s, localpath: %s", remotePath, localFilePath))
fileStats, err := os.Stat(localFilePath)
if err != nil {
http.NotFound(w, r)
return
}
w.Header().Set("Content-Length", strconv.FormatInt(fileStats.Size(), 10))
fileStats, err := os.Stat(localFilePath)
if err != nil {
http.NotFound(w, r)
return
}
w.Header().Set("Content-Length", strconv.FormatInt(fileStats.Size(), 10))
// add some DLNA specific headers
if r.Header.Get("getContentFeatures.dlna.org") != "" {
w.Header().Set("contentFeatures.dlna.org", dms_dlna.ContentFeatures{
SupportRange: true,
}.String())
}
w.Header().Set("transferMode.dlna.org", "Streaming")
// add some DLNA specific headers
if r.Header.Get("getContentFeatures.dlna.org") != "" {
w.Header().Set("contentFeatures.dlna.org", dms_dlna.ContentFeatures{
SupportRange: true,
}.String())
}
w.Header().Set("transferMode.dlna.org", "Streaming")
fileHandle, err := os.Open(localFilePath)
if err != nil {
fmt.Printf("file streaming error: %+v\n", err)
return
}
defer fileHandle.Close()
fileHandle, err := os.Open(localFilePath)
if err != nil {
fmt.Printf("file streaming error: %+v\n", err)
return
}
defer fileHandle.Close()
http.ServeContent(w, r, remotePath, time.Now(), fileHandle)
case resourceStreamPath: //TODO refactor this with stream.go:52?
http.ServeContent(w, r, remotePath, time.Now(), fileHandle)
case resourceStreamPath: //TODO refactor this with stream.go:52?
fileId := components[1]
fileId := components[1]
//TODO figure out format, bitrate
stream, err := s.ms.NewStream(r.Context(), fileId, "mp3", 0, 0)
if err != nil {
log.Error("Error streaming file", "id", fileId, err)
return
}
defer func() {
if err := stream.Close(); err != nil && log.IsGreaterOrEqualTo(log.LevelDebug) {
log.Error("Error closing stream", "id", fileId, "file", stream.Name(), err)
}
}()
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-Content-Duration", strconv.FormatFloat(float64(stream.Duration()), 'G', -1, 32))
http.ServeContent(w, r, stream.Name(), stream.ModTime(), stream)
case resourceArtPath: //TODO refactor this with handle_images.go:39?
artId, err := model.ParseArtworkID(components[1])
if err != nil {
log.Error("Failure to parse ArtworkId", "inputString", components[1], err)
return
}
//TODO size (250)
imgReader, lastUpdate, err := s.art.Get(r.Context(), artId, 250, true)
if err != nil {
log.Error("Failure to retrieve artwork", "artid", artId, err)
return
}
defer imgReader.Close()
w.Header().Set("Cache-Control", "public, max-age=315360000")
w.Header().Set("Last-Modified", lastUpdate.Format(time.RFC1123))
_, err = io.Copy(w, imgReader)
if err != nil {
log.Error("Error writing Artwork Response stream", err)
return
//TODO figure out format, bitrate
stream, err := s.ms.NewStream(r.Context(), fileId, "mp3", 0, 0)
if err != nil {
log.Error("Error streaming file", "id", fileId, err)
return
}
defer func() {
if err := stream.Close(); err != nil && log.IsGreaterOrEqualTo(log.LevelDebug) {
log.Error("Error closing stream", "id", fileId, "file", stream.Name(), err)
}
}()
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-Content-Duration", strconv.FormatFloat(float64(stream.Duration()), 'G', -1, 32))
http.ServeContent(w, r, stream.Name(), stream.ModTime(), stream)
case resourceArtPath: //TODO refactor this with handle_images.go:39?
artId, err := model.ParseArtworkID(components[1])
if err != nil {
log.Error("Failure to parse ArtworkId", "inputString", components[1], err)
return
}
//TODO size (250)
imgReader, lastUpdate, err := s.art.Get(r.Context(), artId, 250, true)
if err != nil {
log.Error("Failure to retrieve artwork", "artid", artId, err)
return
}
defer imgReader.Close()
w.Header().Set("Cache-Control", "public, max-age=315360000")
w.Header().Set("Last-Modified", lastUpdate.Format(time.RFC1123))
_, err = io.Copy(w, imgReader)
if err != nil {
log.Error("Error writing Artwork Response stream", err)
return
}
}
}
@ -423,7 +423,7 @@ func (s *SSDPServer) soapActionResponse(sa upnp.SoapAction, actionRequestXML []b
func (s *SSDPServer) serveHTTP() error {
srv := &http.Server{
Handler: s.handler,
Handler: s.handler,
ReadHeaderTimeout: 10 * time.Second,
}
err := srv.Serve(s.HTTPConn)

View File

@ -39,20 +39,21 @@ type Item struct {
// Object description
type Object struct {
ID string `xml:"id,attr"`
ParentID string `xml:"parentID,attr"`
Restricted int `xml:"restricted,attr"` // indicates whether the object is modifiable
Class string `xml:"upnp:class"`
Icon string `xml:"upnp:icon,omitempty"`
Title string `xml:"dc:title"`
Date Timestamp `xml:"dc:date"`
Artist string `xml:"upnp:artist,omitempty"`
Album string `xml:"upnp:album,omitempty"`
Genre string `xml:"upnp:genre,omitempty"`
AlbumArtURI string `xml:"upnp:albumArtURI,omitempty"`
OriginalTrackNumber int `xml:"upnp:originalTrackNumber,omitempty"`
Searchable int `xml:"searchable,attr"`
ID string `xml:"id,attr"`
ParentID string `xml:"parentID,attr"`
Restricted int `xml:"restricted,attr"` // indicates whether the object is modifiable
Class string `xml:"upnp:class"`
Icon string `xml:"upnp:icon,omitempty"`
Title string `xml:"dc:title"`
Date Timestamp `xml:"dc:date"`
Artist string `xml:"upnp:artist,omitempty"`
Album string `xml:"upnp:album,omitempty"`
Genre string `xml:"upnp:genre,omitempty"`
AlbumArtURI string `xml:"upnp:albumArtURI,omitempty"`
OriginalTrackNumber int `xml:"upnp:originalTrackNumber,omitempty"`
Searchable int `xml:"searchable,attr"`
}
// Timestamp wraps time.Time for formatting purposes
type Timestamp struct {
time.Time

4
go.mod
View File

@ -13,6 +13,7 @@ replace (
require (
github.com/Masterminds/squirrel v1.5.4
github.com/RaveNoX/go-jsoncommentstrip v1.0.0
github.com/anacrolix/dms v1.7.1
github.com/andybalholm/cascadia v1.3.3
github.com/bmatcuk/doublestar/v4 v4.10.0
github.com/bradleyjkemp/cupaloy/v2 v2.8.0
@ -51,6 +52,9 @@ require (
github.com/mileusna/useragent v1.3.5
github.com/onsi/ginkgo/v2 v2.28.1
github.com/onsi/gomega v1.39.1
github.com/onsi/ginkgo/v2 v2.23.4
github.com/onsi/gomega v1.37.0
github.com/oriser/regroup v0.0.0-20240925165441-f6bb0e08289e
github.com/pelletier/go-toml/v2 v2.2.4
github.com/pocketbase/dbx v1.11.0
github.com/pressly/goose/v3 v3.26.0

2
go.sum
View File

@ -121,6 +121,8 @@ github.com/google/go-pipeline v0.0.0-20230411140531-6cbedfc1d3fc/go.mod h1:SL66S
github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef h1:xpF9fUHpoIrrjX24DURVKiwHcFpw19ndIs+FwTSMbno=
github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=