33 #if defined(Q_OS_DARWIN) or defined(__FreeBSD__)
34 #include <sys/param.h>
35 #include <sys/mount.h>
36 #elif defined(__linux__)
41 #include <QApplication>
43 #include <QDomElement>
47 #include <QMutexLocker>
48 #include <QRegularExpression>
49 #include <QTextStream>
52 #include <mythconfig.h>
66 #include <libmythbase/mythversion.h>
71 #include <libavcodec/avcodec.h>
72 #include <libavformat/avformat.h>
73 #include <libavutil/imgutils.h>
78 #include "../mytharchive/archiveutil.h"
79 #include "../mytharchive/remoteavformatcontext.h"
89 static bool copyFile(
const QString &source,
const QString &destination);
91 const QString &xmlFile,
int chanID);
92 static int importVideo(
const QDomElement &itemNode,
const QString &xmlFile);
93 static int exportRecording(QDomElement &itemNode,
const QString &saveDirectory);
94 static int exportVideo(QDomElement &itemNode,
const QString &saveDirectory);
96 static QString
findNodeText(
const QDomElement &elem,
const QString &nodeName);
97 static int getFieldList(QStringList &fieldList,
const QString &tableName);
104 QFile
file(tempDir +
"/logs/mythburn.lck");
106 if (!
file.open(QIODevice::WriteOnly | QIODevice::Truncate))
107 LOG(VB_GENERAL, LOG_ERR,
"NativeArchive: Failed to create lock file");
109 QString pid = QString(
"%1").arg(getpid());
110 file.write(pid.toLatin1());
119 QFile::remove(tempDir +
"/logs/mythburn.lck");
124 QString command = QString(
"mythutil --copyfile --infile '%1' --outfile '%2'")
125 .arg(source, destination);
129 LOG(VB_JOBQUEUE, LOG_ERR,
130 QString(
"Failed while running %1. Result: %2").arg(command).arg(res));
139 LOG(VB_JOBQUEUE, LOG_INFO,
"Creating ISO image");
143 tempDirectory +=
"work/";
146 QString command = mkisofs +
" -R -J -V 'MythTV Archive' -o ";
147 command += tempDirectory +
"mythburn.iso " + sourceDirectory;
152 LOG(VB_JOBQUEUE, LOG_ERR,
153 QString(
"Failed while running mkisofs. Result: %1") .arg(res));
157 LOG(VB_JOBQUEUE, LOG_INFO,
"Finished creating ISO image");
161 static int burnISOImage(
int mediaType,
bool bEraseDVDRW,
bool nativeFormat)
165 LOG(VB_JOBQUEUE, LOG_INFO,
"Burning ISO image to " + dvdDrive);
170 tempDirectory +=
"work/";
176 command +=
" -speed=" + QString::number(driveSpeed);
180 if (mediaType ==
AD_DVD_RW && bEraseDVDRW)
182 command +=
" -use-the-force-luke -Z " + dvdDrive;
183 command +=
" -V 'MythTV Archive' -R -J " + tempDirectory;
187 command +=
" -Z " + dvdDrive;
188 command +=
" -V 'MythTV Archive' -R -J " + tempDirectory;
193 if (mediaType ==
AD_DVD_RW && bEraseDVDRW)
195 command +=
" -dvd-compat -use-the-force-luke -Z " + dvdDrive;
196 command +=
" -dvd-video -V 'MythTV DVD' " + tempDirectory +
"/dvd";
200 command +=
" -dvd-compat -Z " + dvdDrive;
201 command +=
" -dvd-video -V 'MythTV DVD' " + tempDirectory +
"/dvd";
208 LOG(VB_JOBQUEUE, LOG_ERR,
209 QString(
"Failed while running growisofs. Result: %1") .arg(res));
213 LOG(VB_JOBQUEUE, LOG_INFO,
"Finished burning ISO image");
219 static int doBurnDVD(
int mediaType,
bool bEraseDVDRW,
bool nativeFormat)
222 "MythArchiveLastRunStart",
226 int res =
burnISOImage(mediaType, bEraseDVDRW, nativeFormat);
229 "MythArchiveLastRunEnd",
239 QDomDocument doc(
"archivejob");
241 if (!
file.open(QIODevice::ReadOnly))
243 LOG(VB_JOBQUEUE, LOG_ERR,
"Could not open job file: " + jobFile);
247 if (!doc.setContent(&
file))
249 LOG(VB_JOBQUEUE, LOG_ERR,
"Could not load job file: " + jobFile);
257 bool bCreateISO =
false;
258 bool bEraseDVDRW =
false;
259 bool bDoBurn =
false;
260 QString saveDirectory;
263 QDomNodeList nodeList = doc.elementsByTagName(
"options");
264 if (nodeList.count() == 1)
266 QDomNode node = nodeList.item(0);
267 QDomElement
options = node.toElement();
270 bCreateISO = (
options.attribute(
"createiso",
"0") ==
"1");
271 bEraseDVDRW = (
options.attribute(
"erasedvdrw",
"0") ==
"1");
272 bDoBurn = (
options.attribute(
"doburn",
"0") ==
"1");
273 mediaType =
options.attribute(
"mediatype",
"0").toInt();
274 saveDirectory =
options.attribute(
"savedirectory",
"");
275 if (!saveDirectory.endsWith(
"/"))
276 saveDirectory +=
"/";
281 LOG(VB_JOBQUEUE, LOG_ERR,
282 QString(
"Found %1 options nodes - should be 1")
283 .arg(nodeList.count()));
286 LOG(VB_JOBQUEUE, LOG_INFO,
287 QString(
"Options - createiso: %1,"
288 " doburn: %2, mediatype: %3, erasedvdrw: %4")
289 .arg(bCreateISO).arg(bDoBurn).arg(mediaType).arg(bEraseDVDRW));
290 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"savedirectory: %1").arg(saveDirectory));
295 saveDirectory = tempDir;
296 if (!saveDirectory.endsWith(
"/"))
297 saveDirectory +=
"/";
299 saveDirectory +=
"work/";
301 QDir dir(saveDirectory);
305 LOG(VB_GENERAL, LOG_ERR,
306 "NativeArchive: Failed to clear work directory");
308 dir.mkpath(saveDirectory);
311 LOG(VB_JOBQUEUE, LOG_INFO,
312 QString(
"Saving files to : %1").arg(saveDirectory));
315 nodeList = doc.elementsByTagName(
"file");
316 if (nodeList.count() < 1)
318 LOG(VB_JOBQUEUE, LOG_ERR,
"Cannot find any file nodes?");
327 for (
int x = 0; x < nodeList.count(); x++)
329 node = nodeList.item(x);
330 elem = node.toElement();
333 type = elem.attribute(
"type");
335 if (
type.toLower() ==
"recording")
337 else if (
type.toLower() ==
"video")
341 LOG(VB_JOBQUEUE, LOG_ERR,
342 QString(
"Don't know how to archive items of type '%1'")
343 .arg(
type.toLower()));
350 if (mediaType !=
AD_FILE && bDoBurn)
354 LOG(VB_JOBQUEUE, LOG_ERR,
355 "Native archive job failed to complete");
365 LOG(VB_JOBQUEUE, LOG_ERR,
"Native archive job failed to complete");
370 LOG(VB_JOBQUEUE, LOG_INFO,
"Native archive job completed OK");
375 static const QRegularExpression
badChars { R
"((/|\\|:|'|"|\?|\|))" };
389 if (query.
exec(
"DESCRIBE " + tableName))
393 fieldList.append(query.
value(0).toString());
401 return fieldList.count();
405 const QString &saveDirectory)
411 QString title =
fixFilename(itemNode.attribute(
"title"));
412 QString
filename = itemNode.attribute(
"filename");
413 bool doDelete = (itemNode.attribute(
"delete",
"0") ==
"0");
414 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"Archiving %1 (%2), do delete: %3")
415 .arg(title,
filename, doDelete ?
"true" :
"false"));
419 LOG(VB_JOBQUEUE, LOG_ERR,
"Bad title or filename");
425 LOG(VB_JOBQUEUE, LOG_ERR,
426 QString(
"Failed to extract chanID and startTime from '%1'")
432 QDir dir(saveDirectory + title);
434 dir.mkpath(saveDirectory + title);
436 LOG(VB_GENERAL, LOG_ERR,
"Failed to create savedir: " +
ENO);
438 LOG(VB_JOBQUEUE, LOG_INFO,
"Creating xml file for " + title);
439 QDomDocument doc(
"MYTHARCHIVEITEM");
441 QDomElement root = doc.createElement(
"item");
442 doc.appendChild(root);
443 root.setAttribute(
"type",
"recording");
444 root.setAttribute(
"databaseversion",
dbVersion);
446 QDomElement recorded = doc.createElement(
"recorded");
447 root.appendChild(recorded);
450 QStringList fieldList;
454 query.
prepare(
"SELECT " + fieldList.join(
",")
456 " WHERE chanid = :CHANID and starttime = :STARTTIME;");
458 query.
bindValue(
":STARTTIME", startTime);
465 for (
int x = 0; x < fieldList.size(); x++)
467 elem = doc.createElement(fieldList[x]);
468 text = doc.createTextNode(query.
value(x).toString());
469 elem.appendChild(text);
470 recorded.appendChild(elem);
473 LOG(VB_JOBQUEUE, LOG_INFO,
"Created recorded element for " + title);
477 LOG(VB_JOBQUEUE, LOG_INFO,
"Failed to get recorded field list");
481 query.
prepare(
"SELECT chanid, channum, callsign, name "
482 "FROM channel WHERE chanid = :CHANID;");
487 QDomElement channel = doc.createElement(
"channel");
488 channel.setAttribute(
"chanid", query.
value(0).toString());
489 channel.setAttribute(
"channum", query.
value(1).toString());
490 channel.setAttribute(
"callsign", query.
value(2).toString());
491 channel.setAttribute(
"name", query.
value(3).toString());
492 root.appendChild(channel);
493 LOG(VB_JOBQUEUE, LOG_INFO,
"Created channel element for " + title);
498 LOG(VB_JOBQUEUE, LOG_ERR,
499 "Cannot find channel details for chanid " + chanID);
500 QDomElement channel = doc.createElement(
"channel");
501 channel.setAttribute(
"chanid", chanID);
502 channel.setAttribute(
"channum",
"unknown");
503 channel.setAttribute(
"callsign",
"unknown");
504 channel.setAttribute(
"name",
"unknown");
505 root.appendChild(channel);
506 LOG(VB_JOBQUEUE, LOG_INFO,
507 "Created a default channel element for " + title);
511 query.
prepare(
"SELECT credits.person, role, people.name "
512 "FROM recordedcredits AS credits "
513 "LEFT JOIN people ON credits.person = people.person "
514 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
516 query.
bindValue(
":STARTTIME", startTime);
520 QDomElement credits = doc.createElement(
"credits");
523 QDomElement credit = doc.createElement(
"credit");
524 credit.setAttribute(
"personid", query.
value(0).toString());
525 credit.setAttribute(
"name", query.
value(2).toString());
526 credit.setAttribute(
"role", query.
value(1).toString());
527 credits.appendChild(credit);
529 root.appendChild(credits);
530 LOG(VB_JOBQUEUE, LOG_INFO,
"Created credits element for " + title);
534 query.
prepare(
"SELECT `system`, rating FROM recordedrating "
535 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
537 query.
bindValue(
":STARTTIME", startTime);
541 QDomElement
rating = doc.createElement(
"rating");
542 rating.setAttribute(
"system", query.
value(0).toString());
543 rating.setAttribute(
"rating", query.
value(1).toString());
545 LOG(VB_JOBQUEUE, LOG_INFO,
"Created rating element for " + title);
549 QDomElement recordedmarkup = doc.createElement(
"recordedmarkup");
550 query.
prepare(
"SELECT chanid, starttime, mark, type, data "
551 "FROM recordedmarkup "
552 "WHERE chanid = :CHANID and starttime = :STARTTIME;");
554 query.
bindValue(
":STARTTIME", startTime);
559 QDomElement mark = doc.createElement(
"mark");
560 mark.setAttribute(
"mark", query.
value(2).toString());
561 mark.setAttribute(
"type", query.
value(3).toString());
562 mark.setAttribute(
"data", query.
value(4).toString());
563 recordedmarkup.appendChild(mark);
565 root.appendChild(recordedmarkup);
566 LOG(VB_JOBQUEUE, LOG_INFO,
"Created recordedmarkup element for " + title);
570 QDomElement recordedseek = doc.createElement(
"recordedseek");
571 query.
prepare(
"SELECT chanid, starttime, mark, `offset`, type "
573 "WHERE chanid = :CHANID and starttime = :STARTTIME;");
575 query.
bindValue(
":STARTTIME", startTime);
580 QDomElement mark = doc.createElement(
"mark");
581 mark.setAttribute(
"mark", query.
value(2).toString());
582 mark.setAttribute(
"offset", query.
value(3).toString());
583 mark.setAttribute(
"type", query.
value(4).toString());
584 recordedseek.appendChild(mark);
586 root.appendChild(recordedseek);
587 LOG(VB_JOBQUEUE, LOG_INFO,
588 "Created recordedseek element for " + title);
593 QString xmlFile = saveDirectory + title +
"/" + baseName +
".xml";
595 if (!f.open(QIODevice::WriteOnly))
597 LOG(VB_JOBQUEUE, LOG_ERR,
598 "MythNativeWizard: Failed to open file for writing - " + xmlFile);
603 t << doc.toString(4);
607 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
615 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying preview image");
617 + title +
"/" + baseName +
".png");
622 LOG(VB_JOBQUEUE, LOG_INFO,
"Item Archived OK");
628 const QString &saveDirectory)
633 QString coverFile =
"";
635 QString title =
fixFilename(itemNode.attribute(
"title"));
636 QString
filename = itemNode.attribute(
"filename");
637 bool doDelete = (itemNode.attribute(
"delete",
"0") ==
"0");
638 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"Archiving %1 (%2), do delete: %3")
639 .arg(title,
filename, doDelete ?
"true" :
"false"));
643 LOG(VB_JOBQUEUE, LOG_ERR,
"Bad title or filename");
648 QDir dir(saveDirectory + title);
650 dir.mkdir(saveDirectory + title);
652 LOG(VB_JOBQUEUE, LOG_INFO,
"Creating xml file for " + title);
653 QDomDocument doc(
"MYTHARCHIVEITEM");
655 QDomElement root = doc.createElement(
"item");
656 doc.appendChild(root);
657 root.setAttribute(
"type",
"video");
658 root.setAttribute(
"databaseversion",
dbVersion);
660 QDomElement video = doc.createElement(
"videometadata");
661 root.appendChild(video);
665 query.
prepare(
"SELECT intid, title, director, plot, rating, inetref, "
666 "year, userrating, length, showlevel, filename, coverfile, "
667 "childid, browse, playcommand, category "
668 "FROM videometadata WHERE filename = :FILENAME;");
676 elem = doc.createElement(
"intid");
677 text = doc.createTextNode(query.
value(0).toString());
678 intID = query.
value(0).toInt();
679 elem.appendChild(text);
680 video.appendChild(elem);
682 elem = doc.createElement(
"title");
683 text = doc.createTextNode(query.
value(1).toString());
684 elem.appendChild(text);
685 video.appendChild(elem);
687 elem = doc.createElement(
"director");
688 text = doc.createTextNode(query.
value(2).toString());
689 elem.appendChild(text);
690 video.appendChild(elem);
692 elem = doc.createElement(
"plot");
693 text = doc.createTextNode(query.
value(3).toString());
694 elem.appendChild(text);
695 video.appendChild(elem);
697 elem = doc.createElement(
"rating");
698 text = doc.createTextNode(query.
value(4).toString());
699 elem.appendChild(text);
700 video.appendChild(elem);
702 elem = doc.createElement(
"inetref");
703 text = doc.createTextNode(query.
value(5).toString());
704 elem.appendChild(text);
705 video.appendChild(elem);
707 elem = doc.createElement(
"year");
708 text = doc.createTextNode(query.
value(6).toString());
709 elem.appendChild(text);
710 video.appendChild(elem);
712 elem = doc.createElement(
"userrating");
713 text = doc.createTextNode(query.
value(7).toString());
714 elem.appendChild(text);
715 video.appendChild(elem);
717 elem = doc.createElement(
"length");
718 text = doc.createTextNode(query.
value(8).toString());
719 elem.appendChild(text);
720 video.appendChild(elem);
722 elem = doc.createElement(
"showlevel");
723 text = doc.createTextNode(query.
value(9).toString());
724 elem.appendChild(text);
725 video.appendChild(elem);
728 QString fname = query.
value(10).toString();
732 elem = doc.createElement(
"filename");
733 text = doc.createTextNode(fname);
734 elem.appendChild(text);
735 video.appendChild(elem);
737 elem = doc.createElement(
"coverfile");
738 text = doc.createTextNode(query.
value(11).toString());
739 coverFile = query.
value(11).toString();
740 elem.appendChild(text);
741 video.appendChild(elem);
743 elem = doc.createElement(
"childid");
744 text = doc.createTextNode(query.
value(12).toString());
745 elem.appendChild(text);
746 video.appendChild(elem);
748 elem = doc.createElement(
"browse");
749 text = doc.createTextNode(query.
value(13).toString());
750 elem.appendChild(text);
751 video.appendChild(elem);
753 elem = doc.createElement(
"playcommand");
754 text = doc.createTextNode(query.
value(14).toString());
755 elem.appendChild(text);
756 video.appendChild(elem);
758 elem = doc.createElement(
"categoryid");
759 text = doc.createTextNode(query.
value(15).toString());
760 categoryID = query.
value(15).toInt();
761 elem.appendChild(text);
762 video.appendChild(elem);
764 LOG(VB_JOBQUEUE, LOG_INFO,
765 "Created videometadata element for " + title);
769 query.
prepare(
"SELECT intid, category "
770 "FROM videocategory WHERE intid = :INTID;");
775 QDomElement category = doc.createElement(
"category");
776 category.setAttribute(
"intid", query.
value(0).toString());
777 category.setAttribute(
"category", query.
value(1).toString());
778 root.appendChild(category);
779 LOG(VB_JOBQUEUE, LOG_INFO,
780 "Created videocategory element for " + title);
784 QDomElement countries = doc.createElement(
"countries");
785 root.appendChild(countries);
787 query.
prepare(
"SELECT intid, country "
788 "FROM videometadatacountry INNER JOIN videocountry "
789 "ON videometadatacountry.idcountry = videocountry.intid "
790 "WHERE idvideo = :INTID;");
800 QDomElement country = doc.createElement(
"country");
801 country.setAttribute(
"intid", query.
value(0).toString());
802 country.setAttribute(
"country", query.
value(1).toString());
803 countries.appendChild(country);
805 LOG(VB_JOBQUEUE, LOG_INFO,
"Created videocountry element for " + title);
809 QDomElement genres = doc.createElement(
"genres");
810 root.appendChild(genres);
812 query.
prepare(
"SELECT intid, genre "
813 "FROM videometadatagenre INNER JOIN videogenre "
814 "ON videometadatagenre.idgenre = videogenre.intid "
815 "WHERE idvideo = :INTID;");
825 QDomElement genre = doc.createElement(
"genre");
826 genre.setAttribute(
"intid", query.
value(0).toString());
827 genre.setAttribute(
"genre", query.
value(1).toString());
828 genres.appendChild(genre);
830 LOG(VB_JOBQUEUE, LOG_INFO,
"Created videogenre element for " + title);
835 QString xmlFile = saveDirectory + title +
"/"
836 + fileInfo.fileName() +
".xml";
838 if (!f.open(QIODevice::WriteOnly))
840 LOG(VB_JOBQUEUE, LOG_INFO,
841 "MythNativeWizard: Failed to open file for writing - " + xmlFile);
846 t << doc.toString(4);
850 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
852 +
"/" + fileInfo.fileName());
859 fileInfo.setFile(coverFile);
860 if (fileInfo.exists())
862 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying cover file");
863 res =
copyFile(coverFile, saveDirectory + title
864 +
"/" + fileInfo.fileName());
871 LOG(VB_JOBQUEUE, LOG_INFO,
"Item Archived OK");
879 QDomDocument doc(
"mydocument");
881 if (!
file.open(QIODevice::ReadOnly))
883 LOG(VB_JOBQUEUE, LOG_ERR,
884 "Failed to open file for reading - " + xmlFile);
888 if (!doc.setContent(&
file))
891 LOG(VB_JOBQUEUE, LOG_ERR,
892 "Failed to read from xml file - " + xmlFile);
897 QString docType = doc.doctype().name();
900 QDomNodeList itemNodeList;
902 QDomElement itemNode;
904 if (docType ==
"MYTHARCHIVEITEM")
906 itemNodeList = doc.elementsByTagName(
"item");
908 if (itemNodeList.count() < 1)
910 LOG(VB_JOBQUEUE, LOG_ERR,
911 "Couldn't find an 'item' element in XML file");
915 node = itemNodeList.item(0);
916 itemNode = node.toElement();
917 type = itemNode.attribute(
"type");
918 dbVersion = itemNode.attribute(
"databaseversion");
920 LOG(VB_JOBQUEUE, LOG_INFO,
921 QString(
"Archive DB version: %1, Local DB version: %2")
926 LOG(VB_JOBQUEUE, LOG_ERR,
"Not a native archive xml file - " + xmlFile);
930 if (
type ==
"recording")
943 const QString &xmlFile,
int chanID)
945 LOG(VB_JOBQUEUE, LOG_INFO,
946 QString(
"Import recording using chanID: %1").arg(chanID));
947 LOG(VB_JOBQUEUE, LOG_INFO,
948 QString(
"Archived recording xml file: %1").arg(xmlFile));
950 QString videoFile = xmlFile.left(xmlFile.length() - 4);
951 QString basename = videoFile;
952 int pos = videoFile.lastIndexOf(
'/');
954 basename = videoFile.mid(pos + 1);
956 QDomNodeList nodeList = itemNode.elementsByTagName(
"recorded");
957 if (nodeList.count() < 1)
959 LOG(VB_JOBQUEUE, LOG_ERR,
960 "Couldn't find a 'recorded' element in XML file");
964 QDomNode n = nodeList.item(0);
965 QDomElement recordedNode = n.toElement();
966 QString startTime =
findNodeText(recordedNode,
"starttime");
969 query.
prepare(
"SELECT * FROM recorded "
970 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
972 query.
bindValue(
":STARTTIME", startTime);
977 LOG(VB_JOBQUEUE, LOG_ERR,
978 "This recording appears to already exist!!");
985 basename ,
"Default");
988 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file to: " + destFile);
995 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying preview image file to: " + destFile +
".png");
996 if (!
copyFile(videoFile +
".png", destFile +
".png"))
1001 QStringList fieldList;
1002 QStringList bindList;
1003 QDomNodeList nodes = recordedNode.childNodes();
1005 for (
int x = 0; x < nodes.count(); x++)
1007 QDomNode n2 = nodes.item(x);
1008 QString field = n2.nodeName();
1009 fieldList.append(field);
1010 bindList.append(
":" + field.toUpper());
1014 query.
prepare(
"INSERT INTO recorded (" + fieldList.join(
",") +
") "
1015 "VALUES (" + bindList.join(
",") +
");");
1017 query.
bindValue(
":STARTTIME", startTime);
1019 for (
int x = 0; x < fieldList.count(); x++)
1023 LOG(VB_JOBQUEUE, LOG_INFO,
"Inserted recorded details into database");
1028 nodeList = itemNode.elementsByTagName(
"recordedmarkup");
1029 if (nodeList.count() < 1)
1031 LOG(VB_JOBQUEUE, LOG_WARNING,
1032 "Couldn't find a 'recordedmarkup' element in XML file");
1036 QDomNode n3 = nodeList.item(0);
1037 QDomElement markupNode = n3.toElement();
1039 nodeList = markupNode.elementsByTagName(
"mark");
1040 if (nodeList.count() < 1)
1042 LOG(VB_JOBQUEUE, LOG_WARNING,
1043 "Couldn't find any 'mark' elements in XML file");
1048 query.
prepare(
"DELETE FROM recordedmarkup "
1049 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
1051 query.
bindValue(
":STARTTIME", startTime);
1057 for (
int x = 0; x < nodeList.count(); x++)
1059 QDomNode n4 = nodeList.item(x);
1060 QDomElement e = n4.toElement();
1061 query.
prepare(
"INSERT INTO recordedmarkup (chanid, starttime, "
1063 "VALUES(:CHANID,:STARTTIME,:MARK,:TYPE,:DATA);");
1065 query.
bindValue(
":STARTTIME", startTime);
1066 query.
bindValue(
":MARK", e.attribute(
"mark"));
1067 query.
bindValue(
":TYPE", e.attribute(
"type"));
1068 query.
bindValue(
":DATA", e.attribute(
"data"));
1077 LOG(VB_JOBQUEUE, LOG_INFO,
1078 "Inserted recordedmarkup details into database");
1083 nodeList = itemNode.elementsByTagName(
"recordedseek");
1084 if (nodeList.count() < 1)
1086 LOG(VB_JOBQUEUE, LOG_WARNING,
1087 "Couldn't find a 'recordedseek' element in XML file");
1091 QDomNode n5 = nodeList.item(0);
1092 QDomElement markupNode = n5.toElement();
1094 nodeList = markupNode.elementsByTagName(
"mark");
1095 if (nodeList.count() < 1)
1097 LOG(VB_JOBQUEUE, LOG_WARNING,
1098 "Couldn't find any 'mark' elements in XML file");
1103 query.
prepare(
"DELETE FROM recordedseek "
1104 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
1106 query.
bindValue(
":STARTTIME", startTime);
1110 for (
int x = 0; x < nodeList.count(); x++)
1112 QDomNode n6 = nodeList.item(x);
1113 QDomElement e = n6.toElement();
1114 query.
prepare(
"INSERT INTO recordedseek (chanid, starttime, "
1115 "mark, `offset`, type)"
1116 "VALUES(:CHANID,:STARTTIME,:MARK,:OFFSET,:TYPE);");
1118 query.
bindValue(
":STARTTIME", startTime);
1119 query.
bindValue(
":MARK", e.attribute(
"mark"));
1120 query.
bindValue(
":OFFSET", e.attribute(
"offset"));
1121 query.
bindValue(
":TYPE", e.attribute(
"type"));
1130 LOG(VB_JOBQUEUE, LOG_INFO,
1131 "Inserted recordedseek details into database");
1139 LOG(VB_JOBQUEUE, LOG_INFO,
"Import completed OK");
1146 LOG(VB_JOBQUEUE, LOG_INFO,
"Importing video");
1147 LOG(VB_JOBQUEUE, LOG_INFO,
1148 QString(
"Archived video xml file: %1").arg(xmlFile));
1150 QString videoFile = xmlFile.left(xmlFile.length() - 4);
1151 QFileInfo fileInfo(videoFile);
1152 QString basename = fileInfo.fileName();
1154 QDomNodeList nodeList = itemNode.elementsByTagName(
"videometadata");
1155 if (nodeList.count() < 1)
1157 LOG(VB_JOBQUEUE, LOG_ERR,
1158 "Couldn't find a 'videometadata' element in XML file");
1162 QDomNode n = nodeList.item(0);
1163 QDomElement videoNode = n.toElement();
1167 QString origFilename =
findNodeText(videoNode,
"filename");
1168 QStringList dirList = origFilename.split(
"/", Qt::SkipEmptyParts);
1170 for (
int x = 0; x < dirList.count() - 1; x++)
1172 path +=
"/" + dirList[x];
1173 if (!dir.exists(path))
1175 if (!dir.mkdir(path))
1177 LOG(VB_JOBQUEUE, LOG_ERR,
1178 QString(
"Couldn't create directory '%1'").arg(path));
1184 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
1185 if (!
copyFile(videoFile, path +
"/" + basename))
1193 fileInfo.setFile(videoFile);
1194 QString archivePath = fileInfo.absolutePath();
1196 QString coverFilename =
findNodeText(videoNode,
"coverfile");
1197 fileInfo.setFile(coverFilename);
1198 coverFilename = fileInfo.fileName();
1200 fileInfo.setFile(archivePath +
"/" + coverFilename);
1201 if (fileInfo.exists())
1203 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying cover file");
1205 if (!
copyFile(archivePath +
"/" + coverFilename, artworkDir +
"/" + coverFilename))
1212 coverFilename =
"No Cover";
1217 query.
prepare(
"INSERT INTO videometadata (title, director, plot, rating, inetref, "
1218 "year, userrating, length, showlevel, filename, coverfile, "
1219 "childid, browse, playcommand, category) "
1220 "VALUES(:TITLE,:DIRECTOR,:PLOT,:RATING,:INETREF,:YEAR,"
1221 ":USERRATING,:LENGTH,:SHOWLEVEL,:FILENAME,:COVERFILE,"
1222 ":CHILDID,:BROWSE,:PLAYCOMMAND,:CATEGORY);");
1232 query.
bindValue(
":FILENAME", path +
"/" + basename);
1233 query.
bindValue(
":COVERFILE", artworkDir +
"/" + coverFilename);
1241 LOG(VB_JOBQUEUE, LOG_INFO,
1242 "Inserted videometadata details into database");
1252 query.
prepare(
"SELECT intid FROM videometadata WHERE filename = :FILENAME;");
1253 query.
bindValue(
":FILENAME", path +
"/" + basename);
1256 intid = query.
value(0).toInt();
1264 LOG(VB_JOBQUEUE, LOG_INFO,
1265 QString(
"'intid' of inserted video is: %1").arg(intid));
1268 nodeList = itemNode.elementsByTagName(
"genres");
1269 if (nodeList.count() < 1)
1271 LOG(VB_JOBQUEUE, LOG_ERR,
"No 'genres' element found in XML file");
1275 n = nodeList.item(0);
1276 QDomElement genresNode = n.toElement();
1278 nodeList = genresNode.elementsByTagName(
"genre");
1279 if (nodeList.count() < 1)
1281 LOG(VB_JOBQUEUE, LOG_WARNING,
1282 "Couldn't find any 'genre' elements in XML file");
1286 for (
int x = 0; x < nodeList.count(); x++)
1288 n = nodeList.item(x);
1289 QDomElement e = n.toElement();
1291 QString genre = e.attribute(
"genre");
1294 query.
prepare(
"SELECT intid FROM videogenre "
1295 "WHERE genre = :GENRE");
1299 genreID = query.
value(0).toInt();
1304 query.
prepare(
"INSERT INTO videogenre (genre) VALUES(:GENRE);");
1308 "insert videogenre", query);
1311 query.
prepare(
"SELECT intid FROM videogenre "
1312 "WHERE genre = :GENRE");
1314 if (!query.
exec() || !query.
next())
1316 LOG(VB_JOBQUEUE, LOG_ERR,
1317 "Couldn't add genre to database");
1320 genreID = query.
value(0).toInt();
1324 query.
prepare(
"INSERT INTO videometadatagenre (idvideo, idgenre)"
1325 "VALUES (:IDVIDEO, :IDGENRE);");
1330 "insert videometadatagenre", query);
1333 LOG(VB_JOBQUEUE, LOG_INFO,
"Inserted genre details into database");
1338 nodeList = itemNode.elementsByTagName(
"countries");
1339 if (nodeList.count() < 1)
1341 LOG(VB_JOBQUEUE, LOG_INFO,
"No 'countries' element found in XML file");
1345 n = nodeList.item(0);
1346 QDomElement countriesNode = n.toElement();
1348 nodeList = countriesNode.elementsByTagName(
"country");
1349 if (nodeList.count() < 1)
1351 LOG(VB_JOBQUEUE, LOG_WARNING,
1352 "Couldn't find any 'country' elements in XML file");
1356 for (
int x = 0; x < nodeList.count(); x++)
1358 n = nodeList.item(x);
1359 QDomElement e = n.toElement();
1361 QString country = e.attribute(
"country");
1364 query.
prepare(
"SELECT intid FROM videocountry "
1365 "WHERE country = :COUNTRY");
1369 countryID = query.
value(0).toInt();
1374 query.
prepare(
"INSERT INTO videocountry (country) VALUES(:COUNTRY);");
1378 "insert videocountry", query);
1381 query.
prepare(
"SELECT intid FROM videocountry "
1382 "WHERE country = :COUNTRY");
1384 if (!query.
exec() || !query.
next())
1386 LOG(VB_JOBQUEUE, LOG_ERR,
1387 "Couldn't add country to database");
1390 countryID = query.
value(0).toInt();
1394 query.
prepare(
"INSERT INTO videometadatacountry (idvideo, idcountry)"
1395 "VALUES (:IDVIDEO, :IDCOUNTRY);");
1397 query.
bindValue(
":IDCOUNTRY", countryID);
1400 "insert videometadatacountry", query);
1403 LOG(VB_JOBQUEUE, LOG_INFO,
1404 "Inserted country details into database");
1409 nodeList = itemNode.elementsByTagName(
"category");
1410 if (nodeList.count() < 1)
1412 LOG(VB_JOBQUEUE, LOG_ERR,
"No 'category' element found in XML file");
1416 n = nodeList.item(0);
1417 QDomElement e = n.toElement();
1419 QString category = e.attribute(
"category");
1421 query.
prepare(
"SELECT intid FROM videocategory "
1422 "WHERE category = :CATEGORY");
1426 categoryID = query.
value(0).toInt();
1431 query.
prepare(
"INSERT INTO videocategory (category) VALUES(:CATEGORY);");
1435 "insert videocategory", query);
1438 query.
prepare(
"SELECT intid FROM videocategory "
1439 "WHERE category = :CATEGORY");
1443 categoryID = query.
value(0).toInt();
1447 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't add category to database");
1453 query.
prepare(
"UPDATE videometadata "
1454 "SET category = :CATEGORY "
1455 "WHERE intid = :INTID;");
1456 query.
bindValue(
":CATEGORY", categoryID);
1460 "update category", query);
1462 LOG(VB_JOBQUEUE, LOG_INFO,
"Fixed the category in the database");
1465 LOG(VB_JOBQUEUE, LOG_INFO,
"Import completed OK");
1472 QDomNodeList nodeList = elem.elementsByTagName(nodeName);
1473 if (nodeList.count() < 1)
1475 LOG(VB_GENERAL, LOG_ERR,
1476 QString(
"Couldn't find a '%1' element in XML file") .arg(nodeName));
1480 QDomNode n = nodeList.item(0);
1481 QDomElement e = n.toElement();
1484 for (QDomNode node = e.firstChild(); !node.isNull();
1485 node = node.nextSibling())
1487 QDomText
t = node.toText();
1497 if ((nodeName ==
"recgroup") ||
1498 (nodeName ==
"playgroup"))
1502 else if ((nodeName ==
"recordid") ||
1503 (nodeName ==
"seriesid") ||
1504 (nodeName ==
"programid") ||
1505 (nodeName ==
"profile"))
1516 query.
prepare(
"DELETE FROM archiveitems;");
1526 "MythArchiveLastRunStart",
1532 "MythArchiveLastRunEnd",
1535 (res == 0 ?
"Success" :
"Failed"));
1549 static int grabThumbnail(
const QString& inFile,
const QString& thumbList,
const QString& outFile,
int frameCount)
1552 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"grabThumbnail(): Opening '%1'")
1559 LOG(VB_JOBQUEUE, LOG_ERR,
"grabThumbnail(): Couldn't open input file" +
1565 int ret = avformat_find_stream_info(inputFC,
nullptr);
1568 LOG(VB_JOBQUEUE, LOG_ERR,
1569 QString(
"Couldn't get stream info, error #%1").arg(ret));
1574 int videostream = -1;
1579 for (
uint i = 0; i < inputFC->nb_streams; i++)
1581 AVStream *st = inputFC->streams[i];
1582 if (inputFC->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
1585 width = st->codecpar->width;
1586 height = st->codecpar->height;
1587 if (st->r_frame_rate.den && st->r_frame_rate.num)
1588 fps = av_q2d(st->r_frame_rate);
1590 fps = 1/av_q2d(st->time_base);
1595 if (videostream == -1)
1597 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't find a video stream");
1602 AVCodecContext *codecCtx = codecmap.
GetCodecContext(inputFC->streams[videostream]);
1605 const AVCodec * codec = avcodec_find_decoder(codecCtx->codec_id);
1607 if (codec ==
nullptr)
1609 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't find codec for video stream");
1614 if (avcodec_open2(codecCtx, codec,
nullptr) < 0)
1616 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't open codec for video stream");
1621 QStringList list = thumbList.split(
",", Qt::SkipEmptyParts);
1630 memset(&orig, 0,
sizeof(
AVFrame));
1631 memset(&retbuf, 0,
sizeof(
AVFrame));
1634 int bufflen = width * height * 4;
1635 auto *outputbuf =
new unsigned char[bufflen];
1639 bool frameFinished =
false;
1641 while (av_read_frame(inputFC, &pkt) >= 0)
1643 if (pkt.stream_index == videostream)
1646 if (list[thumbCount].toInt() == (
int)(frameNo / fps))
1650 avcodec_flush_buffers(codecCtx);
1651 av_frame_unref(frame);
1652 frameFinished =
false;
1653 ret = avcodec_receive_frame(codecCtx, frame);
1655 frameFinished =
true;
1656 if (ret == 0 || ret == AVERROR(EAGAIN))
1657 avcodec_send_packet(codecCtx, &pkt);
1658 bool keyFrame = (frame->flags & AV_FRAME_FLAG_KEY) != 0;
1660 while (!frameFinished || !keyFrame)
1662 av_packet_unref(&pkt);
1663 int res = av_read_frame(inputFC, &pkt);
1666 if (pkt.stream_index == videostream)
1669 av_frame_unref(frame);
1670 ret = avcodec_receive_frame(codecCtx, frame);
1672 frameFinished =
true;
1673 if (ret == 0 || ret == AVERROR(EAGAIN))
1674 avcodec_send_packet(codecCtx, &pkt);
1675 keyFrame = (frame->flags & AV_FRAME_FLAG_KEY) != 0;
1682 QString saveFormat =
"JPEG";
1683 if (outFile.right(4) ==
".png")
1687 while (count < frameCount)
1695 av_image_fill_arrays(retbuf.data, retbuf.linesize, outputbuf,
1696 AV_PIX_FMT_RGB32, width, height, IMAGE_ALIGN);
1701 copyframe.
Copy(&retbuf, AV_PIX_FMT_RGB32,
tmp,
1702 codecCtx->pix_fmt, width, height);
1704 QImage img(outputbuf, width, height,
1705 QImage::Format_RGB32);
1707 if (!img.save(
filename, qPrintable(saveFormat)))
1709 LOG(VB_GENERAL, LOG_ERR,
1710 QString(
"grabThumbnail(): Failed to save "
1717 if (count <= frameCount)
1720 frameFinished =
false;
1721 while (!frameFinished)
1723 int res = av_read_frame(inputFC, &pkt);
1726 if (pkt.stream_index == videostream)
1729 ret = avcodec_receive_frame(codecCtx, frame);
1731 frameFinished =
true;
1732 if (ret == 0 || ret == AVERROR(EAGAIN))
1733 avcodec_send_packet(codecCtx, &pkt);
1740 if (thumbCount >= list.count())
1745 av_packet_unref(&pkt);
1760 LOG(VB_JOBQUEUE, LOG_INFO,
"Calculating frame count");
1762 AVPacket *pkt = av_packet_alloc();
1765 LOG(VB_GENERAL, LOG_ERR,
"packet allocation failed");
1768 while (av_read_frame(inputFC, pkt) >= 0)
1770 if (pkt->stream_index == vid_id)
1774 av_packet_unref(pkt);
1776 av_packet_free(&pkt);
1785 int pos =
filename.lastIndexOf(
'/');
1800 frm_dir_map_t::iterator it;
1801 uint64_t frames = 0;
1805 if (cutlist.empty())
1811 for (it = cutlist.begin(); it != cutlist.end();)
1820 if (it != cutlist.end())
1842 frames += end - start;
1853 int pos =
filename.lastIndexOf(
'/');
1857 int keyframedist = -1;
1865 if (!posMap.empty())
1872 if (!posMap.empty())
1875 if (fps < 26 && fps > 24)
1881 if (!posMap.empty())
1895 frm_pos_map_t::const_iterator it = posMap.cend();
1897 uint64_t totframes = it.key() * keyframedist;
1901 static int getFileInfo(
const QString& inFile,
const QString& outFile,
int lenMethod)
1904 LOG(VB_JOBQUEUE , LOG_INFO, QString(
"getFileInfo(): Opening '%1'")
1911 LOG(VB_JOBQUEUE, LOG_ERR,
"getFileInfo(): Couldn't open input file" +
1917 int ret = avformat_find_stream_info(inputFC,
nullptr);
1921 LOG(VB_JOBQUEUE, LOG_ERR,
1922 QString(
"Couldn't get stream info, error #%1").arg(ret));
1927 av_dump_format(inputFC, 0, qPrintable(inFile), 0);
1929 QDomDocument doc(
"FILEINFO");
1931 QDomElement root = doc.createElement(
"file");
1932 doc.appendChild(root);
1933 root.setAttribute(
"type", inputFC->iformat->name);
1934 root.setAttribute(
"filename", inFile);
1936 QDomElement streams = doc.createElement(
"streams");
1938 root.appendChild(streams);
1939 streams.setAttribute(
"count", inputFC->nb_streams);
1940 int ffmpegIndex = 0;
1943 for (
uint i = 0; i < inputFC->nb_streams; i++)
1945 AVStream *st = inputFC->streams[i];
1946 std::string buf (256,
'\0');
1948 AVCodecParameters *par = st->codecpar;
1951 avcodec_string(buf.data(), buf.size(), avctx,
static_cast<int>(
false));
1953 switch (st->codecpar->codec_type)
1955 case AVMEDIA_TYPE_VIDEO:
1957 QStringList param = QString::fromStdString(buf).split(
',', Qt::SkipEmptyParts);
1958 QString codec = param[0].remove(
"Video:", Qt::CaseInsensitive).remove(QChar::Null);
1959 QDomElement stream = doc.createElement(
"video");
1960 stream.setAttribute(
"streamindex", i);
1961 stream.setAttribute(
"ffmpegindex", ffmpegIndex++);
1962 stream.setAttribute(
"codec", codec.trimmed());
1963 stream.setAttribute(
"width", par->width);
1964 stream.setAttribute(
"height", par->height);
1965 stream.setAttribute(
"bitrate", (qlonglong)par->bit_rate);
1968 if (st->r_frame_rate.den && st->r_frame_rate.num)
1969 fps = av_q2d(st->r_frame_rate);
1971 fps = 1/av_q2d(st->time_base);
1973 stream.setAttribute(
"fps", fps);
1975 if (par->sample_aspect_ratio.den && par->sample_aspect_ratio.num)
1977 float aspect_ratio = av_q2d(par->sample_aspect_ratio);
1978 if (QString(inputFC->iformat->name) !=
"nuv")
1979 aspect_ratio = ((float)par->width
1980 / par->height) * aspect_ratio;
1982 stream.setAttribute(
"aspectratio", aspect_ratio);
1986 stream.setAttribute(
"aspectratio",
"N/A");
1989 stream.setAttribute(
"id", st->id);
1991 if (st->start_time != (
int) AV_NOPTS_VALUE)
1993 int secs = st->start_time / AV_TIME_BASE;
1994 int us = st->start_time % AV_TIME_BASE;
1995 stream.setAttribute(
"start_time", QString(
"%1.%2")
1996 .arg(secs).arg(av_rescale(us, 1000000, AV_TIME_BASE)));
2000 stream.setAttribute(
"start_time", 0);
2003 streams.appendChild(stream);
2009 int64_t frameCount = 0;
2016 if (inputFC->duration != (
uint) AV_NOPTS_VALUE)
2018 duration = (
uint) (inputFC->duration / AV_TIME_BASE);
2019 root.setAttribute(
"duration", duration);
2020 LOG(VB_JOBQUEUE, LOG_INFO,
2021 QString(
"duration = %1") .arg(duration));
2022 frameCount = (int64_t)(duration * fps);
2026 root.setAttribute(
"duration",
"N/A");
2034 LOG(VB_JOBQUEUE, LOG_INFO,
2035 QString(
"frames = %1").arg(frameCount));
2036 duration = (
uint)(frameCount / fps);
2037 LOG(VB_JOBQUEUE, LOG_INFO,
2038 QString(
"duration = %1").arg(duration));
2039 root.setAttribute(
"duration", duration);
2049 LOG(VB_JOBQUEUE, LOG_INFO,
2050 QString(
"frames = %1").arg(frameCount));
2051 duration = (
uint)(frameCount / fps);
2052 LOG(VB_JOBQUEUE, LOG_INFO,
2053 QString(
"duration = %1").arg(duration));
2054 root.setAttribute(
"duration", duration);
2056 else if (inputFC->duration != (
uint) AV_NOPTS_VALUE)
2058 duration = (
uint) (inputFC->duration / AV_TIME_BASE);
2059 root.setAttribute(
"duration", duration);
2060 LOG(VB_JOBQUEUE, LOG_INFO,
2061 QString(
"duration = %1").arg(duration));
2062 frameCount = (int64_t)(duration * fps);
2066 root.setAttribute(
"duration",
"N/A");
2071 root.setAttribute(
"duration",
"N/A");
2072 LOG(VB_JOBQUEUE, LOG_ERR,
2073 QString(
"Unknown lenMethod (%1)")
2079 LOG(VB_JOBQUEUE, LOG_INFO,
2080 QString(
"cutframes = %1").arg(cutFrames));
2081 int cutduration = (int)(cutFrames / fps);
2082 LOG(VB_JOBQUEUE, LOG_INFO,
2083 QString(
"cutduration = %1").arg(cutduration));
2084 root.setAttribute(
"cutduration", duration - cutduration);
2090 case AVMEDIA_TYPE_AUDIO:
2092 QStringList param = QString::fromStdString(buf).split(
',', Qt::SkipEmptyParts);
2093 QString codec = param[0].remove(
"Audio:", Qt::CaseInsensitive).remove(QChar::Null);
2095 QDomElement stream = doc.createElement(
"audio");
2096 stream.setAttribute(
"streamindex", i);
2097 stream.setAttribute(
"ffmpegindex", ffmpegIndex++);
2101 if (codec.trimmed().toLower() ==
"liba52")
2102 stream.setAttribute(
"codec",
"AC3");
2104 stream.setAttribute(
"codec", codec.trimmed());
2106 stream.setAttribute(
"channels", par->ch_layout.nb_channels);
2108 AVDictionaryEntry *metatag =
2109 av_dict_get(st->metadata,
"language",
nullptr, 0);
2111 stream.setAttribute(
"language", metatag->value);
2113 stream.setAttribute(
"language",
"N/A");
2115 stream.setAttribute(
"id", st->id);
2117 stream.setAttribute(
"samplerate", par->sample_rate);
2118 stream.setAttribute(
"bitrate", (qlonglong)par->bit_rate);
2120 if (st->start_time != (
int) AV_NOPTS_VALUE)
2122 int secs = st->start_time / AV_TIME_BASE;
2123 int us = st->start_time % AV_TIME_BASE;
2124 stream.setAttribute(
"start_time", QString(
"%1.%2")
2125 .arg(secs).arg(av_rescale(us, 1000000, AV_TIME_BASE)));
2129 stream.setAttribute(
"start_time", 0);
2132 streams.appendChild(stream);
2137 case AVMEDIA_TYPE_SUBTITLE:
2139 QStringList param = QString::fromStdString(buf).split(
',', Qt::SkipEmptyParts);
2140 QString codec = param[0].remove(
"Subtitle:", Qt::CaseInsensitive).remove(QChar::Null);
2142 QDomElement stream = doc.createElement(
"subtitle");
2143 stream.setAttribute(
"streamindex", i);
2144 stream.setAttribute(
"ffmpegindex", ffmpegIndex++);
2145 stream.setAttribute(
"codec", codec.trimmed());
2147 AVDictionaryEntry *metatag =
2148 av_dict_get(st->metadata,
"language",
nullptr, 0);
2150 stream.setAttribute(
"language", metatag->value);
2152 stream.setAttribute(
"language",
"N/A");
2154 stream.setAttribute(
"id", st->id);
2156 streams.appendChild(stream);
2161 case AVMEDIA_TYPE_DATA:
2163 QDomElement stream = doc.createElement(
"data");
2164 stream.setAttribute(
"streamindex", i);
2165 stream.setAttribute(
"codec", QString::fromStdString(buf).remove(QChar::Null));
2166 streams.appendChild(stream);
2172 LOG(VB_JOBQUEUE, LOG_ERR,
2173 QString(
"Skipping unsupported codec %1 on stream %2")
2174 .arg(inputFC->streams[i]->codecpar->codec_type).arg(i));
2182 if (!f.open(QIODevice::WriteOnly))
2184 LOG(VB_JOBQUEUE, LOG_ERR,
2185 "Failed to open file for writing - " + outFile);
2190 t << doc.toString(4);
2202 if (!f.open(QIODevice::WriteOnly))
2204 LOG(VB_GENERAL, LOG_ERR,
2205 QString(
"MythArchiveHelper: Failed to open file for writing - %1")
2224 if (
filename.startsWith(
"myth://"))
2233 struct statfs statbuf {};
2235 ((!strcmp(statbuf.f_fstypename,
"nfs")) ||
2236 (!strcmp(statbuf.f_fstypename,
"afpfs")) ||
2237 (!strcmp(statbuf.f_fstypename,
"smbfs"))))
2239 #elif defined(__linux__)
2240 struct statfs statbuf {};
2242 ((statbuf.f_type == 0x6969) ||
2243 (statbuf.f_type == 0x517B)))
2267 add(QStringList{
"-t",
"--createthumbnail"},
2268 "createthumbnail",
false,
2269 "Create one or more thumbnails\n"
2270 "Requires: --infile, --thumblist, --outfile\n"
2271 "Optional: --framecount",
"");
2272 add(
"--infile",
"infile",
"",
2274 "Used with: --createthumbnail, --getfileinfo, --isremote, "
2275 "--sup2dast, --importarchive",
"");
2276 add(
"--outfile",
"outfile",
"",
2277 "Output file name\n"
2278 "Used with: --createthumbnail, --getfileinfo, --getdbparameters, "
2280 "When used with --createthumbnail: eg 'thumb%1-%2.jpg'\n"
2281 " %1 will be replaced with the no. of the thumb\n"
2282 " %2 will be replaced with the frame no.",
"");
2283 add(
"--thumblist",
"thumblist",
"",
2284 "Comma-separated list of required thumbs (in seconds)\n"
2285 "Used with: --createthumbnail",
"");
2286 add(
"--framecount",
"framecount", 1,
2287 "Number of frames to grab (default 1)\n"
2288 "Used with: --createthumbnail",
"");
2290 add(QStringList{
"-i",
"--getfileinfo"},
2291 "getfileinfo",
false,
2292 "Write file info about infile to outfile\n"
2293 "Requires: --infile, --outfile, --method",
"");
2294 add(
"--method",
"method", 0,
2295 "Method of file duration calculation\n"
2296 "Used with: --getfileinfo\n"
2297 " 0 = use av_estimate_timings() (quick but not very accurate - "
2299 " 1 = read all frames (most accurate but slow)\n"
2300 " 2 = use position map in DB (quick, only works for MythTV "
2303 add(QStringList{
"-p",
"--getdbparameters"},
2304 "getdbparameters",
false,
2305 "Write the mysql database parameters to outfile\n"
2306 "Requires: --outfile",
"");
2308 add(QStringList{
"-n",
"--nativearchive"},
2309 "nativearchive",
false,
2310 "Archive files to a native archive format\n"
2311 "Requires: --outfile",
"");
2313 add(QStringList{
"-f",
"--importarchive"},
2314 "importarchive",
false,
2315 "Import an archived file\n"
2316 "Requires: --infile, --chanid",
"");
2317 add(
"--chanid",
"chanid", -1,
2318 "Channel ID to use when inserting records in DB\n"
2319 "Used with: --importarchive",
"");
2321 add(QStringList{
"-r",
"--isremote"},
2323 "Check if infile is on a remote filesystem\n"
2324 "Requires: --infile\n"
2325 "Returns: 0 on error or file not found\n"
2326 " - 1 file is on a local filesystem\n"
2327 " - 2 file is on a remote filesystem",
"");
2329 add(QStringList{
"-b",
"--burndvd"},
2331 "Burn a created DVD to a blank disc\n"
2332 "Optional: --mediatype, --erasedvdrw, --nativeformat",
"");
2333 add(
"--mediatype",
"mediatype", 0,
2334 "Type of media to burn\n"
2335 "Used with: --burndvd\n"
2336 " 0 = single layer DVD (default)\n"
2337 " 1 = dual layer DVD\n"
2338 " 2 = rewritable DVD",
"");
2339 add(
"--erasedvdrw",
"erasedvdrw",
false,
2340 "Force an erase of DVD-R/W Media\n"
2341 "Used with: --burndvd (optional)",
"");
2342 add(
"--nativeformat",
"nativeformat",
false,
2343 "Archive is a native archive format\n"
2344 "Used with: --burndvd (optional)",
"");
2346 add(QStringList{
"-s",
"--sup2dast"},
2348 "Convert projectX subtitles to DVD subtitles\n"
2349 "Requires: --infile, --ifofile, --delay",
"");
2350 add(
"--ifofile",
"ifofile",
"",
2351 "Filename of ifo file\n"
2352 "Used with: --sup2dast",
"");
2353 add(
"--delay",
"delay", 0,
2354 "Delay in ms to add to subtitles (default 0)\n"
2355 "Used with: --sup2dast",
"");
2381 QCoreApplication a(argc, argv);
2382 QCoreApplication::setApplicationName(
"mytharchivehelper");
2385 QString mask(
"jobqueue");
2395 if (!context.Init(
false))
2397 LOG(VB_GENERAL, LOG_ERR,
"Failed to init MythContext, exiting.");
2427 if (inFile.isEmpty())
2429 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile in -t/--grabthumbnail "
2434 if (thumbList.isEmpty())
2436 LOG(VB_GENERAL, LOG_ERR,
"Missing --thumblist in -t/--grabthumbnail"
2441 if (outFile.isEmpty())
2443 LOG(VB_GENERAL, LOG_ERR,
"Missing --outfile in -t/--grabthumbnail "
2449 if (bGetDBParameters)
2451 if (outFile.isEmpty())
2453 LOG(VB_GENERAL, LOG_ERR,
"Missing argument to -p/--getdbparameters "
2461 if (inFile.isEmpty())
2463 LOG(VB_GENERAL, LOG_ERR,
2464 "Missing argument to -r/--isremote option");
2471 if (mediaType < 0 || mediaType > 2)
2473 LOG(VB_GENERAL, LOG_ERR, QString(
"Invalid mediatype given: %1")
2481 if (outFile.isEmpty())
2483 LOG(VB_GENERAL, LOG_ERR,
"Missing argument to -n/--nativearchive "
2491 if (inFile.isEmpty())
2493 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile argument to "
2494 "-f/--importarchive option");
2501 if (inFile.isEmpty())
2503 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile in -i/--getfileinfo "
2508 if (outFile.isEmpty())
2510 LOG(VB_GENERAL, LOG_ERR,
"Missing --outfile in -i/--getfileinfo "
2518 if (inFile.isEmpty())
2520 LOG(VB_GENERAL, LOG_ERR,
2521 "Missing --infile in -s/--sup2dast option");
2525 if (ifoFile.isEmpty())
2527 LOG(VB_GENERAL, LOG_ERR,
2528 "Missing --ifofile in -s/--sup2dast option");
2534 res =
grabThumbnail(inFile, thumbList, outFile, frameCount);
2535 else if (bGetDBParameters)
2537 else if (bNativeArchive)
2539 else if (bImportArchive)
2541 else if (bGetFileInfo)
2546 res =
doBurnDVD(mediaType, bEraseDVDRW, bNativeFormat);
2549 QByteArray inFileBA = inFile.toLocal8Bit();
2550 QByteArray ifoFileBA = ifoFile.toLocal8Bit();
2551 res =
sup2dast(inFileBA.constData(), ifoFileBA.constData(), delay);