1 /*
2  * Strawberry Music Player
3  * This file was part of Clementine.
4  * Copyright 2010, David Sansome <me@davidsansome.com>
5  * Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
6  *
7  * Strawberry is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Strawberry is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with Strawberry.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #ifndef SONG_H
23 #define SONG_H
24 
25 #include "config.h"
26 
27 #include <QtGlobal>
28 #include <QSharedData>
29 #include <QSharedDataPointer>
30 #include <QMetaType>
31 #include <QList>
32 #include <QSet>
33 #include <QMap>
34 #include <QVariant>
35 #include <QString>
36 #include <QStringList>
37 #include <QRegularExpression>
38 #include <QUrl>
39 #include <QFileInfo>
40 #include <QImage>
41 #include <QIcon>
42 
43 class SqlQuery;
44 
45 namespace Engine {
46 struct SimpleMetaBundle;
47 }  // namespace Engine
48 
49 namespace spb {
50 namespace tagreader {
51 class SongMetadata;
52 }  // namespace tagreader
53 }  // namespace spb
54 
55 #ifdef HAVE_LIBGPOD
56 struct _Itdb_Track;
57 #endif
58 
59 #ifdef HAVE_LIBMTP
60 struct LIBMTP_track_struct;
61 #endif
62 
63 class SqlRow;
64 
65 class Song {
66 
67  public:
68 
69   enum Source {
70     Source_Unknown = 0,
71     Source_LocalFile = 1,
72     Source_Collection = 2,
73     Source_CDDA = 3,
74     Source_Device = 4,
75     Source_Stream = 5,
76     Source_Tidal = 6,
77     Source_Subsonic = 7,
78     Source_Qobuz = 8,
79     Source_SomaFM = 9,
80     Source_RadioParadise = 10
81   };
82 
83   // Don't change these values - they're stored in the database, and defined in the tag reader protobuf.
84   // If a new lossless file is added, also add it to IsFileLossless().
85 
86   enum FileType {
87     FileType_Unknown = 0,
88     FileType_WAV = 1,
89     FileType_FLAC = 2,
90     FileType_WavPack = 3,
91     FileType_OggFlac = 4,
92     FileType_OggVorbis = 5,
93     FileType_OggOpus = 6,
94     FileType_OggSpeex = 7,
95     FileType_MPEG = 8,
96     FileType_MP4 = 9,
97     FileType_ASF = 10,
98     FileType_AIFF = 11,
99     FileType_MPC = 12,
100     FileType_TrueAudio = 13,
101     FileType_DSF = 14,
102     FileType_DSDIFF = 15,
103     FileType_PCM = 16,
104     FileType_APE = 17,
105     FileType_MOD = 18,
106     FileType_S3M = 19,
107     FileType_XM = 20,
108     FileType_IT = 21,
109     FileType_CDDA = 90,
110     FileType_Stream = 91,
111   };
112 
113   Song(Song::Source source = Song::Source_Unknown);
114   Song(const Song &other);
115   ~Song();
116 
117   static const QStringList kColumns;
118   static const QString kColumnSpec;
119   static const QString kBindSpec;
120   static const QString kUpdateSpec;
121 
122   static const QStringList kFtsColumns;
123   static const QString kFtsColumnSpec;
124   static const QString kFtsBindSpec;
125   static const QString kFtsUpdateSpec;
126 
127   static const QString kManuallyUnsetCover;
128   static const QString kEmbeddedCover;
129 
130   static const QRegularExpression kAlbumRemoveDisc;
131   static const QRegularExpression kAlbumRemoveMisc;
132   static const QRegularExpression kTitleRemoveMisc;
133 
134   static const QString kVariousArtists;
135 
136   static const QStringList kArticles;
137 
138   static const QStringList kAcceptedExtensions;
139 
140   static QString JoinSpec(const QString &table);
141 
142   static Source SourceFromURL(const QUrl &url);
143   static QString TextForSource(Source source);
144   static QString DescriptionForSource(Source source);
145   static Song::Source SourceFromText(const QString &source);
146   static QIcon IconForSource(Source source);
147   static QString TextForFiletype(FileType filetype);
148   static QString ExtensionForFiletype(FileType filetype);
149   static QIcon IconForFiletype(FileType filetype);
150 
TextForSource()151   QString TextForSource() const { return TextForSource(source()); }
DescriptionForSource()152   QString DescriptionForSource() const { return DescriptionForSource(source()); }
IconForSource()153   QIcon IconForSource() const { return IconForSource(source()); }
TextForFiletype()154   QString TextForFiletype() const { return TextForFiletype(filetype()); }
IconForFiletype()155   QIcon IconForFiletype() const { return IconForFiletype(filetype()); }
156 
157   bool IsFileLossless() const;
158   static FileType FiletypeByMimetype(const QString &mimetype);
159   static FileType FiletypeByDescription(const QString &text);
160   static FileType FiletypeByExtension(const QString &ext);
161   static QString ImageCacheDir(const Song::Source source);
162 
163   // Sort songs alphabetically using their pretty title
164   static int CompareSongsName(const Song &song1, const Song &song2);
165   static void SortSongsListAlphabetically(QList<Song> *songs);
166 
167   // Constructors
168   void Init(const QString &title, const QString &artist, const QString &album, qint64 length_nanosec);
169   void Init(const QString &title, const QString &artist, const QString &album, qint64 beginning, qint64 end);
170   void InitFromProtobuf(const spb::tagreader::SongMetadata &pb);
171   void InitFromQuery(const SqlRow &query, bool reliable_metadata, int col = 0);
172   void InitFromFilePartial(const QString &filename, const QFileInfo &fileinfo);
173   void InitArtManual();
174   void InitArtAutomatic();
175 
176   bool MergeFromSimpleMetaBundle(const Engine::SimpleMetaBundle &bundle);
177 
178 #ifdef HAVE_LIBGPOD
179   void InitFromItdb(_Itdb_Track *track, const QString &prefix);
180   void ToItdb(_Itdb_Track *track) const;
181 #endif
182 
183 #ifdef HAVE_LIBMTP
184   void InitFromMTP(const LIBMTP_track_struct *track, const QString &host);
185   void ToMTP(LIBMTP_track_struct *track) const;
186 #endif
187 
188   // Copies important statistics from the other song to this one, overwriting any data that already exists.
189   // Useful when you want updated tags from disk but you want to keep user stats.
190   void MergeUserSetData(const Song &other);
191 
192   // Save
193   void BindToQuery(SqlQuery *query) const;
194   void BindToFtsQuery(SqlQuery *query) const;
195   void ToXesam(QVariantMap *map) const;
196   void ToProtobuf(spb::tagreader::SongMetadata *pb) const;
197 
198   // Simple accessors
199   bool is_valid() const;
200   bool is_unavailable() const;
201   int id() const;
202 
203   const QString &title() const;
204   const QString &title_sortable() const;
205   const QString &album() const;
206   const QString &album_sortable() const;
207   const QString &artist() const;
208   const QString &artist_sortable() const;
209   const QString &albumartist() const;
210   const QString &albumartist_sortable() const;
211   int track() const;
212   int disc() const;
213   int year() const;
214   int originalyear() const;
215   const QString &genre() const;
216   bool compilation() const;
217   const QString &composer() const;
218   const QString &performer() const;
219   const QString &grouping() const;
220   const QString &comment() const;
221   const QString &lyrics() const;
222 
223   QString artist_id() const;
224   QString album_id() const;
225   QString song_id() const;
226 
227   qint64 beginning_nanosec() const;
228   qint64 end_nanosec() const;
229   qint64 length_nanosec() const;
230 
231   int bitrate() const;
232   int samplerate() const;
233   int bitdepth() const;
234 
235   Source source() const;
236   int directory_id() const;
237   const QUrl &url() const;
238   const QString &basefilename() const;
239   FileType filetype() const;
240   int filesize() const;
241   qint64 mtime() const;
242   qint64 ctime() const;
243 
244   QString fingerprint() const;
245 
246   int playcount() const;
247   int skipcount() const;
248   qint64 lastplayed() const;
249   qint64 lastseen() const;
250 
251   bool compilation_detected() const;
252   bool compilation_off() const;
253   bool compilation_on() const;
254 
255   const QUrl &art_automatic() const;
256   const QUrl &art_manual() const;
257 
258   const QString &cue_path() const;
259   bool has_cue() const;
260 
261   double rating() const;
262 
263   const QString &effective_album() const;
264   int effective_originalyear() const;
265   const QString &effective_albumartist() const;
266   const QString &effective_albumartist_sortable() const;
267 
268   bool is_collection_song() const;
269   bool is_stream() const;
270   bool is_radio() const;
271   bool is_cdda() const;
272   bool is_metadata_good() const;
273   bool art_automatic_is_valid() const;
274   bool art_manual_is_valid() const;
275   bool has_valid_art() const;
276   bool is_compilation() const;
277   bool stream_url_can_expire() const;
278   bool is_module_music() const;
279 
280   // Playlist views are special because you don't want to fill in album artists automatically for compilations, but you do for normal albums:
281   const QString &playlist_albumartist() const;
282   const QString &playlist_albumartist_sortable() const;
283 
284   // Returns true if this Song had it's cover manually unset by user.
285   bool has_manually_unset_cover() const;
286   // This method represents an explicit request to unset this song's cover.
287   void set_manually_unset_cover();
288 
289   // Returns true if this song (it's media file) has an embedded cover.
290   bool has_embedded_cover() const;
291   // Sets a flag saying that this song (it's media file) has an embedded cover.
292   void set_embedded_cover();
293 
294   void clear_art_automatic();
295   void clear_art_manual();
296 
297   static bool save_embedded_cover_supported(const FileType filetype);
save_embedded_cover_supported()298   bool save_embedded_cover_supported() const { return url().isLocalFile() && save_embedded_cover_supported(filetype()) && !has_cue(); };
299 
300   const QUrl &stream_url() const;
301   const QUrl &effective_stream_url() const;
302   const QImage &image() const;
303   bool init_from_file() const;
304 
305   // Pretty accessors
306   QString PrettyTitle() const;
307   QString PrettyTitleWithArtist() const;
308   QString PrettyLength() const;
309   QString PrettyYear() const;
310   QString PrettyOriginalYear() const;
311 
312   QString TitleWithCompilationArtist() const;
313 
314   QString SampleRateBitDepthToText() const;
315 
316   QString PrettyRating() const;
317 
318   // Setters
319   bool IsEditable() const;
320 
321   void set_id(int id);
322   void set_valid(bool v);
323 
324   void set_title(const QString &v);
325   void set_album(const QString &v);
326   void set_artist(const QString &v);
327   void set_albumartist(const QString &v);
328   void set_track(int v);
329   void set_disc(int v);
330   void set_year(int v);
331   void set_originalyear(int v);
332   void set_genre(const QString &v);
333   void set_compilation(bool v);
334   void set_composer(const QString &v);
335   void set_performer(const QString &v);
336   void set_grouping(const QString &v);
337   void set_comment(const QString &v);
338   void set_lyrics(const QString &v);
339 
340   void set_artist_id(const QString &v);
341   void set_album_id(const QString &v);
342   void set_song_id(const QString &v);
343 
344   void set_beginning_nanosec(qint64 v);
345   void set_end_nanosec(qint64 v);
346   void set_length_nanosec(qint64 v);
347 
348   void set_bitrate(int v);
349   void set_samplerate(int v);
350   void set_bitdepth(int v);
351 
352   void set_source(Source v);
353   void set_directory_id(int v);
354   void set_url(const QUrl &v);
355   void set_basefilename(const QString &v);
356   void set_filetype(FileType v);
357   void set_filesize(int v);
358   void set_mtime(qint64 v);
359   void set_ctime(qint64 v);
360   void set_unavailable(bool v);
361 
362   void set_fingerprint(const QString &v);
363 
364   void set_playcount(int v);
365   void set_skipcount(int v);
366   void set_lastplayed(qint64 v);
367   void set_lastseen(qint64 v);
368 
369   void set_compilation_detected(bool v);
370   void set_compilation_on(bool v);
371   void set_compilation_off(bool v);
372 
373   void set_art_automatic(const QUrl &v);
374   void set_art_manual(const QUrl &v);
375 
376   void set_cue_path(const QString &v);
377 
378   void set_rating(const double v);
379 
380   void set_stream_url(const QUrl &v);
381   void set_image(const QImage &i);
382 
383   // Comparison functions
384   bool IsMetadataEqual(const Song &other) const;
385   bool IsMetadataAndMoreEqual(const Song &other) const;
386   bool IsOnSameAlbum(const Song &other) const;
387   bool IsSimilar(const Song &other) const;
388 
389   bool operator==(const Song &other) const;
390   bool operator!=(const Song &other) const;
391 
392   // Two songs that are on the same album will have the same AlbumKey.
393   // It is more efficient to use IsOnSameAlbum, but this function can be used when you need to hash the key to do fast lookups.
394   QString AlbumKey() const;
395 
396   Song &operator=(const Song &other);
397 
398  private:
399   struct Private;
400 
401   static QString sortable(const QString &v);
402 
403   QSharedDataPointer<Private> d;
404 };
405 
406 typedef QList<Song> SongList;
407 typedef QMap<QString, Song> SongMap;
408 
409 Q_DECLARE_METATYPE(Song)
410 Q_DECLARE_METATYPE(SongList)
411 Q_DECLARE_METATYPE(SongMap)
412 
413 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
414 size_t qHash(const Song &song);
415 #else
416 uint qHash(const Song &song);
417 #endif
418 // Hash function using field checked in IsSimilar function
419 size_t HashSimilar(const Song &song);
420 
421 #endif  // SONG_H
422