MythTV master
importnative.cpp
Go to the documentation of this file.
1#include <cstdlib>
2
3// Qt
4#include <QApplication>
5#include <QDir>
6#include <QFileInfo>
7#include <QDomDocument>
8
9// Myth
23
24// mytharchive
25#include "archiveutil.h"
26#include "importnative.h"
27#include "logviewer.h"
28
29
31
32static bool loadDetailsFromXML(const QString &filename, FileDetails *details)
33{
34 QDomDocument doc("mydocument");
35 QFile file(filename);
36 if (!file.open(QIODevice::ReadOnly))
37 return false;
38
39 if (!doc.setContent(&file))
40 {
41 file.close();
42 return false;
43 }
44 file.close();
45
46 QString docType = doc.doctype().name();
47
48 if (docType == "MYTHARCHIVEITEM")
49 {
50 QDomNodeList itemNodeList = doc.elementsByTagName("item");
51 QString type;
52// QString dbVersion;
53
54 if (itemNodeList.count() < 1)
55 {
56 LOG(VB_GENERAL, LOG_ERR,
57 "Couldn't find an 'item' element in XML file");
58 return false;
59 }
60
61 QDomNode n = itemNodeList.item(0);
62 QDomElement e = n.toElement();
63 type = e.attribute("type");
64// dbVersion = e.attribute("databaseversion");
65 if (type == "recording")
66 {
67 QDomNodeList nodeList = e.elementsByTagName("recorded");
68 if (nodeList.count() < 1)
69 {
70 LOG(VB_GENERAL, LOG_ERR,
71 "Couldn't find a 'recorded' element in XML file");
72 return false;
73 }
74
75 n = nodeList.item(0);
76 e = n.toElement();
77 n = e.firstChild();
78 while (!n.isNull())
79 {
80 e = n.toElement();
81 if (!e.isNull())
82 {
83 if (e.tagName() == "title")
84 details->title = e.text();
85
86 if (e.tagName() == "subtitle")
87 details->subtitle = e.text();
88
89 if (e.tagName() == "starttime")
90 details->startTime = MythDate::fromString(e.text());
91
92 if (e.tagName() == "description")
93 details->description = e.text();
94 }
95 n = n.nextSibling();
96 }
97
98 // get channel info
99 n = itemNodeList.item(0);
100 e = n.toElement();
101 nodeList = e.elementsByTagName("channel");
102 if (nodeList.count() < 1)
103 {
104 LOG(VB_GENERAL, LOG_ERR,
105 "Couldn't find a 'channel' element in XML file");
106 details->chanID = "";
107 details->chanNo = "";
108 details->chanName = "";
109 details->callsign = "";
110 return false;
111 }
112
113 n = nodeList.item(0);
114 e = n.toElement();
115 details->chanID = e.attribute("chanid");
116 details->chanNo = e.attribute("channum");
117 details->chanName = e.attribute("name");
118 details->callsign = e.attribute("callsign");
119 return true;
120 }
121 if (type == "video")
122 {
123 QDomNodeList nodeList = e.elementsByTagName("videometadata");
124 if (nodeList.count() < 1)
125 {
126 LOG(VB_GENERAL, LOG_ERR,
127 "Couldn't find a 'videometadata' element in XML file");
128 return false;
129 }
130
131 n = nodeList.item(0);
132 e = n.toElement();
133 n = e.firstChild();
134 while (!n.isNull())
135 {
136 e = n.toElement();
137 if (!e.isNull())
138 {
139 if (e.tagName() == "title")
140 {
141 details->title = e.text();
142 details->subtitle = "";
143 details->startTime = QDateTime();
144 }
145
146 if (e.tagName() == "plot")
147 {
148 details->description = e.text();
149 }
150 }
151 n = n.nextSibling();
152 }
153
154 details->chanID = "N/A";
155 details->chanNo = "N/A";
156 details->chanName = "N/A";
157 details->callsign = "N/A";
158
159 return true;
160 }
161 }
162
163 return false;
164}
165
167
169 FileSelector(parent, nullptr, FSTYPE_FILE, "", "*.xml")
170{
171 m_curDirectory = gCoreContext->GetSetting("MythNativeLoadFilename", "/");
172}
173
175{
176 gCoreContext->SaveSetting("MythNativeLoadFilename", m_curDirectory);
177}
178
180{
181 // Load the theme for this screen
182 bool foundtheme = LoadWindowFromXML("mythnative-ui.xml", "archivefile_selector", this);
183 if (!foundtheme)
184 return false;
185
186 bool err = false;
187 UIUtilW::Assign(this, m_titleText, "title");
188 UIUtilE::Assign(this, m_fileButtonList, "filelist", &err);
189 UIUtilE::Assign(this, m_locationEdit, "location_edit", &err);
190 UIUtilE::Assign(this, m_backButton, "back_button", &err);
191 UIUtilE::Assign(this, m_homeButton, "home_button", &err);
192 UIUtilE::Assign(this, m_nextButton, "next_button", &err);
193 UIUtilE::Assign(this, m_prevButton, "prev_button", &err);
194 UIUtilE::Assign(this, m_cancelButton, "cancel_button", &err);
195 UIUtilE::Assign(this, m_progTitle, "title_text", &err);
196 UIUtilE::Assign(this, m_progSubtitle, "subtitle_text", &err);
197 UIUtilE::Assign(this, m_progStartTime, "starttime_text", &err);
198
199 if (err)
200 {
201 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'archivefile_selector'");
202 return false;
203 }
204
205 if (m_titleText)
206 m_titleText->SetText(tr("Find File To Import"));
207
211
215
218
221
224
226
228
231
232 return true;
233}
234
236{
237 m_xmlFile.clear();
238
239 if (!item)
240 return;
241
242 auto *fileData = item->GetData().value<FileData*>();
243 if (!fileData)
244 return;
245
246 if (loadDetailsFromXML(m_curDirectory + "/" + fileData->filename, &m_details))
247 {
248 m_xmlFile = m_curDirectory + "/" + fileData->filename;
252 .toString("dd MMM yy (hh:mm)"));
253 }
254 else
255 {
259 }
260}
261
263{
264 if (m_xmlFile == "")
265 {
266 ShowOkPopup(tr("The selected item is not a valid archive file!"));
267 }
268 else
269 {
271
272 auto *native = new ImportNative(mainStack, this, m_xmlFile, m_details);
273
274 if (native->Create())
275 mainStack->AddScreen(native);
276 }
277 // No memory leak. ImportNative (via MythUIType) adds the new item
278 // onto the parent mainStack.
279 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
280}
281
283{
284 Close();
285}
286
288{
289 Close();
290}
291
293
295{
296 // Load the theme for this screen
297 bool foundtheme = LoadWindowFromXML("mythnative-ui.xml", "importnative", this);
298 if (!foundtheme)
299 return false;
300
301 bool err = false;
302 UIUtilE::Assign(this, m_progTitleText, "progtitle", &err);
303 UIUtilE::Assign(this, m_progDateTimeText, "progdatetime", &err);
304 UIUtilE::Assign(this, m_progDescriptionText, "progdescription", &err);
305
306 UIUtilE::Assign(this, m_chanIDText, "chanid", &err);
307 UIUtilE::Assign(this, m_chanNoText, "channo", &err);
308 UIUtilE::Assign(this, m_chanNameText, "name", &err);
309 UIUtilE::Assign(this, m_callsignText, "callsign", &err);
310
311 UIUtilE::Assign(this, m_localChanIDText, "local_chanid", &err);
312 UIUtilE::Assign(this, m_localChanNoText, "local_channo", &err);
313 UIUtilE::Assign(this, m_localChanNameText, "local_name", &err);
314 UIUtilE::Assign(this, m_localCallsignText, "local_callsign", &err);
315
316 UIUtilE::Assign(this, m_searchChanIDButton, "searchchanid_button", &err);
317 UIUtilE::Assign(this, m_searchChanNoButton, "searchchanno_button", &err);
318 UIUtilE::Assign(this, m_searchChanNameButton, "searchname_button", &err);
319 UIUtilE::Assign(this, m_searchCallsignButton ,"searchcallsign_button", &err);
320
321 UIUtilE::Assign(this, m_finishButton, "finish_button", &err);
322 UIUtilE::Assign(this, m_prevButton, "prev_button", &err);
323 UIUtilE::Assign(this, m_cancelButton, "cancel_button", &err);
324
325 if (err)
326 {
327 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'importarchive'");
328 return false;
329 }
330
334
339
341
343 .toString("dd MMM yy (hh:mm)"));
345 (m_details.subtitle == "" ? m_details.subtitle + "\n" : "") + m_details.description);
346
351
354
356
358
359 return true;
360}
361
362bool ImportNative::keyPressEvent(QKeyEvent *event)
363{
364 if (GetFocusWidget()->keyPressEvent(event))
365 return true;
366
367 QStringList actions;
368 bool handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
369
370 for (int i = 0; i < actions.size() && !handled; i++)
371 {
372 const QString& action = actions[i];
373 handled = true;
374
375 if (action == "MENU")
376 {
377 }
378 else
379 {
380 handled = false;
381 }
382 }
383
384 if (!handled && MythScreenType::keyPressEvent(event))
385 handled = true;
386
387 return handled;
388}
389
391
392{
393 if (m_details.chanID != "N/A" && m_localChanIDText->GetText() == "")
394 {
395 ShowOkPopup(tr("You need to select a valid channel id!"));
396 return;
397 }
398
399 QString commandline;
400 QString tempDir = gCoreContext->GetSetting("MythArchiveTempDir", "");
401 QString chanID = m_localChanIDText->GetText();
402
403 if (chanID == "")
404 chanID = m_details.chanID;
405
406 if (tempDir == "")
407 return;
408
409 if (!tempDir.endsWith("/"))
410 tempDir += "/";
411
412 QString logDir = tempDir + "logs";
413
414 // remove any existing logs
415 myth_system("rm -f " + logDir + "/*.log");
416
417 commandline = "mytharchivehelper --logpath " + logDir + " --importarchive "
418 "--infile \"" + m_xmlFile +
419 "\" --chanid " + chanID;
420
423 uint retval = myth_system(commandline, flags);
424 if (retval != GENERIC_EXIT_RUNNING && retval != GENERIC_EXIT_OK)
425 {
426 ShowOkPopup(tr("It was not possible to import the Archive. "
427 " An error occured when running 'mytharchivehelper'") );
428 return;
429 }
430
432
434 Close();
435}
436
438{
439 Close();
440}
441
443{
445 Close();
446}
447
448void ImportNative::findChannelMatch(const QString &chanID, const QString &chanNo,
449 const QString &name, const QString &callsign)
450{
451 // find best match in channel table for this recording
452
453 // look for an exact match - maybe the user is importing back an old recording
455 query.prepare("SELECT chanid, channum, name, callsign FROM channel "
456 "WHERE chanid = :CHANID AND channum = :CHANNUM AND name = :NAME "
457 "AND callsign = :CALLSIGN;");
458 query.bindValue(":CHANID", chanID);
459 query.bindValue(":CHANNUM", chanNo);
460 query.bindValue(":NAME", name);
461 query.bindValue(":CALLSIGN", callsign);
462
463 if (query.exec() && query.next())
464 {
465 // got match
466 m_localChanIDText->SetText(query.value(0).toString());
467 m_localChanNoText->SetText(query.value(1).toString());
468 m_localChanNameText->SetText(query.value(2).toString());
469 m_localCallsignText->SetText(query.value(3).toString());
470 return;
471 }
472
473 // try to find callsign
474 query.prepare("SELECT chanid, channum, name, callsign FROM channel "
475 "WHERE callsign = :CALLSIGN;");
476 query.bindValue(":CALLSIGN", callsign);
477
478 if (query.exec() && query.next())
479 {
480 // got match
481 m_localChanIDText->SetText(query.value(0).toString());
482 m_localChanNoText->SetText(query.value(1).toString());
483 m_localChanNameText->SetText(query.value(2).toString());
484 m_localCallsignText->SetText(query.value(3).toString());
485 return;
486 }
487
488 // try to find name
489 query.prepare("SELECT chanid, channum, name, callsign FROM channel "
490 "WHERE name = :NAME;");
491 query.bindValue(":NAME", callsign);
492
493 if (query.exec() && query.next())
494 {
495 // got match
496 m_localChanIDText->SetText(query.value(0).toString());
497 m_localChanNoText->SetText(query.value(1).toString());
498 m_localChanNameText->SetText(query.value(2).toString());
499 m_localCallsignText->SetText(query.value(3).toString());
500 return;
501 }
502
503 // give up
508}
509
510void ImportNative::showList(const QString &caption, QString &value,
511 const INSlot slot)
512{
513 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
514
515 auto *searchDialog = new
516 MythUISearchDialog(popupStack, caption, m_searchList, true, value);
517
518 if (!searchDialog->Create())
519 {
520 delete searchDialog;
521 searchDialog = nullptr;
522 return;
523 }
524
525 connect(searchDialog, &MythUISearchDialog::haveResult, this, slot);
526
527 popupStack->AddScreen(searchDialog);
528}
529
530void ImportNative::fillSearchList(const QString &field)
531{
532
533 m_searchList.clear();
534
535 QString querystr;
536 querystr = QString("SELECT %1 FROM channel ORDER BY %2").arg(field, field);
537
539
540 if (query.exec(querystr))
541 {
542 while (query.next())
543 {
544 m_searchList << query.value(0).toString();
545 }
546 }
547}
548
550{
551 QString s;
552
553 fillSearchList("chanid");
554
555 s = m_chanIDText->GetText();
556 showList(tr("Select a channel id"), s, &ImportNative::gotChanID);
557}
558
559void ImportNative::gotChanID(const QString& value)
560{
562 query.prepare("SELECT chanid, channum, name, callsign "
563 "FROM channel WHERE chanid = :CHANID;");
564 query.bindValue(":CHANID", value);
565
566 if (query.exec() && query.next())
567 {
568 m_localChanIDText->SetText(query.value(0).toString());
569 m_localChanNoText->SetText(query.value(1).toString());
570 m_localChanNameText->SetText(query.value(2).toString());
571 m_localCallsignText->SetText(query.value(3).toString());
572 }
573}
574
576{
577 QString s;
578
579 fillSearchList("channum");
580
581 s = m_chanNoText->GetText();
582 showList(tr("Select a channel number"), s, &ImportNative::gotChanNo);
583}
584
585void ImportNative::gotChanNo(const QString& value)
586{
588 query.prepare("SELECT chanid, channum, name, callsign "
589 "FROM channel WHERE channum = :CHANNUM;");
590 query.bindValue(":CHANNUM", value);
591
592 if (query.exec() && query.next())
593 {
594 m_localChanIDText->SetText(query.value(0).toString());
595 m_localChanNoText->SetText(query.value(1).toString());
596 m_localChanNameText->SetText(query.value(2).toString());
597 m_localCallsignText->SetText(query.value(3).toString());
598 }
599}
600
602{
603 QString s;
604
605 fillSearchList("name");
606
607 s = m_chanNameText->GetText();
608 showList(tr("Select a channel name"), s, &ImportNative::gotName);
609}
610
611void ImportNative::gotName(const QString& value)
612{
614 query.prepare("SELECT chanid, channum, name, callsign "
615 "FROM channel WHERE name = :NAME;");
616 query.bindValue(":NAME", value);
617
618 if (query.exec() && query.next())
619 {
620 m_localChanIDText->SetText(query.value(0).toString());
621 m_localChanNoText->SetText(query.value(1).toString());
622 m_localChanNameText->SetText(query.value(2).toString());
623 m_localCallsignText->SetText(query.value(3).toString());
624 }
625}
626
628{
629 QString s;
630
631 fillSearchList("callsign");
632
633 s = m_callsignText->GetText();
634 showList(tr("Select a Callsign"), s, &ImportNative::gotCallsign);
635}
636
637void ImportNative::gotCallsign(const QString& value)
638{
640 query.prepare("SELECT chanid, channum, name, callsign "
641 "FROM channel WHERE callsign = :CALLSIGN;");
642 query.bindValue(":CALLSIGN", value);
643
644 if (query.exec() && query.next())
645 {
646 m_localChanIDText->SetText(query.value(0).toString());
647 m_localChanNoText->SetText(query.value(1).toString());
648 m_localChanNameText->SetText(query.value(2).toString());
649 m_localCallsignText->SetText(query.value(3).toString());
650 }
651}
MythUIText * m_progSubtitle
Definition: importnative.h:70
bool Create(void) override
void cancelPressed(void)
ArchiveFileSelector(MythScreenStack *parent)
MythUIText * m_progStartTime
Definition: importnative.h:71
void itemSelected(MythUIButtonListItem *item)
~ArchiveFileSelector(void) override
FileDetails m_details
Definition: importnative.h:64
MythUIText * m_progTitle
Definition: importnative.h:69
MythUIButton * m_nextButton
Definition: importnative.h:67
MythUIButton * m_prevButton
Definition: importnative.h:68
void updateSelectedList(void)
MythUIButtonList * m_fileButtonList
Definition: fileselector.h:87
QString m_curDirectory
Definition: fileselector.h:79
MythUIText * m_titleText
Definition: fileselector.h:86
void backPressed(void)
void homePressed(void)
MythUIButton * m_homeButton
Definition: fileselector.h:92
MythUITextEdit * m_locationEdit
Definition: fileselector.h:88
void updateFileList(void)
void locationEditLostFocus(void)
void itemClicked(MythUIButtonListItem *item)
MythUIButton * m_backButton
Definition: fileselector.h:91
MythUIButton * m_cancelButton
Definition: fileselector.h:90
void fillSearchList(const QString &field)
MythUIText * m_localChanNameText
Definition: importnative.h:127
void searchChanID(void)
MythUIButton * m_finishButton
Definition: importnative.h:135
void prevPressed()
bool keyPressEvent(QKeyEvent *e) override
Key event handler.
void showList(const QString &caption, QString &value, INSlot slot)
MythUIText * m_callsignText
Definition: importnative.h:123
MythUIButton * m_searchChanNoButton
Definition: importnative.h:131
void searchCallsign(void)
MythUIText * m_progDescriptionText
Definition: importnative.h:118
QString m_xmlFile
Definition: importnative.h:110
void finishedPressed()
MythScreenType * m_previousScreen
Definition: importnative.h:112
MythUIButton * m_searchChanIDButton
Definition: importnative.h:130
bool Create(void) override
void findChannelMatch(const QString &chanid, const QString &chanNo, const QString &name, const QString &callsign)
MythUIButton * m_cancelButton
Definition: importnative.h:137
MythUIText * m_localCallsignText
Definition: importnative.h:128
void gotCallsign(const QString &value)
void(ImportNative::*)(const QString &) INSlot
Definition: importnative.h:107
MythUIText * m_chanIDText
Definition: importnative.h:120
void gotChanID(const QString &value)
MythUIText * m_chanNoText
Definition: importnative.h:121
MythUIText * m_progTitleText
Definition: importnative.h:116
MythUIText * m_chanNameText
Definition: importnative.h:122
MythUIButton * m_searchCallsignButton
Definition: importnative.h:133
void searchName(void)
void gotName(const QString &value)
void cancelPressed()
MythUIText * m_localChanIDText
Definition: importnative.h:125
MythUIText * m_localChanNoText
Definition: importnative.h:126
MythUIButton * m_prevButton
Definition: importnative.h:136
QStringList m_searchList
Definition: importnative.h:114
void gotChanNo(const QString &value)
MythUIText * m_progDateTimeText
Definition: importnative.h:117
MythUIButton * m_searchChanNameButton
Definition: importnative.h:132
void searchChanNo(void)
FileDetails m_details
Definition: importnative.h:111
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
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 SaveSetting(const QString &key, int newValue)
QString GetSetting(const QString &key, const QString &defaultval="")
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)
void BuildFocusList(void)
MythUIType * GetFocusWidget(void) const
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
bool SetFocusWidget(MythUIType *widget=nullptr)
virtual void Close()
void itemClicked(MythUIButtonListItem *item)
void itemSelected(MythUIButtonListItem *item)
void Clicked()
Provide a dialog to quickly find an entry in a list.
void haveResult(QString)
void SetText(const QString &text, bool moveCursor=true)
QString GetText(void) const
Definition: mythuitext.h:43
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitext.cpp:65
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:115
void LosingFocus(void)
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
@ GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:13
@ GENERIC_EXIT_RUNNING
Process is running.
Definition: exitcodes.h:28
@ FSTYPE_FILE
Definition: fileselector.h:29
unsigned int uint
Definition: freesurround.h:24
static bool loadDetailsFromXML(const QString &filename, FileDetails *details)
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()
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
@ 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)
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:39
QString chanID
Definition: importnative.h:35
QString title
Definition: importnative.h:31
QString chanNo
Definition: importnative.h:36
QString callsign
Definition: importnative.h:38
QString chanName
Definition: importnative.h:37
QDateTime startTime
Definition: importnative.h:33
QString subtitle
Definition: importnative.h:32
QString description
Definition: importnative.h:34
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27