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