Files
lms/src/libs/database/include/database/Release.hpp
T

325 lines
14 KiB
C++

/*
* Copyright (C) 2015 Emeric Poupon
*
* This file is part of LMS.
*
* LMS 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.
*
* LMS 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 LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <filesystem>
#include <optional>
#include <span>
#include <string>
#include <string_view>
#include <vector>
#include <Wt/Dbo/Dbo.h>
#include <Wt/WDateTime.h>
#include "core/EnumSet.hpp"
#include "core/UUID.hpp"
#include "database/ArtistId.hpp"
#include "database/ClusterId.hpp"
#include "database/DirectoryId.hpp"
#include "database/LabelId.hpp"
#include "database/MediaLibraryId.hpp"
#include "database/Object.hpp"
#include "database/ReleaseId.hpp"
#include "database/ReleaseTypeId.hpp"
#include "database/Types.hpp"
#include "database/UserId.hpp"
namespace lms::db
{
class Artist;
class Cluster;
class ClusterType;
class Image;
class Release;
class Session;
class Track;
class User;
class Label final : public Object<Label, LabelId>
{
public:
Label() = default;
static std::size_t getCount(Session& session);
static pointer find(Session& session, LabelId id);
static pointer find(Session& session, std::string_view name);
static RangeResults<LabelId> findOrphanIds(Session& session, std::optional<Range> range = std::nullopt);
// Accessors
std::string_view getName() const { return _name; }
template<class Action>
void persist(Action& a)
{
Wt::Dbo::field(a, _name, "name");
Wt::Dbo::hasMany(a, _releases, Wt::Dbo::ManyToMany, "release_label", "", Wt::Dbo::OnDeleteCascade);
}
private:
static constexpr std::size_t _maxNameLength{ 512 };
friend class Session;
Label(std::string_view name);
static pointer create(Session& session, std::string_view name);
std::string _name;
Wt::Dbo::collection<Wt::Dbo::ptr<Release>> _releases; // releases that match this label
};
class ReleaseType final : public Object<ReleaseType, ReleaseTypeId>
{
public:
ReleaseType() = default;
static std::size_t getCount(Session& session);
static pointer find(Session& session, ReleaseTypeId id);
static pointer find(Session& session, std::string_view name);
static RangeResults<ReleaseTypeId> findOrphanIds(Session& session, std::optional<Range> range = std::nullopt);
// Accessors
std::string_view getName() const { return _name; }
template<class Action>
void persist(Action& a)
{
Wt::Dbo::field(a, _name, "name");
Wt::Dbo::hasMany(a, _releases, Wt::Dbo::ManyToMany, "release_release_type", "", Wt::Dbo::OnDeleteCascade);
}
private:
static constexpr std::size_t _maxNameLength{ 512 };
friend class Session;
ReleaseType(std::string_view name);
static pointer create(Session& session, std::string_view name);
std::string _name;
Wt::Dbo::collection<Wt::Dbo::ptr<Release>> _releases; // releases that match this type
};
class Release final : public Object<Release, ReleaseId>
{
public:
struct FindParameters
{
std::vector<ClusterId> clusters; // if non empty, releases that belong to these clusters
std::vector<std::string_view> keywords; // if non empty, name must match all of these keywords (cannot be set with keywords)
std::string name; // must match this name (cannot be set with keywords)
ReleaseSortMethod sortMethod{ ReleaseSortMethod::None };
std::optional<Range> range;
Wt::WDateTime writtenAfter;
std::optional<DateRange> dateRange;
UserId starringUser; // only releases starred by this user
std::optional<FeedbackBackend> feedbackBackend; // and for this backend
ArtistId artist; // only releases that involved this user
core::EnumSet<TrackArtistLinkType> trackArtistLinkTypes; // and for these link types
core::EnumSet<TrackArtistLinkType> excludedTrackArtistLinkTypes; // but not for these link types
std::string releaseType; // If set, albums that has this release type
MediaLibraryId mediaLibrary; // If set, releases that has at least a track in this library
DirectoryId directory; // if set, releases in this directory (cannot be set with parent directory)
DirectoryId parentDirectory; // if set, releases in this parent directory (cannot be set with directory)
FindParameters& setClusters(std::span<const ClusterId> _clusters)
{
clusters.assign(std::cbegin(_clusters), std::cend(_clusters));
return *this;
}
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords)
{
keywords = _keywords;
return *this;
}
FindParameters& setName(std::string_view _name)
{
name = _name;
return *this;
}
FindParameters& setSortMethod(ReleaseSortMethod _sortMethod)
{
sortMethod = _sortMethod;
return *this;
}
FindParameters& setRange(std::optional<Range> _range)
{
range = _range;
return *this;
}
FindParameters& setWrittenAfter(const Wt::WDateTime& _after)
{
writtenAfter = _after;
return *this;
}
FindParameters& setDateRange(const std::optional<DateRange>& _dateRange)
{
dateRange = _dateRange;
return *this;
}
FindParameters& setStarringUser(UserId _user, FeedbackBackend _feedbackBackend)
{
starringUser = _user;
feedbackBackend = _feedbackBackend;
return *this;
}
FindParameters& setArtist(ArtistId _artist, core::EnumSet<TrackArtistLinkType> _trackArtistLinkTypes = {}, core::EnumSet<TrackArtistLinkType> _excludedTrackArtistLinkTypes = {})
{
artist = _artist;
trackArtistLinkTypes = _trackArtistLinkTypes;
excludedTrackArtistLinkTypes = _excludedTrackArtistLinkTypes;
return *this;
}
FindParameters& setReleaseType(std::string_view _releaseType)
{
releaseType = _releaseType;
return *this;
}
FindParameters& setMediaLibrary(MediaLibraryId _mediaLibrary)
{
mediaLibrary = _mediaLibrary;
return *this;
}
FindParameters& setDirectory(DirectoryId _directory)
{
directory = _directory;
return *this;
}
FindParameters& setParentDirectory(DirectoryId _parentDirectory)
{
parentDirectory = _parentDirectory;
return *this;
}
};
Release() = default;
// Accessors
static std::size_t getCount(Session& session);
static bool exists(Session& session, ReleaseId id);
static pointer find(Session& session, const core::UUID& MBID);
static pointer find(Session& session, ReleaseId id);
static void find(Session& session, ReleaseId& lastRetrievedRelease, std::size_t count, const std::function<void(const Release::pointer&)>& func, MediaLibraryId library = {});
static RangeResults<pointer> find(Session& session, const FindParameters& parameters);
static void find(Session& session, const FindParameters& parameters, const std::function<void(const pointer&)>& func);
static RangeResults<ReleaseId> findIds(Session& session, const FindParameters& parameters);
static std::size_t getCount(Session& session, const FindParameters& parameters);
static RangeResults<ReleaseId> findOrphanIds(Session& session, std::optional<Range> range = std::nullopt); // not track related
// Get the cluster of the tracks that belong to this release
// Each clusters are grouped by cluster type, sorted by the number of occurence (max to min)
// size is the max number of cluster per cluster type
std::vector<std::vector<ObjectPtr<Cluster>>> getClusterGroups(const std::vector<ClusterTypeId>& clusterTypeIds, std::size_t size) const;
// Utility functions (if all tracks have the same values, which is legit to not be the case)
Wt::WDate getDate() const;
std::optional<int> getYear() const;
Wt::WDate getOriginalDate() const;
std::optional<int> getOriginalYear() const;
std::optional<std::string> getCopyright() const;
std::optional<std::string> getCopyrightURL() const;
std::size_t getMeanBitrate() const;
// Accessors
std::string_view getName() const { return _name; }
std::string_view getSortName() const { return _sortName; }
std::optional<core::UUID> getMBID() const { return core::UUID::fromString(_MBID); }
std::optional<core::UUID> getGroupMBID() const { return core::UUID::fromString(_groupMBID); }
std::optional<std::size_t> getTotalDisc() const { return _totalDisc; }
std::size_t getDiscCount() const; // may not be total disc (if incomplete for example)
std::vector<DiscInfo> getDiscs() const;
std::chrono::milliseconds getDuration() const;
Wt::WDateTime getLastWritten() const;
std::string_view getArtistDisplayName() const { return _artistDisplayName; }
bool isCompilation() const { return _isCompilation; }
std::size_t getTrackCount() const;
std::vector<ObjectPtr<ReleaseType>> getReleaseTypes() const;
std::vector<std::string> getLabelNames() const;
std::vector<std::string> getReleaseTypeNames() const;
void visitLabels(const std::function<void(const Label::pointer& label)>& _func) const;
std::string_view getBarcode() const { return _barcode; }
ObjectPtr<Image> getImage() const;
// Setters
void setName(std::string_view name) { _name = name; }
void setSortName(std::string_view sortName) { _sortName = sortName; }
void setMBID(const std::optional<core::UUID>& mbid) { _MBID = mbid ? mbid->getAsString() : ""; }
void setGroupMBID(const std::optional<core::UUID>& mbid) { _groupMBID = mbid ? mbid->getAsString() : ""; }
void setTotalDisc(std::optional<int> totalDisc) { _totalDisc = totalDisc; }
void setArtistDisplayName(std::string_view name) { _artistDisplayName = name; }
void setCompilation(bool value) { _isCompilation = value; }
void clearLabels();
void clearReleaseTypes();
void addLabel(ObjectPtr<Label> releaseType);
void addReleaseType(ObjectPtr<ReleaseType> releaseType);
void setBarcode(std::string_view barcode) { _barcode = barcode; }
void setImage(ObjectPtr<Image> image);
// Get the artists of this release
std::vector<ObjectPtr<Artist>> getArtists(TrackArtistLinkType type = TrackArtistLinkType::Artist) const;
std::vector<ArtistId> getArtistIds(TrackArtistLinkType type = TrackArtistLinkType::Artist) const;
std::vector<ObjectPtr<Artist>> getReleaseArtists() const { return getArtists(TrackArtistLinkType::ReleaseArtist); }
bool hasVariousArtists() const;
std::vector<pointer> getSimilarReleases(std::optional<std::size_t> offset = {}, std::optional<std::size_t> count = {}) const;
bool hasDiscSubtitle() const;
template<class Action>
void persist(Action& a)
{
Wt::Dbo::field(a, _name, "name");
Wt::Dbo::field(a, _sortName, "sort_name");
Wt::Dbo::field(a, _MBID, "mbid");
Wt::Dbo::field(a, _groupMBID, "group_mbid");
Wt::Dbo::field(a, _totalDisc, "total_disc");
Wt::Dbo::field(a, _artistDisplayName, "artist_display_name");
Wt::Dbo::field(a, _isCompilation, "is_compilation");
Wt::Dbo::field(a, _barcode, "barcode");
Wt::Dbo::hasMany(a, _tracks, Wt::Dbo::ManyToOne, "release");
Wt::Dbo::belongsTo(a, _image, "image", Wt::Dbo::OnDeleteSetNull);
Wt::Dbo::hasMany(a, _labels, Wt::Dbo::ManyToMany, "release_label", "", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::hasMany(a, _releaseTypes, Wt::Dbo::ManyToMany, "release_release_type", "", Wt::Dbo::OnDeleteCascade);
}
private:
friend class Session;
Release(const std::string& name, const std::optional<core::UUID>& MBID = {});
static pointer create(Session& session, const std::string& name, const std::optional<core::UUID>& MBID = {});
Wt::WDate getDate(bool original) const;
std::optional<int> getYear(bool original) const;
static constexpr std::size_t _maxNameLength{ 512 };
std::string _name;
std::string _sortName;
std::string _MBID;
std::string _groupMBID;
std::optional<int> _totalDisc{};
std::string _artistDisplayName;
bool _isCompilation{}; // See https://picard-docs.musicbrainz.org/en/appendices/tag_mapping.html#compilation-itunes-5
std::string _barcode;
Wt::Dbo::ptr<Image> _image;
Wt::Dbo::collection<Wt::Dbo::ptr<Track>> _tracks;
Wt::Dbo::collection<Wt::Dbo::ptr<Label>> _labels;
Wt::Dbo::collection<Wt::Dbo::ptr<ReleaseType>> _releaseTypes;
};
} // namespace lms::db