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