MythTV  master
channelimporter.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 /*
3  * Copyright (C) Daniel Kristjansson 2007
4  *
5  * This file is licensed under GPL v2 or (at your option) any later version.
6  *
7  */
8 
9 // C++ includes
10 #include <iostream>
11 #include <utility>
12 #include <algorithm>
13 
14 // Qt includes
15 #include <QTextStream>
16 #include <QElapsedTimer>
17 
18 // MythTV headers
19 #include "channelimporter.h"
20 #include "mythdialogbox.h"
21 #include "mythdb.h"
22 #include "mpegstreamdata.h" // for kEncDecrypted
23 #include "channelutil.h"
24 
25 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
26  #define QT_ENDL endl
27 #else
28  #define QT_ENDL Qt::endl
29 #endif
30 
31 #define LOC QString("ChanImport: ")
32 
33 static const QString kATSCChannelFormat = "%1_%2";
34 
35 static QString map_str(QString str)
36 {
37  if (str.isEmpty())
38  return "";
39  return str;
40 }
41 
43  int sourceid)
44 {
45  if (_transports.empty())
46  {
47  if (m_useGui)
48  {
49  int channels = ChannelUtil::GetChannelCount(sourceid);
50 
51  LOG(VB_GENERAL, LOG_INFO, LOC + (channels ?
52  (m_success ?
53  QString("Found %1 channels")
54  .arg(channels) :
55  "No new channels to process") :
56  "No channels to process.."));
57 
59  channels ?
60  (m_success ? tr("Found %n channel(s)", "", channels) :
61  tr("Failed to find any new channels!"))
62  : tr("Failed to find any channels."));
63  }
64  else
65  {
66  std::cout << (ChannelUtil::GetChannelCount() ?
67  "No new channels to process" :
68  "No channels to process..");
69  }
70 
71  return;
72  }
73 
74  ScanDTVTransportList transports = _transports;
75  QString msg;
76  QTextStream ssMsg(&msg);
77 
78  // Scan parameters
79  {
80  bool require_av = (m_serviceRequirements & kRequireAV) == kRequireAV;
81  bool require_a = (m_serviceRequirements & kRequireAudio) != 0;
82  ssMsg << QT_ENDL << QT_ENDL;
83  ssMsg << "Scan parameters:" << QT_ENDL;
84  ssMsg << "Desired Services : " << (require_av ? "tv" : require_a ? "tv+radio" : "all") << QT_ENDL;
85  ssMsg << "Unencrypted Only : " << (m_ftaOnly ? "yes" : "no") << QT_ENDL;
86  ssMsg << "Logical Channel Numbers only: " << (m_lcnOnly ? "yes" : "no") << QT_ENDL;
87  ssMsg << "Complete scan data required : " << (m_completeOnly ? "yes" : "no") << QT_ENDL;
88  ssMsg << "Full search for old channels: " << (m_fullChannelSearch ? "yes" : "no") << QT_ENDL;
89  ssMsg << "Remove duplicates : " << (m_removeDuplicates ? "yes" : "no") << QT_ENDL;
90  }
91 
92  // Transports and channels before processing
93  if (!transports.empty())
94  {
95  ssMsg << QT_ENDL;
96  ssMsg << "Transport list before processing (" << transports.size() << "):" << QT_ENDL;
97  ssMsg << FormatTransports(transports).toLatin1().constData();
98 
99  ChannelImporterBasicStats info = CollectStats(transports);
100  ssMsg << QT_ENDL;
101  ssMsg << "Channel list before processing (";
102  ssMsg << SimpleCountChannels(transports) << "):" << QT_ENDL;
103  ssMsg << FormatChannels(transports, &info).toLatin1().constData();
104  }
105  LOG(VB_GENERAL, LOG_INFO, LOC + msg);
106 
107  uint saved_scan = 0;
108  if (m_doSave)
109  saved_scan = SaveScan(transports);
110 
111  // Merge transports with the same frequency into one
112  MergeSameFrequency(transports);
113 
114  // Remove duplicate transports with a lower signal strength.
115  if (m_removeDuplicates)
116  {
117  ScanDTVTransportList duplicates;
118  RemoveDuplicates(transports, duplicates);
119  if (!duplicates.empty())
120  {
121  msg = "";
122  ssMsg << QT_ENDL;
123  ssMsg << "Discarded duplicate transports (" << duplicates.size() << "):" << QT_ENDL;
124  ssMsg << FormatTransports(duplicates).toLatin1().constData() << QT_ENDL;
125  ssMsg << "Discarded duplicate channels (" << SimpleCountChannels(duplicates) << "):" << QT_ENDL;
126  ssMsg << FormatChannels(duplicates).toLatin1().constData() << QT_ENDL;
127  LOG(VB_CHANSCAN, LOG_INFO, LOC + msg);
128  }
129  }
130 
131  // Remove the channels that do not pass various criteria.
132  FilterServices(transports);
133 
134  // Remove the channels that have been relocated.
135  if (m_removeDuplicates)
136  {
137  FilterRelocatedServices(transports);
138  }
139 
140  // Pull in DB info in transports
141  // Channels not found in scan but only in DB are returned in db_trans
142  sourceid = transports[0].m_channels[0].m_sourceId;
143  ScanDTVTransportList db_trans = GetDBTransports(sourceid, transports);
144  msg = "";
145  ssMsg << QT_ENDL;
146  if (!db_trans.empty())
147  {
148  ssMsg << QT_ENDL;
149  ssMsg << "Transports with channels in DB but not in scan (";
150  ssMsg << db_trans.size() << "):" << QT_ENDL;
151  ssMsg << FormatTransports(db_trans).toLatin1().constData();
152  }
153 
154  // Make sure "Open Cable" channels are marked that way.
155  FixUpOpenCable(transports);
156 
157  // All channels in the scan after comparing with the database
158  {
159  ChannelImporterBasicStats info = CollectStats(transports);
160  ssMsg << QT_ENDL;
161  ssMsg << "Channel list after compare with database (";
162  ssMsg << SimpleCountChannels(transports) << "):" << QT_ENDL;
163  ssMsg << FormatChannels(transports, &info).toLatin1().constData();
164  }
165 
166  // Add channels from the DB to the channels from the scan
167  // and possibly delete one or more of the off-air channels
168  if (m_doDelete)
169  {
170  ScanDTVTransportList trans = transports;
171  std::copy(db_trans.cbegin(), db_trans.cend(), std::back_inserter(trans));
172  uint deleted_count = DeleteChannels(trans);
173  if (deleted_count)
174  transports = trans;
175  }
176 
177  // Determine System Info standards..
178  ChannelImporterBasicStats info = CollectStats(transports);
179 
180  // Determine uniqueness of various naming schemes
182  CollectUniquenessStats(transports, info);
183 
184  // Final channel list
185  ssMsg << QT_ENDL;
186  ssMsg << "Channel list (" << SimpleCountChannels(transports) << "):" << QT_ENDL;
187  ssMsg << FormatChannels(transports).toLatin1().constData();
188 
189  // Create summary
190  ssMsg << QT_ENDL;
191  ssMsg << GetSummary(info, stats) << QT_ENDL;
192 
193  LOG(VB_GENERAL, LOG_INFO, LOC + msg);
194 
195  if (m_doInsert)
196  InsertChannels(transports, info);
197 
198  if (m_doDelete && sourceid)
199  DeleteUnusedTransports(sourceid);
200 
201  if (m_doDelete || m_doInsert)
202  ScanInfo::MarkProcessed(saved_scan);
203 }
204 
206 {
207  switch (type)
208  {
209  // non-conflicting
210  case kATSCNonConflicting: return "ATSC";
211  case kDVBNonConflicting: return "DVB";
212  case kSCTENonConflicting: return "SCTE";
213  case kMPEGNonConflicting: return "MPEG";
214  case kNTSCNonConflicting: return "NTSC";
215  // conflicting
216  case kATSCConflicting: return "ATSC";
217  case kDVBConflicting: return "DVB";
218  case kSCTEConflicting: return "SCTE";
219  case kMPEGConflicting: return "MPEG";
220  case kNTSCConflicting: return "NTSC";
221  }
222  return "Unknown";
223 }
224 
225 // Ask user what to do with the off-air channels
226 //
228  ScanDTVTransportList &transports)
229 {
230  std::vector<uint> off_air_list;
231  QMap<uint,bool> deleted;
232  ScanDTVTransportList off_air_transports;
233 
234  for (size_t i = 0; i < transports.size(); ++i)
235  {
236  ScanDTVTransport transport_copy;
237  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
238  {
239  ChannelInsertInfo chan = transports[i].m_channels[j];
240  bool was_in_db = (chan.m_dbMplexId != 0U) && (chan.m_channelId != 0U);
241  if (!was_in_db)
242  continue;
243 
244  if (!chan.m_inPmt)
245  {
246  off_air_list.push_back(i<<16|j);
247  AddChanToCopy(transport_copy, transports[i], chan);
248  }
249  }
250  if (!transport_copy.m_channels.empty())
251  off_air_transports.push_back(transport_copy);
252  }
253 
254  if (off_air_list.empty())
255  return 0;
256 
257  // List of off-air channels (in database but not in the scan)
258  std::cout << std::endl << "Off-air channels (" << SimpleCountChannels(off_air_transports) << "):" << std::endl;
259  ChannelImporterBasicStats infoA = CollectStats(off_air_transports);
260  std::cout << FormatChannels(off_air_transports, &infoA).toLatin1().constData() << std::endl;
261 
262  // Ask user whether to delete all or some of these stale channels
263  // if some is selected ask about each individually
264  //: %n is the number of channels
265  QString msg = tr("Found %n off-air channel(s).", "", off_air_list.size());
267  if (kDeleteIgnoreAll == action)
268  return 0;
269 
270  if (kDeleteAll == action)
271  {
272  for (uint item : off_air_list)
273  {
274  int i = item >> 16;
275  int j = item & 0xFFFF;
277  transports[i].m_channels[j].m_channelId);
278  deleted[item] = true;
279  }
280  }
281  else if (kDeleteInvisibleAll == action)
282  {
283  for (uint item : off_air_list)
284  {
285  int i = item >> 16;
286  int j = item & 0xFFFF;
287  int chanid = transports[i].m_channels[j].m_channelId;
288  QString channum = ChannelUtil::GetChanNum(chanid);
290  ChannelUtil::SetChannelValue("channum", QString("_%1").arg(channum),
291  chanid);
292  }
293  }
294  else
295  {
296  // TODO manual delete
297  }
298 
299  // TODO delete encrypted channels when m_ftaOnly set
300 
301  if (deleted.empty())
302  return 0;
303 
304  // Create a new transports list without the deleted channels
305  ScanDTVTransportList newlist;
306  for (size_t i = 0; i < transports.size(); ++i)
307  {
308  newlist.push_back(transports[i]);
309  newlist.back().m_channels.clear();
310  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
311  {
312  if (!deleted.contains(i<<16|j))
313  {
314  newlist.back().m_channels.push_back(
315  transports[i].m_channels[j]);
316  }
317  }
318  }
319 
320  transports = newlist;
321  return deleted.size();
322 }
323 
325 {
327  query.prepare(
328  "SELECT mplexid FROM dtv_multiplex "
329  "WHERE sourceid = :SOURCEID1 AND "
330  " mplexid NOT IN "
331  " (SELECT mplexid "
332  " FROM channel "
333  " WHERE sourceid = :SOURCEID2)");
334  query.bindValue(":SOURCEID1", sourceid);
335  query.bindValue(":SOURCEID2", sourceid);
336  if (!query.exec())
337  {
338  MythDB::DBError("DeleteUnusedTransports() -- select", query);
339  return 0;
340  }
341 
342  QString msg = tr("Found %n unused transport(s).", "", query.size());
343 
344  LOG(VB_GENERAL, LOG_INFO, LOC + msg);
345 
346  if (query.size() == 0)
347  return 0;
348 
350  if (kDeleteIgnoreAll == action)
351  return 0;
352 
353  if (kDeleteAll == action)
354  {
355  query.prepare(
356  "DELETE FROM dtv_multiplex "
357  "WHERE sourceid = :SOURCEID1 AND "
358  " mplexid NOT IN "
359  " (SELECT mplexid "
360  " FROM channel "
361  " WHERE sourceid = :SOURCEID2)");
362  query.bindValue(":SOURCEID1", sourceid);
363  query.bindValue(":SOURCEID2", sourceid);
364  if (!query.exec())
365  {
366  MythDB::DBError("DeleteUnusedTransports() -- delete", query);
367  return 0;
368  }
369  }
370  else
371  {
372  // TODO manual delete
373  LOG(VB_GENERAL, LOG_INFO, LOC + "Manual delete of transport not implemented");
374  }
375  return 0;
376 }
377 
379  const ScanDTVTransportList &transports,
380  const ChannelImporterBasicStats &info)
381 {
382  ScanDTVTransportList list = transports;
383  ScanDTVTransportList inserted;
384  ScanDTVTransportList updated;
385  ScanDTVTransportList skipped_inserts;
386  ScanDTVTransportList skipped_updates;
387 
388  // Insert or update all channels with non-conflicting channum
389  // and complete tuning information.
391  for (; chantype <= (uint) kChannelTypeNonConflictingLast; ++chantype)
392  {
393  auto type = (ChannelType) chantype;
394  uint new_chan = 0;
395  uint old_chan = 0;
396  CountChannels(list, info, type, new_chan, old_chan);
397 
398  if (kNTSCNonConflicting == type)
399  continue;
400 
401  if (old_chan)
402  {
403  //: %n is the number of channels, %1 is the type of channel
404  QString msg = tr("Found %n old %1 channel(s).", "", old_chan)
405  .arg(toString(type));
406 
408  list = UpdateChannels(list, info, action, type, updated, skipped_updates);
409  }
410  if (new_chan)
411  {
412  //: %n is the number of channels, %1 is the type of channel
413  QString msg = tr("Found %n new %1 channel(s).", "", new_chan)
414  .arg(toString(type));
415 
417  list = InsertChannels(list, info, action, type, inserted, skipped_inserts);
418  }
419  }
420 
421  if (!m_isInteractive)
422  return;
423 
424  // If any of the potential uniques is high and inserting
425  // with those as the channum would result in few conflicts
426  // ask user if it is ok to to proceed using it as the channum
427 
428  // For remaining channels with complete tuning information
429  // insert channels with contiguous list of numbers as the channums
430  chantype = (uint) kChannelTypeConflictingFirst;
431  for (; chantype <= (uint) kChannelTypeConflictingLast; ++chantype)
432  {
433  auto type = (ChannelType) chantype;
434  uint new_chan = 0;
435  uint old_chan = 0;
436  CountChannels(list, info, type, new_chan, old_chan);
437 
438  if (old_chan)
439  {
440  //: %n is the number of channels, %1 is the type of channel
441  QString msg = tr("Found %n conflicting old %1 channel(s).",
442  "", old_chan).arg(toString(type));
443 
445  list = UpdateChannels(list, info, action, type, updated, skipped_updates);
446  }
447  if (new_chan)
448  {
449  //: %n is the number of channels, %1 is the type of channel
450  QString msg = tr("Found %n new conflicting %1 channel(s).",
451  "", new_chan).arg(toString(type));
452 
454  list = InsertChannels(list, info, action, type, inserted, skipped_inserts);
455  }
456  }
457 
458  QString msg;
459  QTextStream ssMsg(&msg);
460 
461  if (!updated.empty())
462  {
463  ssMsg << QT_ENDL << QT_ENDL;
464  ssMsg << "Updated old transports (" << updated.size() << "):" << QT_ENDL;
465  ssMsg << FormatTransports(updated).toLatin1().constData();
466 
467  ssMsg << QT_ENDL;
468  ssMsg << "Updated old channels (" << SimpleCountChannels(updated) << "):" << QT_ENDL;
469  ssMsg << FormatChannels(updated).toLatin1().constData();
470  }
471  if (!skipped_updates.empty())
472  {
473  ssMsg << QT_ENDL;
474  ssMsg << "Skipped old channels (" << SimpleCountChannels(skipped_updates) << "):" << QT_ENDL;
475  ssMsg << FormatChannels(skipped_updates).toLatin1().constData();
476  }
477  if (!inserted.empty())
478  {
479  ssMsg << QT_ENDL;
480  ssMsg << "Inserted new channels (" << SimpleCountChannels(inserted) << "):" << QT_ENDL;
481  ssMsg << FormatChannels(inserted).toLatin1().constData();
482  }
483  if (!skipped_inserts.empty())
484  {
485  ssMsg << QT_ENDL;
486  ssMsg << "Skipped new channels (" << SimpleCountChannels(skipped_inserts) << "):" << QT_ENDL;
487  ssMsg << FormatChannels(skipped_inserts).toLatin1().constData();
488  }
489 
490  // Remaining channels and sum uniques again
491  if (!list.empty())
492  {
495  ssMsg << QT_ENDL;
496  ssMsg << "Remaining channels (" << SimpleCountChannels(list) << "):" << QT_ENDL;
497  ssMsg << FormatChannels(list).toLatin1().constData() << QT_ENDL;
498  ssMsg << GetSummary(ninfo, nstats).toLatin1().constData();
499  }
500  LOG(VB_GENERAL, LOG_INFO, LOC + msg);
501 }
502 
503 // ChannelImporter::InsertChannels
504 //
505 // transports List of channels to insert
506 // info Channel statistics
507 // action Insert all, Insert manually, Ignore all
508 // type Channel type such as dvb or atsc
509 // inserted_list List of inserted channels
510 // skipped_list List of skipped channels
511 //
512 // return: List of channels that have not been inserted
513 //
515  const ScanDTVTransportList &transports,
516  const ChannelImporterBasicStats &info,
519  ScanDTVTransportList &inserted_list,
520  ScanDTVTransportList &skipped_list)
521 {
522  ScanDTVTransportList next_list;
523 
524  bool cancel_all = false;
525  bool ok_all = false;
526 
527  // Insert all channels with non-conflicting channum
528  // and complete tuning information.
529  for (const auto & transport : transports)
530  {
531  ScanDTVTransport new_transport;
532  ScanDTVTransport inserted_transport;
533  ScanDTVTransport skipped_transport;
534 
535  for (size_t j = 0; j < transport.m_channels.size(); ++j)
536  {
537  ChannelInsertInfo chan = transport.m_channels[j];
538 
539  bool asked = false;
540  bool filter = false;
541  bool handle = false;
542  if (!chan.m_channelId && (kInsertIgnoreAll == action) &&
543  IsType(info, chan, type))
544  {
545  filter = true;
546  }
547  else if (!chan.m_channelId && IsType(info, chan, type))
548  {
549  handle = true;
550  }
551 
552  if (cancel_all)
553  {
554  handle = false;
555  }
556 
557  if (handle && kInsertManual == action)
558  {
559  OkCancelType rc = QueryUserInsert(transport, chan);
560  if (kOCTCancelAll == rc)
561  {
562  cancel_all = true;
563  handle = false;
564  }
565  else if (kOCTCancel == rc)
566  {
567  handle = false;
568  }
569  else if (kOCTOk == rc)
570  {
571  asked = true;
572  }
573  }
574 
575  if (handle)
576  {
577  bool conflicting = false;
578 
579  if (chan.m_chanNum.isEmpty())
580  {
581  if ((kATSCNonConflicting == type) ||
582  (kATSCConflicting == type))
583  {
585  .arg(chan.m_atscMajorChannel)
586  .arg(chan.m_atscMinorChannel);
587  }
588  else
589  {
590  chan.m_chanNum = QString("%1").arg(chan.m_serviceId);
591  }
592  }
593  conflicting = ChannelUtil::IsConflicting(
594  chan.m_chanNum, chan.m_sourceId);
595 
596  // Only ask if not already asked before with kInsertManual
597  if (m_isInteractive && !asked &&
598  (conflicting || (kChannelTypeConflictingFirst <= type)))
599  {
600  bool ok_done = false;
601  if (ok_all)
602  {
603  QString val = ComputeSuggestedChannelNum(chan);
604  bool ok = CheckChannelNumber(val, chan);
605  if (ok)
606  {
607  chan.m_chanNum = val;
608  conflicting = false;
609  ok_done = true;
610  }
611  }
612  if (!ok_done)
613  {
614  OkCancelType rc =
615  QueryUserResolve(transport, chan);
616 
617  conflicting = true;
618  if (kOCTCancelAll == rc)
619  {
620  cancel_all = true;
621  }
622  else if (kOCTOk == rc)
623  {
624  conflicting = false;
625  }
626  else if (kOCTOkAll == rc)
627  {
628  conflicting = false;
629  ok_all = true;
630  }
631  }
632  }
633 
634  if (conflicting)
635  {
636  handle = false;
637  }
638  }
639 
640  bool inserted = false;
641  if (handle)
642  {
643  int chanid = ChannelUtil::CreateChanID(
644  chan.m_sourceId, chan.m_chanNum);
645 
646  chan.m_channelId = (chanid > 0) ? chanid : 0;
647 
648  if (chan.m_channelId)
649  {
650  uint tsid = chan.m_vctTsId;
651  tsid = (tsid) ? tsid : chan.m_sdtTsId;
652  tsid = (tsid) ? tsid : chan.m_patTsId;
653  tsid = (tsid) ? tsid : chan.m_vctChanTsId;
654 
656  chan.m_sourceId, transport, tsid, chan.m_origNetId);
657  }
658 
659  if (chan.m_channelId && chan.m_dbMplexId)
660  {
661  chan.m_channelId = chanid;
662 
663  inserted = ChannelUtil::CreateChannel(
664  chan.m_dbMplexId,
665  chan.m_sourceId,
666  chan.m_channelId,
667  chan.m_callSign,
668  chan.m_serviceName,
669  chan.m_chanNum,
670  chan.m_serviceId,
671  chan.m_atscMajorChannel,
672  chan.m_atscMinorChannel,
673  chan.m_useOnAirGuide,
675  chan.m_freqId,
676  QString(),
677  chan.m_format,
678  QString(),
679  chan.m_defaultAuthority,
680  chan.m_serviceType);
681 
682  if (!transport.m_iptvTuning.GetDataURL().isEmpty())
684  transport.m_iptvTuning);
685  }
686  }
687 
688  if (inserted)
689  {
690  // Update list of inserted channels
691  AddChanToCopy(inserted_transport, transport, chan);
692  }
693 
694  if (filter)
695  {
696  // Update list of skipped channels
697  AddChanToCopy(skipped_transport, transport, chan);
698  }
699  else if (!inserted)
700  {
701  // Update list of remaining channels
702  AddChanToCopy(new_transport, transport, chan);
703  }
704  }
705 
706  if (!new_transport.m_channels.empty())
707  next_list.push_back(new_transport);
708 
709  if (!skipped_transport.m_channels.empty())
710  skipped_list.push_back(skipped_transport);
711 
712  if (!inserted_transport.m_channels.empty())
713  inserted_list.push_back(inserted_transport);
714  }
715 
716  return next_list;
717 }
718 
719 // ChannelImporter::UpdateChannels
720 //
721 // transports List of channels to update
722 // info Channel statistics
723 // action Update All, Ignore All
724 // type Channel type such as dvb or atsc
725 // updated_list List of updated channels
726 // skipped_list List of skipped channels
727 //
728 // return: List of channels that have not been updated
729 //
731  const ScanDTVTransportList &transports,
732  const ChannelImporterBasicStats &info,
735  ScanDTVTransportList &updated_list,
736  ScanDTVTransportList &skipped_list) const
737 {
738  ScanDTVTransportList next_list;
739 
740  // update all channels with non-conflicting channum
741  // and complete tuning information.
742  for (const auto & transport : transports)
743  {
744  ScanDTVTransport new_transport;
745  ScanDTVTransport updated_transport;
746  ScanDTVTransport skipped_transport;
747 
748  for (size_t j = 0; j < transport.m_channels.size(); ++j)
749  {
750  ChannelInsertInfo chan = transport.m_channels[j];
751 
752  bool filter = false;
753  bool handle = false;
754  if (chan.m_channelId && (kUpdateIgnoreAll == action) &&
755  IsType(info, chan, type))
756  {
757  filter = true;
758  }
759  else if (chan.m_channelId && IsType(info, chan, type))
760  {
761  handle = true;
762  }
763 
764  if (handle)
765  {
766  bool conflicting = false;
767 
769  {
771  }
772  if (chan.m_chanNum.isEmpty())
773  {
774  if ((kATSCNonConflicting == type) ||
775  (kATSCConflicting == type))
776  {
778  .arg(chan.m_atscMajorChannel)
779  .arg(chan.m_atscMinorChannel);
780  }
781  else
782  {
783  chan.m_chanNum = QString("%1").arg(chan.m_serviceId);
784  }
785  }
786  conflicting = ChannelUtil::IsConflicting(
787  chan.m_chanNum, chan.m_sourceId, chan.m_channelId);
788 
789  if (conflicting)
790  {
791  handle = false;
792 
793  // Update list of skipped channels
794  AddChanToCopy(skipped_transport, transport, chan);
795  }
796  }
797 
798  bool updated = false;
799  if (handle)
800  {
802 
803  // Find the matching multiplex. This updates the
804  // transport and network ID's in case the transport
805  // was created manually
806  uint tsid = chan.m_vctTsId;
807  tsid = (tsid) ? tsid : chan.m_sdtTsId;
808  tsid = (tsid) ? tsid : chan.m_patTsId;
809  tsid = (tsid) ? tsid : chan.m_vctChanTsId;
810 
812  chan.m_sourceId, transport, tsid, chan.m_origNetId);
813 
814  updated = ChannelUtil::UpdateChannel(
815  chan.m_dbMplexId,
816  chan.m_sourceId,
817  chan.m_channelId,
818  chan.m_callSign,
819  chan.m_serviceName,
820  chan.m_chanNum,
821  chan.m_serviceId,
822  chan.m_atscMajorChannel,
823  chan.m_atscMinorChannel,
824  chan.m_useOnAirGuide,
825  ((chan.m_visible == kChannelAlwaysVisible ||
827  chan.m_visible :
829  chan.m_freqId,
830  QString(),
831  chan.m_format,
832  QString(),
833  chan.m_defaultAuthority,
834  chan.m_serviceType);
835  }
836 
837  if (updated)
838  {
839  // Update list of updated channels
840  AddChanToCopy(updated_transport, transport, chan);
841  }
842 
843  if (filter)
844  {
845  // Update list of skipped channels
846  AddChanToCopy(skipped_transport, transport, chan);
847  }
848  else if (!updated)
849  {
850  // Update list of remaining channels
851  AddChanToCopy(new_transport, transport, chan);
852  }
853  }
854 
855  if (!new_transport.m_channels.empty())
856  next_list.push_back(new_transport);
857 
858  if (!skipped_transport.m_channels.empty())
859  skipped_list.push_back(skipped_transport);
860 
861  if (!updated_transport.m_channels.empty())
862  updated_list.push_back(updated_transport);
863  }
864 
865  return next_list;
866 }
867 
868 // ChannelImporter::AddChanToCopy
869 //
870 // Add channel to copy of transport.
871 // This is used to keep track of what is done with each channel
872 //
873 // transport_copy with zero to all channels of transport
874 // transport transport with channel info as scanned
875 // chan one channel of transport, to be copied
876 //
878  ScanDTVTransport &transport_copy,
879  const ScanDTVTransport &transport,
880  const ChannelInsertInfo &chan
881 )
882 {
883  if (transport_copy.m_channels.empty())
884  {
885  transport_copy = transport;
886  transport_copy.m_channels.clear();
887  }
888  transport_copy.m_channels.push_back(chan);
889 }
890 
891 // ChannelImporter::MergeSameFrequency
892 //
893 // Merge transports that are on the same frequency by
894 // combining all channels of both transports into one transport
895 //
897 {
898  ScanDTVTransportList no_dups;
899 
901  if (!transports.empty())
902  tuner_type = transports[0].m_tuner_type;
903 
904  bool is_dvbs = ((DTVTunerType::kTunerTypeDVBS1 == tuner_type) ||
905  (DTVTunerType::kTunerTypeDVBS2 == tuner_type));
906 
907  uint freq_mult = (is_dvbs) ? 1 : 1000;
908 
909  std::vector<bool> ignore;
910  ignore.resize(transports.size());
911  for (size_t i = 0; i < transports.size(); ++i)
912  {
913  if (ignore[i])
914  continue;
915 
916  for (size_t j = i+1; j < transports.size(); ++j)
917  {
918  if (!transports[i].IsEqual(
919  tuner_type, transports[j], 500 * freq_mult))
920  {
921  continue;
922  }
923 
924  for (size_t k = 0; k < transports[j].m_channels.size(); ++k)
925  {
926  bool found_same = false;
927  for (size_t l = 0; l < transports[i].m_channels.size(); ++l)
928  {
929  if (transports[j].m_channels[k].IsSameChannel(
930  transports[i].m_channels[l]))
931  {
932  found_same = true;
933  transports[i].m_channels[l].ImportExtraInfo(
934  transports[j].m_channels[k]);
935  }
936  }
937  if (!found_same)
938  transports[i].m_channels.push_back(transports[j].m_channels[k]);
939  }
940  LOG(VB_CHANSCAN, LOG_INFO, LOC +
941  QString("Transport on same frequency:") + FormatTransport(transports[j]));
942  ignore[j] = true;
943  }
944  no_dups.push_back(transports[i]);
945  }
946  transports = no_dups;
947 }
948 
949 // ChannelImporter::RemoveDuplicates
950 //
951 // When there are two transports that have the same list of channels
952 // but that are received on different frequencies then remove
953 // the transport with the weakest signal.
954 //
955 // In DVB two transports are duplicates when the original network ID and the
956 // transport ID are the same. This is possibly different in ATSC.
957 // Here all channels of both transports are compared.
958 //
960 {
961  LOG(VB_CHANSCAN, LOG_INFO, LOC +
962  QString("Number of transports:%1").arg(transports.size()));
963 
964  ScanDTVTransportList no_dups;
965  std::vector<bool> ignore;
966  ignore.resize(transports.size());
967  for (size_t i = 0; i < transports.size(); ++i)
968  {
969  ScanDTVTransport &ta = transports[i];
970  LOG(VB_CHANSCAN, LOG_INFO, LOC + "Transport " +
971  FormatTransport(ta) + QString(" size(%1)").arg(ta.m_channels.size()));
972 
973  if (!ignore[i])
974  {
975  for (size_t j = i+1; j < transports.size(); ++j)
976  {
977  ScanDTVTransport &tb = transports[j];
978  bool found_same = true;
979  bool found_diff = true;
980  if (ta.m_channels.size() == tb.m_channels.size())
981  {
982  LOG(VB_CHANSCAN, LOG_DEBUG, LOC + "Comparing transport A " +
983  FormatTransport(ta) + QString(" size(%1)").arg(ta.m_channels.size()));
984  LOG(VB_CHANSCAN, LOG_DEBUG, LOC + "Comparing transport B " +
985  FormatTransport(tb) + QString(" size(%1)").arg(tb.m_channels.size()));
986 
987  for (size_t k = 0; found_same && k < tb.m_channels.size(); ++k)
988  {
989  if (tb.m_channels[k].IsSameChannel(ta.m_channels[k], 0))
990  {
991  found_diff = false;
992  }
993  else
994  {
995  found_same = false;
996  }
997  }
998  }
999 
1000  // Transport with the lowest signal strength is duplicate
1001  if (found_same && !found_diff)
1002  {
1003  size_t lowss = transports[i].m_signalStrength < transports[j].m_signalStrength ? i : j;
1004  ignore[lowss] = true;
1005  duplicates.push_back(transports[lowss]);
1006 
1007  LOG(VB_CHANSCAN, LOG_INFO, LOC + "Duplicate transports found:");
1008  LOG(VB_CHANSCAN, LOG_INFO, LOC + "Transport A " + FormatTransport(transports[i]));
1009  LOG(VB_CHANSCAN, LOG_INFO, LOC + "Transport B " + FormatTransport(transports[j]));
1010  LOG(VB_CHANSCAN, LOG_INFO, LOC + "Discarding " + FormatTransport(transports[lowss]));
1011  }
1012  }
1013  }
1014  if (!ignore[i])
1015  {
1016  no_dups.push_back(transports[i]);
1017  }
1018  }
1019 
1020  transports = no_dups;
1021 }
1022 
1024 {
1025  bool require_av = (m_serviceRequirements & kRequireAV) == kRequireAV;
1026  bool require_a = (m_serviceRequirements & kRequireAudio) != 0;
1027 
1028  for (auto & transport : transports)
1029  {
1030  ChannelInsertInfoList filtered;
1031  for (auto & channel : transport.m_channels)
1032  {
1033  if (m_ftaOnly && channel.m_isEncrypted &&
1034  channel.m_decryptionStatus != kEncDecrypted)
1035  continue;
1036 
1037  if (require_a && channel.m_isDataService)
1038  continue;
1039 
1040  if (require_av && channel.m_isAudioService)
1041  continue;
1042 
1043  // Filter channels out that do not have a logical channel number
1044  if (m_lcnOnly && channel.m_chanNum.isEmpty())
1045  {
1046  QString msg = FormatChannel(transport, channel);
1047  LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("No LCN: %1").arg(msg));
1048  continue;
1049  }
1050 
1051  // Filter channels out that are not present in PAT and PMT.
1052  if (m_completeOnly &&
1053  !(channel.m_inPat &&
1054  channel.m_inPmt ))
1055  {
1056  QString msg = FormatChannel(transport, channel);
1057  LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Not in PAT/PMT: %1").arg(msg));
1058  continue;
1059  }
1060 
1061  // Filter channels out that are not present in SDT and that are not ATSC
1062  if (m_completeOnly &&
1063  channel.m_atscMajorChannel == 0 &&
1064  channel.m_atscMinorChannel == 0 &&
1065  !(channel.m_inPat &&
1066  channel.m_inPmt &&
1067  channel.m_inSdt &&
1068  (channel.m_patTsId ==
1069  channel.m_sdtTsId)))
1070  {
1071  QString msg = FormatChannel(transport, channel);
1072  LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Not in PAT/PMT/SDT: %1").arg(msg));
1073  continue;
1074  }
1075 
1076  // Filter channels out that do not have a name
1077  if (m_completeOnly && channel.m_serviceName.isEmpty())
1078  {
1079  QString msg = FormatChannel(transport, channel);
1080  LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("No name: %1").arg(msg));
1081  continue;
1082  }
1083 
1084  // Filter channels out only in channels.conf, i.e. not found
1085  if (channel.m_inChannelsConf &&
1086  !(channel.m_inPat ||
1087  channel.m_inPmt ||
1088  channel.m_inVct ||
1089  channel.m_inNit ||
1090  channel.m_inSdt))
1091  continue;
1092 
1093  filtered.push_back(channel);
1094  }
1095  transport.m_channels = filtered;
1096  }
1097 }
1098 
1100 {
1101  QMap<uint64_t, bool> rs;
1102 
1103  // Search all channels to find relocated services
1104  for (auto & transport : transports)
1105  {
1106  for (auto & channel : transport.m_channels)
1107  {
1108  if (channel.m_oldOrigNetId > 0)
1109  {
1110  uint64_t key = ((uint64_t)channel.m_oldOrigNetId << 32) | (channel.m_oldTsId << 16) | channel.m_oldServiceId;
1111  rs[key] = true;
1112  }
1113  }
1114  }
1115 
1116  // Remove all relocated services
1117  for (auto & transport : transports)
1118  {
1119  ChannelInsertInfoList filtered;
1120  for (auto & channel : transport.m_channels)
1121  {
1122  uint64_t key = ((uint64_t)channel.m_origNetId << 32) | (channel.m_sdtTsId << 16) | channel.m_serviceId;
1123  if (rs.value(key, false))
1124  {
1125  QString msg = FormatChannel(transport, channel);
1126  LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Relocated: %1").arg(msg));
1127  continue;
1128  }
1129  filtered.push_back(channel);
1130  }
1131  transport.m_channels = filtered;
1132  }
1133 }
1134 
1143  uint sourceid, ScanDTVTransportList &transports) const
1144 {
1145  ScanDTVTransportList not_in_scan;
1146  int found_in_same_transport = 0;
1147  int found_in_other_transport = 0;
1148  int found_nowhere = 0;
1149 
1151  if (!transports.empty())
1152  tuner_type = transports[0].m_tuner_type;
1153 
1154  bool is_dvbs =
1155  (DTVTunerType::kTunerTypeDVBS1 == tuner_type) ||
1156  (DTVTunerType::kTunerTypeDVBS2 == tuner_type);
1157 
1158  uint freq_mult = (is_dvbs) ? 1 : 1000;
1159 
1161  query.prepare(
1162  "SELECT mplexid "
1163  "FROM dtv_multiplex "
1164  "WHERE sourceid = :SOURCEID "
1165  "GROUP BY mplexid "
1166  "ORDER BY mplexid");
1167  query.bindValue(":SOURCEID", sourceid);
1168 
1169  if (!query.exec())
1170  {
1171  MythDB::DBError("GetDBTransports()", query);
1172  return not_in_scan;
1173  }
1174 
1175  QMap<uint,bool> found_in_scan;
1176  while (query.next())
1177  {
1178  ScanDTVTransport db_transport;
1179  uint mplexid = query.value(0).toUInt();
1180  if (db_transport.FillFromDB(tuner_type, mplexid))
1181  {
1182  if (db_transport.m_channels.empty())
1183  {
1184  continue;
1185  }
1186  }
1187 
1188  bool found_transport = false;
1189  QMap<uint,bool> found_in_database;
1190 
1191  // Search for old channels in the same transport of the scan.
1192  for (size_t ist = 0; ist < transports.size(); ++ist) // All transports in scan
1193  {
1194  ScanDTVTransport &scan_transport = transports[ist]; // Transport from the scan
1195  if (scan_transport.IsEqual(tuner_type, db_transport, 500 * freq_mult, true)) // Same transport?
1196  {
1197  found_transport = true; // Yes
1198  scan_transport.m_mplex = db_transport.m_mplex; // Found multiplex
1199  for (size_t jdc = 0; jdc < db_transport.m_channels.size(); ++jdc) // All channels in database transport
1200  {
1201  if (!found_in_database[jdc]) // Channel not found yet?
1202  {
1203  ChannelInsertInfo &db_chan = db_transport.m_channels[jdc]; // Channel in database transport
1204  for (size_t ksc = 0; ksc < scan_transport.m_channels.size(); ++ksc) // All channels in scanned transport
1205  { // Channel in scanned transport
1206  if (!found_in_scan[(ist<<16)+ksc]) // Scanned channel not yet found?
1207  {
1208  ChannelInsertInfo &scan_chan = scan_transport.m_channels[ksc];
1209  if (db_chan.IsSameChannel(scan_chan, 2)) // Same transport, relaxed check
1210  {
1211  found_in_same_transport++;
1212  found_in_database[jdc] = true; // Channel from db found in scan
1213  found_in_scan[(ist<<16)+ksc] = true; // Channel from scan found in db
1214  scan_chan.m_dbMplexId = db_transport.m_mplex; // Found multiplex
1215  scan_chan.m_channelId = db_chan.m_channelId; // This is the crucial field
1216  break; // Ready with scanned transport
1217  }
1218  }
1219  }
1220  }
1221  }
1222  }
1223  }
1224 
1225  // Search for old channels in all transports of the scan.
1226  // This is done for all channels that have not yet been found.
1227  // This can identify the channels that have moved to another transport.
1228  if (m_fullChannelSearch)
1229  {
1230  for (size_t ist = 0; ist < transports.size(); ++ist) // All transports in scan
1231  {
1232  ScanDTVTransport &scan_transport = transports[ist]; // Scanned transport
1233  for (size_t jdc = 0; jdc < db_transport.m_channels.size(); ++jdc) // All channels in database transport
1234  {
1235  if (!found_in_database[jdc]) // Channel not found yet?
1236  {
1237  ChannelInsertInfo &db_chan = db_transport.m_channels[jdc]; // Channel in database transport
1238  for (size_t ksc = 0; ksc < scan_transport.m_channels.size(); ++ksc) // All channels in scanned transport
1239  {
1240  if (!found_in_scan[(ist<<16)+ksc]) // Scanned channel not yet found?
1241  {
1242  ChannelInsertInfo &scan_chan = scan_transport.m_channels[ksc];
1243  if (db_chan.IsSameChannel(scan_chan, 1)) // Other transport, check
1244  { // network id and service id
1245  found_in_other_transport++;
1246  found_in_database[jdc] = true; // Channel from db found in scan
1247  found_in_scan[(ist<<16)+ksc] = true; // Channel from scan found in db
1248  scan_chan.m_channelId = db_chan.m_channelId; // This is the crucial field
1249  break; // Ready with scanned transport
1250  }
1251  }
1252  }
1253  }
1254  }
1255  }
1256  }
1257 
1258  // If the transport in the database is found in the scan
1259  // then all channels in that transport that are not found
1260  // in the scan are copied to the "not_in_scan" list.
1261  if (found_transport)
1262  {
1263  ScanDTVTransport tmp = db_transport;
1264  tmp.m_channels.clear();
1265 
1266  for (size_t idc = 0; idc < db_transport.m_channels.size(); ++idc)
1267  {
1268  if (!found_in_database[idc])
1269  {
1270  tmp.m_channels.push_back(db_transport.m_channels[idc]);
1271  found_nowhere++;
1272  }
1273  }
1274 
1275  if (!tmp.m_channels.empty())
1276  not_in_scan.push_back(tmp);
1277  }
1278  }
1279  LOG(VB_GENERAL, LOG_INFO, LOC +
1280  QString("Old channels found in same transport: %1")
1281  .arg(found_in_same_transport));
1282  LOG(VB_GENERAL, LOG_INFO, LOC +
1283  QString("Old channels found in other transport: %1")
1284  .arg(found_in_other_transport));
1285  LOG(VB_GENERAL, LOG_INFO, LOC +
1286  QString("Old channels not found (off-air): %1")
1287  .arg(found_nowhere));
1288 
1289  return not_in_scan;
1290 }
1291 
1293 {
1295  for (auto & transport : transports)
1296  {
1297  for (auto & chan : transport.m_channels)
1298  {
1299  if (((chan.m_couldBeOpencable && (chan.m_siStandard == "mpeg")) ||
1300  chan.m_isOpencable) && !chan.m_inVct)
1301  {
1302  chan.m_siStandard = "opencable";
1303  }
1304  }
1305  }
1306 }
1307 
1309  const ScanDTVTransportList &transports)
1310 {
1312  for (const auto & transport : transports)
1313  {
1314  for (const auto & chan : transport.m_channels)
1315  {
1316  int enc = (chan.m_isEncrypted) ?
1317  ((chan.m_decryptionStatus == kEncDecrypted) ? 2 : 1) : 0;
1318  if (chan.m_siStandard == "atsc") info.m_atscChannels[enc] += 1;
1319  if (chan.m_siStandard == "dvb") info.m_dvbChannels[enc] += 1;
1320  if (chan.m_siStandard == "mpeg") info.m_mpegChannels[enc] += 1;
1321  if (chan.m_siStandard == "opencable") info.m_scteChannels[enc] += 1;
1322  if (chan.m_siStandard == "ntsc") info.m_ntscChannels[enc] += 1;
1323  if (chan.m_siStandard != "ntsc")
1324  {
1325  ++info.m_progNumCnt[chan.m_serviceId];
1326  ++info.m_chanNumCnt[map_str(chan.m_chanNum)];
1327  }
1328  if (chan.m_siStandard == "atsc")
1329  {
1330  ++info.m_atscNumCnt[(chan.m_atscMajorChannel << 16) |
1331  (chan.m_atscMinorChannel)];
1332  ++info.m_atscMinCnt[chan.m_atscMinorChannel];
1333  ++info.m_atscMajCnt[chan.m_atscMajorChannel];
1334  }
1335  if (chan.m_siStandard == "ntsc")
1336  {
1337  ++info.m_atscNumCnt[(chan.m_atscMajorChannel << 16) |
1338  (chan.m_atscMinorChannel)];
1339  }
1340  }
1341  }
1342 
1343  return info;
1344 }
1345 
1347  const ScanDTVTransportList &transports,
1348  const ChannelImporterBasicStats &info)
1349 {
1351 
1352  for (const auto & transport : transports)
1353  {
1354  for (const auto & chan : transport.m_channels)
1355  {
1356  stats.m_uniqueProgNum +=
1357  (info.m_progNumCnt[chan.m_serviceId] == 1) ? 1 : 0;
1358  stats.m_uniqueChanNum +=
1359  (info.m_chanNumCnt[map_str(chan.m_chanNum)] == 1) ? 1 : 0;
1360 
1361  if (chan.m_siStandard == "atsc")
1362  {
1363  stats.m_uniqueAtscNum +=
1364  (info.m_atscNumCnt[(chan.m_atscMajorChannel << 16) |
1365  (chan.m_atscMinorChannel)] == 1) ? 1 : 0;
1366  stats.m_uniqueAtscMin +=
1367  (info.m_atscMinCnt[(chan.m_atscMinorChannel)] == 1) ? 1 : 0;
1368  stats.m_maxAtscMajCnt = std::max(
1369  stats.m_maxAtscMajCnt,
1370  info.m_atscMajCnt[chan.m_atscMajorChannel]);
1371  }
1372  }
1373  }
1374 
1375  stats.m_uniqueTotal = (stats.m_uniqueProgNum + stats.m_uniqueAtscNum +
1376  stats.m_uniqueAtscMin + stats.m_uniqueChanNum);
1377 
1378  return stats;
1379 }
1380 
1381 
1383  const ScanDTVTransport &transport,
1384  const ChannelInsertInfo &chan,
1385  const ChannelImporterBasicStats *info)
1386 {
1387  QString msg;
1388  QTextStream ssMsg(&msg);
1389 
1390  ssMsg << transport.m_frequency << ":";
1391 
1392  QString si_standard = (chan.m_siStandard=="opencable") ?
1393  QString("scte") : chan.m_siStandard;
1394 
1395  if (si_standard == "atsc" || si_standard == "scte")
1396  {
1397  ssMsg << (QString("%1:%2:%3-%4:%5:%6=%7=%8:%9")
1398  .arg(chan.m_callSign).arg(chan.m_chanNum)
1399  .arg(chan.m_atscMajorChannel)
1400  .arg(chan.m_atscMinorChannel)
1401  .arg(chan.m_serviceId)
1402  .arg(chan.m_vctTsId)
1403  .arg(chan.m_vctChanTsId)
1404  .arg(chan.m_patTsId)
1405  .arg(si_standard)).toLatin1().constData();
1406  }
1407  else if (si_standard == "dvb")
1408  {
1409  ssMsg << (QString("%1:%2:%3:%4:%5:%6=%7:%8")
1410  .arg(chan.m_serviceName).arg(chan.m_chanNum)
1411  .arg(chan.m_netId).arg(chan.m_origNetId)
1412  .arg(chan.m_serviceId)
1413  .arg(chan.m_sdtTsId)
1414  .arg(chan.m_patTsId)
1415  .arg(si_standard)).toLatin1().constData();
1416  }
1417  else
1418  {
1419  ssMsg << (QString("%1:%2:%3:%4:%5")
1420  .arg(chan.m_callSign).arg(chan.m_chanNum)
1421  .arg(chan.m_serviceId)
1422  .arg(chan.m_patTsId)
1423  .arg(si_standard)).toLatin1().constData();
1424  }
1425 
1426  if (info)
1427  {
1428  ssMsg << ' ';
1429  msg = msg.leftJustified(72);
1430 
1431  ssMsg << chan.m_channelId;
1432 
1433  ssMsg << ":"
1434  << (QString("cnt(pnum:%1,channum:%2)")
1435  .arg(info->m_progNumCnt[chan.m_serviceId])
1436  .arg(info->m_chanNumCnt[map_str(chan.m_chanNum)])
1437  ).toLatin1().constData();
1438 
1439  if (chan.m_siStandard == "atsc")
1440  {
1441  ssMsg <<
1442  (QString(":atsc_cnt(tot:%1,minor:%2)")
1443  .arg(info->m_atscNumCnt[
1444  (chan.m_atscMajorChannel << 16) |
1445  (chan.m_atscMinorChannel)])
1446  .arg(info->m_atscMinCnt[chan.m_atscMinorChannel])
1447  ).toLatin1().constData();
1448  }
1449  }
1450 
1451  return msg;
1452 }
1453 
1466  const ScanDTVTransport &/*transport*/,
1467  const ChannelInsertInfo &chan)
1468 {
1469  QString msg;
1470  QTextStream ssMsg(&msg);
1471 
1472  QString si_standard = (chan.m_siStandard=="opencable") ?
1473  QString("scte") : chan.m_siStandard;
1474 
1475  if (si_standard == "atsc" || si_standard == "scte")
1476  {
1477 
1478  if (si_standard == "atsc")
1479  {
1480  ssMsg << (kATSCChannelFormat
1481  .arg(chan.m_atscMajorChannel)
1482  .arg(chan.m_atscMinorChannel)).toLatin1().constData();
1483  }
1484  else if (chan.m_freqId.isEmpty())
1485  {
1486  ssMsg << (QString("%1-%2")
1487  .arg(chan.m_sourceId)
1488  .arg(chan.m_serviceId)).toLatin1().constData();
1489  }
1490  else
1491  {
1492  ssMsg << (QString("%1-%2")
1493  .arg(chan.m_freqId)
1494  .arg(chan.m_serviceId)).toLatin1().constData();
1495  }
1496 
1497  if (!chan.m_callSign.isEmpty())
1498  ssMsg << (QString(" (%1)")
1499  .arg(chan.m_callSign)).toLatin1().constData();
1500  }
1501  else if (si_standard == "dvb")
1502  {
1503  ssMsg << (QString("%1 (%2 %3)")
1504  .arg(chan.m_serviceName).arg(chan.m_serviceId)
1505  .arg(chan.m_netId)).toLatin1().constData();
1506  }
1507  else if (chan.m_freqId.isEmpty())
1508  {
1509  ssMsg << (QString("%1-%2")
1510  .arg(chan.m_sourceId).arg(chan.m_serviceId))
1511  .toLatin1().constData();
1512  }
1513  else
1514  {
1515  ssMsg << (QString("%1-%2")
1516  .arg(chan.m_freqId).arg(chan.m_serviceId))
1517  .toLatin1().constData();
1518  }
1519 
1520  return msg;
1521 }
1522 
1524  const ScanDTVTransportList &transports_in,
1525  const ChannelImporterBasicStats *info)
1526 {
1527  // Sort transports in order of increasing frequency
1528  struct less_than_key
1529  {
1530  inline bool operator() (const ScanDTVTransport &t1, const ScanDTVTransport &t2)
1531  {
1532  return t1.m_frequency < t2.m_frequency;
1533  }
1534  };
1535  ScanDTVTransportList transports(transports_in);
1536  std::sort(transports.begin(), transports.end(), less_than_key());
1537 
1538  QString msg;
1539 
1540  for (auto & transport : transports)
1541  {
1542  auto fmt_chan = [transport, info](const QString & m, const auto & chan)
1543  { return m + FormatChannel(transport, chan, info) + "\n"; };
1544  msg = std::accumulate(transport.m_channels.cbegin(), transport.m_channels.cend(),
1545  msg, fmt_chan);
1546  }
1547 
1548  return msg;
1549 }
1550 
1552  const ScanDTVTransport &transport)
1553 {
1554  QString msg;
1555  QTextStream ssMsg(&msg);
1556  ssMsg << transport.toString();
1557  ssMsg << QString(" onid:%1").arg(transport.m_networkID);
1558  ssMsg << QString(" tsid:%1").arg(transport.m_transportID);
1559  ssMsg << QString(" ss:%1").arg(transport.m_signalStrength);
1560  return msg;
1561 }
1562 
1564  const ScanDTVTransportList &transports_in)
1565 {
1566  // Sort transports in order of increasing frequency
1567  struct less_than_key
1568  {
1569  inline bool operator() (const ScanDTVTransport &t1, const ScanDTVTransport &t2)
1570  {
1571  return t1.m_frequency < t2.m_frequency;
1572  }
1573  };
1574  ScanDTVTransportList transports(transports_in);
1575  std::sort(transports.begin(), transports.end(), less_than_key());
1576 
1577  auto fmt_trans = [](const QString& msg, const auto & transport)
1578  { return msg + FormatTransport(transport) + "\n"; };
1579  return std::accumulate(transports.cbegin(), transports.cend(),
1580  QString(), fmt_trans);
1581 }
1582 
1584  const ChannelImporterBasicStats &info,
1585  const ChannelImporterUniquenessStats &stats)
1586 {
1587  QString msg = tr("Channels: FTA Enc Dec\n") +
1588  QString("ATSC %1 %2 %3\n")
1589  .arg(info.m_atscChannels[0],3)
1590  .arg(info.m_atscChannels[1],3)
1591  .arg(info.m_atscChannels[2],3) +
1592  QString("DVB %1 %2 %3\n")
1593  .arg(info.m_dvbChannels [0],3)
1594  .arg(info.m_dvbChannels [1],3)
1595  .arg(info.m_dvbChannels [2],3) +
1596  QString("SCTE %1 %2 %3\n")
1597  .arg(info.m_scteChannels[0],3)
1598  .arg(info.m_scteChannels[1],3)
1599  .arg(info.m_scteChannels[2],3) +
1600  QString("MPEG %1 %2 %3\n")
1601  .arg(info.m_mpegChannels[0],3)
1602  .arg(info.m_mpegChannels[1],3)
1603  .arg(info.m_mpegChannels[2],3) +
1604  QString("NTSC %1\n")
1605  .arg(info.m_ntscChannels[0],3) +
1606  tr("Unique: prog %1 atsc %2 atsc minor %3 channum %4\n")
1607  .arg(stats.m_uniqueProgNum).arg(stats.m_uniqueAtscNum)
1608  .arg(stats.m_uniqueAtscMin).arg(stats.m_uniqueChanNum) +
1609  tr("Max atsc major count: %1")
1610  .arg(stats.m_maxAtscMajCnt);
1611 
1612  return msg;
1613 }
1614 
1616  const ChannelImporterBasicStats &info,
1617  const ChannelInsertInfo &chan, ChannelType type)
1618 {
1619  switch (type)
1620  {
1621  case kATSCNonConflicting:
1622  return ((chan.m_siStandard == "atsc") /* &&
1623  (info.m_atscNumCnt[(chan.m_atscMajorChannel << 16) |
1624  (chan.m_atscMinorChannel)] == 1) */);
1625 
1626  case kDVBNonConflicting:
1627  return ((chan.m_siStandard == "dvb") /* &&
1628  (info.m_progNumCnt[chan.m_serviceId] == 1) */);
1629 
1630  case kMPEGNonConflicting:
1631  return ((chan.m_siStandard == "mpeg") &&
1632  (info.m_chanNumCnt[map_str(chan.m_chanNum)] == 1));
1633 
1634  case kSCTENonConflicting:
1635  return (((chan.m_siStandard == "scte") ||
1636  (chan.m_siStandard == "opencable")) &&
1637  (info.m_chanNumCnt[map_str(chan.m_chanNum)] == 1));
1638 
1639  case kNTSCNonConflicting:
1640  return ((chan.m_siStandard == "ntsc") &&
1641  (info.m_atscNumCnt[(chan.m_atscMajorChannel << 16) |
1642  (chan.m_atscMinorChannel)] == 1));
1643 
1644  case kATSCConflicting:
1645  return ((chan.m_siStandard == "atsc") &&
1646  (info.m_atscNumCnt[(chan.m_atscMajorChannel << 16) |
1647  (chan.m_atscMinorChannel)] != 1));
1648 
1649  case kDVBConflicting:
1650  return ((chan.m_siStandard == "dvb") &&
1651  (info.m_progNumCnt[chan.m_serviceId] != 1));
1652 
1653  case kMPEGConflicting:
1654  return ((chan.m_siStandard == "mpeg") &&
1655  (info.m_chanNumCnt[map_str(chan.m_chanNum)] != 1));
1656 
1657  case kSCTEConflicting:
1658  return (((chan.m_siStandard == "scte") ||
1659  (chan.m_siStandard == "opencable")) &&
1660  (info.m_chanNumCnt[map_str(chan.m_chanNum)] != 1));
1661 
1662  case kNTSCConflicting:
1663  return ((chan.m_siStandard == "ntsc") &&
1664  (info.m_atscNumCnt[(chan.m_atscMajorChannel << 16) |
1665  (chan.m_atscMinorChannel)] != 1));
1666  }
1667  return false;
1668 }
1669 
1671  const ScanDTVTransportList &transports,
1672  const ChannelImporterBasicStats &info,
1673  ChannelType type, uint &new_chan, uint &old_chan)
1674 {
1675  new_chan = old_chan = 0;
1676  for (const auto & transport : transports)
1677  {
1678  for (const auto& chan : transport.m_channels)
1679  {
1680  if (IsType(info, chan, type))
1681  {
1682  if (chan.m_channelId)
1683  ++old_chan;
1684  else
1685  ++new_chan;
1686  }
1687  }
1688  }
1689 }
1690 
1692  const ScanDTVTransportList &transports)
1693 {
1694  auto add_count = [](int count, const auto & transport)
1695  { return count + transport.m_channels.size(); };
1696  return std::accumulate(transports.cbegin(), transports.cend(),
1697  0, add_count);
1698 }
1699 
1715  const ChannelInsertInfo &chan)
1716 {
1717  static QMutex s_lastFreeLock;
1718  static QMap<uint,uint> s_lastFreeChanNumMap;
1719  QString chanNum;
1720 
1721  // Suggest existing channel number if non-conflicting
1723  return chan.m_chanNum;
1724 
1725  // Add a suffix to make it unique
1726  for (char suffix = 'A'; suffix <= 'Z'; ++suffix)
1727  {
1728  chanNum = chan.m_chanNum + suffix;
1729  if (!ChannelUtil::IsConflicting(chanNum, chan.m_sourceId))
1730  return chanNum;
1731  }
1732 
1733  // Find unused channel number
1734  QMutexLocker locker(&s_lastFreeLock);
1735  uint last_free_chan_num = s_lastFreeChanNumMap[chan.m_sourceId];
1736  for (last_free_chan_num++; ; ++last_free_chan_num)
1737  {
1738  chanNum = QString::number(last_free_chan_num);
1739  if (!ChannelUtil::IsConflicting(chanNum, chan.m_sourceId))
1740  break;
1741  }
1742  // cppcheck-suppress unreadVariable
1743  s_lastFreeChanNumMap[chan.m_sourceId] = last_free_chan_num;
1744 
1745  return chanNum;
1746 }
1747 
1750 {
1752  if (m_useGui)
1753  {
1754  m_functorRetval = -1;
1755  do
1756  {
1757  MythScreenStack *popupStack =
1758  GetMythMainWindow()->GetStack("popup stack");
1759  auto *deleteDialog =
1760  new MythDialogBox(msg, popupStack, "deletechannels");
1761 
1762  if (deleteDialog->Create())
1763  {
1764  deleteDialog->AddButton(tr("Delete All"));
1765  deleteDialog->AddButton(tr("Set all invisible"));
1766 // deleteDialog->AddButton(tr("Handle manually"));
1767  deleteDialog->AddButton(tr("Ignore All"));
1768  QObject::connect(deleteDialog, &MythDialogBox::Closed, this,
1769  [this](const QString & /*resultId*/, int result)
1770  {
1771  m_functorRetval = result;
1772  m_eventLoop.quit();
1773  });
1774  popupStack->AddScreen(deleteDialog);
1775 
1776  m_eventLoop.exec();
1777  }
1778  } while (m_functorRetval < 0);
1779 
1780  switch (m_functorRetval)
1781  {
1782  case 0: action = kDeleteAll; break;
1783  case 1: action = kDeleteInvisibleAll; break;
1784  case 2: action = kDeleteIgnoreAll; break;
1785  }
1786  }
1787  else if (m_isInteractive)
1788  {
1789  std::cout << msg.toLatin1().constData()
1790  << std::endl
1791  << tr("Do you want to:").toLatin1().constData()
1792  << std::endl
1793  << tr("1. Delete All").toLatin1().constData()
1794  << std::endl
1795  << tr("2. Set all invisible").toLatin1().constData()
1796  << std::endl
1797 // cout << "3. Handle manually" << endl;
1798  << tr("4. Ignore All").toLatin1().constData()
1799  << std::endl;
1800  while (true)
1801  {
1802  std::string ret;
1803  std::cin >> ret;
1804  bool ok = false;
1805  uint val = QString(ret.c_str()).toUInt(&ok);
1806  if (ok && (val == 1 || val == 2 || val == 4))
1807  {
1808  action = (1 == val) ? kDeleteAll : action;
1809  action = (2 == val) ? kDeleteInvisibleAll : action;
1810  //action = (3 == val) ? kDeleteManual : action;
1811  action = (4 == val) ? kDeleteIgnoreAll : action;
1812  break;
1813  }
1814 
1815  //cout << "Please enter either 1, 2, 3 or 4:" << endl;
1816  std::cout << tr("Please enter either 1, 2 or 4:")
1817  .toLatin1().constData() << std::endl;
1818  }
1819  }
1820 
1821  return action;
1822 }
1823 
1826 {
1828  if (m_useGui)
1829  {
1830  m_functorRetval = -1;
1831  do
1832  {
1833  MythScreenStack *popupStack =
1834  GetMythMainWindow()->GetStack("popup stack");
1835  auto *insertDialog =
1836  new MythDialogBox(msg, popupStack, "insertchannels");
1837 
1838  if (insertDialog->Create())
1839  {
1840  insertDialog->AddButton(tr("Insert All"));
1841  insertDialog->AddButton(tr("Insert Manually"));
1842  insertDialog->AddButton(tr("Ignore All"));
1843  QObject::connect(insertDialog, &MythDialogBox::Closed, this,
1844  [this](const QString & /*resultId*/, int result)
1845  {
1846  m_functorRetval = result;
1847  m_eventLoop.quit();
1848  });
1849 
1850  popupStack->AddScreen(insertDialog);
1851  m_eventLoop.exec();
1852  }
1853  } while (m_functorRetval < 0);
1854 
1855  switch (m_functorRetval)
1856  {
1857  case 0: action = kInsertAll; break;
1858  case 1: action = kInsertManual; break;
1859  case 2: action = kInsertIgnoreAll; break;
1860  }
1861  }
1862  else if (m_isInteractive)
1863  {
1864  std::cout << msg.toLatin1().constData()
1865  << std::endl
1866  << tr("Do you want to:").toLatin1().constData()
1867  << std::endl
1868  << tr("1. Insert All").toLatin1().constData()
1869  << std::endl
1870  << tr("2. Insert Manually").toLatin1().constData()
1871  << std::endl
1872  << tr("3. Ignore All").toLatin1().constData()
1873  << std::endl;
1874  while (true)
1875  {
1876  std::string ret;
1877  std::cin >> ret;
1878  bool ok = false;
1879  uint val = QString(ret.c_str()).toUInt(&ok);
1880  if (ok && (1 <= val) && (val <= 3))
1881  {
1882  action = (1 == val) ? kInsertAll : action;
1883  action = (2 == val) ? kInsertManual : action;
1884  action = (3 == val) ? kInsertIgnoreAll : action;
1885  break;
1886  }
1887 
1888  std::cout << tr("Please enter either 1, 2, or 3:")
1889  .toLatin1().constData() << std::endl;
1890  }
1891  }
1892 
1893  return action;
1894 }
1895 
1898 {
1900 
1901  if (m_useGui)
1902  {
1903  m_functorRetval = -1;
1904  do
1905  {
1906  MythScreenStack *popupStack =
1907  GetMythMainWindow()->GetStack("popup stack");
1908  auto *updateDialog =
1909  new MythDialogBox(msg, popupStack, "updatechannels");
1910 
1911  if (updateDialog->Create())
1912  {
1913  updateDialog->AddButton(tr("Update All"));
1914  updateDialog->AddButton(tr("Ignore All"));
1915  QObject::connect(updateDialog, &MythDialogBox::Closed, this,
1916  [this](const QString& /*resultId*/, int result)
1917  {
1918  m_functorRetval = result;
1919  m_eventLoop.quit();
1920  });
1921 
1922  popupStack->AddScreen(updateDialog);
1923  m_eventLoop.exec();
1924  }
1925  } while (m_functorRetval < 0);
1926 
1927  switch (m_functorRetval)
1928  {
1929  case 0: action = kUpdateAll; break;
1930  case 1: action = kUpdateIgnoreAll; break;
1931  }
1932  }
1933  else if (m_isInteractive)
1934  {
1935  std::cout << msg.toLatin1().constData()
1936  << std::endl
1937  << tr("Do you want to:").toLatin1().constData()
1938  << std::endl
1939  << tr("1. Update All").toLatin1().constData()
1940  << std::endl
1941  << tr("2. Update Manually").toLatin1().constData()
1942  << std::endl
1943  << tr("3. Ignore All").toLatin1().constData()
1944  << std::endl;
1945  while (true)
1946  {
1947  std::string ret;
1948  std::cin >> ret;
1949  bool ok = false;
1950  uint val = QString(ret.c_str()).toUInt(&ok);
1951  if (ok && (1 <= val) && (val <= 3))
1952  {
1953  action = (1 == val) ? kUpdateAll : action;
1954  action = (2 == val) ? kUpdateManual : action;
1955  action = (3 == val) ? kUpdateIgnoreAll : action;
1956  break;
1957  }
1958 
1959  std::cout << tr("Please enter either 1, 2, or 3:")
1960  .toLatin1().constData() << std::endl;
1961  }
1962  }
1963 
1964  return action;
1965 }
1966 
1968  MythMainWindow *parent, const QString& title,
1969  const QString& message, QString &text)
1970 {
1971  m_functorRetval = -1;
1972  MythScreenStack *popupStack = parent->GetStack("popup stack");
1973  auto *popup = new MythDialogBox(title, message, popupStack,
1974  "manualchannelpopup");
1975 
1976  if (popup->Create())
1977  {
1978  popup->AddButton(QCoreApplication::translate("(Common)", "OK"));
1979  popup->AddButton(tr("Edit"));
1980  popup->AddButton(QCoreApplication::translate("(Common)", "Cancel"));
1981  popup->AddButton(QCoreApplication::translate("(Common)", "Cancel All"));
1982  QObject::connect(popup, &MythDialogBox::Closed, this,
1983  [this](const QString & /*resultId*/, int result)
1984  {
1985  m_functorRetval = result;
1986  m_eventLoop.quit();
1987  });
1988  popupStack->AddScreen(popup);
1989  m_eventLoop.exec();
1990  }
1991  else
1992  {
1993  delete popup;
1994  popup = nullptr;
1995  }
1996 
1997  // Choice "Edit"
1998  if (1 == m_functorRetval)
1999  {
2000  auto *textEdit =
2001  new MythTextInputDialog(popupStack,
2002  tr("Please enter a unique channel number."),
2003  FilterNone, false, text);
2004  if (textEdit->Create())
2005  {
2006  QObject::connect(textEdit, &MythTextInputDialog::haveResult, this,
2007  [this,&text](QString result)
2008  {
2009  m_functorRetval = 0;
2010  text = std::move(result);
2011  });
2012  QObject::connect(textEdit, &MythTextInputDialog::Exiting, this,
2013  [this]()
2014  {
2015  m_eventLoop.quit();
2016  });
2017 
2018  popupStack->AddScreen(textEdit);
2019  m_eventLoop.exec();
2020  }
2021  else
2022  delete textEdit;
2023  }
2024 
2025  OkCancelType rval = kOCTCancel;
2026  switch (m_functorRetval) {
2027  case 0: rval = kOCTOk; break;
2028  // NOLINTNEXTLINE(bugprone-branch-clone)
2029  case 1: rval = kOCTCancel; break; // "Edit" is done already
2030  case 2: rval = kOCTCancel; break;
2031  case 3: rval = kOCTCancelAll; break;
2032  }
2033  return rval;
2034 }
2035 
2037  MythMainWindow *parent, const QString& title,
2038  const QString& message, QString &text)
2039 {
2040  m_functorRetval = -1;
2041  MythScreenStack *popupStack = parent->GetStack("popup stack");
2042  auto *popup = new MythDialogBox(title, message, popupStack,
2043  "resolvechannelpopup");
2044 
2045  if (popup->Create())
2046  {
2047  popup->AddButton(QCoreApplication::translate("(Common)", "OK"));
2048  popup->AddButton(QCoreApplication::translate("(Common)", "OK All"));
2049  popup->AddButton(tr("Edit"));
2050  popup->AddButton(QCoreApplication::translate("(Common)", "Cancel"));
2051  popup->AddButton(QCoreApplication::translate("(Common)", "Cancel All"));
2052  QObject::connect(popup, &MythDialogBox::Closed, this,
2053  [this](const QString & /*resultId*/, int result)
2054  {
2055  m_functorRetval = result;
2056  m_eventLoop.quit();
2057  });
2058  popupStack->AddScreen(popup);
2059  m_eventLoop.exec();
2060  }
2061  else
2062  {
2063  delete popup;
2064  popup = nullptr;
2065  }
2066 
2067  // Choice "Edit"
2068  if (2 == m_functorRetval)
2069  {
2070  auto *textEdit =
2071  new MythTextInputDialog(popupStack,
2072  tr("Please enter a unique channel number."),
2073  FilterNone, false, text);
2074  if (textEdit->Create())
2075  {
2076  QObject::connect(textEdit, &MythTextInputDialog::haveResult, this,
2077  [this,&text](QString result)
2078  {
2079  m_functorRetval = 0;
2080  text = std::move(result);
2081  });
2082  QObject::connect(textEdit, &MythTextInputDialog::Exiting, this,
2083  [this]()
2084  {
2085  m_eventLoop.quit();
2086  });
2087 
2088  popupStack->AddScreen(textEdit);
2089  m_eventLoop.exec();
2090  }
2091  else
2092  delete textEdit;
2093  }
2094 
2095  OkCancelType rval = kOCTCancel;
2096  switch (m_functorRetval) {
2097  case 0: rval = kOCTOk; break;
2098  case 1: rval = kOCTOkAll; break;
2099  // NOLINTNEXTLINE(bugprone-branch-clone)
2100  case 2: rval = kOCTCancel; break; // "Edit" is done already
2101  case 3: rval = kOCTCancel; break;
2102  case 4: rval = kOCTCancelAll; break;
2103  }
2104  return rval;
2105 }
2106 
2108  const ScanDTVTransport &transport,
2109  ChannelInsertInfo &chan)
2110 {
2111  QString msg = tr("Channel %1 has channel number %2 but that is already in use.")
2112  .arg(SimpleFormatChannel(transport, chan))
2113  .arg(chan.m_chanNum);
2114 
2115  OkCancelType ret = kOCTCancel;
2116 
2117  if (m_useGui)
2118  {
2119  while (true)
2120  {
2121  QString msg2 = msg;
2122  msg2 += "\n";
2123  msg2 += tr("Please enter a unique channel number.");
2124 
2125  QString val = ComputeSuggestedChannelNum(chan);
2126  msg2 += "\n";
2127  msg2 += tr("Default value is %1.").arg(val);
2129  GetMythMainWindow(), tr("Channel Importer"),
2130  msg2, val);
2131 
2132  if (kOCTOk != ret && kOCTOkAll != ret)
2133  break; // user canceled..
2134 
2135  bool ok = CheckChannelNumber(val, chan);
2136  if (ok)
2137  {
2138  chan.m_chanNum = val;
2139  break;
2140  }
2141  }
2142  }
2143  else if (m_isInteractive)
2144  {
2145  std::cout << msg.toLatin1().constData() << std::endl;
2146 
2147  QString cancelStr = QCoreApplication::translate("(Common)",
2148  "Cancel").toLower();
2149  QString cancelAllStr = QCoreApplication::translate("(Common)",
2150  "Cancel All").toLower();
2151  QString msg2 = tr("Please enter a non-conflicting channel number "
2152  "(or type '%1' to skip, '%2' to skip all):")
2153  .arg(cancelStr).arg(cancelAllStr);
2154 
2155  while (true)
2156  {
2157  std::cout << msg2.toLatin1().constData() << std::endl;
2158  std::string sret;
2159  std::cin >> sret;
2160  QString val = QString(sret.c_str());
2161  if (val.toLower() == cancelStr)
2162  {
2163  ret = kOCTCancel;
2164  break; // user canceled..
2165  }
2166  if (val.toLower() == cancelAllStr)
2167  {
2168  ret = kOCTCancelAll;
2169  break; // user canceled..
2170  }
2171 
2172  bool ok = CheckChannelNumber(val, chan);
2173  if (ok)
2174  {
2175  chan.m_chanNum = val;
2176  ret = kOCTOk;
2177  break;
2178  }
2179  }
2180  }
2181 
2182  return ret;
2183 }
2184 
2186  const ScanDTVTransport &transport,
2187  ChannelInsertInfo &chan)
2188 {
2189  QString msg = tr("You chose to manually insert channel %1.")
2190  .arg(SimpleFormatChannel(transport, chan));
2191 
2192  OkCancelType ret = kOCTCancel;
2193 
2194  if (m_useGui)
2195  {
2196  while (true)
2197  {
2198  QString msg2 = msg;
2199  msg2 += " ";
2200  msg2 += tr("Please enter a unique channel number.");
2201 
2202  QString val = ComputeSuggestedChannelNum(chan);
2203  msg2 += " ";
2204  msg2 += tr("Default value is %1").arg(val);
2205  ret = ShowManualChannelPopup(
2206  GetMythMainWindow(), tr("Channel Importer"),
2207  msg2, val);
2208 
2209  if (kOCTOk != ret)
2210  break; // user canceled..
2211 
2212  bool ok = CheckChannelNumber(val, chan);
2213  if (ok)
2214  {
2215  chan.m_chanNum = val;
2216  ret = kOCTOk;
2217  break;
2218  }
2219  }
2220  }
2221  else if (m_isInteractive)
2222  {
2223  std::cout << msg.toLatin1().constData() << std::endl;
2224 
2225  QString cancelStr = QCoreApplication::translate("(Common)", "Cancel").toLower();
2226  QString cancelAllStr = QCoreApplication::translate("(Common)", "Cancel All").toLower();
2227 
2228  //: %1 is the translation of "Cancel", %2 of "Cancel All"
2229  QString msg2 = tr("Please enter a non-conflicting channel number "
2230  "(or type '%1' to skip, '%2' to skip all): ")
2231  .arg(cancelStr).arg(cancelAllStr);
2232 
2233  while (true)
2234  {
2235  std::cout << msg2.toLatin1().constData() << std::endl;
2236  std::string sret;
2237  std::cin >> sret;
2238  QString val = QString(sret.c_str());
2239  if (val.toLower() == cancelStr)
2240  {
2241  ret = kOCTCancel;
2242  break; // user canceled..
2243  }
2244  if (val.toLower() == cancelAllStr)
2245  {
2246  ret = kOCTCancelAll;
2247  break; // user canceled..
2248  }
2249 
2250  bool ok = CheckChannelNumber(val, chan);
2251  if (ok)
2252  {
2253  chan.m_chanNum = val;
2254  ret = kOCTOk;
2255  break;
2256  }
2257  }
2258  }
2259 
2260  return ret;
2261 }
2262 
2263 // ChannelImporter::CheckChannelNumber
2264 //
2265 // Check validity of a new channel number.
2266 // The channel number is not a number but it is a string that starts with a digit.
2267 // The channel number should not yet exist in this video source.
2268 //
2270  const QString &num,
2271  const ChannelInsertInfo &chan)
2272 {
2273  bool ok = (num.length() >= 1);
2274  ok = ok && ((num[0] >= '0') && (num[0] <= '9'));
2275  ok = ok && !ChannelUtil::IsConflicting(
2276  num, chan.m_sourceId, chan.m_channelId);
2277  return ok;
2278 }
DTVMultiplex::m_frequency
uint64_t m_frequency
Definition: dtvmultiplex.h:94
ChannelImporter::kSCTEConflicting
@ kSCTEConflicting
Definition: channelimporter.h:132
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
ChannelImporterBasicStats::m_chanNumCnt
QMap< QString, uint > m_chanNumCnt
Definition: channelimporter.h:53
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:126
channel
QDomElement channel
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:501
ChannelImporter::FixUpOpenCable
static void FixUpOpenCable(ScanDTVTransportList &transports)
Definition: channelimporter.cpp:1292
MSqlQuery::size
int size(void) const
Definition: mythdbcon.h:203
OkCancelType
OkCancelType
Definition: channelimporter.h:27
ChannelImporter::m_isInteractive
bool m_isInteractive
Definition: channelimporter.h:256
ChannelInsertInfo::m_useOnAirGuide
bool m_useOnAirGuide
Definition: channelinfo.h:223
kRequireAV
@ kRequireAV
Definition: channelscantypes.h:9
copy
long long copy(QFile &dst, QFile &src, uint block_size)
Copies src file to dst file.
Definition: mythmiscutil.cpp:309
FilterNone
@ FilterNone
Definition: mythuitextedit.h:19
ChannelImporter::QueryUserUpdate
UpdateAction QueryUserUpdate(const QString &msg)
For multiple channels.
Definition: channelimporter.cpp:1897
ChannelImporter::FormatTransports
static QString FormatTransports(const ScanDTVTransportList &transports_in)
Definition: channelimporter.cpp:1563
ChannelInsertInfo::m_chanNum
QString m_chanNum
Definition: channelinfo.h:218
ChannelInsertInfo::m_atscMajorChannel
uint m_atscMajorChannel
Definition: channelinfo.h:221
SaveScan
uint SaveScan(const ScanDTVTransportList &scan)
Definition: scaninfo.cpp:22
mythdb.h
ChannelImporter::kUpdateIgnoreAll
@ kUpdateIgnoreAll
Definition: channelimporter.h:114
ChannelImporter::CountChannels
static void CountChannels(const ScanDTVTransportList &transports, const ChannelImporterBasicStats &info, ChannelType type, uint &new_chan, uint &old_chan)
Definition: channelimporter.cpp:1670
ScanDTVTransport::m_transportID
uint m_transportID
Definition: dtvmultiplex.h:140
ChannelInsertInfo::m_freqId
QString m_freqId
Definition: channelinfo.h:227
channelimporter.h
kChannelNeverVisible
@ kChannelNeverVisible
Definition: channelinfo.h:25
ChannelImporter::FilterServices
void FilterServices(ScanDTVTransportList &transports) const
Definition: channelimporter.cpp:1023
ChannelImporter::GetDBTransports
ScanDTVTransportList GetDBTransports(uint sourceid, ScanDTVTransportList &transports) const
Adds found channel info to transports list, returns channels in DB which were not found in scan in an...
Definition: channelimporter.cpp:1142
title
QString title
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:636
ChannelUtil::DeleteChannel
static bool DeleteChannel(uint channel_id)
Definition: channelutil.cpp:1766
DTVTunerType::kTunerTypeDVBS1
static const int kTunerTypeDVBS1
Definition: dtvconfparserhelpers.h:93
ChannelInsertInfo::m_origNetId
uint m_origNetId
Definition: channelinfo.h:238
ChannelImporter::kATSCNonConflicting
@ kATSCNonConflicting
Definition: channelimporter.h:122
ChannelInsertInfoList
std::vector< ChannelInsertInfo > ChannelInsertInfoList
Definition: channelinfo.h:259
ChannelInsertInfo::m_inPmt
bool m_inPmt
Definition: channelinfo.h:243
kOCTCancel
@ kOCTCancel
Definition: channelimporter.h:29
ChannelUtil::SetChannelValue
static bool SetChannelValue(const QString &field_name, const QString &value, uint sourceid, const QString &channum)
Definition: channelutil.cpp:1137
ChannelImporter::m_doInsert
bool m_doInsert
Definition: channelimporter.h:258
ChannelImporter::kDVBConflicting
@ kDVBConflicting
Definition: channelimporter.h:131
mythdialogbox.h
ChannelImporter::kNTSCNonConflicting
@ kNTSCNonConflicting
Definition: channelimporter.h:126
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:198
MythScreenStack
Definition: mythscreenstack.h:16
arg
arg(title).arg(filename).arg(doDelete))
ChannelInsertInfo::m_defaultAuthority
QString m_defaultAuthority
Definition: channelinfo.h:231
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
ChannelImporter::ComputeSuggestedChannelNum
static QString ComputeSuggestedChannelNum(const ChannelInsertInfo &chan)
Compute a suggested channel number that is unique in the video source.
Definition: channelimporter.cpp:1714
ChannelUtil::SetVisible
static bool SetVisible(uint channel_id, ChannelVisibleType visible)
Definition: channelutil.cpp:1795
ChannelInsertInfo::m_hidden
bool m_hidden
Definition: channelinfo.h:224
ChannelImporter::kUpdateManual
@ kUpdateManual
Definition: channelimporter.h:113
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
ChannelInsertInfo::m_dbMplexId
uint m_dbMplexId
Definition: channelinfo.h:213
MythDialogBox::Closed
void Closed(QString, int)
ScanDTVTransport::m_networkID
uint m_networkID
Definition: dtvmultiplex.h:139
ChannelImporter::kChannelTypeConflictingFirst
@ kChannelTypeConflictingFirst
Definition: channelimporter.h:129
ChannelImporterUniquenessStats::m_uniqueProgNum
uint m_uniqueProgNum
Definition: channelimporter.h:61
ChannelImporter::UpdateAction
UpdateAction
Definition: channelimporter.h:111
ChannelImporterBasicStats::m_atscChannels
ChanStats m_atscChannels
Definition: channelimporter.h:42
ChannelImporter::m_doDelete
bool m_doDelete
Definition: channelimporter.h:257
kChannelAlwaysVisible
@ kChannelAlwaysVisible
Definition: channelinfo.h:22
ChannelImporter::QueryUserDelete
DeleteAction QueryUserDelete(const QString &msg)
For multiple channels.
Definition: channelimporter.cpp:1749
ChannelUtil::CreateChannel
static bool CreateChannel(uint db_mplexid, uint db_sourceid, uint new_channel_id, const QString &callsign, const QString &service_name, const QString &chan_num, uint service_id, uint atsc_major_channel, uint atsc_minor_channel, bool use_on_air_guide, ChannelVisibleType visible, const QString &freqid, const QString &icon=QString(), QString format="Default", const QString &xmltvid=QString(), const QString &default_authority=QString(), uint service_type=0)
Definition: channelutil.cpp:1482
MythTextInputDialog::haveResult
void haveResult(QString)
ChannelImporterBasicStats::m_atscNumCnt
QMap< uint, uint > m_atscNumCnt
Definition: channelimporter.h:50
MythMainWindow::GetStack
MythScreenStack * GetStack(const QString &stackname)
Definition: mythmainwindow.cpp:329
ChannelImporter::SimpleFormatChannel
static QString SimpleFormatChannel(const ScanDTVTransport &transport, const ChannelInsertInfo &chan)
Format channel information into a simple string.
Definition: channelimporter.cpp:1465
ChannelImporter::m_fullChannelSearch
bool m_fullChannelSearch
Definition: channelimporter.h:264
ChannelImporter::m_serviceRequirements
ServiceRequirements m_serviceRequirements
Definition: channelimporter.h:269
kOCTOk
@ kOCTOk
Definition: channelimporter.h:30
ChannelImporter::m_lcnOnly
bool m_lcnOnly
Definition: channelimporter.h:261
ChannelImporter::Process
void Process(const ScanDTVTransportList &_transports, int sourceid=-1)
Definition: channelimporter.cpp:42
kRequireAudio
@ kRequireAudio
Definition: channelscantypes.h:8
ChannelImporter::SimpleCountChannels
static int SimpleCountChannels(const ScanDTVTransportList &transports)
Definition: channelimporter.cpp:1691
ChannelImporter::QueryUserInsert
InsertAction QueryUserInsert(const QString &msg)
For multiple channels.
Definition: channelimporter.cpp:1825
tmp
static guint32 * tmp
Definition: goom_core.cpp:31
ChannelImporter::DeleteUnusedTransports
uint DeleteUnusedTransports(uint sourceid)
Definition: channelimporter.cpp:324
ChannelInsertInfo::m_atscMinorChannel
uint m_atscMinorChannel
Definition: channelinfo.h:222
ChannelImporter::toString
static QString toString(ChannelType type)
Definition: channelimporter.cpp:205
ChannelInsertInfo
Definition: channelinfo.h:134
DTVTunerType
Definition: dtvconfparserhelpers.h:77
ChannelImporter::DeleteAction
DeleteAction
Definition: channelimporter.h:98
ChannelImporter::AddChanToCopy
static void AddChanToCopy(ScanDTVTransport &transport_copy, const ScanDTVTransport &transport, const ChannelInsertInfo &chan)
Definition: channelimporter.cpp:877
ChannelImporter::m_keepChannelNumbers
bool m_keepChannelNumbers
Definition: channelimporter.h:263
ChannelImporter::m_useGui
bool m_useGui
Definition: channelimporter.h:255
ChannelImporter::m_completeOnly
bool m_completeOnly
Definition: channelimporter.h:262
if
if(query.exec() &&query.next())
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:461
ChannelImporter::CollectUniquenessStats
static ChannelImporterUniquenessStats CollectUniquenessStats(const ScanDTVTransportList &transports, const ChannelImporterBasicStats &info)
Definition: channelimporter.cpp:1346
ChannelImporter::kMPEGConflicting
@ kMPEGConflicting
Definition: channelimporter.h:133
DTVMultiplex::m_mplex
uint m_mplex
Definition: dtvmultiplex.h:110
ChannelImporter::ShowManualChannelPopup
OkCancelType ShowManualChannelPopup(MythMainWindow *parent, const QString &title, const QString &message, QString &text)
Definition: channelimporter.cpp:1967
ChannelImporterBasicStats::m_atscMajCnt
QMap< uint, uint > m_atscMajCnt
Definition: channelimporter.h:52
ChannelImporter::m_ftaOnly
bool m_ftaOnly
Definition: channelimporter.h:260
MythDialogBox
Basic menu dialog, message and a list of options.
Definition: mythdialogbox.h:167
ChannelImporter::kDeleteAll
@ kDeleteAll
Definition: channelimporter.h:99
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
ChannelImporter::kChannelTypeConflictingLast
@ kChannelTypeConflictingLast
Definition: channelimporter.h:135
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:178
ChannelImporter::RemoveDuplicates
static void RemoveDuplicates(ScanDTVTransportList &transports, ScanDTVTransportList &duplicates)
Definition: channelimporter.cpp:959
ChannelImporter::kDVBNonConflicting
@ kDVBNonConflicting
Definition: channelimporter.h:123
ChannelImporter::kSCTENonConflicting
@ kSCTENonConflicting
Definition: channelimporter.h:124
ChannelImporter::m_removeDuplicates
bool m_removeDuplicates
Definition: channelimporter.h:265
ChannelInsertInfo::m_channelId
uint m_channelId
Definition: channelinfo.h:215
ScanDTVTransport::FillFromDB
bool FillFromDB(DTVTunerType type, uint mplexid) override
Definition: dtvmultiplex.cpp:562
ChannelImporter::FormatChannel
static QString FormatChannel(const ScanDTVTransport &transport, const ChannelInsertInfo &chan, const ChannelImporterBasicStats *info=nullptr)
Definition: channelimporter.cpp:1382
ChannelUtil::CreateChanID
static int CreateChanID(uint sourceid, const QString &chan_num)
Creates a unique channel ID for database use.
Definition: channelutil.cpp:1446
ChannelImporter::ShowResolveChannelPopup
OkCancelType ShowResolveChannelPopup(MythMainWindow *parent, const QString &title, const QString &message, QString &text)
Definition: channelimporter.cpp:2036
ScanInfo::MarkProcessed
static bool MarkProcessed(uint scanid)
Definition: scaninfo.cpp:200
ChannelImporter::kInsertManual
@ kInsertManual
Definition: channelimporter.h:107
DTVMultiplex::toString
QString toString() const
Definition: dtvmultiplex.cpp:34
ChannelInsertInfo::IsSameChannel
bool IsSameChannel(const ChannelInsertInfo &other, int relaxed=0) const
Definition: channelinfo.cpp:457
ChannelInsertInfo::m_vctTsId
uint m_vctTsId
Definition: channelinfo.h:235
uint
unsigned int uint
Definition: compat.h:141
ChannelImporter::FormatChannels
static QString FormatChannels(const ScanDTVTransportList &transports, const ChannelImporterBasicStats *info=nullptr)
Definition: channelimporter.cpp:1523
ChannelImporter::m_eventLoop
QEventLoop m_eventLoop
Definition: channelimporter.h:270
kEncDecrypted
@ kEncDecrypted
Definition: mpegstreamdata.h:57
ScanDTVTransportList
std::vector< ScanDTVTransport > ScanDTVTransportList
Definition: dtvmultiplex.h:143
ChannelImporter::FilterRelocatedServices
static void FilterRelocatedServices(ScanDTVTransportList &transports)
Definition: channelimporter.cpp:1099
ChannelImporter::kChannelTypeNonConflictingLast
@ kChannelTypeNonConflictingLast
Definition: channelimporter.h:127
map_str
static QString map_str(QString str)
Definition: channelimporter.cpp:35
ScanDTVTransport::m_signalStrength
int m_signalStrength
Definition: dtvmultiplex.h:141
ChannelInsertInfo::m_callSign
QString m_callSign
Definition: channelinfo.h:216
ChannelInsertInfo::m_format
QString m_format
Definition: channelinfo.h:229
ChannelUtil::UpdateInsertInfoFromDB
static void UpdateInsertInfoFromDB(ChannelInsertInfo &chan)
Definition: channelutil.cpp:1669
channelutil.h
mpegstreamdata.h
ChannelUtil::IsConflicting
static bool IsConflicting(const QString &channum, uint sourceid=0, uint excluded_chanid=0)
Definition: channelutil.h:287
ChannelImporter::CheckChannelNumber
static bool CheckChannelNumber(const QString &num, const ChannelInsertInfo &chan)
Definition: channelimporter.cpp:2269
DTVTunerType::kTunerTypeDVBS2
static const int kTunerTypeDVBS2
Definition: dtvconfparserhelpers.h:94
ChannelImporter::CollectStats
static ChannelImporterBasicStats CollectStats(const ScanDTVTransportList &transports)
Definition: channelimporter.cpp:1308
ChannelImporter::m_functorRetval
int m_functorRetval
Definition: channelimporter.h:267
ChannelUtil::UpdateChannel
static bool UpdateChannel(uint db_mplexid, uint source_id, uint channel_id, const QString &callsign, const QString &service_name, const QString &chan_num, uint service_id, uint atsc_major_channel, uint atsc_minor_channel, bool use_on_air_guide, ChannelVisibleType visible, const QString &freqid=QString(), const QString &icon=QString(), QString format=QString(), const QString &xmltvid=QString(), const QString &default_authority=QString(), uint service_type=0)
Definition: channelutil.cpp:1562
LOC
#define LOC
Definition: channelimporter.cpp:31
ChannelImporterUniquenessStats::m_uniqueChanNum
uint m_uniqueChanNum
Definition: channelimporter.h:64
ChannelInsertInfo::m_serviceName
QString m_serviceName
Definition: channelinfo.h:217
ChannelImporterBasicStats::m_ntscChannels
ChanStats m_ntscChannels
Definition: channelimporter.h:46
ChannelImporterBasicStats::m_progNumCnt
QMap< uint, uint > m_progNumCnt
Definition: channelimporter.h:49
ChannelImporter::kATSCConflicting
@ kATSCConflicting
Definition: channelimporter.h:130
kOCTCancelAll
@ kOCTCancelAll
Definition: channelimporter.h:28
kChannelVisible
@ kChannelVisible
Definition: channelinfo.h:23
ChannelInsertInfo::m_sdtTsId
uint m_sdtTsId
Definition: channelinfo.h:237
ChannelInsertInfo::m_netId
uint m_netId
Definition: channelinfo.h:239
ChannelImporter::kChannelTypeNonConflictingFirst
@ kChannelTypeNonConflictingFirst
Definition: channelimporter.h:121
ChannelImporter::kMPEGNonConflicting
@ kMPEGNonConflicting
Definition: channelimporter.h:125
ChannelImporter::kDeleteIgnoreAll
@ kDeleteIgnoreAll
Definition: channelimporter.h:101
ChannelUtil::CreateIPTVTuningData
static bool CreateIPTVTuningData(uint channel_id, const IPTVTuningData &tuning)
Definition: channelutil.h:144
ChannelUtil::CreateMultiplex
static uint CreateMultiplex(int sourceid, const QString &sistandard, uint64_t frequency, const QString &modulation, int transport_id=-1, int network_id=-1)
Definition: channelutil.cpp:370
ChannelImporter::kInsertAll
@ kInsertAll
Definition: channelimporter.h:106
ChannelImporter::DeleteChannels
uint DeleteChannels(ScanDTVTransportList &transports)
Definition: channelimporter.cpp:227
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:864
ChannelUtil::UpdateChannelNumberFromDB
static void UpdateChannelNumberFromDB(ChannelInsertInfo &chan)
Definition: channelutil.cpp:1643
ChannelImporterUniquenessStats::m_uniqueTotal
uint m_uniqueTotal
Definition: channelimporter.h:65
ChannelInsertInfo::m_serviceId
uint m_serviceId
Definition: channelinfo.h:219
ChannelImporter::kUpdateAll
@ kUpdateAll
Definition: channelimporter.h:112
ChannelImporter::UpdateChannels
ScanDTVTransportList UpdateChannels(const ScanDTVTransportList &transports, const ChannelImporterBasicStats &info, UpdateAction action, ChannelType type, ScanDTVTransportList &updated, ScanDTVTransportList &skipped) const
Definition: channelimporter.cpp:730
ChannelImporter::MergeSameFrequency
static void MergeSameFrequency(ScanDTVTransportList &transports)
Definition: channelimporter.cpp:896
ChannelInsertInfo::m_sourceId
uint m_sourceId
Definition: channelinfo.h:214
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:108
ChannelImporterUniquenessStats::m_uniqueAtscMin
uint m_uniqueAtscMin
Definition: channelimporter.h:63
ChannelImporterUniquenessStats::m_maxAtscMajCnt
uint m_maxAtscMajCnt
Definition: channelimporter.h:66
build_compdb.action
action
Definition: build_compdb.py:9
ChannelImporter::InsertAction
InsertAction
Definition: channelimporter.h:105
ChannelImporter::InsertChannels
void InsertChannels(const ScanDTVTransportList &transports, const ChannelImporterBasicStats &info)
Definition: channelimporter.cpp:378
MythScreenType::Exiting
void Exiting()
ChannelImporter::m_success
bool m_success
Definition: channelimporter.h:266
ChannelImporterBasicStats
Definition: channelimporter.h:37
ChannelImporter::FormatTransport
static QString FormatTransport(const ScanDTVTransport &transport)
Definition: channelimporter.cpp:1551
DTVTunerType::kTunerTypeATSC
static const int kTunerTypeATSC
Definition: dtvconfparserhelpers.h:98
ChannelImporterBasicStats::m_scteChannels
ChanStats m_scteChannels
Definition: channelimporter.h:44
ChannelInsertInfo::m_serviceType
uint m_serviceType
Definition: channelinfo.h:220
ChannelImporter::IsType
static bool IsType(const ChannelImporterBasicStats &info, const ChannelInsertInfo &chan, ChannelType type)
Definition: channelimporter.cpp:1615
ChannelUtil::GetChannelCount
static uint GetChannelCount(int sourceid=-1)
Definition: channelutil.cpp:2261
MythTextInputDialog
Dialog prompting the user to enter a text string.
Definition: mythdialogbox.h:313
QT_ENDL
#define QT_ENDL
Definition: channelimporter.cpp:28
DTVMultiplex::IsEqual
bool IsEqual(DTVTunerType type, const DTVMultiplex &other, uint freq_range=0, bool fuzzy=false) const
Definition: dtvmultiplex.cpp:50
ChannelUtil::GetChanNum
static QString GetChanNum(int chan_id)
Returns the channel-number string of the given channel.
Definition: channelutil.cpp:775
ChannelImporter::QueryUserResolve
OkCancelType QueryUserResolve(const ScanDTVTransport &transport, ChannelInsertInfo &chan)
For a single channel.
Definition: channelimporter.cpp:2107
ChannelImporterUniquenessStats::m_uniqueAtscNum
uint m_uniqueAtscNum
Definition: channelimporter.h:62
ChannelInsertInfo::m_vctChanTsId
uint m_vctChanTsId
Definition: channelinfo.h:236
ChannelImporterBasicStats::m_dvbChannels
ChanStats m_dvbChannels
Definition: channelimporter.h:43
ChannelImporterBasicStats::m_mpegChannels
ChanStats m_mpegChannels
Definition: channelimporter.h:45
ChannelImporter::kInsertIgnoreAll
@ kInsertIgnoreAll
Definition: channelimporter.h:108
MythScreenStack::AddScreen
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Definition: mythscreenstack.cpp:52
ChannelInsertInfo::m_patTsId
uint m_patTsId
Definition: channelinfo.h:234
query
MSqlQuery query(MSqlQuery::InitCon())
ShowOkPopup
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
Definition: mythdialogbox.cpp:557
ChannelImporter::m_doSave
bool m_doSave
Definition: channelimporter.h:259
ChannelImporterBasicStats::m_atscMinCnt
QMap< uint, uint > m_atscMinCnt
Definition: channelimporter.h:51
ChannelImporter::ChannelType
ChannelType
Definition: channelimporter.h:118
MythMainWindow
Definition: mythmainwindow.h:35
ChannelInsertInfo::m_siStandard
QString m_siStandard
Definition: channelinfo.h:240
kATSCChannelFormat
static const QString kATSCChannelFormat
Definition: channelimporter.cpp:33
ChannelImporterUniquenessStats
Definition: channelimporter.h:57
kChannelNotVisible
@ kChannelNotVisible
Definition: channelinfo.h:24
ChannelImporter::kNTSCConflicting
@ kNTSCConflicting
Definition: channelimporter.h:134
ChannelInsertInfo::m_visible
ChannelVisibleType m_visible
Definition: channelinfo.h:226
ChannelImporter::GetSummary
static QString GetSummary(const ChannelImporterBasicStats &info, const ChannelImporterUniquenessStats &stats)
Definition: channelimporter.cpp:1583
ScanDTVTransport
Definition: dtvmultiplex.h:116
ScanDTVTransport::m_channels
ChannelInsertInfoList m_channels
Definition: dtvmultiplex.h:138
ChannelImporter::kDeleteInvisibleAll
@ kDeleteInvisibleAll
Definition: channelimporter.h:102
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808
kOCTOkAll
@ kOCTOkAll
Definition: channelimporter.h:31