33#include <QApplication>
39#include <QMutexLocker>
40#include <QRegularExpression>
44#include <libmythbase/mythconfig.h>
58#include <libmythbase/mythversion.h>
63 #include <libavcodec/avcodec.h>
64 #include <libavformat/avformat.h>
65 #include <libavutil/imgutils.h>
70#include "../mytharchive/archiveutil.h"
71#include "../mytharchive/remoteavformatcontext.h"
81 static bool copyFile(
const QString &source,
const QString &destination);
83 const QString &xmlFile,
int chanID);
84 static int importVideo(
const QDomElement &itemNode,
const QString &xmlFile);
85 static int exportRecording(QDomElement &itemNode,
const QString &saveDirectory);
86 static int exportVideo(QDomElement &itemNode,
const QString &saveDirectory);
88 static QString
findNodeText(
const QDomElement &elem,
const QString &nodeName);
89 static int getFieldList(QStringList &fieldList,
const QString &tableName);
96 QFile
file(tempDir +
"/logs/mythburn.lck");
98 if (!
file.open(QIODevice::WriteOnly | QIODevice::Truncate))
99 LOG(VB_GENERAL, LOG_ERR,
"NativeArchive: Failed to create lock file");
101 QString pid = QString(
"%1").arg(getpid());
102 file.write(pid.toLatin1());
111 QFile::remove(tempDir +
"/logs/mythburn.lck");
116 QString command = QString(
"mythutil --copyfile --infile '%1' --outfile '%2'")
117 .arg(source, destination);
121 LOG(VB_JOBQUEUE, LOG_ERR,
122 QString(
"Failed while running %1. Result: %2").arg(command).arg(res));
131 LOG(VB_JOBQUEUE, LOG_INFO,
"Creating ISO image");
135 tempDirectory +=
"work/";
138 QString command = mkisofs +
" -R -J -V 'MythTV Archive' -o ";
139 command += tempDirectory +
"mythburn.iso " + sourceDirectory;
144 LOG(VB_JOBQUEUE, LOG_ERR,
145 QString(
"Failed while running mkisofs. Result: %1") .arg(res));
149 LOG(VB_JOBQUEUE, LOG_INFO,
"Finished creating ISO image");
153static int burnISOImage(
int mediaType,
bool bEraseDVDRW,
bool nativeFormat)
157 LOG(VB_JOBQUEUE, LOG_INFO,
"Burning ISO image to " + dvdDrive);
162 tempDirectory +=
"work/";
168 command +=
" -speed=" + QString::number(driveSpeed);
172 if (mediaType ==
AD_DVD_RW && bEraseDVDRW)
174 command +=
" -use-the-force-luke -Z " + dvdDrive;
175 command +=
" -V 'MythTV Archive' -R -J " + tempDirectory;
179 command +=
" -Z " + dvdDrive;
180 command +=
" -V 'MythTV Archive' -R -J " + tempDirectory;
185 if (mediaType ==
AD_DVD_RW && bEraseDVDRW)
187 command +=
" -dvd-compat -use-the-force-luke -Z " + dvdDrive;
188 command +=
" -dvd-video -V 'MythTV DVD' " + tempDirectory +
"/dvd";
192 command +=
" -dvd-compat -Z " + dvdDrive;
193 command +=
" -dvd-video -V 'MythTV DVD' " + tempDirectory +
"/dvd";
200 LOG(VB_JOBQUEUE, LOG_ERR,
201 QString(
"Failed while running growisofs. Result: %1") .arg(res));
205 LOG(VB_JOBQUEUE, LOG_INFO,
"Finished burning ISO image");
211static int doBurnDVD(
int mediaType,
bool bEraseDVDRW,
bool nativeFormat)
214 "MythArchiveLastRunStart",
218 int res =
burnISOImage(mediaType, bEraseDVDRW, nativeFormat);
221 "MythArchiveLastRunEnd",
231 QDomDocument doc(
"archivejob");
233 if (!
file.open(QIODevice::ReadOnly))
235 LOG(VB_JOBQUEUE, LOG_ERR,
"Could not open job file: " + jobFile);
239 if (!doc.setContent(&
file))
241 LOG(VB_JOBQUEUE, LOG_ERR,
"Could not load job file: " + jobFile);
249 bool bCreateISO =
false;
250 bool bEraseDVDRW =
false;
251 bool bDoBurn =
false;
252 QString saveDirectory;
255 QDomNodeList nodeList = doc.elementsByTagName(
"options");
256 if (nodeList.count() == 1)
258 QDomNode node = nodeList.item(0);
259 QDomElement
options = node.toElement();
262 bCreateISO = (
options.attribute(
"createiso",
"0") ==
"1");
263 bEraseDVDRW = (
options.attribute(
"erasedvdrw",
"0") ==
"1");
264 bDoBurn = (
options.attribute(
"doburn",
"0") ==
"1");
265 mediaType =
options.attribute(
"mediatype",
"0").toInt();
266 saveDirectory =
options.attribute(
"savedirectory",
"");
267 if (!saveDirectory.endsWith(
"/"))
268 saveDirectory +=
"/";
273 LOG(VB_JOBQUEUE, LOG_ERR,
274 QString(
"Found %1 options nodes - should be 1")
275 .arg(nodeList.count()));
278 LOG(VB_JOBQUEUE, LOG_INFO,
279 QString(
"Options - createiso: %1,"
280 " doburn: %2, mediatype: %3, erasedvdrw: %4")
281 .arg(bCreateISO).arg(bDoBurn).arg(mediaType).arg(bEraseDVDRW));
282 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"savedirectory: %1").arg(saveDirectory));
287 saveDirectory = tempDir;
288 if (!saveDirectory.endsWith(
"/"))
289 saveDirectory +=
"/";
291 saveDirectory +=
"work/";
293 QDir dir(saveDirectory);
297 LOG(VB_GENERAL, LOG_ERR,
298 "NativeArchive: Failed to clear work directory");
300 dir.mkpath(saveDirectory);
303 LOG(VB_JOBQUEUE, LOG_INFO,
304 QString(
"Saving files to : %1").arg(saveDirectory));
307 nodeList = doc.elementsByTagName(
"file");
308 if (nodeList.count() < 1)
310 LOG(VB_JOBQUEUE, LOG_ERR,
"Cannot find any file nodes?");
319 for (
int x = 0; x < nodeList.count(); x++)
321 node = nodeList.item(x);
322 elem = node.toElement();
325 type = elem.attribute(
"type");
327 if (
type.toLower() ==
"recording") {
329 }
else if (
type.toLower() ==
"video") {
332 LOG(VB_JOBQUEUE, LOG_ERR,
333 QString(
"Don't know how to archive items of type '%1'")
334 .arg(
type.toLower()));
341 if (mediaType !=
AD_FILE && bDoBurn)
345 LOG(VB_JOBQUEUE, LOG_ERR,
346 "Native archive job failed to complete");
356 LOG(VB_JOBQUEUE, LOG_ERR,
"Native archive job failed to complete");
361 LOG(VB_JOBQUEUE, LOG_INFO,
"Native archive job completed OK");
366static const QRegularExpression
badChars { R
"((/|\\|:|'|"|\?|\|))" };
380 if (query.
exec(
"DESCRIBE " + tableName))
384 fieldList.append(query.
value(0).toString());
392 return fieldList.count();
396 const QString &saveDirectory)
402 QString title =
fixFilename(itemNode.attribute(
"title"));
403 QString
filename = itemNode.attribute(
"filename");
404 bool doDelete = (itemNode.attribute(
"delete",
"0") ==
"0");
405 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"Archiving %1 (%2), do delete: %3")
406 .arg(title,
filename, doDelete ?
"true" :
"false"));
410 LOG(VB_JOBQUEUE, LOG_ERR,
"Bad title or filename");
416 LOG(VB_JOBQUEUE, LOG_ERR,
417 QString(
"Failed to extract chanID and startTime from '%1'")
423 QDir dir(saveDirectory + title);
425 dir.mkpath(saveDirectory + title);
427 LOG(VB_GENERAL, LOG_ERR,
"Failed to create savedir: " +
ENO);
429 LOG(VB_JOBQUEUE, LOG_INFO,
"Creating xml file for " + title);
430 QDomDocument doc(
"MYTHARCHIVEITEM");
432 QDomElement root = doc.createElement(
"item");
433 doc.appendChild(root);
434 root.setAttribute(
"type",
"recording");
435 root.setAttribute(
"databaseversion",
dbVersion);
437 QDomElement recorded = doc.createElement(
"recorded");
438 root.appendChild(recorded);
441 QStringList fieldList;
445 query.
prepare(
"SELECT " + fieldList.join(
",")
447 " WHERE chanid = :CHANID and starttime = :STARTTIME;");
449 query.
bindValue(
":STARTTIME", startTime);
456 for (
int x = 0; x < fieldList.size(); x++)
458 elem = doc.createElement(fieldList[x]);
459 text = doc.createTextNode(query.
value(x).toString());
460 elem.appendChild(text);
461 recorded.appendChild(elem);
464 LOG(VB_JOBQUEUE, LOG_INFO,
"Created recorded element for " + title);
468 LOG(VB_JOBQUEUE, LOG_INFO,
"Failed to get recorded field list");
472 query.
prepare(
"SELECT chanid, channum, callsign, name "
473 "FROM channel WHERE chanid = :CHANID;");
478 QDomElement channel = doc.createElement(
"channel");
479 channel.setAttribute(
"chanid", query.
value(0).toString());
480 channel.setAttribute(
"channum", query.
value(1).toString());
481 channel.setAttribute(
"callsign", query.
value(2).toString());
482 channel.setAttribute(
"name", query.
value(3).toString());
483 root.appendChild(channel);
484 LOG(VB_JOBQUEUE, LOG_INFO,
"Created channel element for " + title);
489 LOG(VB_JOBQUEUE, LOG_ERR,
490 "Cannot find channel details for chanid " + chanID);
491 QDomElement channel = doc.createElement(
"channel");
492 channel.setAttribute(
"chanid", chanID);
493 channel.setAttribute(
"channum",
"unknown");
494 channel.setAttribute(
"callsign",
"unknown");
495 channel.setAttribute(
"name",
"unknown");
496 root.appendChild(channel);
497 LOG(VB_JOBQUEUE, LOG_INFO,
498 "Created a default channel element for " + title);
502 query.
prepare(
"SELECT credits.person, role, people.name "
503 "FROM recordedcredits AS credits "
504 "LEFT JOIN people ON credits.person = people.person "
505 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
507 query.
bindValue(
":STARTTIME", startTime);
511 QDomElement credits = doc.createElement(
"credits");
514 QDomElement credit = doc.createElement(
"credit");
515 credit.setAttribute(
"personid", query.
value(0).toString());
516 credit.setAttribute(
"name", query.
value(2).toString());
517 credit.setAttribute(
"role", query.
value(1).toString());
518 credits.appendChild(credit);
520 root.appendChild(credits);
521 LOG(VB_JOBQUEUE, LOG_INFO,
"Created credits element for " + title);
525 query.
prepare(
"SELECT `system`, rating FROM recordedrating "
526 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
528 query.
bindValue(
":STARTTIME", startTime);
532 QDomElement
rating = doc.createElement(
"rating");
533 rating.setAttribute(
"system", query.
value(0).toString());
534 rating.setAttribute(
"rating", query.
value(1).toString());
536 LOG(VB_JOBQUEUE, LOG_INFO,
"Created rating element for " + title);
540 QDomElement recordedmarkup = doc.createElement(
"recordedmarkup");
541 query.
prepare(
"SELECT chanid, starttime, mark, type, data "
542 "FROM recordedmarkup "
543 "WHERE chanid = :CHANID and starttime = :STARTTIME;");
545 query.
bindValue(
":STARTTIME", startTime);
550 QDomElement mark = doc.createElement(
"mark");
551 mark.setAttribute(
"mark", query.
value(2).toString());
552 mark.setAttribute(
"type", query.
value(3).toString());
553 mark.setAttribute(
"data", query.
value(4).toString());
554 recordedmarkup.appendChild(mark);
556 root.appendChild(recordedmarkup);
557 LOG(VB_JOBQUEUE, LOG_INFO,
"Created recordedmarkup element for " + title);
561 QDomElement recordedseek = doc.createElement(
"recordedseek");
562 query.
prepare(
"SELECT chanid, starttime, mark, `offset`, type "
564 "WHERE chanid = :CHANID and starttime = :STARTTIME;");
566 query.
bindValue(
":STARTTIME", startTime);
571 QDomElement mark = doc.createElement(
"mark");
572 mark.setAttribute(
"mark", query.
value(2).toString());
573 mark.setAttribute(
"offset", query.
value(3).toString());
574 mark.setAttribute(
"type", query.
value(4).toString());
575 recordedseek.appendChild(mark);
577 root.appendChild(recordedseek);
578 LOG(VB_JOBQUEUE, LOG_INFO,
579 "Created recordedseek element for " + title);
584 QString xmlFile = saveDirectory + title +
"/" + baseName +
".xml";
586 if (!f.open(QIODevice::WriteOnly))
588 LOG(VB_JOBQUEUE, LOG_ERR,
589 "MythNativeWizard: Failed to open file for writing - " + xmlFile);
594 t << doc.toString(4);
598 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
606 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying preview image");
608 + title +
"/" + baseName +
".png");
613 LOG(VB_JOBQUEUE, LOG_INFO,
"Item Archived OK");
619 const QString &saveDirectory)
624 QString coverFile =
"";
626 QString title =
fixFilename(itemNode.attribute(
"title"));
627 QString
filename = itemNode.attribute(
"filename");
628 bool doDelete = (itemNode.attribute(
"delete",
"0") ==
"0");
629 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"Archiving %1 (%2), do delete: %3")
630 .arg(title,
filename, doDelete ?
"true" :
"false"));
634 LOG(VB_JOBQUEUE, LOG_ERR,
"Bad title or filename");
639 QDir dir(saveDirectory + title);
641 dir.mkdir(saveDirectory + title);
643 LOG(VB_JOBQUEUE, LOG_INFO,
"Creating xml file for " + title);
644 QDomDocument doc(
"MYTHARCHIVEITEM");
646 QDomElement root = doc.createElement(
"item");
647 doc.appendChild(root);
648 root.setAttribute(
"type",
"video");
649 root.setAttribute(
"databaseversion",
dbVersion);
651 QDomElement video = doc.createElement(
"videometadata");
652 root.appendChild(video);
656 query.
prepare(
"SELECT intid, title, director, plot, rating, inetref, "
657 "year, userrating, length, showlevel, filename, coverfile, "
658 "childid, browse, playcommand, category "
659 "FROM videometadata WHERE filename = :FILENAME;");
667 elem = doc.createElement(
"intid");
668 text = doc.createTextNode(query.
value(0).toString());
669 intID = query.
value(0).toInt();
670 elem.appendChild(text);
671 video.appendChild(elem);
673 elem = doc.createElement(
"title");
674 text = doc.createTextNode(query.
value(1).toString());
675 elem.appendChild(text);
676 video.appendChild(elem);
678 elem = doc.createElement(
"director");
679 text = doc.createTextNode(query.
value(2).toString());
680 elem.appendChild(text);
681 video.appendChild(elem);
683 elem = doc.createElement(
"plot");
684 text = doc.createTextNode(query.
value(3).toString());
685 elem.appendChild(text);
686 video.appendChild(elem);
688 elem = doc.createElement(
"rating");
689 text = doc.createTextNode(query.
value(4).toString());
690 elem.appendChild(text);
691 video.appendChild(elem);
693 elem = doc.createElement(
"inetref");
694 text = doc.createTextNode(query.
value(5).toString());
695 elem.appendChild(text);
696 video.appendChild(elem);
698 elem = doc.createElement(
"year");
699 text = doc.createTextNode(query.
value(6).toString());
700 elem.appendChild(text);
701 video.appendChild(elem);
703 elem = doc.createElement(
"userrating");
704 text = doc.createTextNode(query.
value(7).toString());
705 elem.appendChild(text);
706 video.appendChild(elem);
708 elem = doc.createElement(
"length");
709 text = doc.createTextNode(query.
value(8).toString());
710 elem.appendChild(text);
711 video.appendChild(elem);
713 elem = doc.createElement(
"showlevel");
714 text = doc.createTextNode(query.
value(9).toString());
715 elem.appendChild(text);
716 video.appendChild(elem);
719 QString fname = query.
value(10).toString();
723 elem = doc.createElement(
"filename");
724 text = doc.createTextNode(fname);
725 elem.appendChild(text);
726 video.appendChild(elem);
728 elem = doc.createElement(
"coverfile");
729 text = doc.createTextNode(query.
value(11).toString());
730 coverFile = query.
value(11).toString();
731 elem.appendChild(text);
732 video.appendChild(elem);
734 elem = doc.createElement(
"childid");
735 text = doc.createTextNode(query.
value(12).toString());
736 elem.appendChild(text);
737 video.appendChild(elem);
739 elem = doc.createElement(
"browse");
740 text = doc.createTextNode(query.
value(13).toString());
741 elem.appendChild(text);
742 video.appendChild(elem);
744 elem = doc.createElement(
"playcommand");
745 text = doc.createTextNode(query.
value(14).toString());
746 elem.appendChild(text);
747 video.appendChild(elem);
749 elem = doc.createElement(
"categoryid");
750 text = doc.createTextNode(query.
value(15).toString());
751 categoryID = query.
value(15).toInt();
752 elem.appendChild(text);
753 video.appendChild(elem);
755 LOG(VB_JOBQUEUE, LOG_INFO,
756 "Created videometadata element for " + title);
760 query.
prepare(
"SELECT intid, category "
761 "FROM videocategory WHERE intid = :INTID;");
766 QDomElement category = doc.createElement(
"category");
767 category.setAttribute(
"intid", query.
value(0).toString());
768 category.setAttribute(
"category", query.
value(1).toString());
769 root.appendChild(category);
770 LOG(VB_JOBQUEUE, LOG_INFO,
771 "Created videocategory element for " + title);
775 QDomElement countries = doc.createElement(
"countries");
776 root.appendChild(countries);
778 query.
prepare(
"SELECT intid, country "
779 "FROM videometadatacountry INNER JOIN videocountry "
780 "ON videometadatacountry.idcountry = videocountry.intid "
781 "WHERE idvideo = :INTID;");
791 QDomElement country = doc.createElement(
"country");
792 country.setAttribute(
"intid", query.
value(0).toString());
793 country.setAttribute(
"country", query.
value(1).toString());
794 countries.appendChild(country);
796 LOG(VB_JOBQUEUE, LOG_INFO,
"Created videocountry element for " + title);
800 QDomElement genres = doc.createElement(
"genres");
801 root.appendChild(genres);
803 query.
prepare(
"SELECT intid, genre "
804 "FROM videometadatagenre INNER JOIN videogenre "
805 "ON videometadatagenre.idgenre = videogenre.intid "
806 "WHERE idvideo = :INTID;");
816 QDomElement genre = doc.createElement(
"genre");
817 genre.setAttribute(
"intid", query.
value(0).toString());
818 genre.setAttribute(
"genre", query.
value(1).toString());
819 genres.appendChild(genre);
821 LOG(VB_JOBQUEUE, LOG_INFO,
"Created videogenre element for " + title);
826 QString xmlFile = saveDirectory + title +
"/"
827 + fileInfo.fileName() +
".xml";
829 if (!f.open(QIODevice::WriteOnly))
831 LOG(VB_JOBQUEUE, LOG_INFO,
832 "MythNativeWizard: Failed to open file for writing - " + xmlFile);
837 t << doc.toString(4);
841 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
843 +
"/" + fileInfo.fileName());
850 fileInfo.setFile(coverFile);
851 if (fileInfo.exists())
853 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying cover file");
854 res =
copyFile(coverFile, saveDirectory + title
855 +
"/" + fileInfo.fileName());
862 LOG(VB_JOBQUEUE, LOG_INFO,
"Item Archived OK");
870 QDomDocument doc(
"mydocument");
872 if (!
file.open(QIODevice::ReadOnly))
874 LOG(VB_JOBQUEUE, LOG_ERR,
875 "Failed to open file for reading - " + xmlFile);
879 if (!doc.setContent(&
file))
882 LOG(VB_JOBQUEUE, LOG_ERR,
883 "Failed to read from xml file - " + xmlFile);
888 QString docType = doc.doctype().name();
891 QDomNodeList itemNodeList;
893 QDomElement itemNode;
895 if (docType ==
"MYTHARCHIVEITEM")
897 itemNodeList = doc.elementsByTagName(
"item");
899 if (itemNodeList.count() < 1)
901 LOG(VB_JOBQUEUE, LOG_ERR,
902 "Couldn't find an 'item' element in XML file");
906 node = itemNodeList.item(0);
907 itemNode = node.toElement();
908 type = itemNode.attribute(
"type");
909 dbVersion = itemNode.attribute(
"databaseversion");
911 LOG(VB_JOBQUEUE, LOG_INFO,
912 QString(
"Archive DB version: %1, Local DB version: %2")
917 LOG(VB_JOBQUEUE, LOG_ERR,
"Not a native archive xml file - " + xmlFile);
921 if (
type ==
"recording")
934 const QString &xmlFile,
int chanID)
936 LOG(VB_JOBQUEUE, LOG_INFO,
937 QString(
"Import recording using chanID: %1").arg(chanID));
938 LOG(VB_JOBQUEUE, LOG_INFO,
939 QString(
"Archived recording xml file: %1").arg(xmlFile));
941 QString videoFile = xmlFile.left(xmlFile.length() - 4);
942 QString basename = videoFile;
943 int pos = videoFile.lastIndexOf(
'/');
945 basename = videoFile.mid(pos + 1);
947 QDomNodeList nodeList = itemNode.elementsByTagName(
"recorded");
948 if (nodeList.count() < 1)
950 LOG(VB_JOBQUEUE, LOG_ERR,
951 "Couldn't find a 'recorded' element in XML file");
955 QDomNode n = nodeList.item(0);
956 QDomElement recordedNode = n.toElement();
957 QString startTime =
findNodeText(recordedNode,
"starttime");
960 query.
prepare(
"SELECT * FROM recorded "
961 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
963 query.
bindValue(
":STARTTIME", startTime);
968 LOG(VB_JOBQUEUE, LOG_ERR,
969 "This recording appears to already exist!!");
976 basename ,
"Default");
979 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file to: " + destFile);
986 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying preview image file to: " + destFile +
".png");
987 if (!
copyFile(videoFile +
".png", destFile +
".png"))
992 QStringList fieldList;
993 QStringList bindList;
994 QDomNodeList nodes = recordedNode.childNodes();
996 for (
int x = 0; x < nodes.count(); x++)
998 QDomNode n2 = nodes.item(x);
999 QString field = n2.nodeName();
1000 fieldList.append(field);
1001 bindList.append(
":" + field.toUpper());
1005 query.
prepare(
"INSERT INTO recorded (" + fieldList.join(
",") +
") "
1006 "VALUES (" + bindList.join(
",") +
");");
1008 query.
bindValue(
":STARTTIME", startTime);
1010 for (
int x = 0; x < fieldList.count(); x++)
1014 LOG(VB_JOBQUEUE, LOG_INFO,
"Inserted recorded details into database");
1019 nodeList = itemNode.elementsByTagName(
"recordedmarkup");
1020 if (nodeList.count() < 1)
1022 LOG(VB_JOBQUEUE, LOG_WARNING,
1023 "Couldn't find a 'recordedmarkup' element in XML file");
1027 QDomNode n3 = nodeList.item(0);
1028 QDomElement markupNode = n3.toElement();
1030 nodeList = markupNode.elementsByTagName(
"mark");
1031 if (nodeList.count() < 1)
1033 LOG(VB_JOBQUEUE, LOG_WARNING,
1034 "Couldn't find any 'mark' elements in XML file");
1039 query.
prepare(
"DELETE FROM recordedmarkup "
1040 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
1042 query.
bindValue(
":STARTTIME", startTime);
1048 for (
int x = 0; x < nodeList.count(); x++)
1050 QDomNode n4 = nodeList.item(x);
1051 QDomElement e = n4.toElement();
1052 query.
prepare(
"INSERT INTO recordedmarkup (chanid, starttime, "
1054 "VALUES(:CHANID,:STARTTIME,:MARK,:TYPE,:DATA);");
1056 query.
bindValue(
":STARTTIME", startTime);
1057 query.
bindValue(
":MARK", e.attribute(
"mark"));
1058 query.
bindValue(
":TYPE", e.attribute(
"type"));
1059 query.
bindValue(
":DATA", e.attribute(
"data"));
1068 LOG(VB_JOBQUEUE, LOG_INFO,
1069 "Inserted recordedmarkup details into database");
1074 nodeList = itemNode.elementsByTagName(
"recordedseek");
1075 if (nodeList.count() < 1)
1077 LOG(VB_JOBQUEUE, LOG_WARNING,
1078 "Couldn't find a 'recordedseek' element in XML file");
1082 QDomNode n5 = nodeList.item(0);
1083 QDomElement markupNode = n5.toElement();
1085 nodeList = markupNode.elementsByTagName(
"mark");
1086 if (nodeList.count() < 1)
1088 LOG(VB_JOBQUEUE, LOG_WARNING,
1089 "Couldn't find any 'mark' elements in XML file");
1094 query.
prepare(
"DELETE FROM recordedseek "
1095 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
1097 query.
bindValue(
":STARTTIME", startTime);
1101 for (
int x = 0; x < nodeList.count(); x++)
1103 QDomNode n6 = nodeList.item(x);
1104 QDomElement e = n6.toElement();
1105 query.
prepare(
"INSERT INTO recordedseek (chanid, starttime, "
1106 "mark, `offset`, type)"
1107 "VALUES(:CHANID,:STARTTIME,:MARK,:OFFSET,:TYPE);");
1109 query.
bindValue(
":STARTTIME", startTime);
1110 query.
bindValue(
":MARK", e.attribute(
"mark"));
1111 query.
bindValue(
":OFFSET", e.attribute(
"offset"));
1112 query.
bindValue(
":TYPE", e.attribute(
"type"));
1121 LOG(VB_JOBQUEUE, LOG_INFO,
1122 "Inserted recordedseek details into database");
1130 LOG(VB_JOBQUEUE, LOG_INFO,
"Import completed OK");
1137 LOG(VB_JOBQUEUE, LOG_INFO,
"Importing video");
1138 LOG(VB_JOBQUEUE, LOG_INFO,
1139 QString(
"Archived video xml file: %1").arg(xmlFile));
1141 QString videoFile = xmlFile.left(xmlFile.length() - 4);
1142 QFileInfo fileInfo(videoFile);
1143 QString basename = fileInfo.fileName();
1145 QDomNodeList nodeList = itemNode.elementsByTagName(
"videometadata");
1146 if (nodeList.count() < 1)
1148 LOG(VB_JOBQUEUE, LOG_ERR,
1149 "Couldn't find a 'videometadata' element in XML file");
1153 QDomNode n = nodeList.item(0);
1154 QDomElement videoNode = n.toElement();
1158 QString origFilename =
findNodeText(videoNode,
"filename");
1159 QStringList dirList = origFilename.split(
"/", Qt::SkipEmptyParts);
1161 for (
int x = 0; x < dirList.count() - 1; x++)
1163 path +=
"/" + dirList[x];
1164 if (!dir.exists(path))
1166 if (!dir.mkdir(path))
1168 LOG(VB_JOBQUEUE, LOG_ERR,
1169 QString(
"Couldn't create directory '%1'").arg(path));
1175 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
1176 if (!
copyFile(videoFile, path +
"/" + basename))
1184 fileInfo.setFile(videoFile);
1185 QString archivePath = fileInfo.absolutePath();
1187 QString coverFilename =
findNodeText(videoNode,
"coverfile");
1188 fileInfo.setFile(coverFilename);
1189 coverFilename = fileInfo.fileName();
1191 fileInfo.setFile(archivePath +
"/" + coverFilename);
1192 if (fileInfo.exists())
1194 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying cover file");
1196 if (!
copyFile(archivePath +
"/" + coverFilename, artworkDir +
"/" + coverFilename))
1203 coverFilename =
"No Cover";
1208 query.
prepare(
"INSERT INTO videometadata (title, director, plot, rating, inetref, "
1209 "year, userrating, length, showlevel, filename, coverfile, "
1210 "childid, browse, playcommand, category) "
1211 "VALUES(:TITLE,:DIRECTOR,:PLOT,:RATING,:INETREF,:YEAR,"
1212 ":USERRATING,:LENGTH,:SHOWLEVEL,:FILENAME,:COVERFILE,"
1213 ":CHILDID,:BROWSE,:PLAYCOMMAND,:CATEGORY);");
1223 query.
bindValue(
":FILENAME", path +
"/" + basename);
1224 query.
bindValue(
":COVERFILE", artworkDir +
"/" + coverFilename);
1232 LOG(VB_JOBQUEUE, LOG_INFO,
1233 "Inserted videometadata details into database");
1243 query.
prepare(
"SELECT intid FROM videometadata WHERE filename = :FILENAME;");
1244 query.
bindValue(
":FILENAME", path +
"/" + basename);
1247 intid = query.
value(0).toInt();
1255 LOG(VB_JOBQUEUE, LOG_INFO,
1256 QString(
"'intid' of inserted video is: %1").arg(intid));
1259 nodeList = itemNode.elementsByTagName(
"genres");
1260 if (nodeList.count() < 1)
1262 LOG(VB_JOBQUEUE, LOG_ERR,
"No 'genres' element found in XML file");
1266 n = nodeList.item(0);
1267 QDomElement genresNode = n.toElement();
1269 nodeList = genresNode.elementsByTagName(
"genre");
1270 if (nodeList.count() < 1)
1272 LOG(VB_JOBQUEUE, LOG_WARNING,
1273 "Couldn't find any 'genre' elements in XML file");
1277 for (
int x = 0; x < nodeList.count(); x++)
1279 n = nodeList.item(x);
1280 QDomElement e = n.toElement();
1282 QString genre = e.attribute(
"genre");
1285 query.
prepare(
"SELECT intid FROM videogenre "
1286 "WHERE genre = :GENRE");
1290 genreID = query.
value(0).toInt();
1295 query.
prepare(
"INSERT INTO videogenre (genre) VALUES(:GENRE);");
1299 "insert videogenre", query);
1302 query.
prepare(
"SELECT intid FROM videogenre "
1303 "WHERE genre = :GENRE");
1305 if (!query.
exec() || !query.
next())
1307 LOG(VB_JOBQUEUE, LOG_ERR,
1308 "Couldn't add genre to database");
1311 genreID = query.
value(0).toInt();
1315 query.
prepare(
"INSERT INTO videometadatagenre (idvideo, idgenre)"
1316 "VALUES (:IDVIDEO, :IDGENRE);");
1321 "insert videometadatagenre", query);
1324 LOG(VB_JOBQUEUE, LOG_INFO,
"Inserted genre details into database");
1329 nodeList = itemNode.elementsByTagName(
"countries");
1330 if (nodeList.count() < 1)
1332 LOG(VB_JOBQUEUE, LOG_INFO,
"No 'countries' element found in XML file");
1336 n = nodeList.item(0);
1337 QDomElement countriesNode = n.toElement();
1339 nodeList = countriesNode.elementsByTagName(
"country");
1340 if (nodeList.count() < 1)
1342 LOG(VB_JOBQUEUE, LOG_WARNING,
1343 "Couldn't find any 'country' elements in XML file");
1347 for (
int x = 0; x < nodeList.count(); x++)
1349 n = nodeList.item(x);
1350 QDomElement e = n.toElement();
1352 QString country = e.attribute(
"country");
1355 query.
prepare(
"SELECT intid FROM videocountry "
1356 "WHERE country = :COUNTRY");
1360 countryID = query.
value(0).toInt();
1365 query.
prepare(
"INSERT INTO videocountry (country) VALUES(:COUNTRY);");
1369 "insert videocountry", query);
1372 query.
prepare(
"SELECT intid FROM videocountry "
1373 "WHERE country = :COUNTRY");
1375 if (!query.
exec() || !query.
next())
1377 LOG(VB_JOBQUEUE, LOG_ERR,
1378 "Couldn't add country to database");
1381 countryID = query.
value(0).toInt();
1385 query.
prepare(
"INSERT INTO videometadatacountry (idvideo, idcountry)"
1386 "VALUES (:IDVIDEO, :IDCOUNTRY);");
1388 query.
bindValue(
":IDCOUNTRY", countryID);
1391 "insert videometadatacountry", query);
1394 LOG(VB_JOBQUEUE, LOG_INFO,
1395 "Inserted country details into database");
1400 nodeList = itemNode.elementsByTagName(
"category");
1401 if (nodeList.count() < 1)
1403 LOG(VB_JOBQUEUE, LOG_ERR,
"No 'category' element found in XML file");
1407 n = nodeList.item(0);
1408 QDomElement e = n.toElement();
1410 QString category = e.attribute(
"category");
1412 query.
prepare(
"SELECT intid FROM videocategory "
1413 "WHERE category = :CATEGORY");
1417 categoryID = query.
value(0).toInt();
1422 query.
prepare(
"INSERT INTO videocategory (category) VALUES(:CATEGORY);");
1426 "insert videocategory", query);
1429 query.
prepare(
"SELECT intid FROM videocategory "
1430 "WHERE category = :CATEGORY");
1434 categoryID = query.
value(0).toInt();
1438 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't add category to database");
1444 query.
prepare(
"UPDATE videometadata "
1445 "SET category = :CATEGORY "
1446 "WHERE intid = :INTID;");
1447 query.
bindValue(
":CATEGORY", categoryID);
1451 "update category", query);
1453 LOG(VB_JOBQUEUE, LOG_INFO,
"Fixed the category in the database");
1456 LOG(VB_JOBQUEUE, LOG_INFO,
"Import completed OK");
1463 QDomNodeList nodeList = elem.elementsByTagName(nodeName);
1464 if (nodeList.count() < 1)
1466 LOG(VB_GENERAL, LOG_ERR,
1467 QString(
"Couldn't find a '%1' element in XML file") .arg(nodeName));
1471 QDomNode n = nodeList.item(0);
1472 QDomElement e = n.toElement();
1475 for (QDomNode node = e.firstChild(); !node.isNull();
1476 node = node.nextSibling())
1478 QDomText
t = node.toText();
1488 if ((nodeName ==
"recgroup") ||
1489 (nodeName ==
"playgroup"))
1493 else if ((nodeName ==
"recordid") ||
1494 (nodeName ==
"seriesid") ||
1495 (nodeName ==
"programid") ||
1496 (nodeName ==
"profile"))
1507 query.
prepare(
"DELETE FROM archiveitems;");
1517 "MythArchiveLastRunStart",
1523 "MythArchiveLastRunEnd",
1526 (res == 0 ?
"Success" :
"Failed"));
1540static int grabThumbnail(
const QString& inFile,
const QString& thumbList,
const QString& outFile,
int frameCount)
1543 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"grabThumbnail(): Opening '%1'")
1550 LOG(VB_JOBQUEUE, LOG_ERR,
"grabThumbnail(): Couldn't open input file" +
1556 int ret = avformat_find_stream_info(inputFC,
nullptr);
1559 LOG(VB_JOBQUEUE, LOG_ERR,
1560 QString(
"Couldn't get stream info, error #%1").arg(ret));
1565 int videostream = -1;
1570 for (
uint i = 0; i < inputFC->nb_streams; i++)
1572 AVStream *st = inputFC->streams[i];
1573 if (inputFC->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
1576 width = st->codecpar->width;
1577 height = st->codecpar->height;
1578 if (st->r_frame_rate.den && st->r_frame_rate.num)
1579 fps = av_q2d(st->r_frame_rate);
1581 fps = 1/av_q2d(st->time_base);
1586 if (videostream == -1)
1588 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't find a video stream");
1593 AVCodecContext *codecCtx = codecmap.
GetCodecContext(inputFC->streams[videostream]);
1596 const AVCodec * codec = avcodec_find_decoder(codecCtx->codec_id);
1598 if (codec ==
nullptr)
1600 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't find codec for video stream");
1605 if (avcodec_open2(codecCtx, codec,
nullptr) < 0)
1607 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't open codec for video stream");
1612 QStringList list = thumbList.split(
",", Qt::SkipEmptyParts);
1621 memset(&orig, 0,
sizeof(
AVFrame));
1622 memset(&retbuf, 0,
sizeof(
AVFrame));
1625 int bufflen = width * height * 4;
1626 auto *outputbuf =
new unsigned char[bufflen];
1630 bool frameFinished =
false;
1632 while (av_read_frame(inputFC, &pkt) >= 0)
1634 if (pkt.stream_index == videostream)
1637 if (list[thumbCount].toInt() == (
int)(frameNo / fps))
1641 avcodec_flush_buffers(codecCtx);
1642 av_frame_unref(frame);
1643 frameFinished =
false;
1644 ret = avcodec_receive_frame(codecCtx, frame);
1646 frameFinished =
true;
1647 if (ret == 0 || ret == AVERROR(EAGAIN))
1648 avcodec_send_packet(codecCtx, &pkt);
1649 bool keyFrame = (frame->flags & AV_FRAME_FLAG_KEY) != 0;
1651 while (!frameFinished || !keyFrame)
1653 av_packet_unref(&pkt);
1654 int res = av_read_frame(inputFC, &pkt);
1657 if (pkt.stream_index == videostream)
1660 av_frame_unref(frame);
1661 ret = avcodec_receive_frame(codecCtx, frame);
1663 frameFinished =
true;
1664 if (ret == 0 || ret == AVERROR(EAGAIN))
1665 avcodec_send_packet(codecCtx, &pkt);
1666 keyFrame = (frame->flags & AV_FRAME_FLAG_KEY) != 0;
1673 QString saveFormat =
"JPEG";
1674 if (outFile.right(4) ==
".png")
1678 while (count < frameCount)
1686 av_image_fill_arrays(retbuf.data, retbuf.linesize, outputbuf,
1687 AV_PIX_FMT_RGB32, width, height, IMAGE_ALIGN);
1692 copyframe.
Copy(&retbuf, AV_PIX_FMT_RGB32, tmp,
1693 codecCtx->pix_fmt, width, height);
1695 QImage img(outputbuf, width, height,
1696 QImage::Format_RGB32);
1698 if (!img.save(
filename, qPrintable(saveFormat)))
1700 LOG(VB_GENERAL, LOG_ERR,
1701 QString(
"grabThumbnail(): Failed to save "
1708 if (count <= frameCount)
1711 frameFinished =
false;
1712 while (!frameFinished)
1714 int res = av_read_frame(inputFC, &pkt);
1717 if (pkt.stream_index == videostream)
1720 ret = avcodec_receive_frame(codecCtx, frame);
1722 frameFinished =
true;
1723 if (ret == 0 || ret == AVERROR(EAGAIN))
1724 avcodec_send_packet(codecCtx, &pkt);
1731 if (thumbCount >= list.count())
1736 av_packet_unref(&pkt);
1751 LOG(VB_JOBQUEUE, LOG_INFO,
"Calculating frame count");
1753 AVPacket *pkt = av_packet_alloc();
1756 LOG(VB_GENERAL, LOG_ERR,
"packet allocation failed");
1759 while (av_read_frame(inputFC, pkt) >= 0)
1761 if (pkt->stream_index == vid_id)
1765 av_packet_unref(pkt);
1767 av_packet_free(&pkt);
1776 int pos =
filename.lastIndexOf(
'/');
1791 frm_dir_map_t::iterator it;
1792 uint64_t frames = 0;
1796 if (cutlist.empty())
1802 for (it = cutlist.begin(); it != cutlist.end();)
1811 if (it != cutlist.end())
1833 frames += end - start;
1844 int pos =
filename.lastIndexOf(
'/');
1848 int keyframedist = -1;
1856 if (!posMap.empty())
1863 if (!posMap.empty())
1866 if (fps < 26 && fps > 24)
1872 if (!posMap.empty())
1886 frm_pos_map_t::const_iterator it = posMap.cend();
1888 uint64_t totframes = it.key() * keyframedist;
1892static int getFileInfo(
const QString& inFile,
const QString& outFile,
int lenMethod)
1895 LOG(VB_JOBQUEUE , LOG_INFO, QString(
"getFileInfo(): Opening '%1'")
1902 LOG(VB_JOBQUEUE, LOG_ERR,
"getFileInfo(): Couldn't open input file" +
1908 int ret = avformat_find_stream_info(inputFC,
nullptr);
1912 LOG(VB_JOBQUEUE, LOG_ERR,
1913 QString(
"Couldn't get stream info, error #%1").arg(ret));
1918 av_dump_format(inputFC, 0, qPrintable(inFile), 0);
1920 QDomDocument doc(
"FILEINFO");
1922 QDomElement root = doc.createElement(
"file");
1923 doc.appendChild(root);
1924 root.setAttribute(
"type", inputFC->iformat->name);
1925 root.setAttribute(
"filename", inFile);
1927 QDomElement streams = doc.createElement(
"streams");
1929 root.appendChild(streams);
1930 streams.setAttribute(
"count", inputFC->nb_streams);
1931 int ffmpegIndex = 0;
1934 for (
uint i = 0; i < inputFC->nb_streams; i++)
1936 AVStream *st = inputFC->streams[i];
1937 std::string buf (256,
'\0');
1939 AVCodecParameters *par = st->codecpar;
1942 avcodec_string(buf.data(), buf.size(), avctx,
static_cast<int>(
false));
1944 switch (st->codecpar->codec_type)
1946 case AVMEDIA_TYPE_VIDEO:
1948 QStringList param = QString::fromStdString(buf).split(
',', Qt::SkipEmptyParts);
1949 QString codec = param[0].remove(
"Video:", Qt::CaseInsensitive).remove(QChar::Null);
1950 QDomElement stream = doc.createElement(
"video");
1951 stream.setAttribute(
"streamindex", i);
1952 stream.setAttribute(
"ffmpegindex", ffmpegIndex++);
1953 stream.setAttribute(
"codec", codec.trimmed());
1954 stream.setAttribute(
"width", par->width);
1955 stream.setAttribute(
"height", par->height);
1956 stream.setAttribute(
"bitrate", (qlonglong)par->bit_rate);
1959 if (st->r_frame_rate.den && st->r_frame_rate.num)
1960 fps = av_q2d(st->r_frame_rate);
1962 fps = 1/av_q2d(st->time_base);
1964 stream.setAttribute(
"fps", fps);
1966 if (par->sample_aspect_ratio.den && par->sample_aspect_ratio.num)
1968 float aspect_ratio = av_q2d(par->sample_aspect_ratio);
1969 if (QString(inputFC->iformat->name) !=
"nuv")
1970 aspect_ratio = ((float)par->width
1971 / par->height) * aspect_ratio;
1973 stream.setAttribute(
"aspectratio", aspect_ratio);
1977 stream.setAttribute(
"aspectratio",
"N/A");
1980 stream.setAttribute(
"id", st->id);
1982 if (st->start_time != (
int) AV_NOPTS_VALUE)
1984 int secs = st->start_time / AV_TIME_BASE;
1985 int us = st->start_time % AV_TIME_BASE;
1986 stream.setAttribute(
"start_time", QString(
"%1.%2")
1987 .arg(secs).arg(av_rescale(us, 1000000, AV_TIME_BASE)));
1991 stream.setAttribute(
"start_time", 0);
1994 streams.appendChild(stream);
2000 int64_t frameCount = 0;
2007 if (inputFC->duration != (
uint) AV_NOPTS_VALUE)
2009 duration = (
uint) (inputFC->duration / AV_TIME_BASE);
2010 root.setAttribute(
"duration", duration);
2011 LOG(VB_JOBQUEUE, LOG_INFO,
2012 QString(
"duration = %1") .arg(duration));
2013 frameCount = (int64_t)(duration * fps);
2017 root.setAttribute(
"duration",
"N/A");
2025 LOG(VB_JOBQUEUE, LOG_INFO,
2026 QString(
"frames = %1").arg(frameCount));
2027 duration = (
uint)(frameCount / fps);
2028 LOG(VB_JOBQUEUE, LOG_INFO,
2029 QString(
"duration = %1").arg(duration));
2030 root.setAttribute(
"duration", duration);
2040 LOG(VB_JOBQUEUE, LOG_INFO,
2041 QString(
"frames = %1").arg(frameCount));
2042 duration = (
uint)(frameCount / fps);
2043 LOG(VB_JOBQUEUE, LOG_INFO,
2044 QString(
"duration = %1").arg(duration));
2045 root.setAttribute(
"duration", duration);
2047 else if (inputFC->duration != (
uint) AV_NOPTS_VALUE)
2049 duration = (
uint) (inputFC->duration / AV_TIME_BASE);
2050 root.setAttribute(
"duration", duration);
2051 LOG(VB_JOBQUEUE, LOG_INFO,
2052 QString(
"duration = %1").arg(duration));
2053 frameCount = (int64_t)(duration * fps);
2057 root.setAttribute(
"duration",
"N/A");
2062 root.setAttribute(
"duration",
"N/A");
2063 LOG(VB_JOBQUEUE, LOG_ERR,
2064 QString(
"Unknown lenMethod (%1)")
2070 LOG(VB_JOBQUEUE, LOG_INFO,
2071 QString(
"cutframes = %1").arg(cutFrames));
2072 int cutduration = (int)(cutFrames / fps);
2073 LOG(VB_JOBQUEUE, LOG_INFO,
2074 QString(
"cutduration = %1").arg(cutduration));
2075 root.setAttribute(
"cutduration", duration - cutduration);
2081 case AVMEDIA_TYPE_AUDIO:
2083 QStringList param = QString::fromStdString(buf).split(
',', Qt::SkipEmptyParts);
2084 QString codec = param[0].remove(
"Audio:", Qt::CaseInsensitive).remove(QChar::Null);
2086 QDomElement stream = doc.createElement(
"audio");
2087 stream.setAttribute(
"streamindex", i);
2088 stream.setAttribute(
"ffmpegindex", ffmpegIndex++);
2092 if (codec.trimmed().toLower() ==
"liba52")
2093 stream.setAttribute(
"codec",
"AC3");
2095 stream.setAttribute(
"codec", codec.trimmed());
2097 stream.setAttribute(
"channels", par->ch_layout.nb_channels);
2099 AVDictionaryEntry *metatag =
2100 av_dict_get(st->metadata,
"language",
nullptr, 0);
2102 stream.setAttribute(
"language", metatag->value);
2104 stream.setAttribute(
"language",
"N/A");
2106 stream.setAttribute(
"id", st->id);
2108 stream.setAttribute(
"samplerate", par->sample_rate);
2109 stream.setAttribute(
"bitrate", (qlonglong)par->bit_rate);
2111 if (st->start_time != (
int) AV_NOPTS_VALUE)
2113 int secs = st->start_time / AV_TIME_BASE;
2114 int us = st->start_time % AV_TIME_BASE;
2115 stream.setAttribute(
"start_time", QString(
"%1.%2")
2116 .arg(secs).arg(av_rescale(us, 1000000, AV_TIME_BASE)));
2120 stream.setAttribute(
"start_time", 0);
2123 streams.appendChild(stream);
2128 case AVMEDIA_TYPE_SUBTITLE:
2130 QStringList param = QString::fromStdString(buf).split(
',', Qt::SkipEmptyParts);
2131 QString codec = param[0].remove(
"Subtitle:", Qt::CaseInsensitive).remove(QChar::Null);
2133 QDomElement stream = doc.createElement(
"subtitle");
2134 stream.setAttribute(
"streamindex", i);
2135 stream.setAttribute(
"ffmpegindex", ffmpegIndex++);
2136 stream.setAttribute(
"codec", codec.trimmed());
2138 AVDictionaryEntry *metatag =
2139 av_dict_get(st->metadata,
"language",
nullptr, 0);
2141 stream.setAttribute(
"language", metatag->value);
2143 stream.setAttribute(
"language",
"N/A");
2145 stream.setAttribute(
"id", st->id);
2147 streams.appendChild(stream);
2152 case AVMEDIA_TYPE_DATA:
2154 QDomElement stream = doc.createElement(
"data");
2155 stream.setAttribute(
"streamindex", i);
2156 stream.setAttribute(
"codec", QString::fromStdString(buf).remove(QChar::Null));
2157 streams.appendChild(stream);
2163 LOG(VB_JOBQUEUE, LOG_ERR,
2164 QString(
"Skipping unsupported codec %1 on stream %2")
2165 .arg(inputFC->streams[i]->codecpar->codec_type).arg(i));
2173 if (!f.open(QIODevice::WriteOnly))
2175 LOG(VB_JOBQUEUE, LOG_ERR,
2176 "Failed to open file for writing - " + outFile);
2181 t << doc.toString(4);
2193 if (!f.open(QIODevice::WriteOnly))
2195 LOG(VB_GENERAL, LOG_ERR,
2196 QString(
"MythArchiveHelper: Failed to open file for writing - %1")
2215 if (
filename.startsWith(
"myth://"))
2245 add(QStringList{
"-t",
"--createthumbnail"},
2246 "createthumbnail",
false,
2247 "Create one or more thumbnails\n"
2248 "Requires: --infile, --thumblist, --outfile\n"
2249 "Optional: --framecount",
"");
2250 add(
"--infile",
"infile",
"",
2252 "Used with: --createthumbnail, --getfileinfo, --isremote, "
2253 "--sup2dast, --importarchive",
"");
2254 add(
"--outfile",
"outfile",
"",
2255 "Output file name\n"
2256 "Used with: --createthumbnail, --getfileinfo, --getdbparameters, "
2258 "When used with --createthumbnail: eg 'thumb%1-%2.jpg'\n"
2259 " %1 will be replaced with the no. of the thumb\n"
2260 " %2 will be replaced with the frame no.",
"");
2261 add(
"--thumblist",
"thumblist",
"",
2262 "Comma-separated list of required thumbs (in seconds)\n"
2263 "Used with: --createthumbnail",
"");
2264 add(
"--framecount",
"framecount", 1,
2265 "Number of frames to grab (default 1)\n"
2266 "Used with: --createthumbnail",
"");
2268 add(QStringList{
"-i",
"--getfileinfo"},
2269 "getfileinfo",
false,
2270 "Write file info about infile to outfile\n"
2271 "Requires: --infile, --outfile, --method",
"");
2272 add(
"--method",
"method", 0,
2273 "Method of file duration calculation\n"
2274 "Used with: --getfileinfo\n"
2275 " 0 = use av_estimate_timings() (quick but not very accurate - "
2277 " 1 = read all frames (most accurate but slow)\n"
2278 " 2 = use position map in DB (quick, only works for MythTV "
2281 add(QStringList{
"-p",
"--getdbparameters"},
2282 "getdbparameters",
false,
2283 "Write the mysql database parameters to outfile\n"
2284 "Requires: --outfile",
"");
2286 add(QStringList{
"-n",
"--nativearchive"},
2287 "nativearchive",
false,
2288 "Archive files to a native archive format\n"
2289 "Requires: --outfile",
"");
2291 add(QStringList{
"-f",
"--importarchive"},
2292 "importarchive",
false,
2293 "Import an archived file\n"
2294 "Requires: --infile, --chanid",
"");
2295 add(
"--chanid",
"chanid", -1,
2296 "Channel ID to use when inserting records in DB\n"
2297 "Used with: --importarchive",
"");
2299 add(QStringList{
"-r",
"--isremote"},
2301 "Check if infile is on a remote filesystem\n"
2302 "Requires: --infile\n"
2303 "Returns: 0 on error or file not found\n"
2304 " - 1 file is on a local filesystem\n"
2305 " - 2 file is on a remote filesystem",
"");
2307 add(QStringList{
"-b",
"--burndvd"},
2309 "Burn a created DVD to a blank disc\n"
2310 "Optional: --mediatype, --erasedvdrw, --nativeformat",
"");
2311 add(
"--mediatype",
"mediatype", 0,
2312 "Type of media to burn\n"
2313 "Used with: --burndvd\n"
2314 " 0 = single layer DVD (default)\n"
2315 " 1 = dual layer DVD\n"
2316 " 2 = rewritable DVD",
"");
2317 add(
"--erasedvdrw",
"erasedvdrw",
false,
2318 "Force an erase of DVD-R/W Media\n"
2319 "Used with: --burndvd (optional)",
"");
2320 add(
"--nativeformat",
"nativeformat",
false,
2321 "Archive is a native archive format\n"
2322 "Used with: --burndvd (optional)",
"");
2324 add(QStringList{
"-s",
"--sup2dast"},
2326 "Convert projectX subtitles to DVD subtitles\n"
2327 "Requires: --infile, --ifofile, --delay",
"");
2328 add(
"--ifofile",
"ifofile",
"",
2329 "Filename of ifo file\n"
2330 "Used with: --sup2dast",
"");
2331 add(
"--delay",
"delay", 0,
2332 "Delay in ms to add to subtitles (default 0)\n"
2333 "Used with: --sup2dast",
"");
2359 QCoreApplication a(argc, argv);
2360 QCoreApplication::setApplicationName(
"mytharchivehelper");
2363 QString mask(
"jobqueue");
2373 if (!context.Init(
false))
2375 LOG(VB_GENERAL, LOG_ERR,
"Failed to init MythContext, exiting.");
2405 if (inFile.isEmpty())
2407 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile in -t/--grabthumbnail "
2412 if (thumbList.isEmpty())
2414 LOG(VB_GENERAL, LOG_ERR,
"Missing --thumblist in -t/--grabthumbnail"
2419 if (outFile.isEmpty())
2421 LOG(VB_GENERAL, LOG_ERR,
"Missing --outfile in -t/--grabthumbnail "
2427 if (bGetDBParameters)
2429 if (outFile.isEmpty())
2431 LOG(VB_GENERAL, LOG_ERR,
"Missing argument to -p/--getdbparameters "
2439 if (inFile.isEmpty())
2441 LOG(VB_GENERAL, LOG_ERR,
2442 "Missing argument to -r/--isremote option");
2449 if (mediaType < 0 || mediaType > 2)
2451 LOG(VB_GENERAL, LOG_ERR, QString(
"Invalid mediatype given: %1")
2459 if (outFile.isEmpty())
2461 LOG(VB_GENERAL, LOG_ERR,
"Missing argument to -n/--nativearchive "
2469 if (inFile.isEmpty())
2471 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile argument to "
2472 "-f/--importarchive option");
2479 if (inFile.isEmpty())
2481 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile in -i/--getfileinfo "
2486 if (outFile.isEmpty())
2488 LOG(VB_GENERAL, LOG_ERR,
"Missing --outfile in -i/--getfileinfo "
2496 if (inFile.isEmpty())
2498 LOG(VB_GENERAL, LOG_ERR,
2499 "Missing --infile in -s/--sup2dast option");
2503 if (ifoFile.isEmpty())
2505 LOG(VB_GENERAL, LOG_ERR,
2506 "Missing --ifofile in -s/--sup2dast option");
2511 if (bGrabThumbnail) {
2512 res =
grabThumbnail(inFile, thumbList, outFile, frameCount);
2513 }
else if (bGetDBParameters) {
2515 }
else if (bNativeArchive) {
2517 }
else if (bImportArchive) {
2519 }
else if (bGetFileInfo) {
2521 }
else if (bIsRemote) {
2523 }
else if (bDoBurn) {
2524 res =
doBurnDVD(mediaType, bEraseDVDRW, bNativeFormat);
2525 }
else if (bSup2Dast) {
2526 QByteArray inFileBA = inFile.toLocal8Bit();
2527 QByteArray ifoFileBA = ifoFile.toLocal8Bit();
2528 res =
sup2dast(inFileBA.constData(), ifoFileBA.constData(), delay);
bool extractDetailsFromFilename(const QString &inFile, QString &chanID, QString &startTime)
QString getBaseName(const QString &filename)
ProgramInfo * getProgramInfoForFile(const QString &inFile)
QString getTempDirectory(bool showError)
Structure containing the basic Database parameters.
QString m_dbName
database name
QString m_dbPassword
DB password.
QString m_dbUserName
DB user name.
QString m_dbHostName
database server
QSqlQuery wrapper that fetches a DB connection from the connection pool.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
QVariant value(int i) const
bool isActive(void) const
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
int Copy(AVFrame *To, const MythVideoFrame *From, unsigned char *Buffer, AVPixelFormat Fmt=AV_PIX_FMT_YUV420P)
Initialise AVFrame and copy contents of VideoFrame frame into it, performing any required conversion.
MythAVFrame little utility class that act as a safe way to allocate an AVFrame which can then be allo...
static void DeinterlaceAVFrame(AVFrame *Frame)
Deinterlace an AVFrame.
MythArchiveHelperCommandLineParser()
void LoadArguments(void) override
AVCodecContext * GetCodecContext(const AVStream *Stream, const AVCodec *Codec=nullptr, bool NullCodec=false)
void FreeCodecContext(const AVStream *Stream)
Parent class for defining application command line parsers.
void addVersion(void)
Canned argument definition for –version.
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.
virtual bool Parse(int argc, const char *const *argv)
Loop through argv and populate arguments with values.
int ConfigureLogging(const QString &mask="general", bool progress=false)
Read in logging options and initialize the logging interface.
void addLogging(const QString &defaultVerbosity="general", LogLevel_t defaultLogLevel=LOG_INFO)
Canned argument definition for all logging options, including –verbose, –logpath, –quiet,...
QString toString(const QString &key) const
Returns stored QVariant as a QString, falling to default if not provided.
static void PrintVersion(void)
Print application version information.
CommandLineArg * add(const QString &arg, const QString &name, bool def, QString help, QString longhelp)
void addHelp(void)
Canned argument definition for –help.
virtual void LoadArguments(void)
uint toUInt(const QString &key) const
Returns stored QVariant as an unsigned integer, falling to default if not provided.
void PrintHelp(void) const
Print command line option help.
Startup context for MythTV.
QString GetHostName(void)
void SaveSetting(const QString &key, int newValue)
QString GetSetting(const QString &key, const QString &defaultval="")
static int GetMasterServerPort(void)
Returns the Master Backend control port If no master server port has been defined in the database,...
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
int GetNumSetting(const QString &key, int defaultval=0)
QString GetMasterHostName(void)
static void DBError(const QString &where, const MSqlQuery &query)
static int importVideo(const QDomElement &itemNode, const QString &xmlFile)
static int doImportArchive(const QString &xmlFile, int chanID)
static int exportVideo(QDomElement &itemNode, const QString &saveDirectory)
static int importRecording(const QDomElement &itemNode, const QString &xmlFile, int chanID)
static int exportRecording(QDomElement &itemNode, const QString &saveDirectory)
static bool copyFile(const QString &source, const QString &destination)
static int getFieldList(QStringList &fieldList, const QString &tableName)
static int doNativeArchive(const QString &jobFile)
static QString findNodeText(const QDomElement &elem, const QString &nodeName)
Holds information on recordings and videos.
void QueryPositionMap(frm_pos_map_t &posMap, MarkTypes type) const
bool QueryCutList(frm_dir_map_t &delMap, bool loadAutosave=false) const
@ GENERIC_EXIT_NO_MYTHCONTEXT
No MythContext available.
@ GENERIC_EXIT_OK
Exited with no error.
@ GENERIC_EXIT_INVALID_CMDLINE
Command line parse error.
void logStop(void)
Entry point for stopping logging for an application.
static const QRegularExpression badChars
static int64_t getFrameCount(AVFormatContext *inputFC, int vid_id)
static int main_local(int argc, char **argv)
static int doNativeArchive(const QString &jobFile)
int main(int argc, char **argv)
static int grabThumbnail(const QString &inFile, const QString &thumbList, const QString &outFile, int frameCount)
static int64_t getCutFrames(const QString &filename, int64_t lastFrame)
static QString fixFilename(const QString &filename)
static int getFileInfo(const QString &inFile, const QString &outFile, int lenMethod)
static int doBurnDVD(int mediaType, bool bEraseDVDRW, bool nativeFormat)
static int getDBParamters(const QString &outFile)
static void clearArchiveTable(void)
static int isRemote(const QString &filename)
static bool createISOImage(QString &sourceDirectory)
static int burnISOImage(int mediaType, bool bEraseDVDRW, bool nativeFormat)
static int doImportArchive(const QString &inFile, int chanID)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QString GetInstallPrefix(void)
#define ENO
This can be appended to the LOG args with "+".
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
bool MythRemoveDirectory(QDir &aDir)
uint myth_system(const QString &command, uint flags, std::chrono::seconds timeout)
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
@ kDatabase
Default UTC, database format.
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
MythCommFlagCommandLineParser cmdline
def rating(profile, smoonURL, gate)
QMap< uint64_t, MarkTypes > frm_dir_map_t
Frame # -> Mark map.
QMap< long long, long long > frm_pos_map_t
Frame # -> File offset map.
int sup2dast(const char *supfile, const char *ifofile, int delay_ms)