sentriz / gonic

music streaming server / free-software subsonic server API implementation
ircs://irc.libera.chat/#gonic
GNU General Public License v3.0
1.6k stars 113 forks source link

Sort getAlbumList "newest" / getAlbumList2 "newest" / admin UI folders by ctime #135

Closed bobberb closed 2 years ago

bobberb commented 3 years ago

Dear sentriz,

Thank you for an excellent lightweight alternative.

I have recently deployed gonic alongside airsonic and I've noticed few small cosmetic issues:

  1. Some album art, if not labeled exactly as cover.jpg or front.jpg or AlbumArt.jpg or equivalent will be missing. How do I trigger a refresh of just the album art?
  2. After renaming "missing" album art, the albums move to the top of the recent folders list. I sort my music mentally by when the folder was created. Gonic updates the "recent albums" ?based on the last modtime of any file within? (such as a renamed album art image). Is there a way to avoid this?
jef commented 3 years ago

Some album art, if not labeled exactly as cover.jpg or front.jpg or AlbumArt.jpg or equivalent will be missing. How do I trigger a refresh of just the album art?

The Subsonic API doesn't support this, so I don't know if this is possible (as of now). You'd have to do a full scan.

After renaming "missing" album art, the albums move to the top of the recent folders list. I sort my music mentally by when the folder was created. Gonic updates the "recent albums" ?based on the last modtime of any file within? (such as a renamed album art image). Is there a way to avoid this?

It is sorted by modified date:

https://github.com/sentriz/gonic/blob/e9cc2e8ea610686396e27645f3d12dde4ac92baf/server/assets/pages/home.tmpl#L119-L124

https://github.com/sentriz/gonic/blob/e9cc2e8ea610686396e27645f3d12dde4ac92baf/server/ctrladmin/handlers.go#L68-L72

https://github.com/sentriz/gonic/blob/6a99cdf299e0b9f2c3254786e19f9e56e8006121/server/scanner/scanner.go#L379-L382

If you wanted to use "created at" or something similar, this would have to be changed to be dynamic. Not sure if that's something that gonic has in scope though, as it's not really a viewing tool. Rather, gonic is a server to host the Subsonic API to have tools like https://airsonic.netlify.app/ serve the media.

bobberb commented 3 years ago

Indeed, I am basing my comparisons off airsonic and not the pure subsonic api, I had a daily albumart refresh - I can easily do a full scan for art.

What does it mean that "this would have to be changed to be dynamic" @jef ? I was ready to maintain a gonic fork myself with just 3 lines changed from Modtime to created_at -- does golang not have this in the os library? My fault for assuming this could just be read from the file like a python time.ctime.

jef commented 3 years ago

What does it mean that "this would have to be changed to be dynamic" @jef

I guess what I mean here is that to give the user an option to choose between the two. Not sure how @sentriz feels about this, so don't take my word. Maybe there is another library that has implemented CreatedAt or perhaps I haven't looked into the os.FileInfo docs well enough, but I don't see that being a problem for you to do this on your fork!

sentriz commented 3 years ago

hey, yeah that's a strange one. that code was written a good while ago but i don't remember why it was implemented that way

looking at some docs now, and it does seem that ctime time is not exposed as simply as mtime is

also for the album art thing - one trick would be to $ touch album_dir/ then do a quick incremental scan

bobberb commented 3 years ago

@sentriz just did some searching myself. Apparently, golang does expose ctime but it is "hidden" according to this repo which exposes them just using the os, syscall, and time libaries.

times repo

`package main

import (
  "log"

  "github.com/djherbis/times"
)

func main() {
  t, err := times.Stat("myfile")
  if err != nil {
    log.Fatal(err.Error())
  }

  log.Println(t.AccessTime())
  log.Println(t.ModTime())

  if t.HasChangeTime() {
    log.Println(t.ChangeTime())
  }

  if t.HasBirthTime() {
    log.Println(t.BirthTime())
  }
}
`

and

https://github.com/djherbis/times/blob/master/times_linux.go

`// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// http://golang.org/src/os/stat_linux.go

package times

import (
    "os"
    "syscall"
    "time"
)

// HasChangeTime and HasBirthTime are true if and only if
// the target OS supports them.
const (
    HasChangeTime = true
    HasBirthTime  = false
)

type timespec struct {
    atime
    mtime
    ctime
    nobtime
}

func timespecToTime(ts syscall.Timespec) time.Time {
    return time.Unix(int64(ts.Sec), int64(ts.Nsec))
}

func getTimespec(fi os.FileInfo) (t timespec) {
    stat := fi.Sys().(*syscall.Stat_t)
    t.atime.v = timespecToTime(stat.Atim)
    t.mtime.v = timespecToTime(stat.Mtim)
    t.ctime.v = timespecToTime(stat.Ctim)
    return t
}`