33 #if defined(Q_OS_DARWIN) or defined(__FreeBSD__)
34 #include <sys/param.h>
35 #include <sys/mount.h>
36 #elif defined(__linux__)
41 #include <QApplication>
43 #include <QDomElement>
47 #include <QMutexLocker>
48 #include <QTextStream>
51 #include <mythconfig.h>
63 #include <libmythbase/mythversion.h>
68 #include <libavcodec/avcodec.h>
69 #include <libavformat/avformat.h>
70 #include <libavutil/imgutils.h>
75 #include "../mytharchive/archiveutil.h"
76 #include "../mytharchive/remoteavformatcontext.h"
78 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
81 #define QT_ENDL Qt::endl
92 static bool copyFile(
const QString &source,
const QString &destination);
94 const QString &xmlFile,
int chanID);
95 static int importVideo(
const QDomElement &itemNode,
const QString &xmlFile);
96 static int exportRecording(QDomElement &itemNode,
const QString &saveDirectory);
97 static int exportVideo(QDomElement &itemNode,
const QString &saveDirectory);
99 static QString
findNodeText(
const QDomElement &elem,
const QString &nodeName);
100 static int getFieldList(QStringList &fieldList,
const QString &tableName);
107 QFile
file(tempDir +
"/logs/mythburn.lck");
109 if (!
file.open(QIODevice::WriteOnly | QIODevice::Truncate))
110 LOG(VB_GENERAL, LOG_ERR,
"NativeArchive: Failed to create lock file");
112 QString pid = QString(
"%1").arg(getpid());
113 file.write(pid.toLatin1());
121 if (QFile::exists(tempDir +
"/logs/mythburn.lck"))
122 QFile::remove(tempDir +
"/logs/mythburn.lck");
127 QString command = QString(
"mythutil --copyfile --infile '%1' --outfile '%2'")
128 .arg(source, destination);
132 LOG(VB_JOBQUEUE, LOG_ERR,
133 QString(
"Failed while running %1. Result: %2").arg(command).arg(res));
142 LOG(VB_JOBQUEUE, LOG_INFO,
"Creating ISO image");
146 tempDirectory +=
"work/";
149 QString command = mkisofs +
" -R -J -V 'MythTV Archive' -o ";
150 command += tempDirectory +
"mythburn.iso " + sourceDirectory;
155 LOG(VB_JOBQUEUE, LOG_ERR,
156 QString(
"Failed while running mkisofs. Result: %1") .arg(res));
160 LOG(VB_JOBQUEUE, LOG_INFO,
"Finished creating ISO image");
164 static int burnISOImage(
int mediaType,
bool bEraseDVDRW,
bool nativeFormat)
168 LOG(VB_JOBQUEUE, LOG_INFO,
"Burning ISO image to " + dvdDrive);
173 tempDirectory +=
"work/";
179 command +=
" -speed=" + QString::number(driveSpeed);
183 if (mediaType ==
AD_DVD_RW && bEraseDVDRW)
185 command +=
" -use-the-force-luke -Z " + dvdDrive;
186 command +=
" -V 'MythTV Archive' -R -J " + tempDirectory;
190 command +=
" -Z " + dvdDrive;
191 command +=
" -V 'MythTV Archive' -R -J " + tempDirectory;
196 if (mediaType ==
AD_DVD_RW && bEraseDVDRW)
198 command +=
" -dvd-compat -use-the-force-luke -Z " + dvdDrive;
199 command +=
" -dvd-video -V 'MythTV DVD' " + tempDirectory +
"/dvd";
203 command +=
" -dvd-compat -Z " + dvdDrive;
204 command +=
" -dvd-video -V 'MythTV DVD' " + tempDirectory +
"/dvd";
211 LOG(VB_JOBQUEUE, LOG_ERR,
212 QString(
"Failed while running growisofs. Result: %1") .arg(res));
216 LOG(VB_JOBQUEUE, LOG_INFO,
"Finished burning ISO image");
222 static int doBurnDVD(
int mediaType,
bool bEraseDVDRW,
bool nativeFormat)
225 "MythArchiveLastRunStart",
229 int res =
burnISOImage(mediaType, bEraseDVDRW, nativeFormat);
232 "MythArchiveLastRunEnd",
242 QDomDocument doc(
"archivejob");
244 if (!
file.open(QIODevice::ReadOnly))
246 LOG(VB_JOBQUEUE, LOG_ERR,
"Could not open job file: " + jobFile);
250 if (!doc.setContent(&
file))
252 LOG(VB_JOBQUEUE, LOG_ERR,
"Could not load job file: " + jobFile);
260 bool bCreateISO =
false;
261 bool bEraseDVDRW =
false;
262 bool bDoBurn =
false;
263 QString saveDirectory;
266 QDomNodeList nodeList = doc.elementsByTagName(
"options");
267 if (nodeList.count() == 1)
269 QDomNode node = nodeList.item(0);
270 QDomElement
options = node.toElement();
273 bCreateISO = (
options.attribute(
"createiso",
"0") ==
"1");
274 bEraseDVDRW = (
options.attribute(
"erasedvdrw",
"0") ==
"1");
275 bDoBurn = (
options.attribute(
"doburn",
"0") ==
"1");
276 mediaType =
options.attribute(
"mediatype",
"0").toInt();
277 saveDirectory =
options.attribute(
"savedirectory",
"");
278 if (!saveDirectory.endsWith(
"/"))
279 saveDirectory +=
"/";
284 LOG(VB_JOBQUEUE, LOG_ERR,
285 QString(
"Found %1 options nodes - should be 1")
286 .arg(nodeList.count()));
289 LOG(VB_JOBQUEUE, LOG_INFO,
290 QString(
"Options - createiso: %1,"
291 " doburn: %2, mediatype: %3, erasedvdrw: %4")
292 .arg(bCreateISO).arg(bDoBurn).arg(mediaType).arg(bEraseDVDRW));
293 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"savedirectory: %1").arg(saveDirectory));
298 saveDirectory = tempDir;
299 if (!saveDirectory.endsWith(
"/"))
300 saveDirectory +=
"/";
302 saveDirectory +=
"work/";
304 QDir dir(saveDirectory);
308 LOG(VB_GENERAL, LOG_ERR,
309 "NativeArchive: Failed to clear work directory");
311 dir.mkpath(saveDirectory);
314 LOG(VB_JOBQUEUE, LOG_INFO,
315 QString(
"Saving files to : %1").arg(saveDirectory));
318 nodeList = doc.elementsByTagName(
"file");
319 if (nodeList.count() < 1)
321 LOG(VB_JOBQUEUE, LOG_ERR,
"Cannot find any file nodes?");
330 for (
int x = 0; x < nodeList.count(); x++)
332 node = nodeList.item(x);
333 elem = node.toElement();
336 type = elem.attribute(
"type");
338 if (
type.toLower() ==
"recording")
340 else if (
type.toLower() ==
"video")
344 LOG(VB_JOBQUEUE, LOG_ERR,
345 QString(
"Don't know how to archive items of type '%1'")
346 .arg(
type.toLower()));
353 if (mediaType !=
AD_FILE && bDoBurn)
357 LOG(VB_JOBQUEUE, LOG_ERR,
358 "Native archive job failed to complete");
368 LOG(VB_JOBQUEUE, LOG_ERR,
"Native archive job failed to complete");
373 LOG(VB_JOBQUEUE, LOG_INFO,
"Native archive job completed OK");
378 static const QRegularExpression
badChars { R
"((/|\\|:|'|"|\?|\|))" };
392 if (query.
exec(
"DESCRIBE " + tableName))
396 fieldList.append(query.
value(0).toString());
402 return fieldList.count();
406 const QString &saveDirectory)
412 QString title =
fixFilename(itemNode.attribute(
"title"));
413 QString
filename = itemNode.attribute(
"filename");
414 bool doDelete = (itemNode.attribute(
"delete",
"0") ==
"0");
415 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"Archiving %1 (%2), do delete: %3")
416 .arg(title,
filename, doDelete ?
"true" :
"false"));
420 LOG(VB_JOBQUEUE, LOG_ERR,
"Bad title or filename");
426 LOG(VB_JOBQUEUE, LOG_ERR,
427 QString(
"Failed to extract chanID and startTime from '%1'")
433 QDir dir(saveDirectory + title);
435 dir.mkpath(saveDirectory + title);
437 LOG(VB_GENERAL, LOG_ERR,
"Failed to create savedir: " +
ENO);
439 LOG(VB_JOBQUEUE, LOG_INFO,
"Creating xml file for " + title);
440 QDomDocument doc(
"MYTHARCHIVEITEM");
442 QDomElement root = doc.createElement(
"item");
443 doc.appendChild(root);
444 root.setAttribute(
"type",
"recording");
445 root.setAttribute(
"databaseversion",
dbVersion);
447 QDomElement recorded = doc.createElement(
"recorded");
448 root.appendChild(recorded);
451 QStringList fieldList;
455 query.
prepare(
"SELECT " + fieldList.join(
",")
457 " WHERE chanid = :CHANID and starttime = :STARTTIME;");
459 query.
bindValue(
":STARTTIME", startTime);
466 for (
int x = 0; x < fieldList.size(); x++)
468 elem = doc.createElement(fieldList[x]);
469 text = doc.createTextNode(query.
value(x).toString());
470 elem.appendChild(text);
471 recorded.appendChild(elem);
474 LOG(VB_JOBQUEUE, LOG_INFO,
"Created recorded element for " + title);
478 LOG(VB_JOBQUEUE, LOG_INFO,
"Failed to get recorded field list");
482 query.
prepare(
"SELECT chanid, channum, callsign, name "
483 "FROM channel WHERE chanid = :CHANID;");
488 QDomElement channel = doc.createElement(
"channel");
489 channel.setAttribute(
"chanid", query.
value(0).toString());
490 channel.setAttribute(
"channum", query.
value(1).toString());
491 channel.setAttribute(
"callsign", query.
value(2).toString());
492 channel.setAttribute(
"name", query.
value(3).toString());
493 root.appendChild(channel);
494 LOG(VB_JOBQUEUE, LOG_INFO,
"Created channel element for " + title);
499 LOG(VB_JOBQUEUE, LOG_ERR,
500 "Cannot find channel details for chanid " + chanID);
501 QDomElement channel = doc.createElement(
"channel");
502 channel.setAttribute(
"chanid", chanID);
503 channel.setAttribute(
"channum",
"unknown");
504 channel.setAttribute(
"callsign",
"unknown");
505 channel.setAttribute(
"name",
"unknown");
506 root.appendChild(channel);
507 LOG(VB_JOBQUEUE, LOG_INFO,
508 "Created a default channel element for " + title);
512 query.
prepare(
"SELECT credits.person, role, people.name "
513 "FROM recordedcredits AS credits "
514 "LEFT JOIN people ON credits.person = people.person "
515 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
517 query.
bindValue(
":STARTTIME", startTime);
521 QDomElement credits = doc.createElement(
"credits");
524 QDomElement credit = doc.createElement(
"credit");
525 credit.setAttribute(
"personid", query.
value(0).toString());
526 credit.setAttribute(
"name", query.
value(2).toString());
527 credit.setAttribute(
"role", query.
value(1).toString());
528 credits.appendChild(credit);
530 root.appendChild(credits);
531 LOG(VB_JOBQUEUE, LOG_INFO,
"Created credits element for " + title);
535 query.
prepare(
"SELECT `system`, rating FROM recordedrating "
536 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
538 query.
bindValue(
":STARTTIME", startTime);
542 QDomElement
rating = doc.createElement(
"rating");
543 rating.setAttribute(
"system", query.
value(0).toString());
544 rating.setAttribute(
"rating", query.
value(1).toString());
546 LOG(VB_JOBQUEUE, LOG_INFO,
"Created rating element for " + title);
550 QDomElement recordedmarkup = doc.createElement(
"recordedmarkup");
551 query.
prepare(
"SELECT chanid, starttime, mark, type, data "
552 "FROM recordedmarkup "
553 "WHERE chanid = :CHANID and starttime = :STARTTIME;");
555 query.
bindValue(
":STARTTIME", startTime);
560 QDomElement
mark = doc.createElement(
"mark");
561 mark.setAttribute(
"mark", query.
value(2).toString());
562 mark.setAttribute(
"type", query.
value(3).toString());
563 mark.setAttribute(
"data", query.
value(4).toString());
564 recordedmarkup.appendChild(
mark);
566 root.appendChild(recordedmarkup);
567 LOG(VB_JOBQUEUE, LOG_INFO,
"Created recordedmarkup element for " + title);
571 QDomElement recordedseek = doc.createElement(
"recordedseek");
572 query.
prepare(
"SELECT chanid, starttime, mark, `offset`, type "
574 "WHERE chanid = :CHANID and starttime = :STARTTIME;");
576 query.
bindValue(
":STARTTIME", startTime);
581 QDomElement
mark = doc.createElement(
"mark");
582 mark.setAttribute(
"mark", query.
value(2).toString());
583 mark.setAttribute(
"offset", query.
value(3).toString());
584 mark.setAttribute(
"type", query.
value(4).toString());
585 recordedseek.appendChild(
mark);
587 root.appendChild(recordedseek);
588 LOG(VB_JOBQUEUE, LOG_INFO,
589 "Created recordedseek element for " + title);
594 QString xmlFile = saveDirectory + title +
"/" + baseName +
".xml";
596 if (!f.open(QIODevice::WriteOnly))
598 LOG(VB_JOBQUEUE, LOG_ERR,
599 "MythNativeWizard: Failed to open file for writing - " + xmlFile);
604 t << doc.toString(4);
608 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
614 if (QFile::exists(
filename +
".png"))
616 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying preview image");
618 + title +
"/" + baseName +
".png");
623 LOG(VB_JOBQUEUE, LOG_INFO,
"Item Archived OK");
629 const QString &saveDirectory)
634 QString coverFile =
"";
636 QString title =
fixFilename(itemNode.attribute(
"title"));
637 QString
filename = itemNode.attribute(
"filename");
638 bool doDelete = (itemNode.attribute(
"delete",
"0") ==
"0");
639 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"Archiving %1 (%2), do delete: %3")
640 .arg(title,
filename, doDelete ?
"true" :
"false"));
644 LOG(VB_JOBQUEUE, LOG_ERR,
"Bad title or filename");
649 QDir dir(saveDirectory + title);
651 dir.mkdir(saveDirectory + title);
653 LOG(VB_JOBQUEUE, LOG_INFO,
"Creating xml file for " + title);
654 QDomDocument doc(
"MYTHARCHIVEITEM");
656 QDomElement root = doc.createElement(
"item");
657 doc.appendChild(root);
658 root.setAttribute(
"type",
"video");
659 root.setAttribute(
"databaseversion",
dbVersion);
661 QDomElement video = doc.createElement(
"videometadata");
662 root.appendChild(video);
666 query.
prepare(
"SELECT intid, title, director, plot, rating, inetref, "
667 "year, userrating, length, showlevel, filename, coverfile, "
668 "childid, browse, playcommand, category "
669 "FROM videometadata WHERE filename = :FILENAME;");
677 elem = doc.createElement(
"intid");
678 text = doc.createTextNode(query.
value(0).toString());
679 intID = query.
value(0).toInt();
680 elem.appendChild(text);
681 video.appendChild(elem);
683 elem = doc.createElement(
"title");
684 text = doc.createTextNode(query.
value(1).toString());
685 elem.appendChild(text);
686 video.appendChild(elem);
688 elem = doc.createElement(
"director");
689 text = doc.createTextNode(query.
value(2).toString());
690 elem.appendChild(text);
691 video.appendChild(elem);
693 elem = doc.createElement(
"plot");
694 text = doc.createTextNode(query.
value(3).toString());
695 elem.appendChild(text);
696 video.appendChild(elem);
698 elem = doc.createElement(
"rating");
699 text = doc.createTextNode(query.
value(4).toString());
700 elem.appendChild(text);
701 video.appendChild(elem);
703 elem = doc.createElement(
"inetref");
704 text = doc.createTextNode(query.
value(5).toString());
705 elem.appendChild(text);
706 video.appendChild(elem);
708 elem = doc.createElement(
"year");
709 text = doc.createTextNode(query.
value(6).toString());
710 elem.appendChild(text);
711 video.appendChild(elem);
713 elem = doc.createElement(
"userrating");
714 text = doc.createTextNode(query.
value(7).toString());
715 elem.appendChild(text);
716 video.appendChild(elem);
718 elem = doc.createElement(
"length");
719 text = doc.createTextNode(query.
value(8).toString());
720 elem.appendChild(text);
721 video.appendChild(elem);
723 elem = doc.createElement(
"showlevel");
724 text = doc.createTextNode(query.
value(9).toString());
725 elem.appendChild(text);
726 video.appendChild(elem);
729 QString fname = query.
value(10).toString();
733 elem = doc.createElement(
"filename");
734 text = doc.createTextNode(fname);
735 elem.appendChild(text);
736 video.appendChild(elem);
738 elem = doc.createElement(
"coverfile");
739 text = doc.createTextNode(query.
value(11).toString());
740 coverFile = query.
value(11).toString();
741 elem.appendChild(text);
742 video.appendChild(elem);
744 elem = doc.createElement(
"childid");
745 text = doc.createTextNode(query.
value(12).toString());
746 elem.appendChild(text);
747 video.appendChild(elem);
749 elem = doc.createElement(
"browse");
750 text = doc.createTextNode(query.
value(13).toString());
751 elem.appendChild(text);
752 video.appendChild(elem);
754 elem = doc.createElement(
"playcommand");
755 text = doc.createTextNode(query.
value(14).toString());
756 elem.appendChild(text);
757 video.appendChild(elem);
759 elem = doc.createElement(
"categoryid");
760 text = doc.createTextNode(query.
value(15).toString());
761 categoryID = query.
value(15).toInt();
762 elem.appendChild(text);
763 video.appendChild(elem);
765 LOG(VB_JOBQUEUE, LOG_INFO,
766 "Created videometadata element for " + title);
770 query.
prepare(
"SELECT intid, category "
771 "FROM videocategory WHERE intid = :INTID;");
776 QDomElement category = doc.createElement(
"category");
777 category.setAttribute(
"intid", query.
value(0).toString());
778 category.setAttribute(
"category", query.
value(1).toString());
779 root.appendChild(category);
780 LOG(VB_JOBQUEUE, LOG_INFO,
781 "Created videocategory element for " + title);
785 QDomElement countries = doc.createElement(
"countries");
786 root.appendChild(countries);
788 query.
prepare(
"SELECT intid, country "
789 "FROM videometadatacountry INNER JOIN videocountry "
790 "ON videometadatacountry.idcountry = videocountry.intid "
791 "WHERE idvideo = :INTID;");
801 QDomElement country = doc.createElement(
"country");
802 country.setAttribute(
"intid", query.
value(0).toString());
803 country.setAttribute(
"country", query.
value(1).toString());
804 countries.appendChild(country);
806 LOG(VB_JOBQUEUE, LOG_INFO,
"Created videocountry element for " + title);
810 QDomElement genres = doc.createElement(
"genres");
811 root.appendChild(genres);
813 query.
prepare(
"SELECT intid, genre "
814 "FROM videometadatagenre INNER JOIN videogenre "
815 "ON videometadatagenre.idgenre = videogenre.intid "
816 "WHERE idvideo = :INTID;");
826 QDomElement genre = doc.createElement(
"genre");
827 genre.setAttribute(
"intid", query.
value(0).toString());
828 genre.setAttribute(
"genre", query.
value(1).toString());
829 genres.appendChild(genre);
831 LOG(VB_JOBQUEUE, LOG_INFO,
"Created videogenre element for " + title);
836 QString xmlFile = saveDirectory + title +
"/"
837 + fileInfo.fileName() +
".xml";
839 if (!f.open(QIODevice::WriteOnly))
841 LOG(VB_JOBQUEUE, LOG_INFO,
842 "MythNativeWizard: Failed to open file for writing - " + xmlFile);
847 t << doc.toString(4);
851 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
853 +
"/" + fileInfo.fileName());
860 fileInfo.setFile(coverFile);
861 if (fileInfo.exists())
863 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying cover file");
864 res =
copyFile(coverFile, saveDirectory + title
865 +
"/" + fileInfo.fileName());
872 LOG(VB_JOBQUEUE, LOG_INFO,
"Item Archived OK");
880 QDomDocument doc(
"mydocument");
882 if (!
file.open(QIODevice::ReadOnly))
884 LOG(VB_JOBQUEUE, LOG_ERR,
885 "Failed to open file for reading - " + xmlFile);
889 if (!doc.setContent(&
file))
892 LOG(VB_JOBQUEUE, LOG_ERR,
893 "Failed to read from xml file - " + xmlFile);
898 QString docType = doc.doctype().name();
901 QDomNodeList itemNodeList;
903 QDomElement itemNode;
905 if (docType ==
"MYTHARCHIVEITEM")
907 itemNodeList = doc.elementsByTagName(
"item");
909 if (itemNodeList.count() < 1)
911 LOG(VB_JOBQUEUE, LOG_ERR,
912 "Couldn't find an 'item' element in XML file");
916 node = itemNodeList.item(0);
917 itemNode = node.toElement();
918 type = itemNode.attribute(
"type");
919 dbVersion = itemNode.attribute(
"databaseversion");
921 LOG(VB_JOBQUEUE, LOG_INFO,
922 QString(
"Archive DB version: %1, Local DB version: %2")
927 LOG(VB_JOBQUEUE, LOG_ERR,
"Not a native archive xml file - " + xmlFile);
931 if (
type ==
"recording")
944 const QString &xmlFile,
int chanID)
946 LOG(VB_JOBQUEUE, LOG_INFO,
947 QString(
"Import recording using chanID: %1").arg(chanID));
948 LOG(VB_JOBQUEUE, LOG_INFO,
949 QString(
"Archived recording xml file: %1").arg(xmlFile));
951 QString videoFile = xmlFile.left(xmlFile.length() - 4);
952 QString basename = videoFile;
953 int pos = videoFile.lastIndexOf(
'/');
955 basename = videoFile.mid(pos + 1);
957 QDomNodeList nodeList = itemNode.elementsByTagName(
"recorded");
958 if (nodeList.count() < 1)
960 LOG(VB_JOBQUEUE, LOG_ERR,
961 "Couldn't find a 'recorded' element in XML file");
965 QDomNode n = nodeList.item(0);
966 QDomElement recordedNode = n.toElement();
967 QString startTime =
findNodeText(recordedNode,
"starttime");
970 query.
prepare(
"SELECT * FROM recorded "
971 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
973 query.
bindValue(
":STARTTIME", startTime);
978 LOG(VB_JOBQUEUE, LOG_ERR,
979 "This recording appears to already exist!!");
986 basename ,
"Default");
989 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file to: " + destFile);
994 if (QFile::exists(videoFile +
".png"))
996 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying preview image file to: " + destFile +
".png");
997 if (!
copyFile(videoFile +
".png", destFile +
".png"))
1002 QStringList fieldList;
1003 QStringList bindList;
1004 QDomNodeList nodes = recordedNode.childNodes();
1006 for (
int x = 0; x < nodes.count(); x++)
1008 QDomNode n2 = nodes.item(x);
1009 QString field = n2.nodeName();
1010 fieldList.append(field);
1011 bindList.append(
":" + field.toUpper());
1015 query.
prepare(
"INSERT INTO recorded (" + fieldList.join(
",") +
") "
1016 "VALUES (" + bindList.join(
",") +
");");
1018 query.
bindValue(
":STARTTIME", startTime);
1020 for (
int x = 0; x < fieldList.count(); x++)
1024 LOG(VB_JOBQUEUE, LOG_INFO,
"Inserted recorded details into database");
1029 nodeList = itemNode.elementsByTagName(
"recordedmarkup");
1030 if (nodeList.count() < 1)
1032 LOG(VB_JOBQUEUE, LOG_WARNING,
1033 "Couldn't find a 'recordedmarkup' element in XML file");
1037 QDomNode n3 = nodeList.item(0);
1038 QDomElement markupNode = n3.toElement();
1040 nodeList = markupNode.elementsByTagName(
"mark");
1041 if (nodeList.count() < 1)
1043 LOG(VB_JOBQUEUE, LOG_WARNING,
1044 "Couldn't find any 'mark' elements in XML file");
1049 query.
prepare(
"DELETE FROM recordedmarkup "
1050 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
1052 query.
bindValue(
":STARTTIME", startTime);
1058 for (
int x = 0; x < nodeList.count(); x++)
1060 QDomNode n4 = nodeList.item(x);
1061 QDomElement e = n4.toElement();
1062 query.
prepare(
"INSERT INTO recordedmarkup (chanid, starttime, "
1064 "VALUES(:CHANID,:STARTTIME,:MARK,:TYPE,:DATA);");
1066 query.
bindValue(
":STARTTIME", startTime);
1067 query.
bindValue(
":MARK", e.attribute(
"mark"));
1068 query.
bindValue(
":TYPE", e.attribute(
"type"));
1069 query.
bindValue(
":DATA", e.attribute(
"data"));
1078 LOG(VB_JOBQUEUE, LOG_INFO,
1079 "Inserted recordedmarkup details into database");
1084 nodeList = itemNode.elementsByTagName(
"recordedseek");
1085 if (nodeList.count() < 1)
1087 LOG(VB_JOBQUEUE, LOG_WARNING,
1088 "Couldn't find a 'recordedseek' element in XML file");
1092 QDomNode n5 = nodeList.item(0);
1093 QDomElement markupNode = n5.toElement();
1095 nodeList = markupNode.elementsByTagName(
"mark");
1096 if (nodeList.count() < 1)
1098 LOG(VB_JOBQUEUE, LOG_WARNING,
1099 "Couldn't find any 'mark' elements in XML file");
1104 query.
prepare(
"DELETE FROM recordedseek "
1105 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
1107 query.
bindValue(
":STARTTIME", startTime);
1111 for (
int x = 0; x < nodeList.count(); x++)
1113 QDomNode n6 = nodeList.item(x);
1114 QDomElement e = n6.toElement();
1115 query.
prepare(
"INSERT INTO recordedseek (chanid, starttime, "
1116 "mark, `offset`, type)"
1117 "VALUES(:CHANID,:STARTTIME,:MARK,:OFFSET,:TYPE);");
1119 query.
bindValue(
":STARTTIME", startTime);
1120 query.
bindValue(
":MARK", e.attribute(
"mark"));
1121 query.
bindValue(
":OFFSET", e.attribute(
"offset"));
1122 query.
bindValue(
":TYPE", e.attribute(
"type"));
1131 LOG(VB_JOBQUEUE, LOG_INFO,
1132 "Inserted recordedseek details into database");
1140 LOG(VB_JOBQUEUE, LOG_INFO,
"Import completed OK");
1147 LOG(VB_JOBQUEUE, LOG_INFO,
"Importing video");
1148 LOG(VB_JOBQUEUE, LOG_INFO,
1149 QString(
"Archived video xml file: %1").arg(xmlFile));
1151 QString videoFile = xmlFile.left(xmlFile.length() - 4);
1152 QFileInfo fileInfo(videoFile);
1153 QString basename = fileInfo.fileName();
1155 QDomNodeList nodeList = itemNode.elementsByTagName(
"videometadata");
1156 if (nodeList.count() < 1)
1158 LOG(VB_JOBQUEUE, LOG_ERR,
1159 "Couldn't find a 'videometadata' element in XML file");
1163 QDomNode n = nodeList.item(0);
1164 QDomElement videoNode = n.toElement();
1168 QString origFilename =
findNodeText(videoNode,
"filename");
1169 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1170 QStringList dirList = origFilename.split(
"/", QString::SkipEmptyParts);
1172 QStringList dirList = origFilename.split(
"/", Qt::SkipEmptyParts);
1175 for (
int x = 0; x < dirList.count() - 1; x++)
1177 path +=
"/" + dirList[x];
1178 if (!dir.exists(path))
1180 if (!dir.mkdir(path))
1182 LOG(VB_JOBQUEUE, LOG_ERR,
1183 QString(
"Couldn't create directory '%1'").arg(path));
1189 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
1190 if (!
copyFile(videoFile, path +
"/" + basename))
1198 fileInfo.setFile(videoFile);
1199 QString archivePath = fileInfo.absolutePath();
1201 QString coverFilename =
findNodeText(videoNode,
"coverfile");
1202 fileInfo.setFile(coverFilename);
1203 coverFilename = fileInfo.fileName();
1205 fileInfo.setFile(archivePath +
"/" + coverFilename);
1206 if (fileInfo.exists())
1208 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying cover file");
1210 if (!
copyFile(archivePath +
"/" + coverFilename, artworkDir +
"/" + coverFilename))
1216 coverFilename =
"No Cover";
1220 query.
prepare(
"INSERT INTO videometadata (title, director, plot, rating, inetref, "
1221 "year, userrating, length, showlevel, filename, coverfile, "
1222 "childid, browse, playcommand, category) "
1223 "VALUES(:TITLE,:DIRECTOR,:PLOT,:RATING,:INETREF,:YEAR,"
1224 ":USERRATING,:LENGTH,:SHOWLEVEL,:FILENAME,:COVERFILE,"
1225 ":CHILDID,:BROWSE,:PLAYCOMMAND,:CATEGORY);");
1235 query.
bindValue(
":FILENAME", path +
"/" + basename);
1236 query.
bindValue(
":COVERFILE", artworkDir +
"/" + coverFilename);
1244 LOG(VB_JOBQUEUE, LOG_INFO,
1245 "Inserted videometadata details into database");
1255 query.
prepare(
"SELECT intid FROM videometadata WHERE filename = :FILENAME;");
1256 query.
bindValue(
":FILENAME", path +
"/" + basename);
1259 intid = query.
value(0).toInt();
1267 LOG(VB_JOBQUEUE, LOG_INFO,
1268 QString(
"'intid' of inserted video is: %1").arg(intid));
1271 nodeList = itemNode.elementsByTagName(
"genres");
1272 if (nodeList.count() < 1)
1274 LOG(VB_JOBQUEUE, LOG_ERR,
"No 'genres' element found in XML file");
1278 n = nodeList.item(0);
1279 QDomElement genresNode = n.toElement();
1281 nodeList = genresNode.elementsByTagName(
"genre");
1282 if (nodeList.count() < 1)
1284 LOG(VB_JOBQUEUE, LOG_WARNING,
1285 "Couldn't find any 'genre' elements in XML file");
1289 for (
int x = 0; x < nodeList.count(); x++)
1291 n = nodeList.item(x);
1292 QDomElement e = n.toElement();
1294 QString genre = e.attribute(
"genre");
1297 query.
prepare(
"SELECT intid FROM videogenre "
1298 "WHERE genre = :GENRE");
1302 genreID = query.
value(0).toInt();
1307 query.
prepare(
"INSERT INTO videogenre (genre) VALUES(:GENRE);");
1311 "insert videogenre", query);
1314 query.
prepare(
"SELECT intid FROM videogenre "
1315 "WHERE genre = :GENRE");
1317 if (!query.
exec() || !query.
next())
1319 LOG(VB_JOBQUEUE, LOG_ERR,
1320 "Couldn't add genre to database");
1323 genreID = query.
value(0).toInt();
1327 query.
prepare(
"INSERT INTO videometadatagenre (idvideo, idgenre)"
1328 "VALUES (:IDVIDEO, :IDGENRE);");
1333 "insert videometadatagenre", query);
1336 LOG(VB_JOBQUEUE, LOG_INFO,
"Inserted genre details into database");
1341 nodeList = itemNode.elementsByTagName(
"countries");
1342 if (nodeList.count() < 1)
1344 LOG(VB_JOBQUEUE, LOG_INFO,
"No 'countries' element found in XML file");
1348 n = nodeList.item(0);
1349 QDomElement countriesNode = n.toElement();
1351 nodeList = countriesNode.elementsByTagName(
"country");
1352 if (nodeList.count() < 1)
1354 LOG(VB_JOBQUEUE, LOG_WARNING,
1355 "Couldn't find any 'country' elements in XML file");
1359 for (
int x = 0; x < nodeList.count(); x++)
1361 n = nodeList.item(x);
1362 QDomElement e = n.toElement();
1364 QString country = e.attribute(
"country");
1367 query.
prepare(
"SELECT intid FROM videocountry "
1368 "WHERE country = :COUNTRY");
1372 countryID = query.
value(0).toInt();
1377 query.
prepare(
"INSERT INTO videocountry (country) VALUES(:COUNTRY);");
1381 "insert videocountry", query);
1384 query.
prepare(
"SELECT intid FROM videocountry "
1385 "WHERE country = :COUNTRY");
1387 if (!query.
exec() || !query.
next())
1389 LOG(VB_JOBQUEUE, LOG_ERR,
1390 "Couldn't add country to database");
1393 countryID = query.
value(0).toInt();
1397 query.
prepare(
"INSERT INTO videometadatacountry (idvideo, idcountry)"
1398 "VALUES (:IDVIDEO, :IDCOUNTRY);");
1400 query.
bindValue(
":IDCOUNTRY", countryID);
1403 "insert videometadatacountry", query);
1406 LOG(VB_JOBQUEUE, LOG_INFO,
1407 "Inserted country details into database");
1412 nodeList = itemNode.elementsByTagName(
"category");
1413 if (nodeList.count() < 1)
1415 LOG(VB_JOBQUEUE, LOG_ERR,
"No 'category' element found in XML file");
1419 n = nodeList.item(0);
1420 QDomElement e = n.toElement();
1422 QString category = e.attribute(
"category");
1424 query.
prepare(
"SELECT intid FROM videocategory "
1425 "WHERE category = :CATEGORY");
1429 categoryID = query.
value(0).toInt();
1434 query.
prepare(
"INSERT INTO videocategory (category) VALUES(:CATEGORY);");
1438 "insert videocategory", query);
1441 query.
prepare(
"SELECT intid FROM videocategory "
1442 "WHERE category = :CATEGORY");
1446 categoryID = query.
value(0).toInt();
1450 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't add category to database");
1456 query.
prepare(
"UPDATE videometadata "
1457 "SET category = :CATEGORY "
1458 "WHERE intid = :INTID;");
1459 query.
bindValue(
":CATEGORY", categoryID);
1463 "update category", query);
1465 LOG(VB_JOBQUEUE, LOG_INFO,
"Fixed the category in the database");
1468 LOG(VB_JOBQUEUE, LOG_INFO,
"Import completed OK");
1475 QDomNodeList nodeList = elem.elementsByTagName(nodeName);
1476 if (nodeList.count() < 1)
1478 LOG(VB_GENERAL, LOG_ERR,
1479 QString(
"Couldn't find a '%1' element in XML file") .arg(nodeName));
1483 QDomNode n = nodeList.item(0);
1484 QDomElement e = n.toElement();
1487 for (QDomNode node = e.firstChild(); !node.isNull();
1488 node = node.nextSibling())
1490 QDomText
t = node.toText();
1500 if ((nodeName ==
"recgroup") ||
1501 (nodeName ==
"playgroup"))
1505 else if ((nodeName ==
"recordid") ||
1506 (nodeName ==
"seriesid") ||
1507 (nodeName ==
"programid") ||
1508 (nodeName ==
"profile"))
1519 query.
prepare(
"DELETE FROM archiveitems;");
1529 "MythArchiveLastRunStart",
1536 "MythArchiveLastRunEnd",
1539 (res == 0 ?
"Success" :
"Failed"));
1554 static int grabThumbnail(
const QString& inFile,
const QString& thumbList,
const QString& outFile,
int frameCount)
1557 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"grabThumbnail(): Opening '%1'")
1564 LOG(VB_JOBQUEUE, LOG_ERR,
"grabThumbnail(): Couldn't open input file" +
1570 int ret = avformat_find_stream_info(inputFC,
nullptr);
1573 LOG(VB_JOBQUEUE, LOG_ERR,
1574 QString(
"Couldn't get stream info, error #%1").arg(ret));
1579 int videostream = -1;
1584 for (
uint i = 0; i < inputFC->nb_streams; i++)
1586 AVStream *st = inputFC->streams[i];
1587 if (inputFC->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
1590 width = st->codecpar->width;
1591 height = st->codecpar->height;
1592 if (st->r_frame_rate.den && st->r_frame_rate.num)
1593 fps = av_q2d(st->r_frame_rate);
1595 fps = 1/av_q2d(st->time_base);
1600 if (videostream == -1)
1602 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't find a video stream");
1607 AVCodecContext *codecCtx = codecmap.
GetCodecContext(inputFC->streams[videostream]);
1610 const AVCodec * codec = avcodec_find_decoder(codecCtx->codec_id);
1612 if (codec ==
nullptr)
1614 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't find codec for video stream");
1619 if (avcodec_open2(codecCtx, codec,
nullptr) < 0)
1621 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't open codec for video stream");
1626 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1627 QStringList list = thumbList.split(
",", QString::SkipEmptyParts);
1629 QStringList list = thumbList.split(
",", Qt::SkipEmptyParts);
1639 memset(&orig, 0,
sizeof(
AVFrame));
1640 memset(&retbuf, 0,
sizeof(
AVFrame));
1643 int bufflen = width * height * 4;
1644 auto *outputbuf =
new unsigned char[bufflen];
1648 bool frameFinished =
false;
1650 while (av_read_frame(inputFC, &pkt) >= 0)
1652 if (pkt.stream_index == videostream)
1655 if (list[thumbCount].toInt() == (
int)(frameNo / fps))
1659 avcodec_flush_buffers(codecCtx);
1660 av_frame_unref(frame);
1661 frameFinished =
false;
1662 ret = avcodec_receive_frame(codecCtx, frame);
1664 frameFinished =
true;
1665 if (ret == 0 || ret == AVERROR(EAGAIN))
1666 avcodec_send_packet(codecCtx, &pkt);
1667 int keyFrame = frame->key_frame;
1669 while (!frameFinished || !keyFrame)
1671 av_packet_unref(&pkt);
1672 int res = av_read_frame(inputFC, &pkt);
1675 if (pkt.stream_index == videostream)
1678 av_frame_unref(frame);
1679 ret = avcodec_receive_frame(codecCtx, frame);
1681 frameFinished =
true;
1682 if (ret == 0 || ret == AVERROR(EAGAIN))
1683 avcodec_send_packet(codecCtx, &pkt);
1684 keyFrame = frame->key_frame;
1691 QString saveFormat =
"JPEG";
1692 if (outFile.right(4) ==
".png")
1696 while (count < frameCount)
1704 av_image_fill_arrays(retbuf.data, retbuf.linesize, outputbuf,
1705 AV_PIX_FMT_RGB32, width, height, IMAGE_ALIGN);
1710 copyframe.
Copy(&retbuf, AV_PIX_FMT_RGB32,
tmp,
1711 codecCtx->pix_fmt, width, height);
1713 QImage img(outputbuf, width, height,
1714 QImage::Format_RGB32);
1716 if (!img.save(
filename, qPrintable(saveFormat)))
1718 LOG(VB_GENERAL, LOG_ERR,
1719 QString(
"grabThumbnail(): Failed to save "
1726 if (count <= frameCount)
1729 frameFinished =
false;
1730 while (!frameFinished)
1732 int res = av_read_frame(inputFC, &pkt);
1735 if (pkt.stream_index == videostream)
1738 ret = avcodec_receive_frame(codecCtx, frame);
1740 frameFinished =
true;
1741 if (ret == 0 || ret == AVERROR(EAGAIN))
1742 avcodec_send_packet(codecCtx, &pkt);
1749 if (thumbCount >= list.count())
1754 av_packet_unref(&pkt);
1769 LOG(VB_JOBQUEUE, LOG_INFO,
"Calculating frame count");
1771 AVPacket *pkt = av_packet_alloc();
1774 LOG(VB_GENERAL, LOG_ERR,
"packet allocation failed");
1777 while (av_read_frame(inputFC, pkt) >= 0)
1779 if (pkt->stream_index == vid_id)
1783 av_packet_unref(pkt);
1785 av_packet_free(&pkt);
1794 int pos =
filename.lastIndexOf(
'/');
1809 frm_dir_map_t::iterator it;
1810 uint64_t frames = 0;
1814 if (cutlist.empty())
1820 for (it = cutlist.begin(); it != cutlist.end();)
1829 if (it != cutlist.end())
1849 frames += end - start;
1860 int pos =
filename.lastIndexOf(
'/');
1864 int keyframedist = -1;
1872 if (!posMap.empty())
1879 if (!posMap.empty())
1882 if (fps < 26 && fps > 24)
1888 if (!posMap.empty())
1902 frm_pos_map_t::const_iterator it = posMap.cend();
1904 uint64_t totframes = it.key() * keyframedist;
1908 static int getFileInfo(
const QString& inFile,
const QString& outFile,
int lenMethod)
1911 LOG(VB_JOBQUEUE , LOG_INFO, QString(
"getFileInfo(): Opening '%1'")
1918 LOG(VB_JOBQUEUE, LOG_ERR,
"getFileInfo(): Couldn't open input file" +
1924 int ret = avformat_find_stream_info(inputFC,
nullptr);
1928 LOG(VB_JOBQUEUE, LOG_ERR,
1929 QString(
"Couldn't get stream info, error #%1").arg(ret));
1934 av_dump_format(inputFC, 0, qPrintable(inFile), 0);
1936 QDomDocument doc(
"FILEINFO");
1938 QDomElement root = doc.createElement(
"file");
1939 doc.appendChild(root);
1940 root.setAttribute(
"type", inputFC->iformat->name);
1941 root.setAttribute(
"filename", inFile);
1943 QDomElement streams = doc.createElement(
"streams");
1945 root.appendChild(streams);
1946 streams.setAttribute(
"count", inputFC->nb_streams);
1947 int ffmpegIndex = 0;
1950 for (
uint i = 0; i < inputFC->nb_streams; i++)
1952 AVStream *st = inputFC->streams[i];
1953 std::string buf (256,
'\0');
1955 AVCodecParameters *par = st->codecpar;
1958 avcodec_string(buf.data(), buf.size(), avctx,
static_cast<int>(
false));
1960 switch (st->codecpar->codec_type)
1962 case AVMEDIA_TYPE_VIDEO:
1964 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1965 QStringList param = QString::fromStdString(buf).split(
',', QString::SkipEmptyParts);
1967 QStringList param = QString::fromStdString(buf).split(
',', Qt::SkipEmptyParts);
1969 QString codec = param[0].remove(
"Video:", Qt::CaseInsensitive).remove(QChar::Null);
1970 QDomElement stream = doc.createElement(
"video");
1971 stream.setAttribute(
"streamindex", i);
1972 stream.setAttribute(
"ffmpegindex", ffmpegIndex++);
1973 stream.setAttribute(
"codec", codec.trimmed());
1974 stream.setAttribute(
"width", par->width);
1975 stream.setAttribute(
"height", par->height);
1976 stream.setAttribute(
"bitrate", (qlonglong)par->bit_rate);
1979 if (st->r_frame_rate.den && st->r_frame_rate.num)
1980 fps = av_q2d(st->r_frame_rate);
1982 fps = 1/av_q2d(st->time_base);
1984 stream.setAttribute(
"fps", fps);
1986 if (par->sample_aspect_ratio.den && par->sample_aspect_ratio.num)
1988 float aspect_ratio = av_q2d(par->sample_aspect_ratio);
1989 if (QString(inputFC->iformat->name) !=
"nuv")
1990 aspect_ratio = ((float)par->width
1991 / par->height) * aspect_ratio;
1993 stream.setAttribute(
"aspectratio", aspect_ratio);
1996 stream.setAttribute(
"aspectratio",
"N/A");
1998 stream.setAttribute(
"id", st->id);
2000 if (st->start_time != (
int) AV_NOPTS_VALUE)
2002 int secs = st->start_time / AV_TIME_BASE;
2003 int us = st->start_time % AV_TIME_BASE;
2004 stream.setAttribute(
"start_time", QString(
"%1.%2")
2005 .arg(secs).arg(av_rescale(us, 1000000, AV_TIME_BASE)));
2008 stream.setAttribute(
"start_time", 0);
2010 streams.appendChild(stream);
2016 int64_t frameCount = 0;
2023 if (inputFC->duration != (
uint) AV_NOPTS_VALUE)
2025 duration = (
uint) (inputFC->duration / AV_TIME_BASE);
2026 root.setAttribute(
"duration", duration);
2027 LOG(VB_JOBQUEUE, LOG_INFO,
2028 QString(
"duration = %1") .arg(duration));
2029 frameCount = (int64_t)(duration * fps);
2032 root.setAttribute(
"duration",
"N/A");
2039 LOG(VB_JOBQUEUE, LOG_INFO,
2040 QString(
"frames = %1").arg(frameCount));
2041 duration = (
uint)(frameCount / fps);
2042 LOG(VB_JOBQUEUE, LOG_INFO,
2043 QString(
"duration = %1").arg(duration));
2044 root.setAttribute(
"duration", duration);
2054 LOG(VB_JOBQUEUE, LOG_INFO,
2055 QString(
"frames = %1").arg(frameCount));
2056 duration = (
uint)(frameCount / fps);
2057 LOG(VB_JOBQUEUE, LOG_INFO,
2058 QString(
"duration = %1").arg(duration));
2059 root.setAttribute(
"duration", duration);
2061 else if (inputFC->duration != (
uint) AV_NOPTS_VALUE)
2063 duration = (
uint) (inputFC->duration / AV_TIME_BASE);
2064 root.setAttribute(
"duration", duration);
2065 LOG(VB_JOBQUEUE, LOG_INFO,
2066 QString(
"duration = %1").arg(duration));
2067 frameCount = (int64_t)(duration * fps);
2070 root.setAttribute(
"duration",
"N/A");
2074 root.setAttribute(
"duration",
"N/A");
2075 LOG(VB_JOBQUEUE, LOG_ERR,
2076 QString(
"Unknown lenMethod (%1)")
2082 LOG(VB_JOBQUEUE, LOG_INFO,
2083 QString(
"cutframes = %1").arg(cutFrames));
2084 int cutduration = (int)(cutFrames / fps);
2085 LOG(VB_JOBQUEUE, LOG_INFO,
2086 QString(
"cutduration = %1").arg(cutduration));
2087 root.setAttribute(
"cutduration", duration - cutduration);
2093 case AVMEDIA_TYPE_AUDIO:
2095 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
2096 QStringList param = QString::fromStdString(buf).split(
',', QString::SkipEmptyParts);
2098 QStringList param = QString::fromStdString(buf).split(
',', Qt::SkipEmptyParts);
2100 QString codec = param[0].remove(
"Audio:", Qt::CaseInsensitive).remove(QChar::Null);
2102 QDomElement stream = doc.createElement(
"audio");
2103 stream.setAttribute(
"streamindex", i);
2104 stream.setAttribute(
"ffmpegindex", ffmpegIndex++);
2108 if (codec.trimmed().toLower() ==
"liba52")
2109 stream.setAttribute(
"codec",
"AC3");
2111 stream.setAttribute(
"codec", codec.trimmed());
2113 stream.setAttribute(
"channels", par->ch_layout.nb_channels);
2115 AVDictionaryEntry *metatag =
2116 av_dict_get(st->metadata,
"language",
nullptr, 0);
2118 stream.setAttribute(
"language", metatag->value);
2120 stream.setAttribute(
"language",
"N/A");
2122 stream.setAttribute(
"id", st->id);
2124 stream.setAttribute(
"samplerate", par->sample_rate);
2125 stream.setAttribute(
"bitrate", (qlonglong)par->bit_rate);
2127 if (st->start_time != (
int) AV_NOPTS_VALUE)
2129 int secs = st->start_time / AV_TIME_BASE;
2130 int us = st->start_time % AV_TIME_BASE;
2131 stream.setAttribute(
"start_time", QString(
"%1.%2")
2132 .arg(secs).arg(av_rescale(us, 1000000, AV_TIME_BASE)));
2135 stream.setAttribute(
"start_time", 0);
2137 streams.appendChild(stream);
2142 case AVMEDIA_TYPE_SUBTITLE:
2144 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
2145 QStringList param = QString::fromStdString(buf).split(
',', QString::SkipEmptyParts);
2147 QStringList param = QString::fromStdString(buf).split(
',', Qt::SkipEmptyParts);
2149 QString codec = param[0].remove(
"Subtitle:", Qt::CaseInsensitive).remove(QChar::Null);
2151 QDomElement stream = doc.createElement(
"subtitle");
2152 stream.setAttribute(
"streamindex", i);
2153 stream.setAttribute(
"ffmpegindex", ffmpegIndex++);
2154 stream.setAttribute(
"codec", codec.trimmed());
2156 AVDictionaryEntry *metatag =
2157 av_dict_get(st->metadata,
"language",
nullptr, 0);
2159 stream.setAttribute(
"language", metatag->value);
2161 stream.setAttribute(
"language",
"N/A");
2163 stream.setAttribute(
"id", st->id);
2165 streams.appendChild(stream);
2170 case AVMEDIA_TYPE_DATA:
2172 QDomElement stream = doc.createElement(
"data");
2173 stream.setAttribute(
"streamindex", i);
2174 stream.setAttribute(
"codec", QString::fromStdString(buf).remove(QChar::Null));
2175 streams.appendChild(stream);
2181 LOG(VB_JOBQUEUE, LOG_ERR,
2182 QString(
"Skipping unsupported codec %1 on stream %2")
2183 .arg(inputFC->streams[i]->codecpar->codec_type).arg(i));
2191 if (!f.open(QIODevice::WriteOnly))
2193 LOG(VB_JOBQUEUE, LOG_ERR,
2194 "Failed to open file for writing - " + outFile);
2199 t << doc.toString(4);
2211 if (!f.open(QIODevice::WriteOnly))
2213 LOG(VB_GENERAL, LOG_ERR,
2214 QString(
"MythArchiveHelper: Failed to open file for writing - %1")
2233 if (
filename.startsWith(
"myth://"))
2242 struct statfs statbuf {};
2244 ((!strcmp(statbuf.f_fstypename,
"nfs")) ||
2245 (!strcmp(statbuf.f_fstypename,
"afpfs")) ||
2246 (!strcmp(statbuf.f_fstypename,
"smbfs"))))
2248 #elif defined(__linux__)
2249 struct statfs statbuf {};
2251 ((statbuf.f_type == 0x6969) ||
2252 (statbuf.f_type == 0x517B)))
2276 add(QStringList{
"-t",
"--createthumbnail"},
2277 "createthumbnail",
false,
2278 "Create one or more thumbnails\n"
2279 "Requires: --infile, --thumblist, --outfile\n"
2280 "Optional: --framecount",
"");
2281 add(
"--infile",
"infile",
"",
2283 "Used with: --createthumbnail, --getfileinfo, --isremote, "
2284 "--sup2dast, --importarchive",
"");
2285 add(
"--outfile",
"outfile",
"",
2286 "Output file name\n"
2287 "Used with: --createthumbnail, --getfileinfo, --getdbparameters, "
2289 "When used with --createthumbnail: eg 'thumb%1-%2.jpg'\n"
2290 " %1 will be replaced with the no. of the thumb\n"
2291 " %2 will be replaced with the frame no.",
"");
2292 add(
"--thumblist",
"thumblist",
"",
2293 "Comma-separated list of required thumbs (in seconds)\n"
2294 "Used with: --createthumbnail",
"");
2295 add(
"--framecount",
"framecount", 1,
2296 "Number of frames to grab (default 1)\n"
2297 "Used with: --createthumbnail",
"");
2299 add(QStringList{
"-i",
"--getfileinfo"},
2300 "getfileinfo",
false,
2301 "Write file info about infile to outfile\n"
2302 "Requires: --infile, --outfile, --method",
"");
2303 add(
"--method",
"method", 0,
2304 "Method of file duration calculation\n"
2305 "Used with: --getfileinfo\n"
2306 " 0 = use av_estimate_timings() (quick but not very accurate - "
2308 " 1 = read all frames (most accurate but slow)\n"
2309 " 2 = use position map in DB (quick, only works for MythTV "
2312 add(QStringList{
"-p",
"--getdbparameters"},
2313 "getdbparameters",
false,
2314 "Write the mysql database parameters to outfile\n"
2315 "Requires: --outfile",
"");
2317 add(QStringList{
"-n",
"--nativearchive"},
2318 "nativearchive",
false,
2319 "Archive files to a native archive format\n"
2320 "Requires: --outfile",
"");
2322 add(QStringList{
"-f",
"--importarchive"},
2323 "importarchive",
false,
2324 "Import an archived file\n"
2325 "Requires: --infile, --chanid",
"");
2326 add(
"--chanid",
"chanid", -1,
2327 "Channel ID to use when inserting records in DB\n"
2328 "Used with: --importarchive",
"");
2330 add(QStringList{
"-r",
"--isremote"},
2332 "Check if infile is on a remote filesystem\n"
2333 "Requires: --infile\n"
2334 "Returns: 0 on error or file not found\n"
2335 " - 1 file is on a local filesystem\n"
2336 " - 2 file is on a remote filesystem",
"");
2338 add(QStringList{
"-b",
"--burndvd"},
2340 "Burn a created DVD to a blank disc\n"
2341 "Optional: --mediatype, --erasedvdrw, --nativeformat",
"");
2342 add(
"--mediatype",
"mediatype", 0,
2343 "Type of media to burn\n"
2344 "Used with: --burndvd\n"
2345 " 0 = single layer DVD (default)\n"
2346 " 1 = dual layer DVD\n"
2347 " 2 = rewritable DVD",
"");
2348 add(
"--erasedvdrw",
"erasedvdrw",
false,
2349 "Force an erase of DVD-R/W Media\n"
2350 "Used with: --burndvd (optional)",
"");
2351 add(
"--nativeformat",
"nativeformat",
false,
2352 "Archive is a native archive format\n"
2353 "Used with: --burndvd (optional)",
"");
2355 add(QStringList{
"-s",
"--sup2dast"},
2357 "Convert projectX subtitles to DVD subtitles\n"
2358 "Requires: --infile, --ifofile, --delay",
"");
2359 add(
"--ifofile",
"ifofile",
"",
2360 "Filename of ifo file\n"
2361 "Used with: --sup2dast",
"");
2362 add(
"--delay",
"delay", 0,
2363 "Delay in ms to add to subtitles (default 0)\n"
2364 "Used with: --sup2dast",
"");
2390 QCoreApplication a(argc, argv);
2391 QCoreApplication::setApplicationName(
"mytharchivehelper");
2395 QString mask(
"jobqueue");
2406 LOG(VB_GENERAL, LOG_ERR,
"Failed to init MythContext, exiting.");
2438 if (inFile.isEmpty())
2440 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile in -t/--grabthumbnail "
2445 if (thumbList.isEmpty())
2447 LOG(VB_GENERAL, LOG_ERR,
"Missing --thumblist in -t/--grabthumbnail"
2452 if (outFile.isEmpty())
2454 LOG(VB_GENERAL, LOG_ERR,
"Missing --outfile in -t/--grabthumbnail "
2460 if (bGetDBParameters)
2462 if (outFile.isEmpty())
2464 LOG(VB_GENERAL, LOG_ERR,
"Missing argument to -p/--getdbparameters "
2472 if (inFile.isEmpty())
2474 LOG(VB_GENERAL, LOG_ERR,
2475 "Missing argument to -r/--isremote option");
2482 if (mediaType < 0 || mediaType > 2)
2484 LOG(VB_GENERAL, LOG_ERR, QString(
"Invalid mediatype given: %1")
2492 if (outFile.isEmpty())
2494 LOG(VB_GENERAL, LOG_ERR,
"Missing argument to -n/--nativearchive "
2502 if (inFile.isEmpty())
2504 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile argument to "
2505 "-f/--importarchive option");
2512 if (inFile.isEmpty())
2514 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile in -i/--getfileinfo "
2519 if (outFile.isEmpty())
2521 LOG(VB_GENERAL, LOG_ERR,
"Missing --outfile in -i/--getfileinfo "
2529 if (inFile.isEmpty())
2531 LOG(VB_GENERAL, LOG_ERR,
2532 "Missing --infile in -s/--sup2dast option");
2536 if (ifoFile.isEmpty())
2538 LOG(VB_GENERAL, LOG_ERR,
2539 "Missing --ifofile in -s/--sup2dast option");
2545 res =
grabThumbnail(inFile, thumbList, outFile, frameCount);
2546 else if (bGetDBParameters)
2548 else if (bNativeArchive)
2550 else if (bImportArchive)
2552 else if (bGetFileInfo)
2557 res =
doBurnDVD(mediaType, bEraseDVDRW, bNativeFormat);
2560 QByteArray inFileBA = inFile.toLocal8Bit();
2561 QByteArray ifoFileBA = ifoFile.toLocal8Bit();
2562 res =
sup2dast(inFileBA.constData(), ifoFileBA.constData(), delay);