00001 #include <unistd.h>
00002
00003 #include <stdlib.h>
00004 #include <unistd.h>
00005 #include <iostream>
00006 #include <cstdlib>
00007 #include <sys/wait.h>
00008
00009
00010 #include <QDir>
00011 #include <QDomDocument>
00012 #include <QApplication>
00013 #include <QKeyEvent>
00014 #include <QTextStream>
00015
00016
00017 #include <mythcontext.h>
00018 #include <mythdb.h>
00019 #include <mythdirs.h>
00020 #include <mythprogressdialog.h>
00021 #include <mythuihelper.h>
00022 #include <mythdialogbox.h>
00023 #include <mythuitext.h>
00024 #include <mythuibutton.h>
00025 #include <mythuicheckbox.h>
00026 #include <mythuibuttonlist.h>
00027 #include <mythuiprogressbar.h>
00028 #include <mythmiscutil.h>
00029 #include <mythsystem.h>
00030 #include <exitcodes.h>
00031
00032
00033 #include "archiveutil.h"
00034 #include "mythburn.h"
00035 #include "editmetadata.h"
00036 #include "fileselector.h"
00037 #include "thumbfinder.h"
00038 #include "recordingselector.h"
00039 #include "videoselector.h"
00040 #include "logviewer.h"
00041
00042 using namespace std;
00043
00044 MythBurn::MythBurn(MythScreenStack *parent,
00045 MythScreenType *destinationScreen,
00046 MythScreenType *themeScreen,
00047 ArchiveDestination archiveDestination, QString name) :
00048 MythScreenType(parent, name),
00049 m_destinationScreen(destinationScreen),
00050 m_themeScreen(themeScreen),
00051 m_archiveDestination(archiveDestination),
00052 m_bCreateISO(false),
00053 m_bDoBurn(false),
00054 m_bEraseDvdRw(false),
00055 m_saveFilename(""),
00056 m_theme(),
00057 m_moveMode(false),
00058 m_nextButton(NULL),
00059 m_prevButton(NULL),
00060 m_cancelButton(NULL),
00061 m_archiveButtonList(NULL),
00062 m_nofilesText(NULL),
00063 m_addrecordingButton(NULL),
00064 m_addvideoButton(NULL),
00065 m_addfileButton(NULL),
00066 m_sizeBar(NULL),
00067 m_maxsizeText(NULL),
00068 m_minsizeText(NULL),
00069 m_currentsizeErrorText(NULL),
00070 m_currentsizeText(NULL)
00071 {
00072
00073 QString thumbDir = getTempDirectory() + "/config/thumbs";
00074 QDir dir(thumbDir);
00075 if (dir.exists() && !MythRemoveDirectory(dir))
00076 LOG(VB_GENERAL, LOG_ERR, "MythBurn: Failed to clear thumb directory");
00077 }
00078
00079 MythBurn::~MythBurn(void)
00080 {
00081 saveConfiguration();
00082
00083 while (!m_profileList.isEmpty())
00084 delete m_profileList.takeFirst();
00085 m_profileList.clear();
00086
00087 while (!m_archiveList.isEmpty())
00088 delete m_archiveList.takeFirst();
00089 m_archiveList.clear();
00090 }
00091
00092 bool MythBurn::Create(void)
00093 {
00094 bool foundtheme = false;
00095
00096
00097 foundtheme = LoadWindowFromXML("mythburn-ui.xml", "mythburn", this);
00098
00099 if (!foundtheme)
00100 return false;
00101
00102 bool err = false;
00103 UIUtilE::Assign(this, m_nextButton, "next_button", &err);
00104 UIUtilE::Assign(this, m_prevButton, "prev_button", &err);
00105 UIUtilE::Assign(this, m_cancelButton, "cancel_button", &err);
00106 UIUtilE::Assign(this, m_nofilesText, "nofiles", &err);
00107 UIUtilE::Assign(this, m_archiveButtonList, "archivelist", &err);
00108 UIUtilE::Assign(this, m_addrecordingButton, "addrecording_button", &err);
00109 UIUtilE::Assign(this, m_addvideoButton, "addvideo_button", &err);
00110 UIUtilE::Assign(this, m_addfileButton, "addfile_button", &err);
00111 UIUtilE::Assign(this, m_maxsizeText, "maxsize", &err);
00112 UIUtilE::Assign(this, m_minsizeText, "minsize", &err);
00113 UIUtilE::Assign(this, m_currentsizeErrorText, "currentsize_error", &err);
00114 UIUtilE::Assign(this, m_currentsizeText, "currentsize", &err);
00115 UIUtilE::Assign(this, m_sizeBar, "size_bar", &err);
00116
00117 if (err)
00118 {
00119 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'mythburn'");
00120 return false;
00121 }
00122
00123 connect(m_nextButton, SIGNAL(Clicked()), this, SLOT(handleNextPage()));
00124 connect(m_prevButton, SIGNAL(Clicked()), this, SLOT(handlePrevPage()));
00125 connect(m_cancelButton, SIGNAL(Clicked()), this, SLOT(handleCancel()));
00126
00127
00128 loadEncoderProfiles();
00129 loadConfiguration();
00130
00131 updateArchiveList();
00132
00133 connect(m_addrecordingButton, SIGNAL(Clicked()),
00134 this, SLOT(handleAddRecording()));
00135
00136 connect(m_addvideoButton, SIGNAL(Clicked()), this, SLOT(handleAddVideo()));
00137 connect(m_addfileButton, SIGNAL(Clicked()), this, SLOT(handleAddFile()));
00138 connect(m_archiveButtonList, SIGNAL(itemClicked(MythUIButtonListItem *)),
00139 this, SLOT(itemClicked(MythUIButtonListItem *)));
00140
00141 BuildFocusList();
00142
00143 SetFocusWidget(m_nextButton);
00144
00145 return true;
00146 }
00147
00148 bool MythBurn::keyPressEvent(QKeyEvent *event)
00149 {
00150 if (!m_moveMode && GetFocusWidget()->keyPressEvent(event))
00151 return true;
00152
00153 bool handled = false;
00154 QStringList actions;
00155 handled = GetMythMainWindow()->TranslateKeyPress("Archive", event, actions);
00156
00157 for (int i = 0; i < actions.size() && !handled; i++)
00158 {
00159 QString action = actions[i];
00160 handled = true;
00161
00162
00163
00164 if (m_moveMode)
00165 {
00166 MythUIButtonListItem *item = m_archiveButtonList->GetItemCurrent();
00167 if (!item)
00168 return false;
00169
00170 if (action == "SELECT" || action == "ESCAPE")
00171 {
00172 m_moveMode = false;
00173 item->DisplayState("off", "movestate");
00174 }
00175 else if (action == "UP")
00176 {
00177 item->MoveUpDown(true);
00178 }
00179 else if (action == "DOWN")
00180 {
00181 item->MoveUpDown(false);
00182 }
00183
00184 return true;
00185 }
00186
00187 if (action == "MENU")
00188 {
00189 showMenu();
00190 }
00191 else if (action == "DELETE")
00192 {
00193 removeItem();
00194 }
00195 else if (action == "INFO")
00196 {
00197 editThumbnails();
00198 }
00199 else if (action == "TOGGLECUT")
00200 {
00201 toggleUseCutlist();
00202 }
00203 else
00204 handled = false;
00205 }
00206
00207 if (!handled && MythScreenType::keyPressEvent(event))
00208 handled = true;
00209
00210 return handled;
00211 }
00212
00213 void MythBurn::updateSizeBar(void)
00214 {
00215 int64_t size = 0;
00216 ArchiveItem *a;
00217 for (int x = 0; x < m_archiveList.size(); x++)
00218 {
00219 a = m_archiveList.at(x);
00220 size += a->newsize;
00221 }
00222
00223 uint usedSpace = size / 1024 / 1024;
00224
00225 QString tmpSize;
00226
00227 m_sizeBar->SetTotal(m_archiveDestination.freeSpace / 1024);
00228 m_sizeBar->SetUsed(usedSpace);
00229
00230 tmpSize = QString("%1 Mb").arg(m_archiveDestination.freeSpace / 1024);
00231
00232 m_maxsizeText->SetText(tmpSize);
00233
00234 m_minsizeText->SetText("0 Mb");
00235
00236 tmpSize = QString("%1 Mb").arg(usedSpace);
00237
00238 if (usedSpace > m_archiveDestination.freeSpace / 1024)
00239 {
00240 m_currentsizeText->Hide();
00241
00242 m_currentsizeErrorText->SetText(tmpSize);
00243 m_currentsizeErrorText->Show();
00244 }
00245 else
00246 {
00247 m_currentsizeErrorText->Hide();
00248
00249 m_currentsizeText->SetText(tmpSize);
00250 m_currentsizeText->Show();
00251 }
00252 }
00253
00254 void MythBurn::loadEncoderProfiles()
00255 {
00256 EncoderProfile *item = new EncoderProfile;
00257 item->name = "NONE";
00258 item->description = "";
00259 item->bitrate = 0.0f;
00260 m_profileList.append(item);
00261
00262
00263
00264 QString filename = GetConfDir() +
00265 "/MythArchive/ffmpeg_dvd_" +
00266 ((gCoreContext->GetSetting("MythArchiveVideoFormat", "pal")
00267 .toLower() == "ntsc") ? "ntsc" : "pal") + ".xml";
00268
00269 if (!QFile::exists(filename))
00270 {
00271
00272 filename = GetShareDir() +
00273 "mytharchive/encoder_profiles/ffmpeg_dvd_" +
00274 ((gCoreContext->GetSetting("MythArchiveVideoFormat", "pal")
00275 .toLower() == "ntsc") ? "ntsc" : "pal") + ".xml";
00276 }
00277
00278 LOG(VB_GENERAL, LOG_NOTICE,
00279 "MythArchive: Loading encoding profiles from " + filename);
00280
00281 QDomDocument doc("mydocument");
00282 QFile file(filename);
00283 if (!file.open(QIODevice::ReadOnly))
00284 return;
00285
00286 if (!doc.setContent( &file ))
00287 {
00288 file.close();
00289 return;
00290 }
00291 file.close();
00292
00293 QDomElement docElem = doc.documentElement();
00294 QDomNodeList profileNodeList = doc.elementsByTagName("profile");
00295 QString name, desc, bitrate;
00296
00297 for (int x = 0; x < (int) profileNodeList.count(); x++)
00298 {
00299 QDomNode n = profileNodeList.item(x);
00300 QDomElement e = n.toElement();
00301 QDomNode n2 = e.firstChild();
00302 while (!n2.isNull())
00303 {
00304 QDomElement e2 = n2.toElement();
00305 if(!e2.isNull())
00306 {
00307 if (e2.tagName() == "name")
00308 name = e2.text();
00309 if (e2.tagName() == "description")
00310 desc = e2.text();
00311 if (e2.tagName() == "bitrate")
00312 bitrate = e2.text();
00313
00314 }
00315 n2 = n2.nextSibling();
00316
00317 }
00318
00319 EncoderProfile *item = new EncoderProfile;
00320 item->name = name;
00321 item->description = desc;
00322 item->bitrate = bitrate.toFloat();
00323 m_profileList.append(item);
00324 }
00325 }
00326
00327 void MythBurn::toggleUseCutlist(void)
00328 {
00329 MythUIButtonListItem *item = m_archiveButtonList->GetItemCurrent();
00330 ArchiveItem *a = qVariantValue<ArchiveItem *>(item->GetData());
00331
00332 if (!a)
00333 return;
00334
00335 if (!a->hasCutlist)
00336 return;
00337
00338 a->useCutlist = !a->useCutlist;
00339
00340 if (a->hasCutlist)
00341 {
00342 if (a->useCutlist)
00343 {
00344 item->SetText(tr("Using Cut List"), "cutlist");
00345 item->DisplayState("using", "cutliststatus");
00346 }
00347 else
00348 {
00349 item->SetText(tr("Not Using Cut List"), "cutlist");
00350 item->DisplayState("notusing", "cutliststatus");
00351 }
00352 }
00353 else
00354 {
00355 item->SetText(tr("No Cut List"), "cutlist");
00356 item->DisplayState("none", "cutliststatus");
00357 }
00358 recalcItemSize(a);
00359 updateSizeBar();
00360 }
00361
00362 void MythBurn::handleNextPage()
00363 {
00364 if (m_archiveList.size() == 0)
00365 {
00366 ShowOkPopup(tr("You need to add at least one item to archive!"));
00367 return;
00368 }
00369
00370 runScript();
00371 }
00372
00373 void MythBurn::handlePrevPage()
00374 {
00375 Close();
00376 }
00377
00378 void MythBurn::handleCancel()
00379 {
00380 m_destinationScreen->Close();
00381 m_themeScreen->Close();
00382 Close();
00383 }
00384
00385 QString MythBurn::loadFile(const QString &filename)
00386 {
00387 QString res = "";
00388
00389 QFile file(filename);
00390
00391 if (!file.exists())
00392 return "";
00393
00394 if (file.open( QIODevice::ReadOnly ))
00395 {
00396 QTextStream stream(&file);
00397
00398 while ( !stream.atEnd() )
00399 {
00400 res = res + stream.readLine();
00401 }
00402 file.close();
00403 }
00404 else
00405 return "";
00406
00407 return res;
00408 }
00409
00410 void MythBurn::updateArchiveList(void)
00411 {
00412 QString message = tr("Retrieving File Information. Please Wait...");
00413
00414 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
00415
00416 MythUIBusyDialog *busyPopup = new
00417 MythUIBusyDialog(message, popupStack, "mythburnbusydialog");
00418
00419 if (busyPopup->Create())
00420 popupStack->AddScreen(busyPopup, false);
00421 else
00422 {
00423 delete busyPopup;
00424 busyPopup = NULL;
00425 }
00426
00427 qApp->processEvents();
00428
00429 m_archiveButtonList->Reset();
00430
00431 if (m_archiveList.size() == 0)
00432 {
00433 m_nofilesText->Show();
00434 }
00435 else
00436 {
00437 ArchiveItem *a;
00438 for (int x = 0; x < m_archiveList.size(); x++)
00439 {
00440 qApp->processEvents();
00441 a = m_archiveList.at(x);
00442
00443
00444 if (a->duration == 0)
00445 {
00446 if (!getFileDetails(a))
00447 LOG(VB_GENERAL, LOG_ERR,
00448 QString("MythBurn: failed to get file details for: %1").arg(a->filename));
00449 }
00450
00451
00452
00453 if (a->encoderProfile == NULL)
00454 a->encoderProfile = getDefaultProfile(a);
00455
00456 recalcItemSize(a);
00457
00458 MythUIButtonListItem* item = new
00459 MythUIButtonListItem(m_archiveButtonList, a->title);
00460 item->SetData(qVariantFromValue(a));
00461 item->SetText(a->subtitle, "subtitle");
00462 item->SetText(a->startDate + " " + a->startTime, "date");
00463 item->SetText(formatSize(a->newsize / 1024, 2), "size");
00464 if (a->hasCutlist)
00465 {
00466 if (a->useCutlist)
00467 {
00468 item->SetText(tr("Using Cut List"), "cutlist");
00469 item->DisplayState("using", "cutliststatus");
00470 }
00471 else
00472 {
00473 item->SetText(tr("Not Using Cut List"), "cutlist");
00474 item->DisplayState("notusing", "cutliststatus");
00475 }
00476 }
00477 else
00478 {
00479 item->SetText(tr("No Cut List"), "cutlist");
00480 item->DisplayState("none", "cutliststatus");
00481 }
00482 item->SetText(tr("Encoder: ") + a->encoderProfile->name, "profile");
00483 }
00484
00485 m_nofilesText->Hide();
00486
00487 m_archiveButtonList->SetItemCurrent(
00488 m_archiveButtonList->GetItemFirst());
00489 }
00490
00491 updateSizeBar();
00492
00493 if (busyPopup)
00494 busyPopup->Close();
00495 }
00496
00497 bool MythBurn::isArchiveItemValid(const QString &type, const QString &filename)
00498 {
00499 if (type == "Recording")
00500 {
00501 QString baseName = getBaseName(filename);
00502
00503 MSqlQuery query(MSqlQuery::InitCon());
00504 query.prepare("SELECT title FROM recorded WHERE basename = :FILENAME");
00505 query.bindValue(":FILENAME", baseName);
00506 if (query.exec() && query.size())
00507 return true;
00508 else
00509 {
00510 LOG(VB_GENERAL, LOG_ERR,
00511 QString("MythArchive: Recording not found (%1)")
00512 .arg(filename));
00513 }
00514 }
00515 else if (type == "Video")
00516 {
00517 MSqlQuery query(MSqlQuery::InitCon());
00518 query.prepare("SELECT title FROM videometadata"
00519 " WHERE filename = :FILENAME");
00520 query.bindValue(":FILENAME", filename);
00521 if (query.exec() && query.size())
00522 return true;
00523 else
00524 {
00525 LOG(VB_GENERAL, LOG_ERR,
00526 QString("MythArchive: Video not found (%1)").arg(filename));
00527 }
00528 }
00529 else if (type == "File")
00530 {
00531 if (QFile::exists(filename))
00532 return true;
00533 else
00534 {
00535 LOG(VB_GENERAL, LOG_ERR,
00536 QString("MythArchive: File not found (%1)").arg(filename));
00537 }
00538 }
00539
00540 LOG(VB_GENERAL, LOG_NOTICE, "MythArchive: Archive item removed from list");
00541
00542 return false;
00543 }
00544
00545 EncoderProfile *MythBurn::getDefaultProfile(ArchiveItem *item)
00546 {
00547 if (!item)
00548 return m_profileList.at(0);
00549
00550 EncoderProfile *profile = NULL;
00551
00552
00553 if (item->videoCodec.toLower() == "mpeg2video")
00554 {
00555
00556 if (gCoreContext->GetSetting("MythArchiveVideoFormat", "pal").toLower()
00557 == "ntsc")
00558 {
00559 if ((item->videoWidth == 720 && item->videoHeight == 480) ||
00560 (item->videoWidth == 704 && item->videoHeight == 480) ||
00561 (item->videoWidth == 352 && item->videoHeight == 480) ||
00562 (item->videoWidth == 352 && item->videoHeight == 240))
00563 {
00564
00565 profile = m_profileList.at(0);
00566 }
00567 }
00568 else
00569 {
00570 if ((item->videoWidth == 720 && item->videoHeight == 576) ||
00571 (item->videoWidth == 704 && item->videoHeight == 576) ||
00572 (item->videoWidth == 352 && item->videoHeight == 576) ||
00573 (item->videoWidth == 352 && item->videoHeight == 288))
00574 {
00575
00576 profile = m_profileList.at(0);
00577 }
00578 }
00579 }
00580
00581 if (!profile)
00582 {
00583
00584 QString defaultProfile =
00585 gCoreContext->GetSetting("MythArchiveDefaultEncProfile", "SP");
00586
00587 for (int x = 0; x < m_profileList.size(); x++)
00588 if (m_profileList.at(x)->name == defaultProfile)
00589 profile = m_profileList.at(x);
00590 }
00591
00592 return profile;
00593 }
00594
00595 void MythBurn::createConfigFile(const QString &filename)
00596 {
00597 QDomDocument doc("mythburn");
00598
00599 QDomElement root = doc.createElement("mythburn");
00600 doc.appendChild(root);
00601
00602 QDomElement job = doc.createElement("job");
00603 job.setAttribute("theme", m_theme);
00604 root.appendChild(job);
00605
00606 QDomElement media = doc.createElement("media");
00607 job.appendChild(media);
00608
00609
00610 ArchiveItem *a;
00611 MythUIButtonListItem *item;
00612 for (int x = 0; x < m_archiveButtonList->GetCount(); x++)
00613 {
00614 item = m_archiveButtonList->GetItemAt(x);
00615 if (!item)
00616 continue;
00617
00618 a = qVariantValue<ArchiveItem *>(item->GetData());
00619 if (!a)
00620 continue;
00621
00622 QDomElement file = doc.createElement("file");
00623 file.setAttribute("type", a->type.toLower() );
00624 file.setAttribute("usecutlist", a->useCutlist);
00625 file.setAttribute("filename", a->filename);
00626 file.setAttribute("encodingprofile", a->encoderProfile->name);
00627 if (a->editedDetails)
00628 {
00629 QDomElement details = doc.createElement("details");
00630 file.appendChild(details);
00631 details.setAttribute("title", a->title);
00632 details.setAttribute("subtitle", a->subtitle);
00633 details.setAttribute("startdate", a->startDate);
00634 details.setAttribute("starttime", a->startTime);
00635 QDomText desc = doc.createTextNode(a->description);
00636 details.appendChild(desc);
00637 }
00638
00639 if (a->thumbList.size() > 0)
00640 {
00641 QDomElement thumbs = doc.createElement("thumbimages");
00642 file.appendChild(thumbs);
00643
00644 for (int x = 0; x < a->thumbList.size(); x++)
00645 {
00646 QDomElement thumb = doc.createElement("thumb");
00647 thumbs.appendChild(thumb);
00648 ThumbImage *thumbImage = a->thumbList.at(x);
00649 thumb.setAttribute("caption", thumbImage->caption);
00650 thumb.setAttribute("filename", thumbImage->filename);
00651 thumb.setAttribute("frame", (int) thumbImage->frame);
00652 }
00653 }
00654
00655 media.appendChild(file);
00656 }
00657
00658
00659 QDomElement options = doc.createElement("options");
00660 options.setAttribute("createiso", m_bCreateISO);
00661 options.setAttribute("doburn", m_bDoBurn);
00662 options.setAttribute("mediatype", m_archiveDestination.type);
00663 options.setAttribute("dvdrsize", (qint64)m_archiveDestination.freeSpace);
00664 options.setAttribute("erasedvdrw", m_bEraseDvdRw);
00665 options.setAttribute("savefilename", m_saveFilename);
00666 job.appendChild(options);
00667
00668
00669 QFile f(filename);
00670 if (!f.open(QIODevice::WriteOnly))
00671 {
00672 LOG(VB_GENERAL, LOG_ERR,
00673 QString("MythBurn::createConfigFile: "
00674 "Failed to open file for writing - %1") .arg(filename));
00675 return;
00676 }
00677
00678 QTextStream t(&f);
00679 t << doc.toString(4);
00680 f.close();
00681 }
00682
00683 void MythBurn::loadConfiguration(void)
00684 {
00685 m_theme = gCoreContext->GetSetting("MythBurnMenuTheme", "");
00686 m_bCreateISO = (gCoreContext->GetSetting("MythBurnCreateISO", "0") == "1");
00687 m_bDoBurn = (gCoreContext->GetSetting("MythBurnBurnDVDr", "1") == "1");
00688 m_bEraseDvdRw = (gCoreContext->GetSetting("MythBurnEraseDvdRw", "0") == "1");
00689 m_saveFilename = gCoreContext->GetSetting("MythBurnSaveFilename", "");
00690
00691 while (!m_archiveList.isEmpty())
00692 delete m_archiveList.takeFirst();
00693 m_archiveList.clear();
00694
00695
00696 MSqlQuery query(MSqlQuery::InitCon());
00697 query.prepare("SELECT type, title, subtitle, description, startdate, "
00698 "starttime, size, filename, hascutlist, duration, "
00699 "cutduration, videowidth, videoheight, filecodec, "
00700 "videocodec, encoderprofile FROM archiveitems "
00701 "ORDER BY intid;");
00702
00703 if (!query.exec())
00704 {
00705 MythDB::DBError("archive item insert", query);
00706 return;
00707 }
00708
00709 while (query.next())
00710 {
00711 ArchiveItem *a = new ArchiveItem;
00712 a->type = query.value(0).toString();
00713 a->title = query.value(1).toString();
00714 a->subtitle = query.value(2).toString();
00715 a->description = query.value(3).toString();
00716 a->startDate = query.value(4).toString();
00717 a->startTime = query.value(5).toString();
00718 a->size = query.value(6).toLongLong();
00719 a->filename = query.value(7).toString();
00720 a->hasCutlist = (query.value(8).toInt() == 1);
00721 a->useCutlist = false;
00722 a->duration = query.value(9).toInt();
00723 a->cutDuration = query.value(10).toInt();
00724 a->videoWidth = query.value(11).toInt();
00725 a->videoHeight = query.value(12).toInt();
00726 a->fileCodec = query.value(13).toString();
00727 a->videoCodec = query.value(14).toString();
00728 a->encoderProfile = getProfileFromName(query.value(15).toString());
00729 a->editedDetails = false;
00730 m_archiveList.append(a);
00731 }
00732 }
00733
00734 EncoderProfile *MythBurn::getProfileFromName(const QString &profileName)
00735 {
00736 for (int x = 0; x < m_profileList.size(); x++)
00737 if (m_profileList.at(x)->name == profileName)
00738 return m_profileList.at(x);
00739
00740 return NULL;
00741 }
00742
00743 void MythBurn::saveConfiguration(void)
00744 {
00745
00746 MSqlQuery query(MSqlQuery::InitCon());
00747 query.prepare("DELETE FROM archiveitems;");
00748 if (!query.exec())
00749 MythDB::DBError("MythBurn::saveConfiguration - deleting archiveitems",
00750 query);
00751
00752
00753 ArchiveItem *a;
00754 MythUIButtonListItem *item;
00755 for (int x = 0; x < m_archiveButtonList->GetCount(); x++)
00756 {
00757 item = m_archiveButtonList->GetItemAt(x);
00758 if (!item)
00759 continue;
00760
00761 a = qVariantValue<ArchiveItem *>(item->GetData());
00762 if (!a)
00763 continue;
00764
00765 query.prepare("INSERT INTO archiveitems (type, title, subtitle, "
00766 "description, startdate, starttime, size, filename, "
00767 "hascutlist, duration, cutduration, videowidth, "
00768 "videoheight, filecodec, videocodec, encoderprofile) "
00769 "VALUES(:TYPE, :TITLE, :SUBTITLE, :DESCRIPTION, :STARTDATE, "
00770 ":STARTTIME, :SIZE, :FILENAME, :HASCUTLIST, :DURATION, "
00771 ":CUTDURATION, :VIDEOWIDTH, :VIDEOHEIGHT, :FILECODEC, "
00772 ":VIDEOCODEC, :ENCODERPROFILE);");
00773 query.bindValue(":TYPE", a->type);
00774 query.bindValue(":TITLE", a->title);
00775 query.bindValue(":SUBTITLE", a->subtitle);
00776 query.bindValue(":DESCRIPTION", a->description);
00777 query.bindValue(":STARTDATE", a->startDate);
00778 query.bindValue(":STARTTIME", a->startTime);
00779 query.bindValue(":SIZE", (qint64)a->size);
00780 query.bindValue(":FILENAME", a->filename);
00781 query.bindValue(":HASCUTLIST", a->hasCutlist);
00782 query.bindValue(":DURATION", a->duration);
00783 query.bindValue(":CUTDURATION", a->cutDuration);
00784 query.bindValue(":VIDEOWIDTH", a->videoWidth);
00785 query.bindValue(":VIDEOHEIGHT", a->videoHeight);
00786 query.bindValue(":FILECODEC", a->fileCodec);
00787 query.bindValue(":VIDEOCODEC", a->videoCodec);
00788 query.bindValue(":ENCODERPROFILE", a->encoderProfile->name);
00789
00790 if (!query.exec())
00791 MythDB::DBError("archive item insert", query);
00792 }
00793 }
00794
00795 void MythBurn::showMenu()
00796 {
00797 if (m_archiveList.size() == 0)
00798 return;
00799
00800 MythUIButtonListItem *item = m_archiveButtonList->GetItemCurrent();
00801 ArchiveItem *curItem = qVariantValue<ArchiveItem *>(item->GetData());
00802
00803 if (!curItem)
00804 return;
00805
00806 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
00807
00808 MythDialogBox *menuPopup = new MythDialogBox(tr("Menu"),
00809 popupStack, "actionmenu");
00810
00811 if (menuPopup->Create())
00812 popupStack->AddScreen(menuPopup);
00813
00814 menuPopup->SetReturnEvent(this, "action");
00815
00816 if (curItem->hasCutlist)
00817 {
00818 if (curItem->useCutlist)
00819 menuPopup->AddButton(tr("Don't Use Cut List"),
00820 SLOT(toggleUseCutlist()));
00821 else
00822 menuPopup->AddButton(tr("Use Cut List"),
00823 SLOT(toggleUseCutlist()));
00824 }
00825
00826 menuPopup->AddButton(tr("Remove Item"), SLOT(removeItem()));
00827 menuPopup->AddButton(tr("Edit Details"), SLOT(editDetails()));
00828 menuPopup->AddButton(tr("Change Encoding Profile"), SLOT(changeProfile()));
00829 menuPopup->AddButton(tr("Edit Thumbnails"), SLOT(editThumbnails()));
00830 }
00831
00832 void MythBurn::removeItem()
00833 {
00834 MythUIButtonListItem *item = m_archiveButtonList->GetItemCurrent();
00835 ArchiveItem *curItem = qVariantValue<ArchiveItem *>(item->GetData());
00836
00837 if (!curItem)
00838 return;
00839
00840 m_archiveList.removeAll(curItem);
00841
00842 updateArchiveList();
00843 }
00844
00845 void MythBurn::editDetails()
00846 {
00847 MythUIButtonListItem *item = m_archiveButtonList->GetItemCurrent();
00848 ArchiveItem *curItem = qVariantValue<ArchiveItem *>(item->GetData());
00849
00850 if (!curItem)
00851 return;
00852
00853 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00854
00855 EditMetadataDialog *editor = new EditMetadataDialog(mainStack, curItem);
00856
00857 connect(editor, SIGNAL(haveResult(bool, ArchiveItem *)),
00858 this, SLOT(editorClosed(bool, ArchiveItem *)));
00859
00860 if (editor->Create())
00861 mainStack->AddScreen(editor);
00862 }
00863
00864 void MythBurn::editThumbnails()
00865 {
00866 MythUIButtonListItem *item = m_archiveButtonList->GetItemCurrent();
00867 ArchiveItem *curItem = qVariantValue<ArchiveItem *>(item->GetData());
00868
00869 if (!curItem)
00870 return;
00871
00872 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00873
00874 ThumbFinder *finder = new ThumbFinder(mainStack, curItem, m_theme);
00875
00876 if (finder->Create())
00877 mainStack->AddScreen(finder);
00878 }
00879
00880 void MythBurn::editorClosed(bool ok, ArchiveItem *item)
00881 {
00882 MythUIButtonListItem *gridItem = m_archiveButtonList->GetItemCurrent();
00883
00884 if (ok && item && gridItem)
00885 {
00886
00887 gridItem->SetText(item->title);
00888 gridItem->SetText(item->subtitle, "subtitle");
00889 gridItem->SetText(item->startDate + " " + item->startTime, "date");
00890 }
00891 }
00892
00893 void MythBurn::changeProfile()
00894 {
00895 MythUIButtonListItem *item = m_archiveButtonList->GetItemCurrent();
00896 ArchiveItem *curItem = qVariantValue<ArchiveItem *>(item->GetData());
00897
00898 if (!curItem)
00899 return;
00900
00901 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
00902
00903 ProfileDialog *profileDialog = new ProfileDialog(popupStack,
00904 curItem, m_profileList);
00905
00906 if (profileDialog->Create())
00907 {
00908 popupStack->AddScreen(profileDialog, false);
00909 connect(profileDialog, SIGNAL(haveResult(int)),
00910 this, SLOT(profileChanged(int)));
00911 }
00912 }
00913
00914 void MythBurn::profileChanged(int profileNo)
00915 {
00916 if (profileNo > m_profileList.size() - 1)
00917 return;
00918
00919 EncoderProfile *profile = m_profileList.at(profileNo);
00920
00921 MythUIButtonListItem *item = m_archiveButtonList->GetItemCurrent();
00922 if (!item)
00923 return;
00924
00925 ArchiveItem *archiveItem = qVariantValue<ArchiveItem *>(item->GetData());
00926 if (!archiveItem)
00927 return;
00928
00929 archiveItem->encoderProfile = profile;
00930
00931 item->SetText(profile->name, "profile");
00932 item->SetText(formatSize(archiveItem->newsize / 1024, 2), "size");
00933
00934 updateSizeBar();
00935 }
00936
00937 void MythBurn::runScript()
00938 {
00939 QString tempDir = getTempDirectory();
00940 QString logDir = tempDir + "logs";
00941 QString configDir = tempDir + "config";
00942 QString commandline;
00943
00944
00945 if (QFile::exists(logDir + "/progress.log"))
00946 QFile::remove(logDir + "/progress.log");
00947
00948
00949 if (QFile::exists(logDir + "/mythburncancel.lck"))
00950 QFile::remove(logDir + "/mythburncancel.lck");
00951
00952 createConfigFile(configDir + "/mydata.xml");
00953 commandline = "python " + GetShareDir() + "mytharchive/scripts/mythburn.py";
00954 commandline += " -j " + configDir + "/mydata.xml";
00955 commandline += " -l " + logDir + "/progress.log";
00956 commandline += " > " + logDir + "/mythburn.log 2>&1 &";
00957
00958 gCoreContext->SaveSetting("MythArchiveLastRunStatus", "Running");
00959
00960 uint flags = kMSRunBackground | kMSDontBlockInputDevs |
00961 kMSDontDisableDrawing;
00962 uint retval = myth_system(commandline, flags);
00963 if (retval != GENERIC_EXIT_RUNNING && retval != GENERIC_EXIT_OK)
00964 {
00965 ShowOkPopup(tr("It was not possible to create the DVD. "
00966 " An error occured when running the scripts"));
00967 }
00968 else
00969 {
00970
00971 showLogViewer();
00972 }
00973
00974 m_destinationScreen->Close();
00975 m_themeScreen->Close();
00976 Close();
00977 }
00978
00979 void MythBurn::handleAddRecording()
00980 {
00981 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00982
00983 RecordingSelector *selector = new RecordingSelector(mainStack,
00984 &m_archiveList);
00985
00986 connect(selector, SIGNAL(haveResult(bool)),
00987 this, SLOT(selectorClosed(bool)));
00988
00989 if (selector->Create())
00990 mainStack->AddScreen(selector);
00991 }
00992
00993 void MythBurn::selectorClosed(bool ok)
00994 {
00995 if (ok)
00996 updateArchiveList();
00997 }
00998
00999 void MythBurn::handleAddVideo()
01000 {
01001 MSqlQuery query(MSqlQuery::InitCon());
01002 query.prepare("SELECT title FROM videometadata");
01003 if (query.exec() && query.size())
01004 {
01005 }
01006 else
01007 {
01008 ShowOkPopup(QObject::tr("You don't have any videos!"));
01009 return;
01010 }
01011
01012 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
01013
01014 VideoSelector *selector = new VideoSelector(mainStack, &m_archiveList);
01015
01016 connect(selector, SIGNAL(haveResult(bool)),
01017 this, SLOT(selectorClosed(bool)));
01018
01019 if (selector->Create())
01020 mainStack->AddScreen(selector);
01021 }
01022
01023 void MythBurn::handleAddFile()
01024 {
01025 QString filter = gCoreContext->GetSetting("MythArchiveFileFilter",
01026 "*.mpg *.mpeg *.mov *.avi *.nuv");
01027
01028 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
01029
01030 FileSelector *selector = new FileSelector(mainStack, &m_archiveList,
01031 FSTYPE_FILELIST, "/", filter);
01032
01033 connect(selector, SIGNAL(haveResult(bool)),
01034 this, SLOT(selectorClosed(bool)));
01035
01036 if (selector->Create())
01037 mainStack->AddScreen(selector);
01038 }
01039
01040 void MythBurn::itemClicked(MythUIButtonListItem *item)
01041 {
01042 m_moveMode = !m_moveMode;
01043
01044 if (m_moveMode)
01045 item->DisplayState("on", "movestate");
01046 else
01047 item->DisplayState("off", "movestate");
01048 }
01049
01051
01052 ProfileDialog::ProfileDialog(
01053 MythScreenStack *parent, ArchiveItem *archiveItem,
01054 QList<EncoderProfile *> profileList) :
01055 MythScreenType(parent, "functionpopup"),
01056 m_archiveItem(archiveItem),
01057 m_profileList(profileList),
01058 m_captionText(NULL),
01059 m_descriptionText(NULL),
01060 m_oldSizeText(NULL),
01061 m_newSizeText(NULL),
01062 m_profile_list(NULL),
01063 m_enabledCheck(NULL),
01064 m_okButton(NULL)
01065 {
01066 }
01067
01068 bool ProfileDialog::Create()
01069 {
01070 if (!LoadWindowFromXML("mythburn-ui.xml", "profilepopup", this))
01071 return false;
01072
01073 bool err = false;
01074 UIUtilE::Assign(this, m_captionText, "caption_text", &err);
01075 UIUtilE::Assign(this, m_descriptionText, "description_text", &err);
01076 UIUtilE::Assign(this, m_oldSizeText, "oldsize_text", &err);
01077 UIUtilE::Assign(this, m_newSizeText, "newsize_text", &err);
01078 UIUtilE::Assign(this, m_profile_list, "profile_list", &err);
01079 UIUtilE::Assign(this, m_okButton, "ok_button", &err);
01080
01081 if (err)
01082 {
01083 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'profilepopup'");
01084 return false;
01085 }
01086
01087 for (int x = 0; x < m_profileList.size(); x++)
01088 {
01089 MythUIButtonListItem *item = new
01090 MythUIButtonListItem(m_profile_list, m_profileList.at(x)->name);
01091 item->SetData(qVariantFromValue(m_profileList.at(x)));
01092 }
01093
01094 connect(m_profile_list, SIGNAL(itemSelected(MythUIButtonListItem*)),
01095 this, SLOT(profileChanged(MythUIButtonListItem*)));
01096
01097
01098 m_profile_list->MoveToNamedPosition(m_archiveItem->encoderProfile->name);
01099
01100 m_captionText->SetText(m_archiveItem->title);
01101 m_oldSizeText->SetText(formatSize(m_archiveItem->size / 1024, 2));
01102
01103 connect(m_okButton, SIGNAL(Clicked()), this, SLOT(save()));
01104
01105 BuildFocusList();
01106
01107 SetFocusWidget(m_profile_list);
01108
01109 return true;
01110 }
01111
01112 void ProfileDialog::profileChanged(MythUIButtonListItem *item)
01113 {
01114 if (!item)
01115 return;
01116
01117 EncoderProfile *profile = qVariantValue<EncoderProfile *>(item->GetData());
01118 if (!profile)
01119 return;
01120
01121 m_descriptionText->SetText(profile->description);
01122
01123 m_archiveItem->encoderProfile = profile;
01124
01125
01126 recalcItemSize(m_archiveItem);
01127
01128 m_newSizeText->SetText(formatSize(m_archiveItem->newsize / 1024, 2));
01129 }
01130
01131
01132 void ProfileDialog::save(void)
01133 {
01134 emit haveResult(m_profile_list->GetCurrentPos());
01135
01136 Close();
01137 }
01138
01140
01141 BurnMenu::BurnMenu(void)
01142 :QObject(NULL)
01143 {
01144 setObjectName("BurnMenu");
01145 }
01146
01147 BurnMenu::~BurnMenu(void)
01148 {
01149 }
01150
01151 void BurnMenu::start(void)
01152 {
01153 if (!gCoreContext->GetSetting("MythArchiveLastRunStatus").startsWith("Success"))
01154 {
01155 showWarningDialog(QObject::tr("Cannot burn a DVD.\n"
01156 "The last run failed to create a DVD."));
01157 return;
01158 }
01159
01160
01161 QString title = QObject::tr("Burn DVD");
01162 QString msg = QObject::tr("\nPlace a blank DVD in the"
01163 " drive and select an option below.");
01164 MythScreenStack *mainStack = GetMythMainWindow()->GetStack("main stack");
01165 MythDialogBox *menuPopup = new MythDialogBox(title, msg, mainStack,
01166 "actionmenu", true);
01167
01168 if (menuPopup->Create())
01169 mainStack->AddScreen(menuPopup);
01170
01171 menuPopup->SetReturnEvent(this, "action");
01172
01173 menuPopup->AddButton(QObject::tr("Burn DVD"));
01174 menuPopup->AddButton(QObject::tr("Burn DVD Rewritable"));
01175 menuPopup->AddButton(QObject::tr("Burn DVD Rewritable (Force Erase)"));
01176 }
01177
01178 void BurnMenu::customEvent(QEvent *event)
01179 {
01180 if (event->type() == DialogCompletionEvent::kEventType)
01181 {
01182 DialogCompletionEvent *dce = (DialogCompletionEvent*)(event);
01183 if (dce->GetId() == "action")
01184 {
01185 doBurn(dce->GetResult());
01186 deleteLater();
01187 }
01188 }
01189 }
01190
01191 void BurnMenu::doBurn(int mode)
01192 {
01193 if ((mode < 0) || (mode > 2))
01194 return;
01195
01196 QString tempDir = getTempDirectory(true);
01197
01198 if (tempDir == "")
01199 return;
01200
01201 QString logDir = tempDir + "logs";
01202 QString configDir = tempDir + "config";
01203 QString commandline;
01204
01205
01206 if (QFile::exists(logDir + "/progress.log"))
01207 QFile::remove(logDir + "/progress.log");
01208
01209
01210 if (QFile::exists(logDir + "/mythburncancel.lck"))
01211 QFile::remove(logDir + "/mythburncancel.lck");
01212
01213 QString sArchiveFormat = QString::number(mode);
01214 bool bEraseDVDRW = (mode == 2);
01215 bool bNativeFormat = gCoreContext->GetSetting("MythArchiveLastRunType")
01216 .startsWith("Native");
01217
01218 commandline = "mytharchivehelper --burndvd --mediatype " + sArchiveFormat +
01219 (bEraseDVDRW ? " --erasedvdrw" : "") +
01220 (bNativeFormat ? " --nativeformat" : "");
01221 commandline += logPropagateArgs;
01222 if (!logPropagateQuiet())
01223 commandline += " --quiet";
01224 commandline += " > " + logDir + "/progress.log 2>&1 &";
01225
01226 uint flags = kMSRunBackground | kMSDontBlockInputDevs |
01227 kMSDontDisableDrawing;
01228 uint retval = myth_system(commandline, flags);
01229 if (retval != GENERIC_EXIT_RUNNING && retval != GENERIC_EXIT_OK)
01230 {
01231 showWarningDialog(QObject::tr("It was not possible to run "
01232 "mytharchivehelper to burn the DVD."));
01233 return;
01234 }
01235
01236
01237 showLogViewer();
01238 }
01239
01240