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  auto *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;
273  QString desc;
274  QString bitrate;
275 
276  for (int x = 0; x < profileNodeList.count(); x++)
277  {
278  QDomNode n = profileNodeList.item(x);
279  QDomElement e = n.toElement();
280  QDomNode n2 = e.firstChild();
281  while (!n2.isNull())
282  {
283  QDomElement e2 = n2.toElement();
284  if(!e2.isNull())
285  {
286  if (e2.tagName() == "name")
287  name = e2.text();
288  if (e2.tagName() == "description")
289  desc = e2.text();
290  if (e2.tagName() == "bitrate")
291  bitrate = e2.text();
292 
293  }
294  n2 = n2.nextSibling();
295 
296  }
297 
298  auto *item2 = new EncoderProfile;
299  item2->name = name;
300  item2->description = desc;
301  item2->bitrate = bitrate.toFloat();
302  m_profileList.append(item2);
303  }
304 }
305 
307 {
309  auto *a = item->GetData().value<ArchiveItem *>();
310 
311  if (!a)
312  return;
313 
314  if (!a->hasCutlist)
315  return;
316 
317  a->useCutlist = !a->useCutlist;
318 
319  if (a->hasCutlist)
320  {
321  if (a->useCutlist)
322  {
323  item->SetText(tr("Using Cut List"), "cutlist");
324  item->DisplayState("using", "cutliststatus");
325  }
326  else
327  {
328  item->SetText(tr("Not Using Cut List"), "cutlist");
329  item->DisplayState("notusing", "cutliststatus");
330  }
331  }
332  else
333  {
334  item->SetText(tr("No Cut List"), "cutlist");
335  item->DisplayState("none", "cutliststatus");
336  }
337  recalcItemSize(a);
338  updateSizeBar();
339 }
340 
342 {
343  if (m_archiveList.empty())
344  {
345  ShowOkPopup(tr("You need to add at least one item to archive!"));
346  return;
347  }
348 
349  runScript();
350 }
351 
353 {
354  Close();
355 }
356 
358 {
360  m_themeScreen->Close();
361  Close();
362 }
363 
364 QString MythBurn::loadFile(const QString &filename)
365 {
366  QString res = "";
367 
368  QFile file(filename);
369 
370  if (!file.exists())
371  return "";
372 
373  if (file.open( QIODevice::ReadOnly ))
374  {
375  QTextStream stream(&file);
376 
377  while ( !stream.atEnd() )
378  {
379  res = res + stream.readLine();
380  }
381  file.close();
382  }
383  else
384  return "";
385 
386  return res;
387 }
388 
390 {
391  QString message = tr("Retrieving File Information. Please Wait...");
392 
393  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
394 
395  auto *busyPopup = new
396  MythUIBusyDialog(message, popupStack, "mythburnbusydialog");
397 
398  if (busyPopup->Create())
399  popupStack->AddScreen(busyPopup, false);
400  else
401  {
402  delete busyPopup;
403  busyPopup = nullptr;
404  }
405 
406  qApp->processEvents();
407 
409 
410  if (m_archiveList.empty())
411  {
412  m_nofilesText->Show();
413  }
414  else
415  {
416  for (int x = 0; x < m_archiveList.size(); x++)
417  {
418  qApp->processEvents();
419  ArchiveItem *a = m_archiveList.at(x);
420 
421  // get duration of this file
422  if (a->duration == 0)
423  {
424  if (!getFileDetails(a))
425  LOG(VB_GENERAL, LOG_ERR,
426  QString("MythBurn: failed to get file details for: %1").arg(a->filename));
427  }
428 
429  // get default encoding profile if needed
430 
431  if (a->encoderProfile == nullptr)
433 
434  recalcItemSize(a);
435 
436  auto* item = new MythUIButtonListItem(m_archiveButtonList, a->title);
437  item->SetData(qVariantFromValue(a));
438  item->SetText(a->subtitle, "subtitle");
439  item->SetText(a->startDate + " " + a->startTime, "date");
440  item->SetText(formatSize(a->newsize / 1024, 2), "size");
441  if (a->hasCutlist)
442  {
443  if (a->useCutlist)
444  {
445  item->SetText(tr("Using Cut List"), "cutlist");
446  item->DisplayState("using", "cutliststatus");
447  }
448  else
449  {
450  item->SetText(tr("Not Using Cut List"), "cutlist");
451  item->DisplayState("notusing", "cutliststatus");
452  }
453  }
454  else
455  {
456  item->SetText(tr("No Cut List"), "cutlist");
457  item->DisplayState("none", "cutliststatus");
458  }
459  item->SetText(tr("Encoder: ") + a->encoderProfile->name, "profile");
460  }
461 
462  m_nofilesText->Hide();
463 
466  }
467 
468  updateSizeBar();
469 
470  if (busyPopup)
471  busyPopup->Close();
472 }
473 
474 bool MythBurn::isArchiveItemValid(const QString &type, const QString &filename)
475 {
476  if (type == "Recording")
477  {
478  QString baseName = getBaseName(filename);
479 
480  MSqlQuery query(MSqlQuery::InitCon());
481  query.prepare("SELECT title FROM recorded WHERE basename = :FILENAME");
482  query.bindValue(":FILENAME", baseName);
483  if (query.exec() && query.size())
484  return true;
485  LOG(VB_GENERAL, LOG_ERR,
486  QString("MythArchive: Recording not found (%1)")
487  .arg(filename));
488  }
489  else if (type == "Video")
490  {
491  MSqlQuery query(MSqlQuery::InitCon());
492  query.prepare("SELECT title FROM videometadata"
493  " WHERE filename = :FILENAME");
494  query.bindValue(":FILENAME", filename);
495  if (query.exec() && query.size())
496  return true;
497  LOG(VB_GENERAL, LOG_ERR,
498  QString("MythArchive: Video not found (%1)").arg(filename));
499  }
500  else if (type == "File")
501  {
502  if (QFile::exists(filename))
503  return true;
504  LOG(VB_GENERAL, LOG_ERR,
505  QString("MythArchive: File not found (%1)").arg(filename));
506  }
507 
508  LOG(VB_GENERAL, LOG_NOTICE, "MythArchive: Archive item removed from list");
509 
510  return false;
511 }
512 
514 {
515  if (!item)
516  return m_profileList.at(0);
517 
518  EncoderProfile *profile = nullptr;
519 
520  // is the file an mpeg2 file?
521  if (item->videoCodec.toLower() == "mpeg2video (main)")
522  {
523  // does the file already have a valid DVD resolution?
524  if (gCoreContext->GetSetting("MythArchiveVideoFormat", "pal").toLower()
525  == "ntsc")
526  {
527  if ((item->videoWidth == 720 && item->videoHeight == 480) ||
528  (item->videoWidth == 704 && item->videoHeight == 480) ||
529  (item->videoWidth == 352 && item->videoHeight == 480) ||
530  (item->videoWidth == 352 && item->videoHeight == 240))
531  {
532  // don't need to re-encode
533  profile = m_profileList.at(0);
534  }
535  }
536  else
537  {
538  if ((item->videoWidth == 720 && item->videoHeight == 576) ||
539  (item->videoWidth == 704 && item->videoHeight == 576) ||
540  (item->videoWidth == 352 && item->videoHeight == 576) ||
541  (item->videoWidth == 352 && item->videoHeight == 288))
542  {
543  // don't need to re-encode
544  profile = m_profileList.at(0);
545  }
546  }
547  }
548 
549  if (!profile)
550  {
551  // file needs re-encoding - use default profile setting
552  QString defaultProfile =
553  gCoreContext->GetSetting("MythArchiveDefaultEncProfile", "SP");
554 
555  for (int x = 0; x < m_profileList.size(); x++)
556  if (m_profileList.at(x)->name == defaultProfile)
557  profile = m_profileList.at(x);
558  }
559 
560  return profile;
561 }
562 
564 {
565  QDomDocument doc("mythburn");
566 
567  QDomElement root = doc.createElement("mythburn");
568  doc.appendChild(root);
569 
570  QDomElement job = doc.createElement("job");
571  job.setAttribute("theme", m_theme);
572  root.appendChild(job);
573 
574  QDomElement media = doc.createElement("media");
575  job.appendChild(media);
576 
577  // now loop though selected archive items and add them to the xml file
578  for (int x = 0; x < m_archiveButtonList->GetCount(); x++)
579  {
581  if (!item)
582  continue;
583 
584  auto *a = item->GetData().value<ArchiveItem *>();
585  if (!a)
586  continue;
587 
588  QDomElement file = doc.createElement("file");
589  file.setAttribute("type", a->type.toLower() );
590  file.setAttribute("usecutlist", static_cast<int>(a->useCutlist));
591  file.setAttribute("filename", a->filename);
592  file.setAttribute("encodingprofile", a->encoderProfile->name);
593  if (a->editedDetails)
594  {
595  QDomElement details = doc.createElement("details");
596  file.appendChild(details);
597  details.setAttribute("title", a->title);
598  details.setAttribute("subtitle", a->subtitle);
599  details.setAttribute("startdate", a->startDate);
600  details.setAttribute("starttime", a->startTime);
601  QDomText desc = doc.createTextNode(a->description);
602  details.appendChild(desc);
603  }
604 
605  if (!a->thumbList.empty())
606  {
607  QDomElement thumbs = doc.createElement("thumbimages");
608  file.appendChild(thumbs);
609 
610  for (int y = 0; y < a->thumbList.size(); y++)
611  {
612  QDomElement thumb = doc.createElement("thumb");
613  thumbs.appendChild(thumb);
614  ThumbImage *thumbImage = a->thumbList.at(y);
615  thumb.setAttribute("caption", thumbImage->caption);
616  thumb.setAttribute("filename", thumbImage->filename);
617  thumb.setAttribute("frame", (int) thumbImage->frame);
618  }
619  }
620 
621  media.appendChild(file);
622  }
623 
624  // add the options to the xml file
625  QDomElement options = doc.createElement("options");
626  options.setAttribute("createiso", static_cast<int>(m_bCreateISO));
627  options.setAttribute("doburn", static_cast<int>(m_bDoBurn));
628  options.setAttribute("mediatype", m_archiveDestination.type);
629  options.setAttribute("dvdrsize", (qint64)m_archiveDestination.freeSpace);
630  options.setAttribute("erasedvdrw", static_cast<int>(m_bEraseDvdRw));
631  options.setAttribute("savefilename", m_saveFilename);
632  job.appendChild(options);
633 
634  // finally save the xml to the file
635  QFile f(filename);
636  if (!f.open(QIODevice::WriteOnly))
637  {
638  LOG(VB_GENERAL, LOG_ERR,
639  QString("MythBurn::createConfigFile: "
640  "Failed to open file for writing - %1") .arg(filename));
641  return;
642  }
643 
644  QTextStream t(&f);
645  t << doc.toString(4);
646  f.close();
647 }
648 
650 {
651  m_theme = gCoreContext->GetSetting("MythBurnMenuTheme", "");
652  m_bCreateISO = (gCoreContext->GetSetting("MythBurnCreateISO", "0") == "1");
653  m_bDoBurn = (gCoreContext->GetSetting("MythBurnBurnDVDr", "1") == "1");
654  m_bEraseDvdRw = (gCoreContext->GetSetting("MythBurnEraseDvdRw", "0") == "1");
655  m_saveFilename = gCoreContext->GetSetting("MythBurnSaveFilename", "");
656 
657  while (!m_archiveList.isEmpty())
658  delete m_archiveList.takeFirst();
659  m_archiveList.clear();
660 
661  // load selected file list
662  MSqlQuery query(MSqlQuery::InitCon());
663  query.prepare("SELECT type, title, subtitle, description, startdate, "
664  "starttime, size, filename, hascutlist, duration, "
665  "cutduration, videowidth, videoheight, filecodec, "
666  "videocodec, encoderprofile FROM archiveitems "
667  "ORDER BY intid;");
668 
669  if (!query.exec())
670  {
671  MythDB::DBError("archive item insert", query);
672  return;
673  }
674 
675  while (query.next())
676  {
677  auto *a = new ArchiveItem;
678  a->type = query.value(0).toString();
679  a->title = query.value(1).toString();
680  a->subtitle = query.value(2).toString();
681  a->description = query.value(3).toString();
682  a->startDate = query.value(4).toString();
683  a->startTime = query.value(5).toString();
684  a->size = query.value(6).toLongLong();
685  a->filename = query.value(7).toString();
686  a->hasCutlist = (query.value(8).toInt() == 1);
687  a->useCutlist = false;
688  a->duration = query.value(9).toInt();
689  a->cutDuration = query.value(10).toInt();
690  a->videoWidth = query.value(11).toInt();
691  a->videoHeight = query.value(12).toInt();
692  a->fileCodec = query.value(13).toString();
693  a->videoCodec = query.value(14).toString();
694  a->encoderProfile = getProfileFromName(query.value(15).toString());
695  a->editedDetails = false;
696  m_archiveList.append(a);
697  }
698 }
699 
700 EncoderProfile *MythBurn::getProfileFromName(const QString &profileName)
701 {
702  for (int x = 0; x < m_profileList.size(); x++)
703  if (m_profileList.at(x)->name == profileName)
704  return m_profileList.at(x);
705 
706  return nullptr;
707 }
708 
710 {
711  // remove all old archive items from DB
712  MSqlQuery query(MSqlQuery::InitCon());
713  query.prepare("DELETE FROM archiveitems;");
714  if (!query.exec())
715  MythDB::DBError("MythBurn::saveConfiguration - deleting archiveitems",
716  query);
717 
718  // save new list of archive items to DB
719  for (int x = 0; x < m_archiveButtonList->GetCount(); x++)
720  {
722  if (!item)
723  continue;
724 
725  auto *a = item->GetData().value<ArchiveItem *>();
726  if (!a)
727  continue;
728 
729  query.prepare("INSERT INTO archiveitems (type, title, subtitle, "
730  "description, startdate, starttime, size, filename, "
731  "hascutlist, duration, cutduration, videowidth, "
732  "videoheight, filecodec, videocodec, encoderprofile) "
733  "VALUES(:TYPE, :TITLE, :SUBTITLE, :DESCRIPTION, :STARTDATE, "
734  ":STARTTIME, :SIZE, :FILENAME, :HASCUTLIST, :DURATION, "
735  ":CUTDURATION, :VIDEOWIDTH, :VIDEOHEIGHT, :FILECODEC, "
736  ":VIDEOCODEC, :ENCODERPROFILE);");
737  query.bindValue(":TYPE", a->type);
738  query.bindValue(":TITLE", a->title);
739  query.bindValue(":SUBTITLE", a->subtitle);
740  query.bindValue(":DESCRIPTION", a->description);
741  query.bindValue(":STARTDATE", a->startDate);
742  query.bindValue(":STARTTIME", a->startTime);
743  query.bindValue(":SIZE", (qint64)a->size);
744  query.bindValue(":FILENAME", a->filename);
745  query.bindValue(":HASCUTLIST", a->hasCutlist);
746  query.bindValue(":DURATION", a->duration);
747  query.bindValue(":CUTDURATION", a->cutDuration);
748  query.bindValue(":VIDEOWIDTH", a->videoWidth);
749  query.bindValue(":VIDEOHEIGHT", a->videoHeight);
750  query.bindValue(":FILECODEC", a->fileCodec);
751  query.bindValue(":VIDEOCODEC", a->videoCodec);
752  query.bindValue(":ENCODERPROFILE", a->encoderProfile->name);
753 
754  if (!query.exec())
755  MythDB::DBError("archive item insert", query);
756  }
757 }
758 
760 {
761  if (m_archiveList.empty())
762  return;
763 
765  auto *curItem = item->GetData().value<ArchiveItem *>();
766 
767  if (!curItem)
768  return;
769 
770  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
771 
772  auto *menuPopup = new MythDialogBox(tr("Menu"), popupStack, "actionmenu");
773 
774  if (menuPopup->Create())
775  popupStack->AddScreen(menuPopup);
776 
777  menuPopup->SetReturnEvent(this, "action");
778 
779  if (curItem->hasCutlist)
780  {
781  if (curItem->useCutlist)
782  menuPopup->AddButton(tr("Don't Use Cut List"),
783  SLOT(toggleUseCutlist()));
784  else
785  menuPopup->AddButton(tr("Use Cut List"),
786  SLOT(toggleUseCutlist()));
787  }
788 
789  menuPopup->AddButton(tr("Remove Item"), SLOT(removeItem()));
790  menuPopup->AddButton(tr("Edit Details"), SLOT(editDetails()));
791  menuPopup->AddButton(tr("Change Encoding Profile"), SLOT(changeProfile()));
792  menuPopup->AddButton(tr("Edit Thumbnails"), SLOT(editThumbnails()));
793 }
794 
796 {
798  auto *curItem = item->GetData().value<ArchiveItem *>();
799 
800  if (!curItem)
801  return;
802 
803  m_archiveList.removeAll(curItem);
804 
806 }
807 
809 {
811  auto *curItem = item->GetData().value<ArchiveItem *>();
812 
813  if (!curItem)
814  return;
815 
817 
818  auto *editor = new EditMetadataDialog(mainStack, curItem);
819 
820  connect(editor, SIGNAL(haveResult(bool, ArchiveItem *)),
821  this, SLOT(editorClosed(bool, ArchiveItem *)));
822 
823  if (editor->Create())
824  mainStack->AddScreen(editor);
825 }
826 
828 {
830  auto *curItem = item->GetData().value<ArchiveItem *>();
831 
832  if (!curItem)
833  return;
834 
836 
837  auto *finder = new ThumbFinder(mainStack, curItem, m_theme);
838 
839  if (finder->Create())
840  mainStack->AddScreen(finder);
841 }
842 
844 {
846 
847  if (ok && item && gridItem)
848  {
849  // update the grid to reflect any changes
850  gridItem->SetText(item->title);
851  gridItem->SetText(item->subtitle, "subtitle");
852  gridItem->SetText(item->startDate + " " + item->startTime, "date");
853  }
854 }
855 
857 {
859  auto *curItem = item->GetData().value<ArchiveItem *>();
860 
861  if (!curItem)
862  return;
863 
864  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
865 
866  auto *profileDialog = new ProfileDialog(popupStack, curItem, m_profileList);
867 
868  if (profileDialog->Create())
869  {
870  popupStack->AddScreen(profileDialog, false);
871  connect(profileDialog, SIGNAL(haveResult(int)),
872  this, SLOT(profileChanged(int)));
873  }
874 }
875 
876 void MythBurn::profileChanged(int profileNo)
877 {
878  if (profileNo > m_profileList.size() - 1)
879  return;
880 
881  EncoderProfile *profile = m_profileList.at(profileNo);
882 
884  if (!item)
885  return;
886 
887  auto *archiveItem = item->GetData().value<ArchiveItem *>();
888  if (!archiveItem)
889  return;
890 
891  archiveItem->encoderProfile = profile;
892 
893  item->SetText(profile->name, "profile");
894  item->SetText(formatSize(archiveItem->newsize / 1024, 2), "size");
895 
896  updateSizeBar();
897 }
898 
900 {
901  QString tempDir = getTempDirectory();
902  QString logDir = tempDir + "logs";
903  QString configDir = tempDir + "config";
904  QString commandline;
905 
906  // remove any existing logs
907  myth_system("rm -f " + logDir + "/*.log");
908 
909  // remove cancel flag file if present
910  if (QFile::exists(logDir + "/mythburncancel.lck"))
911  QFile::remove(logDir + "/mythburncancel.lck");
912 
913  createConfigFile(configDir + "/mydata.xml");
914  commandline = "python " + 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 {
961  MSqlQuery query(MSqlQuery::InitCon());
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_profile_list, "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 (int x = 0; x < m_profileList.size(); x++)
1032  {
1033  auto *item = new
1035  item->SetData(qVariantFromValue(m_profileList.at(x)));
1036  }
1037 
1038  connect(m_profile_list, 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:782
MythUIText * m_oldSizeText
Definition: mythburn.h:42
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:1085
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:563
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
static QString loadFile(const QString &filename)
Definition: mythburn.cpp:364
bool useCutlist
Definition: archiveutil.h:70
Basic menu dialog, message and a list of options.
void itemClicked(MythUIButtonListItem *item)
Definition: mythburn.cpp:1000
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:1056
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:135
MythUIButton * m_nextButton
Definition: mythburn.h:114
void save(void)
Definition: mythburn.cpp:1076
void editorClosed(bool ok, ArchiveItem *item)
Definition: mythburn.cpp:843
void handleCancel(void)
Definition: mythburn.cpp:357
MythScreenStack * GetStack(const QString &stackname)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
EncoderProfile * getDefaultProfile(ArchiveItem *item)
Definition: mythburn.cpp:513
MythScreenStack * GetMainStack()
MythUIButtonListItem * GetItemAt(int pos) const
int videoWidth
Definition: archiveutil.h:68
bool getFileDetails(ArchiveItem *a)
MythUIText * m_descriptionText
Definition: mythburn.h:41
MythUIText * m_currentsizeText
Definition: mythburn.h:129
void toggleUseCutlist(void)
Definition: mythburn.cpp:306
void BuildFocusList(void)
bool m_bDoBurn
Definition: mythburn.h:107
void runScript()
Definition: mythburn.cpp:899
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()
MythUIButton * m_prevButton
Definition: mythburn.h:115
void saveConfiguration(void)
Definition: mythburn.cpp:709
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:74
ArchiveItem * m_archiveItem
Definition: mythburn.h:37
void ShowMenu(void) override
Definition: mythburn.cpp:759
void handleAddFile(void)
Definition: mythburn.cpp:983
bool m_bEraseDvdRw
Definition: mythburn.h:108
QString GetSetting(const QString &key, const QString &defaultval="")
MythUIButton * m_addvideoButton
Definition: mythburn.h:121
void showWarningDialog(const QString &msg)
bool logPropagateQuiet(void)
Check if we are propagating a "--quiet".
Definition: logging.cpp:700
MythBurn(MythScreenStack *parent, MythScreenType *destinationScreen, MythScreenType *themeScreen, ArchiveDestination archiveDestination, const QString &name)
Definition: mythburn.cpp:45
QList< EncoderProfile * > m_profileList
Definition: mythburn.h:104
QString GetShareDir(void)
Definition: mythdirs.cpp:222
static void doBurn(int mode)
Definition: mythburn.cpp:1130
#define GENERIC_EXIT_RUNNING
Process is running.
Definition: exitcodes.h:25
void editDetails(void)
Definition: mythburn.cpp:808
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="")
unsigned int uint
Definition: compat.h:140
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
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:649
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:953
void changeProfile(void)
Definition: mythburn.cpp:856
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 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:959
void loadEncoderProfiles(void)
Definition: mythburn.cpp:231
avoid disabling UI drawing
Definition: mythsystem.h:35
bool keyPressEvent(QKeyEvent *) override
Key event handler.
Definition: mythburn.cpp:127
EncoderProfile * encoderProfile
Definition: archiveutil.h:65
bool m_bCreateISO
Definition: mythburn.h:106
static bool isArchiveItemValid(const QString &type, const QString &filename)
Definition: mythburn.cpp:474
run child in the background
Definition: mythsystem.h:36
void showLogViewer(void)
Definition: logviewer.cpp:24
MythUIProgressBar * m_sizeBar
Definition: mythburn.h:125
void editThumbnails(void)
Definition: mythburn.cpp:827
int videoHeight
Definition: archiveutil.h:68
void SetItemCurrent(MythUIButtonListItem *item)
void handleNextPage(void)
Definition: mythburn.cpp:341
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: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:389
void removeItem(void)
Definition: mythburn.cpp:795
void handleAddRecording(void)
Definition: mythburn.cpp:940
void handlePrevPage(void)
Definition: mythburn.cpp:352
void DisplayState(const QString &state, const QString &name)
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:34
MythUIButton * m_addrecordingButton
Definition: mythburn.h:120
void profileChanged(int profileNo)
Definition: mythburn.cpp:876
MythUIButtonListItem * GetItemCurrent() const
EncoderProfile * getProfileFromName(const QString &profileName)
Definition: mythburn.cpp:700