Browse Source

Add option to specify a number of retries for failed download attempts

master
Gwendal 6 years ago
parent
commit
968748ffe6
4 changed files with 48 additions and 31 deletions
  1. +16
    -14
      README.md
  2. +18
    -7
      src/main/kotlin/bandcampcollectiondownloader/BandcampCollectionDownloader.kt
  3. +7
    -3
      src/main/kotlin/bandcampcollectiondownloader/Main.kt
  4. +7
    -7
      src/test/kotlin/bandcampcollectiodownloader/test/BandcampCollectionDownloaderTests.kt

+ 16
- 14
README.md View File

@ -4,23 +4,25 @@ A command-line tool written in Kotlin to automatically download all albums of a
```
Usage: <main class> [-h] [-c=<pathToCookiesFile>] [-d=<pathToDownloadFolder>] [-f=<audioFormat>] <bandcampUser>
<bandcampUser> The bandcamp user account from which all albums must be downloaded.
Usage: <main class> [-h] [-c=<pathToCookiesFile>] [-d=<pathToDownloadFolder>] [-f=<audioFormat>] [-r=<retries>] <bandcampUser>
<bandcampUser> The bandcamp user account from which all albums must be downloaded.
-c, --cookies-file=<pathToCookiesFile>
A JSON file with valid bandcamp credential cookies.
"Cookie Quick Manager" can be used to obtain this file after logging into bandcamp.
(visit https://addons.mozilla.org/en-US/firefox/addon/cookie-quick-manager/).
If no cookies file is provided, cookies from the local Firefox installation are used (Windows and
Linux only).
A JSON file with valid bandcamp credential cookies.
"Cookie Quick Manager" can be used to obtain this file after logging into bandcamp.
(visit https://addons.mozilla.org/en-US/firefox/addon/cookie-quick-manager/).
If no cookies file is provided, cookies from the local Firefox installation are used (Windows and Linux
only).
-d, --download-folder=<pathToDownloadFolder>
The folder in which downloaded albums must be extracted.
The following structure is considered: <pathToDownloadFolder>/<artist>/<year> - <album>.
(default: current folder)
The folder in which downloaded albums must be extracted.
The following structure is considered: <pathToDownloadFolder>/<artist>/<year> - <album>.
(default: current folder)
-f, --audio-format=<audioFormat>
The chosen audio format of the files to download (default: vorbis).
Possible values: flac, wav, aac-hi, mp3-320, aiff-lossless, vorbis, mp3-v0, alac.
-h, --help Display this help message.
```
The chosen audio format of the files to download (default: vorbis).
Possible values: flac, wav, aac-hi, mp3-320, aiff-lossless, vorbis, mp3-v0, alac.
-h, --help Display this help message.
-r, --retries=<retries> Amount of retries when downloading an album.
```
## Bandcamp authentication


+ 18
- 7
src/main/kotlin/bandcampcollectiondownloader/BandcampCollectionDownloader.kt View File

@ -31,11 +31,10 @@ data class ParsedStatDownload(
)
/**
* Core function called from the main
*/
fun downloadAll(cookiesFile: Path?, bandcampUser: String, downloadFormat: String, downloadFolder: Path) {
fun downloadAll(cookiesFile: Path?, bandcampUser: String, downloadFormat: String, downloadFolder: Path, retries: Int) {
val gson = Gson()
val cookies =
@ -87,8 +86,8 @@ fun downloadAll(cookiesFile: Path?, bandcampUser: String, downloadFormat: String
// If windows, replace colons in file names by a unicode char that looks like a colon
if (isWindows()) {
albumtitle = albumtitle.replace(':','꞉')
artist = artist.replace(':','꞉')
albumtitle = albumtitle.replace(':', '꞉')
artist = artist.replace(':', '꞉')
}
// Prepare artist and album folder
@ -96,13 +95,25 @@ fun downloadAll(cookiesFile: Path?, bandcampUser: String, downloadFormat: String
val artistFolderPath = Paths.get("$downloadFolder").resolve(artist)
val albumFolderPath = artistFolderPath.resolve(albumFolderName)
downloadAlbum(artistFolderPath, albumFolderPath, albumtitle, url, cookies, gson, isSingleTrack, artid)
// Download album, with as many retries as configured
val attempts = retries + 1
for (i in 1..attempts) {
if (i > 1) {
println("Retrying download (${i - 1}/$retries).")
}
try {
downloadAlbum(artistFolderPath, albumFolderPath, albumtitle, url, cookies, gson, isSingleTrack, artid)
} catch (e: Throwable) {
println("""Error while downloading: "${e.javaClass.name}: ${e.message}".""")
if (i == attempts) {
throw BandCampDownloaderError("Could not download album after $retries retries.")
}
}
}
}
}
class BandCampDownloaderError(s: String) : Exception(s)
fun downloadAlbum(artistFolderPath: Path?, albumFolderPath: Path, albumtitle: String, url: String, cookies: Map<String, String>, gson: Gson, isSingleTrack: Boolean, artid: String) {


+ 7
- 3
src/main/kotlin/bandcampcollectiondownloader/Main.kt View File

@ -23,7 +23,10 @@ data class Args(
var pathToDownloadFolder: Path = Paths.get("."),
@CommandLine.Option(names = ["-h", "--help"], usageHelp = true, description = ["Display this help message."])
var help: Boolean = false
var help: Boolean = false,
@CommandLine.Option(names = ["-r", "--retries"], usageHelp = false, description = ["Amount of retries when downloading an album."])
var retries: Int = 3
)
@ -31,7 +34,7 @@ data class Args(
fun main(args: Array<String>) {
// Parsing args
System.setProperty("picocli.usage.width", "120")
System.setProperty("picocli.usage.width", "130")
val parsedArgs: Args =
try {
CommandLine.populateCommand<Args>(Args(), *args)
@ -55,8 +58,9 @@ fun main(args: Array<String>) {
val cookiesFile = parsedArgs.pathToCookiesFile
val downloadFormat = parsedArgs.audioFormat
val downloadFolder = parsedArgs.pathToDownloadFolder
val retries = parsedArgs.retries
try {
downloadAll(cookiesFile, bandcampUser, downloadFormat, downloadFolder)
downloadAll(cookiesFile, bandcampUser, downloadFormat, downloadFolder, retries)
} catch (e: BandCampDownloaderError) {
System.err.println("ERROR: ${e.message}")
}


+ 7
- 7
src/test/kotlin/bandcampcollectiodownloader/test/BandcampCollectionDownloaderTests.kt View File

@ -16,42 +16,42 @@ class BandcampCollectionDownloaderTests {
@Test
fun testErrorCookiesFileNotFound() {
assertThrows<BandCampDownloaderError> {
downloadAll(Paths.get("bli"), "bli", "bli", Paths.get("bli"))
downloadAll(Paths.get("bli"), "bli", "bli", Paths.get("bli"), 0)
}
}
@Test
fun testErrorCookiesFileInvalidJson() {
assertThrows<BandCampDownloaderError> {
downloadAll(Paths.get("./test-data/notjsoncookies.json"), "bli", "bli", Paths.get("bli"))
downloadAll(Paths.get("./test-data/notjsoncookies.json"), "bli", "bli", Paths.get("bli"), 0)
}
}
@Test
fun testErrorCookiesFileInvalidContent_wrongkey() {
assertThrows<BandCampDownloaderError> {
downloadAll(Paths.get("./test-data/invalidcookies_wrongkeys.json"), "bli", "bli", Paths.get("bli"))
downloadAll(Paths.get("./test-data/invalidcookies_wrongkeys.json"), "bli", "bli", Paths.get("bli"), 0)
}
}
@Test
fun testErrorCookiesFileInvalidContent_noarray() {
assertThrows<BandCampDownloaderError> {
downloadAll(Paths.get("./test-data/invalidcookies_noarray.json"), "bli", "bli", Paths.get("bli"))
downloadAll(Paths.get("./test-data/invalidcookies_noarray.json"), "bli", "bli", Paths.get("bli"), 0)
}
}
@Test
fun testErrorInvalidBandcampUser() {
assertThrows<BandCampDownloaderError> {
downloadAll(Paths.get("./test-data/wellformedcookies.json"), "zerz1e3687dfs3df7", "bli", Paths.get("bli"))
downloadAll(Paths.get("./test-data/wellformedcookies.json"), "zerz1e3687dfs3df7", "bli", Paths.get("bli"), 0)
}
}
@Test
fun testErrorCookiesUselessForBandcampUser() {
assertThrows<BandCampDownloaderError> {
downloadAll(Paths.get("./test-data/wellformedcookies.json"), "bli", "bli", Paths.get("bli"))
downloadAll(Paths.get("./test-data/wellformedcookies.json"), "bli", "bli", Paths.get("bli"), 0)
}
}
@ -59,7 +59,7 @@ class BandcampCollectionDownloaderTests {
fun testErrorNoCookiesAtAll() {
addToEnv("HOME", "NOPE")
assertThrows<BandCampDownloaderError> {
downloadAll(null, "bli", "bli", Paths.get("bli"))
downloadAll(null, "bli", "bli", Paths.get("bli"), 0)
}
}


Loading…
Cancel
Save