9#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 auto 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()));
365 QString path = QCoreApplication::applicationDirPath();
367 QString(
"%1/../Resources/lib/python3:%1/../Resources/lib/python3/site-packages:%1/../Resources/lib/python3/lib-dynload:%2")
369 .arg(QProcessEnvironment::systemEnvironment().value(
"PYTHONPATH"))
370 .toUtf8().constData(), 1);
371 QString PYTHON_LOCAL_EXE = path +
"python3";
373 QString PYTHON_LOCAL_EXE = QString(PYTHON_EXE);
377 QString lyricsDir =
GetConfDir() +
"/MythMusic/Lyrics/";
380 dir.mkpath(lyricsDir);
384 LOG(VB_GENERAL, LOG_ERR,
"Missing --songid option");
389 QString grabberName =
"ALL";
404 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find metadata for trackid: %1").arg(songID));
412 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find file for trackid: %1").arg(songID));
417 lyricsFile =
GetConfDir() + QString(
"/MythMusic/Lyrics/%1.txt").arg(songID);
422 if (grabberName !=
"ALL")
424 QFile::remove(lyricsFile);
429 QFile
file(QLatin1String(qPrintable(lyricsFile)));
432 if (
file.open(QIODevice::ReadOnly))
434 QTextStream stream(&
file);
436 while (!stream.atEnd())
438 lyrics.append(stream.readLine());
452 album = mdata->
Album();
453 title = mdata->
Title();
461 LOG(VB_GENERAL, LOG_ERR,
"Missing --artist option");
468 LOG(VB_GENERAL, LOG_ERR,
"Missing --album option");
475 LOG(VB_GENERAL, LOG_ERR,
"Missing --title option");
483 QString scriptDir =
GetShareDir() +
"metadata/Music/lyrics";
488 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find lyric scripts directory: %1").arg(scriptDir));
493 d.setFilter(QDir::Files | QDir::NoDotAndDotDot);
494 d.setNameFilters(QStringList(
"*.py"));
495 QFileInfoList list =
d.entryInfoList();
498 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find any lyric scripts in: %1").arg(scriptDir));
505 for (
const auto& fi : std::as_const(list))
507 LOG(VB_GENERAL, LOG_NOTICE, QString(
"Found lyric script at: %1").arg(fi.filePath()));
508 scripts.append(fi.filePath());
511 QMap<int, LyricsGrabber> grabberMap;
514 for (
int x = 0; x < scripts.count(); x++)
516 QStringList
args { scripts.at(x),
"-v" };
518 p.start(PYTHON_LOCAL_EXE,
args);
519 p.waitForFinished(-1);
520 QString result =
p.readAllStandardOutput();
523#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
528 if (!domDoc.setContent(result,
false, &errorMsg, &errorLine, &errorColumn))
530 LOG(VB_GENERAL, LOG_ERR,
531 QString(
"FindLyrics: Could not parse version from %1").arg(scripts.at(x)) +
532 QString(
"\n\t\t\tError at line: %1 column: %2 msg: %3").arg(errorLine).arg(errorColumn).arg(errorMsg));
536 auto parseResult = domDoc.setContent(result);
539 LOG(VB_GENERAL, LOG_ERR,
540 QString(
"FindLyrics: Could not parse version from %1").arg(scripts.at(x)) +
541 QString(
"\n\t\t\tError at line: %1 column: %2 msg: %3")
542 .arg(parseResult.errorLine).arg(parseResult.errorColumn)
543 .arg(parseResult.errorMessage));
548 QDomNodeList itemList = domDoc.elementsByTagName(
"grabber");
549 QDomNode itemNode = itemList.item(0);
552 grabber.
m_name = itemNode.namedItem(QString(
"name")).toElement().text();
553 grabber.
m_priority = itemNode.namedItem(QString(
"priority")).toElement().text().toInt();
556 grabberMap.insert(grabber.
m_priority, grabber);
560 QMap<int, LyricsGrabber>::const_iterator i = grabberMap.constBegin();
561 while (i != grabberMap.constEnd())
567 if (grabberName !=
"ALL" && grabberName != grabber.
m_name)
570 LOG(VB_GENERAL, LOG_NOTICE, QString(
"Trying grabber: %1, Priority: %2").arg(grabber.
m_name).arg(grabber.
m_priority));
571 QString statusMessage = QObject::tr(
"Searching '%1' for lyrics...").arg(grabber.
m_name);
576 QString(
"--artist=%1").arg(artist),
577 QString(
"--album=%1").arg(album),
578 QString(
"--title=%1").arg(title),
579 QString(
"--filename=%1").arg(
filename) };
580 p.start(PYTHON_LOCAL_EXE,
args);
581 p.waitForFinished(-1);
582 QString result =
p.readAllStandardOutput();
584 LOG(VB_GENERAL, LOG_DEBUG, QString(
"Grabber: %1, Exited with code: %2").arg(grabber.
m_name).arg(
p.exitCode()));
586 if (
p.exitCode() == 0)
588 LOG(VB_GENERAL, LOG_NOTICE, QString(
"Lyrics Found using: %1").arg(grabber.
m_name));
593 QFile
file(QLatin1String(qPrintable(lyricsFile)));
595 if (
file.open(QIODevice::WriteOnly))
597 QTextStream stream(&
file);
AlbumArtImage * getImage(ImageType type)
static QString getTypeFilename(ImageType type)
bool toBool(const QString &key) const
Returns stored QVariant as a boolean.
int toInt(const QString &key) const
Returns stored QVariant as an integer, falling to default if not provided.
QString toString(const QString &key) const
Returns stored QVariant as a QString, falling to default if not provided.
QDateTime toDateTime(const QString &key) const
Returns stored QVariant as a QDateTime, falling to default if not provided.
QString GetHostName(void)
void SendMessage(const QString &message)
int GetNumSetting(const QString &key, int defaultval=0)
QStringList GetDirList(void) const
static bool FindDirs(const QString &group="Default", const QString &hostname="", QStringList *dirlist=nullptr)
Finds and and optionally initialize a directory list associated with a Storage Group.
QString FindFile(const QString &filename)
QString FindNextDirMostFree(void)
@ GENERIC_EXIT_OK
Exited with no error.
@ GENERIC_EXIT_INVALID_CMDLINE
Command line parse error.
@ GENERIC_EXIT_NOT_OK
Exited with error.
static const iso6937table * d
std::chrono::seconds secondsFromFloat(T value)
Helper function for convert a floating point number to a duration.
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QString GetShareDir(void)
#define ENO
This can be appended to the LOG args with "+".
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
QMap< QString, UtilFunc > UtilMap
MythCommFlagCommandLineParser cmdline