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 <libmythbase/mythconfig.h> // PYTHON_EXE
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 {
399 popupStack->AddScreen(busyPopup, false);
400 }
401 else
402 {
403 delete busyPopup;
404 busyPopup = nullptr;
405 }
406
407 QCoreApplication::processEvents();
408
410
411 if (m_archiveList.empty())
412 {
414 }
415 else
416 {
417 for (auto *a : std::as_const(m_archiveList))
418 {
419 QCoreApplication::processEvents();
420 // get duration of this file
421 if (a->duration == 0)
422 {
423 if (!getFileDetails(a))
424 LOG(VB_GENERAL, LOG_ERR,
425 QString("MythBurn: failed to get file details for: %1").arg(a->filename));
426 }
427
428 // get default encoding profile if needed
429
430 if (a->encoderProfile == nullptr)
431 a->encoderProfile = getDefaultProfile(a);
432
434
435 auto* item = new MythUIButtonListItem(m_archiveButtonList, a->title);
436 item->SetData(QVariant::fromValue(a));
437 item->SetText(a->subtitle, "subtitle");
438 item->SetText(a->startDate + " " + a->startTime, "date");
439 item->SetText(StringUtil::formatKBytes(a->newsize / 1024, 2), "size");
440 if (a->hasCutlist)
441 {
442 if (a->useCutlist)
443 {
444 item->SetText(tr("Using Cut List"), "cutlist");
445 item->DisplayState("using", "cutliststatus");
446 }
447 else
448 {
449 item->SetText(tr("Not Using Cut List"), "cutlist");
450 item->DisplayState("notusing", "cutliststatus");
451 }
452 }
453 else
454 {
455 item->SetText(tr("No Cut List"), "cutlist");
456 item->DisplayState("none", "cutliststatus");
457 }
458 item->SetText(tr("Encoder: ") + a->encoderProfile->name, "profile");
459 }
460
462
465 }
466
468
469 if (busyPopup)
470 busyPopup->Close();
471}
472
473bool MythBurn::isArchiveItemValid(const QString &type, const QString &filename)
474{
475 if (type == "Recording")
476 {
477 QString baseName = getBaseName(filename);
478
480 query.prepare("SELECT title FROM recorded WHERE basename = :FILENAME");
481 query.bindValue(":FILENAME", baseName);
482 if (query.exec() && query.size())
483 return true;
484 LOG(VB_GENERAL, LOG_ERR,
485 QString("MythArchive: Recording not found (%1)")
486 .arg(filename));
487 }
488 else if (type == "Video")
489 {
491 query.prepare("SELECT title FROM videometadata"
492 " WHERE filename = :FILENAME");
493 query.bindValue(":FILENAME", filename);
494 if (query.exec() && query.size())
495 return true;
496 LOG(VB_GENERAL, LOG_ERR,
497 QString("MythArchive: Video not found (%1)").arg(filename));
498 }
499 else if (type == "File")
500 {
502 return true;
503 LOG(VB_GENERAL, LOG_ERR,
504 QString("MythArchive: File not found (%1)").arg(filename));
505 }
506
507 LOG(VB_GENERAL, LOG_NOTICE, "MythArchive: Archive item removed from list");
508
509 return false;
510}
511
513{
514 if (!item)
515 return m_profileList.at(0);
516
517 EncoderProfile *profile = nullptr;
518
519 // is the file an mpeg2 file?
520 if (item->videoCodec.toLower() == "mpeg2video (main)")
521 {
522 // does the file already have a valid DVD resolution?
523 if (gCoreContext->GetSetting("MythArchiveVideoFormat", "pal").toLower()
524 == "ntsc")
525 {
526 if ((item->videoWidth == 720 && item->videoHeight == 480) ||
527 (item->videoWidth == 704 && item->videoHeight == 480) ||
528 (item->videoWidth == 352 && item->videoHeight == 480) ||
529 (item->videoWidth == 352 && item->videoHeight == 240))
530 {
531 // don't need to re-encode
532 profile = m_profileList.at(0);
533 }
534 }
535 else
536 {
537 if ((item->videoWidth == 720 && item->videoHeight == 576) ||
538 (item->videoWidth == 704 && item->videoHeight == 576) ||
539 (item->videoWidth == 352 && item->videoHeight == 576) ||
540 (item->videoWidth == 352 && item->videoHeight == 288))
541 {
542 // don't need to re-encode
543 profile = m_profileList.at(0);
544 }
545 }
546 }
547
548 if (!profile)
549 {
550 // file needs re-encoding - use default profile setting
551 QString defaultProfile =
552 gCoreContext->GetSetting("MythArchiveDefaultEncProfile", "SP");
553
554 for (auto *x : std::as_const(m_profileList))
555 if (x->name == defaultProfile)
556 profile = x;
557 }
558
559 return profile;
560}
561
563{
564 QDomDocument doc("mythburn");
565
566 QDomElement root = doc.createElement("mythburn");
567 doc.appendChild(root);
568
569 QDomElement job = doc.createElement("job");
570 job.setAttribute("theme", m_theme);
571 root.appendChild(job);
572
573 QDomElement media = doc.createElement("media");
574 job.appendChild(media);
575
576 // now loop though selected archive items and add them to the xml file
577 for (int x = 0; x < m_archiveButtonList->GetCount(); x++)
578 {
580 if (!item)
581 continue;
582
583 auto *a = item->GetData().value<ArchiveItem *>();
584 if (!a)
585 continue;
586
587 QDomElement file = doc.createElement("file");
588 file.setAttribute("type", a->type.toLower() );
589 file.setAttribute("usecutlist", static_cast<int>(a->useCutlist));
590 file.setAttribute("filename", a->filename);
591 file.setAttribute("encodingprofile", a->encoderProfile->name);
592 if (a->editedDetails)
593 {
594 QDomElement details = doc.createElement("details");
595 file.appendChild(details);
596 details.setAttribute("title", a->title);
597 details.setAttribute("subtitle", a->subtitle);
598 details.setAttribute("startdate", a->startDate);
599 details.setAttribute("starttime", a->startTime);
600 QDomText desc = doc.createTextNode(a->description);
601 details.appendChild(desc);
602 }
603
604 if (!a->thumbList.empty())
605 {
606 QDomElement thumbs = doc.createElement("thumbimages");
607 file.appendChild(thumbs);
608
609 for (auto *thumbImage : std::as_const(a->thumbList))
610 {
611 QDomElement thumb = doc.createElement("thumb");
612 thumbs.appendChild(thumb);
613 thumb.setAttribute("caption", thumbImage->caption);
614 thumb.setAttribute("filename", thumbImage->filename);
615 thumb.setAttribute("frame", (int) thumbImage->frame);
616 }
617 }
618
619 media.appendChild(file);
620 }
621
622 // add the options to the xml file
623 QDomElement options = doc.createElement("options");
624 options.setAttribute("createiso", static_cast<int>(m_bCreateISO));
625 options.setAttribute("doburn", static_cast<int>(m_bDoBurn));
626 options.setAttribute("mediatype", m_archiveDestination.type);
627 options.setAttribute("dvdrsize", (qint64)m_archiveDestination.freeSpace);
628 options.setAttribute("erasedvdrw", static_cast<int>(m_bEraseDvdRw));
629 options.setAttribute("savefilename", m_saveFilename);
630 job.appendChild(options);
631
632 // finally save the xml to the file
633 QFile f(filename);
634 if (!f.open(QIODevice::WriteOnly))
635 {
636 LOG(VB_GENERAL, LOG_ERR,
637 QString("MythBurn::createConfigFile: "
638 "Failed to open file for writing - %1") .arg(filename));
639 return;
640 }
641
642 QTextStream t(&f);
643 t << doc.toString(4);
644 f.close();
645}
646
648{
649 m_theme = gCoreContext->GetSetting("MythBurnMenuTheme", "");
650 m_bCreateISO = (gCoreContext->GetSetting("MythBurnCreateISO", "0") == "1");
651 m_bDoBurn = (gCoreContext->GetSetting("MythBurnBurnDVDr", "1") == "1");
652 m_bEraseDvdRw = (gCoreContext->GetSetting("MythBurnEraseDvdRw", "0") == "1");
653 m_saveFilename = gCoreContext->GetSetting("MythBurnSaveFilename", "");
654
655 while (!m_archiveList.isEmpty())
656 delete m_archiveList.takeFirst();
657 m_archiveList.clear();
658
659 // load selected file list
661 query.prepare("SELECT type, title, subtitle, description, startdate, "
662 "starttime, size, filename, hascutlist, duration, "
663 "cutduration, videowidth, videoheight, filecodec, "
664 "videocodec, encoderprofile FROM archiveitems "
665 "ORDER BY intid;");
666
667 if (!query.exec())
668 {
669 MythDB::DBError("archive item insert", query);
670 return;
671 }
672
673 while (query.next())
674 {
675 auto *a = new ArchiveItem;
676 a->type = query.value(0).toString();
677 a->title = query.value(1).toString();
678 a->subtitle = query.value(2).toString();
679 a->description = query.value(3).toString();
680 a->startDate = query.value(4).toString();
681 a->startTime = query.value(5).toString();
682 a->size = query.value(6).toLongLong();
683 a->filename = query.value(7).toString();
684 a->hasCutlist = (query.value(8).toInt() == 1);
685 a->useCutlist = false;
686 a->duration = query.value(9).toInt();
687 a->cutDuration = query.value(10).toInt();
688 a->videoWidth = query.value(11).toInt();
689 a->videoHeight = query.value(12).toInt();
690 a->fileCodec = query.value(13).toString();
691 a->videoCodec = query.value(14).toString();
692 a->encoderProfile = getProfileFromName(query.value(15).toString());
693 a->editedDetails = false;
694 m_archiveList.append(a);
695 }
696}
697
699{
700 for (auto *x : std::as_const(m_profileList))
701 if (x->name == profileName)
702 return x;
703
704 return nullptr;
705}
706
708{
709 // remove all old archive items from DB
711 query.prepare("DELETE FROM archiveitems;");
712 if (!query.exec())
713 MythDB::DBError("MythBurn::saveConfiguration - deleting archiveitems",
714 query);
715
716 // save new list of archive items to DB
717 for (int x = 0; x < m_archiveButtonList->GetCount(); x++)
718 {
720 if (!item)
721 continue;
722
723 auto *a = item->GetData().value<ArchiveItem *>();
724 if (!a)
725 continue;
726
727 query.prepare("INSERT INTO archiveitems (type, title, subtitle, "
728 "description, startdate, starttime, size, filename, "
729 "hascutlist, duration, cutduration, videowidth, "
730 "videoheight, filecodec, videocodec, encoderprofile) "
731 "VALUES(:TYPE, :TITLE, :SUBTITLE, :DESCRIPTION, :STARTDATE, "
732 ":STARTTIME, :SIZE, :FILENAME, :HASCUTLIST, :DURATION, "
733 ":CUTDURATION, :VIDEOWIDTH, :VIDEOHEIGHT, :FILECODEC, "
734 ":VIDEOCODEC, :ENCODERPROFILE);");
735 query.bindValue(":TYPE", a->type);
736 query.bindValue(":TITLE", a->title);
737 query.bindValue(":SUBTITLE", a->subtitle);
738 query.bindValue(":DESCRIPTION", a->description);
739 query.bindValue(":STARTDATE", a->startDate);
740 query.bindValue(":STARTTIME", a->startTime);
741 query.bindValue(":SIZE", (qint64)a->size);
742 query.bindValue(":FILENAME", a->filename);
743 query.bindValue(":HASCUTLIST", a->hasCutlist);
744 query.bindValue(":DURATION", a->duration);
745 query.bindValue(":CUTDURATION", a->cutDuration);
746 query.bindValue(":VIDEOWIDTH", a->videoWidth);
747 query.bindValue(":VIDEOHEIGHT", a->videoHeight);
748 query.bindValue(":FILECODEC", a->fileCodec);
749 query.bindValue(":VIDEOCODEC", a->videoCodec);
750 query.bindValue(":ENCODERPROFILE", a->encoderProfile->name);
751
752 if (!query.exec())
753 MythDB::DBError("archive item insert", query);
754 }
755}
756
758{
759 if (m_archiveList.empty())
760 return;
761
763 auto *curItem = item->GetData().value<ArchiveItem *>();
764
765 if (!curItem)
766 return;
767
768 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
769
770 auto *menuPopup = new MythDialogBox(tr("Menu"), popupStack, "actionmenu");
771
772 if (menuPopup->Create())
773 popupStack->AddScreen(menuPopup);
774
775 menuPopup->SetReturnEvent(this, "action");
776
777 if (curItem->hasCutlist)
778 {
779 if (curItem->useCutlist)
780 {
781 menuPopup->AddButton(tr("Don't Use Cut List"),
783 }
784 else
785 {
786 menuPopup->AddButton(tr("Use Cut List"),
788 }
789 }
790
791 menuPopup->AddButton(tr("Remove Item"), &MythBurn::removeItem);
792 menuPopup->AddButton(tr("Edit Details"), &MythBurn::editDetails);
793 menuPopup->AddButton(tr("Change Encoding Profile"), &MythBurn::changeProfile);
794 menuPopup->AddButton(tr("Edit Thumbnails"), &MythBurn::editThumbnails);
795}
796
798{
800 auto *curItem = item->GetData().value<ArchiveItem *>();
801
802 if (!curItem)
803 return;
804
805 m_archiveList.removeAll(curItem);
806
808}
809
811{
813 auto *curItem = item->GetData().value<ArchiveItem *>();
814
815 if (!curItem)
816 return;
817
819
820 auto *editor = new EditMetadataDialog(mainStack, curItem);
821
822 connect(editor, &EditMetadataDialog::haveResult,
824
825 if (editor->Create())
826 mainStack->AddScreen(editor);
827}
828
830{
832 auto *curItem = item->GetData().value<ArchiveItem *>();
833
834 if (!curItem)
835 return;
836
838
839 auto *finder = new ThumbFinder(mainStack, curItem, m_theme);
840
841 if (finder->Create())
842 mainStack->AddScreen(finder);
843}
844
846{
848
849 if (ok && item && gridItem)
850 {
851 // update the grid to reflect any changes
852 gridItem->SetText(item->title);
853 gridItem->SetText(item->subtitle, "subtitle");
854 gridItem->SetText(item->startDate + " " + item->startTime, "date");
855 }
856}
857
859{
861 auto *curItem = item->GetData().value<ArchiveItem *>();
862
863 if (!curItem)
864 return;
865
866 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
867
868 auto *profileDialog = new ProfileDialog(popupStack, curItem, m_profileList);
869
870 if (!profileDialog->Create())
871 {
872 delete profileDialog;
873 return;
874 }
875
876 popupStack->AddScreen(profileDialog, false);
877 connect(profileDialog, &ProfileDialog::haveResult,
879}
880
881void MythBurn::profileChanged(int profileNo)
882{
883 if (profileNo > m_profileList.size() - 1)
884 return;
885
886 EncoderProfile *profile = m_profileList.at(profileNo);
887
889 if (!item)
890 return;
891
892 auto *archiveItem = item->GetData().value<ArchiveItem *>();
893 if (!archiveItem)
894 return;
895
896 archiveItem->encoderProfile = profile;
897
898 item->SetText(profile->name, "profile");
899 item->SetText(StringUtil::formatKBytes(archiveItem->newsize / 1024, 2), "size");
900
902}
903
905{
906 QString tempDir = getTempDirectory();
907 QString logDir = tempDir + "logs";
908 QString configDir = tempDir + "config";
909 QString commandline;
910
911 // remove any existing logs
912 myth_system("rm -f " + logDir + "/*.log");
913
914 // remove cancel flag file if present
915 if (QFile::exists(logDir + "/mythburncancel.lck"))
916 QFile::remove(logDir + "/mythburncancel.lck");
917
918 createConfigFile(configDir + "/mydata.xml");
919 commandline = PYTHON_EXE;
920 commandline += " " + GetShareDir() + "mytharchive/scripts/mythburn.py";
921 commandline += " -j " + configDir + "/mydata.xml"; // job file
922 commandline += " -l " + logDir + "/progress.log"; // progress log
923 commandline += " > " + logDir + "/mythburn.log 2>&1 &"; // Logs
924
925 gCoreContext->SaveSetting("MythArchiveLastRunStatus", "Running");
926
929 uint retval = myth_system(commandline, flags);
930 if (retval != GENERIC_EXIT_RUNNING && retval != GENERIC_EXIT_OK)
931 {
932 ShowOkPopup(tr("It was not possible to create the DVD. "
933 " An error occured when running the scripts"));
934 }
935 else
936 {
937 // now show the log viewer
939 }
940
943 Close();
944}
945
947{
949
950 auto *selector = new RecordingSelector(mainStack, &m_archiveList);
951
952 connect(selector, &RecordingSelector::haveResult,
954
955 if (selector->Create())
956 mainStack->AddScreen(selector);
957}
958
960{
961 if (ok)
963}
964
966{
968 query.prepare("SELECT title FROM videometadata");
969 if (query.exec() && query.size())
970 {
971 }
972 else
973 {
974 ShowOkPopup(tr("You don't have any videos!"));
975 return;
976 }
977
979
980 auto *selector = new VideoSelector(mainStack, &m_archiveList);
981
982 connect(selector, &VideoSelector::haveResult,
984
985 if (selector->Create())
986 mainStack->AddScreen(selector);
987}
988
990{
991 QString filter = gCoreContext->GetSetting("MythArchiveFileFilter",
992 "*.mpg *.mpeg *.mov *.avi *.nuv");
993
995
996 auto *selector = new FileSelector(mainStack, &m_archiveList,
997 FSTYPE_FILELIST, "/", filter);
998
999 connect(selector, qOverload<bool>(&FileSelector::haveResult),
1001
1002 if (selector->Create())
1003 mainStack->AddScreen(selector);
1004}
1005
1007{
1009
1010 if (m_moveMode)
1011 item->DisplayState("on", "movestate");
1012 else
1013 item->DisplayState("off", "movestate");
1014}
1015
1017
1019{
1020 if (!LoadWindowFromXML("mythburn-ui.xml", "profilepopup", this))
1021 return false;
1022
1023 bool err = false;
1024 UIUtilE::Assign(this, m_captionText, "caption_text", &err);
1025 UIUtilE::Assign(this, m_descriptionText, "description_text", &err);
1026 UIUtilE::Assign(this, m_oldSizeText, "oldsize_text", &err);
1027 UIUtilE::Assign(this, m_newSizeText, "newsize_text", &err);
1028 UIUtilE::Assign(this, m_profileBtnList, "profile_list", &err);
1029 UIUtilE::Assign(this, m_okButton, "ok_button", &err);
1030
1031 if (err)
1032 {
1033 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'profilepopup'");
1034 return false;
1035 }
1036
1037 for (auto *x : std::as_const(m_profileList))
1038 {
1039 auto *item = new
1041 item->SetData(QVariant::fromValue(x));
1042 }
1043
1046
1047
1049
1052
1054
1056
1058
1059 return true;
1060}
1061
1063{
1064 if (!item)
1065 return;
1066
1067 auto *profile = item->GetData().value<EncoderProfile *>();
1068 if (!profile)
1069 return;
1070
1071 m_descriptionText->SetText(profile->description);
1072
1074
1075 // calc new size
1077
1079}
1080
1081
1083{
1085
1086 Close();
1087}
1088
1090
1092 :QObject(nullptr)
1093{
1094 setObjectName("BurnMenu");
1095}
1096
1098{
1099 if (!gCoreContext->GetSetting("MythArchiveLastRunStatus").startsWith("Success"))
1100 {
1101 showWarningDialog(tr("Cannot burn a DVD.\n"
1102 "The last run failed to create a DVD."));
1103 return;
1104 }
1105
1106 // ask the user what type of disk to burn to
1107 QString title = tr("Burn DVD");
1108 QString msg = tr("\nPlace a blank DVD in the"
1109 " drive and select an option below.");
1110 MythScreenStack *mainStack = GetMythMainWindow()->GetStack("main stack");
1111 auto *menuPopup = new MythDialogBox(title, msg, mainStack,
1112 "actionmenu", true);
1113
1114 if (menuPopup->Create())
1115 mainStack->AddScreen(menuPopup);
1116
1117 menuPopup->SetReturnEvent(this, "action");
1118
1119 menuPopup->AddButton(tr("Burn DVD"));
1120 menuPopup->AddButton(tr("Burn DVD Rewritable"));
1121 menuPopup->AddButton(tr("Burn DVD Rewritable (Force Erase)"));
1122}
1123
1124void BurnMenu::customEvent(QEvent *event)
1125{
1126 if (auto *dce = dynamic_cast<DialogCompletionEvent*>(event))
1127 {
1128 if (dce->GetId() == "action")
1129 {
1130 doBurn(dce->GetResult());
1131 deleteLater();
1132 }
1133 }
1134}
1135
1136void BurnMenu::doBurn(int mode)
1137{
1138 if ((mode < 0) || (mode > 2))
1139 return;
1140
1141 QString tempDir = getTempDirectory(true);
1142
1143 if (tempDir == "")
1144 return;
1145
1146 QString logDir = tempDir + "logs";
1147 QString commandline;
1148
1149 // remove existing progress.log if present
1150 if (QFile::exists(logDir + "/progress.log"))
1151 QFile::remove(logDir + "/progress.log");
1152
1153 // remove cancel flag file if present
1154 if (QFile::exists(logDir + "/mythburncancel.lck"))
1155 QFile::remove(logDir + "/mythburncancel.lck");
1156
1157 QString sArchiveFormat = QString::number(mode);
1158 bool bEraseDVDRW = (mode == 2);
1159 bool bNativeFormat = gCoreContext->GetSetting("MythArchiveLastRunType")
1160 .startsWith("Native");
1161
1162 commandline = "mytharchivehelper --burndvd --mediatype " + sArchiveFormat +
1163 (bEraseDVDRW ? " --erasedvdrw" : "") +
1164 (bNativeFormat ? " --nativeformat" : "");
1165 commandline += logPropagateArgs;
1166 if (!logPropagateQuiet())
1167 commandline += " --quiet";
1168 commandline += " > " + logDir + "/progress.log 2>&1 &";
1169
1172 uint retval = myth_system(commandline, flags);
1173 if (retval != GENERIC_EXIT_RUNNING && retval != GENERIC_EXIT_OK)
1174 {
1175 showWarningDialog(tr("It was not possible to run "
1176 "mytharchivehelper to burn the DVD."));
1177 return;
1178 }
1179
1180 // now show the log viewer
1181 showLogViewer();
1182}
1183
1184/* 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:1136
void start(void)
Definition: mythburn.cpp:1097
BurnMenu(void)
Definition: mythburn.cpp:1091
void customEvent(QEvent *event) override
Definition: mythburn.cpp:1124
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:40
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:838
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:619
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
void selectorClosed(bool ok)
Definition: mythburn.cpp:959
~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:829
MythBurn(MythScreenStack *parent, MythScreenType *destinationScreen, MythScreenType *themeScreen, const ArchiveDestination &archiveDestination, const QString &name)
Definition: mythburn.cpp:43
EncoderProfile * getDefaultProfile(ArchiveItem *item)
Definition: mythburn.cpp:512
void updateSizeBar()
Definition: mythburn.cpp:191
void changeProfile(void)
Definition: mythburn.cpp:858
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:473
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:989
bool Create(void) override
Definition: mythburn.cpp:72
void loadConfiguration(void)
Definition: mythburn.cpp:647
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:810
void handleAddVideo(void)
Definition: mythburn.cpp:965
void handleAddRecording(void)
Definition: mythburn.cpp:946
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: mythburn.cpp:125
void saveConfiguration(void)
Definition: mythburn.cpp:707
MythUIText * m_minsizeText
Definition: mythburn.h:130
void profileChanged(int profileNo)
Definition: mythburn.cpp:881
MythUIButton * m_cancelButton
Definition: mythburn.h:119
bool m_moveMode
Definition: mythburn.h:115
EncoderProfile * getProfileFromName(const QString &profileName)
Definition: mythburn.cpp:698
void itemClicked(MythUIButtonListItem *item)
Definition: mythburn.cpp:1006
void createConfigFile(const QString &filename)
Definition: mythburn.cpp:562
void removeItem(void)
Definition: mythburn.cpp:797
void runScript()
Definition: mythburn.cpp:904
QList< ArchiveItem * > m_archiveList
Definition: mythburn.h:106
QString m_saveFilename
Definition: mythburn.h:112
void editorClosed(bool ok, ArchiveItem *item)
Definition: mythburn.cpp:845
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:757
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:225
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:1018
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:1082
void profileChanged(MythUIButtonListItem *item)
Definition: mythburn.cpp:1062
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:60
@ 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:632
QString logPropagateArgs
Definition: logging.cpp:86
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:283
QString GetConfDir(void)
Definition: mythdirs.cpp:285
#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:281
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