Added most functionality. Missing Cover manipulation and tagging.
This commit is contained in:
commit
0fd2679d51
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
login.json
|
||||
release.json
|
||||
cookies.txt
|
||||
mcat_dl*
|
||||
4
README
Normal file
4
README
Normal file
@ -0,0 +1,4 @@
|
||||
--- JSON Library Source ---
|
||||
https://github.com/nlohmann/json
|
||||
Release - 3.10.5
|
||||
Commit - 4f8fba14066156b73f1189a2b8bd568bde5284c5
|
||||
5
TODO
Normal file
5
TODO
Normal file
@ -0,0 +1,5 @@
|
||||
Add Cover Resize via stb - https://github.com/nothings/stb
|
||||
Add Cover rename to match extension
|
||||
Add MP3 Tagging
|
||||
Add FLAC Tagging
|
||||
Implement main
|
||||
468
main.cpp
Normal file
468
main.cpp
Normal file
@ -0,0 +1,468 @@
|
||||
// C++ Libs
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
// C Libs
|
||||
#include <stdlib.h>
|
||||
|
||||
// File Libs
|
||||
#include "json.hpp"
|
||||
|
||||
struct tag
|
||||
{
|
||||
std::string artist;
|
||||
std::string title;
|
||||
std::string number;
|
||||
};
|
||||
|
||||
constexpr const char RELEASE_JSON[] = "release.json";
|
||||
constexpr bool IS_MP3 = true;
|
||||
constexpr bool IS_FLAC = false;
|
||||
|
||||
#ifdef _WIN32
|
||||
constexpr const char FOLDER_DELIM = '\\';
|
||||
#else
|
||||
constexpr const char FOLDER_DELIM = '/';
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using nlohmann::json;
|
||||
|
||||
// 1. Login
|
||||
// 2. For each release
|
||||
|
||||
// 2.1 Download Release JSON
|
||||
// 2.2 Parse JSON
|
||||
// 2.3 Make Release Folder
|
||||
// (Catalog Number %*d - Artist - Title)
|
||||
// 2.4 Make MP3 & FLAC Folders ??? (If Tracks > 1)
|
||||
// 2.5 Download Cover
|
||||
// 2.6 Make 750x750 Cover JPEG Image
|
||||
// 2.7 Rename Cover Image (Proper Extension)
|
||||
// 2.8 Download Track (MP3)
|
||||
// (Track Num - Artist - Title)
|
||||
// 2.9 Tag MP3
|
||||
// 2.10 Download Track (FLAC)
|
||||
// (Track Num - Artist - Title)
|
||||
// 2.11 Tag FLAC
|
||||
|
||||
// 3. Logout
|
||||
|
||||
void usage(const string& name)
|
||||
{
|
||||
cout << "Usage:" << endl;
|
||||
cout << name << " <options> [path]" << endl;
|
||||
cout << " -1 <catalog release id>" << endl;
|
||||
cout << " Single release" << endl;
|
||||
cout << " -j <json file>" << endl;
|
||||
cout << " JSON file denoting release range" << endl;
|
||||
cout << " path - root path to download to" << endl;
|
||||
cout << " Default: ." << endl;
|
||||
}
|
||||
|
||||
// string get_track_name(const json& info, int track_num)
|
||||
// {
|
||||
|
||||
// return "";
|
||||
// }
|
||||
|
||||
bool system_command(const string& cmd)
|
||||
{
|
||||
int result = system(cmd.c_str());
|
||||
if (0 == result)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool login()
|
||||
{
|
||||
string cmd;
|
||||
|
||||
cmd = "curl -f -L -c cookies.txt -b cookies.txt -X POST";
|
||||
cmd += " https://player.monstercat.app/api/sign-in";
|
||||
cmd += " -H \"Content-Type: application/json\" --data-binary \"@login.json\"";
|
||||
|
||||
return system_command(cmd);
|
||||
}
|
||||
|
||||
bool logout()
|
||||
{
|
||||
string cmd;
|
||||
|
||||
cmd = "curl -f -L -c cookies.txt -b cookies.txt -X POST";
|
||||
cmd += " https://player.monstercat.app/api/sign-out";
|
||||
|
||||
return system_command(cmd);
|
||||
}
|
||||
|
||||
bool download_release_json(const string& catalog_release)
|
||||
{
|
||||
string cmd;
|
||||
|
||||
cmd = "curl -f -L -c cookies.txt -b cookies.txt";
|
||||
cmd += " https://player.monstercat.app/api/catalog/release/";
|
||||
cmd += catalog_release;
|
||||
cmd += " -o ";
|
||||
cmd += RELEASE_JSON;
|
||||
|
||||
return system_command(cmd);
|
||||
}
|
||||
|
||||
bool download_cover(const string& release_id, const string& path)
|
||||
{
|
||||
string cmd;
|
||||
|
||||
cmd = "curl -f -L -c cookies.txt -b cookies.txt";
|
||||
cmd += " \"https://www.monstercat.com/release/";
|
||||
cmd += release_id;
|
||||
cmd += "/cover\" -o \"";
|
||||
cmd += path;
|
||||
cmd += FOLDER_DELIM;
|
||||
cmd += "Cover\"";
|
||||
|
||||
return system_command(cmd);
|
||||
}
|
||||
|
||||
json parse_json_file(const string& filename)
|
||||
{
|
||||
json j;
|
||||
ifstream file;
|
||||
string str;
|
||||
|
||||
file.open(filename);
|
||||
str.assign(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
|
||||
file.close();
|
||||
j = json::parse(str);
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
pair<string, string> get_artist_feat(const string& str)
|
||||
{
|
||||
pair<string, string> result;
|
||||
int pos;
|
||||
|
||||
pos = str.find("feat.");
|
||||
if (string::npos == pos)
|
||||
{
|
||||
result.first = str;
|
||||
result.second = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
result.first = str.substr(0, pos - 1);
|
||||
result.second = str.substr(pos);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pair<string, string> get_artist_title(const json& obj)
|
||||
{
|
||||
pair<string, string> result;
|
||||
pair<string, string> artist_feat;
|
||||
string str;
|
||||
string feat;
|
||||
bool contains;
|
||||
int pos;
|
||||
|
||||
artist_feat = get_artist_feat(obj["Release"]["ArtistsTitle"]);
|
||||
result.first = artist_feat.first;
|
||||
|
||||
contains = false;
|
||||
|
||||
str = obj["Release"]["Title"];
|
||||
if (!artist_feat.second.empty())
|
||||
{
|
||||
str += " (";
|
||||
str += artist_feat.second;
|
||||
str += ')';
|
||||
contains = true;
|
||||
}
|
||||
|
||||
if (!(obj["Release"]["Version"].empty()))
|
||||
{
|
||||
if (contains)
|
||||
{
|
||||
str += " [";
|
||||
}
|
||||
else
|
||||
{
|
||||
str += " (";
|
||||
}
|
||||
|
||||
str += obj["Release"]["Version"];
|
||||
|
||||
str += contains ? ']' : ')';
|
||||
}
|
||||
|
||||
result.second = str;
|
||||
return result;
|
||||
}
|
||||
|
||||
tag get_single_tag(const json& track)
|
||||
{
|
||||
tag result;
|
||||
pair<string, string> artist_title;
|
||||
|
||||
result.number = to_string(track["TrackNumber"]);
|
||||
artist_title = get_artist_title(track);
|
||||
result.artist = artist_title.first;
|
||||
result.title = artist_title.second;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<tag> get_tags_from_release(const json& info)
|
||||
{
|
||||
vector<tag> result;
|
||||
|
||||
for (int i = 0; i < info["Tracks"].size(); ++i)
|
||||
{
|
||||
result.push_back(get_single_tag(info["Tracks"][i]));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool make_dir(const string& path)
|
||||
{
|
||||
string cmd;
|
||||
|
||||
cmd = "mkdir \"";
|
||||
cmd += path;
|
||||
cmd += '\"';
|
||||
|
||||
return system_command(cmd);
|
||||
}
|
||||
|
||||
bool download_track(const string& release_id, const string& track_id,
|
||||
const string& path, const string& filename, bool single_folder, bool is_mp3)
|
||||
{
|
||||
string format_str;
|
||||
string format_ext;
|
||||
string format_folder;
|
||||
|
||||
string cmd;
|
||||
|
||||
if (is_mp3)
|
||||
{
|
||||
format_str = "mp3_320";
|
||||
format_ext = "mp3";
|
||||
format_folder = FOLDER_DELIM;
|
||||
format_folder += "MP3";
|
||||
}
|
||||
else
|
||||
{
|
||||
format_str = "flac";
|
||||
format_ext = "flac";
|
||||
format_folder = FOLDER_DELIM;
|
||||
format_folder += "FLAC";
|
||||
}
|
||||
|
||||
cmd = "curl -f -L -c cookies.txt -b cookies.txt";
|
||||
cmd += " \"https://player.monstercat.app/api/release/";
|
||||
cmd += release_id;
|
||||
cmd += "/track-download/";
|
||||
cmd += track_id;
|
||||
cmd += "?format=";
|
||||
cmd += format_str;
|
||||
cmd += "\" -o \"";
|
||||
cmd += path;
|
||||
|
||||
if (!single_folder)
|
||||
{
|
||||
cmd += format_folder;
|
||||
}
|
||||
|
||||
cmd += FOLDER_DELIM;
|
||||
cmd += filename;
|
||||
cmd += ".";
|
||||
cmd += format_ext;
|
||||
cmd += "\"";
|
||||
|
||||
return system_command(cmd);
|
||||
}
|
||||
|
||||
string num_to_str(int num, int precision)
|
||||
{
|
||||
string result;
|
||||
|
||||
result = to_string(num);
|
||||
|
||||
while (result.size() < precision)
|
||||
{
|
||||
result.insert(result.begin(), '0');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
string get_release_dir_name(const string& main_path, int release_num,
|
||||
const string& artist, const string& title, int precision)
|
||||
{
|
||||
string path;
|
||||
|
||||
path = main_path;
|
||||
path += FOLDER_DELIM;
|
||||
path += num_to_str(release_num, precision);
|
||||
path += " - ";
|
||||
|
||||
if ((artist != "Monstercat") && (artist != "Various Artists"))
|
||||
{
|
||||
path += artist;
|
||||
path += " - ";
|
||||
}
|
||||
|
||||
path += title;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
string get_track_filename(int track_num, const string& artist, const string& title)
|
||||
{
|
||||
string filename;
|
||||
|
||||
if (0 != track_num)
|
||||
{
|
||||
filename = num_to_str(track_num, 2);
|
||||
filename += " - ";
|
||||
}
|
||||
|
||||
if ((artist != "Monstercat") && (artist != "Various Artists"))
|
||||
{
|
||||
filename += artist;
|
||||
filename += " - ";
|
||||
}
|
||||
|
||||
filename += title;
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
bool full_donwload(const string& path, const string& release_prefix,
|
||||
int release_num, const string& release_suffix)
|
||||
{
|
||||
bool ok;
|
||||
string catalog_release_str;
|
||||
json info;
|
||||
string release_dir;
|
||||
string track_filename;
|
||||
pair<string, string> release_artist_title;
|
||||
int release_precision;
|
||||
bool is_single_dir;
|
||||
vector<tag> tags;
|
||||
|
||||
catalog_release_str = release_prefix;
|
||||
catalog_release_str += num_to_str(release_num, 3);
|
||||
catalog_release_str == release_suffix;
|
||||
|
||||
release_precision = 3;
|
||||
if (release_prefix == "MCS")
|
||||
{
|
||||
release_precision = 4;
|
||||
}
|
||||
|
||||
// Download Release JSON
|
||||
ok = download_release_json(catalog_release_str);
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse JSON
|
||||
info = parse_json_file(RELEASE_JSON);
|
||||
|
||||
release_artist_title = get_artist_title(info["Release"]);
|
||||
release_dir = get_release_dir_name(path, release_num,
|
||||
release_artist_title.first, release_artist_title.second,
|
||||
release_precision);
|
||||
|
||||
// Make Release Folder
|
||||
ok = make_dir(release_dir);
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
is_single_dir = (info["Tracks"].size() == 1);
|
||||
|
||||
// Make MP3 & FLAC Folders (If more than 1 Track)
|
||||
if (!is_single_dir)
|
||||
{
|
||||
string tmp;
|
||||
|
||||
tmp = release_dir;
|
||||
tmp += FOLDER_DELIM;
|
||||
tmp += "MP3";
|
||||
|
||||
ok = make_dir(tmp);
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
tmp = release_dir;
|
||||
tmp += FOLDER_DELIM;
|
||||
tmp += "FLAC";
|
||||
|
||||
ok = make_dir(tmp);
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Download Cover
|
||||
ok = download_cover(info["Release"]["Id"], release_dir);
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Make 750x750 Cover JPEG Image
|
||||
// TODO: Rename Cover Image (Proper Extension)
|
||||
|
||||
tags = get_tags_from_release(info);
|
||||
for (int i = 0; i < info["Tracks"].size(); ++i)
|
||||
{
|
||||
track_filename = get_track_filename(info["Tracks"][i]["TrackNumber"],
|
||||
tags[i].artist, tags[i].title);
|
||||
|
||||
// Download Track (MP3)
|
||||
ok = download_track(info["Release"]["Id"], info["Tracks"][i]["Id"],
|
||||
release_dir, track_filename, is_single_dir, IS_MP3);
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Tag MP3
|
||||
|
||||
// Download Track (FLAC)
|
||||
ok = download_track(info["Release"]["Id"], info["Tracks"][i]["Id"],
|
||||
release_dir, track_filename, is_single_dir, IS_FLAC);
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Tag FLAC
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// TODO: Implement
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user