MythTV  master
mythburn.cpp
Go to the documentation of this file.
1 #include <unistd.h>
2 
3 #include <cstdlib>
4 #include <unistd.h>
5 #include <iostream>
6 #include <cstdlib>
7 #include <sys/wait.h> // for WIFEXITED and WEXITSTATUS
8 
9 // qt
10 #include <QDir>
11 #include <QDomDocument>
12 #include <QApplication>
13 #include <QKeyEvent>
14 #include <QTextStream>
15 
16 // myth
17 #include <mythcontext.h>
18 #include <mythdb.h>
19 #include <mythdirs.h>
20 #include <mythprogressdialog.h>
21 #include <mythuihelper.h>
22 #include <mythdialogbox.h>
23 #include <mythuitext.h>
24 #include <mythuibutton.h>
25 #include <mythuicheckbox.h>
26 #include <mythuibuttonlist.h>
27 #include <mythuiprogressbar.h>
28 #include <mythdate.h>
29 #include <mythsystemlegacy.h>
30 #include <mythmiscutil.h>
31 #include <exitcodes.h>
32 #include <mythconfig.h>
33 
34 // mytharchive
35 #include "archiveutil.h"
36 #include "mythburn.h"
37 #include "editmetadata.h"
38 #include "fileselector.h"
39 #include "thumbfinder.h"
40 #include "recordingselector.h"
41 #include "videoselector.h"
42 #include "logviewer.h"
43 
44 using namespace std;
45 
47  MythScreenType *destinationScreen,
48  MythScreenType *themeScreen,
49  ArchiveDestination archiveDestination, const QString& name) :
50  MythScreenType(parent, name),
51  m_destinationScreen(destinationScreen),
52  m_themeScreen(themeScreen),
53  m_archiveDestination(archiveDestination)
54 {
55  // remove any old thumb images
56  QString thumbDir = getTempDirectory() + "/config/thumbs";
57  QDir dir(thumbDir);
58  if (dir.exists() && !MythRemoveDirectory(dir))
59  LOG(VB_GENERAL, LOG_ERR, "MythBurn: Failed to clear thumb directory");
60 }
61 
63 {
65 
66  while (!m_profileList.isEmpty())
67  delete m_profileList.takeFirst();
68  m_profileList.clear();
69 
70  while (!m_archiveList.isEmpty())
71  delete m_archiveList.takeFirst();
72  m_archiveList.clear();
73 }
74 
75 bool MythBurn::Create(void)
76 {
77  // Load the theme for this screen
78  bool foundtheme = LoadWindowFromXML("mythburn-ui.xml", "mythburn", this);
79  if (!foundtheme)
80  return false;
81 
82  bool err = false;
83  UIUtilE::Assign(this, m_nextButton, "next_button", &err);
84  UIUtilE::Assign(this, m_prevButton, "prev_button", &err);
85  UIUtilE::Assign(this, m_cancelButton, "cancel_button", &err);
86  UIUtilE::Assign(this, m_nofilesText, "nofiles", &err);
87  UIUtilE::Assign(this, m_archiveButtonList, "archivelist", &err);
88  UIUtilE::Assign(this, m_addrecordingButton, "addrecording_button", &err);
89  UIUtilE::Assign(this, m_addvideoButton, "addvideo_button", &err);
90  UIUtilE::Assign(this, m_addfileButton, "addfile_button", &err);
91  UIUtilE::Assign(this, m_maxsizeText, "maxsize", &err);
92  UIUtilE::Assign(this, m_minsizeText, "minsize", &err);
93  UIUtilE::Assign(this, m_currentsizeErrorText, "currentsize_error", &err);
94  UIUtilE::Assign(this, m_currentsizeText, "currentsize", &err);
95  UIUtilE::Assign(this, m_sizeBar, "size_bar", &err);
96 
97  if (err)
98  {
99  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'mythburn'");
100  return false;
101  }
102 
103  connect(m_nextButton, SIGNAL(Clicked()), this, SLOT(handleNextPage()));
104  connect(m_prevButton, SIGNAL(Clicked()), this, SLOT(handlePrevPage()));
105  connect(m_cancelButton, SIGNAL(Clicked()), this, SLOT(handleCancel()));
106 
107 
110 
112 
113  connect(m_addrecordingButton, SIGNAL(Clicked()),
114  this, SLOT(handleAddRecording()));
115 
116  connect(m_addvideoButton, SIGNAL(Clicked()), this, SLOT(handleAddVideo()));
117  connect(m_addfileButton, SIGNAL(Clicked()), this, SLOT(handleAddFile()));
119  this, SLOT(itemClicked(MythUIButtonListItem *)));
120 
121  BuildFocusList();
122 
124 
125  return true;
126 }
127 
128 bool MythBurn::keyPressEvent(QKeyEvent *event)
129 {
130  if (!m_moveMode && GetFocusWidget()->keyPressEvent(event))
131  return true;
132 
133  QStringList actions;
134  bool handled = GetMythMainWindow()->TranslateKeyPress("Archive", event, actions);
135 
136  for (int i = 0; i < actions.size() && !handled; i++)
137  {
138  QString action = actions[i];
139  handled = true;
140 
141  // if we are currently moving an item,
142  // we only accept UP/DOWN/SELECT/ESCAPE
143  if (m_moveMode)
144  {
146  if (!item)
147  return false;
148 
149  if (action == "SELECT" || action == "ESCAPE")
150  {
151  m_moveMode = false;
152  item->DisplayState("off", "movestate");
153  }
154  else if (action == "UP")
155  {
156  item->MoveUpDown(true);
157  }
158  else if (action == "DOWN")
159  {
160  item->MoveUpDown(false);
161  }
162 
163  return true;
164  }
165 
166  if (action == "MENU")
167  {
168  ShowMenu();
169  }
170  else if (action == "DELETE")
171  {
172  removeItem();
173  }
174  else if (action == "INFO")
175  {
176  editThumbnails();
177  }
178  else if (action == "TOGGLECUT")
179  {
181  }
182  else
183  handled = false;
184  }
185 
186  if (!handled && MythScreenType::keyPressEvent(event))
187  handled = true;
188 
189  return handled;
190 }
191 
193 {
194  int64_t size = 0;
195  for (const auto *a : qAsConst(m_archiveList))
196  size += a->newsize;
197 
198  uint usedSpace = size / 1024 / 1024;
199 
200  QString tmpSize;
201 
203  m_sizeBar->SetUsed(usedSpace);
204 
205  tmpSize = QString("%1 Mb").arg(m_archiveDestination.freeSpace / 1024);
206 
207  m_maxsizeText->SetText(tmpSize);
208 
209  m_minsizeText->SetText("0 Mb");
210 
211  tmpSize = QString("%1 Mb").arg(usedSpace);
212 
213  if (usedSpace > m_archiveDestination.freeSpace / 1024)
214  {
216 
219  }
220  else
221  {
223 
224  m_currentsizeText->SetText(tmpSize);
226  }
227 }
228 
230 {
231  auto *item = new EncoderProfile;
232  item->name = "NONE";
233  item->description = "";
234  item->bitrate = 0.0F;
235  m_profileList.append(item);
236 
237  // find the encoding profiles
238  // first look in the ConfDir (~/.mythtv)
239  QString filename = GetConfDir() +
240  "/MythArchive/ffmpeg_dvd_" +
241  ((gCoreContext->GetSetting("MythArchiveVideoFormat", "pal")
242  .toLower() == "ntsc") ? "ntsc" : "pal") + ".xml";
243 
244  if (!QFile::exists(filename))
245  {
246  // not found yet so use the default profiles
247  filename = GetShareDir() +
248  "mytharchive/encoder_profiles/ffmpeg_dvd_" +
249  ((gCoreContext->GetSetting("MythArchiveVideoFormat", "pal")
250  .toLower() == "ntsc") ? "ntsc" : "pal") + ".xml";
251  }
252 
253  LOG(VB_GENERAL, LOG_NOTICE,
254  "MythArchive: Loading encoding profiles from " + filename);
255 
256  QDomDocument doc("mydocument");
257  QFile file(filename);
258  if (!file.open(QIODevice::ReadOnly))
259  return;
260 
261  if (!doc.setContent( &file ))
262  {
263  file.close();
264  return;
265  }
266  file.close();
267 
268  QDomElement docElem = doc.documentElement();
269  QDomNodeList profileNodeList = doc.elementsByTagName("profile");
270  QString name;
271  QString desc;
272  QString bitrate;
273 
274  for (int x = 0; x < profileNodeList.count(); x++)
275  {
276  QDomNode n = profileNodeList.item(x);
277  QDomElement e = n.toElement();
278  QDomNode n2 = e.firstChild();
279  while (!n2.isNull())
280  {
281  QDomElement e2 = n2.toElement();
282  if(!e2.isNull())
283  {
284  if (e2.tagName() == "name")
285  name = e2.text();
286  if (e2.tagName() == "description")
287  desc = e2.text();
288  if (e2.tagName() == "bitrate")
289  bitrate = e2.text();
290 
291  }
292  n2 = n2.nextSibling();
293 
294  }
295 
296  auto *item2 = new EncoderProfile;
297  item2->name = name;
298  item2->description = desc;
299  item2->bitrate = bitrate.toFloat();
300  m_profileList.append(item2);
301  }
302 }
303 
305 {
307  auto *a = item->GetData().value<ArchiveItem *>();
308 
309  if (!a)
310  return;
311 
312  if (!a->hasCutlist)
313  return;
314 
315  a->useCutlist = !a->useCutlist;
316 
317  if (a->hasCutlist)
318  {
319  if (a->useCutlist)
320  {
321  item->SetText(tr("Using Cut List"), "cutlist");
322  item->DisplayState("using", "cutliststatus");
323  }
324  else
325  {
326  item->SetText(tr("Not Using Cut List"), "cutlist");
327  item->DisplayState("notusing", "cutliststatus");
328  }
329  }
330  else
331  {
332  item->SetText(tr("No Cut List"), "cutlist");
333  item->DisplayState("none", "cutliststatus");
334  }
335  recalcItemSize(a);
336  updateSizeBar();
337 }
338 
340 {
341  if (m_archiveList.empty())
342  {
343  ShowOkPopup(tr("You need to add at least one item to archive!"));
344  return;
345  }
346 
347  runScript();
348 }
349 
351 {
352  Close();
353 }
354 
356 {
358  m_themeScreen->Close();
359  Close();
360 }
361 
362 QString MythBurn::loadFile(const QString &filename)
363 {
364  QString res = "";
365 
366  QFile file(filename);
367 
368  if (!file.exists())
369  return "";
370 
371  if (file.open( QIODevice::ReadOnly ))
372  {
373  QTextStream stream(&file);
374 
375  while ( !stream.atEnd() )
376  {
377  res = res + stream.readLine();
378  }
379  file.close();
380  }
381  else
382  return "";
383 
384  return res;
385 }
386 
388 {
389  QString message = tr("Retrieving File Information. Please Wait...");
390 
391  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
392 
393  auto *busyPopup = new
394  MythUIBusyDialog(message, popupStack, "mythburnbusydialog");
395 
396  if (busyPopup->Create())
397  popupStack->AddScreen(busyPopup, false);
398  else
399  {
400  delete busyPopup;
401  busyPopup = nullptr;
402  }
403 
404  QCoreApplication::processEvents();
405 
407 
408  if (m_archiveList.empty())
409  {
410  m_nofilesText->Show();
411  }
412  else
413  {
414  for (auto *a : qAsConst(m_archiveList))
415  {
416  QCoreApplication::processEvents();
417  // get duration of this file
418  if (a->duration == 0)
419  {
420  if (!getFileDetails(a))
421  LOG(VB_GENERAL, LOG_ERR,
422  QString("MythBurn: failed to get file details for: %1").arg(a->filename));
423  }
424 
425  // get default encoding profile if needed
426 
427  if (a->encoderProfile == nullptr)
428  a->encoderProfile = getDefaultProfile(a);
429 
430  recalcItemSize(a);
431 
432  auto* item = new MythUIButtonListItem(m_archiveButtonList, a->title);
433  item->SetData(QVariant::fromValue(a));
434  item->SetText(a->subtitle, "subtitle");
435  item->SetText(a->startDate + " " + a->startTime, "date");
436  item->SetText(formatSize(a->newsize / 1024, 2), "size");
437  if (a->hasCutlist)
438  {
439  if (a->useCutlist)
440  {
441  item->SetText(tr("Using Cut List"), "cutlist");
442  item->DisplayState("using", "cutliststatus");
443  }
444  else
445  {
446  item->SetText(tr("Not Using Cut List"), "cutlist");
447  item->DisplayState("notusing", "cutliststatus");
448  }
449  }
450  else
451  {
452  item->SetText(tr("No Cut List"), "cutlist");
453  item->DisplayState("none", "cutliststatus");
454  }
455  item->SetText(tr("Encoder: ") + a->encoderProfile->name, "profile");
456  }
457 
458  m_nofilesText->Hide();
459 
462  }
463 
464  updateSizeBar();
465 
466  if (busyPopup)
467  busyPopup->Close();
468 }
469 
470 bool MythBurn::isArchiveItemValid(const QString &type, const QString &filename)
471 {
472  if (type == "Recording")
473  {
474  QString baseName = getBaseName(filename);
475 
477  query.prepare("SELECT title FROM recorded WHERE basename = :FILENAME");
478  query.bindValue(":FILENAME", baseName);
479  if (query.exec() && query.size())
480  return true;
481  LOG(VB_GENERAL, LOG_ERR,
482  QString("MythArchive: Recording not found (%1)")
483  .arg(filename));
484  }
485  else if (type == "Video")
486  {
488  query.prepare("SELECT title FROM videometadata"
489  " WHERE filename = :FILENAME");
490  query.bindValue(":FILENAME", filename);
491  if (query.exec() && query.size())
492  return true;
493  LOG(VB_GENERAL, LOG_ERR,
494  QString("MythArchive: Video not found (%1)").arg(filename));
495  }
496  else if (type == "File")
497  {
498  if (QFile::exists(filename))
499  return true;
500  LOG(VB_GENERAL, LOG_ERR,
501  QString("MythArchive: File not found (%1)").arg(filename));
502  }
503 
504  LOG(VB_GENERAL, LOG_NOTICE, "MythArchive: Archive item removed from list");
505 
506  return false;
507 }
508 
510 {
511  if (!item)
512  return m_profileList.at(0);
513 
514  EncoderProfile *profile = nullptr;
515 
516  // is the file an mpeg2 file?
517  if (item->videoCodec.toLower() == "mpeg2video (main)")
518  {
519  // does the file already have a valid DVD resolution?
520  if (gCoreContext->GetSetting("MythArchiveVideoFormat", "pal").toLower()
521  == "ntsc")
522  {
523  if ((item->videoWidth == 720 && item->videoHeight == 480) ||
524  (item->videoWidth == 704 && item->videoHeight == 480) ||
525  (item->videoWidth == 352 && item->videoHeight == 480) ||
526  (item->videoWidth == 352 && item->videoHeight == 240))
527  {
528  // don't need to re-encode
529  profile = m_profileList.at(0);
530  }
531  }
532  else
533  {
534  if ((item->videoWidth == 720 && item->videoHeight == 576) ||
535  (item->videoWidth == 704 && item->videoHeight == 576) ||
536  (item->videoWidth == 352 && item->videoHeight == 576) ||
537  (item->videoWidth == 352 && item->videoHeight == 288))
538  {
539  // don't need to re-encode
540  profile = m_profileList.at(0);
541  }
542  }
543  }
544 
545  if (!profile)
546  {
547  // file needs re-encoding - use default profile setting
548  QString defaultProfile =
549  gCoreContext->GetSetting("MythArchiveDefaultEncProfile", "SP");
550 
551  for (auto *x : qAsConst(m_profileList))
552  if (x->name == defaultProfile)
553  profile = x;
554  }
555 
556  return profile;
557 }
558 
560 {
561  QDomDocument doc("mythburn");
562 
563  QDomElement root = doc.createElement("mythburn");
564  doc.appendChild(root);
565 
566  QDomElement job = doc.createElement("job");
567  job.setAttribute("theme", m_theme);
568  root.appendChild(job);
569 
570  QDomElement media = doc.createElement("media");
571  job.appendChild(media);
572 
573  // now loop though selected archive items and add them to the xml file
574  for (int x = 0; x < m_archiveButtonList->GetCount(); x++)
575  {
577  if (!item)
578  continue;
579 
580  auto *a = item->GetData().value<ArchiveItem *>();
581  if (!a)
582  continue;
583 
584  QDomElement file = doc.createElement("file");
585  file.setAttribute("type", a->type.toLower() );
586  file.setAttribute("usecutlist", static_cast<int>(a->useCutlist));
587  file.setAttribute("filename", a->filename);
588  file.setAttribute("encodingprofile", a->encoderProfile->name);
589  if (a->editedDetails)
590  {
591  QDomElement details = doc.createElement("details");
592  file.appendChild(details);
593  details.setAttribute("title", a->title);
594  details.setAttribute("subtitle", a->subtitle);
595  details.setAttribute("startdate", a->startDate);
596  details.setAttribute("starttime", a->startTime);
597  QDomText desc = doc.createTextNode(a->description);
598  details.appendChild(desc);
599  }
600 
601  if (!a->thumbList.empty())
602  {
603  QDomElement thumbs = doc.createElement("thumbimages");
604  file.appendChild(thumbs);
605 
606  for (auto *thumbImage : a->thumbList)
607  {
608  QDomElement thumb = doc.createElement("thumb");
609  thumbs.appendChild(thumb);
610  thumb.setAttribute("caption", thumbImage->caption);
611  thumb.setAttribute("filename", thumbImage->filename);
612  thumb.setAttribute("frame", (int) thumbImage->frame);
613  }
614  }
615 
616  media.appendChild(file);
617  }
618 
619  // add the options to the xml file
620  QDomElement options = doc.createElement("options");
621  options.setAttribute("createiso", static_cast<int>(m_bCreateISO));
622  options.setAttribute("doburn", static_cast<int>(m_bDoBurn));
623  options.setAttribute("mediatype", m_archiveDestination.type);
624  options.setAttribute("dvdrsize", (qint64)m_archiveDestination.freeSpace);
625  options.setAttribute("erasedvdrw", static_cast<int>(m_bEraseDvdRw));
626  options.setAttribute("savefilename", m_saveFilename);
627  job.appendChild(options);
628 
629  // finally save the xml to the file
630  QFile f(filename);
631  if (!f.open(QIODevice::WriteOnly))
632  {
633  LOG(VB_GENERAL, LOG_ERR,
634  QString("MythBurn::createConfigFile: "
635  "Failed to open file for writing - %1") .arg(filename));
636  return;
637  }
638 
639  QTextStream t(&f);
640  t << doc.toString(4);
641  f.close();
642 }
643 
645 {
646  m_theme = gCoreContext->GetSetting("MythBurnMenuTheme", "");
647  m_bCreateISO = (gCoreContext->GetSetting("MythBurnCreateISO", "0") == "1");
648  m_bDoBurn = (gCoreContext->GetSetting("MythBurnBurnDVDr", "1") == "1");
649  m_bEraseDvdRw = (gCoreContext->GetSetting("MythBurnEraseDvdRw", "0") == "1");
650  m_saveFilename = gCoreContext->GetSetting("MythBurnSaveFilename", "");
651 
652  while (!m_archiveList.isEmpty())
653  delete m_archiveList.takeFirst();
654  m_archiveList.clear();
655 
656  // load selected file list
658  query.prepare("SELECT type, title, subtitle, description, startdate, "
659  "starttime, size, filename, hascutlist, duration, "
660  "cutduration, videowidth, videoheight, filecodec, "
661  "videocodec, encoderprofile FROM archiveitems "
662  "ORDER BY intid;");
663 
664  if (!query.exec())
665  {
666  MythDB::DBError("archive item insert", query);
667  return;
668  }
669 
670  while (query.next())
671  {
672  auto *a = new ArchiveItem;
673  a->type = query.value(0).toString();
674  a->title = query.value(1).toString();
675  a->subtitle = query.value(2).toString();
676  a->description = query.value(3).toString();
677  a->startDate = query.value(4).toString();
678  a->startTime = query.value(5).toString();
679  a->size = query.value(6).toLongLong();
680  a->filename = query.value(7).toString();
681  a->hasCutlist = (query.value(8).toInt() == 1);
682  a->useCutlist = false;
683  a->duration = query.value(9).toInt();
684  a->cutDuration = query.value(10).toInt();
685  a->videoWidth = query.value(11).toInt();
686  a->videoHeight = query.value(12).toInt();
687  a->fileCodec = query.value(13).toString();
688  a->videoCodec = query.value(14).toString();
689  a->encoderProfile = getProfileFromName(query.value(15).toString());
690  a->editedDetails = false;
691  m_archiveList.append(a);
692  }
693 }
694 
695 EncoderProfile *MythBurn::getProfileFromName(const QString &profileName)
696 {
697  for (auto *x : qAsConst(m_profileList))
698  if (x->name == profileName)
699  return x;
700 
701  return nullptr;
702 }
703 
705 {
706  // remove all old archive items from DB
708  query.prepare("DELETE FROM archiveitems;");
709  if (!query.exec())
710  MythDB::DBError("MythBurn::saveConfiguration - deleting archiveitems",
711  query);
712 
713  // save new list of archive items to DB
714  for (int x = 0; x < m_archiveButtonList->GetCount(); x++)
715  {
717  if (!item)
718  continue;
719 
720  auto *a = item->GetData().value<ArchiveItem *>();
721  if (!a)
722  continue;
723 
724  query.prepare("INSERT INTO archiveitems (type, title, subtitle, "
725  "description, startdate, starttime, size, filename, "
726  "hascutlist, duration, cutduration, videowidth, "
727  "videoheight, filecodec, videocodec, encoderprofile) "
728  "VALUES(:TYPE, :TITLE, :SUBTITLE, :DESCRIPTION, :STARTDATE, "
729  ":STARTTIME, :SIZE, :FILENAME, :HASCUTLIST, :DURATION, "
730  ":CUTDURATION, :VIDEOWIDTH, :VIDEOHEIGHT, :FILECODEC, "
731  ":VIDEOCODEC, :ENCODERPROFILE);");
732  query.bindValue(":TYPE", a->type);
733  query.bindValue(":TITLE", a->title);
734  query.bindValue(":SUBTITLE", a->subtitle);
735  query.bindValue(":DESCRIPTION", a->description);
736  query.bindValue(":STARTDATE", a->startDate);
737  query.bindValue(":STARTTIME", a->startTime);
738  query.bindValue(":SIZE", (qint64)a->size);
739  query.bindValue(":FILENAME", a->filename);
740  query.bindValue(":HASCUTLIST", a->hasCutlist);
741  query.bindValue(":DURATION", a->duration);
742  query.bindValue(":CUTDURATION", a->cutDuration);
743  query.bindValue(":VIDEOWIDTH", a->videoWidth);
744  query.bindValue(":VIDEOHEIGHT", a->videoHeight);
745  query.bindValue(":FILECODEC", a->fileCodec);
746  query.bindValue(":VIDEOCODEC", a->videoCodec);
747  query.bindValue(":ENCODERPROFILE", a->encoderProfile->name);
748 
749  if (!query.exec())
750  MythDB::DBError("archive item insert", query);
751  }
752 }
753 
755 {
756  if (m_archiveList.empty())
757  return;
758 
760  auto *curItem = item->GetData().value<ArchiveItem *>();
761 
762  if (!curItem)
763  return;
764 
765  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
766 
767  auto *menuPopup = new MythDialogBox(tr("Menu"), popupStack, "actionmenu");
768 
769  if (menuPopup->Create())
770  popupStack->AddScreen(menuPopup);
771 
772  menuPopup->SetReturnEvent(this, "action");
773 
774  if (curItem->hasCutlist)
775  {
776  if (curItem->useCutlist)
777  {
778  menuPopup->AddButton(tr("Don't Use Cut List"),
779  SLOT(toggleUseCutlist()));
780  }
781  else
782  {
783  menuPopup->AddButton(tr("Use Cut List"),
784  SLOT(toggleUseCutlist()));
785  }
786  }
787 
788  menuPopup->AddButton(tr("Remove Item"), SLOT(removeItem()));
789  menuPopup->AddButton(tr("Edit Details"), SLOT(editDetails()));
790  menuPopup->AddButton(tr("Change Encoding Profile"), SLOT(changeProfile()));
791  menuPopup->AddButton(tr("Edit Thumbnails"), SLOT(editThumbnails()));
792 }
793 
795 {
797  auto *curItem = item->GetData().value<ArchiveItem *>();
798 
799  if (!curItem)
800  return;
801 
802  m_archiveList.removeAll(curItem);
803 
805 }
806 
808 {
810  auto *curItem = item->GetData().value<ArchiveItem *>();
811 
812  if (!curItem)
813  return;
814 
816 
817  auto *editor = new EditMetadataDialog(mainStack, curItem);
818 
819  connect(editor, SIGNAL(haveResult(bool, ArchiveItem *)),
820  this, SLOT(editorClosed(bool, ArchiveItem *)));
821 
822  if (editor->Create())
823  mainStack->AddScreen(editor);
824 }
825 
827 {
829  auto *curItem = item->GetData().value<ArchiveItem *>();
830 
831  if (!curItem)
832  return;
833 
835 
836  auto *finder = new ThumbFinder(mainStack, curItem, m_theme);
837 
838  if (finder->Create())
839  mainStack->AddScreen(finder);
840 }
841 
843 {
845 
846  if (ok && item && gridItem)
847  {
848  // update the grid to reflect any changes
849  gridItem->SetText(item->title);
850  gridItem->SetText(item->subtitle, "subtitle");
851  gridItem->SetText(item->startDate + " " + item->startTime, "date");
852  }
853 }
854 
856 {
858  auto *curItem = item->GetData().value<ArchiveItem *>();
859 
860  if (!curItem)
861  return;
862 
863  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
864 
865  auto *profileDialog = new ProfileDialog(popupStack, curItem, m_profileList);
866 
867  if (profileDialog->Create())
868  {
869  popupStack->AddScreen(profileDialog, false);
870  connect(profileDialog, SIGNAL(haveResult(int)),
871  this, SLOT(profileChanged(int)));
872  }
873 }
874 
875 void MythBurn::profileChanged(int profileNo)
876 {
877  if (profileNo > m_profileList.size() - 1)
878  return;
879 
880  EncoderProfile *profile = m_profileList.at(profileNo);
881 
883  if (!item)
884  return;
885 
886  auto *archiveItem = item->GetData().value<ArchiveItem *>();
887  if (!archiveItem)
888  return;
889 
890  archiveItem->encoderProfile = profile;
891 
892  item->SetText(profile->name, "profile");
893  item->SetText(formatSize(archiveItem->newsize / 1024, 2), "size");
894 
895  updateSizeBar();
896 }
897 
899 {
900  QString tempDir = getTempDirectory();
901  QString logDir = tempDir + "logs";
902  QString configDir = tempDir + "config";
903  QString commandline;
904 
905  // remove any existing logs
906  myth_system("rm -f " + logDir + "/*.log");
907 
908  // remove cancel flag file if present
909  if (QFile::exists(logDir + "/mythburncancel.lck"))
910  QFile::remove(logDir + "/mythburncancel.lck");
911 
912  createConfigFile(configDir + "/mydata.xml");
913  commandline = PYTHON_EXE;
914  commandline += " " + GetShareDir() + "mytharchive/scripts/mythburn.py";
915  commandline += " -j " + configDir + "/mydata.xml"; // job file
916  commandline += " -l " + logDir + "/progress.log"; // progress log
917  commandline += " > " + logDir + "/mythburn.log 2>&1 &"; // Logs
918 
919  gCoreContext->SaveSetting("MythArchiveLastRunStatus", "Running");
920 
923  uint retval = myth_system(commandline, flags);
924  if (retval != GENERIC_EXIT_RUNNING && retval != GENERIC_EXIT_OK)
925  {
926  ShowOkPopup(tr("It was not possible to create the DVD. "
927  " An error occured when running the scripts"));
928  }
929  else
930  {
931  // now show the log viewer
932  showLogViewer();
933  }
934 
936  m_themeScreen->Close();
937  Close();
938 }
939 
941 {
943 
944  auto *selector = new RecordingSelector(mainStack, &m_archiveList);
945 
946  connect(selector, SIGNAL(haveResult(bool)),
947  this, SLOT(selectorClosed(bool)));
948 
949  if (selector->Create())
950  mainStack->AddScreen(selector);
951 }
952 
954 {
955  if (ok)
957 }
958 
960 {
962  query.prepare("SELECT title FROM videometadata");
963  if (query.exec() && query.size())
964  {
965  }
966  else
967  {
968  ShowOkPopup(tr("You don't have any videos!"));
969  return;
970  }
971 
973 
974  auto *selector = new VideoSelector(mainStack, &m_archiveList);
975 
976  connect(selector, SIGNAL(haveResult(bool)),
977  this, SLOT(selectorClosed(bool)));
978 
979  if (selector->Create())
980  mainStack->AddScreen(selector);
981 }
982 
984 {
985  QString filter = gCoreContext->GetSetting("MythArchiveFileFilter",
986  "*.mpg *.mpeg *.mov *.avi *.nuv");
987 
989 
990  auto *selector = new FileSelector(mainStack, &m_archiveList,
991  FSTYPE_FILELIST, "/", filter);
992 
993  connect(selector, SIGNAL(haveResult(bool)),
994  this, SLOT(selectorClosed(bool)));
995 
996  if (selector->Create())
997  mainStack->AddScreen(selector);
998 }
999 
1001 {
1003 
1004  if (m_moveMode)
1005  item->DisplayState("on", "movestate");
1006  else
1007  item->DisplayState("off", "movestate");
1008 }
1009 
1011 
1013 {
1014  if (!LoadWindowFromXML("mythburn-ui.xml", "profilepopup", this))
1015  return false;
1016 
1017  bool err = false;
1018  UIUtilE::Assign(this, m_captionText, "caption_text", &err);
1019  UIUtilE::Assign(this, m_descriptionText, "description_text", &err);
1020  UIUtilE::Assign(this, m_oldSizeText, "oldsize_text", &err);
1021  UIUtilE::Assign(this, m_newSizeText, "newsize_text", &err);
1022  UIUtilE::Assign(this, m_profileBtnList, "profile_list", &err);
1023  UIUtilE::Assign(this, m_okButton, "ok_button", &err);
1024 
1025  if (err)
1026  {
1027  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'profilepopup'");
1028  return false;
1029  }
1030 
1031  for (auto *x : m_profileList)
1032  {
1033  auto *item = new
1035  item->SetData(QVariant::fromValue(x));
1036  }
1037 
1038  connect(m_profileBtnList, SIGNAL(itemSelected(MythUIButtonListItem*)),
1039  this, SLOT(profileChanged(MythUIButtonListItem*)));
1040 
1041 
1043 
1046 
1047  connect(m_okButton, SIGNAL(Clicked()), this, SLOT(save()));
1048 
1049  BuildFocusList();
1050 
1052 
1053  return true;
1054 }
1055 
1057 {
1058  if (!item)
1059  return;
1060 
1061  auto *profile = item->GetData().value<EncoderProfile *>();
1062  if (!profile)
1063  return;
1064 
1065  m_descriptionText->SetText(profile->description);
1066 
1068 
1069  // calc new size
1071 
1073 }
1074 
1075 
1077 {
1079 
1080  Close();
1081 }
1082 
1084 
1086  :QObject(nullptr)
1087 {
1088  setObjectName("BurnMenu");
1089 }
1090 
1092 {
1093  if (!gCoreContext->GetSetting("MythArchiveLastRunStatus").startsWith("Success"))
1094  {
1095  showWarningDialog(tr("Cannot burn a DVD.\n"
1096  "The last run failed to create a DVD."));
1097  return;
1098  }
1099 
1100  // ask the user what type of disk to burn to
1101  QString title = tr("Burn DVD");
1102  QString msg = tr("\nPlace a blank DVD in the"
1103  " drive and select an option below.");
1104  MythScreenStack *mainStack = GetMythMainWindow()->GetStack("main stack");
1105  auto *menuPopup = new MythDialogBox(title, msg, mainStack,
1106  "actionmenu", true);
1107 
1108  if (menuPopup->Create())
1109  mainStack->AddScreen(menuPopup);
1110 
1111  menuPopup->SetReturnEvent(this, "action");
1112 
1113  menuPopup->AddButton(tr("Burn DVD"));
1114  menuPopup->AddButton(tr("Burn DVD Rewritable"));
1115  menuPopup->AddButton(tr("Burn DVD Rewritable (Force Erase)"));
1116 }
1117 
1118 void BurnMenu::customEvent(QEvent *event)
1119 {
1120  if (auto *dce = dynamic_cast<DialogCompletionEvent*>(event))
1121  {
1122  if (dce->GetId() == "action")
1123  {
1124  doBurn(dce->GetResult());
1125  deleteLater();
1126  }
1127  }
1128 }
1129 
1130 void BurnMenu::doBurn(int mode)
1131 {
1132  if ((mode < 0) || (mode > 2))
1133  return;
1134 
1135  QString tempDir = getTempDirectory(true);
1136 
1137  if (tempDir == "")
1138  return;
1139 
1140  QString logDir = tempDir + "logs";
1141  QString configDir = tempDir + "config";
1142  QString commandline;
1143 
1144  // remove existing progress.log if present
1145  if (QFile::exists(logDir + "/progress.log"))
1146  QFile::remove(logDir + "/progress.log");
1147 
1148  // remove cancel flag file if present
1149  if (QFile::exists(logDir + "/mythburncancel.lck"))
1150  QFile::remove(logDir + "/mythburncancel.lck");
1151 
1152  QString sArchiveFormat = QString::number(mode);
1153  bool bEraseDVDRW = (mode == 2);
1154  bool bNativeFormat = gCoreContext->GetSetting("MythArchiveLastRunType")
1155  .startsWith("Native");
1156 
1157  commandline = "mytharchivehelper --burndvd --mediatype " + sArchiveFormat +
1158  (bEraseDVDRW ? " --erasedvdrw" : "") +
1159  (bNativeFormat ? " --nativeformat" : "");
1160  commandline += logPropagateArgs;
1161  if (!logPropagateQuiet())
1162  commandline += " --quiet";
1163  commandline += " > " + logDir + "/progress.log 2>&1 &";
1164 
1167  uint retval = myth_system(commandline, flags);
1168  if (retval != GENERIC_EXIT_RUNNING && retval != GENERIC_EXIT_OK)
1169  {
1170  showWarningDialog(tr("It was not possible to run "
1171  "mytharchivehelper to burn the DVD."));
1172  return;
1173  }
1174 
1175  // now show the log viewer
1176  showLogViewer();
1177 }
1178 
1179 /* vim: set expandtab tabstop=4 shiftwidth=4: */
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
MythUIText * m_oldSizeText
Definition: mythburn.h:44
QList< EncoderProfile * > m_profileList
Definition: mythburn.h:40
void SetUsed(int value)
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:864
BurnMenu(void)
Definition: mythburn.cpp:1085
void Show(void)
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
bool TranslateKeyPress(const QString &context, QKeyEvent *e, QStringList &actions, bool allowJumps=true)
Get a list of actions for a keypress in the given context.
MythScreenType * m_themeScreen
Definition: mythburn.h:102
QString m_saveFilename
Definition: mythburn.h:111
MythScreenType * m_destinationScreen
Definition: mythburn.h:101
MythConfirmationDialog * ShowOkPopup(const QString &message, QObject *parent, const char *slot, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
QString getTempDirectory(bool showError)
Definition: archiveutil.cpp:71
MythUIText * m_maxsizeText
Definition: mythburn.h:128
QString getBaseName(const QString &filename)
void haveResult(int profile)
void SaveSetting(const QString &key, int newValue)
QString m_theme
Definition: mythburn.h:112
MythUIButton * m_cancelButton
Definition: mythburn.h:118
void updateSizeBar()
Definition: mythburn.cpp:192
MythUIButton * m_okButton
Definition: mythburn.h:49
void createConfigFile(const QString &filename)
Definition: mythburn.cpp:559
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
static QString loadFile(const QString &filename)
Definition: mythburn.cpp:362
bool useCutlist
Definition: archiveutil.h:71
Basic menu dialog, message and a list of options.
void itemClicked(MythUIButtonListItem *item)
Definition: mythburn.cpp:1000
MythUIText * m_minsizeText
Definition: mythburn.h:129
QString logPropagateArgs
Definition: logging.cpp:89
int size(void) const
Definition: mythdbcon.h:203
void profileChanged(MythUIButtonListItem *item)
Definition: mythburn.cpp:1056
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:135
MythUIButton * m_nextButton
Definition: mythburn.h:116
void save(void)
Definition: mythburn.cpp:1076
void editorClosed(bool ok, ArchiveItem *item)
Definition: mythburn.cpp:842
void handleCancel(void)
Definition: mythburn.cpp:355
QDomDocument doc("MYTHARCHIVEITEM")
MythScreenStack * GetStack(const QString &stackname)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
EncoderProfile * getDefaultProfile(ArchiveItem *item)
Definition: mythburn.cpp:509
MythScreenStack * GetMainStack()
MythUIButtonListItem * GetItemAt(int pos) const
int videoWidth
Definition: archiveutil.h:68
bool getFileDetails(ArchiveItem *a)
MythUIText * m_descriptionText
Definition: mythburn.h:43
MythUIText * m_currentsizeText
Definition: mythburn.h:131
void toggleUseCutlist(void)
Definition: mythburn.cpp:304
void BuildFocusList(void)
bool m_bDoBurn
Definition: mythburn.h:109
void runScript()
Definition: mythburn.cpp:898
int64_t newsize
Definition: archiveutil.h:62
bool MoveToNamedPosition(const QString &position_name)
ARCHIVEDESTINATION type
Definition: archiveutil.h:28
QString startTime
Definition: archiveutil.h:59
ArchiveDestination m_archiveDestination
Definition: mythburn.h:103
QString GetConfDir(void)
Definition: mythdirs.cpp:224
void Hide(void)
QVariant value(int i) const
Definition: mythdbcon.h:198
virtual void Close()
MythUIButton * m_prevButton
Definition: mythburn.h:117
void saveConfiguration(void)
Definition: mythburn.cpp:704
bool Create() override
Definition: mythburn.cpp:1012
void customEvent(QEvent *event) override
Definition: mythburn.cpp:1118
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
bool Create(void) override
Definition: mythburn.cpp:75
ArchiveItem * m_archiveItem
Definition: mythburn.h:39
MSqlQuery query(MSqlQuery::InitCon())
void ShowMenu(void) override
Definition: mythburn.cpp:754
void handleAddFile(void)
Definition: mythburn.cpp:983
bool m_bEraseDvdRw
Definition: mythburn.h:110
QString GetSetting(const QString &key, const QString &defaultval="")
MythUIButton * m_addvideoButton
Definition: mythburn.h:123
void showWarningDialog(const QString &msg)
bool logPropagateQuiet(void)
Check if we are propagating a "--quiet".
Definition: logging.cpp:695
MythBurn(MythScreenStack *parent, MythScreenType *destinationScreen, MythScreenType *themeScreen, ArchiveDestination archiveDestination, const QString &name)
Definition: mythburn.cpp:46
QList< EncoderProfile * > m_profileList
Definition: mythburn.h:106
QString GetShareDir(void)
Definition: mythdirs.cpp:222
static void doBurn(int mode)
Definition: mythburn.cpp:1130
void SetTotal(int value)
#define GENERIC_EXIT_RUNNING
Process is running.
Definition: exitcodes.h:25
void editDetails(void)
Definition: mythburn.cpp:807
~MythBurn(void) override
Definition: mythburn.cpp:62
MythUIButtonList * m_profileBtnList
Definition: mythburn.h:47
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
void SetText(const QString &text, const QString &name="", const QString &state="")
unsigned int uint
Definition: compat.h:140
void recalcItemSize(ArchiveItem *item)
MythUIButton * m_addfileButton
Definition: mythburn.h:124
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
uint myth_system(const QString &command, uint flags, uint timeout)
bool MoveUpDown(bool flag)
QList< ArchiveItem * > m_archiveList
Definition: mythburn.h:105
MythUIText * m_newSizeText
Definition: mythburn.h:45
void loadConfiguration(void)
Definition: mythburn.cpp:644
MythUIText * m_captionText
Definition: mythburn.h:42
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
MythUIType * GetFocusWidget(void) const
QString type
Definition: archiveutil.h:54
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
MythMainWindow * GetMythMainWindow(void)
void selectorClosed(bool ok)
Definition: mythburn.cpp:953
void changeProfile(void)
Definition: mythburn.cpp:855
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
QString subtitle
Definition: archiveutil.h:56
static QString formatSize(int64_t sizeKB, int prec)
QString videoCodec
Definition: archiveutil.h:67
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808
MythUIButtonList * m_archiveButtonList
Definition: mythburn.h:120
bool m_moveMode
Definition: mythburn.h:114
QString startDate
Definition: archiveutil.h:58
MythUIText * m_nofilesText
Definition: mythburn.h:121
QString title
Definition: archiveutil.h:55
void handleAddVideo(void)
Definition: mythburn.cpp:959
void loadEncoderProfiles(void)
Definition: mythburn.cpp:229
avoid disabling UI drawing
Definition: mythsystem.h:35
EncoderProfile * encoderProfile
Definition: archiveutil.h:65
bool m_bCreateISO
Definition: mythburn.h:108
static bool isArchiveItemValid(const QString &type, const QString &filename)
Definition: mythburn.cpp:470
run child in the background
Definition: mythsystem.h:36
void showLogViewer(void)
Definition: logviewer.cpp:24
MythUIProgressBar * m_sizeBar
Definition: mythburn.h:127
void editThumbnails(void)
Definition: mythburn.cpp:826
int videoHeight
Definition: archiveutil.h:69
void SetItemCurrent(MythUIButtonListItem *item)
void handleNextPage(void)
Definition: mythburn.cpp:339
void start(void)
Definition: mythburn.cpp:1091
bool SetFocusWidget(MythUIType *widget=nullptr)
bool MythRemoveDirectory(QDir &aDir)
int64_t size
Definition: archiveutil.h:61
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
QString name
Definition: archiveutil.h:39
MythUIText * m_currentsizeErrorText
Definition: mythburn.h:130
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
MythUIButtonListItem * GetItemFirst() const
Screen in which all other widgets are contained and rendered.
int GetCurrentPos() const
void updateArchiveList(void)
Definition: mythburn.cpp:387
void removeItem(void)
Definition: mythburn.cpp:794
void handleAddRecording(void)
Definition: mythburn.cpp:940
void handlePrevPage(void)
Definition: mythburn.cpp:350
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: mythburn.cpp:128
void DisplayState(const QString &state, const QString &name)
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:34
MythUIButton * m_addrecordingButton
Definition: mythburn.h:122
void profileChanged(int profileNo)
Definition: mythburn.cpp:875
MythUIButtonListItem * GetItemCurrent() const
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
EncoderProfile * getProfileFromName(const QString &profileName)
Definition: mythburn.cpp:695