/* 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 #include #include #include #include #include std::pair mcopy::get_metadata_from_file(const std::string& file) { TagLib::FileRef _file = TagLib::FileRef(TagLib::FileName(file.c_str())); if (_file.isNull() || !_file.audioProperties()) { return {mcopy::FileStatus::Failure, {}}; } TagLib::Tag* tag = _file.tag(); const auto get_title = [&tag]() -> std::string { return tag->title().to8Bit(true); }; const auto get_album = [&tag]() -> std::string { return tag->album().to8Bit(true); }; const auto get_artist = [&tag]() -> std::string { return tag->artist().to8Bit(true); }; const auto get_track = [&tag]() -> std::string { return std::to_string(tag->track()); }; const auto get_year = [&tag]() -> std::string { return std::to_string(tag->year()); }; const auto get_genre = [&tag]() -> std::string { return tag->genre().to8Bit(true); }; const auto get_comment = [&tag]() -> std::string { return tag->comment().to8Bit(true); }; const auto get_extension = [&file]() -> std::string { return file.find_last_of(".") + 1 != file.npos ? file.substr(file.find_last_of(".") + 1) : ""; }; mcopy::FileData data{ get_title(), get_album(), get_artist(), get_track(), get_year(), get_genre(), get_comment(), get_extension(), }; return {mcopy::FileStatus::Success, data}; } void mcopy::write_to_file(const mcopy::Settings& settings, const std::string& format, const std::string& file) { if (!std::filesystem::is_regular_file(file)) { std::cerr << "mcopy: invalid file '" << file << "' is skipped.\n"; if (settings.strict) { std::exit(EXIT_FAILURE); } return; } std::pair pair = mcopy::get_metadata_from_file(file); if (pair.first == mcopy::FileStatus::Failure) { std::cerr << "mcopy: invalid file '" << file << "' is skipped.\n"; if (settings.strict) { std::exit(EXIT_FAILURE); } return; } mcopy::FileData& data = pair.second; #ifdef _WIN32 std::vector the_forbidden_characters{'<', '>', ':', '"', '/', '\\', '|', '?', '*'}; #else std::vector the_forbidden_characters{'/'}; #endif const std::vector data_members = {&data.title, &data.album, &data.artist, &data.track, &data.year, &data.genre, &data.comment, &data.extension}; for (const auto& it : data_members) { for (const auto& c : the_forbidden_characters) { it->erase(std::remove_if(it->begin(), it->end(), [&c](char _c) { return c == _c; }), it->end()); } } std::unordered_map delim_map{ {"@t", data.title}, {"@a", data.album}, {"@A", data.artist}, {"@n", data.track}, {"@y", data.year}, {"@g", data.genre}, {"@c", data.comment}, {"@e", data.extension}, }; std::string output_filename = format; for (auto& it : delim_map) { while (output_filename.find(it.first) != std::string::npos) { if (it.second.empty()) { it.second = "Unknown"; if (settings.ask) { std::cerr << "mcopy: failed to retrieve data to represent " << it.first << ", enter an appropriate replacement: "; std::getline(std::cin, it.second); } } output_filename.replace(output_filename.find(it.first), it.first.length(), it.second); } } std::ifstream ef(output_filename); if (ef.good() && settings.skip_existing) { std::cerr << "mcopy: file << '" << file << "' already exists, skipping\n"; return; } const std::string dir{std::filesystem::path{output_filename}.remove_filename()}; if (dir.empty()) { std::cerr << "mcopy: failed to retrieve directory: " << output_filename << "\n"; if (settings.strict) { std::exit(EXIT_FAILURE); } return; } if (!std::filesystem::exists(dir) && !std::filesystem::create_directories(dir)) { std::cerr << "mcopy: failed to create directory '" << dir << "'\n"; if (settings.strict) { std::exit(EXIT_FAILURE); } return; } if (!std::filesystem::copy_file(file, output_filename, std::filesystem::copy_options::overwrite_existing)) { std::cerr << "mcopy: failed to copy input file '" << file << "' to '" << output_filename << "'\n"; if (settings.strict) std::exit(EXIT_FAILURE); } else if (!settings.quiet) { std::cout << "mcopy: copied input file '" << file << "' to '" << output_filename << "'\n"; } }