00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <iostream>
00028 #include <stdint.h>
00029 #include <sys/wait.h>
00030 #include <unistd.h>
00031 #include <cstdlib>
00032
00033 #include <mythconfig.h>
00034 #if CONFIG_DARWIN or defined(__FreeBSD__)
00035 #include <sys/param.h>
00036 #include <sys/mount.h>
00037 #elif __linux__
00038 #include <sys/vfs.h>
00039 #endif
00040
00041 using namespace std;
00042
00043
00044
00045 #include <QApplication>
00046 #include <QFile>
00047 #include <QDir>
00048 #include <QDomElement>
00049 #include <QImage>
00050 #include <QMutex>
00051 #include <QMutexLocker>
00052 #include <QTextStream>
00053
00054
00055 #include <mythcommandlineparser.h>
00056 #include <mythcontext.h>
00057 #include <mythcoreutil.h>
00058 #include <mythversion.h>
00059 #include <exitcodes.h>
00060 #include <mythdb.h>
00061 #include <programinfo.h>
00062 #include <mythdirs.h>
00063 #include <mythconfig.h>
00064 #include <mythsystem.h>
00065 #include <mythmiscutil.h>
00066 #include <mythlogging.h>
00067
00068 extern "C" {
00069 #include <avcodec.h>
00070 #include <avformat.h>
00071 #include <swscale.h>
00072 #include "pxsup2dast.h"
00073 }
00074
00075
00076 #include "../mytharchive/archiveutil.h"
00077
00078 namespace
00079 {
00080 void cleanup()
00081 {
00082 delete gContext;
00083 gContext = NULL;
00084 }
00085
00086 class CleanupGuard
00087 {
00088 public:
00089 typedef void (*CleanupFunc)();
00090
00091 public:
00092 CleanupGuard(CleanupFunc cleanFunction) :
00093 m_cleanFunction(cleanFunction) {}
00094
00095 ~CleanupGuard()
00096 {
00097 m_cleanFunction();
00098 }
00099
00100 private:
00101 CleanupFunc m_cleanFunction;
00102 };
00103 }
00104
00105 class NativeArchive
00106 {
00107 public:
00108 NativeArchive(void);
00109 ~NativeArchive(void);
00110
00111 int doNativeArchive(const QString &jobFile);
00112 int doImportArchive(const QString &xmlFile, int chanID);
00113 bool copyFile(const QString &source, const QString &destination);
00114 int importRecording(const QDomElement &itemNode,
00115 const QString &xmlFile, int chanID);
00116 int importVideo(const QDomElement &itemNode, const QString &xmlFile);
00117 int exportRecording(QDomElement &itemNode, const QString &saveDirectory);
00118 int exportVideo(QDomElement &itemNode, const QString &saveDirectory);
00119 private:
00120 QString findNodeText(const QDomElement &elem, const QString &nodeName);
00121 };
00122
00123 NativeArchive::NativeArchive(void)
00124 {
00125
00126 QString tempDir = getTempDirectory();
00127 QFile file(tempDir + "/logs/mythburn.lck");
00128
00129 if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
00130 LOG(VB_GENERAL, LOG_ERR, "NativeArchive: Failed to create lock file");
00131
00132 QString pid = QString("%1").arg(getpid());
00133 file.write(pid.toAscii());
00134 file.close();
00135 }
00136
00137 NativeArchive::~NativeArchive(void)
00138 {
00139
00140 QString tempDir = getTempDirectory();
00141 if (QFile::exists(tempDir + "/logs/mythburn.lck"))
00142 QFile::remove(tempDir + "/logs/mythburn.lck");
00143 }
00144
00145 bool NativeArchive::copyFile(const QString &source, const QString &destination)
00146 {
00147 QFile srcFile(source), destFile(destination);
00148
00149 LOG(VB_JOBQUEUE, LOG_INFO, QString("copying from %1").arg(source));
00150 LOG(VB_JOBQUEUE, LOG_INFO, QString("to %2").arg(destination));
00151
00152 if (!srcFile.open(QIODevice::ReadOnly))
00153 {
00154 LOG(VB_JOBQUEUE, LOG_ERR, "Unable to open source file");
00155 return false;
00156 }
00157
00158 if (!destFile.open(QIODevice::WriteOnly))
00159 {
00160 LOG(VB_JOBQUEUE, LOG_ERR, "Unable to open destination file");
00161 LOG(VB_JOBQUEUE, LOG_ERR, "Do you have write access to the directory?");
00162 srcFile.close();
00163 return false;
00164 }
00165
00166
00167 int64_t dummy;
00168 int64_t freeSpace = getDiskSpace(destination, dummy, dummy);
00169
00170 int srcLen, destLen, percent = 0, lastPercent = 0;
00171 int64_t wroteSize = 0, totalSize = srcFile.size();
00172 char buffer[1024*1024];
00173
00174 if (freeSpace != -1 && freeSpace < totalSize / 1024)
00175 {
00176 LOG(VB_JOBQUEUE, LOG_ERR,
00177 "Not enough free space available on destination filesystem.");
00178 LOG(VB_JOBQUEUE, LOG_ERR, QString("Available: %1 Needed %2")
00179 .arg(freeSpace).arg(totalSize));
00180 destFile.close();
00181 srcFile.close();
00182 return false;
00183 }
00184
00185 while ((srcLen = srcFile.read(buffer, sizeof(buffer))) > 0)
00186 {
00187 destLen = destFile.write(buffer, srcLen);
00188
00189 if (destLen == -1 || srcLen != destLen)
00190 {
00191 LOG(VB_JOBQUEUE, LOG_ERR,
00192 "While trying to write to destination file.");
00193 srcFile.close();
00194 destFile.close();
00195 return false;
00196 }
00197 wroteSize += destLen;
00198 percent = (int) ((100.0 * wroteSize) / totalSize);
00199 if (percent % 5 == 0 && percent != lastPercent)
00200 {
00201 LOG(VB_JOBQUEUE, LOG_INFO, QString("%1 out of %2 (%3%) completed")
00202 .arg(formatSize(wroteSize/1024))
00203 .arg(formatSize(totalSize/1024)).arg(percent));
00204 lastPercent = percent;
00205 }
00206 }
00207
00208 srcFile.close();
00209 destFile.close();
00210 if (srcFile.size() != destFile.size())
00211 {
00212 LOG(VB_JOBQUEUE, LOG_ERR, "Copy not completed OK - "
00213 "Source and destination file sizes do not match!!");
00214 LOG(VB_JOBQUEUE, LOG_ERR,
00215 QString("Source is %1 bytes, Destination is %2 bytes")
00216 .arg(srcFile.size()).arg(destFile.size()));
00217 return false;
00218 }
00219 else
00220 LOG(VB_JOBQUEUE, LOG_INFO, "Copy completed OK");
00221
00222 return true;
00223 }
00224
00225 static bool createISOImage(QString &sourceDirectory)
00226 {
00227 LOG(VB_JOBQUEUE, LOG_INFO, "Creating ISO image");
00228
00229 QString tempDirectory = getTempDirectory();
00230
00231 tempDirectory += "work/";
00232
00233 QString mkisofs = gCoreContext->GetSetting("MythArchiveMkisofsCmd", "mkisofs");
00234 QString command = mkisofs + " -R -J -V 'MythTV Archive' -o ";
00235 command += tempDirectory + "mythburn.iso " + sourceDirectory;
00236
00237 uint res = myth_system(command);
00238 if (res != GENERIC_EXIT_OK)
00239 {
00240 LOG(VB_JOBQUEUE, LOG_ERR,
00241 QString("Failed while running mkisofs. Result: %1") .arg(res));
00242 return false;
00243 }
00244
00245 LOG(VB_JOBQUEUE, LOG_INFO, "Finished creating ISO image");
00246 return true;
00247 }
00248
00249 static int burnISOImage(int mediaType, bool bEraseDVDRW, bool nativeFormat)
00250 {
00251 QString dvdDrive = gCoreContext->GetSetting("MythArchiveDVDLocation",
00252 "/dev/dvd");
00253 LOG(VB_JOBQUEUE, LOG_INFO, "Burning ISO image to " + dvdDrive);
00254
00255 int driveSpeed = gCoreContext->GetNumSetting("MythArchiveDriveSpeed");
00256 QString tempDirectory = getTempDirectory();
00257
00258 tempDirectory += "work/";
00259
00260 QString command = gCoreContext->GetSetting("MythArchiveGrowisofsCmd",
00261 "growisofs");
00262
00263 if (driveSpeed)
00264 command += " -speed=" + QString::number(driveSpeed);
00265
00266 if (nativeFormat)
00267 {
00268 if (mediaType == AD_DVD_RW && bEraseDVDRW == true)
00269 {
00270 command += " -use-the-force-luke -Z " + dvdDrive;
00271 command += " -V 'MythTV Archive' -R -J " + tempDirectory;
00272 }
00273 else
00274 {
00275 command += " -Z " + dvdDrive;
00276 command += " -V 'MythTV Archive' -R -J " + tempDirectory;
00277 }
00278 }
00279 else
00280 {
00281 if (mediaType == AD_DVD_RW && bEraseDVDRW == true)
00282 {
00283 command += " -dvd-compat -use-the-force-luke -Z " + dvdDrive;
00284 command += " -dvd-video -V 'MythTV DVD' " + tempDirectory + "/dvd";
00285 }
00286 else
00287 {
00288 command += " -dvd-compat -Z " + dvdDrive;
00289 command += " -dvd-video -V 'MythTV DVD' " + tempDirectory + "/dvd";
00290 }
00291 }
00292
00293 uint res = myth_system(command);
00294 if (res != GENERIC_EXIT_OK)
00295 LOG(VB_JOBQUEUE, LOG_ERR,
00296 QString("Failed while running growisofs. Result: %1") .arg(res));
00297 else
00298 LOG(VB_JOBQUEUE, LOG_INFO, "Finished burning ISO image");
00299
00300 return res;
00301 }
00302
00303 static int doBurnDVD(int mediaType, bool bEraseDVDRW, bool nativeFormat)
00304 {
00305 gCoreContext->SaveSetting("MythArchiveLastRunStart",
00306 QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm"));
00307 gCoreContext->SaveSetting("MythArchiveLastRunStatus", "Running");
00308
00309 int res = burnISOImage(mediaType, bEraseDVDRW, nativeFormat);
00310
00311 gCoreContext->SaveSetting("MythArchiveLastRunEnd",
00312 QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm"));
00313 gCoreContext->SaveSetting("MythArchiveLastRunStatus", "Success");
00314 return res;
00315 }
00316
00317 int NativeArchive::doNativeArchive(const QString &jobFile)
00318 {
00319 QString tempDir = getTempDirectory();
00320
00321 QDomDocument doc("archivejob");
00322 QFile file(jobFile);
00323 if (!file.open(QIODevice::ReadOnly))
00324 {
00325 LOG(VB_JOBQUEUE, LOG_ERR, "Could not open job file: " + jobFile);
00326 return 1;
00327 }
00328
00329 if (!doc.setContent(&file))
00330 {
00331 LOG(VB_JOBQUEUE, LOG_ERR, "Could not load job file: " + jobFile);
00332 file.close();
00333 return 1;
00334 }
00335
00336 file.close();
00337
00338
00339 bool bCreateISO = false;
00340 bool bEraseDVDRW = false;
00341 bool bDoBurn = false;
00342 QString saveDirectory;
00343 int mediaType = 0;
00344
00345 QDomNodeList nodeList = doc.elementsByTagName("options");
00346 if (nodeList.count() == 1)
00347 {
00348 QDomNode node = nodeList.item(0);
00349 QDomElement options = node.toElement();
00350 if (!options.isNull())
00351 {
00352 bCreateISO = (options.attribute("createiso", "0") == "1");
00353 bEraseDVDRW = (options.attribute("erasedvdrw", "0") == "1");
00354 bDoBurn = (options.attribute("doburn", "0") == "1");
00355 mediaType = options.attribute("mediatype", "0").toInt();
00356 saveDirectory = options.attribute("savedirectory", "");
00357 if (!saveDirectory.endsWith("/"))
00358 saveDirectory += "/";
00359 }
00360 }
00361 else
00362 {
00363 LOG(VB_JOBQUEUE, LOG_ERR,
00364 QString("Found %1 options nodes - should be 1")
00365 .arg(nodeList.count()));
00366 return 1;
00367 }
00368 LOG(VB_JOBQUEUE, LOG_INFO,
00369 QString("Options - createiso: %1,"
00370 " doburn: %2, mediatype: %3, erasedvdrw: %4")
00371 .arg(bCreateISO).arg(bDoBurn).arg(mediaType).arg(bEraseDVDRW));
00372 LOG(VB_JOBQUEUE, LOG_INFO, QString("savedirectory: %1").arg(saveDirectory));
00373
00374
00375 if (mediaType != AD_FILE)
00376 {
00377 saveDirectory = tempDir;
00378 if (!saveDirectory.endsWith("/"))
00379 saveDirectory += "/";
00380
00381 saveDirectory += "work/";
00382
00383 QDir dir(saveDirectory);
00384 if (dir.exists())
00385 {
00386 if (!MythRemoveDirectory(dir))
00387 LOG(VB_GENERAL, LOG_ERR,
00388 "NativeArchive: Failed to clear work directory");
00389 }
00390 dir.mkpath(saveDirectory);
00391 }
00392
00393 LOG(VB_JOBQUEUE, LOG_INFO,
00394 QString("Saving files to : %1").arg(saveDirectory));
00395
00396
00397 nodeList = doc.elementsByTagName("file");
00398 if (nodeList.count() < 1)
00399 {
00400 LOG(VB_JOBQUEUE, LOG_ERR, "Cannot find any file nodes?");
00401 return 1;
00402 }
00403
00404
00405 QDomNode node;
00406 QDomElement elem;
00407 QString type = "";
00408
00409 for (int x = 0; x < nodeList.count(); x++)
00410 {
00411 node = nodeList.item(x);
00412 elem = node.toElement();
00413 if (!elem.isNull())
00414 {
00415 type = elem.attribute("type");
00416
00417 if (type.toLower() == "recording")
00418 exportRecording(elem, saveDirectory);
00419 else if (type.toLower() == "video")
00420 exportVideo(elem, saveDirectory);
00421 else
00422 {
00423 LOG(VB_JOBQUEUE, LOG_ERR,
00424 QString("Don't know how to archive items of type '%1'")
00425 .arg(type.toLower()));
00426 continue;
00427 }
00428 }
00429 }
00430
00431
00432 if (mediaType != AD_FILE && bDoBurn)
00433 {
00434 if (!burnISOImage(mediaType, bEraseDVDRW, true))
00435 {
00436 LOG(VB_JOBQUEUE, LOG_ERR,
00437 "Native archive job failed to completed");
00438 return 1;
00439 }
00440 }
00441
00442
00443 if (bCreateISO)
00444 {
00445 if (!createISOImage(saveDirectory))
00446 {
00447 LOG(VB_JOBQUEUE, LOG_ERR, "Native archive job failed to completed");
00448 return 1;
00449 }
00450 }
00451
00452 LOG(VB_JOBQUEUE, LOG_INFO, "Native archive job completed OK");
00453
00454 return 0;
00455 }
00456
00457 static QRegExp badChars = QRegExp("(/|\\\\|:|\'|\"|\\?|\\|)");
00458
00459 static QString fixFilename(const QString &filename)
00460 {
00461 QString ret = filename;
00462 ret.replace(badChars, "_");
00463 return ret;
00464 }
00465
00466 int NativeArchive::exportRecording(QDomElement &itemNode,
00467 const QString &saveDirectory)
00468 {
00469 QString chanID, startTime, title = "", filename = "";
00470 bool doDelete = false;
00471 QString dbVersion = gCoreContext->GetSetting("DBSchemaVer", "");
00472
00473 title = fixFilename(itemNode.attribute("title"));
00474 filename = itemNode.attribute("filename");
00475 doDelete = (itemNode.attribute("delete", "0") == "0");
00476 LOG(VB_JOBQUEUE, LOG_INFO, QString("Archiving %1 (%2), do delete: %3")
00477 .arg(title).arg(filename).arg(doDelete));
00478
00479 if (title == "" || filename == "")
00480 {
00481 LOG(VB_JOBQUEUE, LOG_ERR, "Bad title or filename");
00482 return 0;
00483 }
00484
00485 if (!extractDetailsFromFilename(filename, chanID, startTime))
00486 {
00487 LOG(VB_JOBQUEUE, LOG_ERR,
00488 QString("Failed to extract chanID and startTime from '%1'")
00489 .arg(filename));
00490 return 0;
00491 }
00492
00493
00494 QDir dir(saveDirectory + title);
00495 if (!dir.exists())
00496 dir.mkpath(saveDirectory + title);
00497 if (!dir.exists())
00498 LOG(VB_GENERAL, LOG_ERR, "Failed to create savedir: " + ENO);
00499
00500 LOG(VB_JOBQUEUE, LOG_INFO, "Creating xml file for " + title);
00501 QDomDocument doc("MYTHARCHIVEITEM");
00502
00503 QDomElement root = doc.createElement("item");
00504 doc.appendChild(root);
00505 root.setAttribute("type", "recording");
00506 root.setAttribute("databaseversion", dbVersion);
00507
00508 QDomElement recorded = doc.createElement("recorded");
00509 root.appendChild(recorded);
00510
00511
00512 MSqlQuery query(MSqlQuery::InitCon());
00513 query.prepare("SELECT chanid, starttime, endtime, title, subtitle,"
00514 " description, category, hostname, bookmark, editing,"
00515 " cutlist, autoexpire, commflagged, recgroup, recordid,"
00516 " seriesid, programid, lastmodified, filesize, stars,"
00517 " previouslyshown, originalairdate, preserve, findid,"
00518 " deletepending, transcoder, timestretch, recpriority,"
00519 " basename, progstart, progend, playgroup, profile,"
00520 " duplicate, transcoded FROM recorded "
00521 "WHERE chanid = :CHANID and starttime = :STARTTIME;");
00522 query.bindValue(":CHANID", chanID);
00523 query.bindValue(":STARTTIME", startTime);
00524
00525 if (query.exec() && query.next())
00526 {
00527 QDomElement elem;
00528 QDomText text;
00529
00530 elem = doc.createElement("chanid");
00531 text = doc.createTextNode(query.value(0).toString());
00532 elem.appendChild(text);
00533 recorded.appendChild(elem);
00534
00535 elem = doc.createElement("starttime");
00536 text = doc.createTextNode(query.value(1).toString());
00537 elem.appendChild(text);
00538 recorded.appendChild(elem);
00539
00540 elem = doc.createElement("endtime");
00541 text = doc.createTextNode(query.value(2).toString());
00542 elem.appendChild(text);
00543 recorded.appendChild(elem);
00544
00545 elem = doc.createElement("title");
00546 text = doc.createTextNode(query.value(3).toString());
00547 elem.appendChild(text);
00548 recorded.appendChild(elem);
00549
00550 elem = doc.createElement("subtitle");
00551 text = doc.createTextNode(query.value(4).toString());
00552 elem.appendChild(text);
00553 recorded.appendChild(elem);
00554
00555 elem = doc.createElement("description");
00556 text = doc.createTextNode(query.value(5).toString());
00557 elem.appendChild(text);
00558 recorded.appendChild(elem);
00559
00560 elem = doc.createElement("category");
00561 text = doc.createTextNode(query.value(6).toString());
00562 elem.appendChild(text);
00563 recorded.appendChild(elem);
00564
00565 elem = doc.createElement("hostname");
00566 text = doc.createTextNode(query.value(7).toString());
00567 elem.appendChild(text);
00568 recorded.appendChild(elem);
00569
00570 elem = doc.createElement("bookmark");
00571 text = doc.createTextNode(query.value(8).toString());
00572 elem.appendChild(text);
00573 recorded.appendChild(elem);
00574
00575 elem = doc.createElement("editing");
00576 text = doc.createTextNode(query.value(9).toString());
00577 elem.appendChild(text);
00578 recorded.appendChild(elem);
00579
00580 elem = doc.createElement("cutlist");
00581 text = doc.createTextNode(query.value(10).toString());
00582 elem.appendChild(text);
00583 recorded.appendChild(elem);
00584
00585 elem = doc.createElement("autoexpire");
00586 text = doc.createTextNode(query.value(11).toString());
00587 elem.appendChild(text);
00588 recorded.appendChild(elem);
00589
00590 elem = doc.createElement("commflagged");
00591 text = doc.createTextNode(query.value(12).toString());
00592 elem.appendChild(text);
00593 recorded.appendChild(elem);
00594
00595 elem = doc.createElement("recgroup");
00596 text = doc.createTextNode(query.value(13).toString());
00597 elem.appendChild(text);
00598 recorded.appendChild(elem);
00599
00600 elem = doc.createElement("recordid");
00601 text = doc.createTextNode(query.value(14).toString());
00602 elem.appendChild(text);
00603 recorded.appendChild(elem);
00604
00605 elem = doc.createElement("seriesid");
00606 text = doc.createTextNode(query.value(15).toString());
00607 elem.appendChild(text);
00608 recorded.appendChild(elem);
00609
00610 elem = doc.createElement("programid");
00611 text = doc.createTextNode(query.value(16).toString());
00612 elem.appendChild(text);
00613 recorded.appendChild(elem);
00614
00615 elem = doc.createElement("lastmodified");
00616 text = doc.createTextNode(query.value(17).toString());
00617 elem.appendChild(text);
00618 recorded.appendChild(elem);
00619
00620 elem = doc.createElement("filesize");
00621 text = doc.createTextNode(query.value(18).toString());
00622 elem.appendChild(text);
00623 recorded.appendChild(elem);
00624
00625 elem = doc.createElement("stars");
00626 text = doc.createTextNode(query.value(19).toString());
00627 elem.appendChild(text);
00628 recorded.appendChild(elem);
00629
00630 elem = doc.createElement("previouslyshown");
00631 text = doc.createTextNode(query.value(20).toString());
00632 elem.appendChild(text);
00633 recorded.appendChild(elem);
00634
00635 elem = doc.createElement("originalairdate");
00636 text = doc.createTextNode(query.value(21).toString());
00637 elem.appendChild(text);
00638 recorded.appendChild(elem);
00639
00640 elem = doc.createElement("preserve");
00641 text = doc.createTextNode(query.value(22).toString());
00642 elem.appendChild(text);
00643 recorded.appendChild(elem);
00644
00645 elem = doc.createElement("findid");
00646 text = doc.createTextNode(query.value(23).toString());
00647 elem.appendChild(text);
00648 recorded.appendChild(elem);
00649
00650 elem = doc.createElement("deletepending");
00651 text = doc.createTextNode(query.value(24).toString());
00652 elem.appendChild(text);
00653 recorded.appendChild(elem);
00654
00655 elem = doc.createElement("transcoder");
00656 text = doc.createTextNode(query.value(25).toString());
00657 elem.appendChild(text);
00658 recorded.appendChild(elem);
00659
00660 elem = doc.createElement("timestretch");
00661 text = doc.createTextNode(query.value(26).toString());
00662 elem.appendChild(text);
00663 recorded.appendChild(elem);
00664
00665 elem = doc.createElement("recpriority");
00666 text = doc.createTextNode(query.value(27).toString());
00667 elem.appendChild(text);
00668 recorded.appendChild(elem);
00669
00670 elem = doc.createElement("basename");
00671 text = doc.createTextNode(query.value(28).toString());
00672 elem.appendChild(text);
00673 recorded.appendChild(elem);
00674
00675 elem = doc.createElement("progstart");
00676 text = doc.createTextNode(query.value(29).toString());
00677 elem.appendChild(text);
00678 recorded.appendChild(elem);
00679
00680 elem = doc.createElement("progend");
00681 text = doc.createTextNode(query.value(30).toString());
00682 elem.appendChild(text);
00683 recorded.appendChild(elem);
00684
00685 elem = doc.createElement("playgroup");
00686 text = doc.createTextNode(query.value(31).toString());
00687 elem.appendChild(text);
00688 recorded.appendChild(elem);
00689
00690 elem = doc.createElement("profile");
00691 text = doc.createTextNode(query.value(32).toString());
00692 elem.appendChild(text);
00693 recorded.appendChild(elem);
00694
00695 elem = doc.createElement("duplicate");
00696 text = doc.createTextNode(query.value(33).toString());
00697 elem.appendChild(text);
00698 recorded.appendChild(elem);
00699
00700 elem = doc.createElement("transcoded");
00701 text = doc.createTextNode(query.value(34).toString());
00702 elem.appendChild(text);
00703 recorded.appendChild(elem);
00704 LOG(VB_JOBQUEUE, LOG_INFO, "Created recorded element for " + title);
00705 }
00706
00707
00708 query.prepare("SELECT chanid, channum, callsign, name "
00709 "FROM channel WHERE chanid = :CHANID;");
00710 query.bindValue(":CHANID", chanID);
00711
00712 if (query.exec() && query.next())
00713 {
00714 QDomElement channel = doc.createElement("channel");
00715 channel.setAttribute("chanid", query.value(0).toString());
00716 channel.setAttribute("channum", query.value(1).toString());
00717 channel.setAttribute("callsign", query.value(2).toString());
00718 channel.setAttribute("name", query.value(3).toString());
00719 root.appendChild(channel);
00720 LOG(VB_JOBQUEUE, LOG_INFO, "Created channel element for " + title);
00721 }
00722 else
00723 {
00724
00725 LOG(VB_JOBQUEUE, LOG_ERR,
00726 "Cannot find channel details for chanid " + chanID);
00727 QDomElement channel = doc.createElement("channel");
00728 channel.setAttribute("chanid", chanID);
00729 channel.setAttribute("channum", "unknown");
00730 channel.setAttribute("callsign", "unknown");
00731 channel.setAttribute("name", "unknown");
00732 root.appendChild(channel);
00733 LOG(VB_JOBQUEUE, LOG_INFO,
00734 "Created a default channel element for " + title);
00735 }
00736
00737
00738 query.prepare("SELECT credits.person, role, people.name "
00739 "FROM recordedcredits AS credits "
00740 "LEFT JOIN people ON credits.person = people.person "
00741 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
00742 query.bindValue(":CHANID", chanID);
00743 query.bindValue(":STARTTIME", startTime);
00744
00745 if (query.exec() && query.size())
00746 {
00747 QDomElement credits = doc.createElement("credits");
00748 while (query.next())
00749 {
00750 QDomElement credit = doc.createElement("credit");
00751 credit.setAttribute("personid", query.value(0).toString());
00752 credit.setAttribute("name", query.value(2).toString());
00753 credit.setAttribute("role", query.value(1).toString());
00754 credits.appendChild(credit);
00755 }
00756 root.appendChild(credits);
00757 LOG(VB_JOBQUEUE, LOG_INFO, "Created credits element for " + title);
00758 }
00759
00760
00761 query.prepare("SELECT system, rating FROM recordedrating "
00762 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
00763 query.bindValue(":CHANID", chanID);
00764 query.bindValue(":STARTTIME", startTime);
00765
00766 if (query.exec() && query.next())
00767 {
00768 QDomElement rating = doc.createElement("rating");
00769 rating.setAttribute("system", query.value(0).toString());
00770 rating.setAttribute("rating", query.value(1).toString());
00771 root.appendChild(rating);
00772 LOG(VB_JOBQUEUE, LOG_INFO, "Created rating element for " + title);
00773 }
00774
00775
00776 QDomElement recordedmarkup = doc.createElement("recordedmarkup");
00777 query.prepare("SELECT chanid, starttime, mark, offset, type "
00778 "FROM recordedmarkup "
00779 "WHERE chanid = :CHANID and starttime = :STARTTIME;");
00780 query.bindValue(":CHANID", chanID);
00781 query.bindValue(":STARTTIME", startTime);
00782 if (query.exec() && query.size())
00783 {
00784 while (query.next())
00785 {
00786 QDomElement mark = doc.createElement("mark");
00787 mark.setAttribute("mark", query.value(2).toString());
00788 mark.setAttribute("offset", query.value(3).toString());
00789 mark.setAttribute("type", query.value(4).toString());
00790 recordedmarkup.appendChild(mark);
00791 }
00792 root.appendChild(recordedmarkup);
00793 LOG(VB_JOBQUEUE, LOG_INFO,
00794 "Created recordedmarkup element for " + title);
00795 }
00796
00797
00798 QDomElement recordedseek = doc.createElement("recordedseek");
00799 query.prepare("SELECT chanid, starttime, mark, offset, type "
00800 "FROM recordedseek "
00801 "WHERE chanid = :CHANID and starttime = :STARTTIME;");
00802 query.bindValue(":CHANID", chanID);
00803 query.bindValue(":STARTTIME", startTime);
00804 if (query.exec() && query.size())
00805 {
00806 while (query.next())
00807 {
00808 QDomElement mark = doc.createElement("mark");
00809 mark.setAttribute("mark", query.value(2).toString());
00810 mark.setAttribute("offset", query.value(3).toString());
00811 mark.setAttribute("type", query.value(4).toString());
00812 recordedseek.appendChild(mark);
00813 }
00814 root.appendChild(recordedseek);
00815 LOG(VB_JOBQUEUE, LOG_INFO, "Created recordedseek element for " + title);
00816 }
00817
00818
00819 QString baseName = getBaseName(filename);
00820 QString xmlFile = saveDirectory + title + "/" + baseName + ".xml";
00821 QFile f(xmlFile);
00822 if (!f.open(QIODevice::WriteOnly))
00823 {
00824 LOG(VB_JOBQUEUE, LOG_ERR,
00825 "MythNativeWizard: Failed to open file for writing - " + xmlFile);
00826 return 0;
00827 }
00828
00829 QTextStream t(&f);
00830 t << doc.toString(4);
00831 f.close();
00832
00833
00834 LOG(VB_JOBQUEUE, LOG_INFO, "Copying video file");
00835 bool res = copyFile(filename, saveDirectory + title + "/" + baseName);
00836 if (!res)
00837 return 0;
00838
00839
00840 if (QFile::exists(filename + ".png"))
00841 {
00842 LOG(VB_JOBQUEUE, LOG_INFO, "Copying preview image");
00843 res = copyFile(filename + ".png", saveDirectory
00844 + title + "/" + baseName + ".png");
00845 if (!res)
00846 return 0;
00847 }
00848
00849 LOG(VB_JOBQUEUE, LOG_INFO, "Item Archived OK");
00850
00851 return 1;
00852 }
00853
00854 int NativeArchive::exportVideo(QDomElement &itemNode,
00855 const QString &saveDirectory)
00856 {
00857 QString title = "", filename = "";
00858 bool doDelete = false;
00859 QString dbVersion = gCoreContext->GetSetting("DBSchemaVer", "");
00860 int intID = 0, categoryID = 0;
00861 QString coverFile = "";
00862
00863 title = fixFilename(itemNode.attribute("title"));
00864 filename = itemNode.attribute("filename");
00865 doDelete = (itemNode.attribute("delete", "0") == "0");
00866 LOG(VB_JOBQUEUE, LOG_INFO, QString("Archiving %1 (%2), do delete: %3")
00867 .arg(title).arg(filename).arg(doDelete));
00868
00869 if (title == "" || filename == "")
00870 {
00871 LOG(VB_JOBQUEUE, LOG_ERR, "Bad title or filename");
00872 return 0;
00873 }
00874
00875
00876 QDir dir(saveDirectory + title);
00877 if (!dir.exists())
00878 dir.mkdir(saveDirectory + title);
00879
00880 LOG(VB_JOBQUEUE, LOG_INFO, "Creating xml file for " + title);
00881 QDomDocument doc("MYTHARCHIVEITEM");
00882
00883 QDomElement root = doc.createElement("item");
00884 doc.appendChild(root);
00885 root.setAttribute("type", "video");
00886 root.setAttribute("databaseversion", dbVersion);
00887
00888 QDomElement video = doc.createElement("videometadata");
00889 root.appendChild(video);
00890
00891
00892 MSqlQuery query(MSqlQuery::InitCon());
00893 query.prepare("SELECT intid, title, director, plot, rating, inetref, "
00894 "year, userrating, length, showlevel, filename, coverfile, "
00895 "childid, browse, playcommand, category "
00896 "FROM videometadata WHERE filename = :FILENAME;");
00897 query.bindValue(":FILENAME", filename);
00898
00899 if (query.exec() && query.next())
00900 {
00901 QDomElement elem;
00902 QDomText text;
00903
00904 elem = doc.createElement("intid");
00905 text = doc.createTextNode(query.value(0).toString());
00906 intID = query.value(0).toInt();
00907 elem.appendChild(text);
00908 video.appendChild(elem);
00909
00910 elem = doc.createElement("title");
00911 text = doc.createTextNode(query.value(1).toString());
00912 elem.appendChild(text);
00913 video.appendChild(elem);
00914
00915 elem = doc.createElement("director");
00916 text = doc.createTextNode(query.value(2).toString());
00917 elem.appendChild(text);
00918 video.appendChild(elem);
00919
00920 elem = doc.createElement("plot");
00921 text = doc.createTextNode(query.value(3).toString());
00922 elem.appendChild(text);
00923 video.appendChild(elem);
00924
00925 elem = doc.createElement("rating");
00926 text = doc.createTextNode(query.value(4).toString());
00927 elem.appendChild(text);
00928 video.appendChild(elem);
00929
00930 elem = doc.createElement("inetref");
00931 text = doc.createTextNode(query.value(5).toString());
00932 elem.appendChild(text);
00933 video.appendChild(elem);
00934
00935 elem = doc.createElement("year");
00936 text = doc.createTextNode(query.value(6).toString());
00937 elem.appendChild(text);
00938 video.appendChild(elem);
00939
00940 elem = doc.createElement("userrating");
00941 text = doc.createTextNode(query.value(7).toString());
00942 elem.appendChild(text);
00943 video.appendChild(elem);
00944
00945 elem = doc.createElement("length");
00946 text = doc.createTextNode(query.value(8).toString());
00947 elem.appendChild(text);
00948 video.appendChild(elem);
00949
00950 elem = doc.createElement("showlevel");
00951 text = doc.createTextNode(query.value(9).toString());
00952 elem.appendChild(text);
00953 video.appendChild(elem);
00954
00955
00956 QString fname = query.value(10).toString();
00957 if (fname.startsWith(gCoreContext->GetSetting("VideoStartupDir")))
00958 fname = fname.remove(gCoreContext->GetSetting("VideoStartupDir"));
00959
00960 elem = doc.createElement("filename");
00961 text = doc.createTextNode(fname);
00962 elem.appendChild(text);
00963 video.appendChild(elem);
00964
00965 elem = doc.createElement("coverfile");
00966 text = doc.createTextNode(query.value(11).toString());
00967 coverFile = query.value(11).toString();
00968 elem.appendChild(text);
00969 video.appendChild(elem);
00970
00971 elem = doc.createElement("childid");
00972 text = doc.createTextNode(query.value(12).toString());
00973 elem.appendChild(text);
00974 video.appendChild(elem);
00975
00976 elem = doc.createElement("browse");
00977 text = doc.createTextNode(query.value(13).toString());
00978 elem.appendChild(text);
00979 video.appendChild(elem);
00980
00981 elem = doc.createElement("playcommand");
00982 text = doc.createTextNode(query.value(14).toString());
00983 elem.appendChild(text);
00984 video.appendChild(elem);
00985
00986 elem = doc.createElement("categoryid");
00987 text = doc.createTextNode(query.value(15).toString());
00988 categoryID = query.value(15).toInt();
00989 elem.appendChild(text);
00990 video.appendChild(elem);
00991
00992 LOG(VB_JOBQUEUE, LOG_INFO,
00993 "Created videometadata element for " + title);
00994 }
00995
00996
00997 query.prepare("SELECT intid, category "
00998 "FROM videocategory WHERE intid = :INTID;");
00999 query.bindValue(":INTID", categoryID);
01000
01001 if (query.exec() && query.next())
01002 {
01003 QDomElement category = doc.createElement("category");
01004 category.setAttribute("intid", query.value(0).toString());
01005 category.setAttribute("category", query.value(1).toString());
01006 root.appendChild(category);
01007 LOG(VB_JOBQUEUE, LOG_INFO,
01008 "Created videocategory element for " + title);
01009 }
01010
01011
01012 QDomElement countries = doc.createElement("countries");
01013 root.appendChild(countries);
01014
01015 query.prepare("SELECT intid, country "
01016 "FROM videometadatacountry INNER JOIN videocountry "
01017 "ON videometadatacountry.idcountry = videocountry.intid "
01018 "WHERE idvideo = :INTID;");
01019 query.bindValue(":INTID", intID);
01020
01021 if (!query.exec())
01022 MythDB::DBError("select countries", query);
01023
01024 if (query.isActive() && query.size())
01025 {
01026 while (query.next())
01027 {
01028 QDomElement country = doc.createElement("country");
01029 country.setAttribute("intid", query.value(0).toString());
01030 country.setAttribute("country", query.value(1).toString());
01031 countries.appendChild(country);
01032 }
01033 LOG(VB_JOBQUEUE, LOG_INFO, "Created videocountry element for " + title);
01034 }
01035
01036
01037 QDomElement genres = doc.createElement("genres");
01038 root.appendChild(genres);
01039
01040 query.prepare("SELECT intid, genre "
01041 "FROM videometadatagenre INNER JOIN videogenre "
01042 "ON videometadatagenre.idgenre = videogenre.intid "
01043 "WHERE idvideo = :INTID;");
01044 query.bindValue(":INTID", intID);
01045
01046 if (!query.exec())
01047 MythDB::DBError("select genres", query);
01048
01049 if (query.isActive() && query.size())
01050 {
01051 while (query.next())
01052 {
01053 QDomElement genre = doc.createElement("genre");
01054 genre.setAttribute("intid", query.value(0).toString());
01055 genre.setAttribute("genre", query.value(1).toString());
01056 genres.appendChild(genre);
01057 }
01058 LOG(VB_JOBQUEUE, LOG_INFO, "Created videogenre element for " + title);
01059 }
01060
01061
01062 QFileInfo fileInfo(filename);
01063 QString xmlFile = saveDirectory + title + "/"
01064 + fileInfo.fileName() + ".xml";
01065 QFile f(xmlFile);
01066 if (!f.open(QIODevice::WriteOnly))
01067 {
01068 LOG(VB_JOBQUEUE, LOG_INFO,
01069 "MythNativeWizard: Failed to open file for writing - " + xmlFile);
01070 return 0;
01071 }
01072
01073 QTextStream t(&f);
01074 t << doc.toString(4);
01075 f.close();
01076
01077
01078 LOG(VB_JOBQUEUE, LOG_INFO, "Copying video file");
01079 bool res = copyFile(filename, saveDirectory + title
01080 + "/" + fileInfo.fileName());
01081 if (!res)
01082 {
01083 return 0;
01084 }
01085
01086
01087 fileInfo.setFile(coverFile);
01088 if (fileInfo.exists())
01089 {
01090 LOG(VB_JOBQUEUE, LOG_INFO, "Copying cover file");
01091 bool res = copyFile(coverFile, saveDirectory + title
01092 + "/" + fileInfo.fileName());
01093 if (!res)
01094 {
01095 return 0;
01096 }
01097 }
01098
01099 LOG(VB_JOBQUEUE, LOG_INFO, "Item Archived OK");
01100
01101 return 1;
01102 }
01103
01104 int NativeArchive::doImportArchive(const QString &xmlFile, int chanID)
01105 {
01106
01107 QDomDocument doc("mydocument");
01108 QFile file(xmlFile);
01109 if (!file.open(QIODevice::ReadOnly))
01110 {
01111 LOG(VB_JOBQUEUE, LOG_ERR,
01112 "Failed to open file for reading - " + xmlFile);
01113 return 1;
01114 }
01115
01116 if (!doc.setContent(&file))
01117 {
01118 file.close();
01119 LOG(VB_JOBQUEUE, LOG_ERR,
01120 "Failed to read from xml file - " + xmlFile);
01121 return 1;
01122 }
01123 file.close();
01124
01125 QString docType = doc.doctype().name();
01126 QString type, dbVersion;
01127 QDomNodeList itemNodeList;
01128 QDomNode node;
01129 QDomElement itemNode;
01130
01131 if (docType == "MYTHARCHIVEITEM")
01132 {
01133 itemNodeList = doc.elementsByTagName("item");
01134
01135 if (itemNodeList.count() < 1)
01136 {
01137 LOG(VB_JOBQUEUE, LOG_ERR,
01138 "Couldn't find an 'item' element in XML file");
01139 return 1;
01140 }
01141
01142 node = itemNodeList.item(0);
01143 itemNode = node.toElement();
01144 type = itemNode.attribute("type");
01145 dbVersion = itemNode.attribute("databaseversion");
01146
01147 LOG(VB_JOBQUEUE, LOG_INFO,
01148 QString("Archive DB version: %1, Local DB version: %2")
01149 .arg(dbVersion).arg(gCoreContext->GetSetting("DBSchemaVer")));
01150 }
01151 else
01152 {
01153 LOG(VB_JOBQUEUE, LOG_ERR, "Not a native archive xml file - " + xmlFile);
01154 return 1;
01155 }
01156
01157 if (type == "recording")
01158 {
01159 return importRecording(itemNode, xmlFile, chanID);
01160 }
01161 else if (type == "video")
01162 {
01163 return importVideo(itemNode, xmlFile);
01164 }
01165
01166 return 1;
01167 }
01168
01169 int NativeArchive::importRecording(const QDomElement &itemNode,
01170 const QString &xmlFile, int chanID)
01171 {
01172 LOG(VB_JOBQUEUE, LOG_INFO,
01173 QString("Import recording using chanID: %1").arg(chanID));
01174 LOG(VB_JOBQUEUE, LOG_INFO,
01175 QString("Archived recording xml file: %1").arg(xmlFile));
01176
01177 QString videoFile = xmlFile.left(xmlFile.length() - 4);
01178 QString basename = videoFile;
01179 int pos = videoFile.lastIndexOf('/');
01180 if (pos > 0)
01181 basename = videoFile.mid(pos + 1);
01182
01183 QDomNodeList nodeList = itemNode.elementsByTagName("recorded");
01184 if (nodeList.count() < 1)
01185 {
01186 LOG(VB_JOBQUEUE, LOG_ERR,
01187 "Couldn't find a 'recorded' element in XML file");
01188 return 1;
01189 }
01190
01191 QDomNode n = nodeList.item(0);
01192 QDomElement recordedNode = n.toElement();
01193 QString startTime = findNodeText(recordedNode, "starttime");
01194
01195
01196 MSqlQuery query(MSqlQuery::InitCon());
01197 query.prepare("SELECT * FROM recorded "
01198 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
01199 query.bindValue(":CHANID", chanID);
01200 query.bindValue(":STARTTIME", startTime);
01201 if (query.exec())
01202 {
01203 if (query.isActive() && query.size())
01204 {
01205 LOG(VB_JOBQUEUE, LOG_ERR,
01206 "This recording appears to already exist!!");
01207 return 1;
01208 }
01209 }
01210
01211
01212 QString storageDir = "";
01213 query.prepare("SELECT dirname FROM storagegroup "
01214 "WHERE groupname = :GROUPNAME AND hostname = :HOSTNAME;");
01215 query.bindValue(":GROUPNAME", "Default");
01216 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
01217 if (query.exec())
01218 {
01219 query.first();
01220 storageDir = query.value(0).toString();
01221 }
01222 else
01223 {
01224 LOG(VB_JOBQUEUE, LOG_ERR,
01225 "Failed to get 'Default' storage directory for this host");
01226 return 1;
01227 }
01228
01229
01230 LOG(VB_JOBQUEUE, LOG_INFO, "Copying video file.");
01231 if (!copyFile(videoFile, storageDir + "/" + basename))
01232 return 1;
01233
01234
01235 if (QFile::exists(videoFile + ".png"))
01236 {
01237 LOG(VB_JOBQUEUE, LOG_INFO, "Copying preview image file.");
01238 if (!copyFile(videoFile + ".png", storageDir + "/" + basename + ".png"))
01239 return 1;
01240 }
01241
01242
01243 query.prepare("INSERT INTO recorded (chanid,starttime,endtime,"
01244 "title,subtitle,description,category,hostname,bookmark,"
01245 "editing,cutlist,autoexpire, commflagged,recgroup,"
01246 "recordid, seriesid,programid,lastmodified,filesize,stars,"
01247 "previouslyshown,originalairdate,preserve,findid,deletepending,"
01248 "transcoder,timestretch,recpriority,basename,progstart,progend,"
01249 "playgroup,profile,duplicate,transcoded) "
01250 "VALUES(:CHANID,:STARTTIME,:ENDTIME,:TITLE,"
01251 ":SUBTITLE,:DESCRIPTION,:CATEGORY,:HOSTNAME,"
01252 ":BOOKMARK,:EDITING,:CUTLIST,:AUTOEXPIRE,"
01253 ":COMMFLAGGED,:RECGROUP,:RECORDID,:SERIESID,"
01254 ":PROGRAMID,:LASTMODIFIED,:FILESIZE,:STARS,"
01255 ":PREVIOUSLYSHOWN,:ORIGINALAIRDATE,:PRESERVE,:FINDID,"
01256 ":DELETEPENDING,:TRANSCODER,:TIMESTRETCH,:RECPRIORITY,"
01257 ":BASENAME,:PROGSTART,:PROGEND,:PLAYGROUP,:PROFILE,:DUPLICATE,:TRANSCODED);");
01258 query.bindValue(":CHANID", chanID);
01259 query.bindValue(":STARTTIME", startTime);
01260 query.bindValue(":ENDTIME", findNodeText(recordedNode, "endtime"));
01261 query.bindValue(":TITLE", findNodeText(recordedNode, "title"));
01262 query.bindValue(":SUBTITLE", findNodeText(recordedNode, "subtitle"));
01263 query.bindValue(":DESCRIPTION", findNodeText(recordedNode, "description"));
01264 query.bindValue(":CATEGORY", findNodeText(recordedNode, "category"));
01265 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
01266 query.bindValue(":BOOKMARK", findNodeText(recordedNode, "bookmark"));
01267 query.bindValue(":EDITING", findNodeText(recordedNode, "editing"));
01268 query.bindValue(":CUTLIST", findNodeText(recordedNode, "cutlist"));
01269 query.bindValue(":AUTOEXPIRE", findNodeText(recordedNode, "autoexpire"));
01270 query.bindValue(":COMMFLAGGED", findNodeText(recordedNode, "commflagged"));
01271 query.bindValue(":RECGROUP", findNodeText(recordedNode, "recgroup"));
01272 query.bindValue(":RECORDID", findNodeText(recordedNode, "recordid"));
01273 query.bindValue(":SERIESID", findNodeText(recordedNode, "seriesid"));
01274 query.bindValue(":PROGRAMID", findNodeText(recordedNode, "programid"));
01275 query.bindValue(":LASTMODIFIED", findNodeText(recordedNode, "lastmodified"));
01276 query.bindValue(":FILESIZE", findNodeText(recordedNode, "filesize"));
01277 query.bindValue(":STARS", findNodeText(recordedNode, "stars"));
01278 query.bindValue(":PREVIOUSLYSHOWN", findNodeText(recordedNode, "previouslyshown"));
01279 query.bindValue(":ORIGINALAIRDATE", findNodeText(recordedNode, "originalairdate"));
01280 query.bindValue(":PRESERVE", findNodeText(recordedNode, "preserve"));
01281 query.bindValue(":FINDID", findNodeText(recordedNode, "findid"));
01282 query.bindValue(":DELETEPENDING", findNodeText(recordedNode, "deletepending"));
01283 query.bindValue(":TRANSCODER", findNodeText(recordedNode, "transcoder"));
01284 query.bindValue(":TIMESTRETCH", findNodeText(recordedNode, "timestretch"));
01285 query.bindValue(":RECPRIORITY", findNodeText(recordedNode, "recpriority"));
01286 query.bindValue(":BASENAME", findNodeText(recordedNode, "basename"));
01287 query.bindValue(":PROGSTART", findNodeText(recordedNode, "progstart"));
01288 query.bindValue(":PROGEND", findNodeText(recordedNode, "progend"));
01289 query.bindValue(":PLAYGROUP", findNodeText(recordedNode, "playgroup"));
01290 query.bindValue(":PROFILE", findNodeText(recordedNode, "profile"));
01291 query.bindValue(":DUPLICATE", findNodeText(recordedNode, "duplicate"));
01292 query.bindValue(":TRANSCODED", findNodeText(recordedNode, "transcoded"));
01293
01294 if (query.exec())
01295 LOG(VB_JOBQUEUE, LOG_INFO, "Inserted recorded details into database");
01296 else
01297 MythDB::DBError("recorded insert", query);
01298
01299
01300 nodeList = itemNode.elementsByTagName("recordedmarkup");
01301 if (nodeList.count() < 1)
01302 {
01303 LOG(VB_JOBQUEUE, LOG_WARNING,
01304 "Couldn't find a 'recordedmarkup' element in XML file");
01305 }
01306 else
01307 {
01308 n = nodeList.item(0);
01309 QDomElement markupNode = n.toElement();
01310
01311 nodeList = markupNode.elementsByTagName("mark");
01312 if (nodeList.count() < 1)
01313 {
01314 LOG(VB_JOBQUEUE, LOG_WARNING,
01315 "Couldn't find any 'mark' elements in XML file");
01316 }
01317 else
01318 {
01319 for (int x = 0; x < nodeList.count(); x++)
01320 {
01321 n = nodeList.item(x);
01322 QDomElement e = n.toElement();
01323 query.prepare("INSERT INTO recordedmarkup (chanid, starttime, "
01324 "mark, offset, type)"
01325 "VALUES(:CHANID,:STARTTIME,:MARK,:OFFSET,:TYPE);");
01326 query.bindValue(":CHANID", chanID);
01327 query.bindValue(":STARTTIME", startTime);
01328 query.bindValue(":MARK", e.attribute("mark"));
01329 query.bindValue(":OFFSET", e.attribute("offset"));
01330 query.bindValue(":TYPE", e.attribute("type"));
01331
01332 if (!query.exec())
01333 {
01334 MythDB::DBError("recordedmark insert", query);
01335 return 1;
01336 }
01337 }
01338
01339 LOG(VB_JOBQUEUE, LOG_INFO,
01340 "Inserted recordedmarkup details into database");
01341 }
01342 }
01343
01344
01345 nodeList = itemNode.elementsByTagName("recordedseek");
01346 if (nodeList.count() < 1)
01347 {
01348 LOG(VB_JOBQUEUE, LOG_WARNING,
01349 "Couldn't find a 'recordedseek' element in XML file");
01350 }
01351 else
01352 {
01353 n = nodeList.item(0);
01354 QDomElement markupNode = n.toElement();
01355
01356 nodeList = markupNode.elementsByTagName("mark");
01357 if (nodeList.count() < 1)
01358 {
01359 LOG(VB_JOBQUEUE, LOG_WARNING,
01360 "Couldn't find any 'mark' elements in XML file");
01361 }
01362 else
01363 {
01364 for (int x = 0; x < nodeList.count(); x++)
01365 {
01366 n = nodeList.item(x);
01367 QDomElement e = n.toElement();
01368 query.prepare("INSERT INTO recordedseek (chanid, starttime, "
01369 "mark, offset, type)"
01370 "VALUES(:CHANID,:STARTTIME,:MARK,:OFFSET,:TYPE);");
01371 query.bindValue(":CHANID", chanID);
01372 query.bindValue(":STARTTIME", startTime);
01373 query.bindValue(":MARK", e.attribute("mark"));
01374 query.bindValue(":OFFSET", e.attribute("offset"));
01375 query.bindValue(":TYPE", e.attribute("type"));
01376
01377 if (!query.exec())
01378 {
01379 MythDB::DBError("recordedseek insert", query);
01380 return 1;
01381 }
01382 }
01383
01384 LOG(VB_JOBQUEUE, LOG_INFO,
01385 "Inserted recordedseek details into database");
01386 }
01387 }
01388
01389
01390
01391
01392
01393 LOG(VB_JOBQUEUE, LOG_INFO, "Import completed OK");
01394
01395 return 0;
01396 }
01397
01398 int NativeArchive::importVideo(const QDomElement &itemNode, const QString &xmlFile)
01399 {
01400 LOG(VB_JOBQUEUE, LOG_INFO, "Importing video");
01401 LOG(VB_JOBQUEUE, LOG_INFO,
01402 QString("Archived video xml file: %1").arg(xmlFile));
01403
01404 QString videoFile = xmlFile.left(xmlFile.length() - 4);
01405 QFileInfo fileInfo(videoFile);
01406 QString basename = fileInfo.fileName();
01407
01408 QDomNodeList nodeList = itemNode.elementsByTagName("videometadata");
01409 if (nodeList.count() < 1)
01410 {
01411 LOG(VB_JOBQUEUE, LOG_ERR,
01412 "Couldn't find a 'videometadata' element in XML file");
01413 return 1;
01414 }
01415
01416 QDomNode n = nodeList.item(0);
01417 QDomElement videoNode = n.toElement();
01418
01419
01420 QString path = gCoreContext->GetSetting("VideoStartupDir");
01421 QString origFilename = findNodeText(videoNode, "filename");
01422 QStringList dirList = origFilename.split("/", QString::SkipEmptyParts);
01423 QDir dir;
01424 for (int x = 0; x < dirList.count() - 1; x++)
01425 {
01426 path += "/" + dirList[x];
01427 if (!dir.exists(path))
01428 {
01429 if (!dir.mkdir(path))
01430 {
01431 LOG(VB_JOBQUEUE, LOG_ERR,
01432 QString("Couldn't create directory '%1'").arg(path));
01433 return 1;
01434 }
01435 }
01436 }
01437
01438 LOG(VB_JOBQUEUE, LOG_INFO, "Copying video file");
01439 if (!copyFile(videoFile, path + "/" + basename))
01440 {
01441 return 1;
01442 }
01443
01444
01445 QString artworkDir = gCoreContext->GetSetting("VideoArtworkDir");
01446
01447 fileInfo.setFile(videoFile);
01448 QString archivePath = fileInfo.absolutePath();
01449
01450 QString coverFilename = findNodeText(videoNode, "coverfile");
01451 fileInfo.setFile(coverFilename);
01452 coverFilename = fileInfo.fileName();
01453
01454 fileInfo.setFile(archivePath + "/" + coverFilename);
01455 if (fileInfo.exists())
01456 {
01457 LOG(VB_JOBQUEUE, LOG_INFO, "Copying cover file");
01458
01459 if (!copyFile(archivePath + "/" + coverFilename, artworkDir + "/" + coverFilename))
01460 {
01461 return 1;
01462 }
01463 }
01464 else
01465 coverFilename = "No Cover";
01466
01467
01468 MSqlQuery query(MSqlQuery::InitCon());
01469 query.prepare("INSERT INTO videometadata (title, director, plot, rating, inetref, "
01470 "year, userrating, length, showlevel, filename, coverfile, "
01471 "childid, browse, playcommand, category) "
01472 "VALUES(:TITLE,:DIRECTOR,:PLOT,:RATING,:INETREF,:YEAR,"
01473 ":USERRATING,:LENGTH,:SHOWLEVEL,:FILENAME,:COVERFILE,"
01474 ":CHILDID,:BROWSE,:PLAYCOMMAND,:CATEGORY);");
01475 query.bindValue(":TITLE", findNodeText(videoNode, "title"));
01476 query.bindValue(":DIRECTOR", findNodeText(videoNode, "director"));
01477 query.bindValue(":PLOT", findNodeText(videoNode, "plot"));
01478 query.bindValue(":RATING", findNodeText(videoNode, "rating"));
01479 query.bindValue(":INETREF", findNodeText(videoNode, "inetref"));
01480 query.bindValue(":YEAR", findNodeText(videoNode, "year"));
01481 query.bindValue(":USERRATING", findNodeText(videoNode, "userrating"));
01482 query.bindValue(":LENGTH", findNodeText(videoNode, "length"));
01483 query.bindValue(":SHOWLEVEL", findNodeText(videoNode, "showlevel"));
01484 query.bindValue(":FILENAME", path + "/" + basename);
01485 query.bindValue(":COVERFILE", artworkDir + "/" + coverFilename);
01486 query.bindValue(":CHILDID", findNodeText(videoNode, "childid"));
01487 query.bindValue(":BROWSE", findNodeText(videoNode, "browse"));
01488 query.bindValue(":PLAYCOMMAND", findNodeText(videoNode, "playcommand"));
01489 query.bindValue(":CATEGORY", 0);
01490
01491 if (query.exec())
01492 LOG(VB_JOBQUEUE, LOG_INFO,
01493 "Inserted videometadata details into database");
01494 else
01495 {
01496 MythDB::DBError("videometadata insert", query);
01497 return 1;
01498 }
01499
01500
01501 int intid;
01502 query.prepare("SELECT intid FROM videometadata WHERE filename = :FILENAME;");
01503 query.bindValue(":FILENAME", path + "/" + basename);
01504 if (query.exec() && query.next())
01505 {
01506 intid = query.value(0).toInt();
01507 }
01508 else
01509 {
01510 MythDB::DBError("Failed to get intid", query);
01511 return 1;
01512 }
01513
01514 LOG(VB_JOBQUEUE, LOG_INFO,
01515 QString("'intid' of inserted video is: %1").arg(intid));
01516
01517
01518 nodeList = itemNode.elementsByTagName("genres");
01519 if (nodeList.count() < 1)
01520 {
01521 LOG(VB_JOBQUEUE, LOG_ERR, "No 'genres' element found in XML file");
01522 }
01523 else
01524 {
01525 n = nodeList.item(0);
01526 QDomElement genresNode = n.toElement();
01527
01528 nodeList = genresNode.elementsByTagName("genre");
01529 if (nodeList.count() < 1)
01530 {
01531 LOG(VB_JOBQUEUE, LOG_WARNING,
01532 "Couldn't find any 'genre' elements in XML file");
01533 }
01534 else
01535 {
01536 for (int x = 0; x < nodeList.count(); x++)
01537 {
01538 n = nodeList.item(x);
01539 QDomElement e = n.toElement();
01540 int genreID = e.attribute("intid").toInt();
01541 QString genre = e.attribute("genre");
01542
01543
01544 query.prepare("SELECT intid FROM videogenre "
01545 "WHERE genre = :GENRE");
01546 query.bindValue(":GENRE", genre);
01547 if (query.exec() && query.next())
01548 {
01549 genreID = query.value(0).toInt();
01550 }
01551 else
01552 {
01553
01554 query.prepare("INSERT INTO videogenre (genre) VALUES(:GENRE);");
01555 query.bindValue(":GENRE", genre);
01556 if (!query.exec())
01557 MythDB::DBError("NativeArchive::importVideo - "
01558 "insert videogenre", query);
01559
01560
01561 query.prepare("SELECT intid FROM videogenre "
01562 "WHERE genre = :GENRE");
01563 query.bindValue(":GENRE", genre);
01564 if (query.exec() && query.next())
01565 {
01566 genreID = query.value(0).toInt();
01567 }
01568 else
01569 {
01570 LOG(VB_JOBQUEUE, LOG_ERR,
01571 "Couldn't add genre to database");
01572 continue;
01573 }
01574 }
01575
01576
01577 query.prepare("INSERT INTO videometadatagenre (idvideo, idgenre)"
01578 "VALUES (:IDVIDEO, :IDGENRE);");
01579 query.bindValue(":IDVIDEO", intid);
01580 query.bindValue(":IDGENRE", genreID);
01581 if (!query.exec())
01582 MythDB::DBError("NativeArchive::importVideo - "
01583 "insert videometadatagenre", query);
01584 }
01585
01586 LOG(VB_JOBQUEUE, LOG_INFO, "Inserted genre details into database");
01587 }
01588 }
01589
01590
01591 nodeList = itemNode.elementsByTagName("countries");
01592 if (nodeList.count() < 1)
01593 {
01594 LOG(VB_JOBQUEUE, LOG_INFO, "No 'countries' element found in XML file");
01595 }
01596 else
01597 {
01598 n = nodeList.item(0);
01599 QDomElement countriesNode = n.toElement();
01600
01601 nodeList = countriesNode.elementsByTagName("country");
01602 if (nodeList.count() < 1)
01603 {
01604 LOG(VB_JOBQUEUE, LOG_WARNING,
01605 "Couldn't find any 'country' elements in XML file");
01606 }
01607 else
01608 {
01609 for (int x = 0; x < nodeList.count(); x++)
01610 {
01611 n = nodeList.item(x);
01612 QDomElement e = n.toElement();
01613 int countryID = e.attribute("intid").toInt();
01614 QString country = e.attribute("country");
01615
01616
01617 query.prepare("SELECT intid FROM videocountry "
01618 "WHERE country = :COUNTRY");
01619 query.bindValue(":COUNTRY", country);
01620 if (query.exec() && query.next())
01621 {
01622 countryID = query.value(0).toInt();
01623 }
01624 else
01625 {
01626
01627 query.prepare("INSERT INTO videocountry (country) VALUES(:COUNTRY);");
01628 query.bindValue(":COUNTRY", country);
01629 if (!query.exec())
01630 MythDB::DBError("NativeArchive::importVideo - "
01631 "insert videocountry", query);
01632
01633
01634 query.prepare("SELECT intid FROM videocountry "
01635 "WHERE country = :COUNTRY");
01636 query.bindValue(":COUNTRY", country);
01637 if (query.exec() && query.next())
01638 {
01639 countryID = query.value(0).toInt();
01640 }
01641 else
01642 {
01643 LOG(VB_JOBQUEUE, LOG_ERR,
01644 "Couldn't add country to database");
01645 continue;
01646 }
01647 }
01648
01649
01650 query.prepare("INSERT INTO videometadatacountry (idvideo, idcountry)"
01651 "VALUES (:IDVIDEO, :IDCOUNTRY);");
01652 query.bindValue(":IDVIDEO", intid);
01653 query.bindValue(":IDCOUNTRY", countryID);
01654 if (!query.exec())
01655 MythDB::DBError("NativeArchive::importVideo - "
01656 "insert videometadatacountry", query);
01657 }
01658
01659 LOG(VB_JOBQUEUE, LOG_INFO,
01660 "Inserted country details into database");
01661 }
01662 }
01663
01664
01665 nodeList = itemNode.elementsByTagName("category");
01666 if (nodeList.count() < 1)
01667 {
01668 LOG(VB_JOBQUEUE, LOG_ERR, "No 'category' element found in XML file");
01669 }
01670 else
01671 {
01672 n = nodeList.item(0);
01673 QDomElement e = n.toElement();
01674 int categoryID = e.attribute("intid").toInt();
01675 QString category = e.attribute("category");
01676
01677 query.prepare("SELECT intid FROM videocategory "
01678 "WHERE category = :CATEGORY");
01679 query.bindValue(":CATEGORY", category);
01680 if (query.exec() && query.next())
01681 {
01682 categoryID = query.value(0).toInt();
01683 }
01684 else
01685 {
01686
01687 query.prepare("INSERT INTO videocategory (category) VALUES(:CATEGORY);");
01688 query.bindValue(":CATEGORY", category);
01689 if (!query.exec())
01690 MythDB::DBError("NativeArchive::importVideo - "
01691 "insert videocategory", query);
01692
01693
01694 query.prepare("SELECT intid FROM videocategory "
01695 "WHERE category = :CATEGORY");
01696 query.bindValue(":CATEGORY", category);
01697 if (query.exec() && query.next())
01698 {
01699 categoryID = query.value(0).toInt();
01700 }
01701 else
01702 {
01703 LOG(VB_JOBQUEUE, LOG_ERR, "Couldn't add category to database");
01704 categoryID = 0;
01705 }
01706 }
01707
01708
01709 query.prepare("UPDATE videometadata "
01710 "SET category = :CATEGORY "
01711 "WHERE intid = :INTID;");
01712 query.bindValue(":CATEGORY", categoryID);
01713 query.bindValue(":INTID", intid);
01714 if (!query.exec())
01715 MythDB::DBError("NativeArchive::importVideo - "
01716 "update category", query);
01717
01718 LOG(VB_JOBQUEUE, LOG_INFO, "Fixed the category in the database");
01719 }
01720
01721 LOG(VB_JOBQUEUE, LOG_INFO, "Import completed OK");
01722
01723 return 0;
01724 }
01725
01726 QString NativeArchive::findNodeText(const QDomElement &elem, const QString &nodeName)
01727 {
01728 QDomNodeList nodeList = elem.elementsByTagName(nodeName);
01729 if (nodeList.count() < 1)
01730 {
01731 LOG(VB_GENERAL, LOG_ERR,
01732 QString("Couldn't find a '%1' element in XML file") .arg(nodeName));
01733 return "";
01734 }
01735
01736 QDomNode n = nodeList.item(0);
01737 QDomElement e = n.toElement();
01738 QString res = "";
01739
01740 for (QDomNode node = e.firstChild(); !node.isNull();
01741 node = node.nextSibling())
01742 {
01743 QDomText t = node.toText();
01744 if (!t.isNull())
01745 {
01746 res = t.data();
01747 break;
01748 }
01749 }
01750
01751
01752
01753 if (nodeName == "recgroup")
01754 {
01755 res = "Default";
01756 }
01757 else if (nodeName == "recordid")
01758 {
01759 res = "";
01760 }
01761 else if (nodeName == "seriesid")
01762 {
01763 res = "";
01764 }
01765 else if (nodeName == "programid")
01766 {
01767 res = "";
01768 }
01769 else if (nodeName == "playgroup")
01770 {
01771 res = "Default";
01772 }
01773 else if (nodeName == "profile")
01774 {
01775 res = "";
01776 }
01777
01778 return res;
01779 }
01780
01781 static void clearArchiveTable(void)
01782 {
01783 MSqlQuery query(MSqlQuery::InitCon());
01784 query.prepare("DELETE FROM archiveitems;");
01785
01786 if (!query.exec())
01787 MythDB::DBError("delete archiveitems", query);
01788 }
01789
01790 static int doNativeArchive(const QString &jobFile)
01791 {
01792 gCoreContext->SaveSetting("MythArchiveLastRunType", "Native Export");
01793 gCoreContext->SaveSetting("MythArchiveLastRunStart", QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm"));
01794 gCoreContext->SaveSetting("MythArchiveLastRunStatus", "Running");
01795
01796 NativeArchive na;
01797 int res = na.doNativeArchive(jobFile);
01798 gCoreContext->SaveSetting("MythArchiveLastRunEnd", QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm"));
01799 gCoreContext->SaveSetting("MythArchiveLastRunStatus", (res == 0 ? "Success" : "Failed"));
01800
01801
01802 if (res == 0)
01803 clearArchiveTable();
01804
01805 return res;
01806 }
01807
01808 static int doImportArchive(const QString &inFile, int chanID)
01809 {
01810 NativeArchive na;
01811 return na.doImportArchive(inFile, chanID);
01812 }
01813
01814
01815 static int myth_sws_img_convert(
01816 AVPicture *dst, PixelFormat dst_pix_fmt, AVPicture *src,
01817 PixelFormat pix_fmt, int width, int height)
01818 {
01819 static QMutex lock;
01820 QMutexLocker locker(&lock);
01821
01822 static struct SwsContext *convert_ctx;
01823
01824 convert_ctx = sws_getCachedContext(convert_ctx, width, height, pix_fmt,
01825 width, height, dst_pix_fmt,
01826 SWS_FAST_BILINEAR, NULL, NULL, NULL);
01827 if (!convert_ctx)
01828 {
01829 LOG(VB_GENERAL, LOG_ERR, "myth_sws_img_convert: Cannot initialize "
01830 "the image conversion context");
01831 return -1;
01832 }
01833
01834 sws_scale(convert_ctx, src->data, src->linesize,
01835 0, height, dst->data, dst->linesize);
01836
01837 return 0;
01838 }
01839
01840 static int grabThumbnail(QString inFile, QString thumbList, QString outFile, int frameCount)
01841 {
01842 av_register_all();
01843
01844 AVFormatContext *inputFC = NULL;
01845
01846
01847 LOG(VB_JOBQUEUE, LOG_INFO, QString("grabThumbnail(): Opening '%1'")
01848 .arg(inFile));
01849
01850 QByteArray inFileBA = inFile.toLocal8Bit();
01851
01852 int ret = avformat_open_input(&inputFC, inFileBA.constData(), NULL, NULL);
01853 if (ret)
01854 {
01855 LOG(VB_JOBQUEUE, LOG_ERR, "grabThumbnail(): Couldn't open input file" +
01856 ENO);
01857 return 1;
01858 }
01859
01860
01861 if ((ret = avformat_find_stream_info(inputFC, NULL)) < 0)
01862 {
01863 LOG(VB_JOBQUEUE, LOG_ERR,
01864 QString("Couldn't get stream info, error #%1").arg(ret));
01865 avformat_close_input(&inputFC);
01866 inputFC = NULL;
01867 return 1;
01868 }
01869
01870
01871 int videostream = -1, width, height;
01872 float fps;
01873
01874 for (uint i = 0; i < inputFC->nb_streams; i++)
01875 {
01876 AVStream *st = inputFC->streams[i];
01877 if (inputFC->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
01878 {
01879 videostream = i;
01880 width = st->codec->width;
01881 height = st->codec->height;
01882 if (st->r_frame_rate.den && st->r_frame_rate.num)
01883 fps = av_q2d(st->r_frame_rate);
01884 else
01885 fps = 1/av_q2d(st->time_base);
01886 break;
01887 }
01888 }
01889
01890 if (videostream == -1)
01891 {
01892 LOG(VB_JOBQUEUE, LOG_ERR, "Couldn't find a video stream");
01893 return 1;
01894 }
01895
01896
01897 AVCodecContext *codecCtx = inputFC->streams[videostream]->codec;
01898
01899
01900 AVCodec * codec = avcodec_find_decoder(codecCtx->codec_id);
01901
01902 if (codec == NULL)
01903 {
01904 LOG(VB_JOBQUEUE, LOG_ERR, "Couldn't find codec for video stream");
01905 return 1;
01906 }
01907
01908
01909 if (avcodec_open2(codecCtx, codec, NULL) < 0)
01910 {
01911 LOG(VB_JOBQUEUE, LOG_ERR, "Couldn't open codec for video stream");
01912 return 1;
01913 }
01914
01915
01916 QStringList list = thumbList.split(",", QString::SkipEmptyParts);
01917 AVFrame *frame = avcodec_alloc_frame();
01918 AVPacket pkt;
01919 AVPicture orig;
01920 AVPicture retbuf;
01921 memset(&orig, 0, sizeof(AVPicture));
01922 memset(&retbuf, 0, sizeof(AVPicture));
01923
01924 int bufflen = width * height * 4;
01925 unsigned char *outputbuf = new unsigned char[bufflen];
01926
01927 int frameNo = -1, thumbCount = 0;
01928 int frameFinished = 0;
01929 int keyFrame;
01930
01931 while (av_read_frame(inputFC, &pkt) >= 0)
01932 {
01933 if (pkt.stream_index == videostream)
01934 {
01935 frameNo++;
01936 if (list[thumbCount].toInt() == (int)(frameNo / fps))
01937 {
01938 thumbCount++;
01939
01940 avcodec_flush_buffers(codecCtx);
01941 avcodec_decode_video2(codecCtx, frame, &frameFinished, &pkt);
01942 keyFrame = frame->key_frame;
01943
01944 while (!frameFinished || !keyFrame)
01945 {
01946 av_free_packet(&pkt);
01947 int res = av_read_frame(inputFC, &pkt);
01948 if (res < 0)
01949 break;
01950 if (pkt.stream_index == videostream)
01951 {
01952 frameNo++;
01953 avcodec_decode_video2(codecCtx, frame, &frameFinished, &pkt);
01954 keyFrame = frame->key_frame;
01955 }
01956 }
01957
01958 if (frameFinished)
01959 {
01960
01961 QString saveFormat = "JPEG";
01962 if (outFile.right(4) == ".png")
01963 saveFormat = "PNG";
01964
01965 int count = 0;
01966 while (count < frameCount)
01967 {
01968 QString filename = outFile;
01969 if (filename.contains("%1") && filename.contains("%2"))
01970 filename = filename.arg(thumbCount).arg(count+1);
01971 else if (filename.contains("%1"))
01972 filename = filename.arg(thumbCount);
01973
01974 avpicture_fill(&retbuf, outputbuf,
01975 PIX_FMT_RGB32, width, height);
01976
01977 avpicture_deinterlace((AVPicture*)frame,
01978 (AVPicture*)frame,
01979 codecCtx->pix_fmt, width, height);
01980
01981
01982 myth_sws_img_convert(
01983 &retbuf, PIX_FMT_RGB32,
01984 (AVPicture*) frame,
01985 codecCtx->pix_fmt, width, height);
01986
01987 QImage img(outputbuf, width, height,
01988 QImage::Format_RGB32);
01989
01990 if (!img.save(filename, qPrintable(saveFormat)))
01991 {
01992 LOG(VB_GENERAL, LOG_ERR,
01993 QString("grabThumbnail(): Failed to save "
01994 "thumb: '%1'")
01995 .arg(filename));
01996 }
01997
01998 count++;
01999
02000 if (count <= frameCount)
02001 {
02002
02003 frameFinished = false;
02004 while (!frameFinished)
02005 {
02006 int res = av_read_frame(inputFC, &pkt);
02007 if (res < 0)
02008 break;
02009 if (pkt.stream_index == videostream)
02010 {
02011 frameNo++;
02012 avcodec_decode_video2(codecCtx, frame,
02013 &frameFinished,
02014 &pkt);
02015 }
02016 }
02017 }
02018 }
02019 }
02020
02021 if (thumbCount >= (int) list.count())
02022 break;
02023 }
02024 }
02025
02026 av_free_packet(&pkt);
02027 }
02028
02029 if (outputbuf)
02030 delete[] outputbuf;
02031
02032
02033 av_free(frame);
02034
02035
02036 avcodec_close(codecCtx);
02037
02038
02039 avformat_close_input(&inputFC);
02040
02041 return 0;
02042 }
02043
02044 static int64_t getFrameCount(AVFormatContext *inputFC, int vid_id)
02045 {
02046 AVPacket pkt;
02047 int64_t count = 0;
02048
02049 LOG(VB_JOBQUEUE, LOG_INFO, "Calculating frame count");
02050
02051 av_init_packet(&pkt);
02052
02053 while (av_read_frame(inputFC, &pkt) >= 0)
02054 {
02055 if (pkt.stream_index == vid_id)
02056 {
02057 count++;
02058 }
02059 av_free_packet(&pkt);
02060 }
02061
02062 return count;
02063 }
02064
02065 static int64_t getCutFrames(const QString &filename, int64_t lastFrame)
02066 {
02067
02068 QString basename = filename;
02069 int pos = filename.lastIndexOf('/');
02070 if (pos > 0)
02071 basename = filename.mid(pos + 1);
02072
02073 ProgramInfo *progInfo = getProgramInfoForFile(basename);
02074 if (!progInfo)
02075 return 0;
02076
02077 if (progInfo->IsVideo())
02078 {
02079 delete progInfo;
02080 return 0;
02081 }
02082
02083 frm_dir_map_t cutlist;
02084 frm_dir_map_t::iterator it;
02085 uint64_t frames = 0;
02086
02087 progInfo->QueryCutList(cutlist);
02088
02089 if (cutlist.size() == 0)
02090 {
02091 delete progInfo;
02092 return 0;
02093 }
02094
02095 for (it = cutlist.begin(); it != cutlist.end();)
02096 {
02097 uint64_t start = 0, end = 0;
02098
02099 if (it.value() == MARK_CUT_START)
02100 {
02101 start = it.key();
02102 ++it;
02103 if (it != cutlist.end())
02104 {
02105 end = it.key();
02106 ++it;
02107 }
02108 else
02109 end = lastFrame;
02110 }
02111 else if (it.value() == MARK_CUT_END)
02112 {
02113 start = 0;
02114 end = it.key();
02115 ++it;
02116 }
02117 else
02118 {
02119 ++it;
02120 continue;
02121 }
02122
02123 frames += end - start;
02124 }
02125
02126 delete progInfo;
02127 return frames;
02128 }
02129
02130 static int64_t getFrameCount(const QString &filename, float fps)
02131 {
02132
02133 QString basename = filename;
02134 int pos = filename.lastIndexOf('/');
02135 if (pos > 0)
02136 basename = filename.mid(pos + 1);
02137
02138 int keyframedist = -1;
02139 frm_pos_map_t posMap;
02140
02141 ProgramInfo *progInfo = getProgramInfoForFile(basename);
02142 if (!progInfo)
02143 return 0;
02144
02145 progInfo->QueryPositionMap(posMap, MARK_GOP_BYFRAME);
02146 if (!posMap.empty())
02147 {
02148 keyframedist = 1;
02149 }
02150 else
02151 {
02152 progInfo->QueryPositionMap(posMap, MARK_GOP_START);
02153 if (!posMap.empty())
02154 {
02155 keyframedist = 15;
02156 if (fps < 26 && fps > 24)
02157 keyframedist = 12;
02158 }
02159 else
02160 {
02161 progInfo->QueryPositionMap(posMap, MARK_KEYFRAME);
02162 if (!posMap.empty())
02163 {
02164
02165
02166 return 0;
02167 }
02168 }
02169 }
02170
02171 if (posMap.empty())
02172 return 0;
02173
02174 frm_pos_map_t::const_iterator it = posMap.end();
02175 --it;
02176 uint64_t totframes = it.key() * keyframedist;
02177 return totframes;
02178 }
02179
02180 static int getFileInfo(QString inFile, QString outFile, int lenMethod)
02181 {
02182 const char *type = NULL;
02183
02184 av_register_all();
02185
02186 AVFormatContext *inputFC = NULL;
02187 AVInputFormat *fmt = NULL;
02188
02189 if (type)
02190 fmt = av_find_input_format(type);
02191
02192
02193 LOG(VB_JOBQUEUE, LOG_INFO, QString("getFileInfo(): Opening '%1'")
02194 .arg(inFile));
02195
02196 QByteArray inFileBA = inFile.toLocal8Bit();
02197
02198 int ret = avformat_open_input(&inputFC, inFileBA.constData(), fmt, NULL);
02199
02200 if (ret)
02201 {
02202 LOG(VB_JOBQUEUE, LOG_ERR, "getFileInfo(): Couldn't open input file" +
02203 ENO);
02204 return 1;
02205 }
02206
02207
02208 ret = avformat_find_stream_info(inputFC, NULL);
02209
02210 if (ret < 0)
02211 {
02212 LOG(VB_JOBQUEUE, LOG_ERR,
02213 QString("Couldn't get stream info, error #%1").arg(ret));
02214 avformat_close_input(&inputFC);
02215 inputFC = NULL;
02216 return 1;
02217 }
02218
02219 av_estimate_timings(inputFC, 0);
02220
02221
02222 av_dump_format(inputFC, 0, inFileBA.constData(), 0);
02223
02224 QDomDocument doc("FILEINFO");
02225
02226 QDomElement root = doc.createElement("file");
02227 doc.appendChild(root);
02228 root.setAttribute("type", inputFC->iformat->name);
02229 root.setAttribute("filename", inFile);
02230
02231 QDomElement streams = doc.createElement("streams");
02232
02233 root.appendChild(streams);
02234 streams.setAttribute("count", inputFC->nb_streams);
02235 int ffmpegIndex = 0;
02236 uint duration = 0;
02237
02238 for (uint i = 0; i < inputFC->nb_streams; i++)
02239 {
02240 AVStream *st = inputFC->streams[i];
02241 char buf[256];
02242
02243 avcodec_string(buf, sizeof(buf), st->codec, false);
02244
02245 switch (inputFC->streams[i]->codec->codec_type)
02246 {
02247 case AVMEDIA_TYPE_VIDEO:
02248 {
02249 QStringList param = QString(buf).split(',', QString::SkipEmptyParts);
02250 QString codec = param[0].remove("Video:", Qt::CaseInsensitive);
02251 QDomElement stream = doc.createElement("video");
02252 stream.setAttribute("streamindex", i);
02253 stream.setAttribute("ffmpegindex", ffmpegIndex++);
02254 stream.setAttribute("codec", codec.trimmed());
02255 stream.setAttribute("width", st->codec->width);
02256 stream.setAttribute("height", st->codec->height);
02257 stream.setAttribute("bitrate", st->codec->bit_rate);
02258
02259 float fps;
02260 if (st->r_frame_rate.den && st->r_frame_rate.num)
02261 fps = av_q2d(st->r_frame_rate);
02262 else
02263 fps = 1/av_q2d(st->time_base);
02264
02265 stream.setAttribute("fps", fps);
02266
02267 if (st->codec->sample_aspect_ratio.den && st->codec->sample_aspect_ratio.num)
02268 {
02269 float aspect_ratio = av_q2d(st->codec->sample_aspect_ratio);
02270 if (QString(inputFC->iformat->name) != "nuv")
02271 aspect_ratio = ((float)st->codec->width / st->codec->height) * aspect_ratio;
02272
02273 stream.setAttribute("aspectratio", aspect_ratio);
02274 }
02275 else
02276 stream.setAttribute("aspectratio", "N/A");
02277
02278 stream.setAttribute("id", st->id);
02279
02280 if (st->start_time != (int) AV_NOPTS_VALUE)
02281 {
02282 int secs, us;
02283 secs = st->start_time / AV_TIME_BASE;
02284 us = st->start_time % AV_TIME_BASE;
02285 stream.setAttribute("start_time", QString("%1.%2")
02286 .arg(secs).arg(av_rescale(us, 1000000, AV_TIME_BASE)));
02287 }
02288 else
02289 stream.setAttribute("start_time", 0);
02290
02291 streams.appendChild(stream);
02292
02293
02294
02295 if (duration == 0)
02296 {
02297 int64_t frameCount = 0;
02298
02299 switch (lenMethod)
02300 {
02301 case 0:
02302 {
02303
02304 if (inputFC->duration != (uint) AV_NOPTS_VALUE)
02305 {
02306 duration = (uint) (inputFC->duration / AV_TIME_BASE);
02307 root.setAttribute("duration", duration);
02308 LOG(VB_JOBQUEUE, LOG_INFO,
02309 QString("duration = %1") .arg(duration));
02310 frameCount = (int64_t)(duration * fps);
02311 }
02312 else
02313 root.setAttribute("duration", "N/A");
02314 break;
02315 }
02316 case 1:
02317 {
02318
02319 frameCount = getFrameCount(inputFC, i);
02320 LOG(VB_JOBQUEUE, LOG_INFO,
02321 QString("frames = %1").arg(frameCount));
02322 duration = (uint)(frameCount / fps);
02323 LOG(VB_JOBQUEUE, LOG_INFO,
02324 QString("duration = %1").arg(duration));
02325 root.setAttribute("duration", duration);
02326 break;
02327 }
02328 case 2:
02329 {
02330
02331
02332 frameCount = getFrameCount(inFile, fps);
02333 LOG(VB_JOBQUEUE, LOG_INFO,
02334 QString("frames = %1").arg(frameCount));
02335 duration = (uint)(frameCount / fps);
02336 LOG(VB_JOBQUEUE, LOG_INFO,
02337 QString("duration = %1").arg(duration));
02338 root.setAttribute("duration", duration);
02339 break;
02340 }
02341 default:
02342 root.setAttribute("duration", "N/A");
02343 LOG(VB_JOBQUEUE, LOG_ERR,
02344 QString("Unknown lenMethod (%1)")
02345 .arg(lenMethod));
02346 }
02347
02348
02349 int64_t cutFrames = getCutFrames(inFile, frameCount);
02350 LOG(VB_JOBQUEUE, LOG_INFO,
02351 QString("cutframes = %1").arg(cutFrames));
02352 int cutduration = (int)(cutFrames / fps);
02353 LOG(VB_JOBQUEUE, LOG_INFO,
02354 QString("cutduration = %1").arg(cutduration));
02355 root.setAttribute("cutduration", duration - cutduration);
02356 }
02357
02358 break;
02359 }
02360
02361 case AVMEDIA_TYPE_AUDIO:
02362 {
02363 QStringList param = QString(buf).split(',', QString::SkipEmptyParts);
02364 QString codec = param[0].remove("Audio:", Qt::CaseInsensitive);
02365
02366 QDomElement stream = doc.createElement("audio");
02367 stream.setAttribute("streamindex", i);
02368 stream.setAttribute("ffmpegindex", ffmpegIndex++);
02369
02370
02371
02372 if (codec.trimmed().toLower() == "liba52")
02373 stream.setAttribute("codec", "AC3");
02374 else
02375 stream.setAttribute("codec", codec.trimmed());
02376
02377 stream.setAttribute("channels", st->codec->channels);
02378
02379 AVDictionaryEntry *metatag =
02380 av_dict_get(st->metadata, "language", NULL, 0);
02381 if (metatag)
02382 stream.setAttribute("language", metatag->value);
02383 else
02384 stream.setAttribute("language", "N/A");
02385
02386 stream.setAttribute("id", st->id);
02387
02388 stream.setAttribute("samplerate", st->codec->sample_rate);
02389 stream.setAttribute("bitrate", st->codec->bit_rate);
02390
02391 if (st->start_time != (int) AV_NOPTS_VALUE)
02392 {
02393 int secs, us;
02394 secs = st->start_time / AV_TIME_BASE;
02395 us = st->start_time % AV_TIME_BASE;
02396 stream.setAttribute("start_time", QString("%1.%2")
02397 .arg(secs).arg(av_rescale(us, 1000000, AV_TIME_BASE)));
02398 }
02399 else
02400 stream.setAttribute("start_time", 0);
02401
02402 streams.appendChild(stream);
02403
02404 break;
02405 }
02406
02407 case AVMEDIA_TYPE_SUBTITLE:
02408 {
02409 QStringList param = QString(buf).split(',', QString::SkipEmptyParts);
02410 QString codec = param[0].remove("Subtitle:", Qt::CaseInsensitive);
02411
02412 QDomElement stream = doc.createElement("subtitle");
02413 stream.setAttribute("streamindex", i);
02414 stream.setAttribute("ffmpegindex", ffmpegIndex++);
02415 stream.setAttribute("codec", codec.trimmed());
02416
02417 AVDictionaryEntry *metatag =
02418 av_dict_get(st->metadata, "language", NULL, 0);
02419 if (metatag)
02420 stream.setAttribute("language", metatag->value);
02421 else
02422 stream.setAttribute("language", "N/A");
02423
02424 stream.setAttribute("id", st->id);
02425
02426 streams.appendChild(stream);
02427
02428 break;
02429 }
02430
02431 case AVMEDIA_TYPE_DATA:
02432 {
02433 QDomElement stream = doc.createElement("data");
02434 stream.setAttribute("streamindex", i);
02435 stream.setAttribute("codec", buf);
02436 streams.appendChild(stream);
02437
02438 break;
02439 }
02440
02441 default:
02442 LOG(VB_JOBQUEUE, LOG_ERR,
02443 QString("Skipping unsupported codec %1 on stream %2")
02444 .arg(inputFC->streams[i]->codec->codec_type).arg(i));
02445 break;
02446 }
02447 }
02448
02449
02450 QFile f(outFile);
02451 if (!f.open(QIODevice::WriteOnly))
02452 {
02453 LOG(VB_JOBQUEUE, LOG_ERR,
02454 "Failed to open file for writing - " + outFile);
02455 return 1;
02456 }
02457
02458 QTextStream t(&f);
02459 t << doc.toString(4);
02460 f.close();
02461
02462
02463 avformat_close_input(&inputFC);
02464 inputFC = NULL;
02465
02466 return 0;
02467 }
02468
02469 static int getDBParamters(QString outFile)
02470 {
02471 DatabaseParams params = gContext->GetDatabaseParams();
02472
02473
02474 QFile f(outFile);
02475 if (!f.open(QIODevice::WriteOnly))
02476 {
02477 LOG(VB_GENERAL, LOG_ERR,
02478 QString("MythArchiveHelper: Failed to open file for writing - %1")
02479 .arg(outFile));
02480 return 1;
02481 }
02482
02483 QTextStream t(&f);
02484 t << params.dbHostName << endl;
02485 t << params.dbUserName << endl;
02486 t << params.dbPassword << endl;
02487 t << params.dbName << endl;
02488 t << gCoreContext->GetHostName() << endl;
02489 t << GetInstallPrefix() << endl;
02490 f.close();
02491
02492 return 0;
02493 }
02494
02495 static int isRemote(QString filename)
02496 {
02497
02498 if (!QFile::exists(filename))
02499 return 0;
02500
02501 struct statfs statbuf;
02502 memset(&statbuf, 0, sizeof(statbuf));
02503
02504 #if CONFIG_DARWIN
02505 if ((statfs(qPrintable(filename), &statbuf) == 0) &&
02506 ((!strcmp(statbuf.f_fstypename, "nfs")) ||
02507 (!strcmp(statbuf.f_fstypename, "afpfs")) ||
02508 (!strcmp(statbuf.f_fstypename, "smbfs"))))
02509 return 2;
02510 #elif __linux__
02511 if ((statfs(qPrintable(filename), &statbuf) == 0) &&
02512 ((statbuf.f_type == 0x6969) ||
02513 (statbuf.f_type == 0x517B)))
02514 return 2;
02515 #endif
02516
02517 return 1;
02518 }
02519
02520 class MPUBLIC MythArchiveHelperCommandLineParser : public MythCommandLineParser
02521 {
02522 public:
02523 MythArchiveHelperCommandLineParser();
02524 void LoadArguments(void);
02525 };
02526
02527 MythArchiveHelperCommandLineParser::MythArchiveHelperCommandLineParser() :
02528 MythCommandLineParser("mytharchivehelper")
02529 { LoadArguments(); }
02530
02531 void MythArchiveHelperCommandLineParser::LoadArguments(void)
02532 {
02533 addHelp();
02534 addVersion();
02535 addLogging();
02536
02537 add(QStringList( QStringList() << "-t" << "--createthumbnail" ),
02538 "createthumbnail", false,
02539 "Create one or more thumbnails\n"
02540 "Requires: --infile, --thumblist, --outfile\n"
02541 "Optional: --framecount", "");
02542 add("--infile", "infile", "",
02543 "Input file name\n"
02544 "Used with: --createthumbnail, --getfileinfo, --isremote, "
02545 "--sup2dast, --importarchive", "");
02546 add("--outfile", "outfile", "",
02547 "Output file name\n"
02548 "Used with: --createthumbnail, --getfileinfo, --getdbparameters, "
02549 "--nativearchive\n"
02550 "When used with --createthumbnail: eg 'thumb%1-%2.jpg'\n"
02551 " %1 will be replaced with the no. of the thumb\n"
02552 " %2 will be replaced with the frame no.", "");
02553 add("--thumblist", "thumblist", "",
02554 "Comma-separated list of required thumbs (in seconds)\n"
02555 "Used with: --createthumbnail","");
02556 add("--framecount", "framecount", 1,
02557 "Number of frames to grab (default 1)\n"
02558 "Used with: --createthumbnail", "");
02559
02560 add(QStringList( QStringList() << "-i" << "--getfileinfo" ),
02561 "getfileinfo", false,
02562 "Write file info about infile to outfile\n"
02563 "Requires: --infile, --outfile, --method", "");
02564 add("--method", "method", 0,
02565 "Method of file duration calculation\n"
02566 "Used with: --getfileinfo\n"
02567 " 0 = use av_estimate_timings() (quick but not very accurate - "
02568 "default)\n"
02569 " 1 = read all frames (most accurate but slow)\n"
02570 " 2 = use position map in DB (quick, only works for MythTV "
02571 "recordings)", "");
02572
02573 add(QStringList( QStringList() << "-p" << "--getdbparameters" ),
02574 "getdbparameters", false,
02575 "Write the mysql database parameters to outfile\n"
02576 "Requires: --outfile", "");
02577
02578 add(QStringList( QStringList() << "-n" << "--nativearchive" ),
02579 "nativearchive", false,
02580 "Archive files to a native archive format\n"
02581 "Requires: --outfile", "");
02582
02583 add(QStringList( QStringList() << "-f" << "--importarchive" ),
02584 "importarchive", false,
02585 "Import an archived file\n"
02586 "Requires: --infile, --chanid", "");
02587 add("--chanid", "chanid", -1,
02588 "Channel ID to use when inserting records in DB\n"
02589 "Used with: --importarchive", "");
02590
02591 add(QStringList( QStringList() << "-r" << "--isremote" ),
02592 "isremote", false,
02593 "Check if infile is on a remote filesystem\n"
02594 "Requires: --infile\n"
02595 "Returns: 0 on error or file not found\n"
02596 " - 1 file is on a local filesystem\n"
02597 " - 2 file is on a remote filesystem", "");
02598
02599 add(QStringList( QStringList() << "-b" << "--burndvd" ),
02600 "burndvd", false,
02601 "Burn a created DVD to a blank disc\n"
02602 "Optional: --mediatype, --erasedvdrw, --nativeformat", "");
02603 add("--mediatype", "mediatype", 0,
02604 "Type of media to burn\n"
02605 "Used with: --burndvd\n"
02606 " 0 = single layer DVD (default)\n"
02607 " 1 = dual layer DVD\n"
02608 " 2 = rewritable DVD", "");
02609 add("--erasedvdrw", "erasedvdrw", false,
02610 "Force an erase of DVD-R/W Media\n"
02611 "Used with: --burndvd (optional)", "");
02612 add("--nativeformat", "nativeformat", false,
02613 "Archive is a native archive format\n"
02614 "Used with: --burndvd (optional)", "");
02615
02616 add(QStringList( QStringList() << "-s" << "--sup2dast" ),
02617 "sup2dast", false,
02618 "Convert projectX subtitles to DVD subtitles\n"
02619 "Requires: --infile, --ifofile, --delay", "");
02620 add("--ifofile", "ifofile", "",
02621 "Filename of ifo file\n"
02622 "Used with: --sup2dast", "");
02623 add("--delay", "delay", 0,
02624 "Delay in ms to add to subtitles (default 0)\n"
02625 "Used with: --sup2dast", "");
02626 }
02627
02628
02629
02630 int main(int argc, char **argv)
02631 {
02632 MythArchiveHelperCommandLineParser cmdline;
02633 if (!cmdline.Parse(argc, argv))
02634 {
02635 cmdline.PrintHelp();
02636 return GENERIC_EXIT_INVALID_CMDLINE;
02637 }
02638
02639 if (cmdline.toBool("showhelp"))
02640 {
02641 cmdline.PrintHelp();
02642 return GENERIC_EXIT_OK;
02643 }
02644
02645 if (cmdline.toBool("showversion"))
02646 {
02647 cmdline.PrintVersion();
02648 return GENERIC_EXIT_OK;
02649 }
02650
02651 QCoreApplication a(argc, argv);
02652 QCoreApplication::setApplicationName("mytharchivehelper");
02653
02654
02655 int retval;
02656 QString mask("jobqueue");
02657 if ((retval = cmdline.ConfigureLogging(mask)) != GENERIC_EXIT_OK)
02658 return retval;
02659
02661
02662 close(0);
02663
02664
02665 gContext = new MythContext(MYTH_BINARY_VERSION);
02666 if (!gContext->Init(false))
02667 {
02668 LOG(VB_GENERAL, LOG_ERR, "Failed to init MythContext, exiting.");
02669 delete gContext;
02670 gContext = NULL;
02671 return GENERIC_EXIT_NO_MYTHCONTEXT;
02672 }
02673
02674 int res = 0;
02675 bool bGrabThumbnail = cmdline.toBool("createthumbnail");
02676 bool bGetDBParameters = cmdline.toBool("getdbparameters");
02677 bool bNativeArchive = cmdline.toBool("nativearchive");
02678 bool bImportArchive = cmdline.toBool("importarchive");
02679 bool bGetFileInfo = cmdline.toBool("getfileinfo");
02680 bool bIsRemote = cmdline.toBool("isremote");
02681 bool bDoBurn = cmdline.toBool("burndvd");
02682 bool bEraseDVDRW = cmdline.toBool("erasedvdrw");
02683 bool bNativeFormat = cmdline.toBool("nativeformat");;
02684 bool bSup2Dast = cmdline.toBool("sup2dast");
02685
02686 QString thumbList = cmdline.toString("thumblist");
02687 QString inFile = cmdline.toString("infile");
02688 QString outFile = cmdline.toString("outfile");
02689 QString ifoFile = cmdline.toString("ifofile");
02690
02691 int mediaType = cmdline.toUInt("mediatype");
02692 int lenMethod = cmdline.toUInt("method");
02693 int chanID = cmdline.toInt("chanid");
02694 int frameCount = cmdline.toUInt("framecount");
02695 int delay = cmdline.toUInt("delay");
02696
02697
02698 if (bGrabThumbnail)
02699 {
02700 if (inFile.isEmpty())
02701 {
02702 LOG(VB_GENERAL, LOG_ERR, "Missing --infile in -t/--grabthumbnail "
02703 "option");
02704 return GENERIC_EXIT_INVALID_CMDLINE;
02705 }
02706
02707 if (thumbList.isEmpty())
02708 {
02709 LOG(VB_GENERAL, LOG_ERR, "Missing --thumblist in -t/--grabthumbnail"
02710 " option");
02711 return GENERIC_EXIT_INVALID_CMDLINE;
02712 }
02713
02714 if (outFile.isEmpty())
02715 {
02716 LOG(VB_GENERAL, LOG_ERR, "Missing --outfile in -t/--grabthumbnail "
02717 "option");
02718 return GENERIC_EXIT_INVALID_CMDLINE;
02719 }
02720 }
02721
02722 if (bGetDBParameters)
02723 {
02724 if (outFile.isEmpty())
02725 {
02726 LOG(VB_GENERAL, LOG_ERR, "Missing argument to -p/--getdbparameters "
02727 "option");
02728 return GENERIC_EXIT_INVALID_CMDLINE;
02729 }
02730 }
02731
02732 if (bIsRemote)
02733 {
02734 if (inFile.isEmpty())
02735 {
02736 LOG(VB_GENERAL, LOG_ERR,
02737 "Missing argument to -r/--isremote option");
02738 return GENERIC_EXIT_INVALID_CMDLINE;
02739 }
02740 }
02741
02742 if (bDoBurn)
02743 {
02744 if (mediaType < 0 || mediaType > 2)
02745 {
02746 LOG(VB_GENERAL, LOG_ERR, QString("Invalid mediatype given: %1")
02747 .arg(mediaType));
02748 return GENERIC_EXIT_INVALID_CMDLINE;
02749 }
02750 }
02751
02752 if (bNativeArchive)
02753 {
02754 if (outFile.isEmpty())
02755 {
02756 LOG(VB_GENERAL, LOG_ERR, "Missing argument to -n/--nativearchive "
02757 "option");
02758 return GENERIC_EXIT_INVALID_CMDLINE;
02759 }
02760 }
02761
02762 if (bImportArchive)
02763 {
02764 if (inFile.isEmpty())
02765 {
02766 LOG(VB_GENERAL, LOG_ERR, "Missing --infile argument to "
02767 "-f/--importarchive option");
02768 return GENERIC_EXIT_INVALID_CMDLINE;
02769 }
02770 }
02771
02772 if (bGetFileInfo)
02773 {
02774 if (inFile.isEmpty())
02775 {
02776 LOG(VB_GENERAL, LOG_ERR, "Missing --infile in -i/--getfileinfo "
02777 "option");
02778 return GENERIC_EXIT_INVALID_CMDLINE;
02779 }
02780
02781 if (outFile.isEmpty())
02782 {
02783 LOG(VB_GENERAL, LOG_ERR, "Missing --outfile in -i/--getfileinfo "
02784 "option");
02785 return GENERIC_EXIT_INVALID_CMDLINE;
02786 }
02787 }
02788
02789 if (bSup2Dast)
02790 {
02791 if (inFile.isEmpty())
02792 {
02793 LOG(VB_GENERAL, LOG_ERR,
02794 "Missing --infile in -s/--sup2dast option");
02795 return GENERIC_EXIT_INVALID_CMDLINE;
02796 }
02797
02798 if (ifoFile.isEmpty())
02799 {
02800 LOG(VB_GENERAL, LOG_ERR,
02801 "Missing --ifofile in -s/--sup2dast option");
02802 return GENERIC_EXIT_INVALID_CMDLINE;
02803 }
02804 }
02805
02806 if (bGrabThumbnail)
02807 res = grabThumbnail(inFile, thumbList, outFile, frameCount);
02808 else if (bGetDBParameters)
02809 res = getDBParamters(outFile);
02810 else if (bNativeArchive)
02811 res = doNativeArchive(outFile);
02812 else if (bImportArchive)
02813 res = doImportArchive(inFile, chanID);
02814 else if (bGetFileInfo)
02815 res = getFileInfo(inFile, outFile, lenMethod);
02816 else if (bIsRemote)
02817 res = isRemote(inFile);
02818 else if (bDoBurn)
02819 res = doBurnDVD(mediaType, bEraseDVDRW, bNativeFormat);
02820 else if (bSup2Dast)
02821 {
02822 QByteArray inFileBA = inFile.toLocal8Bit();
02823 QByteArray ifoFileBA = ifoFile.toLocal8Bit();
02824 res = sup2dast(inFileBA.constData(), ifoFileBA.constData(), delay);
02825 }
02826 else
02827 cmdline.PrintHelp();
02828
02829 delete gContext;
02830 gContext = NULL;
02831
02832 exit(res);
02833 }
02834
02835