Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ services:
# - /path/to/cookies.txt:/opt/explo/cookies.txt # Path to optional cookies file (for yt-dlp)
environment:
- TZ=UTC # Change this to the timezone set in ListenBrainz (default is UTC)
- WEB_UI=true
# Web UI credentials (required for login)
- UI_USERNAME=
- UI_PASSWORD=
Expand Down
12 changes: 6 additions & 6 deletions docker/start.sh
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
#!/bin/sh
echo "[setup] Starting web UI..."
# If user incorectly mounts the config path as a directory, we'll try to automatically append it to .env inside it instead of failing.
WEB_CFG_PATH="${WEB_CFG_PATH:-/opt/explo/.env}"
if [ -d "$WEB_CFG_PATH" ]; then
WEB_CFG_PATH="$WEB_CFG_PATH/.env"
echo "[setup] Config path is a directory, using $WEB_CFG_PATH"
WEB_ENV_PATH="${WEB_ENV_PATH:-/opt/explo/.env}"
if [ -d "$WEB_ENV_PATH" ]; then
WEB_ENV_PATH="$WEB_ENV_PATH/.env"
echo "[setup] Config path is a directory, using $WEB_ENV_PATH"
fi
WEB_UI=true WEB_CFG_PATH="$WEB_CFG_PATH" WEB_ADDR="${WEB_ADDR:-:7288}" /opt/explo/explo &
WEB_UI=true WEB_ENV_PATH="$WEB_ENV_PATH" WEB_ADDR="${WEB_ADDR:-:7288}" /opt/explo/explo &
echo "[setup] Web UI available at http://localhost:${WEB_ADDR##*:}"

echo "[setup] Initializing cron jobs..."

# Load *_SCHEDULE and *_FLAGS from .env if not already set in the environment.
# This allows the web UI to configure schedules by writing to the .env file.
_cfg="${WEB_CFG_PATH:-/opt/explo/.env}"
_cfg="${WEB_ENV_PATH:-/opt/explo/.env}"
if [ -f "$_cfg" ]; then
while IFS= read -r _line; do
case "$_line" in \#*|'') continue ;; esac
Expand Down
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ go 1.24.0
toolchain go1.24.3

