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>
65 #include <libmythbase/mythversion.h>
70 #include <libavcodec/avcodec.h>
71 #include <libavformat/avformat.h>
72 #include <libavutil/imgutils.h>
77 #include "../mytharchive/archiveutil.h"
78 #include "../mytharchive/remoteavformatcontext.h"
88 static bool copyFile(
const QString &source,
const QString &destination);
90 const QString &xmlFile,
int chanID);
91 static int importVideo(
const QDomElement &itemNode,
const QString &xmlFile);
92 static int exportRecording(QDomElement &itemNode,
const QString &saveDirectory);
93 static int exportVideo(QDomElement &itemNode,
const QString &saveDirectory);
95 static QString
findNodeText(
const QDomElement &elem,
const QString &nodeName);
96 static int getFieldList(QStringList &fieldList,
const QString &tableName);
103 QFile
file(tempDir +
"/logs/mythburn.lck");
105 if (!
file.open(QIODevice::WriteOnly | QIODevice::Truncate))
106 LOG(VB_GENERAL, LOG_ERR,
"NativeArchive: Failed to create lock file");
108 QString pid = QString(
"%1").arg(getpid());
109 file.write(pid.toLatin1());
118 QFile::remove(tempDir +
"/logs/mythburn.lck");
123 QString command = QString(
"mythutil --copyfile --infile '%1' --outfile '%2'")
124 .arg(source, destination);
128 LOG(VB_JOBQUEUE, LOG_ERR,
129 QString(
"Failed while running %1. Result: %2").arg(command).arg(res));
138 LOG(VB_JOBQUEUE, LOG_INFO,
"Creating ISO image");
142 tempDirectory +=
"work/";
145 QString command = mkisofs +
" -R -J -V 'MythTV Archive' -o ";
146 command += tempDirectory +
"mythburn.iso " + sourceDirectory;
151 LOG(VB_JOBQUEUE, LOG_ERR,
152 QString(
"Failed while running mkisofs. Result: %1") .arg(res));
156 LOG(VB_JOBQUEUE, LOG_INFO,
"Finished creating ISO image");
160 static int burnISOImage(
int mediaType,
bool bEraseDVDRW,
bool nativeFormat)
164 LOG(VB_JOBQUEUE, LOG_INFO,
"Burning ISO image to " + dvdDrive);
169 tempDirectory +=
"work/";
175 command +=
" -speed=" + QString::number(driveSpeed);
179 if (mediaType ==
AD_DVD_RW && bEraseDVDRW)
181 command +=
" -use-the-force-luke -Z " + dvdDrive;
182 command +=
" -V 'MythTV Archive' -R -J " + tempDirectory;
186 command +=
" -Z " + dvdDrive;
187 command +=
" -V 'MythTV Archive' -R -J " + tempDirectory;
192 if (mediaType ==
AD_DVD_RW && bEraseDVDRW)
194 command +=
" -dvd-compat -use-the-force-luke -Z " + dvdDrive;
195 command +=
" -dvd-video -V 'MythTV DVD' " + tempDirectory +
"/dvd";
199 command +=
" -dvd-compat -Z " + dvdDrive;
200 command +=
" -dvd-video -V 'MythTV DVD' " + tempDirectory +
"/dvd";
207 LOG(VB_JOBQUEUE, LOG_ERR,
208 QString(
"Failed while running growisofs. Result: %1") .arg(res));
212 LOG(VB_JOBQUEUE, LOG_INFO,
"Finished burning ISO image");
218 static int doBurnDVD(
int mediaType,
bool bEraseDVDRW,
bool nativeFormat)
221 "MythArchiveLastRunStart",
225 int res =
burnISOImage(mediaType, bEraseDVDRW, nativeFormat);
228 "MythArchiveLastRunEnd",
238 QDomDocument doc(
"archivejob");
240 if (!
file.open(QIODevice::ReadOnly))
242 LOG(VB_JOBQUEUE, LOG_ERR,
"Could not open job file: " + jobFile);
246 if (!doc.setContent(&
file))
248 LOG(VB_JOBQUEUE, LOG_ERR,
"Could not load job file: " + jobFile);
256 bool bCreateISO =
false;
257 bool bEraseDVDRW =
false;
258 bool bDoBurn =
false;
259 QString saveDirectory;
262 QDomNodeList nodeList = doc.elementsByTagName(
"options");
263 if (nodeList.count() == 1)
265 QDomNode node = nodeList.item(0);
266 QDomElement
options = node.toElement();
269 bCreateISO = (
options.attribute(
"createiso",
"0") ==
"1");
270 bEraseDVDRW = (
options.attribute(
"erasedvdrw",
"0") ==
"1");
271 bDoBurn = (
options.attribute(
"doburn",
"0") ==
"1");
272 mediaType =
options.attribute(
"mediatype",
"0").toInt();
273 saveDirectory =
options.attribute(
"savedirectory",
"");
274 if (!saveDirectory.endsWith(
"/"))
275 saveDirectory +=
"/";
280 LOG(VB_JOBQUEUE, LOG_ERR,
281 QString(
"Found %1 options nodes - should be 1")
282 .arg(nodeList.count()));
285 LOG(VB_JOBQUEUE, LOG_INFO,
286 QString(
"Options - createiso: %1,"
287 " doburn: %2, mediatype: %3, erasedvdrw: %4")
288 .arg(bCreateISO).arg(bDoBurn).arg(mediaType).arg(bEraseDVDRW));
289 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"savedirectory: %1").arg(saveDirectory));
294 saveDirectory = tempDir;
295 if (!saveDirectory.endsWith(
"/"))
296 saveDirectory +=
"/";
298 saveDirectory +=
"work/";
300 QDir dir(saveDirectory);
304 LOG(VB_GENERAL, LOG_ERR,
305 "NativeArchive: Failed to clear work directory");
307 dir.mkpath(saveDirectory);
310 LOG(VB_JOBQUEUE, LOG_INFO,
311 QString(
"Saving files to : %1").arg(saveDirectory));
314 nodeList = doc.elementsByTagName(
"file");
315 if (nodeList.count() < 1)
317 LOG(VB_JOBQUEUE, LOG_ERR,
"Cannot find any file nodes?");
326 for (
int x = 0; x < nodeList.count(); x++)
328 node = nodeList.item(x);
329 elem = node.toElement();
332 type = elem.attribute(
"type");
334 if (
type.toLower() ==
"recording")
336 else if (
type.toLower() ==
"video")
340 LOG(VB_JOBQUEUE, LOG_ERR,
341 QString(
"Don't know how to archive items of type '%1'")
342 .arg(
type.toLower()));
349 if (mediaType !=
AD_FILE && bDoBurn)
353 LOG(VB_JOBQUEUE, LOG_ERR,
354 "Native archive job failed to complete");
364 LOG(VB_JOBQUEUE, LOG_ERR,
"Native archive job failed to complete");
369 LOG(VB_JOBQUEUE, LOG_INFO,
"Native archive job completed OK");
374 static const QRegularExpression
badChars { R
"((/|\\|:|'|"|\?|\|))" };
388 if (query.
exec(
"DESCRIBE " + tableName))
392 fieldList.append(query.
value(0).toString());
400 return fieldList.count();
404 const QString &saveDirectory)
410 QString title =
fixFilename(itemNode.attribute(
"title"));
411 QString
filename = itemNode.attribute(
"filename");
412 bool doDelete = (itemNode.attribute(
"delete",
"0") ==
"0");
413 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"Archiving %1 (%2), do delete: %3")
414 .arg(title,
filename, doDelete ?
"true" :
"false"));
418 LOG(VB_JOBQUEUE, LOG_ERR,
"Bad title or filename");
424 LOG(VB_JOBQUEUE, LOG_ERR,
425 QString(
"Failed to extract chanID and startTime from '%1'")
431 QDir dir(saveDirectory + title);
433 dir.mkpath(saveDirectory + title);
435 LOG(VB_GENERAL, LOG_ERR,
"Failed to create savedir: " +
ENO);
437 LOG(VB_JOBQUEUE, LOG_INFO,
"Creating xml file for " + title);
438 QDomDocument doc(
"MYTHARCHIVEITEM");
440 QDomElement root = doc.createElement(
"item");
441 doc.appendChild(root);
442 root.setAttribute(
"type",
"recording");
443 root.setAttribute(
"databaseversion",
dbVersion);
445 QDomElement recorded = doc.createElement(
"recorded");
446 root.appendChild(recorded);
449 QStringList fieldList;
453 query.
prepare(
"SELECT " + fieldList.join(
",")
455 " WHERE chanid = :CHANID and starttime = :STARTTIME;");
457 query.
bindValue(
":STARTTIME", startTime);
464 for (
int x = 0; x < fieldList.size(); x++)
466 elem = doc.createElement(fieldList[x]);
467 text = doc.createTextNode(query.
value(x).toString());
468 elem.appendChild(text);
469 recorded.appendChild(elem);
472 LOG(VB_JOBQUEUE, LOG_INFO,
"Created recorded element for " + title);
476 LOG(VB_JOBQUEUE, LOG_INFO,
"Failed to get recorded field list");
480 query.
prepare(
"SELECT chanid, channum, callsign, name "
481 "FROM channel WHERE chanid = :CHANID;");
486 QDomElement channel = doc.createElement(
"channel");
487 channel.setAttribute(
"chanid", query.
value(0).toString());
488 channel.setAttribute(
"channum", query.
value(1).toString());
489 channel.setAttribute(
"callsign", query.
value(2).toString());
490 channel.setAttribute(
"name", query.
value(3).toString());
491 root.appendChild(channel);
492 LOG(VB_JOBQUEUE, LOG_INFO,
"Created channel element for " + title);
497 LOG(VB_JOBQUEUE, LOG_ERR,
498 "Cannot find channel details for chanid " + chanID);
499 QDomElement channel = doc.createElement(
"channel");
500 channel.setAttribute(
"chanid", chanID);
501 channel.setAttribute(
"channum",
"unknown");
502 channel.setAttribute(
"callsign",
"unknown");
503 channel.setAttribute(
"name",
"unknown");
504 root.appendChild(channel);
505 LOG(VB_JOBQUEUE, LOG_INFO,
506 "Created a default channel element for " + title);
510 query.
prepare(
"SELECT credits.person, role, people.name "
511 "FROM recordedcredits AS credits "
512 "LEFT JOIN people ON credits.person = people.person "
513 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
515 query.
bindValue(
":STARTTIME", startTime);
519 QDomElement credits = doc.createElement(
"credits");
522 QDomElement credit = doc.createElement(
"credit");
523 credit.setAttribute(
"personid", query.
value(0).toString());
524 credit.setAttribute(
"name", query.
value(2).toString());
525 credit.setAttribute(
"role", query.
value(1).toString());
526 credits.appendChild(credit);
528 root.appendChild(credits);
529 LOG(VB_JOBQUEUE, LOG_INFO,
"Created credits element for " + title);
533 query.
prepare(
"SELECT `system`, rating FROM recordedrating "
534 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
536 query.
bindValue(
":STARTTIME", startTime);
540 QDomElement
rating = doc.createElement(
"rating");
541 rating.setAttribute(
"system", query.
value(0).toString());
542 rating.setAttribute(
"rating", query.
value(1).toString());
544 LOG(VB_JOBQUEUE, LOG_INFO,
"Created rating element for " + title);
548 QDomElement recordedmarkup = doc.createElement(
"recordedmarkup");
549 query.
prepare(
"SELECT chanid, starttime, mark, type, data "
550 "FROM recordedmarkup "
551 "WHERE chanid = :CHANID and starttime = :STARTTIME;");
553 query.
bindValue(
":STARTTIME", startTime);
558 QDomElement
mark = doc.createElement(
"mark");
559 mark.setAttribute(
"mark", query.
value(2).toString());
560 mark.setAttribute(
"type", query.
value(3).toString());
561 mark.setAttribute(
"data", query.
value(4).toString());
562 recordedmarkup.appendChild(
mark);
564 root.appendChild(recordedmarkup);
565 LOG(VB_JOBQUEUE, LOG_INFO,
"Created recordedmarkup element for " + title);
569 QDomElement recordedseek = doc.createElement(
"recordedseek");
570 query.
prepare(
"SELECT chanid, starttime, mark, `offset`, type "
572 "WHERE chanid = :CHANID and starttime = :STARTTIME;");
574 query.
bindValue(
":STARTTIME", startTime);
579 QDomElement
mark = doc.createElement(
"mark");
580 mark.setAttribute(
"mark", query.
value(2).toString());
581 mark.setAttribute(
"offset", query.
value(3).toString());
582 mark.setAttribute(
"type", query.
value(4).toString());
583 recordedseek.appendChild(
mark);
585 root.appendChild(recordedseek);
586 LOG(VB_JOBQUEUE, LOG_INFO,
587 "Created recordedseek element for " + title);
592 QString xmlFile = saveDirectory + title +
"/" + baseName +
".xml";
594 if (!f.open(QIODevice::WriteOnly))
596 LOG(VB_JOBQUEUE, LOG_ERR,
597 "MythNativeWizard: Failed to open file for writing - " + xmlFile);
602 t << doc.toString(4);
606 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
614 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying preview image");
616 + title +
"/" + baseName +
".png");
621 LOG(VB_JOBQUEUE, LOG_INFO,
"Item Archived OK");
627 const QString &saveDirectory)
632 QString coverFile =
"";
634 QString title =
fixFilename(itemNode.attribute(
"title"));
635 QString
filename = itemNode.attribute(
"filename");
636 bool doDelete = (itemNode.attribute(
"delete",
"0") ==
"0");
637 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"Archiving %1 (%2), do delete: %3")
638 .arg(title,
filename, doDelete ?
"true" :
"false"));
642 LOG(VB_JOBQUEUE, LOG_ERR,
"Bad title or filename");
647 QDir dir(saveDirectory + title);
649 dir.mkdir(saveDirectory + title);
651 LOG(VB_JOBQUEUE, LOG_INFO,
"Creating xml file for " + title);
652 QDomDocument doc(
"MYTHARCHIVEITEM");
654 QDomElement root = doc.createElement(
"item");
655 doc.appendChild(root);
656 root.setAttribute(
"type",
"video");
657 root.setAttribute(
"databaseversion",
dbVersion);
659 QDomElement video = doc.createElement(
"videometadata");
660 root.appendChild(video);
664 query.
prepare(
"SELECT intid, title, director, plot, rating, inetref, "
665 "year, userrating, length, showlevel, filename, coverfile, "
666 "childid, browse, playcommand, category "
667 "FROM videometadata WHERE filename = :FILENAME;");
675 elem = doc.createElement(
"intid");
676 text = doc.createTextNode(query.
value(0).toString());
677 intID = query.
value(0).toInt();
678 elem.appendChild(text);
679 video.appendChild(elem);
681 elem = doc.createElement(
"title");
682 text = doc.createTextNode(query.
value(1).toString());
683 elem.appendChild(text);
684 video.appendChild(elem);
686 elem = doc.createElement(
"director");
687 text = doc.createTextNode(query.
value(2).toString());
688 elem.appendChild(text);
689 video.appendChild(elem);
691 elem = doc.createElement(
"plot");
692 text = doc.createTextNode(query.
value(3).toString());
693 elem.appendChild(text);
694 video.appendChild(elem);
696 elem = doc.createElement(
"rating");
697 text = doc.createTextNode(query.
value(4).toString());
698 elem.appendChild(text);
699 video.appendChild(elem);
701 elem = doc.createElement(
"inetref");
702 text = doc.createTextNode(query.
value(5).toString());
703 elem.appendChild(text);
704 video.appendChild(elem);
706 elem = doc.createElement(
"year");
707 text = doc.createTextNode(query.
value(6).toString());
708 elem.appendChild(text);
709 video.appendChild(elem);
711 elem = doc.createElement(
"userrating");
712 text = doc.createTextNode(query.
value(7).toString());
713 elem.appendChild(text);
714 video.appendChild(elem);
716 elem = doc.createElement(
"length");
717 text = doc.createTextNode(query.
value(8).toString());
718 elem.appendChild(text);
719 video.appendChild(elem);
721 elem = doc.createElement(
"showlevel");
722 text = doc.createTextNode(query.
value(9).toString());
723 elem.appendChild(text);
724 video.appendChild(elem);
727 QString fname = query.
value(10).toString();
731 elem = doc.createElement(
"filename");
732 text = doc.createTextNode(fname);
733 elem.appendChild(text);
734 video.appendChild(elem);
736 elem = doc.createElement(
"coverfile");
737 text = doc.createTextNode(query.
value(11).toString());
738 coverFile = query.
value(11).toString();
739 elem.appendChild(text);
740 video.appendChild(elem);
742 elem = doc.createElement(
"childid");
743 text = doc.createTextNode(query.
value(12).toString());
744 elem.appendChild(text);
745 video.appendChild(elem);
747 elem = doc.createElement(
"browse");
748 text = doc.createTextNode(query.
value(13).toString());
749 elem.appendChild(text);
750 video.appendChild(elem);
752 elem = doc.createElement(
"playcommand");
753 text = doc.createTextNode(query.
value(14).toString());
754 elem.appendChild(text);
755 video.appendChild(elem);
757 elem = doc.createElement(
"categoryid");
758 text = doc.createTextNode(query.
value(15).toString());
759 categoryID = query.
value(15).toInt();
760 elem.appendChild(text);
761 video.appendChild(elem);
763 LOG(VB_JOBQUEUE, LOG_INFO,
764 "Created videometadata element for " + title);
768 query.
prepare(
"SELECT intid, category "
769 "FROM videocategory WHERE intid = :INTID;");
774 QDomElement category = doc.createElement(
"category");
775 category.setAttribute(
"intid", query.
value(0).toString());
776 category.setAttribute(
"category", query.
value(1).toString());
777 root.appendChild(category);
778 LOG(VB_JOBQUEUE, LOG_INFO,
779 "Created videocategory element for " + title);
783 QDomElement countries = doc.createElement(
"countries");
784 root.appendChild(countries);
786 query.
prepare(
"SELECT intid, country "
787 "FROM videometadatacountry INNER JOIN videocountry "
788 "ON videometadatacountry.idcountry = videocountry.intid "
789 "WHERE idvideo = :INTID;");
799 QDomElement country = doc.createElement(
"country");
800 country.setAttribute(
"intid", query.
value(0).toString());
801 country.setAttribute(
"country", query.
value(1).toString());
802 countries.appendChild(country);
804 LOG(VB_JOBQUEUE, LOG_INFO,
"Created videocountry element for " + title);
808 QDomElement genres = doc.createElement(
"genres");
809 root.appendChild(genres);
811 query.
prepare(
"SELECT intid, genre "
812 "FROM videometadatagenre INNER JOIN videogenre "
813 "ON videometadatagenre.idgenre = videogenre.intid "
814 "WHERE idvideo = :INTID;");
824 QDomElement genre = doc.createElement(
"genre");
825 genre.setAttribute(
"intid", query.
value(0).toString());
826 genre.setAttribute(
"genre", query.
value(1).toString());
827 genres.appendChild(genre);
829 LOG(VB_JOBQUEUE, LOG_INFO,
"Created videogenre element for " + title);
834 QString xmlFile = saveDirectory + title +
"/"
835 + fileInfo.fileName() +
".xml";
837 if (!f.open(QIODevice::WriteOnly))
839 LOG(VB_JOBQUEUE, LOG_INFO,
840 "MythNativeWizard: Failed to open file for writing - " + xmlFile);
845 t << doc.toString(4);
849 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
851 +
"/" + fileInfo.fileName());
858 fileInfo.setFile(coverFile);
859 if (fileInfo.exists())
861 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying cover file");
862 res =
copyFile(coverFile, saveDirectory + title
863 +
"/" + fileInfo.fileName());
870 LOG(VB_JOBQUEUE, LOG_INFO,
"Item Archived OK");
878 QDomDocument doc(
"mydocument");
880 if (!
file.open(QIODevice::ReadOnly))
882 LOG(VB_JOBQUEUE, LOG_ERR,
883 "Failed to open file for reading - " + xmlFile);
887 if (!doc.setContent(&
file))
890 LOG(VB_JOBQUEUE, LOG_ERR,
891 "Failed to read from xml file - " + xmlFile);
896 QString docType = doc.doctype().name();
899 QDomNodeList itemNodeList;
901 QDomElement itemNode;
903 if (docType ==
"MYTHARCHIVEITEM")
905 itemNodeList = doc.elementsByTagName(
"item");
907 if (itemNodeList.count() < 1)
909 LOG(VB_JOBQUEUE, LOG_ERR,
910 "Couldn't find an 'item' element in XML file");
914 node = itemNodeList.item(0);
915 itemNode = node.toElement();
916 type = itemNode.attribute(
"type");
917 dbVersion = itemNode.attribute(
"databaseversion");
919 LOG(VB_JOBQUEUE, LOG_INFO,
920 QString(
"Archive DB version: %1, Local DB version: %2")
925 LOG(VB_JOBQUEUE, LOG_ERR,
"Not a native archive xml file - " + xmlFile);
929 if (
type ==
"recording")
942 const QString &xmlFile,
int chanID)
944 LOG(VB_JOBQUEUE, LOG_INFO,
945 QString(
"Import recording using chanID: %1").arg(chanID));
946 LOG(VB_JOBQUEUE, LOG_INFO,
947 QString(
"Archived recording xml file: %1").arg(xmlFile));
949 QString videoFile = xmlFile.left(xmlFile.length() - 4);
950 QString basename = videoFile;
951 int pos = videoFile.lastIndexOf(
'/');
953 basename = videoFile.mid(pos + 1);
955 QDomNodeList nodeList = itemNode.elementsByTagName(
"recorded");
956 if (nodeList.count() < 1)
958 LOG(VB_JOBQUEUE, LOG_ERR,
959 "Couldn't find a 'recorded' element in XML file");
963 QDomNode n = nodeList.item(0);
964 QDomElement recordedNode = n.toElement();
965 QString startTime =
findNodeText(recordedNode,
"starttime");
968 query.
prepare(
"SELECT * FROM recorded "
969 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
971 query.
bindValue(
":STARTTIME", startTime);
976 LOG(VB_JOBQUEUE, LOG_ERR,
977 "This recording appears to already exist!!");
984 basename ,
"Default");
987 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file to: " + destFile);
994 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying preview image file to: " + destFile +
".png");
995 if (!
copyFile(videoFile +
".png", destFile +
".png"))
1000 QStringList fieldList;
1001 QStringList bindList;
1002 QDomNodeList nodes = recordedNode.childNodes();
1004 for (
int x = 0; x < nodes.count(); x++)
1006 QDomNode n2 = nodes.item(x);
1007 QString field = n2.nodeName();
1008 fieldList.append(field);
1009 bindList.append(
":" + field.toUpper());
1013 query.
prepare(
"INSERT INTO recorded (" + fieldList.join(
",") +
") "
1014 "VALUES (" + bindList.join(
",") +
");");
1016 query.
bindValue(
":STARTTIME", startTime);
1018 for (
int x = 0; x < fieldList.count(); x++)
1022 LOG(VB_JOBQUEUE, LOG_INFO,
"Inserted recorded details into database");
1027 nodeList = itemNode.elementsByTagName(
"recordedmarkup");
1028 if (nodeList.count() < 1)
1030 LOG(VB_JOBQUEUE, LOG_WARNING,
1031 "Couldn't find a 'recordedmarkup' element in XML file");
1035 QDomNode n3 = nodeList.item(0);
1036 QDomElement markupNode = n3.toElement();
1038 nodeList = markupNode.elementsByTagName(
"mark");
1039 if (nodeList.count() < 1)
1041 LOG(VB_JOBQUEUE, LOG_WARNING,
1042 "Couldn't find any 'mark' elements in XML file");
1047 query.
prepare(
"DELETE FROM recordedmarkup "
1048 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
1050 query.
bindValue(
":STARTTIME", startTime);
1056 for (
int x = 0; x < nodeList.count(); x++)
1058 QDomNode n4 = nodeList.item(x);
1059 QDomElement e = n4.toElement();
1060 query.
prepare(
"INSERT INTO recordedmarkup (chanid, starttime, "
1062 "VALUES(:CHANID,:STARTTIME,:MARK,:TYPE,:DATA);");
1064 query.
bindValue(
":STARTTIME", startTime);
1065 query.
bindValue(
":MARK", e.attribute(
"mark"));
1066 query.
bindValue(
":TYPE", e.attribute(
"type"));
1067 query.
bindValue(
":DATA", e.attribute(
"data"));
1076 LOG(VB_JOBQUEUE, LOG_INFO,
1077 "Inserted recordedmarkup details into database");
1082 nodeList = itemNode.elementsByTagName(
"recordedseek");
1083 if (nodeList.count() < 1)
1085 LOG(VB_JOBQUEUE, LOG_WARNING,
1086 "Couldn't find a 'recordedseek' element in XML file");
1090 QDomNode n5 = nodeList.item(0);
1091 QDomElement markupNode = n5.toElement();
1093 nodeList = markupNode.elementsByTagName(
"mark");
1094 if (nodeList.count() < 1)
1096 LOG(VB_JOBQUEUE, LOG_WARNING,
1097 "Couldn't find any 'mark' elements in XML file");
1102 query.
prepare(
"DELETE FROM recordedseek "
1103 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
1105 query.
bindValue(
":STARTTIME", startTime);
1109 for (
int x = 0; x < nodeList.count(); x++)
1111 QDomNode n6 = nodeList.item(x);
1112 QDomElement e = n6.toElement();
1113 query.
prepare(
"INSERT INTO recordedseek (chanid, starttime, "
1114 "mark, `offset`, type)"
1115 "VALUES(:CHANID,:STARTTIME,:MARK,:OFFSET,:TYPE);");
1117 query.
bindValue(
":STARTTIME", startTime);
1118 query.
bindValue(
":MARK", e.attribute(
"mark"));
1119 query.
bindValue(
":OFFSET", e.attribute(
"offset"));
1120 query.
bindValue(
":TYPE", e.attribute(
"type"));
1129 LOG(VB_JOBQUEUE, LOG_INFO,
1130 "Inserted recordedseek details into database");
1138 LOG(VB_JOBQUEUE, LOG_INFO,
"Import completed OK");
1145 LOG(VB_JOBQUEUE, LOG_INFO,
"Importing video");
1146 LOG(VB_JOBQUEUE, LOG_INFO,
1147 QString(
"Archived video xml file: %1").arg(xmlFile));
1149 QString videoFile = xmlFile.left(xmlFile.length() - 4);
1150 QFileInfo fileInfo(videoFile);
1151 QString basename = fileInfo.fileName();
1153 QDomNodeList nodeList = itemNode.elementsByTagName(
"videometadata");
1154 if (nodeList.count() < 1)
1156 LOG(VB_JOBQUEUE, LOG_ERR,
1157 "Couldn't find a 'videometadata' element in XML file");
1161 QDomNode n = nodeList.item(0);
1162 QDomElement videoNode = n.toElement();
1166 QString origFilename =
findNodeText(videoNode,
"filename");
1167 QStringList dirList = origFilename.split(
"/", Qt::SkipEmptyParts);
1169 for (
int x = 0; x < dirList.count() - 1; x++)
1171 path +=
"/" + dirList[x];
1172 if (!dir.exists(path))
1174 if (!dir.mkdir(path))
1176 LOG(VB_JOBQUEUE, LOG_ERR,
1177 QString(
"Couldn't create directory '%1'").arg(path));
1183 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying video file");
1184 if (!
copyFile(videoFile, path +
"/" + basename))
1192 fileInfo.setFile(videoFile);
1193 QString archivePath = fileInfo.absolutePath();
1195 QString coverFilename =
findNodeText(videoNode,
"coverfile");
1196 fileInfo.setFile(coverFilename);
1197 coverFilename = fileInfo.fileName();
1199 fileInfo.setFile(archivePath +
"/" + coverFilename);
1200 if (fileInfo.exists())
1202 LOG(VB_JOBQUEUE, LOG_INFO,
"Copying cover file");
1204 if (!
copyFile(archivePath +
"/" + coverFilename, artworkDir +
"/" + coverFilename))
1211 coverFilename =
"No Cover";
1216 query.
prepare(
"INSERT INTO videometadata (title, director, plot, rating, inetref, "
1217 "year, userrating, length, showlevel, filename, coverfile, "
1218 "childid, browse, playcommand, category) "
1219 "VALUES(:TITLE,:DIRECTOR,:PLOT,:RATING,:INETREF,:YEAR,"
1220 ":USERRATING,:LENGTH,:SHOWLEVEL,:FILENAME,:COVERFILE,"
1221 ":CHILDID,:BROWSE,:PLAYCOMMAND,:CATEGORY);");
1231 query.
bindValue(
":FILENAME", path +
"/" + basename);
1232 query.
bindValue(
":COVERFILE", artworkDir +
"/" + coverFilename);
1240 LOG(VB_JOBQUEUE, LOG_INFO,
1241 "Inserted videometadata details into database");
1251 query.
prepare(
"SELECT intid FROM videometadata WHERE filename = :FILENAME;");
1252 query.
bindValue(
":FILENAME", path +
"/" + basename);
1255 intid = query.
value(0).toInt();
1263 LOG(VB_JOBQUEUE, LOG_INFO,
1264 QString(
"'intid' of inserted video is: %1").arg(intid));
1267 nodeList = itemNode.elementsByTagName(
"genres");
1268 if (nodeList.count() < 1)
1270 LOG(VB_JOBQUEUE, LOG_ERR,
"No 'genres' element found in XML file");
1274 n = nodeList.item(0);
1275 QDomElement genresNode = n.toElement();
1277 nodeList = genresNode.elementsByTagName(
"genre");
1278 if (nodeList.count() < 1)
1280 LOG(VB_JOBQUEUE, LOG_WARNING,
1281 "Couldn't find any 'genre' elements in XML file");
1285 for (
int x = 0; x < nodeList.count(); x++)
1287 n = nodeList.item(x);
1288 QDomElement e = n.toElement();
1290 QString genre = e.attribute(
"genre");
1293 query.
prepare(
"SELECT intid FROM videogenre "
1294 "WHERE genre = :GENRE");
1298 genreID = query.
value(0).toInt();
1303 query.
prepare(
"INSERT INTO videogenre (genre) VALUES(:GENRE);");
1307 "insert videogenre", query);
1310 query.
prepare(
"SELECT intid FROM videogenre "
1311 "WHERE genre = :GENRE");
1313 if (!query.
exec() || !query.
next())
1315 LOG(VB_JOBQUEUE, LOG_ERR,
1316 "Couldn't add genre to database");
1319 genreID = query.
value(0).toInt();
1323 query.
prepare(
"INSERT INTO videometadatagenre (idvideo, idgenre)"
1324 "VALUES (:IDVIDEO, :IDGENRE);");
1329 "insert videometadatagenre", query);
1332 LOG(VB_JOBQUEUE, LOG_INFO,
"Inserted genre details into database");
1337 nodeList = itemNode.elementsByTagName(
"countries");
1338 if (nodeList.count() < 1)
1340 LOG(VB_JOBQUEUE, LOG_INFO,
"No 'countries' element found in XML file");
1344 n = nodeList.item(0);
1345 QDomElement countriesNode = n.toElement();
1347 nodeList = countriesNode.elementsByTagName(
"country");
1348 if (nodeList.count() < 1)
1350 LOG(VB_JOBQUEUE, LOG_WARNING,
1351 "Couldn't find any 'country' elements in XML file");
1355 for (
int x = 0; x < nodeList.count(); x++)
1357 n = nodeList.item(x);
1358 QDomElement e = n.toElement();
1360 QString country = e.attribute(
"country");
1363 query.
prepare(
"SELECT intid FROM videocountry "
1364 "WHERE country = :COUNTRY");
1368 countryID = query.
value(0).toInt();
1373 query.
prepare(
"INSERT INTO videocountry (country) VALUES(:COUNTRY);");
1377 "insert videocountry", query);
1380 query.
prepare(
"SELECT intid FROM videocountry "
1381 "WHERE country = :COUNTRY");
1383 if (!query.
exec() || !query.
next())
1385 LOG(VB_JOBQUEUE, LOG_ERR,
1386 "Couldn't add country to database");
1389 countryID = query.
value(0).toInt();
1393 query.
prepare(
"INSERT INTO videometadatacountry (idvideo, idcountry)"
1394 "VALUES (:IDVIDEO, :IDCOUNTRY);");
1396 query.
bindValue(
":IDCOUNTRY", countryID);
1399 "insert videometadatacountry", query);
1402 LOG(VB_JOBQUEUE, LOG_INFO,
1403 "Inserted country details into database");
1408 nodeList = itemNode.elementsByTagName(
"category");
1409 if (nodeList.count() < 1)
1411 LOG(VB_JOBQUEUE, LOG_ERR,
"No 'category' element found in XML file");
1415 n = nodeList.item(0);
1416 QDomElement e = n.toElement();
1418 QString category = e.attribute(
"category");
1420 query.
prepare(
"SELECT intid FROM videocategory "
1421 "WHERE category = :CATEGORY");
1425 categoryID = query.
value(0).toInt();
1430 query.
prepare(
"INSERT INTO videocategory (category) VALUES(:CATEGORY);");
1434 "insert videocategory", query);
1437 query.
prepare(
"SELECT intid FROM videocategory "
1438 "WHERE category = :CATEGORY");
1442 categoryID = query.
value(0).toInt();
1446 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't add category to database");
1452 query.
prepare(
"UPDATE videometadata "
1453 "SET category = :CATEGORY "
1454 "WHERE intid = :INTID;");
1455 query.
bindValue(
":CATEGORY", categoryID);
1459 "update category", query);
1461 LOG(VB_JOBQUEUE, LOG_INFO,
"Fixed the category in the database");
1464 LOG(VB_JOBQUEUE, LOG_INFO,
"Import completed OK");
1471 QDomNodeList nodeList = elem.elementsByTagName(nodeName);
1472 if (nodeList.count() < 1)
1474 LOG(VB_GENERAL, LOG_ERR,
1475 QString(
"Couldn't find a '%1' element in XML file") .arg(nodeName));
1479 QDomNode n = nodeList.item(0);
1480 QDomElement e = n.toElement();
1483 for (QDomNode node = e.firstChild(); !node.isNull();
1484 node = node.nextSibling())
1486 QDomText
t = node.toText();
1496 if ((nodeName ==
"recgroup") ||
1497 (nodeName ==
"playgroup"))
1501 else if ((nodeName ==
"recordid") ||
1502 (nodeName ==
"seriesid") ||
1503 (nodeName ==
"programid") ||
1504 (nodeName ==
"profile"))
1515 query.
prepare(
"DELETE FROM archiveitems;");
1525 "MythArchiveLastRunStart",
1531 "MythArchiveLastRunEnd",
1534 (res == 0 ?
"Success" :
"Failed"));
1548 static int grabThumbnail(
const QString& inFile,
const QString& thumbList,
const QString& outFile,
int frameCount)
1551 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"grabThumbnail(): Opening '%1'")
1558 LOG(VB_JOBQUEUE, LOG_ERR,
"grabThumbnail(): Couldn't open input file" +
1564 int ret = avformat_find_stream_info(inputFC,
nullptr);
1567 LOG(VB_JOBQUEUE, LOG_ERR,
1568 QString(
"Couldn't get stream info, error #%1").arg(ret));
1573 int videostream = -1;
1578 for (
uint i = 0; i < inputFC->nb_streams; i++)
1580 AVStream *st = inputFC->streams[i];
1581 if (inputFC->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
1584 width = st->codecpar->width;
1585 height = st->codecpar->height;
1586 if (st->r_frame_rate.den && st->r_frame_rate.num)
1587 fps = av_q2d(st->r_frame_rate);
1589 fps = 1/av_q2d(st->time_base);
1594 if (videostream == -1)
1596 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't find a video stream");
1601 AVCodecContext *codecCtx = codecmap.
GetCodecContext(inputFC->streams[videostream]);
1604 const AVCodec * codec = avcodec_find_decoder(codecCtx->codec_id);
1606 if (codec ==
nullptr)
1608 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't find codec for video stream");
1613 if (avcodec_open2(codecCtx, codec,
nullptr) < 0)
1615 LOG(VB_JOBQUEUE, LOG_ERR,
"Couldn't open codec for video stream");
1620 QStringList list = thumbList.split(
",", Qt::SkipEmptyParts);
1629 memset(&orig, 0,
sizeof(
AVFrame));
1630 memset(&retbuf, 0,
sizeof(
AVFrame));
1633 int bufflen = width * height * 4;
1634 auto *outputbuf =
new unsigned char[bufflen];
1638 bool frameFinished =
false;
1640 while (av_read_frame(inputFC, &pkt) >= 0)
1642 if (pkt.stream_index == videostream)
1645 if (list[thumbCount].toInt() == (
int)(frameNo / fps))
1649 avcodec_flush_buffers(codecCtx);
1650 av_frame_unref(frame);
1651 frameFinished =
false;
1652 ret = avcodec_receive_frame(codecCtx, frame);
1654 frameFinished =
true;
1655 if (ret == 0 || ret == AVERROR(EAGAIN))
1656 avcodec_send_packet(codecCtx, &pkt);
1657 bool keyFrame = (frame->flags & AV_FRAME_FLAG_KEY) != 0;
1659 while (!frameFinished || !keyFrame)
1661 av_packet_unref(&pkt);
1662 int res = av_read_frame(inputFC, &pkt);
1665 if (pkt.stream_index == videostream)
1668 av_frame_unref(frame);
1669 ret = avcodec_receive_frame(codecCtx, frame);
1671 frameFinished =
true;
1672 if (ret == 0 || ret == AVERROR(EAGAIN))
1673 avcodec_send_packet(codecCtx, &pkt);
1674 keyFrame = (frame->flags & AV_FRAME_FLAG_KEY) != 0;
1681 QString saveFormat =
"JPEG";
1682 if (outFile.right(4) ==
".png")
1686 while (count < frameCount)
1694 av_image_fill_arrays(retbuf.data, retbuf.linesize, outputbuf,
1695 AV_PIX_FMT_RGB32, width, height, IMAGE_ALIGN);
1700 copyframe.
Copy(&retbuf, AV_PIX_FMT_RGB32,
tmp,
1701 codecCtx->pix_fmt, width, height);
1703 QImage img(outputbuf, width, height,
1704 QImage::Format_RGB32);
1706 if (!img.save(
filename, qPrintable(saveFormat)))
1708 LOG(VB_GENERAL, LOG_ERR,
1709 QString(
"grabThumbnail(): Failed to save "
1716 if (count <= frameCount)
1719 frameFinished =
false;
1720 while (!frameFinished)
1722 int res = av_read_frame(inputFC, &pkt);
1725 if (pkt.stream_index == videostream)
1728 ret = avcodec_receive_frame(codecCtx, frame);
1730 frameFinished =
true;
1731 if (ret == 0 || ret == AVERROR(EAGAIN))
1732 avcodec_send_packet(codecCtx, &pkt);
1739 if (thumbCount >= list.count())
1744 av_packet_unref(&pkt);
1759 LOG(VB_JOBQUEUE, LOG_INFO,
"Calculating frame count");
1761 AVPacket *pkt = av_packet_alloc();
1764 LOG(VB_GENERAL, LOG_ERR,
"packet allocation failed");
1767 while (av_read_frame(inputFC, pkt) >= 0)
1769 if (pkt->stream_index == vid_id)
1773 av_packet_unref(pkt);
1775 av_packet_free(&pkt);
1784 int pos =
filename.lastIndexOf(
'/');
1799 frm_dir_map_t::iterator it;
1800 uint64_t frames = 0;
1804 if (cutlist.empty())
1810 for (it = cutlist.begin(); it != cutlist.end();)
1819 if (it != cutlist.end())
1841 frames += end - start;
1852 int pos =
filename.lastIndexOf(
'/');
1856 int keyframedist = -1;
1864 if (!posMap.empty())
1871 if (!posMap.empty())
1874 if (fps < 26 && fps > 24)
1880 if (!posMap.empty())
1894 frm_pos_map_t::const_iterator it = posMap.cend();
1896 uint64_t totframes = it.key() * keyframedist;
1900 static int getFileInfo(
const QString& inFile,
const QString& outFile,
int lenMethod)
1903 LOG(VB_JOBQUEUE , LOG_INFO, QString(
"getFileInfo(): Opening '%1'")
1910 LOG(VB_JOBQUEUE, LOG_ERR,
"getFileInfo(): Couldn't open input file" +
1916 int ret = avformat_find_stream_info(inputFC,
nullptr);
1920 LOG(VB_JOBQUEUE, LOG_ERR,
1921 QString(
"Couldn't get stream info, error #%1").arg(ret));
1926 av_dump_format(inputFC, 0, qPrintable(inFile), 0);
1928 QDomDocument doc(
"FILEINFO");
1930 QDomElement root = doc.createElement(
"file");
1931 doc.appendChild(root);
1932 root.setAttribute(
"type", inputFC->iformat->name);
1933 root.setAttribute(
"filename", inFile);
1935 QDomElement streams = doc.createElement(
"streams");
1937 root.appendChild(streams);
1938 streams.setAttribute(
"count", inputFC->nb_streams);
1939 int ffmpegIndex = 0;
1942 for (
uint i = 0; i < inputFC->nb_streams; i++)
1944 AVStream *st = inputFC->streams[i];
1945 std::string buf (256,
'\0');
1947 AVCodecParameters *par = st->codecpar;
1950 avcodec_string(buf.data(), buf.size(), avctx,
static_cast<int>(
false));
1952 switch (st->codecpar->codec_type)
1954 case AVMEDIA_TYPE_VIDEO:
1956 QStringList param = QString::fromStdString(buf).split(
',', Qt::SkipEmptyParts);
1957 QString codec = param[0].remove(
"Video:", Qt::CaseInsensitive).remove(QChar::Null);
1958 QDomElement stream = doc.createElement(
"video");
1959 stream.setAttribute(
"streamindex", i);
1960 stream.setAttribute(
"ffmpegindex", ffmpegIndex++);
1961 stream.setAttribute(
"codec", codec.trimmed());
1962 stream.setAttribute(
"width", par->width);
1963 stream.setAttribute(
"height", par->height);
1964 stream.setAttribute(
"bitrate", (qlonglong)par->bit_rate);
1967 if (st->r_frame_rate.den && st->r_frame_rate.num)
1968 fps = av_q2d(st->r_frame_rate);
1970 fps = 1/av_q2d(st->time_base);
1972 stream.setAttribute(
"fps", fps);
1974 if (par->sample_aspect_ratio.den && par->sample_aspect_ratio.num)
1976 float aspect_ratio = av_q2d(par->sample_aspect_ratio);
1977 if (QString(inputFC->iformat->name) !=
"nuv")
1978 aspect_ratio = ((float)par->width
1979 / par->height) * aspect_ratio;
1981 stream.setAttribute(
"aspectratio", aspect_ratio);
1985 stream.setAttribute(
"aspectratio",
"N/A");
1988 stream.setAttribute(
"id", st->id);
1990 if (st->start_time != (
int) AV_NOPTS_VALUE)
1992 int secs = st->start_time / AV_TIME_BASE;
1993 int us = st->start_time % AV_TIME_BASE;
1994 stream.setAttribute(
"start_time", QString(
"%1.%2")
1995 .arg(secs).arg(av_rescale(us, 1000000, AV_TIME_BASE)));
1999 stream.setAttribute(
"start_time", 0);
2002 streams.appendChild(stream);
2008 int64_t frameCount = 0;
2015 if (inputFC->duration != (
uint) AV_NOPTS_VALUE)
2017 duration = (
uint) (inputFC->duration / AV_TIME_BASE);
2018 root.setAttribute(
"duration", duration);
2019 LOG(VB_JOBQUEUE, LOG_INFO,
2020 QString(
"duration = %1") .arg(duration));
2021 frameCount = (int64_t)(duration * fps);
2025 root.setAttribute(
"duration",
"N/A");
2033 LOG(VB_JOBQUEUE, LOG_INFO,
2034 QString(
"frames = %1").arg(frameCount));
2035 duration = (
uint)(frameCount / fps);
2036 LOG(VB_JOBQUEUE, LOG_INFO,
2037 QString(
"duration = %1").arg(duration));
2038 root.setAttribute(
"duration", duration);
2048 LOG(VB_JOBQUEUE, LOG_INFO,
2049 QString(
"frames = %1").arg(frameCount));
2050 duration = (
uint)(frameCount / fps);
2051 LOG(VB_JOBQUEUE, LOG_INFO,
2052 QString(
"duration = %1").arg(duration));
2053 root.setAttribute(
"duration", duration);
2055 else if (inputFC->duration != (
uint) AV_NOPTS_VALUE)
2057 duration = (
uint) (inputFC->duration / AV_TIME_BASE);
2058 root.setAttribute(
"duration", duration);
2059 LOG(VB_JOBQUEUE, LOG_INFO,
2060 QString(
"duration = %1").arg(duration));
2061 frameCount = (int64_t)(duration * fps);
2065 root.setAttribute(
"duration",
"N/A");
2070 root.setAttribute(
"duration",
"N/A");
2071 LOG(VB_JOBQUEUE, LOG_ERR,
2072 QString(
"Unknown lenMethod (%1)")
2078 LOG(VB_JOBQUEUE, LOG_INFO,
2079 QString(
"cutframes = %1").arg(cutFrames));
2080 int cutduration = (int)(cutFrames / fps);
2081 LOG(VB_JOBQUEUE, LOG_INFO,
2082 QString(
"cutduration = %1").arg(cutduration));
2083 root.setAttribute(
"cutduration", duration - cutduration);
2089 case AVMEDIA_TYPE_AUDIO:
2091 QStringList param = QString::fromStdString(buf).split(
',', Qt::SkipEmptyParts);
2092 QString codec = param[0].remove(
"Audio:", Qt::CaseInsensitive).remove(QChar::Null);
2094 QDomElement stream = doc.createElement(
"audio");
2095 stream.setAttribute(
"streamindex", i);
2096 stream.setAttribute(
"ffmpegindex", ffmpegIndex++);
2100 if (codec.trimmed().toLower() ==
"liba52")
2101 stream.setAttribute(
"codec",
"AC3");
2103 stream.setAttribute(
"codec", codec.trimmed());
2105 stream.setAttribute(
"channels", par->ch_layout.nb_channels);
2107 AVDictionaryEntry *metatag =
2108 av_dict_get(st->metadata,
"language",
nullptr, 0);
2110 stream.setAttribute(
"language", metatag->value);
2112 stream.setAttribute(
"language",
"N/A");
2114 stream.setAttribute(
"id", st->id);
2116 stream.setAttribute(
"samplerate", par->sample_rate);
2117 stream.setAttribute(
"bitrate", (qlonglong)par->bit_rate);
2119 if (st->start_time != (
int) AV_NOPTS_VALUE)
2121 int secs = st->start_time / AV_TIME_BASE;
2122 int us = st->start_time % AV_TIME_BASE;
2123 stream.setAttribute(
"start_time", QString(
"%1.%2")
2124 .arg(secs).arg(av_rescale(us, 1000000, AV_TIME_BASE)));
2128 stream.setAttribute(
"start_time", 0);
2131 streams.appendChild(stream);
2136 case AVMEDIA_TYPE_SUBTITLE:
2138 QStringList param = QString::fromStdString(buf).split(
',', Qt::SkipEmptyParts);
2139 QString codec = param[0].remove(
"Subtitle:", Qt::CaseInsensitive).remove(QChar::Null);
2141 QDomElement stream = doc.createElement(
"subtitle");
2142 stream.setAttribute(
"streamindex", i);
2143 stream.setAttribute(
"ffmpegindex", ffmpegIndex++);
2144 stream.setAttribute(
"codec", codec.trimmed());
2146 AVDictionaryEntry *metatag =
2147 av_dict_get(st->metadata,
"language",
nullptr, 0);
2149 stream.setAttribute(
"language", metatag->value);
2151 stream.setAttribute(
"language",
"N/A");
2153 stream.setAttribute(
"id", st->id);
2155 streams.appendChild(stream);
2160 case AVMEDIA_TYPE_DATA:
2162 QDomElement stream = doc.createElement(
"data");
2163 stream.setAttribute(
"streamindex", i);
2164 stream.setAttribute(
"codec", QString::fromStdString(buf).remove(QChar::Null));
2165 streams.appendChild(stream);
2171 LOG(VB_JOBQUEUE, LOG_ERR,
2172 QString(
"Skipping unsupported codec %1 on stream %2")
2173 .arg(inputFC->streams[i]->codecpar->codec_type).arg(i));
2181 if (!f.open(QIODevice::WriteOnly))
2183 LOG(VB_JOBQUEUE, LOG_ERR,
2184 "Failed to open file for writing - " + outFile);
2189 t << doc.toString(4);
2201 if (!f.open(QIODevice::WriteOnly))
2203 LOG(VB_GENERAL, LOG_ERR,
2204 QString(
"MythArchiveHelper: Failed to open file for writing - %1")
2223 if (
filename.startsWith(
"myth://"))
2232 struct statfs statbuf {};
2234 ((!strcmp(statbuf.f_fstypename,
"nfs")) ||
2235 (!strcmp(statbuf.f_fstypename,
"afpfs")) ||
2236 (!strcmp(statbuf.f_fstypename,
"smbfs"))))
2238 #elif defined(__linux__)
2239 struct statfs statbuf {};
2241 ((statbuf.f_type == 0x6969) ||
2242 (statbuf.f_type == 0x517B)))
2266 add(QStringList{
"-t",
"--createthumbnail"},
2267 "createthumbnail",
false,
2268 "Create one or more thumbnails\n"
2269 "Requires: --infile, --thumblist, --outfile\n"
2270 "Optional: --framecount",
"");
2271 add(
"--infile",
"infile",
"",
2273 "Used with: --createthumbnail, --getfileinfo, --isremote, "
2274 "--sup2dast, --importarchive",
"");
2275 add(
"--outfile",
"outfile",
"",
2276 "Output file name\n"
2277 "Used with: --createthumbnail, --getfileinfo, --getdbparameters, "
2279 "When used with --createthumbnail: eg 'thumb%1-%2.jpg'\n"
2280 " %1 will be replaced with the no. of the thumb\n"
2281 " %2 will be replaced with the frame no.",
"");
2282 add(
"--thumblist",
"thumblist",
"",
2283 "Comma-separated list of required thumbs (in seconds)\n"
2284 "Used with: --createthumbnail",
"");
2285 add(
"--framecount",
"framecount", 1,
2286 "Number of frames to grab (default 1)\n"
2287 "Used with: --createthumbnail",
"");
2289 add(QStringList{
"-i",
"--getfileinfo"},
2290 "getfileinfo",
false,
2291 "Write file info about infile to outfile\n"
2292 "Requires: --infile, --outfile, --method",
"");
2293 add(
"--method",
"method", 0,
2294 "Method of file duration calculation\n"
2295 "Used with: --getfileinfo\n"
2296 " 0 = use av_estimate_timings() (quick but not very accurate - "
2298 " 1 = read all frames (most accurate but slow)\n"
2299 " 2 = use position map in DB (quick, only works for MythTV "
2302 add(QStringList{
"-p",
"--getdbparameters"},
2303 "getdbparameters",
false,
2304 "Write the mysql database parameters to outfile\n"
2305 "Requires: --outfile",
"");
2307 add(QStringList{
"-n",
"--nativearchive"},
2308 "nativearchive",
false,
2309 "Archive files to a native archive format\n"
2310 "Requires: --outfile",
"");
2312 add(QStringList{
"-f",
"--importarchive"},
2313 "importarchive",
false,
2314 "Import an archived file\n"
2315 "Requires: --infile, --chanid",
"");
2316 add(
"--chanid",
"chanid", -1,
2317 "Channel ID to use when inserting records in DB\n"
2318 "Used with: --importarchive",
"");
2320 add(QStringList{
"-r",
"--isremote"},
2322 "Check if infile is on a remote filesystem\n"
2323 "Requires: --infile\n"
2324 "Returns: 0 on error or file not found\n"
2325 " - 1 file is on a local filesystem\n"
2326 " - 2 file is on a remote filesystem",
"");
2328 add(QStringList{
"-b",
"--burndvd"},
2330 "Burn a created DVD to a blank disc\n"
2331 "Optional: --mediatype, --erasedvdrw, --nativeformat",
"");
2332 add(
"--mediatype",
"mediatype", 0,
2333 "Type of media to burn\n"
2334 "Used with: --burndvd\n"
2335 " 0 = single layer DVD (default)\n"
2336 " 1 = dual layer DVD\n"
2337 " 2 = rewritable DVD",
"");
2338 add(
"--erasedvdrw",
"erasedvdrw",
false,
2339 "Force an erase of DVD-R/W Media\n"
2340 "Used with: --burndvd (optional)",
"");
2341 add(
"--nativeformat",
"nativeformat",
false,
2342 "Archive is a native archive format\n"
2343 "Used with: --burndvd (optional)",
"");
2345 add(QStringList{
"-s",
"--sup2dast"},
2347 "Convert projectX subtitles to DVD subtitles\n"
2348 "Requires: --infile, --ifofile, --delay",
"");
2349 add(
"--ifofile",
"ifofile",
"",
2350 "Filename of ifo file\n"
2351 "Used with: --sup2dast",
"");
2352 add(
"--delay",
"delay", 0,
2353 "Delay in ms to add to subtitles (default 0)\n"
2354 "Used with: --sup2dast",
"");
2380 QCoreApplication a(argc, argv);
2381 QCoreApplication::setApplicationName(
"mytharchivehelper");
2384 QString mask(
"jobqueue");
2396 LOG(VB_GENERAL, LOG_ERR,
"Failed to init MythContext, exiting.");
2428 if (inFile.isEmpty())
2430 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile in -t/--grabthumbnail "
2435 if (thumbList.isEmpty())
2437 LOG(VB_GENERAL, LOG_ERR,
"Missing --thumblist in -t/--grabthumbnail"
2442 if (outFile.isEmpty())
2444 LOG(VB_GENERAL, LOG_ERR,
"Missing --outfile in -t/--grabthumbnail "
2450 if (bGetDBParameters)
2452 if (outFile.isEmpty())
2454 LOG(VB_GENERAL, LOG_ERR,
"Missing argument to -p/--getdbparameters "
2462 if (inFile.isEmpty())
2464 LOG(VB_GENERAL, LOG_ERR,
2465 "Missing argument to -r/--isremote option");
2472 if (mediaType < 0 || mediaType > 2)
2474 LOG(VB_GENERAL, LOG_ERR, QString(
"Invalid mediatype given: %1")
2482 if (outFile.isEmpty())
2484 LOG(VB_GENERAL, LOG_ERR,
"Missing argument to -n/--nativearchive "
2492 if (inFile.isEmpty())
2494 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile argument to "
2495 "-f/--importarchive option");
2502 if (inFile.isEmpty())
2504 LOG(VB_GENERAL, LOG_ERR,
"Missing --infile in -i/--getfileinfo "
2509 if (outFile.isEmpty())
2511 LOG(VB_GENERAL, LOG_ERR,
"Missing --outfile in -i/--getfileinfo "
2519 if (inFile.isEmpty())
2521 LOG(VB_GENERAL, LOG_ERR,
2522 "Missing --infile in -s/--sup2dast option");
2526 if (ifoFile.isEmpty())
2528 LOG(VB_GENERAL, LOG_ERR,
2529 "Missing --ifofile in -s/--sup2dast option");
2535 res =
grabThumbnail(inFile, thumbList, outFile, frameCount);
2536 else if (bGetDBParameters)
2538 else if (bNativeArchive)
2540 else if (bImportArchive)
2542 else if (bGetFileInfo)
2547 res =
doBurnDVD(mediaType, bEraseDVDRW, bNativeFormat);
2550 QByteArray inFileBA = inFile.toLocal8Bit();
2551 QByteArray ifoFileBA = ifoFile.toLocal8Bit();
2552 res =
sup2dast(inFileBA.constData(), ifoFileBA.constData(), delay);