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>
64 #include <libmythbase/mythversion.h>
69 #include <libavcodec/avcodec.h>
70 #include <libavformat/avformat.h>
71 #include <libavutil/imgutils.h>
76 #include "../mytharchive/archiveutil.h"
77 #include "../mytharchive/remoteavformatcontext.h"
87 static bool copyFile(
const QString &source,
const QString &destination);
89 const QString &xmlFile,
int chanID);
90 static int importVideo(
const QDomElement &itemNode,
const QString &xmlFile);
91 static int exportRecording(QDomElement &itemNode,
const QString &saveDirectory);
92 static int exportVideo(QDomElement &itemNode,
const QString &saveDirectory);
94 static QString
findNodeText(
const QDomElement &elem,
const QString &nodeName);
95 static int getFieldList(QStringList &fieldList,
const QString &tableName);
102 QFile
file(tempDir +
"/logs/mythburn.lck");
104 if (!
file.open(QIODevice::WriteOnly | QIODevice::Truncate))
105 LOG(VB_GENERAL, LOG_ERR,
"NativeArchive: Failed to create lock file");
107 QString pid = QString(
"%1").arg(getpid());
108 file.write(pid.toLatin1());
116 if (QFile::exists(tempDir +
"/logs/mythburn.lck"))
117 QFile::remove(tempDir +
"/logs/mythburn.lck");
122 QString command = QString(
"mythutil --copyfile --infile '%1' --outfile '%2'")
123 .arg(source, destination);
127 LOG(VB_JOBQUEUE, LOG_ERR,
128 QString(
"Failed while running %1. Result: %2").arg(command).arg(res));
137 LOG(VB_JOBQUEUE, LOG_INFO,
"Creating ISO image");
141 tempDirectory +=
"work/";
144 QString command = mkisofs +
" -R -J -V 'MythTV Archive' -o ";
145 command += tempDirectory +
"mythburn.iso " + sourceDirectory;
150 LOG(VB_JOBQUEUE, LOG_ERR,
151 QString(
"Failed while running mkisofs. Result: %1") .arg(res));
155 LOG(VB_JOBQUEUE, LOG_INFO,
"Finished creating ISO image");
159 static int burnISOImage(
int mediaType,
bool bEraseDVDRW,
bool nativeFormat)
163 LOG(VB_JOBQUEUE, LOG_INFO,
"Burning ISO image to " + dvdDrive);
168 tempDirectory +=
"work/";
174 command +=
" -speed=" + QString::number(driveSpeed);
178 if (mediaType ==
AD_DVD_RW && bEraseDVDRW)
180 command +=
" -use-the-force-luke -Z " + dvdDrive;
181 command +=
" -V 'MythTV Archive' -R -J " + tempDirectory;
185 command +=
" -Z " + dvdDrive;
186 command +=
" -V 'MythTV Archive' -R -J " + tempDirectory;
191 if (mediaType ==
AD_DVD_RW && bEraseDVDRW)
193 command +=
" -dvd-compat -use-the-force-luke -Z " + dvdDrive;
194 command +=
" -dvd-video -V 'MythTV DVD' " + tempDirectory +
"/dvd";
198 command +=
" -dvd-compat -Z " + dvdDrive;
199 command +=
" -dvd-video -V 'MythTV DVD' " + tempDirectory +
"/dvd";
206 LOG(VB_JOBQUEUE, LOG_ERR,
207 QString(
"Failed while running growisofs. Result: %1") .arg(res));
211 LOG(VB_JOBQUEUE, LOG_INFO,
"Finished burning ISO image");
217 static int doBurnDVD(
int mediaType,
bool bEraseDVDRW,
bool nativeFormat)
220 "MythArchiveLastRunStart",
224 int res =
burnISOImage(mediaType, bEraseDVDRW, nativeFormat);
227 "MythArchiveLastRunEnd",
237 QDomDocument doc(
"archivejob");
239 if (!
file.open(QIODevice::ReadOnly))
241 LOG(VB_JOBQUEUE, LOG_ERR,
"Could not open job file: " + jobFile);
245 if (!doc.setContent(&
file))
247 LOG(VB_JOBQUEUE, LOG_ERR,
"Could not load job file: " + jobFile);
255 bool bCreateISO =
false;
256 bool bEraseDVDRW =
false;
257 bool bDoBurn =
false;
258 QString saveDirectory;
261 QDomNodeList nodeList = doc.elementsByTagName(
"options");
262 if (nodeList.count() == 1)
264 QDomNode node = nodeList.item(0);
265 QDomElement
options = node.toElement();
268 bCreateISO = (
options.attribute(
"createiso",
"0") ==
"1");
269 bEraseDVDRW = (
options.attribute(
"erasedvdrw",
"0") ==
"1");
270 bDoBurn = (
options.attribute(
"doburn",
"0") ==
"1");
271 mediaType =
options.attribute(
"mediatype",
"0").toInt();
272 saveDirectory =
options.attribute(
"savedirectory",
"");
273 if (!saveDirectory.endsWith(
"/"))
274 saveDirectory +=
"/";
279 LOG(VB_JOBQUEUE, LOG_ERR,
280 QString(
"Found %1 options nodes - should be 1")
281 .arg(nodeList.count()));
284 LOG(VB_JOBQUEUE, LOG_INFO,
285 QString(
"Options - createiso: %1,"
286 " doburn: %2, mediatype: %3, erasedvdrw: %4")
287 .arg(bCreateISO).arg(bDoBurn).arg(mediaType).arg(bEraseDVDRW));
288 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"savedirectory: %1").arg(saveDirectory));
293 saveDirectory = tempDir;
294 if (!saveDirectory.endsWith(
"/"))
295 saveDirectory +=
"/";
297 saveDirectory +=
"work/";
299 QDir dir(saveDirectory);
303 LOG(VB_GENERAL, LOG_ERR,
304 "NativeArchive: Failed to clear work directory");
306 dir.mkpath(saveDirectory);
309 LOG(VB_JOBQUEUE, LOG_INFO,
310 QString(
"Saving files to : %1").arg(saveDirectory));
313 nodeList = doc.elementsByTagName(
"file");
314 if (nodeList.count() < 1)
316 LOG(VB_JOBQUEUE, LOG_ERR,
"Cannot find any file nodes?");
325 for (
int x = 0; x < nodeList.count(); x++)
327 node = nodeList.item(x);
328 elem = node.toElement();
331 type = elem.attribute(
"type");
333 if (
type.toLower() ==
"recording")
335 else if (
type.toLower() ==
"video")
339 LOG(VB_JOBQUEUE, LOG_ERR,
340 QString(
"Don't know how to archive items of type '%1'")
341 .arg(
type.toLower()));
348 if (mediaType !=
AD_FILE && bDoBurn)
352 LOG(VB_JOBQUEUE, LOG_ERR,
353 "Native archive job failed to complete");
363 LOG(VB_JOBQUEUE, LOG_ERR,
"Native archive job failed to complete");
368 LOG(VB_JOBQUEUE, LOG_INFO,
"Native archive job completed OK");
373 static const QRegularExpression
badChars { R
"((/|\\|:|'|"|\?|\|))" };
387 if (query.
exec(
"DESCRIBE " + tableName))
391 fieldList.append(query.
value(0).toString());
397 return fieldList.count();
401 const QString &saveDirectory)
407 QString title =
fixFilename(itemNode.attribute(
"title"));
408 QString
filename = itemNode.attribute(
"filename");
409 bool doDelete = (itemNode.attribute(
"delete",
"0") ==
"0");
410 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"Archiving %1 (%2), do delete: %3")
411 .arg(title,
filename, doDelete ?
"true" :
"false"));
415 LOG(VB_JOBQUEUE, LOG_ERR,
"Bad title or filename");
421 LOG(VB_JOBQUEUE, LOG_ERR,
422 QString(
"Failed to extract chanID and startTime from '%1'")
428 QDir dir(saveDirectory + title);
430 dir.mkpath(saveDirectory + title);
432 LOG(VB_GENERAL, LOG_ERR,
"Failed to create savedir: " +
ENO);
434 LOG(VB_JOBQUEUE, LOG_INFO,
"Creating xml file for " + title);
435 QDomDocument doc(
"MYTHARCHIVEITEM");
437 QDomElement root = doc.createElement(
"item");
438 doc.appendChild(root);
439 root.setAttribute(
"type",
"recording");
440 root.setAttribute(
"databaseversion",
dbVersion);
442 QDomElement recorded = doc.createElement(
"recorded");
443 root.appendChild(recorded);
446 QStringList fieldList;
450 query.
prepare(
"SELECT " + fieldList.join(
",")
452 " WHERE chanid = :CHANID and starttime = :STARTTIME;");
454 query.
bindValue(
":STARTTIME", startTime);
461 for (
int x = 0; x < fieldList.size(); x++)
463 elem = doc.createElement(fieldList[x]);
464 text = doc.createTextNode(query.
value(x).toString());
465 elem.appendChild(text);
466 recorded.appendChild(elem);
469 LOG(VB_JOBQUEUE, LOG_INFO,
"Created recorded element for " + title);
473 LOG(VB_JOBQUEUE, LOG_INFO,
"Failed to get recorded field list");
477 query.
prepare(
"SELECT chanid, channum, callsign, name "
478 "FROM channel WHERE chanid = :CHANID;");
483 QDomElement channel = doc.createElement(
"channel");
484 channel.setAttribute(
"chanid", query.
value(0).toString());
485 channel.setAttribute(
"channum", query.
value(1).toString());
486 channel.setAttribute(
"callsign", query.
value(2).toString());
487 channel.setAttribute(
"name", query.
value(3).toString());
488 root.appendChild(channel);
489 LOG(VB_JOBQUEUE, LOG_INFO,
"Created channel element for " + title);
494 LOG(VB_JOBQUEUE, LOG_ERR,
495 "Cannot find channel details for chanid " + chanID);
496 QDomElement channel = doc.createElement(
"channel");
497 channel.setAttribute(
"chanid", chanID);
498 channel.setAttribute(
"channum",
"unknown");
499 channel.setAttribute(
"callsign",
"unknown");
500 channel.setAttribute(
"name",
"unknown");
501 root.appendChild(channel);
502 LOG(VB_JOBQUEUE, LOG_INFO,
503 "Created a default channel element for " + title);
507 query.
prepare(
"SELECT credits.person, role, people.name "
508 "FROM recordedcredits AS credits "
509 "LEFT JOIN people ON credits.person = people.person "
510 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
512 query.
bindValue(
":STARTTIME", startTime);
516 QDomElement credits = doc.createElement(
"credits");
519 QDomElement credit = doc.createElement(
"credit");
520 credit.setAttribute(
"personid", query.
value(0).toString());
521 credit.setAttribute(
"name", query.
value(2).toString());
522 credit.setAttribute(
"role", query.
value(1).toString());
523 credits.appendChild(credit);
525 root.appendChild(credits);
526 LOG(VB_JOBQUEUE, LOG_INFO,
"Created credits element for " + title);
530 query.
prepare(
"SELECT `system`, rating FROM recordedrating "
531 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
533 query.
bindValue(
":STARTTIME", startTime);
537 QDomElement
rating = doc.createElement(
"rating");
538 rating.setAttribute(
"system", query.
value(0).toString());
539 rating.setAttribute(
"rating", query.
value(1).toString());
541 LOG(VB_JOBQUEUE, LOG_INFO,
"Created rating element for " + title);
545 QDomElement recordedmarkup = doc.createElement(
"recordedmarkup");
546 query.
prepare(
"SELECT chanid, starttime, mark, type, data "
547 "FROM recordedmarkup "
548 "WHERE chanid = :CHANID and starttime = :STARTTIME;");
550 query.
bindValue(
":STARTTIME", startTime);
555 QDomElement
mark = doc.createElement(
"mark");
556 mark.setAttribute(
"mark", query.
value(2).toString());
557 mark.setAttribute(
"type", query.
value(3).toString());
558 mark.setAttribute(
"data", query.
value(4).toString());
559 recordedmarkup.appendChild(
mark);
561 root.appendChild(recordedmarkup);
562 LOG(VB_JOBQUEUE, LOG_INFO,
"Created recordedmarkup element for " + title);
566 QDomElement recordedseek = doc.createElement(
"recordedseek");
567 query.
prepare(
"SELECT chanid, starttime, mark, `offset`, type "
569 "WHERE chanid = :CHANID and starttime = :STARTTIME;");
571 query.
bindValue(
":STARTTIME", startTime);
576 QDomElement
mark = doc.createElement(
"mark");
577 mark.setAttribute(
"mark", query.
value(2).toString());
578 mark.setAttribute(
"offset", query.
value(3).toString());
579 mark.setAttribute(
"type", query.
value(4).toString());
580 recordedseek.appendChild(
mark);
582 root.appendChild(recordedseek);
583 LOG(VB_JOBQUEUE, LOG_INFO,
584 "Created recordedseek element for " + title);
589 QString xmlFile = saveDirectory + title +
"/" + baseName +
".xml";
591 if (!f.open(QIODevice::WriteOnly))
593 LOG(VB_JOBQUEUE, LOG_ERR,
594 "MythNativeWizard: Failed to open file for writing - " + xmlFile);
599 t << doc.toString(4);
603 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
609 if (QFile::exists(
filename +
".png"))
611 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying preview image");
613 + title +
"/" + baseName +
".png");
618 LOG(VB_JOBQUEUE, LOG_INFO,
"Item Archived OK");
624 const QString &saveDirectory)
629 QString coverFile =
"";
631 QString title =
fixFilename(itemNode.attribute(
"title"));
632 QString
filename = itemNode.attribute(
"filename");
633 bool doDelete = (itemNode.attribute(
"delete",
"0") ==
"0");
634 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"Archiving %1 (%2), do delete: %3")
635 .arg(title,
filename, doDelete ?
"true" :
"false"));
639 LOG(VB_JOBQUEUE, LOG_ERR,
"Bad title or filename");
644 QDir dir(saveDirectory + title);
646 dir.mkdir(saveDirectory + title);
648 LOG(VB_JOBQUEUE, LOG_INFO,
"Creating xml file for " + title);
649 QDomDocument doc(
"MYTHARCHIVEITEM");
651 QDomElement root = doc.createElement(
"item");
652 doc.appendChild(root);
653 root.setAttribute(
"type",
"video");
654 root.setAttribute(
"databaseversion",
dbVersion);
656 QDomElement video = doc.createElement(
"videometadata");
657 root.appendChild(video);
661 query.
prepare(
"SELECT intid, title, director, plot, rating, inetref, "
662 "year, userrating, length, showlevel, filename, coverfile, "
663 "childid, browse, playcommand, category "
664 "FROM videometadata WHERE filename = :FILENAME;");
672 elem = doc.createElement(
"intid");
673 text = doc.createTextNode(query.
value(0).toString());
674 intID = query.
value(0).toInt();
675 elem.appendChild(text);
676 video.appendChild(elem);
678 elem = doc.createElement(
"title");
679 text = doc.createTextNode(query.
value(1).toString());
680 elem.appendChild(text);
681 video.appendChild(elem);
683 elem = doc.createElement(
"director");
684 text = doc.createTextNode(query.
value(2).toString());
685 elem.appendChild(text);
686 video.appendChild(elem);
688 elem = doc.createElement(
"plot");
689 text = doc.createTextNode(query.
value(3).toString());
690 elem.appendChild(text);
691 video.appendChild(elem);
693 elem = doc.createElement(
"rating");
694 text = doc.createTextNode(query.
value(4).toString());
695 elem.appendChild(text);
696 video.appendChild(elem);
698 elem = doc.createElement(
"inetref");
699 text = doc.createTextNode(query.
value(5).toString());
700 elem.appendChild(text);
701 video.appendChild(elem);
703 elem = doc.createElement(
"year");
704 text = doc.createTextNode(query.
value(6).toString());
705 elem.appendChild(text);
706 video.appendChild(elem);
708 elem = doc.createElement(
"userrating");
709 text = doc.createTextNode(query.
value(7).toString());
710 elem.appendChild(text);
711 video.appendChild(elem);
713 elem = doc.createElement(
"length");
714 text = doc.createTextNode(query.
value(8).toString());
715 elem.appendChild(text);
716 video.appendChild(elem);
718 elem = doc.createElement(
"showlevel");
719 text = doc.createTextNode(query.
value(9).toString());
720 elem.appendChild(text);
721 video.appendChild(elem);
724 QString fname = query.
value(10).toString();
728 elem = doc.createElement(
"filename");
729 text = doc.createTextNode(fname);
730 elem.appendChild(text);
731 video.appendChild(elem);
733 elem = doc.createElement(
"coverfile");
734 text = doc.createTextNode(query.
value(11).toString());
735 coverFile = query.
value(11).toString();
736 elem.appendChild(text);
737 video.appendChild(elem);
739 elem = doc.createElement(
"childid");
740 text = doc.createTextNode(query.
value(12).toString());
741 elem.appendChild(text);
742 video.appendChild(elem);
744 elem = doc.createElement(
"browse");
745 text = doc.createTextNode(query.
value(13).toString());
746 elem.appendChild(text);
747 video.appendChild(elem);
749 elem = doc.createElement(
"playcommand");
750 text = doc.createTextNode(query.
value(14).toString());
751 elem.appendChild(text);
752 video.appendChild(elem);
754 elem = doc.createElement(
"categoryid");
755 text = doc.createTextNode(query.
value(15).toString());
756 categoryID = query.
value(15).toInt();
757 elem.appendChild(text);
758 video.appendChild(elem);
760 LOG(VB_JOBQUEUE, LOG_INFO,
761 "Created videometadata element for " + title);
765 query.
prepare(
"SELECT intid, category "
766 "FROM videocategory WHERE intid = :INTID;");
771 QDomElement category = doc.createElement(
"category");
772 category.setAttribute(
"intid", query.
value(0).toString());
773 category.setAttribute(
"category", query.
value(1).toString());
774 root.appendChild(category);
775 LOG(VB_JOBQUEUE, LOG_INFO,
776 "Created videocategory element for " + title);
780 QDomElement countries = doc.createElement(
"countries");
781 root.appendChild(countries);
783 query.
prepare(
"SELECT intid, country "
784 "FROM videometadatacountry INNER JOIN videocountry "
785 "ON videometadatacountry.idcountry = videocountry.intid "
786 "WHERE idvideo = :INTID;");
796 QDomElement country = doc.createElement(
"country");
797 country.setAttribute(
"intid", query.
value(0).toString());
798 country.setAttribute(
"country", query.
value(1).toString());
799 countries.appendChild(country);
801 LOG(VB_JOBQUEUE, LOG_INFO,
"Created videocountry element for " + title);
805 QDomElement genres = doc.createElement(
"genres");
806 root.appendChild(genres);
808 query.
prepare(
"SELECT intid, genre "
809 "FROM videometadatagenre INNER JOIN videogenre "
810 "ON videometadatagenre.idgenre = videogenre.intid "
811 "WHERE idvideo = :INTID;");
821 QDomElement genre = doc.createElement(
"genre");
822 genre.setAttribute(
"intid", query.
value(0).toString());
823 genre.setAttribute(
"genre", query.
value(1).toString());
824 genres.appendChild(genre);
826 LOG(VB_JOBQUEUE, LOG_INFO,
"Created videogenre element for " + title);
831 QString xmlFile = saveDirectory + title +
"/"
832 + fileInfo.fileName() +
".xml";
834 if (!f.open(QIODevice::WriteOnly))
836 LOG(VB_JOBQUEUE, LOG_INFO,
837 "MythNativeWizard: Failed to open file for writing - " + xmlFile);
842 t << doc.toString(4);
846 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
848 +
"/" + fileInfo.fileName());
855 fileInfo.setFile(coverFile);
856 if (fileInfo.exists())
858 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying cover file");
859 res =
copyFile(coverFile, saveDirectory + title
860 +
"/" + fileInfo.fileName());
867 LOG(VB_JOBQUEUE, LOG_INFO,
"Item Archived OK");
875 QDomDocument doc(
"mydocument");
877 if (!
file.open(QIODevice::ReadOnly))
879 LOG(VB_JOBQUEUE, LOG_ERR,
880 "Failed to open file for reading - " + xmlFile);
884 if (!doc.setContent(&
file))
887 LOG(VB_JOBQUEUE, LOG_ERR,
888 "Failed to read from xml file - " + xmlFile);
893 QString docType = doc.doctype().name();
896 QDomNodeList itemNodeList;
898 QDomElement itemNode;
900 if (docType ==
"MYTHARCHIVEITEM")
902 itemNodeList = doc.elementsByTagName(
"item");
904 if (itemNodeList.count() < 1)
906 LOG(VB_JOBQUEUE, LOG_ERR,
907 "Couldn't find an 'item' element in XML file");
911 node = itemNodeList.item(0);
912 itemNode = node.toElement();
913 type = itemNode.attribute(
"type");
914 dbVersion = itemNode.attribute(
"databaseversion");
916 LOG(VB_JOBQUEUE, LOG_INFO,
917 QString(
"Archive DB version: %1, Local DB version: %2")
922 LOG(VB_JOBQUEUE, LOG_ERR,
"Not a native archive xml file - " + xmlFile);
926 if (
type ==
"recording")
939 const QString &xmlFile,
int chanID)
941 LOG(VB_JOBQUEUE, LOG_INFO,
942 QString(
"Import recording using chanID: %1").arg(chanID));
943 LOG(VB_JOBQUEUE, LOG_INFO,
944 QString(
"Archived recording xml file: %1").arg(xmlFile));
946 QString videoFile = xmlFile.left(xmlFile.length() - 4);
947 QString basename = videoFile;
948 int pos = videoFile.lastIndexOf(
'/');
950 basename = videoFile.mid(pos + 1);
952 QDomNodeList nodeList = itemNode.elementsByTagName(
"recorded");
953 if (nodeList.count() < 1)
955 LOG(VB_JOBQUEUE, LOG_ERR,
956 "Couldn't find a 'recorded' element in XML file");
960 QDomNode n = nodeList.item(0);
961 QDomElement recordedNode = n.toElement();
962 QString startTime =
findNodeText(recordedNode,
"starttime");
965 query.
prepare(
"SELECT * FROM recorded "
966 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
968 query.
bindValue(
":STARTTIME", startTime);
973 LOG(VB_JOBQUEUE, LOG_ERR,
974 "This recording appears to already exist!!");
981 basename ,
"Default");
984 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file to: " + destFile);
989 if (QFile::exists(videoFile +
".png"))
991 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying preview image file to: " + destFile +
".png");
992 if (!
copyFile(videoFile +
".png", destFile +
".png"))
997 QStringList fieldList;
998 QStringList bindList;
999 QDomNodeList nodes = recordedNode.childNodes();
1001 for (
int x = 0; x < nodes.count(); x++)
1003 QDomNode n2 = nodes.item(x);
1004 QString field = n2.nodeName();
1005 fieldList.append(field);
1006 bindList.append(
":" + field.toUpper());
1010 query.
prepare(
"INSERT INTO recorded (" + fieldList.join(
",") +
") "
1011 "VALUES (" + bindList.join(
",") +
");");
1013 query.
bindValue(
":STARTTIME", startTime);
1015 for (
int x = 0; x < fieldList.count(); x++)
1019 LOG(VB_JOBQUEUE, LOG_INFO,
"Inserted recorded details into database");
1024 nodeList = itemNode.elementsByTagName(
"recordedmarkup");
1025 if (nodeList.count() < 1)
1027 LOG(VB_JOBQUEUE, LOG_WARNING,
1028 "Couldn't find a 'recordedmarkup' element in XML file");
1032 QDomNode n3 = nodeList.item(0);
1033 QDomElement markupNode = n3.toElement();
1035 nodeList = markupNode.elementsByTagName(
"mark");
1036 if (nodeList.count() < 1)
1038 LOG(VB_JOBQUEUE, LOG_WARNING,
1039 "Couldn't find any 'mark' elements in XML file");
1044 query.
prepare(
"DELETE FROM recordedmarkup "
1045 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
1047 query.
bindValue(
":STARTTIME", startTime);
1053 for (
int x = 0; x < nodeList.count(); x++)
1055 QDomNode n4 = nodeList.item(x);
1056 QDomElement e = n4.toElement();
1057 query.
prepare(
"INSERT INTO recordedmarkup (chanid, starttime, "
1059 "VALUES(:CHANID,:STARTTIME,:MARK,:TYPE,:DATA);");
1061 query.
bindValue(
":STARTTIME", startTime);
1062 query.
bindValue(
":MARK", e.attribute(
"mark"));
1063 query.
bindValue(
":TYPE", e.attribute(
"type"));
1064 query.
bindValue(
":DATA", e.attribute(
"data"));
1073 LOG(VB_JOBQUEUE, LOG_INFO,
1074 "Inserted recordedmarkup details into database");
1079 nodeList = itemNode.elementsByTagName(
"recordedseek");
1080 if (nodeList.count() < 1)
1082 LOG(VB_JOBQUEUE, LOG_WARNING,
1083 "Couldn't find a 'recordedseek' element in XML file");
1087 QDomNode n5 = nodeList.item(0);
1088 QDomElement markupNode = n5.toElement();
1090 nodeList = markupNode.elementsByTagName(
"mark");
1091 if (nodeList.count() < 1)
1093 LOG(VB_JOBQUEUE, LOG_WARNING,
1094 "Couldn't find any 'mark' elements in XML file");
1099 query.
prepare(
"DELETE FROM recordedseek "
1100 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
1102 query.
bindValue(
":STARTTIME", startTime);
1106 for (
int x = 0; x < nodeList.count(); x++)
1108 QDomNode n6 = nodeList.item(x);
1109 QDomElement e = n6.toElement();
1110 query.
prepare(
"INSERT INTO recordedseek (chanid, starttime, "
1111 "mark, `offset`, type)"
1112 "VALUES(:CHANID,:STARTTIME,:MARK,:OFFSET,:TYPE);");
1114 query.
bindValue(
":STARTTIME", startTime);
1115 query.
bindValue(
":MARK", e.attribute(
"mark"));
1116 query.
bindValue(
":OFFSET", e.attribute(
"offset"));
1117 query.
bindValue(
":TYPE", e.attribute(
"type"));
1126 LOG(VB_JOBQUEUE, LOG_INFO,
1127 "Inserted recordedseek details into database");
1135 LOG(VB_JOBQUEUE, LOG_INFO,
"Import completed OK");
1142 LOG(VB_JOBQUEUE, LOG_INFO,
"Importing video");
1143 LOG(VB_JOBQUEUE, LOG_INFO,
1144 QString(
"Archived video xml file: %1").arg(xmlFile));
1146 QString videoFile = xmlFile.left(xmlFile.length() - 4);
1147 QFileInfo fileInfo(videoFile);
1148 QString basename = fileInfo.fileName();
1150 QDomNodeList nodeList = itemNode.elementsByTagName(
"videometadata");
1151 if (nodeList.count() < 1)
1153 LOG(VB_JOBQUEUE, LOG_ERR,
1154 "Couldn't find a 'videometadata' element in XML file");
1158 QDomNode n = nodeList.item(0);
1159 QDomElement videoNode = n.toElement();
1163 QString origFilename =
findNodeText(videoNode,
"filename");
1164 QStringList dirList = origFilename.split(
"/", Qt::SkipEmptyParts);
1166 for (
int x = 0; x < dirList.count() - 1; x++)
1168 path +=
"/" + dirList[x];
1169 if (!dir.exists(path))
1171 if (!dir.mkdir(path))
1173 LOG(VB_JOBQUEUE, LOG_ERR,
1174 QString(
"Couldn't create directory '%1'").arg(path));
1180 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
1181 if (!
copyFile(videoFile, path +
"/" + basename))
1189 fileInfo.setFile(videoFile);
1190 QString archivePath = fileInfo.absolutePath();
1192 QString coverFilename =
findNodeText(videoNode,
"coverfile");
1193 fileInfo.setFile(coverFilename);
1194 coverFilename = fileInfo.fileName();
1196 fileInfo.setFile(archivePath +
"/" + coverFilename);
1197 if (fileInfo.exists())
1199 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying cover file");
1201 if (!
copyFile(archivePath +
"/" + coverFilename, artworkDir +
"/" + coverFilename))
1207 coverFilename =
"No Cover";
1211 query.
prepare(
"INSERT INTO videometadata (title, director, plot, rating, inetref, "
1212 "year, userrating, length, showlevel, filename, coverfile, "
1213 "childid, browse, playcommand, category) "
1214 "VALUES(:TITLE,:DIRECTOR,:PLOT,:RATING,:INETREF,:YEAR,"
1215 ":USERRATING,:LENGTH,:SHOWLEVEL,:FILENAME,:COVERFILE,"
1216 ":CHILDID,:BROWSE,:PLAYCOMMAND,:CATEGORY);");
1226 query.
bindValue(
":FILENAME", path +
"/" + basename);
1227 query.
bindValue(
":COVERFILE", artworkDir +
"/" + coverFilename);
1235 LOG(VB_JOBQUEUE, LOG_INFO,
1236 "Inserted videometadata details into database");
1246 query.
prepare(
"SELECT intid FROM videometadata WHERE filename = :FILENAME;");
1247 query.
bindValue(
":FILENAME", path +
"/" + basename);
1250 intid = query.
value(0).toInt();
1258 LOG(VB_JOBQUEUE, LOG_INFO,
1259 QString(
"'intid' of inserted video is: %1").arg(intid));
1262 nodeList = itemNode.elementsByTagName(
"genres");
1263 if (nodeList.count() < 1)
1265 LOG(VB_JOBQUEUE, LOG_ERR,
"No 'genres' element found in XML file");
1269 n = nodeList.item(0);
1270 QDomElement genresNode = n.toElement();
1272 nodeList = genresNode.elementsByTagName(
"genre");
1273 if (nodeList.count() < 1)
1275 LOG(VB_JOBQUEUE, LOG_WARNING,
1276 "Couldn't find any 'genre' elements in XML file");
1280 for (
int x = 0; x < nodeList.count(); x++)
1282 n = nodeList.item(x);
1283 QDomElement e = n.toElement();
1285 QString genre = e.attribute(
"genre");
1288 query.
prepare(
"SELECT intid FROM videogenre "
1289 "WHERE genre = :GENRE");
1293 genreID = query.
value(0).toInt();
1298 query.
prepare(
"INSERT INTO videogenre (genre) VALUES(:GENRE);");
1302 "insert videogenre", query);
1305 query.
prepare(
"SELECT intid FROM videogenre "
1306 "WHERE genre = :GENRE");
1308 if (!query.
exec() || !query.
next())
1310 LOG(VB_JOBQUEUE, LOG_ERR,
1311 "Couldn't add genre to database");
1314 genreID = query.
value(0).toInt();
1318 query.
prepare(
"INSERT INTO videometadatagenre (idvideo, idgenre)"
1319 "VALUES (:IDVIDEO, :IDGENRE);");
1324 "insert videometadatagenre", query);
1327 LOG(VB_JOBQUEUE, LOG_INFO,
"Inserted genre details into database");
1332 nodeList = itemNode.elementsByTagName(
"countries");
1333 if (nodeList.count() < 1)
1335 LOG(VB_JOBQUEUE, LOG_INFO,
"No 'countries' element found in XML file");
1339 n = nodeList.item(0);
1340 QDomElement countriesNode = n.toElement();
1342 nodeList = countriesNode.elementsByTagName(
"country");
1343 if (nodeList.count() < 1)
1345 LOG(VB_JOBQUEUE, LOG_WARNING,
1346 "Couldn't find any 'country' elements in XML file");
1350 for (
int x = 0; x < nodeList.count(); x++)
1352 n = nodeList.item(x);
1353 QDomElement e = n.toElement();
1355 QString country = e.attribute(
"country");
1358 query.
prepare(
"SELECT intid FROM videocountry "
1359 "WHERE country = :COUNTRY");
1363 countryID = query.
value(0).toInt();
1368 query.
prepare(
"INSERT INTO videocountry (country) VALUES(:COUNTRY);");
1372 "insert videocountry", query);
1375 query.
prepare(
"SELECT intid FROM videocountry "
1376 "WHERE country = :COUNTRY");
1378 if (!query.
exec() || !query.
next())
1380 LOG(VB_JOBQUEUE, LOG_ERR,
1381 "Couldn't add country to database");
1384 countryID = query.
value(0).toInt();
1388 query.
prepare(
"INSERT INTO videometadatacountry (idvideo, idcountry)"
1389 "VALUES (:IDVIDEO, :IDCOUNTRY);");
1391 query.
bindValue(
":IDCOUNTRY", countryID);
1394 "insert videometadatacountry", query);
1397 LOG(VB_JOBQUEUE, LOG_INFO,
1398 "Inserted country details into database");
1403 nodeList = itemNode.elementsByTagName(
"category");
1404 if (nodeList.count() < 1)
1406 LOG(VB_JOBQUEUE, LOG_ERR,
"No 'category' element found in XML file");
1410 n = nodeList.item(0);
1411 QDomElement e = n.toElement();
1413 QString category = e.attribute(
"category");
1415 query.
prepare(
"SELECT intid FROM videocategory "
1416 "WHERE category = :CATEGORY");
1420 categoryID = query.
value(0).toInt();
1425 query.
prepare(
"INSERT INTO videocategory (category) VALUES(:CATEGORY);");
1429 "insert videocategory", query);
1432 query.
prepare(
"SELECT intid FROM videocategory "
1433 "WHERE category = :CATEGORY");
1437 categoryID = query.
value(0).toInt();
1441 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't add category to database");
1447 query.
prepare(
"UPDATE videometadata "
1448 "SET category = :CATEGORY "
1449 "WHERE intid = :INTID;");
1450 query.
bindValue(
":CATEGORY", categoryID);
1454 "update category", query);
1456 LOG(VB_JOBQUEUE, LOG_INFO,
"Fixed the category in the database");
1459 LOG(VB_JOBQUEUE, LOG_INFO,
"Import completed OK");
1466 QDomNodeList nodeList = elem.elementsByTagName(nodeName);
1467 if (nodeList.count() < 1)
1469 LOG(VB_GENERAL, LOG_ERR,
1470 QString(
"Couldn't find a '%1' element in XML file") .arg(nodeName));
1474 QDomNode n = nodeList.item(0);
1475 QDomElement e = n.toElement();
1478 for (QDomNode node = e.firstChild(); !node.isNull();
1479 node = node.nextSibling())
1481 QDomText
t = node.toText();
1491 if ((nodeName ==
"recgroup") ||
1492 (nodeName ==
"playgroup"))
1496 else if ((nodeName ==
"recordid") ||
1497 (nodeName ==
"seriesid") ||
1498 (nodeName ==
"programid") ||
1499 (nodeName ==
"profile"))
1510 query.
prepare(
"DELETE FROM archiveitems;");
1520 "MythArchiveLastRunStart",
1526 "MythArchiveLastRunEnd",
1529 (res == 0 ?
"Success" :
"Failed"));
1543 static int grabThumbnail(
const QString& inFile,
const QString& thumbList,
const QString& outFile,
int frameCount)
1546 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"grabThumbnail(): Opening '%1'")
1553 LOG(VB_JOBQUEUE, LOG_ERR,
"grabThumbnail(): Couldn't open input file" +
1559 int ret = avformat_find_stream_info(inputFC,
nullptr);
1562 LOG(VB_JOBQUEUE, LOG_ERR,
1563 QString(
"Couldn't get stream info, error #%1").arg(ret));
1568 int videostream = -1;
1573 for (
uint i = 0; i < inputFC->nb_streams; i++)
1575 AVStream *st = inputFC->streams[i];
1576 if (inputFC->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
1579 width = st->codecpar->width;
1580 height = st->codecpar->height;
1581 if (st->r_frame_rate.den && st->r_frame_rate.num)
1582 fps = av_q2d(st->r_frame_rate);
1584 fps = 1/av_q2d(st->time_base);
1589 if (videostream == -1)
1591 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't find a video stream");
1596 AVCodecContext *codecCtx = codecmap.
GetCodecContext(inputFC->streams[videostream]);
1599 const AVCodec * codec = avcodec_find_decoder(codecCtx->codec_id);
1601 if (codec ==
nullptr)
1603 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't find codec for video stream");
1608 if (avcodec_open2(codecCtx, codec,
nullptr) < 0)
1610 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't open codec for video stream");
1615 QStringList list = thumbList.split(
",", Qt::SkipEmptyParts);
1624 memset(&orig, 0,
sizeof(
AVFrame));
1625 memset(&retbuf, 0,
sizeof(
AVFrame));
1628 int bufflen = width * height * 4;
1629 auto *outputbuf =
new unsigned char[bufflen];
1633 bool frameFinished =
false;
1635 while (av_read_frame(inputFC, &pkt) >= 0)
1637 if (pkt.stream_index == videostream)
1640 if (list[thumbCount].toInt() == (
int)(frameNo / fps))
1644 avcodec_flush_buffers(codecCtx);
1645 av_frame_unref(frame);
1646 frameFinished =
false;
1647 ret = avcodec_receive_frame(codecCtx, frame);
1649 frameFinished =
true;
1650 if (ret == 0 || ret == AVERROR(EAGAIN))
1651 avcodec_send_packet(codecCtx, &pkt);
1652 int keyFrame = frame->key_frame;
1654 while (!frameFinished || !keyFrame)
1656 av_packet_unref(&pkt);
1657 int res = av_read_frame(inputFC, &pkt);
1660 if (pkt.stream_index == videostream)
1663 av_frame_unref(frame);
1664 ret = avcodec_receive_frame(codecCtx, frame);
1666 frameFinished =
true;
1667 if (ret == 0 || ret == AVERROR(EAGAIN))
1668 avcodec_send_packet(codecCtx, &pkt);
1669 keyFrame = frame->key_frame;
1676 QString saveFormat =
"JPEG";
1677 if (outFile.right(4) ==
".png")
1681 while (count < frameCount)
1689 av_image_fill_arrays(retbuf.data, retbuf.linesize, outputbuf,
1690 AV_PIX_FMT_RGB32, width, height, IMAGE_ALIGN);
1695 copyframe.
Copy(&retbuf, AV_PIX_FMT_RGB32,
tmp,
1696 codecCtx->pix_fmt, width, height);
1698 QImage img(outputbuf, width, height,
1699 QImage::Format_RGB32);
1701 if (!img.save(
filename, qPrintable(saveFormat)))
1703 LOG(VB_GENERAL, LOG_ERR,
1704 QString(
"grabThumbnail(): Failed to save "
1711 if (count <= frameCount)
1714 frameFinished =
false;
1715 while (!frameFinished)
1717 int res = av_read_frame(inputFC, &pkt);
1720 if (pkt.stream_index == videostream)
1723 ret = avcodec_receive_frame(codecCtx, frame);
1725 frameFinished =
true;
1726 if (ret == 0 || ret == AVERROR(EAGAIN))
1727 avcodec_send_packet(codecCtx, &pkt);
1734 if (thumbCount >= list.count())
1739 av_packet_unref(&pkt);
1754 LOG(VB_JOBQUEUE, LOG_INFO,
"Calculating frame count");
1756 AVPacket *pkt = av_packet_alloc();
1759 LOG(VB_GENERAL, LOG_ERR,
"packet allocation failed");
1762 while (av_read_frame(inputFC, pkt) >= 0)
1764 if (pkt->stream_index == vid_id)
1768 av_packet_unref(pkt);
1770 av_packet_free(&pkt);
1779 int pos =
filename.lastIndexOf(
'/');
1794 frm_dir_map_t::iterator it;
1795 uint64_t frames = 0;
1799 if (cutlist.empty())
1805 for (it = cutlist.begin(); it != cutlist.end();)
1814 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;
1893 static 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);
1977 stream.setAttribute(
"aspectratio",
"N/A");
1979 stream.setAttribute(
"id", st->id);
1981 if (st->start_time != (
int) AV_NOPTS_VALUE)
1983 int secs = st->start_time / AV_TIME_BASE;
1984 int us = st->start_time % AV_TIME_BASE;
1985 stream.setAttribute(
"start_time", QString(
"%1.%2")
1986 .arg(secs).arg(av_rescale(us, 1000000, AV_TIME_BASE)));
1989 stream.setAttribute(
"start_time", 0);
1991 streams.appendChild(stream);
1997 int64_t frameCount = 0;
2004 if (inputFC->duration != (
uint) AV_NOPTS_VALUE)
2006 duration = (
uint) (inputFC->duration / AV_TIME_BASE);
2007 root.setAttribute(
"duration", duration);
2008 LOG(VB_JOBQUEUE, LOG_INFO,
2009 QString(
"duration = %1") .arg(duration));
2010 frameCount = (int64_t)(duration * fps);
2013 root.setAttribute(
"duration",
"N/A");
2020 LOG(VB_JOBQUEUE, LOG_INFO,
2021 QString(
"frames = %1").arg(frameCount));
2022 duration = (
uint)(frameCount / fps);
2023 LOG(VB_JOBQUEUE, LOG_INFO,
2024 QString(
"duration = %1").arg(duration));
2025 root.setAttribute(
"duration", duration);
2035 LOG(VB_JOBQUEUE, LOG_INFO,
2036 QString(
"frames = %1").arg(frameCount));
2037 duration = (
uint)(frameCount / fps);
2038 LOG(VB_JOBQUEUE, LOG_INFO,
2039 QString(
"duration = %1").arg(duration));
2040 root.setAttribute(
"duration", duration);
2042 else if (inputFC->duration != (
uint) AV_NOPTS_VALUE)
2044 duration = (
uint) (inputFC->duration / AV_TIME_BASE);
2045 root.setAttribute(
"duration", duration);
2046 LOG(VB_JOBQUEUE, LOG_INFO,
2047 QString(
"duration = %1").arg(duration));
2048 frameCount = (int64_t)(duration * fps);
2051 root.setAttribute(
"duration",
"N/A");
2055 root.setAttribute(
"duration",
"N/A");
2056 LOG(VB_JOBQUEUE, LOG_ERR,
2057 QString(
"Unknown lenMethod (%1)")
2063 LOG(VB_JOBQUEUE, LOG_INFO,
2064 QString(
"cutframes = %1").arg(cutFrames));
2065 int cutduration = (int)(cutFrames / fps);
2066 LOG(VB_JOBQUEUE, LOG_INFO,
2067 QString(
"cutduration = %1").arg(cutduration));
2068 root.setAttribute(
"cutduration", duration - cutduration);
2074 case AVMEDIA_TYPE_AUDIO:
2076 QStringList param = QString::fromStdString(buf).split(
',', Qt::SkipEmptyParts);
2077 QString codec = param[0].remove(
"Audio:", Qt::CaseInsensitive).remove(QChar::Null);
2079 QDomElement stream = doc.createElement(
"audio");
2080 stream.setAttribute(
"streamindex", i);
2081 stream.setAttribute(
"ffmpegindex", ffmpegIndex++);
2085 if (codec.trimmed().toLower() ==
"liba52")
2086 stream.setAttribute(
"codec",
"AC3");
2088 stream.setAttribute(
"codec", codec.trimmed());
2090 stream.setAttribute(
"channels", par->ch_layout.nb_channels);
2092 AVDictionaryEntry *metatag =
2093 av_dict_get(st->metadata,
"language",
nullptr, 0);
2095 stream.setAttribute(
"language", metatag->value);
2097 stream.setAttribute(
"language",
"N/A");
2099 stream.setAttribute(
"id", st->id);
2101 stream.setAttribute(
"samplerate", par->sample_rate);
2102 stream.setAttribute(
"bitrate", (qlonglong)par->bit_rate);
2104 if (st->start_time != (
int) AV_NOPTS_VALUE)
2106 int secs = st->start_time / AV_TIME_BASE;
2107 int us = st->start_time % AV_TIME_BASE;
2108 stream.setAttribute(
"start_time", QString(
"%1.%2")
2109 .arg(secs).arg(av_rescale(us, 1000000, AV_TIME_BASE)));
2112 stream.setAttribute(
"start_time", 0);
2114 streams.appendChild(stream);
2119 case AVMEDIA_TYPE_SUBTITLE:
2121 QStringList param = QString::fromStdString(buf).split(
',', Qt::SkipEmptyParts);
2122 QString codec = param[0].remove(
"Subtitle:", Qt::CaseInsensitive).remove(QChar::Null);
2124 QDomElement stream = doc.createElement(
"subtitle");
2125 stream.setAttribute(
"streamindex", i);
2126 stream.setAttribute(
"ffmpegindex", ffmpegIndex++);
2127 stream.setAttribute(
"codec", codec.trimmed());
2129 AVDictionaryEntry *metatag =
2130 av_dict_get(st->metadata,
"language",
nullptr, 0);
2132 stream.setAttribute(
"language", metatag->value);
2134 stream.setAttribute(
"language",
"N/A");
2136 stream.setAttribute(
"id", st->id);
2138 streams.appendChild(stream);
2143 case AVMEDIA_TYPE_DATA:
2145 QDomElement stream = doc.createElement(
"data");
2146 stream.setAttribute(
"streamindex", i);
2147 stream.setAttribute(
"codec", QString::fromStdString(buf).remove(QChar::Null));
2148 streams.appendChild(stream);
2154 LOG(VB_JOBQUEUE, LOG_ERR,
2155 QString(
"Skipping unsupported codec %1 on stream %2")
2156 .arg(inputFC->streams[i]->codecpar->codec_type).arg(i));
2164 if (!f.open(QIODevice::WriteOnly))
2166 LOG(VB_JOBQUEUE, LOG_ERR,
2167 "Failed to open file for writing - " + outFile);
2172 t << doc.toString(4);
2184 if (!f.open(QIODevice::WriteOnly))
2186 LOG(VB_GENERAL, LOG_ERR,
2187 QString(
"MythArchiveHelper: Failed to open file for writing - %1")
2206 if (
filename.startsWith(
"myth://"))
2215 struct statfs statbuf {};
2217 ((!strcmp(statbuf.f_fstypename,
"nfs")) ||
2218 (!strcmp(statbuf.f_fstypename,
"afpfs")) ||
2219 (!strcmp(statbuf.f_fstypename,
"smbfs"))))
2221 #elif defined(__linux__)
2222 struct statfs statbuf {};
2224 ((statbuf.f_type == 0x6969) ||
2225 (statbuf.f_type == 0x517B)))
2249 add(QStringList{
"-t",
"--createthumbnail"},
2250 "createthumbnail",
false,
2251 "Create one or more thumbnails\n"
2252 "Requires: --infile, --thumblist, --outfile\n"
2253 "Optional: --framecount",
"");
2254 add(
"--infile",
"infile",
"",
2256 "Used with: --createthumbnail, --getfileinfo, --isremote, "
2257 "--sup2dast, --importarchive",
"");
2258 add(
"--outfile",
"outfile",
"",
2259 "Output file name\n"
2260 "Used with: --createthumbnail, --getfileinfo, --getdbparameters, "
2262 "When used with --createthumbnail: eg 'thumb%1-%2.jpg'\n"
2263 " %1 will be replaced with the no. of the thumb\n"
2264 " %2 will be replaced with the frame no.",
"");
2265 add(
"--thumblist",
"thumblist",
"",
2266 "Comma-separated list of required thumbs (in seconds)\n"
2267 "Used with: --createthumbnail",
"");
2268 add(
"--framecount",
"framecount", 1,
2269 "Number of frames to grab (default 1)\n"
2270 "Used with: --createthumbnail",
"");
2272 add(QStringList{
"-i",
"--getfileinfo"},
2273 "getfileinfo",
false,
2274 "Write file info about infile to outfile\n"
2275 "Requires: --infile, --outfile, --method",
"");
2276 add(
"--method",
"method", 0,
2277 "Method of file duration calculation\n"
2278 "Used with: --getfileinfo\n"
2279 " 0 = use av_estimate_timings() (quick but not very accurate - "
2281 " 1 = read all frames (most accurate but slow)\n"
2282 " 2 = use position map in DB (quick, only works for MythTV "
2285 add(QStringList{
"-p",
"--getdbparameters"},
2286 "getdbparameters",
false,
2287 "Write the mysql database parameters to outfile\n"
2288 "Requires: --outfile",
"");
2290 add(QStringList{
"-n",
"--nativearchive"},
2291 "nativearchive",
false,
2292 "Archive files to a native archive format\n"
2293 "Requires: --outfile",
"");
2295 add(QStringList{
"-f",
"--importarchive"},
2296 "importarchive",
false,
2297 "Import an archived file\n"
2298 "Requires: --infile, --chanid",
"");
2299 add(
"--chanid",
"chanid", -1,
2300 "Channel ID to use when inserting records in DB\n"
2301 "Used with: --importarchive",
"");
2303 add(QStringList{
"-r",
"--isremote"},
2305 "Check if infile is on a remote filesystem\n"
2306 "Requires: --infile\n"
2307 "Returns: 0 on error or file not found\n"
2308 " - 1 file is on a local filesystem\n"
2309 " - 2 file is on a remote filesystem",
"");
2311 add(QStringList{
"-b",
"--burndvd"},
2313 "Burn a created DVD to a blank disc\n"
2314 "Optional: --mediatype, --erasedvdrw, --nativeformat",
"");
2315 add(
"--mediatype",
"mediatype", 0,
2316 "Type of media to burn\n"
2317 "Used with: --burndvd\n"
2318 " 0 = single layer DVD (default)\n"
2319 " 1 = dual layer DVD\n"
2320 " 2 = rewritable DVD",
"");
2321 add(
"--erasedvdrw",
"erasedvdrw",
false,
2322 "Force an erase of DVD-R/W Media\n"
2323 "Used with: --burndvd (optional)",
"");
2324 add(
"--nativeformat",
"nativeformat",
false,
2325 "Archive is a native archive format\n"
2326 "Used with: --burndvd (optional)",
"");
2328 add(QStringList{
"-s",
"--sup2dast"},
2330 "Convert projectX subtitles to DVD subtitles\n"
2331 "Requires: --infile, --ifofile, --delay",
"");
2332 add(
"--ifofile",
"ifofile",
"",
2333 "Filename of ifo file\n"
2334 "Used with: --sup2dast",
"");
2335 add(
"--delay",
"delay", 0,
2336 "Delay in ms to add to subtitles (default 0)\n"
2337 "Used with: --sup2dast",
"");
2363 QCoreApplication a(argc, argv);
2364 QCoreApplication::setApplicationName(
"mytharchivehelper");
2367 QString mask(
"jobqueue");
2379 LOG(VB_GENERAL, LOG_ERR,
"Failed to init MythContext, exiting.");
2411 if (inFile.isEmpty())
2413 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile in -t/--grabthumbnail "
2418 if (thumbList.isEmpty())
2420 LOG(VB_GENERAL, LOG_ERR,
"Missing --thumblist in -t/--grabthumbnail"
2425 if (outFile.isEmpty())
2427 LOG(VB_GENERAL, LOG_ERR,
"Missing --outfile in -t/--grabthumbnail "
2433 if (bGetDBParameters)
2435 if (outFile.isEmpty())
2437 LOG(VB_GENERAL, LOG_ERR,
"Missing argument to -p/--getdbparameters "
2445 if (inFile.isEmpty())
2447 LOG(VB_GENERAL, LOG_ERR,
2448 "Missing argument to -r/--isremote option");
2455 if (mediaType < 0 || mediaType > 2)
2457 LOG(VB_GENERAL, LOG_ERR, QString(
"Invalid mediatype given: %1")
2465 if (outFile.isEmpty())
2467 LOG(VB_GENERAL, LOG_ERR,
"Missing argument to -n/--nativearchive "
2475 if (inFile.isEmpty())
2477 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile argument to "
2478 "-f/--importarchive option");
2485 if (inFile.isEmpty())
2487 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile in -i/--getfileinfo "
2492 if (outFile.isEmpty())
2494 LOG(VB_GENERAL, LOG_ERR,
"Missing --outfile in -i/--getfileinfo "
2502 if (inFile.isEmpty())
2504 LOG(VB_GENERAL, LOG_ERR,
2505 "Missing --infile in -s/--sup2dast option");
2509 if (ifoFile.isEmpty())
2511 LOG(VB_GENERAL, LOG_ERR,
2512 "Missing --ifofile in -s/--sup2dast option");
2518 res =
grabThumbnail(inFile, thumbList, outFile, frameCount);
2519 else if (bGetDBParameters)
2521 else if (bNativeArchive)
2523 else if (bImportArchive)
2525 else if (bGetFileInfo)
2530 res =
doBurnDVD(mediaType, bEraseDVDRW, bNativeFormat);
2533 QByteArray inFileBA = inFile.toLocal8Bit();
2534 QByteArray ifoFileBA = ifoFile.toLocal8Bit();
2535 res =
sup2dast(inFileBA.constData(), ifoFileBA.constData(), delay);