require (
github.com/go-co-op/gocron/v2 v2.21.1
github.com/ilyakaznacheev/cleanenv v1.5.0
github.com/nikoksr/notify v1.4.0
github.com/spf13/pflag v1.0.10
github.com/u2takey/ffmpeg-go v0.5.0
github.com/wader/goutubedl v0.0.0-20250417150709-083444e4ab87
golang.org/x/crypto v0.46.0
golang.org/x/sync v0.19.0
golang.org/x/text v0.32.0
maunium.net/go/mautrix v0.26.0
Expand All @@ -21,12 +23,16 @@ require (
github.com/aws/aws-sdk-go v1.38.20 // indirect
github.com/bwmarrin/discordgo v0.29.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/jonboulle/clockwork v0.5.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rs/zerolog v1.34.0 // indirect
github.com/stretchr/objx v0.5.3 // indirect
github.com/stretchr/testify v1.11.1 // indirect
Expand All @@ -36,7 +42,6 @@ require (
github.com/tidwall/sjson v1.2.5 // indirect
github.com/u2takey/go-utils v0.3.1 // indirect
go.mau.fi/util v0.9.3 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/sys v0.39.0 // indirect
Expand Down
21 changes: 20 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ github.com/aws/aws-sdk-go v1.38.20/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2z
github.com/bwmarrin/discordgo v0.29.0 h1:FmWeXFaKUwrcL3Cx65c20bTRW+vOb6k8AnaP+EgjDno=
github.com/bwmarrin/discordgo v0.29.0/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
Expand All @@ -15,13 +16,17 @@ github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44am
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-co-op/gocron/v2 v2.21.1 h1:QYOK6iOQVCut+jDcs4zRdWRTBHRxRCEeeFi1TnAmgbU=
github.com/go-co-op/gocron/v2 v2.21.1/go.mod h1:5lEiCKk1oVJV39Zg7/YG10OnaVrDAV5GGR6O0663k6U=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
Expand All @@ -34,11 +39,17 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
Expand All @@ -53,11 +64,16 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
github.com/nikoksr/notify v1.4.0 h1:pnIU0FB5IgIZ5B+YVwpuqVwh5ZmZqLEuc4NXeqUP39s=
github.com/nikoksr/notify v1.4.0/go.mod h1:qHDdy6k9D90hPQ48PSHm4AUCCCpryv1OxFW+9pgo7hw=
github.com/panjf2000/ants/v2 v2.4.2/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
Expand Down Expand Up @@ -93,6 +109,8 @@ github.com/wader/osleaktest v0.0.0-20191111175233-f643b0fed071 h1:QkrG4Zr5OVFuC9
github.com/wader/osleaktest v0.0.0-20191111175233-f643b0fed071/go.mod h1:XD6emOFPHVzb0+qQpiNOdPL2XZ0SRUM0N5JHuq6OmXo=
go.mau.fi/util v0.9.3 h1:aqNF8KDIN8bFpFbybSk+mEBil7IHeBwlujfyTnvP0uU=
go.mau.fi/util v0.9.3/go.mod h1:krWWfBM1jWTb5f8NCa2TLqWMQuM81X7TGQjhMjBeXmQ=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
gocv.io/x/gocv v0.25.0/go.mod h1:Rar2PS6DV+T4FL+PM535EImD/h13hGVaHhnCu1xarBs=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
Expand Down Expand Up @@ -128,8 +146,9 @@ golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
Expand Down
17 changes: 15 additions & 2 deletions src/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Config struct {
DiscoveryCfg DiscoveryConfig
ClientCfg ClientConfig
NotifyCfg NotifyConfig
ServerCfg ServerConfig
Flags Flags
PersistENV bool `env:"PERSIST" env-default:"true"`
Persist bool
Expand All @@ -31,13 +32,25 @@ type Config struct {

type Flags struct {
CfgPath string
CfgSet bool
Playlist string
DownloadMode string
ExcludeLocal bool
Persist bool
PersistSet bool
}

type ServerConfig struct {
Enabled bool `env:"WEB_UI" env-default:"false"`
Port string `env:"WEB_ADDR" env-default:":7288"`
Username string `env:"UI_USERNAME"`
Password string `env:"UI_PASSWORD"`
WebDataDir string `env:"WEB_DATA_PATH" env-default:"/opt/explo/config/"`
WebEnvPath string `env:"WEB_ENV_PATH" env-default:"/opt/explo/.env"`
CacheSizeMB int64 `env:"WEB_CACHE_MB" env-default:"500"`
ExploPath string
}

type ClientConfig struct {
ClientID string `env:"CLIENT_ID" env-default:"explo"`
LibraryName string `env:"LIBRARY_NAME" env-default:"Explo"`
Expand Down Expand Up @@ -89,7 +102,7 @@ type DownloadConfig struct {
}

type Filters struct {
Extensions []string `env:"EXTENSIONS" env-default:"flac,mp3"`
Extensions []string `env:"EXTENSIONS" env-default:"flac,mp3"` // slskd
MinBitDepth int `env:"MIN_BIT_DEPTH" env-default:"8"`
MinBitRate int `env:"MIN_BITRATE" env-default:"256"`
FilterList []string `env:"FILTER_LIST" env-default:"live,remix,instrumental,extended,clean,acapella"`
Expand All @@ -99,7 +112,7 @@ type Youtube struct {
APIKey string `env:"YOUTUBE_API_KEY"`
FfmpegPath string `env:"FFMPEG_PATH"`
YtdlpPath string `env:"YTDLP_PATH"`
FileExtension string `env:"TRACK_EXTENSION" env-default:"opus"`
FileExtension string `env:"TRACK_EXTENSION" env-default:"opus"` // yt-dlp
CookiesPath string `env:"COOKIES_PATH" env-default:"./cookies.txt"`
Filters Filters
}
Expand Down
7 changes: 7 additions & 0 deletions src/config/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func (cfg *Config) GetFlags() error {
os.Exit(0)
}
persistSet := flag.Lookup("persist").Changed
cfgSet := flag.Lookup("config").Changed

// Validation for playlist
if !contains(validPlaylists, playlist) {
Expand All @@ -50,6 +51,7 @@ func (cfg *Config) GetFlags() error {
}

cfg.Flags.CfgPath = configPath
cfg.Flags.CfgSet = cfgSet
cfg.Flags.Playlist = playlist
cfg.Flags.DownloadMode = downloadMode
cfg.Flags.ExcludeLocal = excludeLocal
Expand All @@ -64,6 +66,11 @@ func (cfg *Config) GetFlags() error {
func (cfg *Config) MergeFlags() {
cfg.DiscoveryCfg.Listenbrainz.ImportPlaylist = cfg.Flags.Playlist
cfg.DownloadCfg.ExcludeLocal = cfg.Flags.ExcludeLocal

if cfg.Flags.CfgSet {
cfg.ServerCfg.WebEnvPath = cfg.Flags.CfgPath
}

if cfg.Flags.PersistSet {
cfg.Persist = cfg.Flags.Persist
} else {
Expand Down
38 changes: 17 additions & 21 deletions src/main/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,6 @@ func setup(cfg *config.Config) {
}

func main() {
if os.Getenv("WEB_UI") == "true" {
cfgPath := os.Getenv("WEB_CFG_PATH")
if cfgPath == "" {
cfgPath = ".env"
}
exploPath, err := os.Executable()
if err != nil {
log.Fatal("could not determine executable path: ", err)
}
addr := os.Getenv("WEB_ADDR")
if addr == "" {
addr = ":7288"
}
srv := backend.NewServer(addr, cfgPath, exploPath)
log.Fatal(srv.Start())
}

var cfg config.Config
if err := cfg.GetFlags(); err != nil {
log.Fatal(err)
Expand All @@ -62,6 +45,17 @@ func main() {
setup(&cfg)
slog.Info("Starting Explo...")

if cfg.ServerCfg.Enabled {

exploPath, err := os.Executable()
if err != nil {
log.Fatal("could not determine executable path: ", err)
}

cfg.ServerCfg.ExploPath = exploPath
srv := backend.NewServer(cfg.ServerCfg)
log.Fatal(srv.Start())
}
httpClient := initHttpClient()
discovery := discovery.NewDiscoverer(cfg.DiscoveryCfg, httpClient)
tracks, err := discovery.Discover()
Expand Down Expand Up @@ -104,11 +98,13 @@ func main() {
}
}

added := make(map[string]bool)
for _, t := range tracks {
added[t.CleanTitle+"|"+t.Artist] = true
if cfg.ServerCfg.Enabled {
added := make(map[string]bool)
for _, t := range tracks {
added[t.CleanTitle+"|"+t.Artist] = true
}
backend.WritePlaylistCache(cfg.Flags.CfgPath, cfg.Flags.Playlist, allTracks, added)
}
backend.WritePlaylistCache(cfg.Flags.CfgPath, cfg.Flags.Playlist, allTracks, added)

if err := client.CreatePlaylist(tracks); err != nil {
slog.Warn(err.Error())
Expand Down
89 changes: 89 additions & 0 deletions src/web/backend/jobs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package backend

// Jobs running on a schedule go here i.e cache cleanups (and playlist imports in the future)

import (
"path/filepath"
"log/slog"
"os"
"slices"
"time"

"github.com/go-co-op/gocron/v2"
)


type Jobs struct {
scheduler gocron.Scheduler
}

type fileInfo struct {
path string
size int64
modTime time.Time
}

func NewJobs() (*Jobs) {
scheduler, err := gocron.NewScheduler()
if err != nil {
slog.Error("failed creating cron scheduler")
}

return &Jobs{ scheduler: scheduler}
}

func (j *Jobs) Start() {
j.scheduler.Start()
}

func (j *Jobs) RegisterCoverCleanup(schedule, coversDir string, maxBytes int64) error {
_, err := j.scheduler.NewJob(
gocron.CronJob(schedule, false),
gocron.NewTask(func() {
slog.Info("running cache cleanup")

trimCacheDir(coversDir, maxBytes)
}),
)

return err
}

func trimCacheDir(dataDir string, maxBytes int64) {

var files []fileInfo
var total int64

err := filepath.Walk(dataDir, func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() {
return nil
}

files = append(files, fileInfo{
path: path,
size: info.Size(),
modTime: info.ModTime(),
})

total += info.Size()
return nil
})

if err != nil || total <= maxBytes {
return
}

slices.SortFunc(files, func(a, b fileInfo) int {
return a.modTime.Compare(b.modTime)
})

for _, f := range files {
if total <= maxBytes {
break
}

if err := os.Remove(f.path); err == nil {
total -= f.size
}
}
}
Loading
Loading