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