3 #include <QDomDocument>
10 #include "libmythbase/mythconfig.h"
20 #include <libavformat/avformat.h>
21 #include <libavcodec/avcodec.h>
35 LOG(VB_GENERAL, LOG_ERR,
"Missing --songid option");
43 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find metadata for trackid: %1").arg(songID));
82 LOG(VB_GENERAL, LOG_ERR, QString(
"Failed to write to tag for trackid: %1").arg(songID));
98 LOG(VB_GENERAL, LOG_ERR,
"Missing --songid option");
104 LOG(VB_GENERAL, LOG_ERR,
"Missing --imagetype option");
114 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find metadata for trackid: %1").arg(songID));
121 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find image of type: %1").arg(
type));
128 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find a tagger for this file: %1").arg(mdata->
Filename(
false)));
135 LOG(VB_GENERAL, LOG_ERR, QString(
"Either the image isn't embedded or the tagger doesn't support embedded images"));
148 if (!dirList.empty())
153 LOG(VB_GENERAL, LOG_ERR,
"Cannot find a directory in the 'MusicArt' storage group to save to");
158 path +=
"/AlbumArt/";
172 saveImage->save(path +
filename,
"JPEG");
191 LOG(VB_GENERAL, LOG_ERR,
"Failed to find any directories in the 'Music' storage group");
196 fscan->SearchDirs(dirList);
207 LOG(VB_GENERAL, LOG_ERR,
"Can't update the radio streams the DB schema hasn't been updated yet! Aborting");
221 LOG(VB_GENERAL, LOG_ERR,
"Missing --songid option");
230 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find metadata for trackid: %1").arg(songID));
238 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find file for trackid: %1").arg(songID));
242 AVFormatContext *inputFC =
nullptr;
243 AVInputFormat *fmt =
nullptr;
246 LOG(VB_GENERAL, LOG_DEBUG, QString(
"CalcTrackLength: Opening '%1'")
249 QByteArray inFileBA = musicFile.toLocal8Bit();
251 int ret = avformat_open_input(&inputFC, inFileBA.constData(), fmt,
nullptr);
255 LOG(VB_GENERAL, LOG_ERR,
"CalcTrackLength: Couldn't open input file" +
261 ret = avformat_find_stream_info(inputFC,
nullptr);
265 LOG(VB_GENERAL, LOG_ERR,
266 QString(
"CalcTrackLength: Couldn't get stream info, error #%1").arg(ret));
267 avformat_close_input(&inputFC);
272 std::chrono::seconds duration = 0s;
275 for (
uint i = 0; i < inputFC->nb_streams; i++)
277 AVStream *st = inputFC->streams[i];
278 std::array<char,256> buf {};
280 const AVCodec *pCodec = avcodec_find_decoder(st->codecpar->codec_id);
283 LOG(VB_GENERAL, LOG_WARNING,
284 QString(
"avcodec_find_decoder fail for %1").arg(st->codecpar->codec_id));
287 AVCodecContext *avctx = avcodec_alloc_context3(pCodec);
288 avcodec_parameters_to_context(avctx, st->codecpar);
289 avctx->pkt_timebase = st->time_base;
291 avcodec_string(buf.data(), buf.size(), avctx,
static_cast<int>(
false));
293 switch (inputFC->streams[i]->codecpar->codec_type)
295 case AVMEDIA_TYPE_AUDIO:
297 AVPacket *pkt = av_packet_alloc();
300 LOG(VB_GENERAL, LOG_ERR,
"packet allocation failed");
304 while (av_read_frame(inputFC, pkt) >= 0)
306 if (pkt->stream_index == (
int)i)
307 time = time + pkt->duration;
309 av_packet_unref(pkt);
312 av_packet_free(&pkt);
319 LOG(VB_GENERAL, LOG_ERR,
320 QString(
"Skipping unsupported codec %1 on stream %2")
321 .arg(inputFC->streams[i]->codecpar->codec_type).arg(i));
324 avcodec_free_context(&avctx);
328 avformat_close_input(&inputFC);
331 std::chrono::seconds dbLength = duration_cast<std::chrono::seconds>(mdata->
Length());
332 if (dbLength != duration)
334 LOG(VB_GENERAL, LOG_INFO, QString(
"The length of this track in the database was %1s "
335 "it is now %2s").arg(dbLength.count()).arg(duration.count()));
346 LOG(VB_GENERAL, LOG_INFO, QString(
"The length of this track is unchanged %1s")
347 .arg(dbLength.count()));
364 QString lyricsDir =
GetConfDir() +
"/MythMusic/Lyrics/";
367 dir.mkpath(lyricsDir);
371 LOG(VB_GENERAL, LOG_ERR,
"Missing --songid option");
376 QString grabberName =
"ALL";
391 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find metadata for trackid: %1").arg(songID));
399 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find file for trackid: %1").arg(songID));
404 lyricsFile =
GetConfDir() + QString(
"/MythMusic/Lyrics/%1.txt").arg(songID);
409 if (grabberName !=
"ALL")
410 QFile::remove(lyricsFile);
414 QFile
file(QLatin1String(qPrintable(lyricsFile)));
417 if (
file.open(QIODevice::ReadOnly))
419 QTextStream stream(&
file);
421 while (!stream.atEnd())
423 lyrics.append(stream.readLine());
437 album = mdata->
Album();
438 title = mdata->
Title();
446 LOG(VB_GENERAL, LOG_ERR,
"Missing --artist option");
453 LOG(VB_GENERAL, LOG_ERR,
"Missing --album option");
460 LOG(VB_GENERAL, LOG_ERR,
"Missing --title option");
468 QString scriptDir =
GetShareDir() +
"metadata/Music/lyrics";
473 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find lyric scripts directory: %1").arg(scriptDir));
478 d.setFilter(QDir::Files | QDir::NoDotAndDotDot);
479 d.setNameFilters(QStringList(
"*.py"));
480 QFileInfoList list =
d.entryInfoList();
483 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find any lyric scripts in: %1").arg(scriptDir));
490 for (
const auto& fi : std::as_const(list))
492 LOG(VB_GENERAL, LOG_NOTICE, QString(
"Found lyric script at: %1").arg(fi.filePath()));
493 scripts.append(fi.filePath());
496 QMap<int, LyricsGrabber> grabberMap;
499 for (
int x = 0; x < scripts.count(); x++)
501 QStringList
args { scripts.at(x),
"-v" };
503 p.start(PYTHON_EXE,
args);
504 p.waitForFinished(-1);
505 QString result =
p.readAllStandardOutput();
512 if (!domDoc.setContent(result,
false, &errorMsg, &errorLine, &errorColumn))
514 LOG(VB_GENERAL, LOG_ERR,
515 QString(
"FindLyrics: Could not parse version from %1").arg(scripts.at(x)) +
516 QString(
"\n\t\t\tError at line: %1 column: %2 msg: %3").arg(errorLine).arg(errorColumn).arg(errorMsg));
520 QDomNodeList itemList = domDoc.elementsByTagName(
"grabber");
521 QDomNode itemNode = itemList.item(0);
524 grabber.
m_name = itemNode.namedItem(QString(
"name")).toElement().text();
525 grabber.
m_priority = itemNode.namedItem(QString(
"priority")).toElement().text().toInt();
528 grabberMap.insert(grabber.
m_priority, grabber);
532 QMap<int, LyricsGrabber>::const_iterator i = grabberMap.constBegin();
533 while (i != grabberMap.constEnd())
539 if (grabberName !=
"ALL" && grabberName != grabber.
m_name)
542 LOG(VB_GENERAL, LOG_NOTICE, QString(
"Trying grabber: %1, Priority: %2").arg(grabber.
m_name).arg(grabber.
m_priority));
543 QString statusMessage = QObject::tr(
"Searching '%1' for lyrics...").arg(grabber.
m_name);
548 QString(
"--artist=%1").arg(artist),
549 QString(
"--album=%1").arg(album),
550 QString(
"--title=%1").arg(title),
551 QString(
"--filename=%1").arg(
filename) };
552 p.start(PYTHON_EXE,
args);
553 p.waitForFinished(-1);
554 QString result =
p.readAllStandardOutput();
556 LOG(VB_GENERAL, LOG_DEBUG, QString(
"Grabber: %1, Exited with code: %2").arg(grabber.
m_name).arg(
p.exitCode()));
558 if (
p.exitCode() == 0)
560 LOG(VB_GENERAL, LOG_NOTICE, QString(
"Lyrics Found using: %1").arg(grabber.
m_name));
565 QFile
file(QLatin1String(qPrintable(lyricsFile)));
567 if (
file.open(QIODevice::WriteOnly))
569 QTextStream stream(&
file);