MythTV master
metaio.cpp
Go to the documentation of this file.
1
2// POSIX C headers
3#include <utime.h>
4
5// Qt
6#include <QFileInfo>
7
8// libmythmetadata
9#include "metaio.h"
10#include "musicmetadata.h"
11#include "metaioid3.h"
12#include "metaiooggopus.h"
13#include "metaiooggvorbis.h"
14#include "metaioflacvorbis.h"
15#include "metaiomp4.h"
16#include "metaiowavpack.h"
17#include "metaioavfcomment.h"
18
19// libmythbase
22
23const QString MetaIO::kValidFileExtensions(".mp3|.mp2|.ogg|.oga|.opus|.flac|.wma|.wav|.ac3|.oma|.omg|"
24 ".atp|.ra|.dts|.aac|.m4a|.aa3|.tta|.mka|.aiff|.swa|.wv");
25
27 : m_filenameFormat(gCoreContext->GetSetting("NonID3FileNameFormat").toUpper())
28{
29}
30
31// static
33{
34 QFileInfo fi(filename);
35 QString extension = fi.suffix().toLower();
36
37 if (extension.isEmpty() || !MetaIO::kValidFileExtensions.contains(extension))
38 {
39 LOG(VB_FILE, LOG_WARNING, QString("MetaIO: unknown extension: '%1'").arg(extension));
40 return nullptr;
41 }
42
43 if (extension == "mp3" || extension == "mp2")
44 return new MetaIOID3;
45 if (extension == "ogg" || extension == "oga")
46 return new MetaIOOggVorbis;
47 if (extension == "opus")
48 return new MetaIOOggOpus;
49 if (extension == "flac")
50 {
51 auto *tagger = new MetaIOFLACVorbis;
52 if (tagger->TagExists(filename))
53 return tagger;
54 delete tagger;
55 return new MetaIOID3;
56 }
57 if (extension == "m4a")
58 return new MetaIOMP4;
59 if (extension == "wv")
60 return new MetaIOWavPack;
61 return new MetaIOAVFComment;
62}
63
64// static
66{
67 MusicMetadata *mdata = nullptr;
69 bool ignoreID3 = (gCoreContext->GetNumSetting("Ignore_ID3", 0) == 1);
70
71 if (tagger)
72 {
73 if (!ignoreID3)
74 mdata = tagger->read(filename);
75
76 if (ignoreID3 || !mdata)
77 mdata = tagger->readFromFilename(filename);
78
79 delete tagger;
80 }
81
82 if (!mdata)
83 {
84 LOG(VB_GENERAL, LOG_ERR,
85 QString("MetaIO::readMetadata(): Could not read '%1'")
86 .arg(filename));
87 }
88
89 return mdata;
90}
91
92// static
94{
95 //FIXME: we need a relative path for createFromFilename()
96 // but readMetadata() needs an absolute path
98 if (mdata)
99 return mdata;
100
101 return readMetadata(filename);
102}
103
105 QString &artist, QString &album, QString &title,
106 QString &genre, int &tracknum)
107{
108 QString lfilename = filename;
109 // Clear
110 artist.clear();
111 album.clear();
112 title.clear();
113 genre.clear();
114 tracknum = 0;
115
116 int part_num = 0;
117 // Replace
118 lfilename.replace('_', ' ');
119 lfilename = lfilename.section('.', 0, -2);
120 QStringList fmt_list = m_filenameFormat.split("/");
121 QStringList::iterator fmt_it = fmt_list.begin();
122
123 // go through loop once to get minimum part number
124 for (; fmt_it != fmt_list.end(); ++fmt_it, --part_num) {}
125
126 // reset to go through loop for real
127 fmt_it = fmt_list.begin();
128 for(; fmt_it != fmt_list.end(); ++fmt_it, ++part_num)
129 {
130 QString part_str = lfilename.section( "/", part_num, part_num);
131
132 if ( *fmt_it == "GENRE" )
133 {
134 genre = part_str;
135 }
136 else if ( *fmt_it == "ARTIST" )
137 {
138 artist = part_str;
139 }
140 else if ( *fmt_it == "ALBUM" )
141 {
142 album = part_str;
143 }
144 else if ( *fmt_it == "TITLE" )
145 {
146 title = part_str;
147 }
148 else if ( *fmt_it == "TRACK_TITLE" )
149 {
150 QStringList tracktitle_list = part_str.split("-");
151 if (tracktitle_list.size() > 1)
152 {
153 tracknum = tracktitle_list[0].toInt();
154 title = tracktitle_list[1].simplified();
155 }
156 else
157 {
158 title = part_str;
159 }
160 }
161 else if ( *fmt_it == "ARTIST_TITLE" )
162 {
163 QStringList artisttitle_list = part_str.split("-");
164 if (artisttitle_list.size() > 1)
165 {
166 artist = artisttitle_list[0].simplified();
167 title = artisttitle_list[1].simplified();
168 }
169 else
170 {
171 if (title.isEmpty())
172 title = part_str;
173 if (artist.isEmpty())
174 artist = part_str;
175 }
176 }
177 }
178}
179
180MusicMetadata* MetaIO::readFromFilename(const QString &filename, bool blnLength)
181{
182 QString artist;
183 QString album;
184 QString title;
185 QString genre;
186 int tracknum = 0;
187
188 readFromFilename(filename, artist, album, title, genre, tracknum);
189
190 std::chrono::milliseconds length = blnLength ? getTrackLength(filename) : 0ms;
191
192 auto *retdata = new MusicMetadata(filename, artist, "", album, title, genre,
193 0, tracknum, length);
194
195 return retdata;
196}
197
204{
205 QString artist;
206 QString album;
207 QString title;
208 QString genre;
209 int tracknum = 0;
210
211 const QString filename = metadata->Filename(false);
212
213 if (filename.isEmpty())
214 return;
215
216 readFromFilename(filename, artist, album, title, genre, tracknum);
217
218 if (metadata->Artist().isEmpty())
219 metadata->setArtist(artist);
220
221 if (metadata->Album().isEmpty())
222 metadata->setAlbum(album);
223
224 if (metadata->Title().isEmpty())
225 metadata->setTitle(title);
226
227 if (metadata->Genre().isEmpty())
228 metadata->setGenre(genre);
229
230 if (metadata->Track() <= 0)
231 metadata->setTrack(tracknum);
232}
233
235{
236 if (stat(m_filename.toLocal8Bit().constData(), &m_fileinfo) == -1)
237 {
238 LOG(VB_GENERAL, LOG_ERR,
239 QString("MetaIO::saveTimeStamps: failed to stat file: %1").arg(m_filename) + ENO);
240 }
241}
242
244{
245 struct utimbuf new_times {.actime=m_fileinfo.st_atime,
246 .modtime=m_fileinfo.st_mtime};
247
248 if (utime(m_filename.toLocal8Bit().constData(), &new_times) < 0)
249 {
250 LOG(VB_GENERAL, LOG_ERR,
251 QString("MetaIO::restoreTimeStamps: failed to utime file: %1").arg(m_filename) + ENO);
252 }
253}
Attempt to read metadata in files without a specific metadata reading implementation.
Read and write Vorbis (Xiph) tags in a FLAC file.
Read and write metadata in MPEG (mp3) ID3V2 tags.
Definition: metaioid3.h:39
Read and write metadata in MP4 container tags.
Definition: metaiomp4.h:17
Read and write Opus (Xiph) tags in an Ogg container.
Definition: metaiooggopus.h:19
Read and write Vorbis (Xiph) tags in an Ogg container.
Read and write metadata in Wavpack APE tags.
Definition: metaiowavpack.h:21
Definition: metaio.h:18
void restoreTimeStamps(void)
Definition: metaio.cpp:243
static MusicMetadata * getMetadata(const QString &filename)
Get the metadata for filename.
Definition: metaio.cpp:93
virtual MusicMetadata * read(const QString &filename)=0
Reads MusicMetadata from a file.
void saveTimeStamps(void)
Definition: metaio.cpp:234
QString m_filenameFormat
Definition: metaio.h:169
void readFromFilename(const QString &filename, QString &artist, QString &album, QString &title, QString &genre, int &tracknum)
Reads MusicMetadata based on the folder/filename.
Definition: metaio.cpp:104
static const QString kValidFileExtensions
Definition: metaio.h:160
MetaIO()
Definition: metaio.cpp:26
static MusicMetadata * readMetadata(const QString &filename)
Read the metadata from filename directly.
Definition: metaio.cpp:65
QString m_filename
Definition: metaio.h:168
virtual std::chrono::milliseconds getTrackLength(const QString &filename)=0
static MetaIO * createTagger(const QString &filename)
Finds an appropriate tagger for the given file.
Definition: metaio.cpp:32
static MusicMetadata * createFromFilename(const QString &filename)
void setGenre(const QString &lgenre)
QString Title() const
void setTitle(const QString &ltitle, const QString &ltitle_sort=nullptr)
int Track() const
QString Filename(bool find=true)
QString Artist() const
void setAlbum(const QString &lalbum, const QString &lalbum_sort=nullptr)
void setTrack(int ltrack)
QString Genre() const
QString Album() const
void setArtist(const QString &lartist, const QString &lartist_sort=nullptr)
int GetNumSetting(const QString &key, int defaultval=0)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:74
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39