33#include <QApplication>
39#include <QMutexLocker>
40#include <QRegularExpression>
44#include <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")
333 LOG(VB_JOBQUEUE, LOG_ERR,
334 QString(
"Don't know how to archive items of type '%1'")
335 .arg(
type.toLower()));
342 if (mediaType !=
AD_FILE && bDoBurn)
346 LOG(VB_JOBQUEUE, LOG_ERR,
347 "Native archive job failed to complete");
357 LOG(VB_JOBQUEUE, LOG_ERR,
"Native archive job failed to complete");
362 LOG(VB_JOBQUEUE, LOG_INFO,
"Native archive job completed OK");
367static const QRegularExpression
badChars { R
"((/|\\|:|'|"|\?|\|))" };
381 if (query.
exec(
"DESCRIBE " + tableName))
385 fieldList.append(query.
value(0).toString());
393 return fieldList.count();
397 const QString &saveDirectory)
403 QString title =
fixFilename(itemNode.attribute(
"title"));
404 QString
filename = itemNode.attribute(
"filename");
405 bool doDelete = (itemNode.attribute(
"delete",
"0") ==
"0");
406 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"Archiving %1 (%2), do delete: %3")
407 .arg(title,
filename, doDelete ?
"true" :
"false"));
411 LOG(VB_JOBQUEUE, LOG_ERR,
"Bad title or filename");
417 LOG(VB_JOBQUEUE, LOG_ERR,
418 QString(
"Failed to extract chanID and startTime from '%1'")
424 QDir dir(saveDirectory + title);
426 dir.mkpath(saveDirectory + title);
428 LOG(VB_GENERAL, LOG_ERR,
"Failed to create savedir: " +
ENO);
430 LOG(VB_JOBQUEUE, LOG_INFO,
"Creating xml file for " + title);
431 QDomDocument doc(
"MYTHARCHIVEITEM");
433 QDomElement root = doc.createElement(
"item");
434 doc.appendChild(root);
435 root.setAttribute(
"type",
"recording");
436 root.setAttribute(
"databaseversion",
dbVersion);
438 QDomElement recorded = doc.createElement(
"recorded");
439 root.appendChild(recorded);
442 QStringList fieldList;
446 query.
prepare(
"SELECT " + fieldList.join(
",")
448 " WHERE chanid = :CHANID and starttime = :STARTTIME;");
450 query.
bindValue(
":STARTTIME", startTime);
457 for (
int x = 0; x < fieldList.size(); x++)
459 elem = doc.createElement(fieldList[x]);
460 text = doc.createTextNode(query.
value(x).toString());
461 elem.appendChild(text);
462 recorded.appendChild(elem);
465 LOG(VB_JOBQUEUE, LOG_INFO,
"Created recorded element for " + title);
469 LOG(VB_JOBQUEUE, LOG_INFO,
"Failed to get recorded field list");
473 query.
prepare(
"SELECT chanid, channum, callsign, name "
474 "FROM channel WHERE chanid = :CHANID;");
479 QDomElement channel = doc.createElement(
"channel");
480 channel.setAttribute(
"chanid", query.
value(0).toString());
481 channel.setAttribute(
"channum", query.
value(1).toString());
482 channel.setAttribute(
"callsign", query.
value(2).toString());
483 channel.setAttribute(
"name", query.
value(3).toString());
484 root.appendChild(channel);
485 LOG(VB_JOBQUEUE, LOG_INFO,
"Created channel element for " + title);
490 LOG(VB_JOBQUEUE, LOG_ERR,
491 "Cannot find channel details for chanid " + chanID);
492 QDomElement channel = doc.createElement(
"channel");
493 channel.setAttribute(
"chanid", chanID);
494 channel.setAttribute(
"channum",
"unknown");
495 channel.setAttribute(
"callsign",
"unknown");
496 channel.setAttribute(
"name",
"unknown");
497 root.appendChild(channel);
498 LOG(VB_JOBQUEUE, LOG_INFO,
499 "Created a default channel element for " + title);
503 query.
prepare(
"SELECT credits.person, role, people.name "
504 "FROM recordedcredits AS credits "
505 "LEFT JOIN people ON credits.person = people.person "
506 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
508 query.
bindValue(
":STARTTIME", startTime);
512 QDomElement credits = doc.createElement(
"credits");
515 QDomElement credit = doc.createElement(
"credit");
516 credit.setAttribute(
"personid", query.
value(0).toString());
517 credit.setAttribute(
"name", query.
value(2).toString());
518 credit.setAttribute(
"role", query.
value(1).toString());
519 credits.appendChild(credit);
521 root.appendChild(credits);
522 LOG(VB_JOBQUEUE, LOG_INFO,
"Created credits element for " + title);
526 query.
prepare(
"SELECT `system`, rating FROM recordedrating "
527 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
529 query.
bindValue(
":STARTTIME", startTime);
533 QDomElement
rating = doc.createElement(
"rating");
534 rating.setAttribute(
"system", query.
value(0).toString());
535 rating.setAttribute(
"rating", query.
value(1).toString());
537 LOG(VB_JOBQUEUE, LOG_INFO,
"Created rating element for " + title);
541 QDomElement recordedmarkup = doc.createElement(
"recordedmarkup");
542 query.
prepare(
"SELECT chanid, starttime, mark, type, data "
543 "FROM recordedmarkup "
544 "WHERE chanid = :CHANID and starttime = :STARTTIME;");
546 query.
bindValue(
":STARTTIME", startTime);
551 QDomElement mark = doc.createElement(
"mark");
552 mark.setAttribute(
"mark", query.
value(2).toString());
553 mark.setAttribute(
"type", query.
value(3).toString());
554 mark.setAttribute(
"data", query.
value(4).toString());
555 recordedmarkup.appendChild(mark);
557 root.appendChild(recordedmarkup);
558 LOG(VB_JOBQUEUE, LOG_INFO,
"Created recordedmarkup element for " + title);
562 QDomElement recordedseek = doc.createElement(
"recordedseek");
563 query.
prepare(
"SELECT chanid, starttime, mark, `offset`, type "
565 "WHERE chanid = :CHANID and starttime = :STARTTIME;");
567 query.
bindValue(
":STARTTIME", startTime);
572 QDomElement mark = doc.createElement(
"mark");
573 mark.setAttribute(
"mark", query.
value(2).toString());
574 mark.setAttribute(
"offset", query.
value(3).toString());
575 mark.setAttribute(
"type", query.
value(4).toString());
576 recordedseek.appendChild(mark);
578 root.appendChild(recordedseek);
579 LOG(VB_JOBQUEUE, LOG_INFO,
580 "Created recordedseek element for " + title);
585 QString xmlFile = saveDirectory + title +
"/" + baseName +
".xml";
587 if (!f.open(QIODevice::WriteOnly))
589 LOG(VB_JOBQUEUE, LOG_ERR,
590 "MythNativeWizard: Failed to open file for writing - " + xmlFile);
595 t << doc.toString(4);
599 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
607 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying preview image");
609 + title +
"/" + baseName +
".png");
614 LOG(VB_JOBQUEUE, LOG_INFO,
"Item Archived OK");
620 const QString &saveDirectory)
625 QString coverFile =
"";
627 QString title =
fixFilename(itemNode.attribute(
"title"));
628 QString
filename = itemNode.attribute(
"filename");
629 bool doDelete = (itemNode.attribute(
"delete",
"0") ==
"0");
630 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"Archiving %1 (%2), do delete: %3")
631 .arg(title,
filename, doDelete ?
"true" :
"false"));
635 LOG(VB_JOBQUEUE, LOG_ERR,
"Bad title or filename");
640 QDir dir(saveDirectory + title);
642 dir.mkdir(saveDirectory + title);
644 LOG(VB_JOBQUEUE, LOG_INFO,
"Creating xml file for " + title);
645 QDomDocument doc(
"MYTHARCHIVEITEM");
647 QDomElement root = doc.createElement(
"item");
648 doc.appendChild(root);
649 root.setAttribute(
"type",
"video");
650 root.setAttribute(
"databaseversion",
dbVersion);
652 QDomElement video = doc.createElement(
"videometadata");
653 root.appendChild(video);
657 query.
prepare(
"SELECT intid, title, director, plot, rating, inetref, "
658 "year, userrating, length, showlevel, filename, coverfile, "
659 "childid, browse, playcommand, category "
660 "FROM videometadata WHERE filename = :FILENAME;");
668 elem = doc.createElement(
"intid");
669 text = doc.createTextNode(query.
value(0).toString());
670 intID = query.
value(0).toInt();
671 elem.appendChild(text);
672 video.appendChild(elem);
674 elem = doc.createElement(
"title");
675 text = doc.createTextNode(query.
value(1).toString());
676 elem.appendChild(text);
677 video.appendChild(elem);
679 elem = doc.createElement(
"director");
680 text = doc.createTextNode(query.
value(2).toString());
681 elem.appendChild(text);
682 video.appendChild(elem);
684 elem = doc.createElement(
"plot");
685 text = doc.createTextNode(query.
value(3).toString());
686 elem.appendChild(text);
687 video.appendChild(elem);
689 elem = doc.createElement(
"rating");
690 text = doc.createTextNode(query.
value(4).toString());
691 elem.appendChild(text);
692 video.appendChild(elem);
694 elem = doc.createElement(
"inetref");
695 text = doc.createTextNode(query.
value(5).toString());
696 elem.appendChild(text);
697 video.appendChild(elem);
699 elem = doc.createElement(
"year");
700 text = doc.createTextNode(query.
value(6).toString());
701 elem.appendChild(text);
702 video.appendChild(elem);
704 elem = doc.createElement(
"userrating");
705 text = doc.createTextNode(query.
value(7).toString());
706 elem.appendChild(text);
707 video.appendChild(elem);
709 elem = doc.createElement(
"length");
710 text = doc.createTextNode(query.
value(8).toString());
711 elem.appendChild(text);
712 video.appendChild(elem);
714 elem = doc.createElement(
"showlevel");
715 text = doc.createTextNode(query.
value(9).toString());
716 elem.appendChild(text);
717 video.appendChild(elem);
720 QString fname = query.
value(10).toString();
724 elem = doc.createElement(
"filename");
725 text = doc.createTextNode(fname);
726 elem.appendChild(text);
727 video.appendChild(elem);
729 elem = doc.createElement(
"coverfile");
730 text = doc.createTextNode(query.
value(11).toString());
731 coverFile = query.
value(11).toString();
732 elem.appendChild(text);
733 video.appendChild(elem);
735 elem = doc.createElement(
"childid");
736 text = doc.createTextNode(query.
value(12).toString());
737 elem.appendChild(text);
738 video.appendChild(elem);
740 elem = doc.createElement(
"browse");
741 text = doc.createTextNode(query.
value(13).toString());
742 elem.appendChild(text);
743 video.appendChild(elem);
745 elem = doc.createElement(
"playcommand");
746 text = doc.createTextNode(query.
value(14).toString());
747 elem.appendChild(text);
748 video.appendChild(elem);
750 elem = doc.createElement(
"categoryid");
751 text = doc.createTextNode(query.
value(15).toString());
752 categoryID = query.
value(15).toInt();
753 elem.appendChild(text);
754 video.appendChild(elem);
756 LOG(VB_JOBQUEUE, LOG_INFO,
757 "Created videometadata element for " + title);
761 query.
prepare(
"SELECT intid, category "
762 "FROM videocategory WHERE intid = :INTID;");
767 QDomElement category = doc.createElement(
"category");
768 category.setAttribute(
"intid", query.
value(0).toString());
769 category.setAttribute(
"category", query.
value(1).toString());
770 root.appendChild(category);
771 LOG(VB_JOBQUEUE, LOG_INFO,
772 "Created videocategory element for " + title);
776 QDomElement countries = doc.createElement(
"countries");
777 root.appendChild(countries);
779 query.
prepare(
"SELECT intid, country "
780 "FROM videometadatacountry INNER JOIN videocountry "
781 "ON videometadatacountry.idcountry = videocountry.intid "
782 "WHERE idvideo = :INTID;");
792 QDomElement country = doc.createElement(
"country");
793 country.setAttribute(
"intid", query.
value(0).toString());
794 country.setAttribute(
"country", query.
value(1).toString());
795 countries.appendChild(country);
797 LOG(VB_JOBQUEUE, LOG_INFO,
"Created videocountry element for " + title);
801 QDomElement genres = doc.createElement(
"genres");
802 root.appendChild(genres);
804 query.
prepare(
"SELECT intid, genre "
805 "FROM videometadatagenre INNER JOIN videogenre "
806 "ON videometadatagenre.idgenre = videogenre.intid "
807 "WHERE idvideo = :INTID;");
817 QDomElement genre = doc.createElement(
"genre");
818 genre.setAttribute(
"intid", query.
value(0).toString());
819 genre.setAttribute(
"genre", query.
value(1).toString());
820 genres.appendChild(genre);
822 LOG(VB_JOBQUEUE, LOG_INFO,
"Created videogenre element for " + title);
827 QString xmlFile = saveDirectory + title +
"/"
828 + fileInfo.fileName() +
".xml";
830 if (!f.open(QIODevice::WriteOnly))
832 LOG(VB_JOBQUEUE, LOG_INFO,
833 "MythNativeWizard: Failed to open file for writing - " + xmlFile);
838 t << doc.toString(4);
842 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
844 +
"/" + fileInfo.fileName());
851 fileInfo.setFile(coverFile);
852 if (fileInfo.exists())
854 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying cover file");
855 res =
copyFile(coverFile, saveDirectory + title
856 +
"/" + fileInfo.fileName());
863 LOG(VB_JOBQUEUE, LOG_INFO,
"Item Archived OK");
871 QDomDocument doc(
"mydocument");
873 if (!
file.open(QIODevice::ReadOnly))
875 LOG(VB_JOBQUEUE, LOG_ERR,
876 "Failed to open file for reading - " + xmlFile);
880 if (!doc.setContent(&
file))
883 LOG(VB_JOBQUEUE, LOG_ERR,
884 "Failed to read from xml file - " + xmlFile);
889 QString docType = doc.doctype().name();
892 QDomNodeList itemNodeList;
894 QDomElement itemNode;
896 if (docType ==
"MYTHARCHIVEITEM")
898 itemNodeList = doc.elementsByTagName(
"item");
900 if (itemNodeList.count() < 1)
902 LOG(VB_JOBQUEUE, LOG_ERR,
903 "Couldn't find an 'item' element in XML file");
907 node = itemNodeList.item(0);
908 itemNode = node.toElement();
909 type = itemNode.attribute(
"type");
910 dbVersion = itemNode.attribute(
"databaseversion");
912 LOG(VB_JOBQUEUE, LOG_INFO,
913 QString(
"Archive DB version: %1, Local DB version: %2")
918 LOG(VB_JOBQUEUE, LOG_ERR,
"Not a native archive xml file - " + xmlFile);
922 if (
type ==
"recording")
935 const QString &xmlFile,
int chanID)
937 LOG(VB_JOBQUEUE, LOG_INFO,
938 QString(
"Import recording using chanID: %1").arg(chanID));
939 LOG(VB_JOBQUEUE, LOG_INFO,
940 QString(
"Archived recording xml file: %1").arg(xmlFile));
942 QString videoFile = xmlFile.left(xmlFile.length() - 4);
943 QString basename = videoFile;
944 int pos = videoFile.lastIndexOf(
'/');
946 basename = videoFile.mid(pos + 1);
948 QDomNodeList nodeList = itemNode.elementsByTagName(
"recorded");
949 if (nodeList.count() < 1)
951 LOG(VB_JOBQUEUE, LOG_ERR,
952 "Couldn't find a 'recorded' element in XML file");
956 QDomNode n = nodeList.item(0);
957 QDomElement recordedNode = n.toElement();
958 QString startTime =
findNodeText(recordedNode,
"starttime");
961 query.
prepare(
"SELECT * FROM recorded "
962 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
964 query.
bindValue(
":STARTTIME", startTime);
969 LOG(VB_JOBQUEUE, LOG_ERR,
970 "This recording appears to already exist!!");
977 basename ,
"Default");
980 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file to: " + destFile);
987 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying preview image file to: " + destFile +
".png");
988 if (!
copyFile(videoFile +
".png", destFile +
".png"))
993 QStringList fieldList;
994 QStringList bindList;
995 QDomNodeList nodes = recordedNode.childNodes();
997 for (
int x = 0; x < nodes.count(); x++)
999 QDomNode n2 = nodes.item(x);
1000 QString field = n2.nodeName();
1001 fieldList.append(field);
1002 bindList.append(
":" + field.toUpper());
1006 query.
prepare(
"INSERT INTO recorded (" + fieldList.join(
",") +
") "
1007 "VALUES (" + bindList.join(
",") +
");");
1009 query.
bindValue(
":STARTTIME", startTime);
1011 for (
int x = 0; x < fieldList.count(); x++)
1015 LOG(VB_JOBQUEUE, LOG_INFO,
"Inserted recorded details into database");
1020 nodeList = itemNode.elementsByTagName(
"recordedmarkup");
1021 if (nodeList.count() < 1)
1023 LOG(VB_JOBQUEUE, LOG_WARNING,
1024 "Couldn't find a 'recordedmarkup' element in XML file");
1028 QDomNode n3 = nodeList.item(0);
1029 QDomElement markupNode = n3.toElement();
1031 nodeList = markupNode.elementsByTagName(
"mark");
1032 if (nodeList.count() < 1)
1034 LOG(VB_JOBQUEUE, LOG_WARNING,
1035 "Couldn't find any 'mark' elements in XML file");
1040 query.
prepare(
"DELETE FROM recordedmarkup "
1041 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
1043 query.
bindValue(
":STARTTIME", startTime);
1049 for (
int x = 0; x < nodeList.count(); x++)
1051 QDomNode n4 = nodeList.item(x);
1052 QDomElement e = n4.toElement();
1053 query.
prepare(
"INSERT INTO recordedmarkup (chanid, starttime, "
1055 "VALUES(:CHANID,:STARTTIME,:MARK,:TYPE,:DATA);");
1057 query.
bindValue(
":STARTTIME", startTime);
1058 query.
bindValue(
":MARK", e.attribute(
"mark"));
1059 query.
bindValue(
":TYPE", e.attribute(
"type"));
1060 query.
bindValue(
":DATA", e.attribute(
"data"));
1069 LOG(VB_JOBQUEUE, LOG_INFO,
1070 "Inserted recordedmarkup details into database");
1075 nodeList = itemNode.elementsByTagName(
"recordedseek");
1076 if (nodeList.count() < 1)
1078 LOG(VB_JOBQUEUE, LOG_WARNING,
1079 "Couldn't find a 'recordedseek' element in XML file");
1083 QDomNode n5 = nodeList.item(0);
1084 QDomElement markupNode = n5.toElement();
1086 nodeList = markupNode.elementsByTagName(
"mark");
1087 if (nodeList.count() < 1)
1089 LOG(VB_JOBQUEUE, LOG_WARNING,
1090 "Couldn't find any 'mark' elements in XML file");
1095 query.
prepare(
"DELETE FROM recordedseek "
1096 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
1098 query.
bindValue(
":STARTTIME", startTime);
1102 for (
int x = 0; x < nodeList.count(); x++)
1104 QDomNode n6 = nodeList.item(x);
1105 QDomElement e = n6.toElement();
1106 query.
prepare(
"INSERT INTO recordedseek (chanid, starttime, "
1107 "mark, `offset`, type)"
1108 "VALUES(:CHANID,:STARTTIME,:MARK,:OFFSET,:TYPE);");
1110 query.
bindValue(
":STARTTIME", startTime);
1111 query.
bindValue(
":MARK", e.attribute(
"mark"));
1112 query.
bindValue(
":OFFSET", e.attribute(
"offset"));
1113 query.
bindValue(
":TYPE", e.attribute(
"type"));
1122 LOG(VB_JOBQUEUE, LOG_INFO,
1123 "Inserted recordedseek details into database");
1131 LOG(VB_JOBQUEUE, LOG_INFO,
"Import completed OK");
1138 LOG(VB_JOBQUEUE, LOG_INFO,
"Importing video");
1139 LOG(VB_JOBQUEUE, LOG_INFO,
1140 QString(
"Archived video xml file: %1").arg(xmlFile));
1142 QString videoFile = xmlFile.left(xmlFile.length() - 4);
1143 QFileInfo fileInfo(videoFile);
1144 QString basename = fileInfo.fileName();
1146 QDomNodeList nodeList = itemNode.elementsByTagName(
"videometadata");
1147 if (nodeList.count() < 1)
1149 LOG(VB_JOBQUEUE, LOG_ERR,
1150 "Couldn't find a 'videometadata' element in XML file");
1154 QDomNode n = nodeList.item(0);
1155 QDomElement videoNode = n.toElement();
1159 QString origFilename =
findNodeText(videoNode,
"filename");
1160 QStringList dirList = origFilename.split(
"/", Qt::SkipEmptyParts);
1162 for (
int x = 0; x < dirList.count() - 1; x++)
1164 path +=
"/" + dirList[x];
1165 if (!dir.exists(path))
1167 if (!dir.mkdir(path))
1169 LOG(VB_JOBQUEUE, LOG_ERR,
1170 QString(
"Couldn't create directory '%1'").arg(path));
1176 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
1177 if (!
copyFile(videoFile, path +
"/" + basename))
1185 fileInfo.setFile(videoFile);
1186 QString archivePath = fileInfo.absolutePath();
1188 QString coverFilename =
findNodeText(videoNode,
"coverfile");
1189 fileInfo.setFile(coverFilename);
1190 coverFilename = fileInfo.fileName();
1192 fileInfo.setFile(archivePath +
"/" + coverFilename);
1193 if (fileInfo.exists())
1195 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying cover file");
1197 if (!
copyFile(archivePath +
"/" + coverFilename, artworkDir +
"/" + coverFilename))
1204 coverFilename =
"No Cover";
1209 query.
prepare(
"INSERT INTO videometadata (title, director, plot, rating, inetref, "
1210 "year, userrating, length, showlevel, filename, coverfile, "
1211 "childid, browse, playcommand, category) "
1212 "VALUES(:TITLE,:DIRECTOR,:PLOT,:RATING,:INETREF,:YEAR,"
1213 ":USERRATING,:LENGTH,:SHOWLEVEL,:FILENAME,:COVERFILE,"
1214 ":CHILDID,:BROWSE,:PLAYCOMMAND,:CATEGORY);");
1224 query.
bindValue(
":FILENAME", path +
"/" + basename);
1225 query.
bindValue(
":COVERFILE", artworkDir +
"/" + coverFilename);
1233 LOG(VB_JOBQUEUE, LOG_INFO,
1234 "Inserted videometadata details into database");
1244 query.
prepare(
"SELECT intid FROM videometadata WHERE filename = :FILENAME;");
1245 query.
bindValue(
":FILENAME", path +
"/" + basename);
1248 intid = query.
value(0).toInt();
1256 LOG(VB_JOBQUEUE, LOG_INFO,
1257 QString(
"'intid' of inserted video is: %1").arg(intid));
1260 nodeList = itemNode.elementsByTagName(
"genres");
1261 if (nodeList.count() < 1)
1263 LOG(VB_JOBQUEUE, LOG_ERR,
"No 'genres' element found in XML file");
1267 n = nodeList.item(0);
1268 QDomElement genresNode = n.toElement();
1270 nodeList = genresNode.elementsByTagName(
"genre");
1271 if (nodeList.count() < 1)
1273 LOG(VB_JOBQUEUE, LOG_WARNING,
1274 "Couldn't find any 'genre' elements in XML file");
1278 for (
int x = 0; x < nodeList.count(); x++)
1280 n = nodeList.item(x);
1281 QDomElement e = n.toElement();
1283 QString genre = e.attribute(
"genre");
1286 query.
prepare(
"SELECT intid FROM videogenre "
1287 "WHERE genre = :GENRE");
1291 genreID = query.
value(0).toInt();
1296 query.
prepare(
"INSERT INTO videogenre (genre) VALUES(:GENRE);");
1300 "insert videogenre", query);
1303 query.
prepare(
"SELECT intid FROM videogenre "
1304 "WHERE genre = :GENRE");
1306 if (!query.
exec() || !query.
next())
1308 LOG(VB_JOBQUEUE, LOG_ERR,
1309 "Couldn't add genre to database");
1312 genreID = query.
value(0).toInt();
1316 query.
prepare(
"INSERT INTO videometadatagenre (idvideo, idgenre)"
1317 "VALUES (:IDVIDEO, :IDGENRE);");
1322 "insert videometadatagenre", query);
1325 LOG(VB_JOBQUEUE, LOG_INFO,
"Inserted genre details into database");
1330 nodeList = itemNode.elementsByTagName(
"countries");
1331 if (nodeList.count() < 1)
1333 LOG(VB_JOBQUEUE, LOG_INFO,
"No 'countries' element found in XML file");
1337 n = nodeList.item(0);
1338 QDomElement countriesNode = n.toElement();
1340 nodeList = countriesNode.elementsByTagName(
"country");
1341 if (nodeList.count() < 1)
1343 LOG(VB_JOBQUEUE, LOG_WARNING,
1344 "Couldn't find any 'country' elements in XML file");
1348 for (
int x = 0; x < nodeList.count(); x++)
1350 n = nodeList.item(x);
1351 QDomElement e = n.toElement();
1353 QString country = e.attribute(
"country");
1356 query.
prepare(
"SELECT intid FROM videocountry "
1357 "WHERE country = :COUNTRY");
1361 countryID = query.
value(0).toInt();
1366 query.
prepare(
"INSERT INTO videocountry (country) VALUES(:COUNTRY);");
1370 "insert videocountry", query);
1373 query.
prepare(
"SELECT intid FROM videocountry "
1374 "WHERE country = :COUNTRY");
1376 if (!query.
exec() || !query.
next())
1378 LOG(VB_JOBQUEUE, LOG_ERR,
1379 "Couldn't add country to database");
1382 countryID = query.
value(0).toInt();
1386 query.
prepare(
"INSERT INTO videometadatacountry (idvideo, idcountry)"
1387 "VALUES (:IDVIDEO, :IDCOUNTRY);");
1389 query.
bindValue(
":IDCOUNTRY", countryID);
1392 "insert videometadatacountry", query);
1395 LOG(VB_JOBQUEUE, LOG_INFO,
1396 "Inserted country details into database");
1401 nodeList = itemNode.elementsByTagName(
"category");
1402 if (nodeList.count() < 1)
1404 LOG(VB_JOBQUEUE, LOG_ERR,
"No 'category' element found in XML file");
1408 n = nodeList.item(0);
1409 QDomElement e = n.toElement();
1411 QString category = e.attribute(
"category");
1413 query.
prepare(
"SELECT intid FROM videocategory "
1414 "WHERE category = :CATEGORY");
1418 categoryID = query.
value(0).toInt();
1423 query.
prepare(
"INSERT INTO videocategory (category) VALUES(:CATEGORY);");
1427 "insert videocategory", query);
1430 query.
prepare(
"SELECT intid FROM videocategory "
1431 "WHERE category = :CATEGORY");
1435 categoryID = query.
value(0).toInt();
1439 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't add category to database");
1445 query.
prepare(
"UPDATE videometadata "
1446 "SET category = :CATEGORY "
1447 "WHERE intid = :INTID;");
1448 query.
bindValue(
":CATEGORY", categoryID);
1452 "update category", query);
1454 LOG(VB_JOBQUEUE, LOG_INFO,
"Fixed the category in the database");
1457 LOG(VB_JOBQUEUE, LOG_INFO,
"Import completed OK");
1464 QDomNodeList nodeList = elem.elementsByTagName(nodeName);
1465 if (nodeList.count() < 1)
1467 LOG(VB_GENERAL, LOG_ERR,
1468 QString(
"Couldn't find a '%1' element in XML file") .arg(nodeName));
1472 QDomNode n = nodeList.item(0);
1473 QDomElement e = n.toElement();
1476 for (QDomNode node = e.firstChild(); !node.isNull();
1477 node = node.nextSibling())
1479 QDomText
t = node.toText();
1489 if ((nodeName ==
"recgroup") ||
1490 (nodeName ==
"playgroup"))
1494 else if ((nodeName ==
"recordid") ||
1495 (nodeName ==
"seriesid") ||
1496 (nodeName ==
"programid") ||
1497 (nodeName ==
"profile"))
1508 query.
prepare(
"DELETE FROM archiveitems;");
1518 "MythArchiveLastRunStart",
1524 "MythArchiveLastRunEnd",
1527 (res == 0 ?
"Success" :
"Failed"));
1541static int grabThumbnail(
const QString& inFile,
const QString& thumbList,
const QString& outFile,
int frameCount)
1544 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"grabThumbnail(): Opening '%1'")
1551 LOG(VB_JOBQUEUE, LOG_ERR,
"grabThumbnail(): Couldn't open input file" +
1557 int ret = avformat_find_stream_info(inputFC,
nullptr);
1560 LOG(VB_JOBQUEUE, LOG_ERR,
1561 QString(
"Couldn't get stream info, error #%1").arg(ret));
1566 int videostream = -1;
1571 for (
uint i = 0; i < inputFC->nb_streams; i++)
1573 AVStream *st = inputFC->streams[i];
1574 if (inputFC->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
1577 width = st->codecpar->width;
1578 height = st->codecpar->height;
1579 if (st->r_frame_rate.den && st->r_frame_rate.num)
1580 fps = av_q2d(st->r_frame_rate);
1582 fps = 1/av_q2d(st->time_base);
1587 if (videostream == -1)
1589 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't find a video stream");
1594 AVCodecContext *codecCtx = codecmap.
GetCodecContext(inputFC->streams[videostream]);
1597 const AVCodec * codec = avcodec_find_decoder(codecCtx->codec_id);
1599 if (codec ==
nullptr)
1601 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't find codec for video stream");
1606 if (avcodec_open2(codecCtx, codec,
nullptr) < 0)
1608 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't open codec for video stream");
1613 QStringList list = thumbList.split(
",", Qt::SkipEmptyParts);
1622 memset(&orig, 0,
sizeof(
AVFrame));
1623 memset(&retbuf, 0,
sizeof(
AVFrame));
1626 int bufflen = width * height * 4;
1627 auto *outputbuf =
new unsigned char[bufflen];
1631 bool frameFinished =
false;
1633 while (av_read_frame(inputFC, &pkt) >= 0)
1635 if (pkt.stream_index == videostream)
1638 if (list[thumbCount].toInt() == (
int)(frameNo / fps))
1642 avcodec_flush_buffers(codecCtx);
1643 av_frame_unref(frame);
1644 frameFinished =
false;
1645 ret = avcodec_receive_frame(codecCtx, frame);
1647 frameFinished =
true;
1648 if (ret == 0 || ret == AVERROR(EAGAIN))
1649 avcodec_send_packet(codecCtx, &pkt);
1650 bool keyFrame = (frame->flags & AV_FRAME_FLAG_KEY) != 0;
1652 while (!frameFinished || !keyFrame)
1654 av_packet_unref(&pkt);
1655 int res = av_read_frame(inputFC, &pkt);
1658 if (pkt.stream_index == videostream)
1661 av_frame_unref(frame);
1662 ret = avcodec_receive_frame(codecCtx, frame);
1664 frameFinished =
true;
1665 if (ret == 0 || ret == AVERROR(EAGAIN))
1666 avcodec_send_packet(codecCtx, &pkt);
1667 keyFrame = (frame->flags & AV_FRAME_FLAG_KEY) != 0;
1674 QString saveFormat =
"JPEG";
1675 if (outFile.right(4) ==
".png")
1679 while (count < frameCount)
1687 av_image_fill_arrays(retbuf.data, retbuf.linesize, outputbuf,
1688 AV_PIX_FMT_RGB32, width, height, IMAGE_ALIGN);
1693 copyframe.
Copy(&retbuf, AV_PIX_FMT_RGB32,
tmp,
1694 codecCtx->pix_fmt, width, height);
1696 QImage img(outputbuf, width, height,
1697 QImage::Format_RGB32);
1699 if (!img.save(
filename, qPrintable(saveFormat)))
1701 LOG(VB_GENERAL, LOG_ERR,
1702 QString(
"grabThumbnail(): Failed to save "
1709 if (count <= frameCount)
1712 frameFinished =
false;
1713 while (!frameFinished)
1715 int res = av_read_frame(inputFC, &pkt);
1718 if (pkt.stream_index == videostream)
1721 ret = avcodec_receive_frame(codecCtx, frame);
1723 frameFinished =
true;
1724 if (ret == 0 || ret == AVERROR(EAGAIN))
1725 avcodec_send_packet(codecCtx, &pkt);
1732 if (thumbCount >= list.count())
1737 av_packet_unref(&pkt);
1752 LOG(VB_JOBQUEUE, LOG_INFO,
"Calculating frame count");
1754 AVPacket *pkt = av_packet_alloc();
1757 LOG(VB_GENERAL, LOG_ERR,
"packet allocation failed");
1760 while (av_read_frame(inputFC, pkt) >= 0)
1762 if (pkt->stream_index == vid_id)
1766 av_packet_unref(pkt);
1768 av_packet_free(&pkt);
1777 int pos =
filename.lastIndexOf(
'/');
1792 frm_dir_map_t::iterator it;
1793 uint64_t frames = 0;
1797 if (cutlist.empty())
1803 for (it = cutlist.begin(); it != cutlist.end();)
1812 if (it != cutlist.end())
1834 frames += end - start;
1845 int pos =
filename.lastIndexOf(
'/');
1849 int keyframedist = -1;
1857 if (!posMap.empty())
1864 if (!posMap.empty())
1867 if (fps < 26 && fps > 24)
1873 if (!posMap.empty())
1887 frm_pos_map_t::const_iterator it = posMap.cend();
1889 uint64_t totframes = it.key() * keyframedist;
1893static int getFileInfo(
const QString& inFile,
const QString& outFile,
int lenMethod)
1896 LOG(VB_JOBQUEUE , LOG_INFO, QString(
"getFileInfo(): Opening '%1'")
1903 LOG(VB_JOBQUEUE, LOG_ERR,
"getFileInfo(): Couldn't open input file" +
1909 int ret = avformat_find_stream_info(inputFC,
nullptr);
1913 LOG(VB_JOBQUEUE, LOG_ERR,
1914 QString(
"Couldn't get stream info, error #%1").arg(ret));
1919 av_dump_format(inputFC, 0, qPrintable(inFile), 0);
1921 QDomDocument doc(
"FILEINFO");
1923 QDomElement root = doc.createElement(
"file");
1924 doc.appendChild(root);
1925 root.setAttribute(
"type", inputFC->iformat->name);
1926 root.setAttribute(
"filename", inFile);
1928 QDomElement streams = doc.createElement(
"streams");
1930 root.appendChild(streams);
1931 streams.setAttribute(
"count", inputFC->nb_streams);
1932 int ffmpegIndex = 0;
1935 for (
uint i = 0; i < inputFC->nb_streams; i++)
1937 AVStream *st = inputFC->streams[i];
1938 std::string buf (256,
'\0');
1940 AVCodecParameters *par = st->codecpar;
1943 avcodec_string(buf.data(), buf.size(), avctx,
static_cast<int>(
false));
1945 switch (st->codecpar->codec_type)
1947 case AVMEDIA_TYPE_VIDEO:
1949 QStringList param = QString::fromStdString(buf).split(
',', Qt::SkipEmptyParts);
1950 QString codec = param[0].remove(
"Video:", Qt::CaseInsensitive).remove(QChar::Null);
1951 QDomElement stream = doc.createElement(
"video");
1952 stream.setAttribute(
"streamindex", i);
1953 stream.setAttribute(
"ffmpegindex", ffmpegIndex++);
1954 stream.setAttribute(
"codec", codec.trimmed());
1955 stream.setAttribute(
"width", par->width);
1956 stream.setAttribute(
"height", par->height);
1957 stream.setAttribute(
"bitrate", (qlonglong)par->bit_rate);
1960 if (st->r_frame_rate.den && st->r_frame_rate.num)
1961 fps = av_q2d(st->r_frame_rate);
1963 fps = 1/av_q2d(st->time_base);
1965 stream.setAttribute(
"fps", fps);
1967 if (par->sample_aspect_ratio.den && par->sample_aspect_ratio.num)
1969 float aspect_ratio = av_q2d(par->sample_aspect_ratio);
1970 if (QString(inputFC->iformat->name) !=
"nuv")
1971 aspect_ratio = ((float)par->width
1972 / par->height) * aspect_ratio;
1974 stream.setAttribute(
"aspectratio", aspect_ratio);
1978 stream.setAttribute(
"aspectratio",
"N/A");
1981 stream.setAttribute(
"id", st->id);
1983 if (st->start_time != (
int) AV_NOPTS_VALUE)
1985 int secs = st->start_time / AV_TIME_BASE;
1986 int us = st->start_time % AV_TIME_BASE;
1987 stream.setAttribute(
"start_time", QString(
"%1.%2")
1988 .arg(secs).arg(av_rescale(us, 1000000, AV_TIME_BASE)));
1992 stream.setAttribute(
"start_time", 0);
1995 streams.appendChild(stream);
2001 int64_t frameCount = 0;
2008 if (inputFC->duration != (
uint) AV_NOPTS_VALUE)
2010 duration = (
uint) (inputFC->duration / AV_TIME_BASE);
2011 root.setAttribute(
"duration", duration);
2012 LOG(VB_JOBQUEUE, LOG_INFO,
2013 QString(
"duration = %1") .arg(duration));
2014 frameCount = (int64_t)(duration * fps);
2018 root.setAttribute(
"duration",
"N/A");
2026 LOG(VB_JOBQUEUE, LOG_INFO,
2027 QString(
"frames = %1").arg(frameCount));
2028 duration = (
uint)(frameCount / fps);
2029 LOG(VB_JOBQUEUE, LOG_INFO,
2030 QString(
"duration = %1").arg(duration));
2031 root.setAttribute(
"duration", duration);
2041 LOG(VB_JOBQUEUE, LOG_INFO,
2042 QString(
"frames = %1").arg(frameCount));
2043 duration = (
uint)(frameCount / fps);
2044 LOG(VB_JOBQUEUE, LOG_INFO,
2045 QString(
"duration = %1").arg(duration));
2046 root.setAttribute(
"duration", duration);
2048 else if (inputFC->duration != (
uint) AV_NOPTS_VALUE)
2050 duration = (
uint) (inputFC->duration / AV_TIME_BASE);
2051 root.setAttribute(
"duration", duration);
2052 LOG(VB_JOBQUEUE, LOG_INFO,
2053 QString(
"duration = %1").arg(duration));
2054 frameCount = (int64_t)(duration * fps);
2058 root.setAttribute(
"duration",
"N/A");
2063 root.setAttribute(
"duration",
"N/A");
2064 LOG(VB_JOBQUEUE, LOG_ERR,
2065 QString(
"Unknown lenMethod (%1)")
2071 LOG(VB_JOBQUEUE, LOG_INFO,
2072 QString(
"cutframes = %1").arg(cutFrames));
2073 int cutduration = (int)(cutFrames / fps);
2074 LOG(VB_JOBQUEUE, LOG_INFO,
2075 QString(
"cutduration = %1").arg(cutduration));
2076 root.setAttribute(
"cutduration", duration - cutduration);
2082 case AVMEDIA_TYPE_AUDIO:
2084 QStringList param = QString::fromStdString(buf).split(
',', Qt::SkipEmptyParts);
2085 QString codec = param[0].remove(
"Audio:", Qt::CaseInsensitive).remove(QChar::Null);
2087 QDomElement stream = doc.createElement(
"audio");
2088 stream.setAttribute(
"streamindex", i);
2089 stream.setAttribute(
"ffmpegindex", ffmpegIndex++);
2093 if (codec.trimmed().toLower() ==
"liba52")
2094 stream.setAttribute(
"codec",
"AC3");
2096 stream.setAttribute(
"codec", codec.trimmed());
2098 stream.setAttribute(
"channels", par->ch_layout.nb_channels);
2100 AVDictionaryEntry *metatag =
2101 av_dict_get(st->metadata,
"language",
nullptr, 0);
2103 stream.setAttribute(
"language", metatag->value);
2105 stream.setAttribute(
"language",
"N/A");
2107 stream.setAttribute(
"id", st->id);
2109 stream.setAttribute(
"samplerate", par->sample_rate);
2110 stream.setAttribute(
"bitrate", (qlonglong)par->bit_rate);
2112 if (st->start_time != (
int) AV_NOPTS_VALUE)
2114 int secs = st->start_time / AV_TIME_BASE;
2115 int us = st->start_time % AV_TIME_BASE;
2116 stream.setAttribute(
"start_time", QString(
"%1.%2")
2117 .arg(secs).arg(av_rescale(us, 1000000, AV_TIME_BASE)));
2121 stream.setAttribute(
"start_time", 0);
2124 streams.appendChild(stream);
2129 case AVMEDIA_TYPE_SUBTITLE:
2131 QStringList param = QString::fromStdString(buf).split(
',', Qt::SkipEmptyParts);
2132 QString codec = param[0].remove(
"Subtitle:", Qt::CaseInsensitive).remove(QChar::Null);
2134 QDomElement stream = doc.createElement(
"subtitle");
2135 stream.setAttribute(
"streamindex", i);
2136 stream.setAttribute(
"ffmpegindex", ffmpegIndex++);
2137 stream.setAttribute(
"codec", codec.trimmed());
2139 AVDictionaryEntry *metatag =
2140 av_dict_get(st->metadata,
"language",
nullptr, 0);
2142 stream.setAttribute(
"language", metatag->value);
2144 stream.setAttribute(
"language",
"N/A");
2146 stream.setAttribute(
"id", st->id);
2148 streams.appendChild(stream);
2153 case AVMEDIA_TYPE_DATA:
2155 QDomElement stream = doc.createElement(
"data");
2156 stream.setAttribute(
"streamindex", i);
2157 stream.setAttribute(
"codec", QString::fromStdString(buf).remove(QChar::Null));
2158 streams.appendChild(stream);
2164 LOG(VB_JOBQUEUE, LOG_ERR,
2165 QString(
"Skipping unsupported codec %1 on stream %2")
2166 .arg(inputFC->streams[i]->codecpar->codec_type).arg(i));
2174 if (!f.open(QIODevice::WriteOnly))
2176 LOG(VB_JOBQUEUE, LOG_ERR,
2177 "Failed to open file for writing - " + outFile);
2182 t << doc.toString(4);
2194 if (!f.open(QIODevice::WriteOnly))
2196 LOG(VB_GENERAL, LOG_ERR,
2197 QString(
"MythArchiveHelper: Failed to open file for writing - %1")
2216 if (
filename.startsWith(
"myth://"))
2246 add(QStringList{
"-t",
"--createthumbnail"},
2247 "createthumbnail",
false,
2248 "Create one or more thumbnails\n"
2249 "Requires: --infile, --thumblist, --outfile\n"
2250 "Optional: --framecount",
"");
2251 add(
"--infile",
"infile",
"",
2253 "Used with: --createthumbnail, --getfileinfo, --isremote, "
2254 "--sup2dast, --importarchive",
"");
2255 add(
"--outfile",
"outfile",
"",
2256 "Output file name\n"
2257 "Used with: --createthumbnail, --getfileinfo, --getdbparameters, "
2259 "When used with --createthumbnail: eg 'thumb%1-%2.jpg'\n"
2260 " %1 will be replaced with the no. of the thumb\n"
2261 " %2 will be replaced with the frame no.",
"");
2262 add(
"--thumblist",
"thumblist",
"",
2263 "Comma-separated list of required thumbs (in seconds)\n"
2264 "Used with: --createthumbnail",
"");
2265 add(
"--framecount",
"framecount", 1,
2266 "Number of frames to grab (default 1)\n"
2267 "Used with: --createthumbnail",
"");
2269 add(QStringList{
"-i",
"--getfileinfo"},
2270 "getfileinfo",
false,
2271 "Write file info about infile to outfile\n"
2272 "Requires: --infile, --outfile, --method",
"");
2273 add(
"--method",
"method", 0,
2274 "Method of file duration calculation\n"
2275 "Used with: --getfileinfo\n"
2276 " 0 = use av_estimate_timings() (quick but not very accurate - "
2278 " 1 = read all frames (most accurate but slow)\n"
2279 " 2 = use position map in DB (quick, only works for MythTV "
2282 add(QStringList{
"-p",
"--getdbparameters"},
2283 "getdbparameters",
false,
2284 "Write the mysql database parameters to outfile\n"
2285 "Requires: --outfile",
"");
2287 add(QStringList{
"-n",
"--nativearchive"},
2288 "nativearchive",
false,
2289 "Archive files to a native archive format\n"
2290 "Requires: --outfile",
"");
2292 add(QStringList{
"-f",
"--importarchive"},
2293 "importarchive",
false,
2294 "Import an archived file\n"
2295 "Requires: --infile, --chanid",
"");
2296 add(
"--chanid",
"chanid", -1,
2297 "Channel ID to use when inserting records in DB\n"
2298 "Used with: --importarchive",
"");
2300 add(QStringList{
"-r",
"--isremote"},
2302 "Check if infile is on a remote filesystem\n"
2303 "Requires: --infile\n"
2304 "Returns: 0 on error or file not found\n"
2305 " - 1 file is on a local filesystem\n"
2306 " - 2 file is on a remote filesystem",
"");
2308 add(QStringList{
"-b",
"--burndvd"},
2310 "Burn a created DVD to a blank disc\n"
2311 "Optional: --mediatype, --erasedvdrw, --nativeformat",
"");
2312 add(
"--mediatype",
"mediatype", 0,
2313 "Type of media to burn\n"
2314 "Used with: --burndvd\n"
2315 " 0 = single layer DVD (default)\n"
2316 " 1 = dual layer DVD\n"
2317 " 2 = rewritable DVD",
"");
2318 add(
"--erasedvdrw",
"erasedvdrw",
false,
2319 "Force an erase of DVD-R/W Media\n"
2320 "Used with: --burndvd (optional)",
"");
2321 add(
"--nativeformat",
"nativeformat",
false,
2322 "Archive is a native archive format\n"
2323 "Used with: --burndvd (optional)",
"");
2325 add(QStringList{
"-s",
"--sup2dast"},
2327 "Convert projectX subtitles to DVD subtitles\n"
2328 "Requires: --infile, --ifofile, --delay",
"");
2329 add(
"--ifofile",
"ifofile",
"",
2330 "Filename of ifo file\n"
2331 "Used with: --sup2dast",
"");
2332 add(
"--delay",
"delay", 0,
2333 "Delay in ms to add to subtitles (default 0)\n"
2334 "Used with: --sup2dast",
"");
2360 QCoreApplication a(argc, argv);
2361 QCoreApplication::setApplicationName(
"mytharchivehelper");
2364 QString mask(
"jobqueue");
2374 if (!context.Init(
false))
2376 LOG(VB_GENERAL, LOG_ERR,
"Failed to init MythContext, exiting.");
2406 if (inFile.isEmpty())
2408 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile in -t/--grabthumbnail "
2413 if (thumbList.isEmpty())
2415 LOG(VB_GENERAL, LOG_ERR,
"Missing --thumblist in -t/--grabthumbnail"
2420 if (outFile.isEmpty())
2422 LOG(VB_GENERAL, LOG_ERR,
"Missing --outfile in -t/--grabthumbnail "
2428 if (bGetDBParameters)
2430 if (outFile.isEmpty())
2432 LOG(VB_GENERAL, LOG_ERR,
"Missing argument to -p/--getdbparameters "
2440 if (inFile.isEmpty())
2442 LOG(VB_GENERAL, LOG_ERR,
2443 "Missing argument to -r/--isremote option");
2450 if (mediaType < 0 || mediaType > 2)
2452 LOG(VB_GENERAL, LOG_ERR, QString(
"Invalid mediatype given: %1")
2460 if (outFile.isEmpty())
2462 LOG(VB_GENERAL, LOG_ERR,
"Missing argument to -n/--nativearchive "
2470 if (inFile.isEmpty())
2472 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile argument to "
2473 "-f/--importarchive option");
2480 if (inFile.isEmpty())
2482 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile in -i/--getfileinfo "
2487 if (outFile.isEmpty())
2489 LOG(VB_GENERAL, LOG_ERR,
"Missing --outfile in -i/--getfileinfo "
2497 if (inFile.isEmpty())
2499 LOG(VB_GENERAL, LOG_ERR,
2500 "Missing --infile in -s/--sup2dast option");
2504 if (ifoFile.isEmpty())
2506 LOG(VB_GENERAL, LOG_ERR,
2507 "Missing --ifofile in -s/--sup2dast option");
2513 res =
grabThumbnail(inFile, thumbList, outFile, frameCount);
2514 else if (bGetDBParameters)
2516 else if (bNativeArchive)
2518 else if (bImportArchive)
2520 else if (bGetFileInfo)
2525 res =
doBurnDVD(mediaType, bEraseDVDRW, bNativeFormat);
2528 QByteArray inFileBA = inFile.toLocal8Bit();
2529 QByteArray ifoFileBA = ifoFile.toLocal8Bit();
2530 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)
MythCommFlagCommandLineParser cmdline
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.
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)