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