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