diff --git a/README.md b/README.md index 13592978..12dac0bc 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,16 @@ $ gopm get -v -g From here it's a normal [BeeGo](http://beego.me) development cycle. Some useful commands: -* Start local server with `bee run` -* Start test runner on the browser with `NOLOG=1 goconvey --port 9090` -* Test from command line with `go test ./... -v` +```bash +# Start local server (with hot reload) +$ bee run + +# Start test runner on the browser +$ NOLOG=1 goconvey --port 9090 + +# Runa all tests +$ go test ./... -v +``` ### Useful Links diff --git a/conf/app.conf b/conf/app.conf index c3418695..2713c092 100644 --- a/conf/app.conf +++ b/conf/app.conf @@ -5,7 +5,10 @@ autoRender = false copyRequestBody = true apiVersion = 1.0.0 -musicFolder=./iTunes.xml +ignoredArticles="The El La Los Las Le Les Os As O A" +indexGroups=A B C D E F G H I J K L M N O P Q R S T U V W X-Z(XYZ) + +musicFolder=./iTunesFull.xml user=deluan password=wordpass dbPath = ./devDb @@ -21,4 +24,4 @@ httpPort = 8081 enableAdmin = false user=deluan password=wordpass -dbPath = ./tmp/testDb \ No newline at end of file +dbPath = /tmp/testDb \ No newline at end of file diff --git a/models/artist.go b/models/artist.go index 9c9360ce..7637bcff 100644 --- a/models/artist.go +++ b/models/artist.go @@ -1,6 +1,22 @@ package models +import ( + "strings" + "github.com/astaxie/beego" +) + type Artist struct { Id string Name string +} + +func NoArticle(name string) string { + articles := strings.Split(beego.AppConfig.String("ignoredArticles"), " ") + for _, a := range articles { + n := strings.TrimPrefix(name, a + " ") + if (n != name) { + return n + } + } + return name } \ No newline at end of file diff --git a/models/media_file.go b/models/media_file.go index 9e9b8ca6..900e4920 100644 --- a/models/media_file.go +++ b/models/media_file.go @@ -1,13 +1,27 @@ package models -import "time" +import ( + "time" +) type MediaFile struct { - Id string - Path string - Album string - Artist string - Title string - CreatedAt time.Time - UpdatedAt time.Time + Id string + Path string + Title string + Album string + Artist string + AlbumArtist string + Compilation bool + CreatedAt time.Time + UpdatedAt time.Time +} + +func (m*MediaFile) RealArtist() string { + if (m.Compilation) { + return "Various Artists" + } + if (m.AlbumArtist != "") { + return m.AlbumArtist + } + return m.Artist } \ No newline at end of file diff --git a/repositories/base_repository.go b/repositories/base_repository.go index 0fdec44b..362c7f8b 100644 --- a/repositories/base_repository.go +++ b/repositories/base_repository.go @@ -56,7 +56,7 @@ func (r *BaseRepository) saveOrUpdate(rec interface{}) error { if err != nil { return err } - docId, err := r.queryFirstKey(`{"in": ["Id"], "eq": "%s"}`, m["Id"]) + docId, err := r.queryFirstKey(`{"in": ["Id"], "eq": "%s", "limit": 1}`, m["Id"]) if docId == 0 { _, err = r.col.Insert(m) return err diff --git a/repositories/init_database.go b/repositories/init_database.go index 42d66212..06064ba4 100644 --- a/repositories/init_database.go +++ b/repositories/init_database.go @@ -11,20 +11,38 @@ var ( once sync.Once ) -func createCollection(name string) *db.Col { +func createCollection(name string, ix ...interface{}) *db.Col { + log := false + if dbInstance().Use(name) == nil { + if err := dbInstance().Create(name); err != nil { + beego.Error(err) + } + log = true + } + col := dbInstance().Use(name) - if col != nil { - return col - } - if err := dbInstance().Create(name); err != nil { - beego.Error(err) - } - if err := col.Index([]string{"Id"}); err != nil { - beego.Error(name, err) + + createIndex(col, []string{"Id"}, log) + for _, i := range ix { + switch i := i.(type) { + case string: + createIndex(col, []string{i}, log) + case []string: + createIndex(col, i, log) + default: + beego.Error("Trying to create an Index with an invalid type: ", i) + } } return col } +func createIndex(col *db.Col, path []string, log bool) (err error) { + if err := col.Index(path); err != nil && log { + beego.Error(path, err) + } + return err +} + func dbInstance() *db.DB { once.Do(func() { instance, err := db.OpenDB(beego.AppConfig.String("dbPath")) diff --git a/repositories/init_database_test.go b/repositories/init_database_test.go new file mode 100644 index 00000000..2e06a4d4 --- /dev/null +++ b/repositories/init_database_test.go @@ -0,0 +1,54 @@ +package repositories + +import ( + . "github.com/smartystreets/goconvey/convey" + _ "github.com/deluan/gosonic/tests" + "testing" +) + +const ( + testCollectionName = "TestCollection" +) + +func TestCreateCollection(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + dbInstance().Drop(testCollectionName) + + Convey("Given an empty DB", t, func() { + + Convey("When creating a new collection", func() { + newCol := createCollection(testCollectionName) + + Convey("Then it should create the collection", func() { + So(dbInstance().Use(testCollectionName), ShouldNotBeNil) + }) + Convey("And it should create a default index on Id", func() { + allIndexes := newCol.AllIndexes() + So(len(allIndexes), ShouldEqual, 1) + So(len(allIndexes[0]), ShouldEqual, 1) + So(allIndexes[0], ShouldContain, "Id") + }) + }) + + Convey("When creating a new collection with a 'Name' index", func() { + newCol := createCollection(testCollectionName, "Name") + + Convey("Then it should create a default index [Id] and an index on 'Name'", func() { + listOfIndexes := newCol.AllIndexes() + + So(len(listOfIndexes), ShouldEqual, 2) + + var allPaths = make(map[string]bool) + for _, i := range listOfIndexes { + allPaths[i[0]] = true + } + + So(allPaths, ShouldContainKey, "Id") + So(allPaths, ShouldContainKey, "Name") + }) + }) + }) +} \ No newline at end of file diff --git a/scanner/itunes_scanner.go b/scanner/itunes_scanner.go index f8c1ec38..ecc7f48a 100644 --- a/scanner/itunes_scanner.go +++ b/scanner/itunes_scanner.go @@ -21,6 +21,8 @@ func (s *ItunesScanner) LoadFolder(path string) []Track { mediaFiles[i].Album = t.Album mediaFiles[i].Title = t.Name mediaFiles[i].Artist = t.Artist + mediaFiles[i].AlbumArtist = t.AlbumArtist + mediaFiles[i].Compilation = t.Compilation path, _ = url.QueryUnescape(t.Location) mediaFiles[i].Path = strings.TrimPrefix(path, "file://") mediaFiles[i].CreatedAt = t.DateAdded diff --git a/scanner/scanner.go b/scanner/scanner.go index be690d79..5a6884f2 100644 --- a/scanner/scanner.go +++ b/scanner/scanner.go @@ -4,6 +4,9 @@ import ( "github.com/astaxie/beego" "github.com/deluan/gosonic/repositories" "github.com/deluan/gosonic/models" + "strings" + "fmt" + "encoding/json" ) type Scanner interface { @@ -23,12 +26,15 @@ func doImport(mediaFolder string, scanner Scanner) { func updateDatastore(files []Track) { mfRepo := repositories.NewMediaFileRepository() + var artistIndex = make(map[string]map[string]string) for _, t := range files { m := &models.MediaFile{ Id: t.Id, Album: t.Album, Artist: t.Artist, + AlbumArtist: t.AlbumArtist, Title: t.Title, + Compilation: t.Compilation, Path: t.Path, CreatedAt: t.CreatedAt, UpdatedAt: t.UpdatedAt, @@ -37,6 +43,24 @@ func updateDatastore(files []Track) { if err != nil { beego.Error(err) } + collectIndex(m, artistIndex) } - mfRepo.Dump() + //mfRepo.Dump() + j,_ := json.MarshalIndent(artistIndex, "", " ") + fmt.Println(string(j)) +} + +func collectIndex(m *models.MediaFile, artistIndex map[string]map[string]string) { + name := m.RealArtist() + indexName := strings.ToLower(models.NoArticle(name)) + if indexName == "" { + return + } + initial := strings.ToUpper(indexName[0:1]) + artists := artistIndex[initial] + if artists == nil { + artists = make(map[string]string) + artistIndex[initial] = artists + } + artists[indexName] = name } \ No newline at end of file diff --git a/scanner/track.go b/scanner/track.go index c87e3721..478c8a99 100644 --- a/scanner/track.go +++ b/scanner/track.go @@ -5,11 +5,13 @@ import ( ) type Track struct { - Id string - Path string - Album string - Artist string - Title string - CreatedAt time.Time - UpdatedAt time.Time + Id string + Path string + Title string + Album string + Artist string + AlbumArtist string + Compilation bool + CreatedAt time.Time + UpdatedAt time.Time } \ No newline at end of file diff --git a/tests/api/get_license_test.go b/tests/api/get_license_test.go index 6c4ee16e..df07b799 100644 --- a/tests/api/get_license_test.go +++ b/tests/api/get_license_test.go @@ -1,4 +1,4 @@ -package api_test +package api import ( "encoding/xml" diff --git a/tests/api/get_music_folders_test.go b/tests/api/get_music_folders_test.go index 7901bf22..717c0979 100644 --- a/tests/api/get_music_folders_test.go +++ b/tests/api/get_music_folders_test.go @@ -1,4 +1,4 @@ -package api_test +package api import ( _ "github.com/deluan/gosonic/routers" diff --git a/tests/api/ping_test.go b/tests/api/ping_test.go index 4cda8086..b0e65263 100644 --- a/tests/api/ping_test.go +++ b/tests/api/ping_test.go @@ -1,4 +1,4 @@ -package api_test +package api import ( "encoding/xml" diff --git a/tests/api/validation_test.go b/tests/api/validation_test.go index bb859ec5..0d840a68 100644 --- a/tests/api/validation_test.go +++ b/tests/api/validation_test.go @@ -1,4 +1,4 @@ -package api_test +package api import ( "encoding/xml"