-
-
Notifications
You must be signed in to change notification settings - Fork 117
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit e9c0f09
Showing
527 changed files
with
477,627 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
*.db | ||
scanner | ||
server |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"log" | ||
"os" | ||
"path" | ||
"path/filepath" | ||
"strings" | ||
"time" | ||
|
||
"github.com/sentriz/gonic/db" | ||
"github.com/sentriz/gonic/model" | ||
|
||
"github.com/dhowden/tag" | ||
"github.com/jinzhu/gorm" | ||
_ "github.com/jinzhu/gorm/dialects/sqlite" | ||
"github.com/karrick/godirwalk" | ||
) | ||
|
||
var ( | ||
orm *gorm.DB | ||
tx *gorm.DB | ||
cLastAlbum = &lastAlbum{} | ||
audioExtensions = map[string]bool{ | ||
".mp3": true, | ||
".flac": true, | ||
".aac": true, | ||
".m4a": true, | ||
} | ||
coverFilenames = map[string]bool{ | ||
"cover.png": true, | ||
"cover.jpg": true, | ||
"cover.jpeg": true, | ||
"folder.png": true, | ||
"folder.jpg": true, | ||
"folder.jpeg": true, | ||
"album.png": true, | ||
"album.jpg": true, | ||
"album.jpeg": true, | ||
"front.png": true, | ||
"front.jpg": true, | ||
"front.jpeg": true, | ||
} | ||
) | ||
|
||
type lastAlbum struct { | ||
coverModTime time.Time // 1st needed for cover insertion | ||
coverPath string // 2rd needed for cover insertion | ||
id string // 3nd needed for cover insertion | ||
} | ||
|
||
func (l *lastAlbum) isEmpty() bool { | ||
return l.coverPath == "" | ||
} | ||
|
||
func isAudio(filename string) bool { | ||
ext := strings.ToLower(filepath.Ext(filename)) | ||
_, ok := audioExtensions[ext] | ||
return ok | ||
} | ||
|
||
func isCover(filename string) bool { | ||
_, ok := coverFilenames[strings.ToLower(filename)] | ||
return ok | ||
} | ||
|
||
func readTags(fullPath string) (tag.Metadata, error) { | ||
trackData, err := os.Open(fullPath) | ||
if err != nil { | ||
return nil, fmt.Errorf("when tags from disk: %v\n", err) | ||
} | ||
defer trackData.Close() | ||
tags, err := tag.ReadFrom(trackData) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return tags, nil | ||
} | ||
|
||
func handleFolderCompletion(fullPath string, info *godirwalk.Dirent) error { | ||
log.Printf("processed folder `%s`\n", fullPath) | ||
if cLastAlbum.isEmpty() { | ||
return nil | ||
} | ||
cover := model.Cover{ | ||
Path: cLastAlbum.coverPath, | ||
} | ||
err := tx.Where(cover).First(&cover).Error | ||
if !gorm.IsRecordNotFoundError(err) && | ||
!cLastAlbum.coverModTime.After(cover.UpdatedAt) { | ||
return nil | ||
} | ||
image, err := ioutil.ReadFile(cLastAlbum.coverPath) | ||
if err != nil { | ||
return fmt.Errorf("when reading cover: %v\n", err) | ||
} | ||
cover.Image = image | ||
cover.AlbumID = cLastAlbum.id | ||
tx.Save(&cover) | ||
cLastAlbum = &lastAlbum{} | ||
return nil | ||
} | ||
|
||
func processFile(fullPath string, info *godirwalk.Dirent) error { | ||
if info.IsDir() { | ||
return nil | ||
} | ||
stat, err := os.Stat(fullPath) | ||
if err != nil { | ||
return fmt.Errorf("when stating file: %v\n", err) | ||
} | ||
modTime := stat.ModTime() | ||
_, filename := path.Split(fullPath) | ||
if isCover(filename) { | ||
cLastAlbum.coverModTime = modTime // 1st needed for cover insertion | ||
cLastAlbum.coverPath = fullPath // 2nd needed for cover insertion | ||
return nil | ||
} | ||
if !isAudio(filename) { | ||
return nil | ||
} | ||
// set track basics | ||
track := model.Track{ | ||
Path: fullPath, | ||
} | ||
err = tx.Where(track).First(&track).Error | ||
if !gorm.IsRecordNotFoundError(err) && !modTime.After(track.UpdatedAt) { | ||
return nil | ||
} | ||
tags, err := readTags(fullPath) | ||
if err != nil { | ||
return fmt.Errorf("when reading tags: %v\n", err) | ||
} | ||
trackNumber, totalTracks := tags.Track() | ||
discNumber, TotalDiscs := tags.Disc() | ||
track.Path = fullPath | ||
track.Title = tags.Title() | ||
track.DiscNumber = discNumber | ||
track.TotalDiscs = TotalDiscs | ||
track.TotalTracks = totalTracks | ||
track.TrackNumber = trackNumber | ||
track.Year = tags.Year() | ||
// set artist | ||
artist := model.Artist{ | ||
Name: tags.AlbumArtist(), | ||
} | ||
err = tx.Where(artist).First(&artist).Error | ||
if gorm.IsRecordNotFoundError(err) { | ||
artist.Name = tags.AlbumArtist() | ||
tx.Save(&artist) | ||
} | ||
track.ArtistID = artist.ID | ||
// set album | ||
album := model.Album{ | ||
ArtistID: artist.ID, | ||
Title: tags.Album(), | ||
} | ||
err = tx.Where(album).First(&album).Error | ||
if gorm.IsRecordNotFoundError(err) { | ||
album.Title = tags.Album() | ||
album.ArtistID = artist.ID | ||
tx.Save(&album) | ||
} | ||
track.AlbumID = album.ID | ||
// set the _3rd_ variable for cover insertion. | ||
// it will be used by the `handleFolderCompletion` function | ||
cLastAlbum.id = album.ID | ||
// save track | ||
tx.Save(&track) | ||
return nil | ||
} | ||
|
||
func main() { | ||
if len(os.Args) != 2 { | ||
log.Fatalf("usage: %s <path to music>", os.Args[0]) | ||
} | ||
orm = db.New() | ||
orm.SetLogger(log.New(os.Stdout, "gorm ", 0)) | ||
orm.AutoMigrate( | ||
&model.Album{}, | ||
&model.Artist{}, | ||
&model.Track{}, | ||
&model.Cover{}, | ||
) | ||
startTime := time.Now() | ||
tx = orm.Begin() | ||
err := godirwalk.Walk(os.Args[1], &godirwalk.Options{ | ||
Callback: processFile, | ||
PostChildrenCallback: handleFolderCompletion, | ||
Unsorted: true, | ||
}) | ||
if err != nil { | ||
log.Fatalf("error when walking: %v\n", err) | ||
} | ||
tx.Commit() | ||
log.Printf("scanned in %s\n", time.Since(startTime)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
|
||
"github.com/sentriz/gonic/db" | ||
"github.com/sentriz/gonic/handler" | ||
"github.com/sentriz/gonic/router" | ||
) | ||
|
||
func main() { | ||
d := db.New() | ||
r := router.New() | ||
h := &handler.Handler{ | ||
DB: d, | ||
Router: r, | ||
} | ||
rest := r.Group("/rest") | ||
rest.GET("", h.GetTest) | ||
log.Fatal(r.Start("127.0.0.1:5001")) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package db | ||
|
||
import ( | ||
"log" | ||
|
||
"github.com/jinzhu/gorm" | ||
) | ||
|
||
// New creates a new GORM connection to the database | ||
func New() *gorm.DB { | ||
db, err := gorm.Open("sqlite3", "gonic.db") | ||
if err != nil { | ||
log.Printf("when opening database: %v\n", err) | ||
} | ||
return db | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
module github.com/sentriz/gonic | ||
|
||
require ( | ||
cloud.google.com/go v0.37.1 // indirect | ||
github.com/denisenkom/go-mssqldb v0.0.0-20190315220205-a8ed825ac853 // indirect | ||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect | ||
github.com/dhowden/tag v0.0.0-20181104225729-a9f04c2798ca | ||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect | ||
github.com/go-sql-driver/mysql v1.4.1 // indirect | ||
github.com/gofrs/uuid v3.2.0+incompatible // indirect | ||
github.com/jinzhu/gorm v1.9.2 | ||
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a // indirect | ||
github.com/jinzhu/now v1.0.0 // indirect | ||
github.com/karrick/godirwalk v1.8.0 | ||
github.com/labstack/echo v3.3.10+incompatible | ||
github.com/labstack/gommon v0.2.8 // 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/myesui/uuid v1.0.0 // indirect | ||
github.com/stretchr/testify v1.3.0 // indirect | ||
github.com/twinj/uuid v1.0.0 | ||
github.com/valyala/fasttemplate v1.0.1 // indirect | ||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576 // indirect | ||
google.golang.org/appengine v1.5.0 // indirect | ||
) |
Oops, something went wrong.