This commit is contained in:
parent
e56ffc60a3
commit
aaa467eee0
2
main.go
2
main.go
@ -4,6 +4,7 @@ import (
|
|||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
|
|
||||||
"git.asdf.cafe/abs3nt/gospt-ng/src/app"
|
"git.asdf.cafe/abs3nt/gospt-ng/src/app"
|
||||||
|
"git.asdf.cafe/abs3nt/gospt-ng/src/components/cache"
|
||||||
"git.asdf.cafe/abs3nt/gospt-ng/src/components/cli"
|
"git.asdf.cafe/abs3nt/gospt-ng/src/components/cli"
|
||||||
"git.asdf.cafe/abs3nt/gospt-ng/src/components/commands"
|
"git.asdf.cafe/abs3nt/gospt-ng/src/components/commands"
|
||||||
)
|
)
|
||||||
@ -14,6 +15,7 @@ func main() {
|
|||||||
fx.Populate(&s),
|
fx.Populate(&s),
|
||||||
app.Config,
|
app.Config,
|
||||||
fx.Provide(
|
fx.Provide(
|
||||||
|
cache.NewCache,
|
||||||
commands.NewCommander,
|
commands.NewCommander,
|
||||||
),
|
),
|
||||||
fx.Invoke(
|
fx.Invoke(
|
||||||
|
117
src/components/cache/cache.go
vendored
Normal file
117
src/components/cache/cache.go
vendored
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.uber.org/fx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CacheEntry struct {
|
||||||
|
Expire time.Time `json:"e"`
|
||||||
|
Value string `json:"v"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CacheResult struct {
|
||||||
|
fx.Out
|
||||||
|
|
||||||
|
Cache *Cache
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cache struct {
|
||||||
|
Root string
|
||||||
|
Log *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
type CacheParams struct {
|
||||||
|
fx.In
|
||||||
|
|
||||||
|
Log *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCache(p CacheParams) CacheResult {
|
||||||
|
c := &Cache{
|
||||||
|
Root: filepath.Join(os.TempDir(), "gospt.cache"),
|
||||||
|
Log: p.Log,
|
||||||
|
}
|
||||||
|
return CacheResult{
|
||||||
|
Cache: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) load() (map[string]CacheEntry, error) {
|
||||||
|
out := map[string]CacheEntry{}
|
||||||
|
cache, err := os.Open(c.Root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(cache).Decode(&out); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) save(m map[string]CacheEntry) error {
|
||||||
|
payload, err := json.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
slog.Debug("CACHE", "saving", string(payload))
|
||||||
|
err = os.WriteFile(c.Root, payload, 0o600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) GetOrDo(key string, do func() (string, error), ttl time.Duration) (string, error) {
|
||||||
|
conf, err := c.load()
|
||||||
|
if err != nil {
|
||||||
|
slog.Debug("CACHE", "failed read", err)
|
||||||
|
return c.Do(key, do, ttl)
|
||||||
|
}
|
||||||
|
val, ok := conf[key]
|
||||||
|
if !ok {
|
||||||
|
return c.Do(key, do, ttl)
|
||||||
|
}
|
||||||
|
if time.Now().After(val.Expire) {
|
||||||
|
return c.Do(key, do, ttl)
|
||||||
|
}
|
||||||
|
return val.Value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Do(key string, do func() (string, error), ttl time.Duration) (string, error) {
|
||||||
|
if do == nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
res, err := do()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return c.Put(key, res, ttl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Put(key string, value string, ttl time.Duration) (string, error) {
|
||||||
|
conf, err := c.load()
|
||||||
|
if err != nil {
|
||||||
|
conf = map[string]CacheEntry{}
|
||||||
|
}
|
||||||
|
conf[key] = CacheEntry{
|
||||||
|
Expire: time.Now().Add(ttl),
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
slog.Debug("CACHE", "new item", fmt.Sprintf("%s: %s", key, value))
|
||||||
|
err = c.save(conf)
|
||||||
|
if err != nil {
|
||||||
|
slog.Debug("CACHE", "failed to save", err)
|
||||||
|
}
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Clear() error {
|
||||||
|
return os.Remove(c.Root)
|
||||||
|
}
|
@ -204,6 +204,13 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
|
|||||||
return c.ClearRadio()
|
return c.ClearRadio()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "status",
|
||||||
|
Usage: "Prints the current status",
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
return c.Status()
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "devices",
|
Name: "devices",
|
||||||
Usage: "Lists available devices",
|
Usage: "Lists available devices",
|
||||||
|
@ -7,6 +7,8 @@ import (
|
|||||||
|
|
||||||
"github.com/zmb3/spotify/v2"
|
"github.com/zmb3/spotify/v2"
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
|
|
||||||
|
"git.asdf.cafe/abs3nt/gospt-ng/src/components/cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommanderResult struct {
|
type CommanderResult struct {
|
||||||
@ -21,6 +23,7 @@ type CommanderParams struct {
|
|||||||
Context context.Context
|
Context context.Context
|
||||||
Client *spotify.Client
|
Client *spotify.Client
|
||||||
Log *slog.Logger
|
Log *slog.Logger
|
||||||
|
Cache *cache.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
type Commander struct {
|
type Commander struct {
|
||||||
@ -28,6 +31,7 @@ type Commander struct {
|
|||||||
Client *spotify.Client
|
Client *spotify.Client
|
||||||
User *spotify.PrivateUser
|
User *spotify.PrivateUser
|
||||||
Log *slog.Logger
|
Log *slog.Logger
|
||||||
|
Cache *cache.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCommander(p CommanderParams) CommanderResult {
|
func NewCommander(p CommanderParams) CommanderResult {
|
||||||
@ -41,6 +45,7 @@ func NewCommander(p CommanderParams) CommanderResult {
|
|||||||
Client: p.Client,
|
Client: p.Client,
|
||||||
User: currentUser,
|
User: currentUser,
|
||||||
Log: p.Log,
|
Log: p.Log,
|
||||||
|
Cache: p.Cache,
|
||||||
}
|
}
|
||||||
return CommanderResult{
|
return CommanderResult{
|
||||||
Commander: c,
|
Commander: c,
|
||||||
|
38
src/components/commands/status.go
Normal file
38
src/components/commands/status.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/zmb3/spotify/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Commander) Status() error {
|
||||||
|
state, err := c.Cache.GetOrDo("state", func() (string, error) {
|
||||||
|
state, err := c.Client.PlayerState(c.Context)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
str, err := c.FormatState(state)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return str, nil
|
||||||
|
}, 5*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(state)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Commander) FormatState(state *spotify.PlayerState) (string, error) {
|
||||||
|
state.Item.AvailableMarkets = []string{}
|
||||||
|
state.Item.Album.AvailableMarkets = []string{}
|
||||||
|
out, err := json.MarshalIndent(state, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return (string(out)), nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user