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