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