/* id3.cpp */
/* Copyright (C) 2011-2020 Michael Lugmair (Lucio Carreras)
*
* This file is part of sayonara player
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "Tagging.h"
#include "Tagging/TaggingCover.h"
#include "Tagging/TaggingExtraFields.h"
#include "Tagging/TaggingUtils.h"
#include "Tagging/Models/Discnumber.h"
#include "Tagging/Models/Popularimeter.h"
#include "Utils/Algorithm.h"
#include "Utils/Utils.h"
#include "Utils/FileUtils.h"
#include "Utils/MetaData/MetaData.h"
#include "Utils/Logger/Logger.h"
#include
#include
#include
#include
using namespace Tagging::Utils;
namespace FileUtils = ::Util::File;
namespace
{
struct ReadingProperties
{
TagLib::AudioProperties::ReadStyle readStyle {TagLib::AudioProperties::ReadStyle::Fast};
bool readAudioProperties {true};
};
ReadingProperties getReadingProperties(Tagging::Quality quality)
{
ReadingProperties readingProperties;
switch(quality)
{
case Tagging::Quality::Quality:
readingProperties.readStyle = TagLib::AudioProperties::Accurate;
break;
case Tagging::Quality::Standard:
readingProperties.readStyle = TagLib::AudioProperties::Average;
break;
case Tagging::Quality::Fast:
readingProperties.readStyle = TagLib::AudioProperties::Fast;
break;
case Tagging::Quality::Dirty:
readingProperties.readStyle = TagLib::AudioProperties::Fast;
readingProperties.readAudioProperties = false;
break;
};
return readingProperties;
}
QString getTitleFromFilename(const QString& filepath)
{
const auto filename = FileUtils::getFilenameOfPath(filepath);
return (filename.size() > 4)
? filename.left(filename.length() - 4)
: filename;
}
void setDate(MetaData& track)
{
const auto fileInfo = QFileInfo(track.filepath());
QDateTime createDate;
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
createDate = fileInfo.birthTime();
if(!createDate.isValid())
{
createDate = fileInfo.metadataChangeTime();
}
#else
createDate = fileInfo.created();
#endif
if(!createDate.isValid())
{
createDate = fileInfo.lastModified();
}
track.setCreatedDate(::Util::dateToInt(createDate));
track.setModifiedDate(Util::dateToInt(fileInfo.lastModified()));
}
QStringList extractGenres(const QString& genreString)
{
auto genres = genreString.split(QRegExp(",|/|;"));
Util::Algorithm::transform(genres, [](const auto& genre) {
return genre.trimmed();
});
genres.removeDuplicates();
genres.removeAll("");
return genres;
}
}
bool Tagging::Utils::getMetaDataOfFile(MetaData& track, Quality quality)
{
const auto fileInfo = QFileInfo(track.filepath());
track.setFilesize(static_cast(fileInfo.size()));
setDate(track);
if(fileInfo.size() <= 0)
{
return false;
}
const auto readingProperties = getReadingProperties(quality);
auto fileRef = TagLib::FileRef(TagLib::FileName(track.filepath().toUtf8()),
readingProperties.readAudioProperties,
readingProperties.readStyle);
if(!isValidFile(fileRef))
{
spLog(Log::Warning, "Tagging") << "Cannot open tags for " << track.filepath() << ": Err 1";
return false;
}
const auto parsedTag = getParsedTagFromFileRef(fileRef);
if(!parsedTag.tag)
{
return false;
}
const auto artist = convertString(parsedTag.tag->artist());
const auto album = convertString(parsedTag.tag->album());
const auto title = convertString(parsedTag.tag->title());
const auto genre = convertString(parsedTag.tag->genre());
const auto comment = convertString(parsedTag.tag->comment());
const auto year = parsedTag.tag->year();
const auto trackNumber = parsedTag.tag->track();
const auto bitrate = (quality != Quality::Dirty)
? fileRef.audioProperties()->bitrate() * 1000
: 0;
const auto length = (quality != Quality::Dirty)
? fileRef.audioProperties()->length() * 1000
: 0;
const auto genres = extractGenres(genre);
track.setAlbum(album);
track.setArtist(artist);
track.setTitle(title.isEmpty() ? getTitleFromFilename(track.filepath()) : title);
track.setDurationMs(length);
track.setYear(Year(year));
track.setTrackNumber(static_cast(trackNumber));
track.setBitrate(Bitrate(bitrate));
track.setGenres(genres);
track.setComment(comment);
Tagging::readAlbumArtist(track, parsedTag);
Tagging::readDiscnumber(track, parsedTag);
Tagging::readPopularimeter(track, parsedTag);
const auto hasCover = static_cast(Tagging::hasCover(parsedTag));
track.addCustomField("has-album-art", "", QString::number(hasCover));
return true;
}
bool Tagging::Utils::setMetaDataOfFile(const MetaData& md)
{
const auto filepath = md.filepath();
const auto fileInfo = QFileInfo(filepath);
if(fileInfo.size() <= 0)
{
return false;
}
auto fileRef = TagLib::FileRef(TagLib::FileName(filepath.toUtf8()));
if(!isValidFile(fileRef))
{
spLog(Log::Warning, "Tagging") << "Cannot open tags for " << md.filepath() << ": Err 2";
return false;
}
const auto album = convertString(md.album());
const auto artist = convertString(md.artist());
const auto title = convertString(md.title());
const auto genre = convertString(md.genresToString());
const auto comment = convertString(md.comment());
const auto parsedTag = getParsedTagFromFileRef(fileRef);
if(!parsedTag.tag)
{
return false;
}
parsedTag.tag->setAlbum(album);
parsedTag.tag->setArtist(artist);
parsedTag.tag->setTitle(title);
parsedTag.tag->setGenre(genre);
parsedTag.tag->setYear(md.year());
parsedTag.tag->setTrack(md.trackNumber());
parsedTag.tag->setComment(comment);
Tagging::writePopularimeter(parsedTag, Models::Popularimeter("sayonara player", md.rating(), 0));
Tagging::writeDiscnumber(parsedTag, Models::Discnumber(md.discnumber(), md.discCount()));
Tagging::writeAlbumArtist(parsedTag, md.albumArtist());
const auto success = fileRef.save();
if(!success)
{
spLog(Log::Warning, "Tagging") << "Could not save " << md.filepath();
}
return success;
}