Bandcamp album downloader
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

142 lines
3.2 KiB

package main
import (
"net/http"
"html"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"regexp"
"strings"
)
// Ex URL
// https://t4.bcbits.com/stream/38d4a255ce4892bf15bf7d9da7c7f1f0/mp3-128/959653720?p=0&ts=1605565848&t=cd2422d5cc518447361854887348f003836fc0e7&token=1605565848_c59ee9e21807e7687d59174610689b207837cb77
var artist string
var album string
var filepath string
func getPageSource(u string) (string, error) {
resp, err := http.Get(u)
if err != nil {
return "", fmt.Errorf("Error retrieving document %q", u)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
return string(body), err
}
func getTrack(url, track string, index int, ch chan string) {
index++
url = strings.ReplaceAll(url, "&", "&")
url = url[6:len(url)-6]
if len(track) > 33 {
track = track[26:len(track)-7]
}
track = html.UnescapeString(track)
track = strings.ReplaceAll(track, " ", "-")
resp, err := http.Get(url)
if err != nil {
ch <- fmt.Sprintf("GET ERR > %s", err.Error())
return
}
defer resp.Body.Close()
out, err := os.Create(fmt.Sprintf("%s%s_%s_%02d_%s.mp3", filepath, artist, album, index, track))
if err != nil {
ch <- fmt.Sprintf("FILE ERR > %s", err.Error())
return
}
defer out.Close()
io.Copy(out, resp.Body)
ch <- fmt.Sprintf("SUCCESS > %02d - %s", index, track)
}
func create_dirs() {
if filepath == "./" {
return
}
os.MkdirAll(filepath, 0755)
}
func getArtistAndAlbum(titlestring string) {
if len(titlestring) < 18 || artist != "" || album != "" {
return
}
titlestring = titlestring[7:len(titlestring)-8]
titlesplit := strings.Split(titlestring, "|")
album = strings.TrimSpace(titlesplit[0])
if len(titlesplit) > 1 {
artist = strings.TrimSpace(titlesplit[1])
}
}
func printHelp() {
art := `borrow
Syntax: borrow [options] _url_
Options:
`
_, _ = fmt.Fprint(os.Stdout, art)
flag.PrintDefaults()
}
func main() {
flag.StringVar(&artist, "artist", "", "Overrides the artist/band name")
flag.StringVar(&album, "album", "", "Overrides the album name")
flag.StringVar(&filepath, "path", "./", "Output path")
flag.Usage = printHelp
flag.Parse()
if len(flag.Args()) < 1 {
fmt.Println("No url provided")
return
}
if !strings.HasSuffix(filepath, "/") {
filepath = filepath + "/"
}
url := flag.Args()[0]
fileWrite := make(chan string)
ure := regexp.MustCompile(`(?U)&quot;https\://t4\.bcbits\.com/stream/\w+/mp3-128/.*&quot;`)
trackNames := regexp.MustCompile(`(?Ui)<span class="track-title">.*</span>`)
title := regexp.MustCompile(`(?Ui)<title>.*</title>`)
fmt.Println("Getting page source...")
src, err := getPageSource(url)
if err != nil {
fmt.Print(err.Error())
return
}
fmt.Println("Finging tracks...")
tracks := ure.FindAllString(src, -1)
names := trackNames.FindAllString(src, -1)
t := title.FindString(src)
getArtistAndAlbum(t)
artist = strings.ReplaceAll(artist, " ", "-")
album = strings.ReplaceAll(album, " ", "-")
fmt.Printf("Found %d tracks!\n", len(tracks))
fmt.Println("Retrieving tracks")
for i, u := range tracks {
if i < len(names) {
go getTrack(u, names[i], i, fileWrite)
} else {
go getTrack(u, "?", i, fileWrite)
}
}
count := 0
for {
val := <- fileWrite
fmt.Println(val)
count++
if count+1 >= len(tracks) {
break
}
}
fmt.Println("Done.")
}