summaryrefslogtreecommitdiffstats
path: root/src/track/trackrecord.h
blob: 82811bcdbd0de6909983c53a01b93fc4280195fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#pragma once

#include "proto/keys.pb.h"

#include "track/trackid.h"
#include "track/cue.h"
#include "track/keys.h"
#include "track/keyutils.h"
#include "track/trackmetadata.h"
#include "track/playcounter.h"

#include "library/coverart.h"
#include "util/color/rgbcolor.h"


namespace mixxx {

// Properties of tracks that are stored in the database.
class TrackRecord final {
    // Properties that parsed from and (optionally) written back to their
    // source, i.e. the corresponding file
    MIXXX_DECL_PROPERTY(TrackMetadata, metadata, Metadata)

    // The unique ID of track. This value is only set once after the track
    // has been inserted or is loaded from the library DB.
    MIXXX_DECL_PROPERTY(TrackId, id, Id)

    // TODO(uklotz): Change data type from bool to QDateTime
    //
    // Both import and export of metadata can be tracked by a single time
    // stamp, the direction doesn't matter. The value should be set to the
    // modification time stamp provided by the metadata source. This would
    // enable us to update the metadata of all tracks in the database after
    // the external metadata has been modified, i.e. if the corresponding
    // files have been modified.
    //
    // Requires a database update! We could reuse the 'header_parsed' column.
    // During migration the boolean value will be substituted with either a
    // default time stamp 1970-01-01 00:00:00.000 or NULL respectively.
    MIXXX_DECL_PROPERTY(bool /*QDateTime*/, metadataSynchronized, MetadataSynchronized)

    MIXXX_DECL_PROPERTY(CoverInfoRelative, coverInfo, CoverInfo)

    MIXXX_DECL_PROPERTY(QDateTime, dateAdded, DateAdded)
    MIXXX_DECL_PROPERTY(QString, fileType, FileType)
    MIXXX_DECL_PROPERTY(QString, url, Url)
    MIXXX_DECL_PROPERTY(PlayCounter, playCounter, PlayCounter)
    MIXXX_DECL_PROPERTY(RgbColor::optional_t, color, Color)
    MIXXX_DECL_PROPERTY(CuePosition, cuePoint, CuePoint)
    MIXXX_DECL_PROPERTY(int, rating, Rating)
    MIXXX_DECL_PROPERTY(bool, bpmLocked, BpmLocked)

  public:
    // Data migration: Reload track total from file tags if not initialized
    // yet. The added column "tracktotal" has been initialized with the
    // default value "//".
    // See also: Schema revision 26 in schema.xml
    // Public only for testing purposes!
    static const QString kTrackTotalPlaceholder;

    explicit TrackRecord(TrackId id = TrackId());
    TrackRecord(TrackRecord&&) = default;
    TrackRecord(const TrackRecord&) = default;
    /*non-virtual*/ ~TrackRecord() = default;

    TrackRecord& operator=(TrackRecord&&) = default;
    TrackRecord& operator=(const TrackRecord&) = default;

    static constexpr int kMinRating = 0;
    static constexpr int kMaxRating = 5;
    static constexpr int kNoRating = kMinRating;

    static bool isValidRating(int rating) {
        return rating >= kMinRating && rating <= kMaxRating;
    }
    bool hasRating() const {
        return getRating() != kNoRating;
    }

    void setKeys(const Keys& keys);
    void resetKeys() {
        setKeys(Keys());
    }
    const Keys& getKeys() const {
        return m_keys;
    }

    track::io::key::ChromaticKey getGlobalKey() const {
        if (getKeys().isValid()) {
            return getKeys().getGlobalKey();
        } else {
            return track::io::key::INVALID;
        }
    }
    bool updateGlobalKey(
            track::io::key::ChromaticKey key,
            track::io::key::Source keySource);

    QString getGlobalKeyText() const {
        return KeyUtils::getGlobalKeyText(getKeys());
    }
    bool updateGlobalKeyText(
            const QString& keyText,
            track::io::key::Source keySource);

    // Merge the current metadata with new and additional properties
    // imported from the file. Since these properties are not (yet)
    // stored in the library or have been added later all existing
    // data must be preserved and never be overwritten! This allows
    // a gradual migration by selectively reimporting the required
    // data when needed.
    //
    // Returns true if any property has been modified or false otherwise.
    bool mergeImportedMetadata(
            const TrackMetadata& importedMetadata);

    /// Update the stream info after opening the audio stream during
    /// a session.
    /// Returns true if the corresponding metadata properties have been
    /// updated and false otherwise.
    bool updateStreamInfoFromSource(
            const mixxx::audio::StreamInfo& streamInfoFromSource);
    /// Check if the stream info is supposed to be reliable and accurate.
    /// TODO: Also flag the stream info as "accurate" in the database and
    /// invoke updateStreamInfoFromSource() accordingly when loading tracks
    /// from the database.
    bool hasStreamInfoFromSource() const {
        return static_cast<bool>(m_streamInfoFromSource);
    }
    const std::optional<audio::StreamInfo>& getStreamInfoFromSource() const {
        return m_streamInfoFromSource;
    }

private:
    Keys m_keys;

    // TODO: Use TrackMetadata as single source of truth and do not
    // store this information redundantly.
    //
    // PROPOSAL (as implememted by https://gitlab.com/uklotzde/aoide-rs):
    // This redesign requires to track the status of some or all track
    // metadata (which includes the stream info properties) by a set of
    // bitflags:
    //  - UNRELIABLE = 0 (default)
    //    Parsed from file tags which are considered inaccurate and
    //    are often imprecise
    //  - RELIABLE =   1 << 0
    //    Reported by a decoder when opening an audio/video stream for
    //    reading. Nevertheless different decoders may report slightly
    //    differing values.
    //  - LOCKED =     1 << 1
    //    Locked metadata will not be updated automatically, neither when
    //    parsing file tags nor when decoding an audio/video stream.
    //    While locked the stale flag is never set.
    //  - STALE =      1 << 2
    //    Stale metadata should be re-imported depending on the other flags.
    std::optional<audio::StreamInfo> m_streamInfoFromSource;

    /// Equality comparison
    ///
    /// Exception: The member m_streamInfoFromSource must not be considered
    /// for equality comparisons! It is only needed for verifying consistency
    /// during updates and as a flags when a track is loaded.
    friend bool operator==(const TrackRecord& lhs, const TrackRecord& rhs);
};

inline bool operator!=(const TrackRecord& lhs, const TrackRecord& rhs) {
    return !(lhs == rhs);
}

} // namespace mixxx