monstercat-downloader/monstercat_dl.cpp

448 lines
8.3 KiB
C++

#include "monstercat_dl.h"
#include "curl_dl.h"
#include <fstream>
#include <sstream>
using std::endl;
using std::map;
using std::ofstream;
using std::string;
using std::stringstream;
using nlohmann::json;
static bool json_extract(const json& j, const string& key, string& value)
{
bool result = false;
if (j.contains(key) && j[key].is_string())
{
value = j[key];
result = true;
}
return result;
}
static string trim_whitespace(const string& s)
{
const string whitespace = " \t\r\n\f\v";
string result(s);
size_t pos;
pos = result.find_first_not_of(whitespace);
result.erase(0, pos);
pos = result.find_last_not_of(whitespace);
result.erase(pos + 1);
return result;
}
string Monstercat_DL::calc_proper_artist(const string& artist_raw)
{
size_t pos;
string result;
pos = artist_raw.find("feat.");
if (string::npos == pos)
{
return artist_raw;
}
result = artist_raw.substr(0, pos);
result = trim_whitespace(result);
return result;
}
string Monstercat_DL::calc_proper_title(const string& artist_raw,
const string& title_raw, const string& version_raw)
{
size_t pos;
string result;
result = trim_whitespace(title_raw);
pos = artist_raw.find("feat.");
if (string::npos != pos)
{
result += " (";
result += trim_whitespace(artist_raw.substr(pos));
result += ")";
}
if (!version_raw.empty())
{
result += " (";
result += trim_whitespace(version_raw);
result += ")";
}
return result;
}
Monstercat_DL::Monstercat_DL()
: m_log(&std::cout), m_base_url("https://player.monstercat.app/api/"),
m_is_logged_in(false)
{}
Monstercat_DL::~Monstercat_DL()
{
}
bool Monstercat_DL::login(const string& user, const string& pass)
{
CURL_DL& curl = CURL_DL::get_handle();
bool ok;
json data;
string url;
stringstream out;
data["Email"] = user;
data["Password"] = pass;
url = m_base_url;
url += "sign-in";
ok = curl.post_json(url, data, &out);
if (!ok)
{
if (nullptr != m_log)
{
*m_log << "Could not post json" << endl;
*m_log << "CURL:" << curl.get_error() << endl;
}
return false;
}
data.clear();
out >> data;
if (data.contains("Needs2FA") &&
data["Needs2FA"].is_boolean() &&
data["Needs2FA"] == false)
{
m_is_logged_in = true;
return true;
}
return false;
}
bool Monstercat_DL::logout()
{
CURL_DL& curl = CURL_DL::get_handle();
bool ok;
json data;
string url;
if (!m_is_logged_in)
{
return false;
}
url = m_base_url;
url += "sign-out";
ok = curl.post_json(url, data, nullptr);
if (!ok)
{
if (nullptr != m_log)
{
*m_log << "Could not post json" << endl;
*m_log << "CURL:" << curl.get_error() << endl;
}
return false;
}
return ok;
}
json Monstercat_DL::get_release_json(const string& catalog_id)
{
CURL_DL& curl = CURL_DL::get_handle();
bool ok;
json result;
string url;
stringstream out;
url = m_base_url;
url += "catalog/release/";
url += catalog_id;
ok = curl.download(url, &out);
if (!ok)
{
if (nullptr != m_log)
{
*m_log << "Could not download json" << endl;
*m_log << "CURL:" << curl.get_error() << endl;
}
return result;
}
out >> result;
return result;
}
json Monstercat_DL::get_browse_json(const string& release_id)
{
CURL_DL& curl = CURL_DL::get_handle();
bool ok;
json result;
string url;
stringstream out;
url = m_base_url;
url += "catalog/browse?offset=0&limit=0&search=&sort=&nogold=false&releaseId=";
url += release_id;
ok = curl.download(url, &out);
if (!ok)
{
if (nullptr != m_log)
{
*m_log << "Could not download json" << endl;
*m_log << "CURL:" << curl.get_error() << endl;
}
return result;
}
out >> result;
return result;
}
Release Monstercat_DL::parse_release_json(const json& release_json)
{
Release result;
json release_object;
json tracks_object;
string artist_raw;
string title_raw;
string version_raw;
Track temp_track;
if (release_json.contains("Release"))
{
release_object = release_json["Release"];
}
else
{
return result;
}
// Parse Release
json_extract(release_object, "CatalogId", result.catalog_id);
json_extract(release_object, "Id", result.id);
json_extract(release_object, "Type", result.type);
json_extract(release_object, "ReleaseDate", result.release_date);
{
size_t pos = result.release_date.find('T');
if (pos != string::npos)
{
result.release_date.erase(pos);
}
}
json_extract(release_object, "ArtistsTitle", artist_raw);
json_extract(release_object, "Title", title_raw);
json_extract(release_object, "Version", version_raw);
result.artist = calc_proper_artist(artist_raw);
result.title = calc_proper_title(artist_raw, title_raw, version_raw);
if (release_json.contains("Tracks") && release_json["Tracks"].is_array())
{
tracks_object = release_json["Tracks"];
}
else
{
return result;
}
// Parse Tracks
for (json& curr_track : tracks_object)
{
if (curr_track.contains("TrackNumber") &&
curr_track["TrackNumber"].is_number_integer())
{
temp_track.number = curr_track["TrackNumber"];
}
json_extract(curr_track, "Id", temp_track.id);
json_extract(curr_track, "ArtistsTitle", artist_raw);
json_extract(curr_track, "Title", title_raw);
json_extract(curr_track, "Version", version_raw);
temp_track.artist = calc_proper_artist(artist_raw);
temp_track.title = calc_proper_title(artist_raw, title_raw, version_raw);
result.tracks.push_back(temp_track);
}
return result;
}
void Monstercat_DL::add_extended_mixes(Release& release, const json& browse_json)
{
int track_num;
json data_obj;
json file_obj;
Track *track;
string mime_type;
if (browse_json.contains("Data") && browse_json["Data"].is_array())
{
data_obj = browse_json["Data"];
}
for (json& track_json : data_obj)
{
// Get the track number
track_num = 0;
if (track_json.contains("TrackNumber") && track_json["TrackNumber"].is_number_integer())
{
track_num = track_json["TrackNumber"];
}
// File must exist and be an object
if (!track_json.contains("File") || !track_json["File"].is_object())
{
continue;
}
// Find track
track = nullptr;
for (int i = 0; i < release.tracks.size(); ++i)
{
if (release.tracks[i].number == track_num)
{
track = &(release.tracks[i]);
break;
}
}
if (nullptr == track)
{
if (nullptr != m_log)
{
*m_log << "Could not find track number " << track_num << " for catalog id " << release.catalog_id << endl;
}
return;
}
file_obj = track_json["File"];
json_extract(file_obj, "Id", track->extended_mix_file_id);
json_extract(file_obj, "MimeType", mime_type);
if ("audio/wav" == mime_type)
{
track->extended_mix_extension = ".wav";
}
else
{
if (nullptr != m_log)
{
*m_log << "Unknown MIME type for catalog id " << release.catalog_id << " - " << mime_type << endl;
}
}
return;
}
}
bool Monstercat_DL::download_cover(const string& catalog_id, const string& path)
{
CURL_DL& curl = CURL_DL::get_handle();
bool ok;
string url;
stringstream out;
ofstream out_file;
map<string, string> out_headers;
string filename;
url = "https://www.monstercat.com/release/";
url += catalog_id;
url += "/cover";
ok = curl.download(url, &out, nullptr, &out_headers);
if (!ok)
{
if (nullptr != m_log)
{
*m_log << "Could not download image" << endl;
*m_log << "CURL:" << curl.get_error() << endl;
}
return false;
}
filename = path;
if (0 == out_headers.count("content-type"))
{
if (nullptr != m_log)
{
*m_log << "No content-type" << endl;
}
}
else
{
if (out_headers["content-type"] == "image/jpeg")
{
filename += ".jpg";
}
else if (out_headers["content-type"] == "image/png")
{
filename += ".png";
}
else
{
if (nullptr != m_log)
{
*m_log << "Unknown Content-Type for cover - " << out_headers["content-type"] << endl;
}
}
}
out_file.open(filename, std::ios::binary);
if (!out_file.is_open())
{
if (nullptr != m_log)
{
*m_log << "Could not open file for write" << endl;
*m_log << filename << endl;
}
return false;
}
out_file << out.rdbuf();
out_file.close();
return true;
}
bool Monstercat_DL::download_track(const string& release_id,
const string& track_id, const string& path)
{
string url;
url = m_base_url;
url += "release/";
url += release_id;
url += "/track-download/";
url += track_id;
return false;
}
bool Monstercat_DL::download_file(const string& file_id, const string& path)
{
string url;
url = m_base_url;
url += "file/";
url += file_id;
url += "/open?download=true";
return false;
}