Skip to content

Commit

Permalink
See Release Notes
Browse files Browse the repository at this point in the history
  • Loading branch information
LumePart committed Jun 28, 2024
1 parent baf4e0a commit 335d20e
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 166 deletions.
37 changes: 12 additions & 25 deletions listenbrainz.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,7 @@ type Playlists struct {
Data struct {
Date time.Time `json:"date"`
Identifier string `json:"identifier"`
Extension struct {
HTTPSMusicbrainzOrgDocJspfPlaylist struct {
AdditionalMetadata struct {
AlgorithmMetadata struct {
SourcePatch string `json:"source_patch"`
} `json:"algorithm_metadata"`
} `json:"additional_metadata"`
} `json:"https://musicbrainz.org/doc/jspf#playlist"`
} `json:"extension"`
Title string `json:"title"`
} `json:"playlist"`
} `json:"playlists"`
}
Expand All @@ -78,7 +70,7 @@ type Exploration struct {
Tracks []struct {
Album string `json:"album"`
Creator string `json:"creator"`
Identifier string `json:"identifier"`
Identifier []string `json:"identifier"`
Title string `json:"title"`
} `json:"track"`
} `json:"playlist"`
Expand All @@ -96,10 +88,7 @@ func getReccs(cfg Listenbrainz) []string {

body := lbRequest(fmt.Sprintf("cf/recommendation/user/%s/recording", cfg.User))

err := json.Unmarshal(body, &reccs)
if err != nil {
log.Fatalf("Failed to unmarshal body: %s", err.Error())
}
json.Unmarshal(body, &reccs)

Check failure on line 91 in listenbrainz.go

View workflow job for this annotation

GitHub Actions / Lint files

Error return value of `json.Unmarshal` is not checked (errcheck)

for _, rec := range reccs.Payload.Mbids {
mbids = append(mbids, rec.RecordingMbid)
Expand All @@ -120,9 +109,8 @@ func getTracks(mbids []string) Track {

err := json.Unmarshal(body, &recordings)
if err != nil {
log.Fatalf("Failed to unmarshal body: %s", err.Error())
log.Fatalf("failed to unmarshal body: %s", err.Error())
}

for _, recording := range recordings {
tracks = append(tracks, struct {
Album string
Expand All @@ -147,21 +135,20 @@ func getWeeklyExploration(cfg Listenbrainz) (string, error) {

err := json.Unmarshal(body, &playlists)
if err != nil {
log.Fatalf("Failed to unmarshal body: %s", err.Error())
log.Fatalf("failed to unmarshal body: %s", err.Error())
}

for _, playlist := range playlists.Playlist {

_, currentWeek := time.Now().Local().ISOWeek()
_, creationWeek := playlist.Data.Date.ISOWeek()

if playlist.Data.Extension.HTTPSMusicbrainzOrgDocJspfPlaylist.AdditionalMetadata.AlgorithmMetadata.SourcePatch == "weekly-exploration" && currentWeek == creationWeek {
exploration := playlist.Data.Identifier
id := strings.Split(exploration, "/")
if strings.Contains(playlist.Data.Title, "Weekly Exploration") && currentWeek == creationWeek {
id := strings.Split(playlist.Data.Identifier, "/")
return id[len(id)-1], nil
}
}
return "", fmt.Errorf("failed to get exploration playlist")
return "", fmt.Errorf("failed to get new exploration playlist, check if ListenBrainz has generated one")
}

func parseWeeklyExploration(identifier string) Track {
Expand All @@ -174,7 +161,7 @@ func parseWeeklyExploration(identifier string) Track {

err := json.Unmarshal(body, &exploration)
if err != nil {
log.Fatalf("Failed to unmarshal body: %s", err.Error())
log.Fatalf("failed to unmarshal body: %s", err.Error())
}

for _, track := range exploration.Playlist.Tracks {
Expand All @@ -199,17 +186,17 @@ func lbRequest(path string) []byte { // Handle ListenBrainz API requests
reqString := fmt.Sprintf("https://api.listenbrainz.org/1/%s", path)
req, err := http.NewRequest(http.MethodGet, reqString, nil)
if err != nil {
log.Fatalf("Failed to initialize request: %s", err.Error())
log.Fatalf("Failed to initialize request: %v", err)
}

resp, err := client.Do(req)
if err != nil {
log.Fatalf("Failed to make request: %s", err.Error())
log.Fatalf("Failed to make request: %v", err)
}

body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Failed to read response body: %s", err.Error())
log.Fatalf("Failed to read response body: %v", err)
}
return body
}
37 changes: 15 additions & 22 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import (
"fmt"
"log"
"os"
"time"
"path"
"time"

"github.com/ilyakaznacheev/cleanenv"
)

Expand Down Expand Up @@ -39,21 +40,21 @@ type Listenbrainz struct {
func readEnv() Config {
var cfg Config

err := cleanenv.ReadConfig("./local.env", &cfg)
err := cleanenv.ReadConfig("./local.env",&cfg)
if err != nil {
panic(err)
}
return cfg
}

func cleanUp(cfg Youtube, songs []string) { // Remove downloaded webms
func cleanUp(cfg Config, songs []string) { // Remove downloaded webms

for _, song := range songs {
path := fmt.Sprintf("%s%s.webm", cfg.DownloadDir,song)
path := fmt.Sprintf("%s%s.webm", cfg.Youtube.DownloadDir,song)

err := os.Remove(path)
if err != nil {
log.Printf("Failed to remove file: %v", err.Error())
log.Printf("failed to remove file: %v", err)
}
}

Expand All @@ -66,10 +67,7 @@ func deleteSongs(cfg Youtube) { // Deletes all songs if persist equals false
}
for _, entry := range entries {
if !(entry.IsDir()) {
err := os.Remove(path.Join(cfg.DownloadDir, entry.Name()))
if err != nil {
log.Printf("failed to delete file: %v", err)
}
os.Remove(path.Join(cfg.DownloadDir, entry.Name()))
}
}
}
Expand All @@ -78,46 +76,41 @@ func deleteSongs(cfg Youtube) { // Deletes all songs if persist equals false
func main() {
cfg := readEnv()
cfg.Subsonic = genToken(cfg.Subsonic)

if !(cfg.Persist) {

deleteSongs(cfg.Youtube)
playlists, err := getDiscoveryPlaylist(cfg.Subsonic)

if err != nil {
log.Fatal(err.Error())
}
delPlaylists(playlists, cfg.Subsonic)
}


var tracks Track
var tracks Track

if cfg.Listenbrainz.Discovery == "playlist" {
id, err := getWeeklyExploration(cfg.Listenbrainz)
if err != nil {
log.Fatal(err.Error())
}
tracks = parseWeeklyExploration(id)
} else { // use recommendations from API
} else {
mbids := getReccs(cfg.Listenbrainz)
tracks = getTracks(mbids)
}
var files []string
var songs []string

for _, track := range tracks {
song, file := downloadAndFormat(track.Title, track.Artist, track.Album, cfg.Youtube)
song, file := gatherVideo(cfg.Youtube, track.Title, track.Artist, track.Album)
files = append(files, file)
songs = append(songs, song)
}

cleanUp(cfg.Youtube, files)
cleanUp(cfg, files)
scan(cfg.Subsonic)
log.Printf("Sleeping for %v minutes, to allow scan to complete..", cfg.Sleep)
log.Printf("sleeping for %v minutes, to allow scan to complete..", cfg.Sleep)
time.Sleep(time.Duration(cfg.Sleep) * time.Minute)
err := createPlaylist(cfg.Subsonic, songs, cfg.Persist)

if err != nil {
log.Fatalf("failed to create playlist: %s", err.Error())
}
createPlaylist(cfg.Subsonic, songs, cfg.Persist)
}
47 changes: 20 additions & 27 deletions subsonic.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@ type Response struct {
} `json:"subsonic-response"`
}


func genToken(cfg Subsonic) Subsonic {

var salt = make([]byte, 6)


_, err := rand.Read(salt[:])
if err != nil {
log.Fatalf("failed to read salt: %s", err.Error())
log.Fatalf("failed to read salt: %v", err)
}

saltStr := base64.StdEncoding.EncodeToString(salt)
Expand All @@ -65,6 +67,7 @@ func genToken(cfg Subsonic) Subsonic {
}

func searchTrack(cfg Subsonic, track string) (string, error) {

cleanedTrack := url.QueryEscape(track)

reqParam := fmt.Sprintf("search3?query=%s&f=json", cleanedTrack)
Expand All @@ -83,48 +86,40 @@ func searchTrack(cfg Subsonic, track string) (string, error) {
}
return resp.SubsonicResponse.SearchResult3.Song[0].ID, nil
}
func createPlaylist(cfg Subsonic, tracks []string, persist bool) {

func createPlaylist(cfg Subsonic, tracks []string, persist bool) error {
var trackIDs strings.Builder
var trackIDs string
var reqParam string

for _, track := range tracks { // Get track IDs from app and format them
ID, err := searchTrack(cfg, track)
if err != nil {
return err
}
if ID == "" { // If no ID is found, ignore track
log.Printf("can't find ID for %s", track)
continue
}
trackIDs.WriteString("&songId=" + ID)
if ID == "" || err != nil { // if ID is empty, skip song
continue
}
trackIDs += "&songId="+ID
}
if persist {
year, week := time.Now().ISOWeek()
reqParam = fmt.Sprintf("createPlaylist?name=Discover-Weekly-%v-Week%v&%s", year, week, trackIDs.String())
reqParam = fmt.Sprintf("createPlaylist?name=Discover-Weekly-%v-Week%v&%s", year, week, trackIDs)
} else {
// TODO: Delete/update playlist
reqParam = fmt.Sprintf("createPlaylist?name=Discover-Weekly&%s", trackIDs.String())
reqParam = fmt.Sprintf("createPlaylist?name=Discover-Weekly&%s", trackIDs)
}

_, err := subsonicRequest(reqParam, cfg)

return err
subsonicRequest(reqParam, cfg)

Check failure on line 107 in subsonic.go

View workflow job for this annotation

GitHub Actions / Lint files

Error return value is not checked (errcheck)
}

func scan(cfg Subsonic) {

reqParam := "startScan?f=json"
_, err := subsonicRequest(reqParam, cfg)
if err != nil {
log.Println("failed to initialize music folder scan")
}

subsonicRequest(reqParam, cfg)

Check failure on line 114 in subsonic.go

View workflow job for this annotation

GitHub Actions / Lint files

Error return value is not checked (errcheck)
}

func getDiscoveryPlaylist(cfg Subsonic) ([]string, error) {
reqParam := "getPlaylists?f=json"

var resp Response
var playlists []string
reqParam := "getPlaylists?f=json"

body, err := subsonicRequest(reqParam, cfg)
if err != nil {
return nil, err
Expand All @@ -147,14 +142,12 @@ func delPlaylists(playlists []string, cfg Subsonic) {

for _, id := range playlists {
reqParam := fmt.Sprintf("deletePlaylist?id=%s", id)
_, err := subsonicRequest(reqParam, cfg)
if err != nil {
log.Println("failed to delete playlist")
}
subsonicRequest(reqParam, cfg)

Check failure on line 145 in subsonic.go

View workflow job for this annotation

GitHub Actions / Lint files

Error return value is not checked (errcheck)
}
}

func subsonicRequest(reqParams string, cfg Subsonic) ([]byte, error) {

reqURL := fmt.Sprintf("%s/rest/%s&u=%s&t=%s&s=%s&v=%s&c=%s", cfg.URL, reqParams, cfg.User, cfg.Token, cfg.Salt, cfg.Version, cfg.ID)

req, err := http.NewRequest(http.MethodGet, reqURL, nil)
Expand Down
Loading

0 comments on commit 335d20e

Please sign in to comment.