TTKMusicPlayer  4.3.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 Data
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_WSEPARATOR, 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::format() noexcept
139 {
141 }
142 
143 QString MusicSongMeta::description() noexcept
144 {
146 }
147 
148 void MusicSongMeta::setArtist(const QString &artist) noexcept
149 {
150  songMeta()->m_metaData[TagMeta::ARTIST] = artist;
151 }
152 
153 void MusicSongMeta::setTitle(const QString &title) noexcept
154 {
155  songMeta()->m_metaData[TagMeta::TITLE] = title;
156 }
157 
158 void MusicSongMeta::setAlbum(const QString &album) noexcept
159 {
160  songMeta()->m_metaData[TagMeta::ALBUM] = album;
161 }
162 
163 void MusicSongMeta::setComment(const QString &comment) noexcept
164 {
165  songMeta()->m_metaData[TagMeta::COMMENT] = comment;
166 }
167 
168 void MusicSongMeta::setYear(const QString &year) noexcept
169 {
170  songMeta()->m_metaData[TagMeta::YEAR] = year;
171 }
172 
173 void MusicSongMeta::setTrackNum(const QString &track) noexcept
174 {
175  songMeta()->m_metaData[TagMeta::TRACK] = track;
176 }
177 
178 void MusicSongMeta::setGenre(const QString &genre) noexcept
179 {
180  songMeta()->m_metaData[TagMeta::GENRE] = genre;
181 }
182 
183 void MusicSongMeta::setRating(const QString &rating) noexcept
184 {
185  songMeta()->m_metaData[TagMeta::RATING] = rating;
186 }
187 
188 void MusicSongMeta::setCover(const QPixmap &cover)
189 {
190 #if TTK_VERSION >= TTK_VERSION_CHECK(2,5,3,0)
191  if(cover.width() > 500 || cover.height() > 500)
192  {
193  songMeta()->m_cover = cover.scaled(500, 500, Qt::KeepAspectRatio);
194  }
195  else
196  {
197  songMeta()->m_cover = cover;
198  }
199 #else
200  Q_UNUSED(cover);
201 #endif
202 }
203 
204 void MusicSongMeta::setCover(const QByteArray &data)
205 {
206  if(data.isEmpty())
207  {
208  TTK_ERROR_STREAM("Input byte data is empty");
209  return;
210  }
211 #if TTK_VERSION >= TTK_VERSION_CHECK(2,5,3,0)
212  QPixmap pix;
213  pix.loadFromData(data);
214  setCover(pix);
215 #else
216  Q_UNUSED(data);
217 #endif
218 }
219 
220 QPixmap MusicSongMeta::cover() noexcept
221 {
222 #if TTK_VERSION >= TTK_VERSION_CHECK(2,5,3,0)
223  return songMeta()->m_cover;
224 #else
225  return QPixmap();
226 #endif
227 }
228 
229 QString MusicSongMeta::lyrics() noexcept
230 {
231  return songMeta()->m_lyrics;
232 }
233 
234 QString MusicSongMeta::sampleRate() noexcept
235 {
237 }
238 
239 QString MusicSongMeta::bitrate() noexcept
240 {
241  const QString &bitrate = songMeta()->m_metaData[TagMeta::BITRATE];
242  return bitrate.isEmpty() ? TTK_DEFAULT_STR : bitrate + " kbps";
243 }
244 
245 QString MusicSongMeta::duration() noexcept
246 {
248 }
249 
251 {
252  if(this == &other)
253  {
254  return;
255  }
256 
257  m_offset = other.m_offset;
258  m_path = other.m_path;
259 
260  for(const Data *data : qAsConst(m_songMetas))
261  {
262  m_songMetas << new Data(*data);
263  }
264 }
265 
267  : m_offset(other.m_offset),
268  m_path(other.m_path),
269  m_songMetas(std::move(other.m_songMetas))
270 {
271 
272 }
273 
275 {
276  if(this == &other)
277  {
278  return *this;
279  }
280 
281  m_offset = other.m_offset;
282  m_path = other.m_path;
283 
284  for(const Data *data : qAsConst(m_songMetas))
285  {
286  m_songMetas << new Data(*data);
287  }
288  return *this;
289 }
290 
292 {
293  if(this == &other)
294  {
295  return *this;
296  }
297 
298  std::swap(m_offset, other.m_offset);
299  std::swap(m_path, other.m_path);
300  std::swap(m_songMetas, other.m_songMetas);
301  return *this;
302 }
303 
304 void MusicSongMeta::setSongMetaIndex(int index) noexcept
305 {
306  if(index < 0 || index >= songMetaCount())
307  {
308  return;
309  }
310 
311  m_offset = index;
312 }
313 
315 {
316  return m_songMetas.count();
317 }
318 
320 {
321  qDeleteAll(m_songMetas);
322  m_songMetas.clear();
323  m_offset = -1;
324 }
325 
327 {
328  if(m_songMetas.isEmpty())
329  {
330  m_songMetas << new Data;
331  m_offset = 0;
332  }
333 
334  return (m_offset < 0 || m_offset >= songMetaCount()) ? nullptr : m_songMetas[m_offset];
335 }
336 
338 {
339  const QString &v = songMeta()->m_metaData[type];
341 }
342 
343 
344 #ifdef Q_OS_UNIX
345 static constexpr const char *SPLITER = "*******************************************************************\n";
346 #else
347 static constexpr const char *SPLITER = "************************************************************************\n";
348 #endif
349 
351 {
352  clearSongMeta();
353 
355  if(factory)
356  {
357  qint64 length = 0;
358  QStringList files;
359  const QList<TrackInfo*> infos(factory->createPlayList(m_path, TrackInfo::AllParts, &files));
360 
361  for(TrackInfo *info : qAsConst(infos))
362  {
363  Data *data = new Data;
364  data->m_path = info->path();
365  data->m_metaData[TagMeta::URL] = files.isEmpty() ? m_path : files.first();
366 
367  data->m_metaData[TagMeta::SAMPLERATE] = info->value(Qmmp::SAMPLERATE);
368  data->m_metaData[TagMeta::BITRATE] = info->value(Qmmp::BITRATE);
369  data->m_metaData[TagMeta::CHANNEL] = info->value(Qmmp::CHANNELS);
370  data->m_metaData[TagMeta::FORMAT] = info->value(Qmmp::FORMAT_NAME);
371 
372  data->m_metaData[TagMeta::TITLE] = info->value(Qmmp::TITLE);
373  data->m_metaData[TagMeta::ARTIST] = info->value(Qmmp::ARTIST);
374  data->m_metaData[TagMeta::ALBUM] = info->value(Qmmp::ALBUM);
375  data->m_metaData[TagMeta::YEAR] = info->value(Qmmp::YEAR);
376  data->m_metaData[TagMeta::COMMENT] = info->value(Qmmp::COMMENT);
377  data->m_metaData[TagMeta::TRACK] = info->value(Qmmp::TRACK);
378  data->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(data->m_metaData[TagMeta::URL]).size() * 8.0f / bitrate;
387  }
388  }
389 
391 
392  m_songMetas << data;
393  m_offset = m_songMetas.count() - 1;
394  }
395 
396  qDeleteAll(infos);
397 
398  if(m_songMetas.isEmpty())
399  {
400  return false;
401  }
402 
403  QPixmap cover;
404  QString lyrics, rating, description;
405  const DecoderProperties &properties = factory->properties();
406 
407  description += "ShortName: " + properties.shortName + TTK_LINEFEED;
408  description += "DecoderName: " + properties.name + TTK_LINEFEED;
409  description += "Description: " + properties.description + TTK_LINEFEED;
410  description += SPLITER;
411 
412  const MetaDataModel *model = factory->createMetaDataModel(m_path, true);
413  if(model)
414  {
415  cover = QPixmap::fromImage(model->cover());
416  lyrics = model->lyrics();
417 
418  const QList<MetaDataItem> &properties = model->extraProperties();
419  for(const MetaDataItem &item : qAsConst(properties))
420  {
421  if(item.name() == "Rating")
422  {
423  rating = item.value().toString();
424  }
425 
426  QString value = item.value().toString();
427  if(value.contains(TTK_LINEFEED))
428  {
429  value = TTK_LINEFEED + value;
430  }
431 
432  description += item.name() + ": " + value + TTK_LINEFEED;
433  }
434 
435  if(!properties.isEmpty())
436  {
437  description += SPLITER;
438  }
439 
440  for(const MetaDataItem &item : model->descriptions())
441  {
442  QString value = item.value().toString();
443  if(value.contains(TTK_LINEFEED))
444  {
445  value = TTK_LINEFEED + value;
446  }
447 
448  description += item.name() + ": " + value + TTK_LINEFEED;
449  description += SPLITER;
450  }
451 
452  delete model;
453  }
454 
455  for(Data *data : qAsConst(m_songMetas))
456  {
457  if(!cover.isNull())
458  {
459  data->m_cover = cover;
460  }
461 
462  if(!lyrics.isEmpty())
463  {
464  data->m_lyrics = lyrics;
465  }
466 
467  if(!rating.isEmpty())
468  {
469  data->m_metaData[TagMeta::RATING] = rating;
470  }
471 
472  const QString &format = "Format: " + data->m_metaData[TagMeta::FORMAT] + TTK_LINEFEED;
473  data->m_metaData[TagMeta::DESCRIPTION] = format + description;
474  }
475  }
476 
477  return !m_songMetas.isEmpty();
478 }
479 
481 {
483  if(!factory)
484  {
485  return false;
486  }
487 
488  MetaDataModel *model = factory->createMetaDataModel(m_path, false);
489  if(model)
490  {
491  const QList<TagModel*> &tags = model->tags();
492  if(!tags.isEmpty())
493  {
494  TagModel *tagModel = tags.first();
495  if(tags.count() == 3)
496  {
497  tagModel = tags[1]; //id3v2 mode tag
498  }
499 
500  tagModel->setValue(Qmmp::ALBUM, album());
501  tagModel->setValue(Qmmp::ARTIST, artist());
502  tagModel->setValue(Qmmp::TITLE, title());
503  tagModel->setValue(Qmmp::YEAR, year());
504  tagModel->setValue(Qmmp::GENRE, genre());
505  tagModel->setValue(Qmmp::TRACK, trackNum());
506  tagModel->save();
507  }
508 
509  const QPixmap &pix = cover();
510  if(!pix.isNull())
511  {
512  model->setCover(pix.toImage());
513  }
514  else
515  {
516  model->removeCover();
517  }
518  }
519 
520  delete model;
521  return true;
522 }
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
QString title() noexcept
virtual void removeCover()
void setGenre(const QString &genre) noexcept
static constexpr const char * SPLITER
QString format() noexcept
QString artist() noexcept
#define TTK_DEFAULT_STR
Definition: ttkglobal.h:276
Container of extra file/track property.
Definition: metadatamodel.h:34
Data * songMeta() noexcept
QString lyrics() noexcept
QString fileBasePath() noexcept
virtual MetaDataModel * createMetaDataModel(const QString &path, bool readOnly)=0
static bool isTrack(const QString &url) noexcept
Definition: musicformats.cpp:5
bool readInformation()
QString filePath() const noexcept
Definition: ttkcompat.h:39
void swap(TTKAny &left, TTKAny &right) noexcept
Definition: ttkany.h:224
QMap< TagMeta::Type, QString > m_metaData
The StateHandler class provides is the base interface class of tag editor.
Definition: tagmodel.h:30
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
QString m_lyrics
virtual QString lyrics() const
void setTitle(const QString &title) noexcept
#define qAsConst
Definition: ttkqtglobal.h:57
void setCover(const QPixmap &cover)
QString formatString(TagMeta::Type type) noexcept
QString album() noexcept
virtual void setCover(const QImage &img)
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
virtual DecoderProperties properties() const =0
#define TTK_SEPARATOR
Definition: ttkglobal.h:269
MusicSongMeta & operator=(const MusicSongMeta &other) noexcept
The class of the music meta.
int songMetaCount() const noexcept
#define TTK_WSEPARATOR
Definition: ttkglobal.h:270
virtual void setValue(Qmmp::MetaData key, const QString &value)=0
QPixmap cover() noexcept
~MusicSongMeta() noexcept
QString duration() noexcept
QPixmap m_cover
QList< Data * > m_songMetas
static DecoderFactory * findByFilePath(const QString &path, bool useContent=false)
void setComment(const QString &comment) noexcept
virtual QList< MetaDataItem > descriptions() const
QString trackNum() noexcept
#define TTK_LINEFEED
Definition: ttkglobal.h:271
virtual QImage cover() const
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
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:76
MusicSongMeta() noexcept
void setArtist(const QString &artist) noexcept
QString rating() noexcept
QString m_path
virtual QList< MetaDataItem > extraProperties() const
static qint64 formatDuration(const QString &time) noexcept
Definition: ttktime.cpp:123