18#include <taglib/fileref.h>
19#include <taglib/tag.h>
20#include <taglib/id3v2tag.h>
21#include <taglib/mpegfile.h>
22#include <taglib/attachedpictureframe.h>
23#include <taglib/tpropertymap.h>
24#include <taglib/flacfile.h>
27#include <unordered_map>
29namespace fs = std::filesystem;
40 std::string
title =
"Unknown Title";
41 std::string
artist =
"<Unknown Artist>";
42 std::string
album =
"Unknown Album";
43 std::string
genre =
"Unknown Genre";
59 template <
class Archive>
62 ar(
title,
artist,
album,
genre,
comment,
year,
track,
discNumber,
lyrics,
additionalProperties,
filePath,
duration,
bitrate);
96 auto parseFromInode(ino_t inode,
const std::string& directory) -> std::unordered_map<std::string, Metadata>;
127 std::cerr << errMsg << std::endl;
133#ifndef __EMSCRIPTEN__
138 std::cout <<
"-- [TAG PARSE] Parsing file: " << filePath << std::endl;
141 TagLib::FileRef file(filePath.c_str());
146 errMsg =
"Error: Failed to open file: " + filePath;
149 std::cout <<
"[TAG PARSE] " << errMsg << std::endl;
151 metadata.title = filePath.substr(filePath.find_last_of(
"/\\") + 1);
157 errMsg =
"Error: No tag information found in file: " + filePath;
160 std::cout <<
"[TAG PARSE] " << errMsg << std::endl;
164 metadata.title = filePath.substr(filePath.find_last_of(
"/\\") + 1);
169 TagLib::Tag* tag = file.tag();
170 metadata.title = tag->title().isEmpty() ? filePath.substr(filePath.find_last_of(
"/\\") + 1) : tag->title().to8Bit(
true);
171 metadata.artist = tag->artist().isEmpty() ?
"<Unknown Artist>" : tag->artist().to8Bit(
true);
172 metadata.album = tag->album().isEmpty() ?
"Unknown Album" : tag->album().to8Bit(
true);
173 metadata.genre = tag->genre().isEmpty() ?
"Unknown Genre" : tag->genre().to8Bit(
true);
174 metadata.comment = tag->comment().isEmpty() ?
"No Comment" : tag->comment().to8Bit(
true);
175 metadata.year = tag->year() == 0 ? 0 : tag->year();
178 metadata.track = tag->track();
180 std::cout <<
"[TAG PARSE] Track: " << (tag->track() == 0 ?
"(No Track, using fallback)" : std::to_string(tag->track())) << std::endl;
184 if (tag->track() == 0) {
185 if (metadata.artist ==
"<Unknown Artist>") {
190 if (
debugLogBool) std::cout <<
"[TAG PARSE] Assigned track number: " << metadata.track << std::endl;
193 TagLib::AudioProperties *audioProperties = file.audioProperties();
194 if (audioProperties) {
195 metadata.duration = audioProperties->length();
196 metadata.bitrate = (audioProperties->bitrate() == 0) ? -1 : audioProperties->bitrate();
199 std::cout <<
"[TAG PARSE] Audio properties found:" << std::endl;
200 std::cout <<
"[TAG PARSE] Duration: " << metadata.duration <<
" seconds" << std::endl;
201 std::cout <<
"[TAG PARSE] Bitrate: " << metadata.bitrate <<
" kbps" << std::endl;
205 std::cout <<
"[TAG PARSE] No audio properties found." << std::endl;
207 metadata.duration = 0.0f;
208 metadata.bitrate = 0;
212 metadata.filePath = filePath;
214 std::cout <<
"[TAG PARSE] File Path: " << metadata.filePath << std::endl;
218 TagLib::PropertyMap properties = file.file()->properties();
219 if (properties.contains(
"DISCNUMBER")) {
220 metadata.discNumber = properties[
"DISCNUMBER"].toString().toInt();
222 std::cout <<
"[TAG PARSE] Disc Number: " << metadata.discNumber << std::endl;
225 metadata.discNumber = 0;
227 std::cout <<
"[TAG PARSE] Disc Number: (Not Available)" << std::endl;
231 if (properties.contains(
"LYRICS")) {
232 metadata.lyrics = properties[
"LYRICS"].toString().to8Bit(
true);
234 metadata.lyrics =
"No Lyrics";
236 std::cout <<
"[TAG PARSE] Lyrics: (Not Available)" << std::endl;
241 if (!properties.isEmpty()) {
242 if (
debugLogBool) std::cout <<
"[TAG PARSE] Additional properties found!" << std::endl;
243 for (
const auto& prop : properties) {
244 std::string key = prop.first.to8Bit(
true);
245 std::string value = prop.second.toString().to8Bit(
true);
246 metadata.additionalProperties[key] = value;
255 std::unordered_map<std::string, Metadata> metadataMap;
256 std::string tempErrMsg;
258 for (
const auto& entry : fs::recursive_directory_iterator(directory)) {
259 struct stat fileStat;
260 if (stat(entry.path().c_str(), &fileStat) == 0) {
261 if (fileStat.st_ino == inode) {
263 if (
parseFile(entry.path().string(), metadata)) {
264 metadataMap[entry.path().string()] = metadata;
266 tempErrMsg =
"Error: Unable to parse metadata for file: " + entry.path().string();
271 tempErrMsg =
"Error: Unable to stat file: " + entry.path().string();
307 std::cout <<
"Title: " << metadata.
title << std::endl;
308 std::cout <<
"Artist: " << metadata.
artist << std::endl;
309 std::cout <<
"Album: " << metadata.
album << std::endl;
310 std::cout <<
"Genre: " << metadata.
genre << std::endl;
311 std::cout <<
"Comment: " << metadata.
comment << std::endl;
312 std::cout <<
"Year: " << metadata.
year << std::endl;
313 std::cout <<
"Track: " << metadata.
track << std::endl;
314 std::cout <<
"Disc Number: " << metadata.
discNumber << std::endl;
315 std::cout <<
"Lyrics: " << std::endl << metadata.
lyrics << std::endl;
316 std::cout <<
"+++++++++++++++++++++++++++" << std::endl;
325auto extractThumbnail(
const std::string& audioFilePath,
const std::string& outputImagePath) ->
bool {
327 std::string extension = audioFilePath.substr(audioFilePath.find_last_of(
'.') + 1);
329 if (extension ==
"mp3") {
331 TagLib::MPEG::File mpegFile(audioFilePath.c_str());
332 if (!mpegFile.isValid()) {
333 std::cerr <<
"Error: Could not open MP3 file." << std::endl;
337 TagLib::ID3v2::Tag* id3v2Tag = mpegFile.ID3v2Tag();
339 std::cerr <<
"Error: No ID3v2 tags found in the MP3 file." << std::endl;
343 const TagLib::ID3v2::FrameList& frameList = id3v2Tag->frameListMap()[
"APIC"];
344 if (frameList.isEmpty()) {
345 std::cerr <<
"Error: No embedded album art found in the MP3 file." << std::endl;
349 auto* apicFrame =
dynamic_cast<TagLib::ID3v2::AttachedPictureFrame*
>(frameList.front());
351 std::cerr <<
"Error: Failed to retrieve album art from MP3." << std::endl;
355 const auto& pictureData = apicFrame->picture();
356 std::ofstream outputFile(outputImagePath, std::ios::binary);
358 std::cerr <<
"Error: Could not create output image file." << std::endl;
362 outputFile.write(
reinterpret_cast<const char*
>(pictureData.data()), pictureData.size());
364 }
else if (extension ==
"flac") {
366 TagLib::FLAC::File flacFile(audioFilePath.c_str(),
true);
367 if (!flacFile.isValid()) {
368 std::cerr <<
"Error: Could not open FLAC file." << std::endl;
372 const TagLib::List<TagLib::FLAC::Picture*>& pictureList = flacFile.pictureList();
373 if (pictureList.isEmpty()) {
374 std::cerr <<
"Error: No album art found in the FLAC file." << std::endl;
378 const auto& pictureData = pictureList.front()->data();
379 std::ofstream outputFile(outputImagePath, std::ios::binary);
381 std::cerr <<
"Error: Could not create output image file." << std::endl;
385 outputFile.write(
reinterpret_cast<const char*
>(pictureData.data()), pictureData.size());
388 std::cerr <<
"Error: Unsupported file format. Only MP3 and FLAC are supported." << std::endl;
325auto extractThumbnail(
const std::string& audioFilePath,
const std::string& outputImagePath) ->
bool {
…}
auto parseFile(const std::string &filePath, Metadata &metadata) -> bool
Parse metadata from an audio file.
Definition taglib_parser.h:136
auto parseFromInode(ino_t inode, const std::string &directory) -> std::unordered_map< std::string, Metadata >
Parse metadata from files in a directory based on inode.
Definition taglib_parser.h:254
TagLibParser(const std::string &debugString)
Constructor for TagLibParser.
Definition taglib_parser.h:120
void printMetadata(const Metadata &metadata)
Prints the metadata of a song to the console.
Definition taglib_parser.h:306
std::string debugLogBoolStr
Definition taglib_parser.h:113
int unknownArtistTracks
Definition taglib_parser.h:117
bool debugLogBool
Definition taglib_parser.h:115
auto extractThumbnail(const std::string &audioFilePath, const std::string &outputImagePath) -> bool
Extracts the thumbnail (album art) from an audio file and saves it to an image file....
Definition taglib_parser.h:325
void sendErrMsg(std::string debugLogBoolStr, std::string errMsg)
Sends an error message based on the debug log setting.
Definition taglib_parser.h:122