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