add stream

This commit is contained in:
sentriz
2019-04-05 14:17:59 +01:00
parent dd84d4df27
commit ec53d04730
8 changed files with 74 additions and 52 deletions

View File

@@ -11,8 +11,8 @@ import (
"time" "time"
"github.com/sentriz/gonic/db" "github.com/sentriz/gonic/db"
"github.com/sentriz/gonic/tags"
"github.com/dhowden/tag"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite" _ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/karrick/godirwalk" "github.com/karrick/godirwalk"
@@ -22,11 +22,12 @@ var (
orm *gorm.DB orm *gorm.DB
tx *gorm.DB tx *gorm.DB
cLastAlbum = &lastAlbum{} cLastAlbum = &lastAlbum{}
audioExtensions = map[string]bool{ audioExtensions = map[string]string{
".mp3": true, "mp3": "audio/mpeg",
".flac": true, "flac": "audio/x-flac",
".aac": true, "aac": "audio/x-aac",
".m4a": true, "m4a": "audio/m4a",
"ogg": "audio/ogg",
} }
coverFilenames = map[string]bool{ coverFilenames = map[string]bool{
"cover.png": true, "cover.png": true,
@@ -54,22 +55,21 @@ func (l *lastAlbum) isEmpty() bool {
return l.coverPath == "" return l.coverPath == ""
} }
func isAudio(filename string) bool {
ext := strings.ToLower(filepath.Ext(filename))
_, ok := audioExtensions[ext]
return ok
}
func isCover(filename string) bool { func isCover(filename string) bool {
_, ok := coverFilenames[strings.ToLower(filename)] _, ok := coverFilenames[strings.ToLower(filename)]
return ok return ok
} }
func readTags(fullPath string) (tags.Metadata, error) { func readTags(fullPath string) (tag.Metadata, error) {
tags, err := tags.Read(fullPath) trackData, err := os.Open(fullPath)
if err != nil { if err != nil {
return nil, fmt.Errorf("when tags from disk: %v", err) return nil, fmt.Errorf("when tags from disk: %v", err)
} }
defer trackData.Close()
tags, err := tag.ReadFrom(trackData)
if err != nil {
return nil, err
}
return tags, nil return tags, nil
} }
@@ -112,7 +112,11 @@ func handleFile(fullPath string, info *godirwalk.Dirent) error {
cLastAlbum.coverPath = fullPath // 2nd needed for cover insertion cLastAlbum.coverPath = fullPath // 2nd needed for cover insertion
return nil return nil
} }
if !isAudio(filename) { longExt := filepath.Ext(filename)
extension := strings.ToLower(longExt[1:])
// check if us audio and save mime type for later
mime, ok := audioExtensions[extension]
if !ok {
return nil return nil
} }
// set track basics // set track basics
@@ -128,13 +132,17 @@ func handleFile(fullPath string, info *godirwalk.Dirent) error {
if err != nil { if err != nil {
return fmt.Errorf("when reading tags: %v", err) return fmt.Errorf("when reading tags: %v", err)
} }
trackNumber, totalTracks := tags.Track()
discNumber, TotalDiscs := tags.Disc()
track.Path = fullPath track.Path = fullPath
track.Title = tags.Title() track.Title = tags.Title()
track.DiscNumber = uint(tags.Disc()) track.DiscNumber = uint(discNumber)
track.TotalDiscs = uint(tags.TotalDiscs()) track.TotalDiscs = uint(TotalDiscs)
track.TrackNumber = uint(tags.Track()) track.TotalTracks = uint(totalTracks)
track.TotalTracks = uint(tags.TotalTracks()) track.TrackNumber = uint(trackNumber)
track.Year = uint(tags.Year()) track.Year = uint(tags.Year())
track.Suffix = extension
track.ContentType = mime
// set artist { // set artist {
artist := db.Artist{ artist := db.Artist{
Name: tags.AlbumArtist(), Name: tags.AlbumArtist(),

View File

@@ -40,6 +40,7 @@ func main() {
mux.HandleFunc("/rest/getIndexes.view", withWare(cont.GetIndexes)) mux.HandleFunc("/rest/getIndexes.view", withWare(cont.GetIndexes))
mux.HandleFunc("/rest/getMusicDirectory.view", withWare(cont.GetMusicDirectory)) mux.HandleFunc("/rest/getMusicDirectory.view", withWare(cont.GetMusicDirectory))
mux.HandleFunc("/rest/getCoverArt.view", withWare(cont.GetCoverArt)) mux.HandleFunc("/rest/getCoverArt.view", withWare(cont.GetCoverArt))
mux.HandleFunc("/rest/stream.view", withWare(cont.Stream))
mux.HandleFunc("/rest/getMusicFolders.view", withWare(cont.GetMusicFolders)) mux.HandleFunc("/rest/getMusicFolders.view", withWare(cont.GetMusicFolders))
mux.HandleFunc("/rest/getPlaylists.view", withWare(cont.GetPlaylists)) mux.HandleFunc("/rest/getPlaylists.view", withWare(cont.GetPlaylists))
mux.HandleFunc("/rest/getGenres.view", withWare(cont.GetGenres)) mux.HandleFunc("/rest/getGenres.view", withWare(cont.GetGenres))

View File

@@ -32,6 +32,8 @@ type Track struct {
TotalTracks uint TotalTracks uint
TrackNumber uint TrackNumber uint
Year uint Year uint
Suffix string
ContentType string
Path string `gorm:"not null;unique_index"` Path string `gorm:"not null;unique_index"`
} }

7
go.mod
View File

@@ -3,7 +3,7 @@ module github.com/sentriz/gonic
require ( require (
cloud.google.com/go v0.37.1 // indirect cloud.google.com/go v0.37.1 // indirect
github.com/denisenkom/go-mssqldb v0.0.0-20190315220205-a8ed825ac853 // indirect github.com/denisenkom/go-mssqldb v0.0.0-20190315220205-a8ed825ac853 // indirect
github.com/dhowden/tag v0.0.0-20181104225729-a9f04c2798ca // indirect github.com/dhowden/tag v0.0.0-20181104225729-a9f04c2798ca
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect
github.com/go-sql-driver/mysql v1.4.1 // indirect github.com/go-sql-driver/mysql v1.4.1 // indirect
github.com/gofrs/uuid v3.2.0+incompatible // indirect github.com/gofrs/uuid v3.2.0+incompatible // indirect
@@ -11,15 +11,10 @@ require (
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a // indirect github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a // indirect
github.com/jinzhu/now v1.0.0 // indirect github.com/jinzhu/now v1.0.0 // indirect
github.com/karrick/godirwalk v1.8.0 github.com/karrick/godirwalk v1.8.0
github.com/labstack/gommon v0.2.8 // indirect
github.com/lib/pq v1.0.0 // indirect github.com/lib/pq v1.0.0 // indirect
github.com/mattn/go-colorable v0.1.1 // indirect
github.com/mattn/go-isatty v0.0.7 // indirect
github.com/mattn/go-sqlite3 v1.10.0 // indirect github.com/mattn/go-sqlite3 v1.10.0 // indirect
github.com/myesui/uuid v1.0.0 // indirect github.com/myesui/uuid v1.0.0 // indirect
github.com/twinj/uuid v1.0.0 github.com/twinj/uuid v1.0.0
github.com/valyala/fasttemplate v1.0.1 // indirect
github.com/wtolson/go-taglib v0.0.0-20180718000046-586eb63c2628 // indirect
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576 // indirect golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576 // indirect
google.golang.org/appengine v1.5.0 // indirect google.golang.org/appengine v1.5.0 // indirect
) )

15
go.sum
View File

@@ -66,15 +66,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -102,12 +95,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/twinj/uuid v1.0.0 h1:fzz7COZnDrXGTAOHGuUGYd6sG+JMq+AoE7+Jlu0przk= github.com/twinj/uuid v1.0.0 h1:fzz7COZnDrXGTAOHGuUGYd6sG+JMq+AoE7+Jlu0przk=
github.com/twinj/uuid v1.0.0/go.mod h1:mMgcE1RHFUFqe5AfiwlINXisXfDGro23fWdPUfOMjRY= github.com/twinj/uuid v1.0.0/go.mod h1:mMgcE1RHFUFqe5AfiwlINXisXfDGro23fWdPUfOMjRY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/wtolson/go-taglib v0.0.0-20180718000046-586eb63c2628 h1:hYOyf8es7yvMucqlPar3CdobJeY0+0OmR5iT4hHYW54=
github.com/wtolson/go-taglib v0.0.0-20180718000046-586eb63c2628/go.mod h1:p+WHGfN/a+Ol37Pm7EIOO/6Cylieb2qn1jmKfxtSsUg=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A= go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
@@ -150,8 +137,6 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

View File

@@ -2,7 +2,9 @@ package handler
import ( import (
"fmt" "fmt"
"io"
"net/http" "net/http"
"os"
"strconv" "strconv"
"unicode" "unicode"
@@ -89,16 +91,19 @@ func browseAlbum(c *gorm.DB, album *db.Album) *subsonic.Directory {
dir.Children = make([]subsonic.Child, len(tracks)) dir.Children = make([]subsonic.Child, len(tracks))
for i, track := range tracks { for i, track := range tracks {
dir.Children[i] = subsonic.Child{ dir.Children[i] = subsonic.Child{
ID: track.ID, ID: track.ID,
Title: track.Title, Title: track.Title,
Parent: album.ID, Parent: album.ID,
Artist: artist.Name, Artist: artist.Name,
ArtistID: artist.ID, ArtistID: artist.ID,
Album: album.Title, Album: album.Title,
AlbumID: album.ID, AlbumID: album.ID,
IsDir: false, IsDir: false,
Path: track.Path, Path: track.Path,
CoverArt: cover.ID, CoverArt: cover.ID,
ContentType: track.ContentType,
Suffix: track.Suffix,
Duration: 0,
} }
} }
return &dir return &dir
@@ -143,6 +148,32 @@ func (c *Controller) GetCoverArt(w http.ResponseWriter, req *http.Request) {
w.Write(cover.Image) w.Write(cover.Image)
} }
func (c *Controller) Stream(w http.ResponseWriter, req *http.Request) {
idStr := req.URL.Query().Get("id")
if idStr == "" {
respondError(w, req, 10, "please provide an `id` parameter")
return
}
id, _ := strconv.Atoi(idStr)
var track db.Track
c.DB.First(&track, id)
if track.Path == "" {
respondError(w, req, 70, fmt.Sprintf("media with id `%d` was not found", id))
return
}
file, err := os.Open(track.Path)
if err != nil {
respondError(w, req, 0, fmt.Sprintf("error while streaming media: %v", err))
return
}
stat, _ := file.Stat()
size := strconv.FormatInt(stat.Size(), 10)
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Length", size)
file.Seek(0, 0)
io.Copy(w, file)
}
func (c *Controller) GetMusicFolders(w http.ResponseWriter, req *http.Request) {} func (c *Controller) GetMusicFolders(w http.ResponseWriter, req *http.Request) {}
func (c *Controller) GetPlaylists(w http.ResponseWriter, req *http.Request) {} func (c *Controller) GetPlaylists(w http.ResponseWriter, req *http.Request) {}
func (c *Controller) GetGenres(w http.ResponseWriter, req *http.Request) {} func (c *Controller) GetGenres(w http.ResponseWriter, req *http.Request) {}

View File

@@ -32,7 +32,7 @@ func checkCredentialsOldWay(password, givenPassword string) bool {
func (c *Controller) LogConnection(next http.HandlerFunc) http.HandlerFunc { func (c *Controller) LogConnection(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
log.Printf("connection from %s", r.RemoteAddr) log.Printf("connection from `%s` for `%s`", r.RemoteAddr, r.URL)
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
} }
} }

View File

@@ -90,7 +90,7 @@ type Child struct {
Size uint `xml:"size,attr,omitempty" json:"size,omitempty"` Size uint `xml:"size,attr,omitempty" json:"size,omitempty"`
ContentType string `xml:"contentType,attr,omitempty" json:"contentType,omitempty"` ContentType string `xml:"contentType,attr,omitempty" json:"contentType,omitempty"`
Suffix string `xml:"suffix,attr,omitempty" json:"suffix,omitempty"` Suffix string `xml:"suffix,attr,omitempty" json:"suffix,omitempty"`
Duration uint `xml:"duration,attr,omitempty" json:"duration,omitempty"` Duration uint `xml:"duration,attr,omitempty" json:"duration"`
BitRate uint `xml:"bitRate,attr,omitempty" json:"bitrate,omitempty"` BitRate uint `xml:"bitRate,attr,omitempty" json:"bitrate,omitempty"`
Path string `xml:"path,attr,omitempty" json:"path,omitempty"` Path string `xml:"path,attr,omitempty" json:"path,omitempty"`
} }