2019-12-21 19:00:07 +01:00
|
|
|
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
|
|
|
|
|
|
|
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
|
|
|
|
|
2020-03-19 11:18:29 +01:00
|
|
|
import com.grack.nanojson.JsonObject;
|
|
|
|
import com.grack.nanojson.JsonParser;
|
|
|
|
import com.grack.nanojson.JsonParserException;
|
2020-04-26 23:05:56 +02:00
|
|
|
import com.grack.nanojson.JsonWriter;
|
2020-10-08 17:56:03 +02:00
|
|
|
import org.jsoup.Jsoup;
|
|
|
|
import org.jsoup.nodes.Document;
|
2019-12-22 00:42:26 +01:00
|
|
|
import org.schabi.newpipe.extractor.NewPipe;
|
2019-12-21 19:00:07 +01:00
|
|
|
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
2019-12-22 00:42:26 +01:00
|
|
|
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
2020-06-04 19:36:58 +02:00
|
|
|
import org.schabi.newpipe.extractor.localization.DateWrapper;
|
2020-06-04 19:04:25 +02:00
|
|
|
import org.schabi.newpipe.extractor.utils.Utils;
|
2019-12-21 19:00:07 +01:00
|
|
|
|
2019-12-22 00:42:26 +01:00
|
|
|
import java.io.IOException;
|
2020-06-04 19:36:58 +02:00
|
|
|
import java.text.ParseException;
|
|
|
|
import java.text.SimpleDateFormat;
|
|
|
|
import java.util.*;
|
2019-12-21 19:00:07 +01:00
|
|
|
|
|
|
|
public class BandcampExtractorHelper {
|
|
|
|
|
|
|
|
/**
|
2020-10-08 17:56:03 +02:00
|
|
|
* <p>Get an attribute of a web page as JSON
|
2020-04-20 23:03:12 +02:00
|
|
|
*
|
|
|
|
* <p>Originally a part of bandcampDirect.</p>
|
2019-12-21 19:00:07 +01:00
|
|
|
*
|
|
|
|
* @param html The HTML where the JSON we're looking for is stored inside a
|
|
|
|
* variable inside some JavaScript block
|
|
|
|
* @param variable Name of the variable
|
|
|
|
* @return The JsonObject stored in the variable with this name
|
|
|
|
*/
|
2020-11-24 09:41:40 +01:00
|
|
|
public static JsonObject getJsonData(final String html, final String variable)
|
|
|
|
throws JsonParserException, ArrayIndexOutOfBoundsException {
|
|
|
|
final Document document = Jsoup.parse(html);
|
|
|
|
final String json = document.getElementsByAttribute(variable).attr(variable);
|
2020-10-08 17:56:03 +02:00
|
|
|
return JsonParser.object().from(json);
|
2019-12-21 19:00:07 +01:00
|
|
|
}
|
|
|
|
|
2019-12-22 00:42:26 +01:00
|
|
|
/**
|
|
|
|
* Translate all these parameters together to the URL of the corresponding album or track
|
|
|
|
* using the mobile api
|
|
|
|
*/
|
2020-11-24 09:41:40 +01:00
|
|
|
public static String getStreamUrlFromIds(final long bandId, final long itemId, final String itemType)
|
|
|
|
throws ParsingException {
|
2019-12-22 00:42:26 +01:00
|
|
|
|
|
|
|
try {
|
2020-11-24 09:41:40 +01:00
|
|
|
final String jsonString = NewPipe.getDownloader().get(
|
2019-12-22 00:42:26 +01:00
|
|
|
"https://bandcamp.com/api/mobile/22/tralbum_details?band_id=" + bandId
|
|
|
|
+ "&tralbum_id=" + itemId + "&tralbum_type=" + itemType.substring(0, 1))
|
|
|
|
.responseBody();
|
|
|
|
|
2020-03-19 11:18:29 +01:00
|
|
|
return JsonParser.object().from(jsonString).getString("bandcamp_url").replace("http://", "https://");
|
2019-12-22 00:42:26 +01:00
|
|
|
|
2020-11-24 09:41:40 +01:00
|
|
|
} catch (final JsonParserException | ReCaptchaException | IOException e) {
|
2019-12-22 00:42:26 +01:00
|
|
|
throw new ParsingException("Ids could not be translated to URL", e);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-12-21 19:00:07 +01:00
|
|
|
/**
|
|
|
|
* Concatenate all non-null and non-empty strings together while separating them using
|
|
|
|
* the comma parameter
|
|
|
|
*/
|
2020-11-24 09:41:40 +01:00
|
|
|
public static String smartConcatenate(final String[] strings, final String comma) {
|
|
|
|
final StringBuilder result = new StringBuilder();
|
2019-12-21 19:00:07 +01:00
|
|
|
|
|
|
|
// Remove empty strings
|
2020-11-24 09:41:40 +01:00
|
|
|
final ArrayList<String> list = new ArrayList<>(Arrays.asList(strings));
|
2019-12-21 19:00:07 +01:00
|
|
|
for (int i = list.size() - 1; i >= 0; i--) {
|
2020-06-04 19:16:08 +02:00
|
|
|
if (Utils.isNullOrEmpty(list.get(i)) || list.get(i).equals("null")) {
|
2019-12-21 19:00:07 +01:00
|
|
|
list.remove(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append remaining strings to result
|
|
|
|
for (int i = 0; i < list.size(); i++) {
|
2020-11-24 09:41:40 +01:00
|
|
|
result.append(list.get(i));
|
2019-12-21 19:00:07 +01:00
|
|
|
|
|
|
|
if (i != list.size() - 1) {
|
|
|
|
// This is not the last iteration yet
|
|
|
|
result.append(comma);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return String.valueOf(result);
|
|
|
|
}
|
2020-04-26 23:05:56 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetch artist details from mobile endpoint.
|
|
|
|
* <a href=https://notabug.org/fynngodau/bandcampDirect/wiki/rewindBandcamp+%E2%80%93+Fetching+artist+details>
|
2020-11-24 14:01:31 +01:00
|
|
|
* More technical info.</a>
|
2020-04-26 23:05:56 +02:00
|
|
|
*/
|
|
|
|
public static JsonObject getArtistDetails(String id) throws ParsingException {
|
|
|
|
try {
|
|
|
|
return
|
|
|
|
JsonParser.object().from(
|
|
|
|
NewPipe.getDownloader().post(
|
|
|
|
"https://bandcamp.com/api/mobile/22/band_details",
|
|
|
|
null,
|
|
|
|
JsonWriter.string()
|
|
|
|
.object()
|
|
|
|
.value("band_id", id)
|
|
|
|
.end()
|
|
|
|
.done()
|
|
|
|
.getBytes()
|
|
|
|
).responseBody()
|
|
|
|
);
|
2020-11-24 09:41:40 +01:00
|
|
|
} catch (final IOException | ReCaptchaException | JsonParserException e) {
|
2020-04-26 23:05:56 +02:00
|
|
|
throw new ParsingException("Could not download band details", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param id The image ID
|
|
|
|
* @param album Whether this is the cover of an album
|
|
|
|
* @return Url of image with this ID in size 10 which is 1200x1200 (we could also choose size 0
|
|
|
|
* but we don't want something as large as 3460x3460 here, do we?)
|
|
|
|
*/
|
2020-11-24 09:41:40 +01:00
|
|
|
public static String getImageUrl(final long id, final boolean album) {
|
2020-04-26 23:05:56 +02:00
|
|
|
return "https://f4.bcbits.com/img/" + (album ? 'a' : "") + id + "_10.jpg";
|
|
|
|
}
|
2020-06-04 19:36:58 +02:00
|
|
|
|
2020-12-05 15:08:26 +01:00
|
|
|
/**
|
|
|
|
* @return <code>true</code> if the given url looks like it comes from a bandcamp custom domain
|
|
|
|
* or if it comes from bandcamp.com itself
|
|
|
|
*/
|
|
|
|
public static boolean isSupportedDomain(final String url) throws ParsingException {
|
|
|
|
|
|
|
|
// Accept all bandcamp.com URLs
|
|
|
|
if (url.toLowerCase().matches("https?://.+\\.bandcamp\\.com(/.*)?")) return true;
|
|
|
|
|
|
|
|
try {
|
|
|
|
// Accept all other URLs if they contain a <meta> tag that says they are generated by bandcamp
|
|
|
|
return Jsoup.parse(
|
|
|
|
NewPipe.getDownloader().get(url).responseBody()
|
|
|
|
)
|
|
|
|
.getElementsByAttributeValue("name", "generator")
|
|
|
|
.attr("content").equals("Bandcamp");
|
|
|
|
} catch (IOException | ReCaptchaException e) {
|
|
|
|
throw new ParsingException("Could not determine whether URL is custom domain " +
|
|
|
|
"(not available? network error?)");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-24 09:41:40 +01:00
|
|
|
static DateWrapper parseDate(final String textDate) throws ParsingException {
|
2020-06-04 19:36:58 +02:00
|
|
|
try {
|
2020-11-24 09:41:40 +01:00
|
|
|
final Date date = new SimpleDateFormat("dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH).parse(textDate);
|
|
|
|
final Calendar calendar = Calendar.getInstance();
|
2020-06-04 19:36:58 +02:00
|
|
|
calendar.setTime(date);
|
|
|
|
return new DateWrapper(calendar, false);
|
2020-11-24 09:41:40 +01:00
|
|
|
} catch (final ParseException e) {
|
2020-06-04 19:36:58 +02:00
|
|
|
throw new ParsingException("Could not extract date", e);
|
|
|
|
}
|
|
|
|
}
|
2019-12-21 19:00:07 +01:00
|
|
|
}
|