Skip to content
Open
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
3 changes: 2 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
branches:
- dev
- feat/add-lidarr-download-config
pull_request:
branches: ['*']
workflow_dispatch:
Expand Down Expand Up @@ -133,7 +134,7 @@ jobs:
build-dev:
name: Build/publish dev image
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/dev'
if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/feat/add-lidarr-download-config'
permissions:
contents: read
packages: write
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
Expand Down Expand Up @@ -121,6 +122,7 @@ golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 h1:MDfG8Cvcqlt9XXrmEiD4epKn7
golang.org/x/exp v0.0.0-20251209150349-8475f28825e9/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
Expand Down
11 changes: 11 additions & 0 deletions sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,17 @@ YOUTUBE_API_KEY=
# Comma-separated (without spaces) keywords to avoid, when filtering slskd results (default: live,remix,instrumental,extended,clean,acapella)
# FILTER_LIST=live,remix,instrumental,extended,clean,acapella

# === Lidarr Configuration ===

# LIDARR_API_KEY=
# LIDARR_RETRY=
# LIDARR_DL_ATTEMPTS=
# LIDARR_DIR=
# MIGRATE_DOWNLOADS=
# LIDARR_TIMEOUT=
# LIDARR_SCHEME=
# LIDARR_URL=

# === Metadata / Formatting ===

