remove globals
This commit is contained in:
@@ -236,6 +236,7 @@ type Flash struct {
|
||||
Type FlashType
|
||||
}
|
||||
|
||||
//nolint:gochecknoinits
|
||||
func init() {
|
||||
gob.Register(&Flash{})
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ func (c *Controller) ServeHome(r *http.Request) *Response {
|
||||
c.DB.
|
||||
Where("user_id=?", user.ID).
|
||||
Find(&data.TranscodePreferences)
|
||||
for profile := range encode.Profiles {
|
||||
for profile := range encode.Profiles() {
|
||||
data.TranscodeProfiles = append(data.TranscodeProfiles, profile)
|
||||
}
|
||||
//
|
||||
|
||||
@@ -72,7 +72,7 @@ func serveTrackRaw(w http.ResponseWriter, r *http.Request, opts serveTrackOption
|
||||
}
|
||||
|
||||
func serveTrackEncode(w http.ResponseWriter, r *http.Request, opts serveTrackOptions) {
|
||||
profile := encode.Profiles[opts.pref.Profile]
|
||||
profile := encode.Profiles()[opts.pref.Profile]
|
||||
bitrate := encode.GetBitrate(opts.maxBitrate, profile)
|
||||
trackPath := path.Join(opts.musicPath, opts.track.RelPath())
|
||||
cacheKey := encode.CacheKey(trackPath, opts.pref.Profile, bitrate)
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"go.senan.xyz/gonic/version"
|
||||
)
|
||||
|
||||
var (
|
||||
const (
|
||||
apiVersion = "1.9.0"
|
||||
xmlns = "http://subsonic.org/restapi"
|
||||
)
|
||||
|
||||
@@ -31,9 +31,8 @@ func wrapMigrations(migrs ...gormigrate.Migration) []*gormigrate.Migration {
|
||||
return ret
|
||||
}
|
||||
|
||||
var (
|
||||
dbMaxOpenConns = 1
|
||||
dbOptions = url.Values{
|
||||
func defaultOptions() url.Values {
|
||||
return url.Values{
|
||||
// with this, multiple connections share a single data and schema cache.
|
||||
// see https://www.sqlite.org/sharedcache.html
|
||||
"cache": {"shared"},
|
||||
@@ -43,7 +42,7 @@ var (
|
||||
"_journal_mode": {"WAL"},
|
||||
"_foreign_keys": {"true"},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
type DB struct {
|
||||
*gorm.DB
|
||||
@@ -55,13 +54,13 @@ func New(path string) (*DB, error) {
|
||||
Scheme: "file",
|
||||
Opaque: path,
|
||||
}
|
||||
url.RawQuery = dbOptions.Encode()
|
||||
url.RawQuery = defaultOptions().Encode()
|
||||
db, err := gorm.Open("sqlite3", url.String())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("with gorm: %w", err)
|
||||
}
|
||||
db.SetLogger(log.New(os.Stdout, "gorm ", 0))
|
||||
db.DB().SetMaxOpenConns(dbMaxOpenConns)
|
||||
db.DB().SetMaxOpenConns(1)
|
||||
migrOptions := &gormigrate.Options{
|
||||
TableName: "migrations",
|
||||
IDColumnName: "id",
|
||||
@@ -69,13 +68,13 @@ func New(path string) (*DB, error) {
|
||||
UseTransaction: false,
|
||||
}
|
||||
migr := gormigrate.New(db, migrOptions, wrapMigrations(
|
||||
migrationInitSchema,
|
||||
migrationCreateInitUser,
|
||||
migrationMergePlaylist,
|
||||
migrationCreateTranscode,
|
||||
migrationAddGenre,
|
||||
migrationUpdateTranscodePrefIDX,
|
||||
migrationAddAlbumIDX,
|
||||
migrateInitSchema(),
|
||||
migrateCreateInitUser(),
|
||||
migrateMergePlaylist(),
|
||||
migrateCreateTranscode(),
|
||||
migrateAddGenre(),
|
||||
migrateUpdateTranscodePrefIDX(),
|
||||
migrateAddAlbumIDX(),
|
||||
))
|
||||
if err = migr.Migrate(); err != nil {
|
||||
return nil, fmt.Errorf("migrating to latest version: %w", err)
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
// $ date '+%Y%m%d%H%M'
|
||||
|
||||
// not really a migration
|
||||
var migrationInitSchema = gormigrate.Migration{
|
||||
func migrateInitSchema() gormigrate.Migration {
|
||||
return gormigrate.Migration{
|
||||
ID: "202002192100",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.AutoMigrate(
|
||||
@@ -25,10 +25,11 @@ var migrationInitSchema = gormigrate.Migration{
|
||||
).
|
||||
Error
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// not really a migration
|
||||
var migrationCreateInitUser = gormigrate.Migration{
|
||||
func migrateCreateInitUser() gormigrate.Migration {
|
||||
return gormigrate.Migration{
|
||||
ID: "202002192019",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
const (
|
||||
@@ -49,9 +50,11 @@ var migrationCreateInitUser = gormigrate.Migration{
|
||||
}).
|
||||
Error
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var migrationMergePlaylist = gormigrate.Migration{
|
||||
func migrateMergePlaylist() gormigrate.Migration {
|
||||
return gormigrate.Migration{
|
||||
ID: "202002192222",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if !tx.HasTable("playlist_items") {
|
||||
@@ -69,9 +72,11 @@ var migrationMergePlaylist = gormigrate.Migration{
|
||||
).
|
||||
Error
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var migrationCreateTranscode = gormigrate.Migration{
|
||||
func migrateCreateTranscode() gormigrate.Migration {
|
||||
return gormigrate.Migration{
|
||||
ID: "202003111222",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.AutoMigrate(
|
||||
@@ -79,9 +84,11 @@ var migrationCreateTranscode = gormigrate.Migration{
|
||||
).
|
||||
Error
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var migrationAddGenre = gormigrate.Migration{
|
||||
func migrateAddGenre() gormigrate.Migration {
|
||||
return gormigrate.Migration{
|
||||
ID: "202003121330",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.AutoMigrate(
|
||||
@@ -91,9 +98,11 @@ var migrationAddGenre = gormigrate.Migration{
|
||||
).
|
||||
Error
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var migrationUpdateTranscodePrefIDX = gormigrate.Migration{
|
||||
func migrateUpdateTranscodePrefIDX() gormigrate.Migration {
|
||||
return gormigrate.Migration{
|
||||
ID: "202003241509",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
var hasIDX int
|
||||
@@ -130,9 +139,11 @@ var migrationUpdateTranscodePrefIDX = gormigrate.Migration{
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var migrationAddAlbumIDX = gormigrate.Migration{
|
||||
func migrateAddAlbumIDX() gormigrate.Migration {
|
||||
return gormigrate.Migration{
|
||||
ID: "202004302006",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.AutoMigrate(
|
||||
@@ -140,4 +151,5 @@ var migrationAddAlbumIDX = gormigrate.Migration{
|
||||
).
|
||||
Error
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,8 +94,8 @@ func (t *Track) Ext() string {
|
||||
}
|
||||
|
||||
func (t *Track) MIME() string {
|
||||
ext := t.Ext()
|
||||
return mime.Types[ext]
|
||||
v, _ := mime.FromExtension(t.Ext())
|
||||
return v
|
||||
}
|
||||
|
||||
func (t *Track) RelPath() string {
|
||||
|
||||
@@ -13,6 +13,10 @@ import (
|
||||
"github.com/cespare/xxhash"
|
||||
)
|
||||
|
||||
const (
|
||||
buffLen = 4096
|
||||
)
|
||||
|
||||
type Profile struct {
|
||||
Format string
|
||||
Bitrate int
|
||||
@@ -20,15 +24,14 @@ type Profile struct {
|
||||
forceRG bool
|
||||
}
|
||||
|
||||
var (
|
||||
Profiles = map[string]*Profile{
|
||||
func Profiles() map[string]Profile {
|
||||
return map[string]Profile{
|
||||
"mp3": {"mp3", 128, []string{"-c:a", "libmp3lame"}, false},
|
||||
"mp3_rg": {"mp3", 128, []string{"-c:a", "libmp3lame"}, true},
|
||||
"opus": {"opus", 96, []string{"-c:a", "libopus", "-vbr", "constrained"}, false},
|
||||
"opus_rg": {"opus", 96, []string{"-c:a", "libopus", "-vbr", "constrained"}, true},
|
||||
}
|
||||
bufLen = 4096
|
||||
)
|
||||
}
|
||||
|
||||
// copy command output to http response body using io.copy (simpler, but may increase ttfb)
|
||||
//nolint:deadcode,unused
|
||||
@@ -45,7 +48,7 @@ func copyCmdOutput(out, cache io.Writer, pipeReader io.Reader) {
|
||||
// copy command output to http response manually with a buffer (should reduce ttfb)
|
||||
//nolint:deadcode,unused
|
||||
func writeCmdOutput(out, cache io.Writer, pipeReader io.ReadCloser) {
|
||||
buffer := make([]byte, bufLen)
|
||||
buffer := make([]byte, buffLen)
|
||||
for {
|
||||
n, err := pipeReader.Read(buffer)
|
||||
if err != nil {
|
||||
@@ -70,7 +73,7 @@ func writeCmdOutput(out, cache io.Writer, pipeReader io.ReadCloser) {
|
||||
}
|
||||
|
||||
// pre-format the ffmpeg command with needed options
|
||||
func ffmpegCommand(filePath string, profile *Profile, bitrate string) *exec.Cmd {
|
||||
func ffmpegCommand(filePath string, profile Profile, bitrate string) *exec.Cmd {
|
||||
args := []string{
|
||||
"-v", "0",
|
||||
"-i", filePath,
|
||||
@@ -94,7 +97,7 @@ func ffmpegCommand(filePath string, profile *Profile, bitrate string) *exec.Cmd
|
||||
return exec.Command("/usr/bin/ffmpeg", args...) //nolint:gosec
|
||||
}
|
||||
|
||||
func Encode(out io.Writer, trackPath, cachePath string, profile *Profile, bitrate string) error {
|
||||
func Encode(out io.Writer, trackPath, cachePath string, profile Profile, bitrate string) error {
|
||||
// prepare the command and file descriptors
|
||||
cmd := ffmpegCommand(trackPath, profile, bitrate)
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
@@ -126,13 +129,13 @@ func Encode(out io.Writer, trackPath, cachePath string, profile *Profile, bitrat
|
||||
|
||||
// CacheKey generates the filename for the new transcode save
|
||||
func CacheKey(sourcePath string, profile, bitrate string) string {
|
||||
format := Profiles[profile].Format
|
||||
format := Profiles()[profile].Format
|
||||
hash := xxhash.Sum64String(sourcePath)
|
||||
return fmt.Sprintf("%x-%s-%s.%s", hash, profile, bitrate, format)
|
||||
}
|
||||
|
||||
// GetBitrate checks if the client forces bitrate lower than set in profile
|
||||
func GetBitrate(clientBitrate int, profile *Profile) string {
|
||||
func GetBitrate(clientBitrate int, profile Profile) string {
|
||||
bitrate := profile.Bitrate
|
||||
if clientBitrate != 0 && clientBitrate < bitrate {
|
||||
bitrate = clientBitrate
|
||||
|
||||
@@ -10,16 +10,15 @@ import (
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"go.senan.xyz/gonic/server/db"
|
||||
)
|
||||
|
||||
var (
|
||||
const (
|
||||
baseURL = "https://ws.audioscrobbler.com/2.0/"
|
||||
client = &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
ErrLastFM = errors.New("last.fm error")
|
||||
)
|
||||
|
||||
@@ -27,7 +26,7 @@ var (
|
||||
|
||||
func getParamSignature(params url.Values, secret string) string {
|
||||
// the parameters must be in order before hashing
|
||||
paramKeys := make([]string, 0)
|
||||
paramKeys := make([]string, 0, len(params))
|
||||
for k := range params {
|
||||
paramKeys = append(paramKeys, k)
|
||||
}
|
||||
@@ -45,7 +44,7 @@ func getParamSignature(params url.Values, secret string) string {
|
||||
func makeRequest(method string, params url.Values) (LastFM, error) {
|
||||
req, _ := http.NewRequest(method, baseURL, nil)
|
||||
req.URL.RawQuery = params.Encode()
|
||||
resp, err := client.Do(req)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return LastFM{}, fmt.Errorf("get: %w", err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package mime
|
||||
|
||||
var Types = map[string]string{
|
||||
// this package is at such a high level in the hierarchy because
|
||||
// it's used by both `server/db` and `server/scanner`
|
||||
|
||||
func FromExtension(ext string) (string, bool) {
|
||||
types := map[string]string{
|
||||
"mp3": "audio/mpeg",
|
||||
"flac": "audio/x-flac",
|
||||
"aac": "audio/x-aac",
|
||||
@@ -8,4 +12,7 @@ var Types = map[string]string{
|
||||
"m4b": "audio/m4b",
|
||||
"ogg": "audio/ogg",
|
||||
"opus": "audio/ogg",
|
||||
}
|
||||
v, ok := types[ext]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func decoded(in string) string {
|
||||
|
||||
// isScanning acts as an atomic boolean semaphore. we don't
|
||||
// want to have more than one scan going on at a time
|
||||
var isScanning int32
|
||||
var isScanning int32 //nolint:gochecknoglobals
|
||||
|
||||
func IsScanning() bool {
|
||||
return atomic.LoadInt32(&isScanning) == 1
|
||||
@@ -224,7 +224,8 @@ type item struct {
|
||||
stat os.FileInfo
|
||||
}
|
||||
|
||||
var coverFilenames = map[string]struct{}{
|
||||
func isCover(filename string) bool {
|
||||
known := map[string]struct{}{
|
||||
"cover.png": {},
|
||||
"cover.jpg": {},
|
||||
"cover.jpeg": {},
|
||||
@@ -237,6 +238,9 @@ var coverFilenames = map[string]struct{}{
|
||||
"front.png": {},
|
||||
"front.jpg": {},
|
||||
"front.jpeg": {},
|
||||
}
|
||||
_, ok := known[filename]
|
||||
return ok
|
||||
}
|
||||
|
||||
// ## begin callbacks
|
||||
@@ -267,8 +271,8 @@ func (s *Scanner) callbackItem(fullPath string, info *godirwalk.Dirent) error {
|
||||
if isDir {
|
||||
return s.handleFolder(it)
|
||||
}
|
||||
lowerFilename := strings.ToLower(filename)
|
||||
if _, ok := coverFilenames[lowerFilename]; ok {
|
||||
filenameLow := strings.ToLower(filename)
|
||||
if isCover(filenameLow) {
|
||||
s.curCover = filename
|
||||
return nil
|
||||
}
|
||||
@@ -276,7 +280,7 @@ func (s *Scanner) callbackItem(fullPath string, info *godirwalk.Dirent) error {
|
||||
if ext == "" {
|
||||
return nil
|
||||
}
|
||||
if _, ok := mime.Types[ext[1:]]; ok {
|
||||
if _, ok := mime.FromExtension(ext[1:]); ok {
|
||||
return s.handleTrack(it)
|
||||
}
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user