00001 #include <unistd.h>
00002 #include <stdlib.h>
00003
00004 #include <iostream>
00005 #include <cstdlib>
00006
00007
00008 #include <QFile>
00009 #include <QKeyEvent>
00010 #include <QTextStream>
00011 #include <QDomDocument>
00012
00013
00014 #include <mythcontext.h>
00015 #include <remoteutil.h>
00016 #include <programinfo.h>
00017 #include <mythdb.h>
00018 #include <mythdialogbox.h>
00019 #include <mythuitext.h>
00020 #include <mythuibutton.h>
00021 #include <mythuicheckbox.h>
00022 #include <mythuibuttonlist.h>
00023 #include <mythuiprogressbar.h>
00024 #include <mythmainwindow.h>
00025 #include <mythsystem.h>
00026 #include <exitcodes.h>
00027
00028
00029 #include "exportnative.h"
00030 #include "fileselector.h"
00031 #include "archiveutil.h"
00032 #include "recordingselector.h"
00033 #include "videoselector.h"
00034 #include "logviewer.h"
00035
00036 ExportNative::ExportNative(
00037 MythScreenStack *parent, MythScreenType *previousScreen,
00038 ArchiveDestination archiveDestination, QString name) :
00039 MythScreenType(parent, name),
00040 m_previousScreen(previousScreen),
00041 m_archiveDestination(archiveDestination),
00042 m_usedSpace(0),
00043 m_bCreateISO(false),
00044 m_bDoBurn(false),
00045 m_bEraseDvdRw(false),
00046 m_saveFilename(),
00047 m_archiveButtonList(NULL),
00048 m_nextButton(NULL),
00049 m_prevButton(NULL),
00050 m_cancelButton(NULL),
00051 m_addrecordingButton(NULL),
00052 m_addvideoButton(NULL),
00053 m_freespaceText(NULL),
00054 m_titleText(NULL),
00055 m_datetimeText(NULL),
00056 m_descriptionText(NULL),
00057 m_filesizeText(NULL),
00058 m_nofilesText(NULL),
00059 m_maxsizeText(NULL),
00060 m_minsizeText(NULL),
00061 m_currsizeText(NULL),
00062 m_currsizeErrText(NULL),
00063 m_sizeBar(NULL)
00064 {
00065 }
00066
00067 ExportNative::~ExportNative(void)
00068 {
00069 saveConfiguration();
00070
00071 while (!m_archiveList.isEmpty())
00072 delete m_archiveList.takeFirst();
00073 m_archiveList.clear();
00074 }
00075
00076 bool ExportNative::Create(void)
00077 {
00078 bool foundtheme = false;
00079
00080
00081 foundtheme = LoadWindowFromXML("mythnative-ui.xml", "exportnative", this);
00082
00083 if (!foundtheme)
00084 return false;
00085
00086 bool err = false;
00087 UIUtilE::Assign(this, m_nextButton, "next_button", &err);
00088 UIUtilE::Assign(this, m_prevButton, "prev_button", &err);
00089 UIUtilE::Assign(this, m_cancelButton, "cancel_button", &err);
00090
00091 UIUtilE::Assign(this, m_titleText, "progtitle", &err);
00092 UIUtilE::Assign(this, m_datetimeText, "progdatetime", &err);
00093 UIUtilE::Assign(this, m_descriptionText, "progdescription", &err);
00094 UIUtilE::Assign(this, m_filesizeText, "filesize", &err);
00095 UIUtilE::Assign(this, m_nofilesText, "nofiles", &err);
00096 UIUtilE::Assign(this, m_sizeBar, "size_bar", &err);
00097 UIUtilE::Assign(this, m_archiveButtonList, "archivelist", &err);
00098 UIUtilE::Assign(this, m_addrecordingButton, "addrecording_button", &err);
00099 UIUtilE::Assign(this, m_addvideoButton, "addvideo_button", &err);
00100
00101 UIUtilW::Assign(this, m_maxsizeText, "maxsize");
00102 UIUtilW::Assign(this, m_minsizeText, "minsize");
00103 UIUtilW::Assign(this, m_currsizeText, "currentsize");
00104 UIUtilW::Assign(this, m_currsizeErrText, "currentsize_error");
00105
00106 if (err)
00107 {
00108 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'exportnative'");
00109 return false;
00110 }
00111
00112 connect(m_nextButton, SIGNAL(Clicked()), this, SLOT(handleNextPage()));
00113 connect(m_prevButton, SIGNAL(Clicked()), this, SLOT(handlePrevPage()));
00114 connect(m_cancelButton, SIGNAL(Clicked()), this, SLOT(handleCancel()));
00115
00116
00117 getArchiveList();
00118 connect(m_archiveButtonList, SIGNAL(itemSelected(MythUIButtonListItem *)),
00119 this, SLOT(titleChanged(MythUIButtonListItem *)));
00120
00121 connect(m_addrecordingButton, SIGNAL(Clicked()), this, SLOT(handleAddRecording()));
00122 connect(m_addvideoButton, SIGNAL(Clicked()), this, SLOT(handleAddVideo()));
00123
00124 BuildFocusList();
00125
00126 SetFocusWidget(m_nextButton);
00127
00128 loadConfiguration();
00129
00130 return true;
00131 }
00132
00133 bool ExportNative::keyPressEvent(QKeyEvent *event)
00134 {
00135 if (GetFocusWidget()->keyPressEvent(event))
00136 return true;
00137
00138 bool handled = false;
00139 QStringList actions;
00140 handled = GetMythMainWindow()->TranslateKeyPress("Archive", event, actions);
00141
00142 for (int i = 0; i < actions.size() && !handled; i++)
00143 {
00144 QString action = actions[i];
00145 handled = true;
00146
00147 if (action == "MENU")
00148 {
00149 showMenu();
00150 }
00151 else if (action == "DELETE")
00152 {
00153 removeItem();
00154 }
00155
00156 else
00157 handled = false;
00158 }
00159
00160 if (!handled && MythScreenType::keyPressEvent(event))
00161 handled = true;
00162
00163 return handled;
00164 }
00165
00166 void ExportNative::updateSizeBar()
00167 {
00168 int64_t size = 0;
00169 ArchiveItem *a;
00170
00171 for (int x = 0; x < m_archiveList.size(); x++)
00172 {
00173 a = m_archiveList.at(x);
00174 size += a->size;
00175 }
00176
00177 m_usedSpace = size / 1024 / 1024;
00178 uint freeSpace = m_archiveDestination.freeSpace / 1024;
00179
00180 QString tmpSize;
00181
00182 m_sizeBar->SetTotal(freeSpace);
00183 m_sizeBar->SetUsed(m_usedSpace);
00184
00185 tmpSize.sprintf("%0d Mb", freeSpace);
00186
00187 if (m_maxsizeText)
00188 m_maxsizeText->SetText(tmpSize);
00189
00190 if (m_minsizeText)
00191 m_minsizeText->SetText("0 Mb");
00192
00193 tmpSize.sprintf("%0d Mb", m_usedSpace);
00194
00195 if (m_usedSpace > freeSpace)
00196 {
00197 if (m_currsizeText)
00198 m_currsizeText->Hide();
00199
00200 if (m_currsizeErrText)
00201 {
00202 m_currsizeErrText->Show();
00203 m_currsizeErrText->SetText(tmpSize);
00204 }
00205 }
00206 else
00207 {
00208 if (m_currsizeErrText)
00209 m_currsizeErrText->Hide();
00210
00211 if (m_currsizeText)
00212 {
00213 m_currsizeText->Show();
00214 m_currsizeText->SetText(tmpSize);
00215 }
00216 }
00217 }
00218
00219 void ExportNative::titleChanged(MythUIButtonListItem *item)
00220 {
00221 ArchiveItem *a;
00222
00223 a = qVariantValue<ArchiveItem *>(item->GetData());
00224
00225 if (!a)
00226 return;
00227
00228 m_titleText->SetText(a->title);
00229
00230 m_datetimeText->SetText(a->startDate + " " + a->startTime);
00231
00232 m_descriptionText->SetText(
00233 (a->subtitle != "" ? a->subtitle + "\n" : "") + a->description);
00234
00235 m_filesizeText->SetText(formatSize(a->size / 1024, 2));
00236 }
00237
00238 void ExportNative::handleNextPage()
00239 {
00240 if (m_archiveList.size() == 0)
00241 {
00242 ShowOkPopup(tr("You need to add at least one item to archive!"));
00243 return;
00244 }
00245
00246 runScript();
00247
00248 m_previousScreen->Close();
00249 Close();
00250 }
00251
00252 void ExportNative::handlePrevPage()
00253 {
00254 Close();
00255 }
00256
00257 void ExportNative::handleCancel()
00258 {
00259 m_previousScreen->Close();
00260 Close();
00261 }
00262
00263 void ExportNative::updateArchiveList(void)
00264 {
00265 m_archiveButtonList->Reset();
00266
00267 if (m_archiveList.size() == 0)
00268 {
00269 m_titleText->Reset();
00270 m_datetimeText->Reset();
00271 m_descriptionText->Reset();
00272 m_filesizeText->Reset();
00273 m_nofilesText->Show();
00274 }
00275 else
00276 {
00277 ArchiveItem *a;
00278 for (int x = 0; x < m_archiveList.size(); x++)
00279 {
00280 a = m_archiveList.at(x);
00281
00282 MythUIButtonListItem* item = new MythUIButtonListItem(m_archiveButtonList, a->title);
00283 item->SetData(qVariantFromValue(a));
00284 }
00285
00286 m_archiveButtonList->SetItemCurrent(m_archiveButtonList->GetItemFirst());
00287 titleChanged(m_archiveButtonList->GetItemCurrent());
00288 m_nofilesText->Hide();
00289 }
00290
00291 updateSizeBar();
00292 }
00293
00294 void ExportNative::getArchiveListFromDB(void)
00295 {
00296 while (!m_archiveList.isEmpty())
00297 delete m_archiveList.takeFirst();
00298 m_archiveList.clear();
00299
00300 MSqlQuery query(MSqlQuery::InitCon());
00301 query.prepare("SELECT intid, type, title, subtitle, description, size, "
00302 "startdate, starttime, filename, hascutlist "
00303 "FROM archiveitems WHERE type = 'Recording' OR type = 'Video' "
00304 "ORDER BY title, subtitle");
00305
00306 if (query.exec())
00307 {
00308 while (query.next())
00309 {
00310 ArchiveItem *item = new ArchiveItem;
00311
00312 item->id = query.value(0).toInt();
00313 item->type = query.value(1).toString();
00314 item->title = query.value(2).toString();
00315 item->subtitle = query.value(3).toString();
00316 item->description = query.value(4).toString();
00317 item->size = query.value(5).toLongLong();
00318 item->startDate = query.value(6).toString();
00319 item->startTime = query.value(7).toString();
00320 item->filename = query.value(8).toString();
00321 item->hasCutlist = (query.value(9).toInt() > 0);
00322 item->useCutlist = false;
00323 item->editedDetails = false;
00324
00325 m_archiveList.append(item);
00326 }
00327 }
00328 }
00329
00330 void ExportNative::getArchiveList(void)
00331 {
00332 getArchiveListFromDB();
00333 updateArchiveList();
00334 }
00335
00336 void ExportNative::loadConfiguration(void)
00337 {
00338 m_bCreateISO = (gCoreContext->GetSetting("MythNativeCreateISO", "0") == "1");
00339 m_bDoBurn = (gCoreContext->GetSetting("MythNativeBurnDVDr", "1") == "1");
00340 m_bEraseDvdRw = (gCoreContext->GetSetting("MythNativeEraseDvdRw", "0") == "1");
00341 m_saveFilename = gCoreContext->GetSetting("MythNativeSaveFilename", "");
00342 }
00343
00344 void ExportNative::saveConfiguration(void)
00345 {
00346
00347 MSqlQuery query(MSqlQuery::InitCon());
00348 query.prepare("DELETE FROM archiveitems;");
00349 if (!query.exec())
00350 MythDB::DBError("ExportNative::saveConfiguration - "
00351 "deleting archiveitems", query);
00352
00353
00354 ArchiveItem *a;
00355 for (int x = 0; x < m_archiveList.size(); x++)
00356 {
00357 a = m_archiveList.at(x);
00358
00359 query.prepare("INSERT INTO archiveitems (type, title, subtitle, "
00360 "description, startdate, starttime, size, filename, hascutlist, "
00361 "duration, cutduration, videowidth, videoheight, filecodec,"
00362 "videocodec, encoderprofile) "
00363 "VALUES(:TYPE, :TITLE, :SUBTITLE, :DESCRIPTION, :STARTDATE, "
00364 ":STARTTIME, :SIZE, :FILENAME, :HASCUTLIST, :DURATION, "
00365 ":CUTDURATION, :VIDEOWIDTH, :VIDEOHEIGHT, :FILECODEC, "
00366 ":VIDEOCODEC, :ENCODERPROFILE);");
00367 query.bindValue(":TYPE", a->type);
00368 query.bindValue(":TITLE", a->title);
00369 query.bindValue(":SUBTITLE", a->subtitle);
00370 query.bindValue(":DESCRIPTION", a->description);
00371 query.bindValue(":STARTDATE", a->startDate);
00372 query.bindValue(":STARTTIME", a->startTime);
00373 query.bindValue(":SIZE", 0);
00374 query.bindValue(":FILENAME", a->filename);
00375 query.bindValue(":HASCUTLIST", a->hasCutlist);
00376 query.bindValue(":DURATION", 0);
00377 query.bindValue(":CUTDURATION", 0);
00378 query.bindValue(":VIDEOWIDTH", 0);
00379 query.bindValue(":VIDEOHEIGHT", 0);
00380 query.bindValue(":FILECODEC", "");
00381 query.bindValue(":VIDEOCODEC", "");
00382 query.bindValue(":ENCODERPROFILE", "");
00383
00384 if (!query.exec())
00385 MythDB::DBError("archive item insert", query);
00386 }
00387 }
00388
00389 void ExportNative::showMenu()
00390 {
00391 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
00392
00393 MythDialogBox *menuPopup = new MythDialogBox(tr("Menu"), popupStack, "actionmenu");
00394
00395 if (menuPopup->Create())
00396 popupStack->AddScreen(menuPopup);
00397
00398 menuPopup->SetReturnEvent(this, "action");
00399
00400 menuPopup->AddButton(tr("Remove Item"), SLOT(removeItem()));
00401 }
00402
00403 void ExportNative::removeItem()
00404 {
00405 MythUIButtonListItem *item = m_archiveButtonList->GetItemCurrent();
00406 ArchiveItem *curItem = qVariantValue<ArchiveItem *>(item->GetData());
00407
00408 if (!curItem)
00409 return;
00410
00411 MSqlQuery query(MSqlQuery::InitCon());
00412 query.prepare("DELETE FROM archiveitems WHERE filename = :FILENAME;");
00413 query.bindValue(":FILENAME", curItem->filename);
00414 if (query.exec() && query.numRowsAffected())
00415 {
00416 getArchiveList();
00417 }
00418 }
00419
00420 void ExportNative::createConfigFile(const QString &filename)
00421 {
00422 QDomDocument doc("NATIVEARCHIVEJOB");
00423
00424 QDomElement root = doc.createElement("nativearchivejob");
00425 doc.appendChild(root);
00426
00427 QDomElement job = doc.createElement("job");
00428 root.appendChild(job);
00429
00430 QDomElement media = doc.createElement("media");
00431 job.appendChild(media);
00432
00433
00434 ArchiveItem *a;
00435 for (int x = 0; x < m_archiveList.size(); x++)
00436 {
00437 a = m_archiveList.at(x);
00438
00439 QDomElement file = doc.createElement("file");
00440 file.setAttribute("type", a->type.toLower() );
00441 file.setAttribute("title", a->title);
00442 file.setAttribute("filename", a->filename);
00443 file.setAttribute("delete", "0");
00444 media.appendChild(file);
00445 }
00446
00447
00448 QDomElement options = doc.createElement("options");
00449 options.setAttribute("createiso", m_bCreateISO);
00450 options.setAttribute("doburn", m_bDoBurn);
00451 options.setAttribute("mediatype", m_archiveDestination.type);
00452 options.setAttribute("dvdrsize", (qint64)m_archiveDestination.freeSpace);
00453 options.setAttribute("erasedvdrw", m_bEraseDvdRw);
00454 options.setAttribute("savedirectory", m_saveFilename);
00455 job.appendChild(options);
00456
00457
00458 QFile f(filename);
00459 if (!f.open(QIODevice::WriteOnly))
00460 {
00461 LOG(VB_GENERAL, LOG_ERR,
00462 QString("ExportNative::createConfigFile: "
00463 "Failed to open file for writing - %1") .arg(filename));
00464 return;
00465 }
00466
00467 QTextStream t(&f);
00468 t << doc.toString(4);
00469 f.close();
00470 }
00471
00472 void ExportNative::runScript()
00473 {
00474 QString tempDir = getTempDirectory();
00475 QString logDir = tempDir + "logs";
00476 QString configDir = tempDir + "config";
00477 QString commandline;
00478
00479
00480 if (QFile::exists(logDir + "/progress.log"))
00481 QFile::remove(logDir + "/progress.log");
00482
00483
00484 if (QFile::exists(logDir + "/mythburncancel.lck"))
00485 QFile::remove(logDir + "/mythburncancel.lck");
00486
00487 createConfigFile(configDir + "/mydata.xml");
00488 commandline = "mytharchivehelper --nativearchive --outfile " + configDir +
00489 "/mydata.xml";
00490 commandline += logPropagateArgs;
00491 if (!logPropagateQuiet())
00492 commandline += " --quiet";
00493 commandline += " > " + logDir + "/progress.log 2>&1 &";
00494
00495 uint flags = kMSRunBackground | kMSDontBlockInputDevs |
00496 kMSDontDisableDrawing;
00497 uint retval = myth_system(commandline, flags);
00498 if (retval != GENERIC_EXIT_RUNNING && retval != GENERIC_EXIT_OK)
00499 {
00500 ShowOkPopup(QObject::tr("It was not possible to create the DVD. "
00501 "An error occured when running the scripts") );
00502 return;
00503 }
00504
00505 showLogViewer();
00506 }
00507
00508 void ExportNative::handleAddRecording()
00509 {
00510 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00511
00512 RecordingSelector *selector = new RecordingSelector(mainStack, &m_archiveList);
00513
00514 connect(selector, SIGNAL(haveResult(bool)),
00515 this, SLOT(selectorClosed(bool)));
00516
00517 if (selector->Create())
00518 mainStack->AddScreen(selector);
00519 }
00520
00521 void ExportNative::selectorClosed(bool ok)
00522 {
00523 if (ok)
00524 updateArchiveList();
00525 }
00526
00527 void ExportNative::handleAddVideo()
00528 {
00529 MSqlQuery query(MSqlQuery::InitCon());
00530 query.prepare("SELECT title FROM videometadata");
00531 if (query.exec() && query.size())
00532 {
00533 }
00534 else
00535 {
00536 ShowOkPopup(tr("You don't have any videos!"));
00537 return;
00538 }
00539
00540 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00541
00542 VideoSelector *selector = new VideoSelector(mainStack, &m_archiveList);
00543
00544 connect(selector, SIGNAL(haveResult(bool)),
00545 this, SLOT(selectorClosed(bool)));
00546
00547 if (selector->Create())
00548 mainStack->AddScreen(selector);
00549 }