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