Added most functionality. Missing Cover manipulation and tagging.

This commit is contained in:
DWW 2022-07-18 17:53:23 +03:00
commit 0fd2679d51
6 changed files with 22576 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
login.json
release.json
cookies.txt
mcat_dl*

4
Makefile Normal file
View File

@ -0,0 +1,4 @@
all:
g++ main.cpp -o mcat_dl
.PHONY: all

4
README Normal file
View File

@ -0,0 +1,4 @@
--- JSON Library Source ---
https://github.com/nlohmann/json
Release - 3.10.5
Commit - 4f8fba14066156b73f1189a2b8bd568bde5284c5

5
TODO Normal file
View 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

22091
json.hpp Normal file

File diff suppressed because it is too large Load Diff

468
main.cpp Normal file
View 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;
}