TTKMusicPlayer  3.7.0.0
TTKMusicPlayer imitates Kugou UI, the music player uses of qmmp core library based on Qt for windows and linux
musicsongmeta.cpp
Go to the documentation of this file.
1 #include "musicsongmeta.h"
2 #include "musicformats.h"
3 #include "musicstringutils.h"
4 #include "ttktime.h"
5 #include "ttkversion.h"
6 
7 #include <qmmp/decoder.h>
8 #include <qmmp/metadatamodel.h>
9 #include <qmmp/decoderfactory.h>
10 
14 struct MusicMeta
15 {
16  QString m_path;
17  QPixmap m_cover;
18  QString m_lyrics;
19  QMap<TagMeta::Type, QString> m_metaData;
20 };
21 
22 
24  : m_offset(-1)
25 {
26 
27 }
28 
30 {
31  clearSongMeta();
32 }
33 
34 bool MusicSongMeta::read(const QString &url)
35 {
36  bool track = false;
37  QString path(url);
38  // replace windows \\ path to / path
39  path.replace(TTK_RSEPARATOR, TTK_SEPARATOR);
40 
41  if(MusicFormats::isTrack(url))
42  {
43  path = path.section("://", -1);
44  if(path.contains("#"))
45  {
46  path = path.section("#", 0, 0);
47  track = true;
48  }
49  }
50 
51  const QFile file(path);
52  if(!file.exists() || file.size() <= 0)
53  {
54  return false;
55  }
56 
57  m_path = path;
58  if(!readInformation())
59  {
60  return false;
61  }
62 
63  setSongMetaIndex(track ? url.section("#", -1).toInt() - 1 : 0);
64  return true;
65 }
66 
68 {
69  return saveInformation();
70 }
71 
72 QString MusicSongMeta::filePath() const noexcept
73 {
74  return m_path;
75 }
76 
77 QString MusicSongMeta::fileBasePath() noexcept
78 {
79  return songMeta()->m_path;
80 }
81 
83 {
84  return songMeta()->m_metaData[TagMeta::URL];
85 }
86 
87 QString MusicSongMeta::artist() noexcept
88 {
90 }
91 
92 QString MusicSongMeta::title() noexcept
93 {
95 }
96 
97 QString MusicSongMeta::album() noexcept
98 {
100 }
101 
102 QString MusicSongMeta::comment() noexcept
103 {
105 }
106 
107 QString MusicSongMeta::year() noexcept
108 {
109  return songMeta()->m_metaData[TagMeta::YEAR];
110 }
111 
112 QString MusicSongMeta::trackNum() noexcept
113 {
114  const QString &v = songMeta()->m_metaData[TagMeta::TRACK];
115  bool ok = false;
116  if(v.toInt(&ok) > 0)
117  {
118  return !ok ? TTK_DEFAULT_STR : v;
119  }
120  return TTK_DEFAULT_STR;
121 }
122 
123 QString MusicSongMeta::genre() noexcept
124 {
126 }
127 
128 QString MusicSongMeta::rating() noexcept
129 {
131 }
132 
133 QString MusicSongMeta::channel() noexcept
134 {
136 }
137 
138 QString MusicSongMeta::description() noexcept
139 {
141 }
142 
143 void MusicSongMeta::setArtist(const QString &artist) noexcept
144 {
145  songMeta()->m_metaData[TagMeta::ARTIST] = artist;
146 }
147 
148 void MusicSongMeta::setTitle(const QString &title) noexcept
149 {
150  songMeta()->m_metaData[TagMeta::TITLE] = title;
151 }
152 
153 void MusicSongMeta::setAlbum(const QString &album) noexcept
154 {
155  songMeta()->m_metaData[TagMeta::ALBUM] = album;
156 }
157 
158 void MusicSongMeta::setComment(const QString &comment) noexcept
159 {
160  songMeta()->m_metaData[TagMeta::COMMENT] = comment;
161 }
162 
163 void MusicSongMeta::setYear(const QString &year) noexcept
164 {
165  songMeta()->m_metaData[TagMeta::YEAR] = year;
166 }
167 
168 void MusicSongMeta::setTrackNum(const QString &track) noexcept
169 {
170  songMeta()->m_metaData[TagMeta::TRACK] = track;
171 }
172 
173 void MusicSongMeta::setGenre(const QString &genre) noexcept
174 {
175  songMeta()->m_metaData[TagMeta::GENRE] = genre;
176 }
177 
178 void MusicSongMeta::setRating(const QString &rating) noexcept
179 {
180  songMeta()->m_metaData[TagMeta::RATING] = rating;
181 }
182 
183 void MusicSongMeta::setCover(const QPixmap &cover) noexcept
184 {
185 #if TTK_VERSION >= TTK_VERSION_CHECK(2,5,3,0)
186  if(cover.width() > 500 || cover.height() > 500)
187  {
188  songMeta()->m_cover = cover.scaled(500, 500, Qt::KeepAspectRatio);
189  }
190  else
191  {
192  songMeta()->m_cover = cover;
193  }
194 #else
195  Q_UNUSED(cover);
196 #endif
197 }
198 
199 void MusicSongMeta::setCover(const QByteArray &data) noexcept
200 {
201  if(data.isEmpty())
202  {
203  TTK_ERROR_STREAM("Input byte data is empty");
204  return;
205  }
206 #if TTK_VERSION >= TTK_VERSION_CHECK(2,5,3,0)
207  QPixmap pix;
208  pix.loadFromData(data);
209  setCover(pix);
210 #else
211  Q_UNUSED(data);
212 #endif
213 }
214 
215 QPixmap MusicSongMeta::cover() noexcept
216 {
217 #if TTK_VERSION >= TTK_VERSION_CHECK(2,5,3,0)
218  return songMeta()->m_cover;
219 #else
220  return QPixmap();
221 #endif
222 }
223 
224 QString MusicSongMeta::lyrics() noexcept
225 {
226  return songMeta()->m_lyrics;
227 }
228 
229 QString MusicSongMeta::sampleRate() noexcept
230 {
232 }
233 
234 QString MusicSongMeta::bitrate() noexcept
235 {
236  const QString &bitrate = songMeta()->m_metaData[TagMeta::BITRATE];
237  return bitrate.isEmpty() ? TTK_DEFAULT_STR : bitrate + " kbps";
238 }
239 
240 QString MusicSongMeta::duration() noexcept
241 {
243 }
244 
246 {
247  if(this == &other)
248  {
249  return;
250  }
251 
252  m_offset = other.m_offset;
253  m_path = other.m_path;
254 
255  for(const MusicMeta *meta : qAsConst(m_songMetas))
256  {
257  m_songMetas << new MusicMeta(*meta);
258  }
259 }
260 
262  : m_offset(other.m_offset),
263  m_path(other.m_path),
264  m_songMetas(std::move(other.m_songMetas))
265 {
266 
267 }
268 
270 {
271  if(this == &other)
272  {
273  return *this;
274  }
275 
276  m_offset = other.m_offset;
277  m_path = other.m_path;
278 
279  for(const MusicMeta *meta : qAsConst(m_songMetas))
280  {
281  m_songMetas << new MusicMeta(*meta);
282  }
283 
284  return *this;
285 }
286 
288 {
289  if(this == &other)
290  {
291  return *this;
292  }
293 
294  std::swap(m_offset, other.m_offset);
295  std::swap(m_path, other.m_path);
296  std::swap(m_songMetas, other.m_songMetas);
297  return *this;
298 }
299 
300 void MusicSongMeta::setSongMetaIndex(int index) noexcept
301 {
302  if(index < 0 || index >= songMetaCount())
303  {
304  return;
305  }
306 
307  m_offset = index;
308 }
309 
311 {
312  return m_songMetas.count();
313 }
314 
316 {
317  qDeleteAll(m_songMetas);
318  m_songMetas.clear();
319  m_offset = -1;
320 }
321 
323 {
324  if(m_songMetas.isEmpty())
325  {
326  m_songMetas << new MusicMeta;
327  m_offset = 0;
328  }
329 
330  if(m_offset < 0 || m_offset >= songMetaCount())
331  {
332  return nullptr;
333  }
334 
335  return m_songMetas[m_offset];
336 }
337 
339 {
340  const QString &v = songMeta()->m_metaData[type];
342 }
343 
344 
345 #ifdef Q_OS_UNIX
346 static constexpr const char *SPLITER = "*******************************************************************\n";
347 #else
348 static constexpr const char *SPLITER = "************************************************************************\n";
349 #endif
350 
352 {
353  clearSongMeta();
354 
356  if(factory)
357  {
358  qint64 length = 0;
359  QStringList files;
360  const QList<TrackInfo*> infos(factory->createPlayList(m_path, TrackInfo::AllParts, &files));
361 
362  for(TrackInfo *info : qAsConst(infos))
363  {
364  MusicMeta *meta = new MusicMeta;
365  meta->m_path = info->path();
366  meta->m_metaData[TagMeta::URL] = files.isEmpty() ? meta->m_path : files.front();
367 
368  meta->m_metaData[TagMeta::SAMPLERATE] = info->value(Qmmp::SAMPLERATE);
369  meta->m_metaData[TagMeta::BITRATE] = info->value(Qmmp::BITRATE);
370  meta->m_metaData[TagMeta::CHANNEL] = info->value(Qmmp::CHANNELS);
371 
372  meta->m_metaData[TagMeta::TITLE] = info->value(Qmmp::TITLE);
373  meta->m_metaData[TagMeta::ARTIST] = info->value(Qmmp::ARTIST);
374  meta->m_metaData[TagMeta::ALBUM] = info->value(Qmmp::ALBUM);
375  meta->m_metaData[TagMeta::YEAR] = info->value(Qmmp::YEAR);
376  meta->m_metaData[TagMeta::COMMENT] = info->value(Qmmp::COMMENT);
377  meta->m_metaData[TagMeta::TRACK] = info->value(Qmmp::TRACK);
378  meta->m_metaData[TagMeta::GENRE] = info->value(Qmmp::GENRE);
379 
380  length = info->duration();
381  if(length == 0)
382  {
383  const int bitrate = info->value(Qmmp::BITRATE).toInt();
384  if(bitrate > 0)
385  {
386  length = QFileInfo(meta->m_metaData[TagMeta::URL]).size() * 8.0f / bitrate;
387  }
388  }
389 
391 
392  m_songMetas << meta;
393  m_offset = m_songMetas.count() - 1;
394  }
395  qDeleteAll(infos);
396 
397  if(m_songMetas.isEmpty())
398  {
399  return false;
400  }
401 
402  QString description;
403  const DecoderProperties &properties = factory->properties();
404 
405  description += "ShortName: " + properties.shortName + "\n";
406  description += "DecoderName: " + properties.name + "\n";
407  description += "Description: " + properties.description + "\n";
408  description += SPLITER;
409 
410  const MetaDataModel *model = factory->createMetaDataModel(m_path, true);
411  if(model)
412  {
413  songMeta()->m_cover = model->cover();
414  songMeta()->m_lyrics = model->lyrics();
415 
416  for(const MetaDataItem &item : model->extraProperties())
417  {
418  if(item.name() == "Rating")
419  {
420  songMeta()->m_metaData[TagMeta::RATING] = item.value().toString();
421  }
422 
423  QString value = item.value().toString();
424  if(value.contains("\n"))
425  {
426  value = "\n" + value;
427  }
428 
429  description += item.name() + ": " + value + "\n";
430  description += SPLITER;
431  }
432 
433  for(const MetaDataItem &item : model->descriptions())
434  {
435  QString value = item.value().toString();
436  if(value.contains("\n"))
437  {
438  value = "\n" + value;
439  }
440 
441  description += item.name() + ": " + value + "\n";
442  description += SPLITER;
443  }
444 
445  delete model;
446  }
447 
449  }
450 
451  return !m_songMetas.isEmpty();
452 }
453 
455 {
457  if(!factory)
458  {
459  return false;
460  }
461 
462  MetaDataModel *model = factory->createMetaDataModel(m_path, false);
463  if(model)
464  {
465  const QList<TagModel* > &tags = model->tags();
466  if(!tags.isEmpty())
467  {
468  TagModel *tagModel = tags.front();
469  if(tags.count() == 3)
470  {
471  tagModel = tags[1]; //id3v2 mode tag
472  }
473 
474  tagModel->setValue(Qmmp::ALBUM, album());
475  tagModel->setValue(Qmmp::ARTIST, artist());
476  tagModel->setValue(Qmmp::TITLE, title());
477  tagModel->setValue(Qmmp::YEAR, year());
478  tagModel->setValue(Qmmp::GENRE, genre());
479  tagModel->setValue(Qmmp::TRACK, trackNum());
480  tagModel->save();
481  }
482 
483  const QPixmap &pix = cover();
484  if(!pix.isNull())
485  {
486  model->setCover(pix);
487  }
488  else
489  {
490  model->removeCover();
491  }
492  }
493 
494  delete model;
495  return true;
496 }
TTK_MODULE_EXPORT QString charactersReplace(const QString &value)
QString comment() noexcept
bool read(const QString &url)
QString genre() noexcept
void setSongMetaIndex(int index) noexcept
QString sampleRate() noexcept
void setTrackNum(const QString &track) noexcept
virtual QPixmap cover() const
QString title() noexcept
virtual void removeCover()
void setGenre(const QString &genre) noexcept
static constexpr const char * SPLITER
QString artist() noexcept
#define TTK_DEFAULT_STR
Definition: ttkglobal.h:200
Container of extra file/track property.
Definition: metadatamodel.h:34
QString lyrics() noexcept
QString fileBasePath() noexcept
virtual MetaDataModel * createMetaDataModel(const QString &path, bool readOnly)=0
QList< MusicMeta * > m_songMetas
The class of the music meta.
virtual void setCover(const QPixmap &pix)
bool readInformation()
QString m_path
QString filePath() const noexcept
Definition: ttkcompat.h:39
void swap(TTKAny &left, TTKAny &right) noexcept
Definition: ttkany.h:212
#define TTK_RSEPARATOR
Definition: ttkglobal.h:196
The StateHandler class provides is the base interface class of tag editor.
Definition: tagmodel.h:30
QString m_lyrics
QString bitrate() noexcept
void clearSongMeta() noexcept
bool saveInformation()
The MetaDataModel is the base interface class of metadata access.
Definition: metadatamodel.h:78
void setYear(const QString &year) noexcept
virtual QString lyrics() const
void setTitle(const QString &title) noexcept
#define qAsConst
Definition: ttkqtglobal.h:53
QString formatString(TagMeta::Type type) noexcept
QString album() noexcept
Input plugin interface (decoder factory).
virtual void save()
The TrackInfo class stores metadata and other information about track.
Definition: trackinfo.h:32
void setRating(const QString &rating) noexcept
QString description() noexcept
MusicMeta * songMeta() noexcept
virtual DecoderProperties properties() const =0
#define TTK_SEPARATOR
Definition: ttkglobal.h:195
MusicSongMeta & operator=(const MusicSongMeta &other) noexcept
int songMetaCount() const noexcept
virtual void setValue(Qmmp::MetaData key, const QString &value)=0
static bool isTrack(const QString &url)
Definition: musicformats.cpp:5
QPixmap cover() noexcept
~MusicSongMeta() noexcept
QString duration() noexcept
QPixmap m_cover
static DecoderFactory * findByFilePath(const QString &path, bool useContent=false)
void setComment(const QString &comment) noexcept
virtual QList< MetaDataItem > descriptions() const
QString trackNum() noexcept
Structure to store input plugin properties.
The class of the music song meta.
Definition: musicsongmeta.h:30
#define const
Definition: zconf.h:233
QString channel() noexcept
void setAlbum(const QString &album) noexcept
QString fileRelatedPath() noexcept
QMap< TagMeta::Type, QString > m_metaData
virtual QList< TrackInfo * > createPlayList(const QString &fileName, TrackInfo::Parts parts, QStringList *ignoredPaths)=0
virtual QList< TagModel * > tags() const
QString year() noexcept
#define TTK_ERROR_STREAM(msg)
Definition: ttklogger.h:69
MusicSongMeta() noexcept
void setArtist(const QString &artist) noexcept
QString rating() noexcept
void setCover(const QPixmap &cover) noexcept
virtual QList< MetaDataItem > extraProperties() const
static qint64 formatDuration(const QString &time) noexcept
Definition: ttktime.cpp:123