package main import ( "encoding/json" "fmt" "io" "log" "music-downloader/monochrome" "net/http" "os" "path" "strconv" "sync" "go.senan.xyz/taglib" ) func sendJSON(w http.ResponseWriter, data any, status int) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) if err := json.NewEncoder(w).Encode(data); err != nil { http.Error(w, err.Error(), 500) } } func main() { mClient := monochrome.NewClient(monochrome.ClientConfig{ ApiURL: "https://api.monochrome.tf", }) mux := http.NewServeMux() mux.HandleFunc("GET /search", func(w http.ResponseWriter, r *http.Request) { q := r.URL.Query().Get("q") results, err := mClient.SearchAlbum(q) if err != nil { http.Error(w, err.Error(), 404) return } sendJSON(w, results, 200) }) mux.HandleFunc("GET /download-album/{id}", func(w http.ResponseWriter, r *http.Request) { id, err := strconv.Atoi(r.PathValue("id")) if err != nil { http.Error(w, err.Error(), 500) return } album, err := mClient.AlbumInfo(id) if err != nil { http.Error(w, err.Error(), 404) return } if album == nil { http.Error(w, "album not found for some reason", 404) return } albumCoverImg, err := mClient.AlbumCoverImage(album.CoverID) if err != nil { log.Printf("failed to download album cover: %v\n", err) } if err := os.Mkdir("./Music/"+album.Title, 0777); err != nil { http.Error(w, fmt.Sprintf("failed to create album directory: %v", err), 500) return } type Response struct { sync.Mutex count int } response := new(Response) var wg sync.WaitGroup for _, track := range album.Items { wg.Go(func() { info, err := mClient.TrackInfo(track.Item.ID, track.Item.AudioQuality) if err != nil { log.Printf("failed to get track info: %v\n", err) return } manifest, err := mClient.DecodeManifest(info.Manifest) if err != nil { log.Printf("failed to decode track manifest: %v\n", err) return } if manifest.EncryptionType != monochrome.TrackManifestEncryptionNone { log.Println("file is encrypted; can't download") return } if len(manifest.URLs) == 0 { log.Println("track manifest doesn't have urls array") return } resp, err := http.Get(manifest.URLs[0]) if err != nil { log.Printf("failed to download track data: %v\n", err) return } defer resp.Body.Close() data, err := io.ReadAll(resp.Body) if err != nil { log.Printf("failed to read track data: %v\n", err) return } fPath := path.Join("./Music/", album.Title, track.Item.Title+".flac") if err := os.WriteFile(fPath, data, 0644); err != nil { log.Printf("failed to save track file: %v\n", err) return } artists := []string{} for _, artist := range album.Artists { artists = append(artists, artist.Name) } if err := taglib.WriteTags(fPath, map[string][]string{ taglib.Artist: {album.Artist.Name}, taglib.Artists: artists, taglib.AlbumArtist: artists, taglib.TrackNumber: {strconv.Itoa(track.Item.TrackNumber)}, taglib.Album: {album.Title}, taglib.Title: {track.Item.Title}, taglib.ReleaseDate: {album.ReleaseDate}, }, 0); err != nil { log.Printf("failed to add metadata tags to track file: %v\n", err) } if err := taglib.WriteImage(fPath, albumCoverImg); err != nil { log.Printf("failed to add album cover to track metadata: %v\n", err) } response.Lock() response.count++ response.Unlock() }) } wg.Wait() sendJSON(w, struct { DownloadedFilesCount int `json:"downloadedFilesCount"` TotalFilesCount int `json:"totalFilesCount"` }{ DownloadedFilesCount: response.count, TotalFilesCount: len(album.Items), }, 200) }) log.Println("starting http server") if err := http.ListenAndServe(":5000", mux); err != nil { log.Fatalf("failed to start http server: %v\n", err) } }