# Set to true to merge featured artists into title (recommended), false appends them to artist field (default: true)
Expand Down
36 changes: 28 additions & 8 deletions src/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ type DownloadConfig struct {
Youtube Youtube
YoutubeMusic YoutubeMusic
Slskd Slskd
Lidarr Lidarr
ExcludeLocal bool
KeepPermissions bool `env:"KEEP_PERMISSIONS" env-default:"true"` // keep original file permissions when migrating download
RenameTrack bool `env:"RENAME_TRACK" env-default:"false"` // Rename track in {title}-{artist} format
Expand Down Expand Up @@ -143,18 +144,35 @@ type SlskdMon struct {
Duration int `env:"SLSKD_MONITOR_DURATION" env-default:"15"`
}

type Lidarr struct {
APIKey string `env:"LIDARR_API_KEY"`
Retry int `env:"LIDARR_RETRY" env-default:"5"` // Number of times to check search status before skipping the track
DownloadAttempts int `env:"LIDARR_DL_ATTEMPTS" env-default:"3"` // Max number of files to attempt downloading per track
LidarrDir string `env:"LIDARR_DIR" env-default:"/lidarr/"`
MigrateDL bool `env:"MIGRATE_DOWNLOADS" env-default:"false"` // Move downloads from LidarrDir to DownloadDir
Timeout int `env:"LIDARR_TIMEOUT" env-default:"20"`
URL string `env:"LIDARR_URL"`
Filters Filters
MonitorConfig LidarrMon
}

type LidarrMon struct {
Interval time.Duration `env:"LIDARR_MONITOR_INTERVAL" env-default:"1m"`
Duration time.Duration `env:"LIDARR_MONITOR_DURATION" env-default:"15m"`
}

type DiscoveryConfig struct {
Discovery string `env:"DISCOVERY_SERVICE" env-default:"listenbrainz"`
Discovery string `env:"DISCOVERY_SERVICE" env-default:"listenbrainz"`
ArtistBlacklist []string `env:"ARTIST_BLACKLIST"`
Listenbrainz Listenbrainz
Listenbrainz Listenbrainz
}
type Listenbrainz struct {
Discovery string `env:"LISTENBRAINZ_DISCOVERY" env-default:"playlist"`
User string `env:"LISTENBRAINZ_USER"`
ImportPlaylist string
SingleArtist bool `env:"SINGLE_ARTIST" env-default:"true"`
CoverArtSize string `env:"COVER_ART_SIZE" env-default:"250"`
EnrichTrackMetadata bool `env:"ENRICH_TRACK_METADATA" env-default:"false"`
Discovery string `env:"LISTENBRAINZ_DISCOVERY" env-default:"playlist"`
User string `env:"LISTENBRAINZ_USER"`
ImportPlaylist string
SingleArtist bool `env:"SINGLE_ARTIST" env-default:"true"`
CoverArtSize string `env:"COVER_ART_SIZE" env-default:"250"`
EnrichTrackMetadata bool `env:"ENRICH_TRACK_METADATA" env-default:"false"`
}

type NotifyConfig struct {
Expand Down Expand Up @@ -215,6 +233,7 @@ func (cfg *Config) CommonFixes() {
cfg.DownloadCfg.Youtube.CoversDir = filepath.Join(filepath.Dir(cfg.Flags.CfgPath), "cache", "covers")
cfg.ClientCfg.URL = fixBaseURL(cfg.ClientCfg.URL)
cfg.DownloadCfg.Slskd.URL = fixBaseURL(cfg.DownloadCfg.Slskd.URL)
cfg.DownloadCfg.Lidarr.URL = fixBaseURL(cfg.DownloadCfg.Lidarr.URL)
cfg.NormalizeDir()
}

Expand All @@ -223,6 +242,7 @@ func (cfg *Config) NormalizeDir() {
cfg.ClientCfg.PlaylistDir = fixDir(cfg.ClientCfg.PlaylistDir)
}
cfg.DownloadCfg.Slskd.SlskdDir = fixDir(cfg.DownloadCfg.Slskd.SlskdDir)
cfg.DownloadCfg.Lidarr.LidarrDir = fixDir(cfg.DownloadCfg.Lidarr.LidarrDir)
cfg.DownloadCfg.DownloadDir = fixDir(cfg.DownloadCfg.DownloadDir)
}

Expand Down
16 changes: 8 additions & 8 deletions src/discovery/listenbrainz.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type Metadata struct {
Artist struct {
ArtistCreditID int `json:"artist_credit_id"`
Artists []struct {
ArtistMbid string `json:"artist_mbid"`
MusicBrainzArtistID string `json:"artist_mbid"`
BeginYear int `json:"begin_year"`
EndYear int `json:"end_year,omitempty"`
JoinPhrase string `json:"join_phrase"`
Expand Down Expand Up @@ -121,7 +121,7 @@ type Exploration struct {
AdditionalMetadata struct {
Artists []struct {
ArtistCreditName string `json:"artist_credit_name"`
ArtistMbid string `json:"artist_mbid"`
MusicBrainzArtistID string `json:"artist_mbid"`
JoinPhrase string `json:"join_phrase"`
} `json:"artists"`
CaaID int64 `json:"caa_id"`
Expand Down Expand Up @@ -347,7 +347,7 @@ func (c *ListenBrainz) getTracks(mbids []string, singleArtist bool) ([]*models.T
MusicBrainzTrackID: mbTrackID,
MusicBrainzAlbumID: rel.CaaReleaseMbid,
MusicBrainzReleaseGroupID: rel.ReleaseGroupMbid,
MusicBrainzArtistID: recArtists[0].ArtistMbid,
MusicBrainzArtistID: recArtists[0].MusicBrainzArtistID,
})
}

Expand Down Expand Up @@ -396,7 +396,7 @@ func (c *ListenBrainz) enrichTracks(tracks []*models.Track, singleArtist bool) (
mainArtist := recording.Artist.Name
mainArtistID := ""
if len(recording.Artist.Artists) > 0 {
mainArtistID = recording.Artist.Artists[0].ArtistMbid
mainArtistID = recording.Artist.Artists[0].MusicBrainzArtistID
}

recArtists := recording.Artist.Artists
Expand Down Expand Up @@ -550,7 +550,7 @@ func (c *ListenBrainz) enrichTracks(tracks []*models.Track, singleArtist bool) (
if len(recArtists) == 0 {
return ""
}
return recArtists[0].ArtistMbid
return recArtists[0].MusicBrainzArtistID
}(),
}
}
Expand Down Expand Up @@ -692,9 +692,9 @@ func (c *ListenBrainz) parsePlaylist(identifier string, singleArtist bool) (stri
recordingMBID = parts[len(parts)-1]
}

artistMBID := ""
MusicBrainzArtistID := ""
if len(trackMeta.Artists) > 0 {
artistMBID = trackMeta.Artists[0].ArtistMbid
MusicBrainzArtistID = trackMeta.Artists[0].MusicBrainzArtistID
}

tracks = append(tracks, &models.Track{
Expand All @@ -706,7 +706,7 @@ func (c *ListenBrainz) parsePlaylist(identifier string, singleArtist bool) (stri
Duration: track.Duration,
CoverURL: coverURL,
MusicBrainzTrackID: recordingMBID,
MusicBrainzArtistID: artistMBID,
MusicBrainzArtistID: MusicBrainzArtistID,
MusicBrainzAlbumID: trackMeta.CaaReleaseMbid,
})
}
Expand Down
7 changes: 7 additions & 0 deletions src/downloader/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ func NewDownloader(cfg *cfg.DownloadConfig, httpClient *util.HttpClient, filterL
slskdClient := NewSlskd(cfg.Slskd, cfg.DownloadDir)
slskdClient.AddHeader()
downloader = append(downloader, slskdClient)
case "lidarr":
lidarrClient := NewLidarr(cfg.Lidarr, cfg.DownloadDir)
lidarrClient.AddHeader()
downloader = append(downloader, lidarrClient)
default:
return nil, fmt.Errorf("downloader '%s' not supported", service)
}
Expand Down Expand Up @@ -101,6 +105,9 @@ func (c *DownloadClient) needsDownloadDir() bool {
return true
}
}
if c.Cfg.Lidarr.MigrateDL {
return c.Cfg.Lidarr.MigrateDL
}
return c.Cfg.Slskd.MigrateDL
}

Expand Down
Loading
Loading