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