186 lines
5.3 KiB
C++
186 lines
5.3 KiB
C++
/* mcopy
|
|
*
|
|
* Rename music by specifying a format
|
|
* Licensed under the GNU General Public License version 3.
|
|
* See included LICENSE file for copyright and license details.
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <filesystem>
|
|
#include <string>
|
|
#include <fstream>
|
|
#include <regex>
|
|
#include <cstdlib>
|
|
#include <tag.h>
|
|
#include <fileref.h>
|
|
|
|
#ifndef VERSION
|
|
#define VERSION "0.1"
|
|
#endif
|
|
|
|
bool ask = true;
|
|
bool quiet = false;
|
|
|
|
void help() {
|
|
std::cout <<
|
|
"mcopy " << VERSION << "\n\n" <<
|
|
"mcopy -f, --format Specify a format to output files using\n" <<
|
|
"mcopy -a, --ask Ask the user if metadata cannot be retrieved from file\n" <<
|
|
"mcopy -na, --no-ask Don't ask if metadata cannot be retrieved from file\n" <<
|
|
"mcopy -q, --quiet Don't print status messages. Warnings will still be printed\n" <<
|
|
"mcopy -nq, --no-quiet Print status messages\n" <<
|
|
"mcopy -h, --help Display help\n" <<
|
|
"mcopy -v, --version Display version\n" <<
|
|
"\n" <<
|
|
"Example: mcopy --format \"/home/anon/Music/Albums/@A/@a/@n. @A - @t.flac\" \"~/Downloads/myflac1.flac\" \"~/Downloads/myflac2.flac\n" <<
|
|
"\n" <<
|
|
"Formats:\n" <<
|
|
"\n" <<
|
|
"@A Artist\n" <<
|
|
"@a Album name\n" <<
|
|
"@t Title\n" <<
|
|
"@n Track number\n";
|
|
}
|
|
|
|
TagLib::FileRef getFile(std::string str) {
|
|
const char *cstr = str.c_str();
|
|
TagLib::FileName fn(cstr);
|
|
TagLib::FileRef file(fn);
|
|
|
|
return file;
|
|
}
|
|
|
|
void getMetadataFromFile(std::string format, std::string str) {
|
|
TagLib::FileRef file = getFile(str);
|
|
TagLib::Tag *tag = file.tag();
|
|
|
|
if (file.isNull() || !file.audioProperties()) {
|
|
std::cerr << "File '" << str << "' is not valid. Skipping.\n";
|
|
return;
|
|
}
|
|
|
|
std::string title = tag->title().to8Bit(true);
|
|
std::string album = tag->album().to8Bit(true);
|
|
std::string artist = tag->artist().to8Bit(true);
|
|
std::string track = std::to_string(tag->track());
|
|
|
|
// If information is missing, ask for the missing details
|
|
if (title.empty()) {
|
|
title = "Unknown";
|
|
|
|
if (ask) {
|
|
std::cerr << "mcopy: Could not retrieve title; please specify\n> ";
|
|
std::getline(std::cin, title);
|
|
}
|
|
} else if (album.empty()) {
|
|
album = "Unknown";
|
|
|
|
if (ask) {
|
|
std::cerr << "mcopy: Could not retrieve album; please specify\n> ";
|
|
std::getline(std::cin, album);
|
|
}
|
|
} else if (artist.empty()) {
|
|
artist = "Unknown";
|
|
|
|
if (ask) {
|
|
std::cerr << "mcopy: Could not retrieve artist; please specify\n> ";
|
|
std::getline(std::cin, artist);
|
|
}
|
|
} else if (track.empty()) {
|
|
track = "Unknown";
|
|
|
|
if (ask) {
|
|
std::cerr << "mcopy: Could not retrieve track; please specify\n> ";
|
|
std::getline(std::cin, track);
|
|
}
|
|
}
|
|
|
|
if (!std::cin) // Failed to get details, skipping
|
|
return;
|
|
|
|
std::string filename = format;
|
|
|
|
// TODO: Replace this junk
|
|
filename = std::regex_replace(filename, std::regex("@t"), title);
|
|
filename = std::regex_replace(filename, std::regex("@a"), album);
|
|
filename = std::regex_replace(filename, std::regex("@A"), artist);
|
|
filename = std::regex_replace(filename, std::regex("@n"), track);
|
|
|
|
std::ifstream ef(filename);
|
|
|
|
if (static_cast<bool>(ef.good())) {
|
|
std::cerr << "mcopy: File already exists, skipping.\n";
|
|
return;
|
|
}
|
|
|
|
std::filesystem::path fs;
|
|
std::string dir = (fs = filename).remove_filename();
|
|
|
|
if (dir.empty()) {
|
|
std::cerr << "mcopy: Failed to create directory '" << dir << "'\n";
|
|
return;
|
|
}
|
|
|
|
std::filesystem::create_directories(dir);
|
|
|
|
if (!std::filesystem::copy_file(str, filename)) {
|
|
std::cerr << "mcopy: Failed to copy file " << str << " to " << filename << "\n";
|
|
std::exit(1);
|
|
} else if (!quiet) {
|
|
std::cout << "mcopy: Copied file " << str << " to " << filename << "\n";
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
bool set{false};
|
|
std::string format;
|
|
|
|
for (int i{1}; i < argc; i++) {
|
|
std::string arg = argv[i];
|
|
|
|
if (!arg.compare("-h") || !arg.compare("--help")) {
|
|
help();
|
|
} else if (!arg.compare("-v") || !arg.compare("--version")) {
|
|
std::cout << "mcopy " << VERSION << '\n';
|
|
return 0;
|
|
} else if (!arg.compare("-a") || !arg.compare("--ask")) {
|
|
ask = true;
|
|
} else if (!arg.compare("-na") || !arg.compare("--no-ask")) {
|
|
ask = false;
|
|
} else if (!arg.compare("-q") || !arg.compare("--quiet")) {
|
|
quiet = true;
|
|
} else if (!arg.compare("-nq") || !arg.compare("--no-quiet")) {
|
|
quiet = false;
|
|
} else if (!arg.compare("-f") || !arg.compare("--format")) {
|
|
if (argc > i+1) {
|
|
format = argv[++i];
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!format.compare("")) {
|
|
std::cerr << "mcopy: You must specify a format.\n";
|
|
return 1;
|
|
}
|
|
|
|
if (argc < 4) {
|
|
std::cerr << "mcopy: You must specify a file to copy.\n";
|
|
return 1;
|
|
}
|
|
|
|
for (int i{1}; i < argc; i++) {
|
|
std::ifstream f(argv[i]);
|
|
|
|
if (static_cast<bool>(f.good())) {
|
|
set = true;
|
|
getMetadataFromFile(format, argv[i]);
|
|
}
|
|
}
|
|
|
|
if (!set) {
|
|
std::cerr << "mcopy: File not found.\n";
|
|
return 1;
|
|
}
|
|
}
|