This commit is contained in:
emeric
2019-03-10 13:49:51 +01:00
parent 99d592a71e
commit 4d01a1e996
12 changed files with 272 additions and 196 deletions
+6
View File
@@ -266,6 +266,12 @@ Track::getCopyrightURL() const
return _copyrightURL != "" ? boost::make_optional<std::string>(_copyrightURL) : boost::none;
}
std::vector<Wt::Dbo::ptr<Artist>>
Track::getArtists() const
{
return std::vector<Wt::Dbo::ptr<Artist>>(_artists.begin(), _artists.end());
}
Wt::Dbo::ptr<TrackFeatures>
Track::getTrackFeatures() const
{
+27 -27
View File
@@ -96,33 +96,33 @@ class Track : public Wt::Dbo::Dbo<Track>
void setMBID(const std::string& MBID) { _MBID = MBID; }
void setCopyright(const std::string& copyright) { _copyright = std::string(copyright, 0, _maxCopyrightLength); }
void setCopyrightURL(const std::string& copyrightURL) { _copyrightURL = std::string(copyrightURL, 0, _maxCopyrightURLLength); }
void setArtist(Wt::Dbo::ptr<Artist> artist) { _artist = artist; }
void setArtists(std::vector<Wt::Dbo::ptr<Artist>>& artists);
void setRelease(Wt::Dbo::ptr<Release> release) { _release = release; }
void eraseClusters() { _clusters.clear(); }
void eraseFeatures() { /*_trackFeatures.reset();*/ }
std::size_t getScanVersion() const { return _scanVersion; }
boost::optional<std::size_t> getTrackNumber() const;
boost::optional<std::size_t> getTotalTrackNumber() const;
boost::optional<std::size_t> getDiscNumber() const;
boost::optional<std::size_t> getTotalDiscNumber() const;
std::string getName() const { return _name; }
boost::filesystem::path getPath() const { return _filePath; }
std::chrono::milliseconds getDuration() const { return _duration; }
boost::optional<int> getYear() const;
boost::optional<int> getOriginalYear() const;
Wt::WDateTime getLastWriteTime() const { return _fileLastWrite; }
Wt::WDateTime getAddedTime() const { return _fileAdded; }
const std::vector<unsigned char>& getChecksum() const { return _fileChecksum; }
bool hasCover() const { return _hasCover; }
const std::string& getMBID() const { return _MBID; }
boost::optional<std::string> getCopyright() const;
boost::optional<std::string> getCopyrightURL() const;
Wt::Dbo::ptr<Artist> getArtist() const { return _artist; }
Wt::Dbo::ptr<Release> getRelease() const { return _release; }
std::size_t getScanVersion() const { return _scanVersion; }
boost::optional<std::size_t> getTrackNumber() const;
boost::optional<std::size_t> getTotalTrackNumber() const;
boost::optional<std::size_t> getDiscNumber() const;
boost::optional<std::size_t> getTotalDiscNumber() const;
std::string getName() const { return _name; }
boost::filesystem::path getPath() const { return _filePath; }
std::chrono::milliseconds getDuration() const { return _duration; }
boost::optional<int> getYear() const;
boost::optional<int> getOriginalYear() const;
Wt::WDateTime getLastWriteTime() const { return _fileLastWrite; }
Wt::WDateTime getAddedTime() const { return _fileAdded; }
const std::vector<unsigned char>& getChecksum() const { return _fileChecksum; }
bool hasCover() const { return _hasCover; }
const std::string& getMBID() const { return _MBID; }
boost::optional<std::string> getCopyright() const;
boost::optional<std::string> getCopyrightURL() const;
std::vector<Wt::Dbo::ptr<Artist>> getArtists() const;
Wt::Dbo::ptr<Release> getRelease() const { return _release; }
std::vector<Wt::Dbo::ptr<Cluster>> getClusters() const;
bool hasTrackFeatures() const;
Wt::Dbo::ptr<TrackFeatures> getTrackFeatures() const;
bool hasTrackFeatures() const;
Wt::Dbo::ptr<TrackFeatures> getTrackFeatures() const;
std::vector<std::vector<Wt::Dbo::ptr<Cluster>>> getClusterGroups(std::vector<Wt::Dbo::ptr<ClusterType>> clusterTypes, std::size_t size) const;
@@ -148,7 +148,7 @@ class Track : public Wt::Dbo::Dbo<Track>
Wt::Dbo::field(a, _copyright, "copyright");
Wt::Dbo::field(a, _copyrightURL, "copyright_url");
Wt::Dbo::belongsTo(a, _release, "release", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::belongsTo(a, _artist, "artist", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::hasMany(a, _artists, Wt::Dbo::ManyToMany, "track_artist", "", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::hasMany(a, _clusters, Wt::Dbo::ManyToMany, "track_cluster", "", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::hasMany(a, _playlistEntries, Wt::Dbo::ManyToOne, "track");
Wt::Dbo::hasOne(a, _trackFeatures);
@@ -181,11 +181,11 @@ class Track : public Wt::Dbo::Dbo<Track>
std::string _copyright;
std::string _copyrightURL;
Wt::Dbo::ptr<Artist> _artist;
Wt::Dbo::ptr<Release> _release;
Wt::Dbo::collection<Wt::Dbo::ptr<Cluster>> _clusters;
Wt::Dbo::ptr<Release> _release;
Wt::Dbo::collection<Wt::Dbo::ptr<Artist>> _artists;
Wt::Dbo::collection<Wt::Dbo::ptr<Cluster>> _clusters;
Wt::Dbo::collection<Wt::Dbo::ptr<TrackListEntry>> _playlistEntries;
Wt::Dbo::weak_ptr<TrackFeatures> _trackFeatures;
Wt::Dbo::weak_ptr<TrackFeatures> _trackFeatures;
};
+23 -4
View File
@@ -72,7 +72,20 @@ AvFormat::parse(const boost::filesystem::path& p, bool debug)
std::cout << "TAG = " << tag << ", VAL = " << value << std::endl;
if (tag == "ARTIST")
items.insert( std::make_pair(MetaData::Type::Artist, stringTrim( value) ));
{
if (items.find(MetaData::Type::Artists) == items.end())
items[MetaData::Type::Artists] = std::set<std::string>{ stringTrim( value) };
}
else if (tag == "ARTISTS")
{
std::vector<std::string> strings {splitString(value, "/;")}; // Picard separator is '/'
std::set<std::string> artists;
for (const std::string& string : strings)
artists.insert(stringTrim(string));
items[MetaData::Type::Artists] = std::move(artists);
}
else if (tag == "ALBUM")
items.insert( std::make_pair(MetaData::Type::Album, stringTrim( value) ));
else if (tag == "TITLE")
@@ -133,7 +146,13 @@ AvFormat::parse(const boost::filesystem::path& p, bool debug)
else if (tag == "MUSICBRAINZ ARTIST ID"
|| tag == "MUSICBRAINZ_ARTISTID")
{
items.insert( std::make_pair(MetaData::Type::MusicBrainzArtistID, stringTrim(value)) );
std::vector<std::string> strings {splitString(value, "/")};
std::set<std::string> mbids;
for (const std::string& string : strings)
mbids.insert(stringTrim(string));
items[MetaData::Type::MusicBrainzArtistID] = std::move(mbids);
}
else if (tag == "MUSICBRAINZ ALBUM ID"
|| tag == "MUSICBRAINZ_ALBUMID")
@@ -152,7 +171,7 @@ AvFormat::parse(const boost::filesystem::path& p, bool debug)
}
else if (_clusterTypeNames.find(tag) != _clusterTypeNames.end())
{
std::vector<std::string> clusterNames = splitString(value, ";,\\");
std::vector<std::string> clusterNames = splitString(value, "/,;");
if (!clusterNames.empty())
{
@@ -167,7 +186,7 @@ AvFormat::parse(const boost::filesystem::path& p, bool debug)
}
catch(Av::MediaFileException& e)
{
return items;
return boost::none;
}
return items;
+34 -31
View File
@@ -22,54 +22,57 @@
#include <map>
#include <set>
#include <boost/any.hpp>
#include <boost/optional.hpp>
#include <boost/filesystem.hpp>
namespace MetaData
{
using Clusters = std::map<std::string /* type */, std::set<std::string> /* names */>;
enum class Type
struct Artist
{
// Name Type of the value
Artist, // string
Title, // string
Album, // string
Clusters, // Clusters, ex: { "genre", {"death metal", "brutal death"} }, { "albumgrouping", {"metal"} }
Duration, // std::chrono::milliseconds
TrackNumber, // size_t
DiscNumber, // size_t
TotalTrack, // size_t
TotalDisc, // size_t
Year, // int
OriginalYear, // int
HasCover, // bool
AudioStreams, // vector<AudioStream>
MusicBrainzArtistID, // string
MusicBrainzAlbumID, // string
MusicBrainzTrackID, // string
MusicBrainzRecordingID, // string
AcoustID, // string
Copyright, // string
CopyrightURL, // string
std::string name;
std::string musicBrainzArtistID;
};
// Used by Streams
struct Album
{
std::string name;
std::string musicBrainzAlbumID;
}
struct AudioStream
{
// TODO codec?
std::size_t bitRate;
unsigned bitRate;
};
// Type and associated data
// See enum Type's comments
using Items = std::map<Type, boost::any>;
using Clusters = std::map<std::string /* type */, std::set<std::string> /* names */>;
struct Track
{
std::vector<Artist> artists;
boost::optional<Artist> albumArtist;
std::string title;
std::string musicBrainzTrackID;
std::string musicBrainzRecordID;
boost::optional<Album> album;
Clusters clusters;
std::chrono::milliseconds duration {};
boost::optional<std::size_t> trackNumber;
boost::optional<std::size_t> totalTrack;
boost::optional<std::size_t> discNumber;
boost::optional<std::size_t> totalDisc;
boost::optional<int> year;
boost::optional<int> originalYear;
bool hasCover {false};
std::vector<AudioStream> audioStreams;
std::string acoustId;
std::string copyright;
std::string copyrightURL;
};
class Parser
{
public:
virtual boost::optional<Items> parse(const boost::filesystem::path& p, bool debug = false) = 0;
virtual boost::optional<Track> parse(const boost::filesystem::path& p, bool debug = false) = 0;
void setClusterTypeNames(const std::set<std::string>& clusterTypeNames) { _clusterTypeNames = clusterTypeNames; }
+37 -14
View File
@@ -34,9 +34,9 @@ namespace MetaData
boost::optional<Items>
TagLibParser::parse(const boost::filesystem::path& p, bool debug)
{
TagLib::FileRef f(p.string().c_str(),
TagLib::FileRef f {p.string().c_str(),
true, // read audio properties
TagLib::AudioProperties::Average);
TagLib::AudioProperties::Average};
if (f.isNull())
return boost::none;
@@ -47,13 +47,12 @@ TagLibParser::parse(const boost::filesystem::path& p, bool debug)
Items items;
{
TagLib::AudioProperties *properties = f.audioProperties();
const TagLib::AudioProperties *properties {f.audioProperties() };
std::chrono::milliseconds duration(properties->length() * 1000);
items.insert( std::make_pair(MetaData::Type::Duration, duration) );
items[MetaData::Type::Duration] = std::chrono::milliseconds {properties->length() * 1000};
MetaData::AudioStream audioStream = { .bitRate = static_cast<std::size_t>(properties->bitrate() * 1000) };
items.insert( std::make_pair(MetaData::Type::AudioStreams, std::vector<MetaData::AudioStream>(1, audioStream ) ));
MetaData::AudioStream audioStream {.bitRate = static_cast<std::size_t>(properties->bitrate() * 1000)};
items[MetaData::Type::AudioStreams] = std::vector<MetaData::AudioStream> {audioStream};
}
// Not that good embedded pictures handling
@@ -71,7 +70,7 @@ TagLibParser::parse(const boost::filesystem::path& p, bool debug)
if (f.tag())
{
MetaData::Clusters clusters;
TagLib::PropertyMap properties = f.file()->properties();
const TagLib::PropertyMap& properties {f.file()->properties()};
for(auto property : properties)
{
@@ -93,7 +92,23 @@ TagLibParser::parse(const boost::filesystem::path& p, bool debug)
}
if (tag == "ARTIST")
items.insert( std::make_pair(MetaData::Type::Artist, stringTrim( values.front().to8Bit(true))));
{
// Lower priority than ARTISTS
if (items.find(MetaData::Type::Artists) == items.end())
items[MetaData::Type::Artists] = std::vector<std::string>{ stringTrim( values.front().to8Bit(true)) };
}
else if (tag == "ARTISTS")
{
// Higher priority than ARTISTS
std::vector<std::string> strings {splitString(values.front().to8Bit(), "/;")}; // Picard separator is '/'
std::vector<std::string> artists;
for (const std::string& string : strings)
artists.emplace_back(stringTrim(string));
items[MetaData::Type::Artists] = std::move(artists);
}
else if (tag == "ALBUM")
items.insert( std::make_pair(MetaData::Type::Album, stringTrim( values.front().to8Bit(true))));
else if (tag == "TITLE")
@@ -104,7 +119,15 @@ TagLibParser::parse(const boost::filesystem::path& p, bool debug)
items.insert( std::make_pair(MetaData::Type::MusicBrainzTrackID, stringTrim( values.front().to8Bit(true))));
}
else if (tag == "MUSICBRAINZ_ARTISTID")
items.insert( std::make_pair(MetaData::Type::MusicBrainzArtistID, stringTrim( values.front().to8Bit(true))));
{
std::vector<std::string> strings {splitString(values.front().to8Bit(), "/")}; // Picard separator is '/'
std::vector<std::string> mbids;
for (const std::string& string : strings)
mbids.emplace_back(stringTrim(string));
items[MetaData::Type::MusicBrainzArtistID] = std::move(mbids);
}
else if (tag == "MUSICBRAINZ_ALBUMID")
items.insert( std::make_pair(MetaData::Type::MusicBrainzAlbumID, stringTrim( values.front().to8Bit(true))));
else if (tag == "MUSICBRAINZ_TRACKID")
@@ -113,25 +136,25 @@ TagLibParser::parse(const boost::filesystem::path& p, bool debug)
items.insert( std::make_pair(MetaData::Type::AcoustID, stringTrim( values.front().to8Bit(true))));
else if (tag == "TRACKTOTAL")
{
auto totalTrack = readAs<std::size_t>(values.front().to8Bit(true));
auto totalTrack {readAs<std::size_t>(values.front().to8Bit(true)) };
if (totalTrack)
items[MetaData::Type::TotalTrack] = *totalTrack;
}
else if (tag == "TRACKNUMBER")
{
// Expecting 'Number/Total'
auto strings = splitString(values.front().to8Bit(), "/");
std::vector<std::string> strings {splitString(values.front().to8Bit(), "/")};
if (!strings.empty())
{
auto number = readAs<std::size_t>(strings[0]);
auto number {readAs<std::size_t>(strings[0])};
if (number)
items.insert( std::make_pair(MetaData::Type::TrackNumber, *number ));
// Lower priority than TRACKTOTAL
if (strings.size() > 1 && items.find(MetaData::Type::TotalTrack) == items.end())
{
auto totalTrack = readAs<std::size_t>(strings[1]);
auto totalTrack {readAs<std::size_t>(strings[1])};
if (totalTrack)
items[MetaData::Type::TotalTrack] = *totalTrack;
}
+1 -1
View File
@@ -475,7 +475,7 @@ MediaScanner::scanAudioFile(const boost::filesystem::path& file, bool forceScan,
}
// ***** Artist
Artist::pointer artist;
std::set<Artist::pointer> artists;
{
std::string artistName;
std::string artistMusicBrainzID;
@@ -153,14 +153,16 @@ getSimilarArtists(Wt::Dbo::Session& session, Database::IdType artistId, std::siz
if (!track)
continue;
auto trackArtist = track->getArtist();
if (!trackArtist || trackArtist.id() == artistId)
continue;
for (auto trackArtist : track->getArtists())
{
if (!trackArtist || trackArtist.id() == artistId)
continue;
if (std::find(res.begin(), res.end(), trackArtist.id()) != res.end())
continue;
if (std::find(res.begin(), res.end(), trackArtist.id()) != res.end())
continue;
res.push_back(trackArtist.id());
res.push_back(trackArtist.id());
}
if (res.size() == maxCount)
break;
+2 -2
View File
@@ -55,7 +55,7 @@ bool readList(const std::string& str, const std::string& separators, std::list<s
}
std::vector<std::string>
splitString(std::string string, std::string separators)
splitString(const std::string& string, const std::string& separators)
{
std::vector<std::string> res;
@@ -65,7 +65,7 @@ splitString(std::string string, std::string separators)
}
std::string
joinStrings(std::vector<std::string> strings, std::string delimiter)
joinStrings(const std::vector<std::string>& strings, const std::string& delimiter)
{
return boost::algorithm::join(strings, delimiter);
}
+2 -2
View File
@@ -34,10 +34,10 @@ bool
readList(const std::string& str, const std::string& separators, std::list<std::string>& results);
std::vector<std::string>
splitString(std::string string, std::string separators);
splitString(const std::string& string, const std::string& separators);
std::string
joinStrings(std::vector<std::string> strings, std::string delimiter);
joinStrings(const std::vector<std::string>& strings, const std::string& delimiter);
std::string
stringTrim(const std::string& str, const std::string& whitespaces = " \t");