Skip to content

Commit 861f437

Browse files
authored
Merge pull request #26 from avedor/fix/make-linter-happy
Address lint failures; add lint job to PRs
2 parents e95dd88 + 27a4655 commit 861f437

File tree

5 files changed

+107
-74
lines changed

5 files changed

+107
-74
lines changed

.github/workflows/release.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ on:
55
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
66
branches:
77
- dev
8+
pull_request:
9+
branches: ['*']
810

911
name: Latest Release
1012

@@ -22,7 +24,7 @@ jobs:
2224
with:
2325
go-version: '1.23'
2426
- name: golangci-lint
25-
uses: golangci/golangci-lint-action@v3.7.0
27+
uses: golangci/golangci-lint-action@v8.0.0
2628
with:
2729
version: latest
2830
release:

src/client/client.go

Lines changed: 46 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import (
1313
// Client manages interactions with the selected music system
1414
type Client struct {
1515
System string
16-
Cfg *config.ClientConfig
17-
API APIClient
16+
Cfg *config.ClientConfig
17+
API APIClient
1818
}
1919

2020
type APIClient interface {
@@ -31,85 +31,96 @@ type APIClient interface {
3131
}
3232

3333
// NewClient initializes a client and sets up authentication
34-
func NewClient(cfg *config.Config, httpClient *util.HttpClient) *Client {
34+
func NewClient(cfg *config.Config, httpClient *util.HttpClient) (*Client, error) {
3535
c := &Client{
3636
System: cfg.System,
37-
Cfg: &cfg.ClientCfg,
37+
Cfg: &cfg.ClientCfg,
3838
}
3939
switch c.System {
40-
40+
4141
case "emby":
4242
c.API = NewEmby(cfg.ClientCfg, httpClient)
4343

4444
case "jellyfin":
4545
c.API = NewJellyfin(cfg.ClientCfg, httpClient)
46-
46+
4747
case "mpd":
4848
c.API = NewMPD(cfg.ClientCfg)
4949

5050
case "plex":
5151
c.API = NewPlex(cfg.ClientCfg, httpClient)
5252

53-
case "subsonic":
53+
case "subsonic":
5454
c.API = NewSubsonic(cfg.ClientCfg, httpClient)
55-
55+
5656
default:
5757
log.Fatalf("unknown system: %s. Use a supported system (emby, jellyfin, mpd, plex, or subsonic).", c.System)
5858
}
5959

60-
c.systemSetup() // Run setup automatically
61-
return c
60+
if err := c.systemSetup(); err != nil { // Run setup automatically
61+
return nil, fmt.Errorf("setup failed: %w", err)
62+
}
63+
64+
return c, nil
6265
}
6366

6467
// systemSetup checks needed credentials and initializes the selected system
65-
func (c *Client) systemSetup() {
68+
func (c *Client) systemSetup() error {
6669
switch c.System {
6770
case "subsonic":
6871
if c.Cfg.Creds.User == "" || c.Cfg.Creds.Password == "" {
69-
log.Fatal("Subsonic USER and PASSWORD are required")
72+
return fmt.Errorf("Subsonic USER and PASSWORD are required")
7073
}
71-
c.API.GetAuth()
74+
return c.API.GetAuth()
7275

7376
case "jellyfin":
7477
if c.Cfg.Creds.APIKey == "" {
75-
log.Fatal("Jellyfin API_KEY is required")
78+
return fmt.Errorf("Jellyfin API_KEY is required")
79+
}
80+
if err := c.API.AddHeader(); err != nil {
81+
return err
7682
}
77-
c.API.AddHeader()
78-
c.API.GetLibrary()
83+
return c.API.GetLibrary()
7984

8085
case "mpd":
8186
if c.Cfg.PlaylistDir == "" {
82-
log.Fatal("MPD PLAYLIST_DIR is required")
87+
return fmt.Errorf("MPD PLAYLIST_DIR is required")
8388
}
89+
return nil
8490

8591
case "plex":
8692
if (c.Cfg.Creds.User == "" || c.Cfg.Creds.Password == "") && c.Cfg.Creds.APIKey == "" {
87-
log.Fatal("Plex USER/PASSWORD or API_KEY is required")
93+
return fmt.Errorf("Plex USER/PASSWORD or API_KEY is required")
8894
}
89-
c.API.AddHeader()
9095
if c.Cfg.Creds.APIKey == "" {
9196
if err := c.API.GetAuth(); err != nil {
92-
log.Fatal(err)
97+
return err
9398
}
9499

95100
}
96-
c.API.AddHeader()
97-
c.API.GetLibrary()
101+
if err := c.API.AddHeader(); err != nil {
102+
return err
103+
}
104+
return c.API.GetLibrary()
98105

99106
case "emby":
100107
if c.Cfg.Creds.APIKey == "" {
101-
log.Fatal("Emby API_KEY is required")
108+
return fmt.Errorf("Emby API_KEY is required")
109+
}
110+
if err := c.API.AddHeader(); err != nil {
111+
return err
102112
}
103-
c.API.AddHeader()
104-
c.API.GetLibrary()
113+
return c.API.GetLibrary()
105114

106115
default:
107-
log.Fatalf("Unknown system: %s. Use a supported system (emby, jellyfin, mpd, plex, or subsonic).", c.System)
116+
return fmt.Errorf("unknown system: %s. Use a supported system (emby, jellyfin, mpd, plex, or subsonic)", c.System)
108117
}
109118
}
110119

111120
func (c *Client) CheckTracks(tracks []*models.Track) {
112-
c.API.SearchSongs(tracks)
121+
if err := c.API.SearchSongs(tracks); err != nil {
122+
log.Printf("warning: SearchSongs failed: %v", err)
123+
}
113124
}
114125

115126
func (c *Client) CreatePlaylist(tracks []*models.Track) error {
@@ -123,12 +134,13 @@ func (c *Client) CreatePlaylist(tracks []*models.Track) error {
123134

124135
log.Printf("[%s] Refreshing library...", c.System)
125136
time.Sleep(time.Duration(c.Cfg.Sleep) * time.Minute)
126-
c.API.SearchSongs(tracks) // search newly added songs
137+
if err := c.API.SearchSongs(tracks); err != nil { // search newly added songs
138+
log.Printf("warning: SearchSongs failed: %v", err)
139+
}
127140
if err := c.API.CreatePlaylist(tracks); err != nil {
128141
return fmt.Errorf("[%s] failed to create playlist: %s", c.System, err.Error())
129142
}
130143

131-
132144
description := "Created by Explo using recommendations from ListenBrainz"
133145
if err := c.API.UpdatePlaylist(description); err != nil {
134146
return fmt.Errorf("[%s] failed to update playlist: %s", c.System, err.Error())
@@ -137,10 +149,11 @@ func (c *Client) CreatePlaylist(tracks []*models.Track) error {
137149
}
138150

139151
func (c *Client) DeletePlaylist() error {
140-
c.API.SearchPlaylist()
141-
err := c.API.DeletePlaylist()
142-
if err != nil {
152+
if err := c.API.SearchPlaylist(); err != nil {
153+
return fmt.Errorf("warning: SearchSongs failed: %v", err)
154+
}
155+
if err := c.API.DeletePlaylist(); err != nil {
143156
return fmt.Errorf("[%s] failed to delete playlist: %s", c.System, err.Error())
144157
}
145158
return nil
146-
}
159+
}

src/downloader/youtube.go

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,16 @@ import (
1818
)
1919

2020
type Videos struct {
21-
Items []Item `json:"items"`
21+
Items []Item `json:"items"`
2222
}
2323

2424
type ID struct {
2525
VideoID string `json:"videoId"`
2626
}
2727

2828
type Snippet struct {
29-
Title string `json:"title"`
30-
ChannelTitle string `json:"channelTitle"`
29+
Title string `json:"title"`
30+
ChannelTitle string `json:"channelTitle"`
3131
}
3232

3333
type Item struct {
@@ -37,19 +37,19 @@ type Item struct {
3737

3838
type Youtube struct {
3939
DownloadDir string
40-
HttpClient *util.HttpClient
41-
Cfg cfg.Youtube
40+
HttpClient *util.HttpClient
41+
Cfg cfg.Youtube
4242
}
4343

4444
func NewYoutube(cfg cfg.Youtube, discovery, downloadDir string, httpClient *util.HttpClient) *Youtube { // init downloader cfg for youtube
4545
return &Youtube{
4646
DownloadDir: downloadDir,
47-
Cfg: cfg,
48-
HttpClient: httpClient}
47+
Cfg: cfg,
48+
HttpClient: httpClient}
4949
}
5050

5151
func (c *Youtube) QueryTrack(track *models.Track) error { // Queries youtube for the song
52-
52+
5353
escQuery := url.PathEscape(fmt.Sprintf("%s - %s", track.Title, track.Artist))
5454
queryURL := fmt.Sprintf("https://youtube.googleapis.com/youtube/v3/search?part=snippet&q=%s&type=video&videoCategoryId=10&key=%s", escQuery, c.Cfg.APIKey)
5555

@@ -83,7 +83,7 @@ func (c *Youtube) GetTrack(track *models.Track) error {
8383
}
8484

8585
func getTopic(cfg cfg.Youtube, videos Videos, track models.Track) string { // gets song under artist topic or personal channel
86-
86+
8787
for _, v := range videos.Items {
8888
if (strings.Contains(v.Snippet.ChannelTitle, "- Topic") || v.Snippet.ChannelTitle == track.MainArtist) && filter(track, v.Snippet.Title, cfg.FilterList) {
8989
return v.ID.VideoID
@@ -109,29 +109,38 @@ func getVideo(ctx context.Context, c Youtube, videoID string) (*goutubedl.Downlo
109109
}
110110

111111
return downloadResult, nil
112-
112+
113113
}
114114

115115
func saveVideo(c Youtube, track models.Track, stream *goutubedl.DownloadResult) bool {
116116

117-
defer stream.Close()
117+
defer func() {
118+
if err := stream.Close(); err != nil {
119+
log.Printf("warning: stream close failed: %v", err)
120+
}
121+
}()
118122

119123
input := fmt.Sprintf("%s%s_TEMP.mp3", c.DownloadDir, track.File)
120124
file, err := os.Create(input)
121125
if err != nil {
122126
log.Fatalf("Failed to create song file: %s", err.Error())
123127
}
124-
defer file.Close()
128+
129+
defer func() {
130+
if err := file.Close(); err != nil {
131+
log.Printf("warning: file close failed: %v", err)
132+
}
133+
}()
125134

126135
if _, err = io.Copy(file, stream); err != nil {
127136
log.Printf("Failed to copy stream to file: %s", err.Error())
128-
os.Remove(input)
137+
_ = os.Remove(input) //nolint:errcheck // best-effort cleanup
129138
return false
130139
}
131140

132141
cmd := ffmpeg.Input(input).Output(fmt.Sprintf("%s%s.mp3", c.DownloadDir, track.File), ffmpeg.KwArgs{
133-
"map": "0:a",
134-
"metadata": []string{"artist="+track.Artist,"title="+track.Title,"album="+track.Album},
142+
"map": "0:a",
143+
"metadata": []string{"artist=" + track.Artist, "title=" + track.Title, "album=" + track.Album},
135144
"loglevel": "error",
136145
}).OverWriteOutput().ErrorToStdOut()
137146

@@ -141,19 +150,19 @@ func saveVideo(c Youtube, track models.Track, stream *goutubedl.DownloadResult)
141150

142151
if err = cmd.Run(); err != nil {
143152
log.Printf("Failed to convert audio: %s", err.Error())
144-
os.Remove(input)
153+
_ = os.Remove(input) //nolint:errcheck // best-effort cleanup
145154
return false
146155
}
147-
os.Remove(input)
156+
_ = os.Remove(input) //nolint:errcheck // best-effort cleanup
148157
return true
149158
}
150159

151160
func gatherVideo(cfg cfg.Youtube, videos Videos, track models.Track) string { // filter out video ID
152-
161+
153162
// Try to get the video from the official or topic channel
154163
if id := getTopic(cfg, videos, track); id != "" {
155164
return id
156-
165+
157166
}
158167
// If official video isn't found, try the first suitable channel
159168
for _, video := range videos.Items {
@@ -171,19 +180,19 @@ func fetchAndSaveVideo(ctx context.Context, cfg Youtube, track models.Track) boo
171180
log.Printf("failed getting stream for video ID %s: %s", track.ID, err.Error())
172181
return false
173182
}
174-
183+
175184
if stream != nil {
176185
return saveVideo(cfg, track, stream)
177186
}
178-
187+
179188
log.Printf("stream was nil for video ID %s", track.ID)
180189
return false
181190
}
182191

183192
func filter(track models.Track, videoTitle string, filterList []string) bool { // ignore titles that have a specific keyword (defined in .env)
184193

185194
for _, keyword := range filterList {
186-
if (!containsLower(track.Title,keyword) && !containsLower(track.Artist, keyword) && containsLower(videoTitle, keyword)) {
195+
if !containsLower(track.Title, keyword) && !containsLower(track.Artist, keyword) && containsLower(videoTitle, keyword) {
187196
return false
188197
}
189198
}
@@ -193,7 +202,7 @@ func filter(track models.Track, videoTitle string, filterList []string) bool { /
193202
func containsLower(str string, substr string) bool {
194203

195204
return strings.Contains(
196-
strings.ToLower(str),
197-
strings.ToLower(substr),
198-
)
199-
}
205+
strings.ToLower(str),
206+
strings.ToLower(substr),
207+
)
208+
}

src/main/main.go renamed to src/main.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import (
1212
)
1313

1414
type Song struct {
15-
Title string
15+
Title string
1616
Artist string
17-
Album string
17+
Album string
1818
}
1919

2020
func initHttpClient() *util.HttpClient {
@@ -33,7 +33,10 @@ func main() {
3333
cfg := config.ReadEnv()
3434
setup(&cfg)
3535
httpClient := initHttpClient()
36-
client := client.NewClient(&cfg, httpClient)
36+
client, err := client.NewClient(&cfg, httpClient)
37+
if err != nil {
38+
log.Fatal(err)
39+
}
3740
discovery := discovery.NewDiscoverer(cfg.DiscoveryCfg, httpClient)
3841
downloader := downloader.NewDownloader(&cfg.DownloadCfg, httpClient)
3942

@@ -57,6 +60,6 @@ func main() {
5760
if err := client.CreatePlaylist(tracks); err != nil {
5861
log.Println(err)
5962
} else {
60-
log.Printf("[%s] %s playlist created successfully",cfg.System, cfg.ClientCfg.PlaylistName)
63+
log.Printf("[%s] %s playlist created successfully", cfg.System, cfg.ClientCfg.PlaylistName)
6164
}
62-
}
65+
}

0 commit comments

Comments
 (0)