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 
13 // Qt includes
14 #include <QTextStream>
15 
16 using namespace std;
17 
18 // MythTV headers
19 #include "channelimporter.h"
20 #include "mythdialogbox.h"
21 #include "mythdb.h"
22 #include "mpegstreamdata.h" // for kEncDecrypted
23 #include "channelutil.h"
24 
25 #define LOC QString("ChanImport: ")
26 
27 static QString map_str(QString str)
28 {
29  if (str.isEmpty())
30  return "";
31  return str;
32 }
33 
35  int sourceid)
36 {
37  if (_transports.empty())
38  {
39  if (m_use_gui)
40  {
41  int channels = ChannelUtil::GetChannelCount(sourceid);
42 
43  LOG(VB_GENERAL, LOG_INFO, LOC + (channels ?
44  (m_success ?
45  QString("Found %1 channels")
46  .arg(channels) :
47  "No new channels to process") :
48  "No channels to process.."));
49 
51  channels ?
52  (m_success ? tr("Found %n channel(s)", "", channels) :
53  tr("Failed to find any new channels!"))
54  : tr("Failed to find any channels."));
55  }
56  else
57  {
58  cout << (ChannelUtil::GetChannelCount() ?
59  "No new channels to process" :
60  "No channels to process..");
61  }
62 
63  return;
64  }
65 
66  ScanDTVTransportList transports = _transports;
67 
68  // Print some scan parameters
69  {
70  cout << endl << "Scan parameters:" << endl;
71  bool require_av = (m_service_requirements & kRequireAV) == kRequireAV;
72  bool require_a = (m_service_requirements & kRequireAudio) != 0;
73  cout << "Desired Services : " << (require_av ? "tv" : require_a ? "tv+radio" : "all") << endl;
74  cout << "Unencrypted Only : " << (m_fta_only ? "yes" : "no") << endl;
75  cout << "Logical Channel Numbers only: " << (m_lcn_only ? "yes" : "no") << endl;
76  cout << "Complete scan data required : " << (m_complete_only ? "yes" : "no") << endl;
77  cout << "Full search for old channels: " << (m_full_channel_search ? "yes" : "no") << endl;
78  }
79 
80  // Print out each channel
81  if (VERBOSE_LEVEL_CHECK(VB_CHANSCAN, LOG_ANY))
82  {
83  cout << endl << "Channel list before processing (";
84  cout << SimpleCountChannels(transports) << "):" << endl;
85  ChannelImporterBasicStats infoA = CollectStats(transports);
86  cout << FormatChannels(transports, &infoA).toLatin1().constData() << endl;
87  cout << endl;
88  }
89 
90  uint saved_scan = 0;
91  if (m_do_save)
92  saved_scan = SaveScan(transports);
93 
94  CleanupDuplicates(transports);
95 
96  FilterServices(transports);
97 
98  // Print out each transport
99  uint transports_scanned_size = transports.size();
100  if (VERBOSE_LEVEL_CHECK(VB_CHANSCAN, LOG_ANY))
101  {
102  cout << endl;
103  cout << "Transport list (" << transports_scanned_size << "):" << endl;
104  cout << FormatTransports(transports).toLatin1().constData() << endl;
105  }
106 
107  // Pull in DB info in transports
108  // Channels not found in scan but only in DB are returned in db_trans
109  sourceid = transports[0].m_channels[0].m_source_id;
110  ScanDTVTransportList db_trans = GetDBTransports(sourceid, transports);
111 
112  // Make sure "Open Cable" channels are marked that way.
113  FixUpOpenCable(transports);
114 
115  // All channels in the scan after comparing with the database
116  if (VERBOSE_LEVEL_CHECK(VB_CHANSCAN, LOG_ANY))
117  {
118  cout << endl << "Channel list after compare with database (";
119  cout << SimpleCountChannels(transports) << "):" << endl;
120  ChannelImporterBasicStats infoA = CollectStats(transports);
121  cout << FormatChannels(transports, &infoA).toLatin1().constData() << endl;
122  cout << endl;
123  }
124 
125  // Add channels from the DB to the channels from the scan
126  // and possibly delete one or more of the off-air channels
127  if (m_do_delete)
128  {
129  ScanDTVTransportList trans = transports;
130  for (size_t i = 0; i < db_trans.size(); ++i)
131  trans.push_back(db_trans[i]);
132  uint deleted_count = DeleteChannels(trans);
133  if (deleted_count)
134  transports = trans;
135  }
136 
137  // Determine System Info standards..
138  ChannelImporterBasicStats info = CollectStats(transports);
139 
140  // Determine uniqueness of various naming schemes
142  CollectUniquenessStats(transports, info);
143 
144  // Print out each channel
145  cout << endl;
146  cout << "Channel list (" << SimpleCountChannels(transports) << "):" << endl;
147  cout << FormatChannels(transports, &info).toLatin1().constData() << endl;
148 
149  // Create summary
150  QString msg = GetSummary(transports_scanned_size, info, stats);
151  cout << msg.toLatin1().constData() << endl << endl;
152 
153  if (m_do_insert)
154  InsertChannels(transports, info);
155 
156  if (m_do_delete && sourceid)
157  DeleteUnusedTransports(sourceid);
158 
159  if (m_do_delete || m_do_insert)
160  ScanInfo::MarkProcessed(saved_scan);
161 }
162 
164 {
165  switch (type)
166  {
167  // non-conflicting
168  case kATSCNonConflicting: return "ATSC";
169  case kDVBNonConflicting: return "DVB";
170  case kSCTENonConflicting: return "SCTE";
171  case kMPEGNonConflicting: return "MPEG";
172  case kNTSCNonConflicting: return "NTSC";
173  // conflicting
174  case kATSCConflicting: return "ATSC";
175  case kDVBConflicting: return "DVB";
176  case kSCTEConflicting: return "SCTE";
177  case kMPEGConflicting: return "MPEG";
178  case kNTSCConflicting: return "NTSC";
179  }
180  return "Unknown";
181 }
182 
183 // Ask user what to do with the off-air channels
184 //
186  ScanDTVTransportList &transports)
187 {
188  vector<uint> off_air_list;
189  QMap<uint,bool> deleted;
190  ScanDTVTransportList off_air_transports;
191 
192  for (size_t i = 0; i < transports.size(); ++i)
193  {
194  ScanDTVTransport transport_copy;
195  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
196  {
197  ChannelInsertInfo chan = transports[i].m_channels[j];
198  bool was_in_db = chan.m_db_mplexid && chan.m_channel_id;
199  if (!was_in_db)
200  continue;
201 
202  if (!chan.m_in_pmt)
203  {
204  off_air_list.push_back(i<<16|j);
205  AddChanToCopy(transport_copy, transports[i], chan);
206  }
207  }
208  if (transport_copy.m_channels.size() > 0)
209  off_air_transports.push_back(transport_copy);
210  }
211 
212  if (off_air_list.empty())
213  {
214  return 0;
215  }
216  else
217  {
218  // List of off-air channels (in database but not in the scan)
219  cout << endl << "Off-air channels (" << SimpleCountChannels(off_air_transports) << "):" << endl;
220  ChannelImporterBasicStats infoA = CollectStats(off_air_transports);
221  cout << FormatChannels(off_air_transports, &infoA).toLatin1().constData() << endl;
222  }
223 
224  // Ask user whether to delete all or some of these stale channels
225  // if some is selected ask about each individually
226  //: %n is the number of channels
227  QString msg = tr("Found %n off-air channel(s).", "", off_air_list.size());
228  DeleteAction action = QueryUserDelete(msg);
229  if (kDeleteIgnoreAll == action)
230  return 0;
231 
232  if (kDeleteAll == action)
233  {
234  for (size_t k = 0; k < off_air_list.size(); ++k)
235  {
236  int i = off_air_list[k] >> 16, j = off_air_list[k] & 0xFFFF;
238  transports[i].m_channels[j].m_channel_id);
239  deleted[off_air_list[k]] = true;
240  }
241  }
242  else if (kDeleteInvisibleAll == action)
243  {
244  for (size_t k = 0; k < off_air_list.size(); ++k)
245  {
246  int i = off_air_list[k] >> 16, j = off_air_list[k] & 0xFFFF;
247  int chanid = transports[i].m_channels[j].m_channel_id;
248  QString channum = ChannelUtil::GetChanNum(chanid);
249  ChannelUtil::SetVisible(chanid, false);
250  ChannelUtil::SetChannelValue("channum", QString("_%1").arg(channum),
251  chanid);
252  }
253  }
254  else
255  {
256  // TODO manual delete
257  }
258 
259  // TODO delete encrypted channels when m_fta_only set
260 
261  if (deleted.empty())
262  return 0;
263 
264  // Create a new transports list without the deleted channels
265  ScanDTVTransportList newlist;
266  for (size_t i = 0; i < transports.size(); ++i)
267  {
268  newlist.push_back(transports[i]);
269  newlist.back().m_channels.clear();
270  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
271  {
272  if (!deleted.contains(i<<16|j))
273  {
274  newlist.back().m_channels.push_back(
275  transports[i].m_channels[j]);
276  }
277  }
278  }
279 
280  transports = newlist;
281  return deleted.size();
282 }
283 
285 {
286  MSqlQuery query(MSqlQuery::InitCon());
287  query.prepare(
288  "SELECT mplexid FROM dtv_multiplex "
289  "WHERE sourceid = :SOURCEID1 AND "
290  " mplexid NOT IN "
291  " (SELECT mplexid "
292  " FROM channel "
293  " WHERE sourceid = :SOURCEID2)");
294  query.bindValue(":SOURCEID1", sourceid);
295  query.bindValue(":SOURCEID2", sourceid);
296  if (!query.exec())
297  {
298  MythDB::DBError("DeleteUnusedTransports() -- select", query);
299  return 0;
300  }
301 
302  QString msg = tr("Found %n unused transport(s).", "", query.size());
303 
304  LOG(VB_GENERAL, LOG_INFO, LOC + msg);
305 
306  if (query.size() == 0)
307  return 0;
308 
309  DeleteAction action = QueryUserDelete(msg);
310  if (kDeleteIgnoreAll == action)
311  return 0;
312 
313  if (kDeleteAll == action)
314  {
315  query.prepare(
316  "DELETE FROM dtv_multiplex "
317  "WHERE sourceid = :SOURCEID1 AND "
318  " mplexid NOT IN "
319  " (SELECT mplexid "
320  " FROM channel "
321  " WHERE sourceid = :SOURCEID2)");
322  query.bindValue(":SOURCEID1", sourceid);
323  query.bindValue(":SOURCEID2", sourceid);
324  if (!query.exec())
325  {
326  MythDB::DBError("DeleteUnusedTransports() -- delete", query);
327  return 0;
328  }
329  }
330  else
331  {
332  // TODO manual delete
333  LOG(VB_GENERAL, LOG_INFO, LOC + "Manual delete of transport not implemented");
334  }
335  return 0;
336 }
337 
339  const ScanDTVTransportList &transports,
340  const ChannelImporterBasicStats &info)
341 {
342  ScanDTVTransportList list = transports;
343  ScanDTVTransportList inserted;
344  ScanDTVTransportList updated;
345  ScanDTVTransportList skipped_inserts;
346  ScanDTVTransportList skipped_updates;
347 
348  // Insert or update all channels with non-conflicting channum
349  // and complete tuning information.
350  uint chantype = (uint) kChannelTypeNonConflictingFirst;
351  for (; chantype <= (uint) kChannelTypeNonConflictingLast; ++chantype)
352  {
353  ChannelType type = (ChannelType) chantype;
354  uint new_chan = 0;
355  uint old_chan = 0;
356  CountChannels(list, info, type, new_chan, old_chan);
357 
358  if (kNTSCNonConflicting == type)
359  continue;
360 
361  if (old_chan)
362  {
363  //: %n is the number of channels, %1 is the type of channel
364  QString msg = tr("Found %n old %1 channel(s).", "", old_chan)
365  .arg(toString(type));
366 
367  UpdateAction action = QueryUserUpdate(msg);
368  list = UpdateChannels(list, info, action, type, updated, skipped_updates);
369  }
370  if (new_chan)
371  {
372  //: %n is the number of channels, %1 is the type of channel
373  QString msg = tr("Found %n new %1 channel(s).", "", new_chan)
374  .arg(toString(type));
375 
376  InsertAction action = QueryUserInsert(msg);
377  list = InsertChannels(list, info, action, type, inserted, skipped_inserts);
378  }
379  }
380 
381  if (!m_is_interactive)
382  return;
383 
384  // If any of the potential uniques is high and inserting
385  // with those as the channum would result in few conflicts
386  // ask user if it is ok to to proceed using it as the channum
387 
388  // For remaining channels with complete tuning information
389  // insert channels with contiguous list of numbers as the channums
390  chantype = (uint) kChannelTypeConflictingFirst;
391  for (; chantype <= (uint) kChannelTypeConflictingLast; ++chantype)
392  {
393  ChannelType type = (ChannelType) chantype;
394  uint new_chan = 0;
395  uint old_chan = 0;
396  CountChannels(list, info, type, new_chan, old_chan);
397 
398  if (old_chan)
399  {
400  //: %n is the number of channels, %1 is the type of channel
401  QString msg = tr("Found %n conflicting old %1 channel(s).",
402  "", old_chan).arg(toString(type));
403 
404  UpdateAction action = QueryUserUpdate(msg);
405  list = UpdateChannels(list, info, action, type, updated, skipped_updates);
406  }
407  if (new_chan)
408  {
409  //: %n is the number of channels, %1 is the type of channel
410  QString msg = tr("Found %n new conflicting %1 channel(s).",
411  "", new_chan).arg(toString(type));
412 
413  InsertAction action = QueryUserInsert(msg);
414  list = InsertChannels(list, info, action, type, inserted, skipped_inserts);
415  }
416  }
417 
418  // List what has been done with each channel
419  if (updated.size() > 0)
420  {
421  cout << endl << "Updated old channels (" << SimpleCountChannels(updated) << "):" << endl;
422  cout << FormatChannels(updated).toLatin1().constData() << endl;
423  }
424  if (skipped_updates.size() > 0)
425  {
426  cout << endl << "Skipped old channels (" << SimpleCountChannels(skipped_updates) << "):" << endl;
427  cout << FormatChannels(skipped_updates).toLatin1().constData() << endl;
428  }
429  if (inserted.size() > 0)
430  {
431  cout << endl << "Inserted new channels (" << SimpleCountChannels(inserted) << "):" << endl;
432  cout << FormatChannels(inserted).toLatin1().constData() << endl;
433  }
434  if (skipped_inserts.size() > 0)
435  {
436  cout << endl << "Skipped new channels (" << SimpleCountChannels(skipped_inserts) << "):" << endl;
437  cout << FormatChannels(skipped_inserts).toLatin1().constData() << endl;
438  }
439 
440  // Remaining channels and sum uniques again
441  if (list.size() > 0)
442  {
443  ChannelImporterBasicStats ninfo = CollectStats(list);
444  ChannelImporterUniquenessStats nstats = CollectUniquenessStats(list, ninfo);
445  cout << "Remaining channels (" << SimpleCountChannels(list) << "):" << endl;
446  cout << FormatChannels(list).toLatin1().constData() << endl;
447  cout << endl;
448  cout << GetSummary(list.size(), ninfo, nstats).toLatin1().constData();
449  cout << endl;
450  }
451 }
452 
453 // ChannelImporter::InsertChannels
454 //
455 // transports List of channels to update
456 // info Channel statistics
457 // action Insert all, Insert manually, Ignore all
458 // type Channel type such as dvb or atsc
459 // inserted_list List of inserted channels
460 // skipped_list List of skipped channels
461 //
462 // return: List of transports/channels that have not been inserted
463 //
465  const ScanDTVTransportList &transports,
466  const ChannelImporterBasicStats &info,
469  ScanDTVTransportList &inserted_list,
470  ScanDTVTransportList &skipped_list)
471 {
472  QString channelFormat = "%1_%2";
473 
474  ScanDTVTransportList next_list;
475 
476  bool cancel_all = false;
477  bool ok_all = false;
478 
479  // Insert all channels with non-conflicting channum
480  // and complete tuning information.
481  for (size_t i = 0; i < transports.size(); ++i)
482  {
483  ScanDTVTransport new_transport;
484  ScanDTVTransport inserted_transport;
485  ScanDTVTransport skipped_transport;
486 
487  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
488  {
489  ChannelInsertInfo chan = transports[i].m_channels[j];
490 
491  bool asked = false;
492  bool filter = false;
493  bool handle = false;
494  if (!chan.m_channel_id && (kInsertIgnoreAll == action) &&
495  IsType(info, chan, type))
496  {
497  filter = true;
498  }
499  else if (!chan.m_channel_id && IsType(info, chan, type))
500  {
501  handle = true;
502  }
503 
504  if (cancel_all)
505  {
506  handle = false;
507  }
508 
509  if (handle && kInsertManual == action)
510  {
511  OkCancelType rc = QueryUserInsert(transports[i], chan);
512  if (kOCTCancelAll == rc)
513  {
514  cancel_all = true;
515  handle = false;
516  }
517  else if (kOCTCancel == rc)
518  {
519  handle = false;
520  }
521  else if (kOCTOk == rc)
522  {
523  asked = true;
524  }
525  }
526 
527  if (handle)
528  {
529  bool conflicting = false;
530 
531  if (chan.m_chan_num.isEmpty() ||
533  {
534  if ((kATSCNonConflicting == type) ||
535  (kATSCConflicting == type))
536  {
537  chan.m_chan_num = channelFormat
538  .arg(chan.m_atsc_major_channel)
539  .arg(chan.m_atsc_minor_channel);
540  }
541  else if (chan.m_si_standard == "dvb")
542  {
543  chan.m_chan_num = QString("%1")
544  .arg(chan.m_service_id);
545  }
546  else if (chan.m_freqid.isEmpty())
547  {
548  chan.m_chan_num = QString("%1-%2")
549  .arg(chan.m_source_id)
550  .arg(chan.m_service_id);
551  }
552  else
553  {
554  chan.m_chan_num = QString("%1-%2")
555  .arg(chan.m_freqid)
556  .arg(chan.m_service_id);
557  }
558 
559  conflicting = ChannelUtil::IsConflicting(
560  chan.m_chan_num, chan.m_source_id);
561  }
562 
563  // Only ask if not already asked before with kInsertManual
564  if (m_is_interactive && !asked &&
565  (conflicting || (kChannelTypeConflictingFirst <= type)))
566  {
567  bool ok_done = false;
568  if (ok_all)
569  {
570  QString val = ComputeSuggestedChannelNum(chan);
571  bool ok = CheckChannelNumber(val, chan);
572  if (ok)
573  {
574  chan.m_chan_num = val;
575  conflicting = false;
576  ok_done = true;
577  }
578  }
579  if (!ok_done)
580  {
581  OkCancelType rc =
582  QueryUserResolve(transports[i], chan);
583 
584  conflicting = true;
585  if (kOCTCancelAll == rc)
586  {
587  cancel_all = true;
588  }
589  else if (kOCTOk == rc)
590  {
591  conflicting = false;
592  }
593  else if (kOCTOkAll == rc)
594  {
595  conflicting = false;
596  ok_all = true;
597  }
598  }
599  }
600 
601  if (conflicting)
602  {
603  handle = false;
604  }
605  }
606 
607  bool inserted = false;
608  if (handle)
609  {
610  int chanid = ChannelUtil::CreateChanID(
611  chan.m_source_id, chan.m_chan_num);
612 
613  chan.m_channel_id = (chanid > 0) ? chanid : chan.m_channel_id;
614 
615  if (chan.m_channel_id)
616  {
617  uint tsid = chan.m_vct_tsid;
618  tsid = (tsid) ? tsid : chan.m_sdt_tsid;
619  tsid = (tsid) ? tsid : chan.m_pat_tsid;
620  tsid = (tsid) ? tsid : chan.m_vct_chan_tsid;
621 
622  if (!chan.m_db_mplexid)
623  {
625  chan.m_source_id, transports[i], tsid, chan.m_orig_netid);
626  }
627  else
628  {
629  // Find the matching multiplex. This updates the
630  // transport and network ID's in case the transport
631  // was created manually
633  tsid, chan.m_orig_netid);
634  if (id >= 0)
635  chan.m_db_mplexid = id;
636  }
637  }
638 
639  if (chan.m_channel_id && chan.m_db_mplexid)
640  {
641  chan.m_channel_id = chanid;
642 
643  inserted = ChannelUtil::CreateChannel(
644  chan.m_db_mplexid,
645  chan.m_source_id,
646  chan.m_channel_id,
647  chan.m_callsign,
648  chan.m_service_name,
649  chan.m_chan_num,
650  chan.m_service_id,
653  chan.m_use_on_air_guide,
654  chan.m_hidden, chan.m_hidden_in_guide,
655  chan.m_freqid,
656  QString(),
657  chan.m_format,
658  QString(),
659  chan.m_default_authority);
660 
661  if (!transports[i].m_iptv_tuning.GetDataURL().isEmpty())
663  transports[i].m_iptv_tuning);
664  }
665  }
666 
667  if (inserted)
668  {
669  // Update list of inserted channels
670  AddChanToCopy(inserted_transport, transports[i], chan);
671  }
672 
673  if (filter)
674  {
675  // Update list of skipped channels
676  AddChanToCopy(skipped_transport, transports[i], chan);
677  }
678  else if (!inserted)
679  {
680  // Update list of remaining channels
681  AddChanToCopy(new_transport, transports[i], chan);
682  }
683  }
684 
685  if (new_transport.m_channels.size() > 0)
686  next_list.push_back(new_transport);
687 
688  if (skipped_transport.m_channels.size() > 0)
689  skipped_list.push_back(skipped_transport);
690 
691  if (inserted_transport.m_channels.size() > 0)
692  inserted_list.push_back(inserted_transport);
693  }
694 
695  return next_list;
696 }
697 
698 // ChannelImporter::UpdateChannels
699 //
700 // transports list of transports/channels to update
701 // info Channel statistics
702 // action Update All, Ignore All
703 // type Channel type such as dvb or atsc
704 // inserted List of inserted channels
705 // skipped List of skipped channels
706 //
707 // return: List of transports/channels that have not been updated
708 //
710  const ScanDTVTransportList &transports,
711  const ChannelImporterBasicStats &info,
714  ScanDTVTransportList &updated_list,
715  ScanDTVTransportList &skipped_list)
716 {
717  QString channelFormat = "%1_%2";
718  bool renameChannels = false;
719 
720  ScanDTVTransportList next_list;
721 
722  // update all channels with non-conflicting channum
723  // and complete tuning information.
724  for (size_t i = 0; i < transports.size(); ++i)
725  {
726  ScanDTVTransport new_transport;
727  ScanDTVTransport updated_transport;
728  ScanDTVTransport skipped_transport;
729 
730  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
731  {
732  ChannelInsertInfo chan = transports[i].m_channels[j];
733 
734  bool filter = false;
735  bool handle = false;
736  if (chan.m_channel_id && (kUpdateIgnoreAll == action) &&
737  IsType(info, chan, type))
738  {
739  filter = true;
740  }
741  else if (chan.m_channel_id && IsType(info, chan, type))
742  {
743  handle = true;
744  }
745 
746  if (handle)
747  {
748  bool conflicting = false;
749 
750  if (m_keep_channel_numbers)
751  {
753  }
754  if (chan.m_chan_num.isEmpty() || renameChannels ||
756  chan.m_chan_num, chan.m_source_id, chan.m_channel_id))
757  {
758  if (kATSCNonConflicting == type)
759  {
760  chan.m_chan_num = channelFormat
761  .arg(chan.m_atsc_major_channel)
762  .arg(chan.m_atsc_minor_channel);
763  }
764  else if (chan.m_si_standard == "dvb")
765  {
766  chan.m_chan_num = QString("%1")
767  .arg(chan.m_service_id);
768  }
769  else if (chan.m_freqid.isEmpty())
770  {
771  chan.m_chan_num = QString("%1-%2")
772  .arg(chan.m_source_id)
773  .arg(chan.m_service_id);
774  }
775  else
776  {
777  chan.m_chan_num = QString("%1-%2")
778  .arg(chan.m_freqid)
779  .arg(chan.m_service_id);
780  }
781 
782  conflicting = ChannelUtil::IsConflicting(
783  chan.m_chan_num, chan.m_source_id, chan.m_channel_id);
784  }
785 
786  if (conflicting)
787  {
788  handle = false;
789 
790  // Update list of skipped channels
791  AddChanToCopy(skipped_transport, transports[i], chan);
792  }
793  }
794 
795  bool updated = false;
796  if (handle)
797  {
799 
800  // Find the matching multiplex. This updates the
801  // transport and network ID's in case the transport
802  // was created manually
803  uint tsid = chan.m_vct_tsid;
804  tsid = (tsid) ? tsid : chan.m_sdt_tsid;
805  tsid = (tsid) ? tsid : chan.m_pat_tsid;
806  tsid = (tsid) ? tsid : chan.m_vct_chan_tsid;
808  tsid, chan.m_orig_netid);
809  if (id >= 0)
810  chan.m_db_mplexid = id;
811 
812  updated = ChannelUtil::UpdateChannel(
813  chan.m_db_mplexid,
814  chan.m_source_id,
815  chan.m_channel_id,
816  chan.m_callsign,
817  chan.m_service_name,
818  chan.m_chan_num,
819  chan.m_service_id,
822  chan.m_use_on_air_guide,
823  chan.m_hidden, chan.m_hidden_in_guide,
824  chan.m_freqid,
825  QString(),
826  chan.m_format,
827  QString(),
828  chan.m_default_authority);
829  }
830 
831  if (updated)
832  {
833  // Update list of updated channels
834  AddChanToCopy(updated_transport, transports[i], chan);
835  }
836 
837  if (filter)
838  {
839  // Update list of skipped channels
840  AddChanToCopy(skipped_transport, transports[i], chan);
841  }
842  else if (!updated)
843  {
844  // Update list of remaining channels
845  AddChanToCopy(new_transport, transports[i], chan);
846  }
847  }
848 
849  if (new_transport.m_channels.size() > 0)
850  next_list.push_back(new_transport);
851 
852  if (skipped_transport.m_channels.size() > 0)
853  skipped_list.push_back(skipped_transport);
854 
855  if (updated_transport.m_channels.size() > 0)
856  updated_list.push_back(updated_transport);
857  }
858 
859  return next_list;
860 }
861 
862 // ChannelImporter::AddChanToCopy
863 //
864 // Add channel to copy of transport.
865 // This is used to keep track of what is done with each channel
866 //
867 // transport_copy with zero to all channels of transport
868 // transport transport with channel info as scanned
869 // chan one channel of transport, to be copied
870 //
872  ScanDTVTransport &transport_copy,
873  const ScanDTVTransport &transport,
874  const ChannelInsertInfo &chan
875 )
876 {
877  if (transport_copy.m_channels.size() == 0)
878  {
879  transport_copy = transport;
880  transport_copy.m_channels.clear();
881  }
882  transport_copy.m_channels.push_back(chan);
883 }
884 
886 {
887  ScanDTVTransportList no_dups;
888 
890  if (!transports.empty())
891  tuner_type = transports[0].m_tuner_type;
892 
893  bool is_dvbs = ((DTVTunerType::kTunerTypeDVBS1 == tuner_type) ||
894  (DTVTunerType::kTunerTypeDVBS2 == tuner_type));
895 
896  uint freq_mult = (is_dvbs) ? 1 : 1000;
897 
898  vector<bool> ignore;
899  ignore.resize(transports.size());
900  for (size_t i = 0; i < transports.size(); ++i)
901  {
902  if (ignore[i])
903  continue;
904 
905  for (size_t j = i+1; j < transports.size(); ++j)
906  {
907  if (!transports[i].IsEqual(
908  tuner_type, transports[j], 500 * freq_mult))
909  {
910  continue;
911  }
912 
913  for (size_t k = 0; k < transports[j].m_channels.size(); ++k)
914  {
915  bool found_same = false;
916  for (size_t l = 0; l < transports[i].m_channels.size(); ++l)
917  {
918  if (transports[j].m_channels[k].IsSameChannel(
919  transports[i].m_channels[l]))
920  {
921  found_same = true;
922  transports[i].m_channels[l].ImportExtraInfo(
923  transports[j].m_channels[k]);
924  }
925  }
926  if (!found_same)
927  transports[i].m_channels.push_back(transports[j].m_channels[k]);
928  }
929  ignore[j] = true;
930  }
931  no_dups.push_back(transports[i]);
932  }
933 
934  transports = no_dups;
935 }
936 
938 {
939  bool require_av = (m_service_requirements & kRequireAV) == kRequireAV;
940  bool require_a = (m_service_requirements & kRequireAudio) != 0;
941 
942  for (size_t i = 0; i < transports.size(); ++i)
943  {
944  ChannelInsertInfoList filtered;
945  for (size_t k = 0; k < transports[i].m_channels.size(); ++k)
946  {
947  if (m_fta_only && transports[i].m_channels[k].m_is_encrypted &&
948  transports[i].m_channels[k].m_decryption_status != kEncDecrypted)
949  continue;
950 
951  if (require_a && transports[i].m_channels[k].m_is_data_service)
952  continue;
953 
954  if (require_av && transports[i].m_channels[k].m_is_audio_service)
955  continue;
956 
957  // Filter channels out that do not have a logical channel number
958  if (m_lcn_only && transports[i].m_channels[k].m_chan_num.isEmpty())
959  {
960  QString msg = FormatChannel(transports[i], transports[i].m_channels[k]);
961  LOG(VB_CHANSCAN, LOG_DEBUG, LOC + QString("No LCN: %1").arg(msg));
962  continue;
963  }
964 
965  // Filter channels out that are not present in PAT, PMT and SDT.
966  if (m_complete_only &&
967  !(transports[i].m_channels[k].m_in_pat &&
968  transports[i].m_channels[k].m_in_pmt &&
969  transports[i].m_channels[k].m_in_sdt &&
970  (transports[i].m_channels[k].m_pat_tsid ==
971  transports[i].m_channels[k].m_sdt_tsid)))
972  {
973  QString msg = FormatChannel(transports[i], transports[i].m_channels[k]);
974  LOG(VB_CHANSCAN, LOG_DEBUG, LOC + QString("Not in PAT/PMT/SDT: %1").arg(msg));
975  continue;
976  }
977 
978  // Filter channels out that do not have a name
979  if (m_complete_only && transports[i].m_channels[k].m_service_name.isEmpty())
980  {
981  QString msg = FormatChannel(transports[i], transports[i].m_channels[k]);
982  LOG(VB_CHANSCAN, LOG_DEBUG, LOC + QString("No name: %1").arg(msg));
983  continue;
984  }
985 
986  // Filter channels out only in channels.conf, i.e. not found
987  if (transports[i].m_channels[k].m_in_channels_conf &&
988  !(transports[i].m_channels[k].m_in_pat ||
989  transports[i].m_channels[k].m_in_pmt ||
990  transports[i].m_channels[k].m_in_vct ||
991  transports[i].m_channels[k].m_in_nit ||
992  transports[i].m_channels[k].m_in_sdt))
993  continue;
994 
995  filtered.push_back(transports[i].m_channels[k]);
996  }
997  transports[i].m_channels = filtered;
998  }
999 }
1000 
1009  uint sourceid, ScanDTVTransportList &transports) const
1010 {
1011  ScanDTVTransportList not_in_scan;
1012  int found_in_same_transport = 0;
1013  int found_in_other_transport = 0;
1014  int found_nowhere = 0;
1015 
1017  if (!transports.empty())
1018  tuner_type = transports[0].m_tuner_type;
1019 
1020  bool is_dvbs =
1021  (DTVTunerType::kTunerTypeDVBS1 == tuner_type) ||
1022  (DTVTunerType::kTunerTypeDVBS2 == tuner_type);
1023 
1024  uint freq_mult = (is_dvbs) ? 1 : 1000;
1025 
1026  MSqlQuery query(MSqlQuery::InitCon());
1027  query.prepare(
1028  "SELECT mplexid "
1029  "FROM dtv_multiplex "
1030  "WHERE sourceid = :SOURCEID "
1031  "GROUP BY mplexid "
1032  "ORDER BY mplexid");
1033  query.bindValue(":SOURCEID", sourceid);
1034 
1035  if (!query.exec())
1036  {
1037  MythDB::DBError("GetDBTransports()", query);
1038  return not_in_scan;
1039  }
1040 
1041  while (query.next())
1042  {
1043  ScanDTVTransport db_transport;
1044  uint mplexid = query.value(0).toUInt();
1045  if (db_transport.FillFromDB(tuner_type, mplexid))
1046  {
1047  if (db_transport.m_channels.empty())
1048  {
1049  continue;
1050  }
1051  }
1052 
1053  bool found_transport = false;
1054  QMap<uint,bool> found_chan;
1055 
1056  // Search for old channels in the same transport of the scan.
1057  for (size_t ist = 0; ist < transports.size(); ++ist) // All transports in scan
1058  {
1059  ScanDTVTransport &transport = transports[ist]; // Scanned transport
1060 
1061  if (transport.IsEqual(tuner_type, db_transport, 500 * freq_mult, true)) // Same transport?
1062  {
1063  found_transport = true;
1064  transport.m_mplex = db_transport.m_mplex; // Found multiplex
1065 
1066  for (size_t jdc = 0; jdc < db_transport.m_channels.size(); ++jdc) // All channels in database transport
1067  {
1068  if (!found_chan[jdc]) // Channel not found yet?
1069  {
1070  ChannelInsertInfo &db_chan = db_transport.m_channels[jdc]; // Channel in database transport
1071 
1072  for (size_t ktc = 0; ktc < transport.m_channels.size(); ++ktc) // All channels in scanned transport
1073  {
1074  ChannelInsertInfo &chan = transport.m_channels[ktc]; // Channel in scanned transport
1075  if (db_chan.IsSameChannel(chan, 2)) // Same transport, relaxed check
1076  {
1077  found_in_same_transport++;
1078  found_chan[jdc] = true; // Found channel from database in scan
1079  chan.m_db_mplexid = mplexid; // Found multiplex
1080  chan.m_channel_id = db_chan.m_channel_id; // This is the crucial field
1081  break; // Ready with scanned transport
1082  }
1083  }
1084  }
1085  }
1086  }
1087  }
1088 
1089  // Search for old channels in all transports of the scan.
1090  // This is done for all channels that have not yet been found.
1091  // This can identify the channels that have moved to another transport.
1092  if (m_full_channel_search)
1093  {
1094  for (size_t idc = 0; idc < db_transport.m_channels.size(); ++idc) // All channels in database transport
1095  {
1096  ChannelInsertInfo &db_chan = db_transport.m_channels[idc]; // Channel in database transport
1097 
1098  for (size_t jst = 0; jst < transports.size() && !found_chan[idc]; ++jst) // All transports in scan until found
1099  {
1100  ScanDTVTransport &transport = transports[jst]; // Scanned transport
1101  for (size_t ksc = 0; ksc < transport.m_channels.size(); ++ksc) // All channels in scanned transport
1102  {
1103  ChannelInsertInfo &chan = transport.m_channels[ksc]; // Channel in scanned transport
1104  if (db_chan.IsSameChannel(chan, 1)) // Different transport, check
1105  { // network id and service id
1106  found_in_other_transport++;
1107  found_chan[idc] = true; // Found channel from database in scan
1108  chan.m_channel_id = db_chan.m_channel_id; // This is the crucial field
1109  break; // Ready with scanned transport
1110  }
1111  }
1112  }
1113  }
1114  }
1115 
1116  // If the transport in the database is found in the scan
1117  // then all channels in that transport that are not found
1118  // in the scan are copied to the "not_in_scan" list.
1119  if (found_transport)
1120  {
1121  ScanDTVTransport tmp = db_transport;
1122  tmp.m_channels.clear();
1123 
1124  for (size_t idc = 0; idc < db_transport.m_channels.size(); ++idc)
1125  {
1126  if (!found_chan[idc])
1127  {
1128  tmp.m_channels.push_back(db_transport.m_channels[idc]);
1129  found_nowhere++;
1130  }
1131  }
1132 
1133  if (!tmp.m_channels.empty())
1134  not_in_scan.push_back(tmp);
1135  }
1136  }
1137  LOG(VB_GENERAL, LOG_INFO, LOC +
1138  QString("Old channels found in same transport: %1")
1139  .arg(found_in_same_transport));
1140  LOG(VB_GENERAL, LOG_INFO, LOC +
1141  QString("Old channels found in other transport: %1")
1142  .arg(found_in_other_transport));
1143  LOG(VB_GENERAL, LOG_INFO, LOC +
1144  QString("Old channels not found (off-air): %1")
1145  .arg(found_nowhere));
1146 
1147  return not_in_scan;
1148 }
1149 
1151 {
1153  for (size_t i = 0; i < transports.size(); ++i)
1154  {
1155  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
1156  {
1157  ChannelInsertInfo &chan = transports[i].m_channels[j];
1158  if (((chan.m_could_be_opencable && (chan.m_si_standard == "mpeg")) ||
1159  chan.m_is_opencable) && !chan.m_in_vct)
1160  {
1161  chan.m_si_standard = "opencable";
1162  }
1163  }
1164  }
1165 }
1166 
1168  const ScanDTVTransportList &transports)
1169 {
1171  for (size_t i = 0; i < transports.size(); ++i)
1172  {
1173  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
1174  {
1175  const ChannelInsertInfo &chan = transports[i].m_channels[j];
1176  int enc = (chan.m_is_encrypted) ?
1177  ((chan.m_decryption_status == kEncDecrypted) ? 2 : 1) : 0;
1178  info.m_atsc_channels[enc] += (chan.m_si_standard == "atsc");
1179  info.m_dvb_channels [enc] += (chan.m_si_standard == "dvb");
1180  info.m_mpeg_channels[enc] += (chan.m_si_standard == "mpeg");
1181  info.m_scte_channels[enc] += (chan.m_si_standard == "opencable");
1182  info.m_ntsc_channels[enc] += (chan.m_si_standard == "ntsc");
1183  if (chan.m_si_standard != "ntsc")
1184  {
1185  ++info.m_prognum_cnt[chan.m_service_id];
1186  ++info.m_channum_cnt[map_str(chan.m_chan_num)];
1187  }
1188  if (chan.m_si_standard == "atsc")
1189  {
1190  ++info.m_atscnum_cnt[(chan.m_atsc_major_channel << 16) |
1191  (chan.m_atsc_minor_channel)];
1192  ++info.m_atscmin_cnt[chan.m_atsc_minor_channel];
1193  ++info.m_atscmaj_cnt[chan.m_atsc_major_channel];
1194  }
1195  if (chan.m_si_standard == "ntsc")
1196  {
1197  ++info.m_atscnum_cnt[(chan.m_atsc_major_channel << 16) |
1198  (chan.m_atsc_minor_channel)];
1199  }
1200  }
1201  }
1202 
1203  return info;
1204 }
1205 
1207  const ScanDTVTransportList &transports,
1208  const ChannelImporterBasicStats &info)
1209 {
1211 
1212  for (size_t i = 0; i < transports.size(); ++i)
1213  {
1214  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
1215  {
1216  const ChannelInsertInfo &chan = transports[i].m_channels[j];
1217  stats.m_unique_prognum +=
1218  (info.m_prognum_cnt[chan.m_service_id] == 1) ? 1 : 0;
1219  stats.m_unique_channum +=
1220  (info.m_channum_cnt[map_str(chan.m_chan_num)] == 1) ? 1 : 0;
1221 
1222  if (chan.m_si_standard == "atsc")
1223  {
1224  stats.m_unique_atscnum +=
1225  (info.m_atscnum_cnt[(chan.m_atsc_major_channel << 16) |
1226  (chan.m_atsc_minor_channel)] == 1) ? 1 : 0;
1227  stats.m_unique_atscmin +=
1228  (info.m_atscmin_cnt[(chan.m_atsc_minor_channel)] == 1) ? 1 : 0;
1229  stats.m_max_atscmajcnt = max(
1230  stats.m_max_atscmajcnt,
1231  info.m_atscmaj_cnt[chan.m_atsc_major_channel]);
1232  }
1233  }
1234  }
1235 
1236  stats.m_unique_total = (stats.m_unique_prognum + stats.m_unique_atscnum +
1237  stats.m_unique_atscmin + stats.m_unique_channum);
1238 
1239  return stats;
1240 }
1241 
1242 
1244  const ScanDTVTransport &transport,
1245  const ChannelInsertInfo &chan,
1246  const ChannelImporterBasicStats *info)
1247 {
1248  QString msg;
1249  QTextStream ssMsg(&msg);
1250 
1251  ssMsg << transport.m_modulation.toString().toLatin1().constData()
1252  << ":";
1253  ssMsg << transport.m_frequency << ":";
1254 
1255  QString si_standard = (chan.m_si_standard=="opencable") ?
1256  QString("scte") : chan.m_si_standard;
1257 
1258  if (si_standard == "atsc" || si_standard == "scte")
1259  ssMsg << (QString("%1:%2:%3-%4:%5:%6=%7=%8:%9")
1260  .arg(chan.m_callsign).arg(chan.m_chan_num)
1261  .arg(chan.m_atsc_major_channel)
1262  .arg(chan.m_atsc_minor_channel)
1263  .arg(chan.m_service_id)
1264  .arg(chan.m_vct_tsid)
1265  .arg(chan.m_vct_chan_tsid)
1266  .arg(chan.m_pat_tsid)
1267  .arg(si_standard)).toLatin1().constData();
1268  else if (si_standard == "dvb")
1269  ssMsg << (QString("%1:%2:%3:%4:%5:%6=%7:%8")
1270  .arg(chan.m_service_name).arg(chan.m_chan_num)
1271  .arg(chan.m_netid).arg(chan.m_orig_netid)
1272  .arg(chan.m_service_id)
1273  .arg(chan.m_sdt_tsid)
1274  .arg(chan.m_pat_tsid)
1275  .arg(si_standard)).toLatin1().constData();
1276  else
1277  ssMsg << (QString("%1:%2:%3:%4:%5")
1278  .arg(chan.m_callsign).arg(chan.m_chan_num)
1279  .arg(chan.m_service_id)
1280  .arg(chan.m_pat_tsid)
1281  .arg(si_standard)).toLatin1().constData();
1282 
1283  if (info)
1284  {
1285  ssMsg <<"\t"
1286  << chan.m_channel_id;
1287  }
1288 
1289  if (info)
1290  {
1291  ssMsg << ":"
1292  << (QString("cnt(pnum:%1,channum:%2)")
1293  .arg(info->m_prognum_cnt[chan.m_service_id])
1294  .arg(info->m_channum_cnt[map_str(chan.m_chan_num)])
1295  ).toLatin1().constData();
1296  if (chan.m_si_standard == "atsc")
1297  {
1298  ssMsg <<
1299  (QString(":atsc_cnt(tot:%1,minor:%2)")
1300  .arg(info->m_atscnum_cnt[
1301  (chan.m_atsc_major_channel << 16) |
1302  (chan.m_atsc_minor_channel)])
1303  .arg(info->m_atscmin_cnt[
1304  chan.m_atsc_minor_channel])
1305  ).toLatin1().constData();
1306  }
1307  }
1308 
1309  return msg;
1310 }
1311 
1324  const ScanDTVTransport &/*transport*/,
1325  const ChannelInsertInfo &chan)
1326 {
1327  QString msg;
1328  QTextStream ssMsg(&msg);
1329 
1330  QString si_standard = (chan.m_si_standard=="opencable") ?
1331  QString("scte") : chan.m_si_standard;
1332 
1333  if (si_standard == "atsc" || si_standard == "scte")
1334  {
1335 
1336  if (si_standard == "atsc")
1337  ssMsg << (QString("%1-%2")
1338  .arg(chan.m_atsc_major_channel)
1339  .arg(chan.m_atsc_minor_channel)).toLatin1().constData();
1340  else if (chan.m_freqid.isEmpty())
1341  ssMsg << (QString("%1-%2")
1342  .arg(chan.m_source_id)
1343  .arg(chan.m_service_id)).toLatin1().constData();
1344  else
1345  ssMsg << (QString("%1-%2")
1346  .arg(chan.m_freqid)
1347  .arg(chan.m_service_id)).toLatin1().constData();
1348 
1349  if (!chan.m_callsign.isEmpty())
1350  ssMsg << (QString(" (%1)")
1351  .arg(chan.m_callsign)).toLatin1().constData();
1352  }
1353  else if (si_standard == "dvb")
1354  ssMsg << (QString("%1 (%2 %3)")
1355  .arg(chan.m_service_name).arg(chan.m_service_id)
1356  .arg(chan.m_netid)).toLatin1().constData();
1357  else if (chan.m_freqid.isEmpty())
1358  ssMsg << (QString("%1-%2")
1359  .arg(chan.m_source_id).arg(chan.m_service_id))
1360  .toLatin1().constData();
1361  else
1362  ssMsg << (QString("%1-%2")
1363  .arg(chan.m_freqid).arg(chan.m_service_id))
1364  .toLatin1().constData();
1365 
1366  return msg;
1367 }
1368 
1370  const ScanDTVTransportList &transports_in,
1371  const ChannelImporterBasicStats *info)
1372 {
1373  // Sort transports in order of increasing frequency
1374  struct less_than_key
1375  {
1376  inline bool operator() (const ScanDTVTransport &t1, const ScanDTVTransport &t2)
1377  {
1378  return t1.m_frequency < t2.m_frequency;
1379  }
1380  };
1381  ScanDTVTransportList transports(transports_in);
1382  std::sort(transports.begin(), transports.end(), less_than_key());
1383 
1384  QString msg;
1385 
1386  for (size_t i = 0; i < transports.size(); ++i)
1387  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
1388  msg += FormatChannel(transports[i], transports[i].m_channels[j],
1389  info) + "\n";
1390 
1391  return msg;
1392 }
1393 
1395  const ScanDTVTransport &transport)
1396 {
1397  QString msg;
1398  QTextStream ssMsg(&msg);
1399 
1400  ssMsg << transport.m_modulation.toString().toLatin1().constData() << ":";
1401  ssMsg << transport.m_frequency;
1402 
1403  return msg;
1404 }
1405 
1407  const ScanDTVTransportList &transports_in)
1408 {
1409  // Sort transports in order of increasing frequency
1410  struct less_than_key
1411  {
1412  inline bool operator() (const ScanDTVTransport &t1, const ScanDTVTransport &t2)
1413  {
1414  return t1.m_frequency < t2.m_frequency;
1415  }
1416  };
1417  ScanDTVTransportList transports(transports_in);
1418  std::sort(transports.begin(), transports.end(), less_than_key());
1419 
1420  QString msg;
1421 
1422  for (size_t i = 0; i < transports.size(); ++i)
1423  msg += FormatTransport(transports[i]) + "\n";
1424 
1425  return msg;
1426 }
1427 
1429  uint transport_count,
1430  const ChannelImporterBasicStats &info,
1431  const ChannelImporterUniquenessStats &stats)
1432 {
1433  //: %n is the number of transports
1434  QString msg = tr("Found %n transport(s):\n", "", transport_count);
1435  msg += tr("Channels: FTA Enc Dec\n") +
1436  QString("ATSC %1 %2 %3\n")
1437  .arg(info.m_atsc_channels[0],3).arg(info.m_atsc_channels[1],3)
1438  .arg(info.m_atsc_channels[2],3) +
1439  QString("DVB %1 %2 %3\n")
1440  .arg(info.m_dvb_channels [0],3).arg(info.m_dvb_channels [1],3)
1441  .arg(info.m_dvb_channels [2],3) +
1442  QString("SCTE %1 %2 %3\n")
1443  .arg(info.m_scte_channels[0],3).arg(info.m_scte_channels[1],3)
1444  .arg(info.m_scte_channels[2],3) +
1445  QString("MPEG %1 %2 %3\n")
1446  .arg(info.m_mpeg_channels[0],3).arg(info.m_mpeg_channels[1],3)
1447  .arg(info.m_mpeg_channels[2],3) +
1448  QString("NTSC %1\n").arg(info.m_ntsc_channels[0],3) +
1449  tr("Unique: prog %1 atsc %2 atsc minor %3 channum %4\n")
1450  .arg(stats.m_unique_prognum).arg(stats.m_unique_atscnum)
1451  .arg(stats.m_unique_atscmin).arg(stats.m_unique_channum) +
1452  tr("Max atsc major count: %1")
1453  .arg(stats.m_max_atscmajcnt);
1454 
1455  return msg;
1456 }
1457 
1459  const ChannelImporterBasicStats &info,
1460  const ChannelInsertInfo &chan, ChannelType type)
1461 {
1462  switch (type)
1463  {
1464  case kATSCNonConflicting:
1465  return ((chan.m_si_standard == "atsc") /* &&
1466  (info.m_atscnum_cnt[(chan.m_atsc_major_channel << 16) |
1467  (chan.m_atsc_minor_channel)] == 1) */);
1468 
1469  case kDVBNonConflicting:
1470  return ((chan.m_si_standard == "dvb") /* &&
1471  (info.m_prognum_cnt[chan.m_service_id] == 1) */);
1472 
1473  case kMPEGNonConflicting:
1474  return ((chan.m_si_standard == "mpeg") &&
1475  (info.m_channum_cnt[map_str(chan.m_chan_num)] == 1));
1476 
1477  case kSCTENonConflicting:
1478  return (((chan.m_si_standard == "scte") ||
1479  (chan.m_si_standard == "opencable")) &&
1480  (info.m_channum_cnt[map_str(chan.m_chan_num)] == 1));
1481 
1482  case kNTSCNonConflicting:
1483  return ((chan.m_si_standard == "ntsc") &&
1484  (info.m_atscnum_cnt[(chan.m_atsc_major_channel << 16) |
1485  (chan.m_atsc_minor_channel)] == 1));
1486 
1487  case kATSCConflicting:
1488  return ((chan.m_si_standard == "atsc") &&
1489  (info.m_atscnum_cnt[(chan.m_atsc_major_channel << 16) |
1490  (chan.m_atsc_minor_channel)] != 1));
1491 
1492  case kDVBConflicting:
1493  return ((chan.m_si_standard == "dvb") &&
1494  (info.m_prognum_cnt[chan.m_service_id] != 1));
1495 
1496  case kMPEGConflicting:
1497  return ((chan.m_si_standard == "mpeg") &&
1498  (info.m_channum_cnt[map_str(chan.m_chan_num)] != 1));
1499 
1500  case kSCTEConflicting:
1501  return (((chan.m_si_standard == "scte") ||
1502  (chan.m_si_standard == "opencable")) &&
1503  (info.m_channum_cnt[map_str(chan.m_chan_num)] != 1));
1504 
1505  case kNTSCConflicting:
1506  return ((chan.m_si_standard == "ntsc") &&
1507  (info.m_atscnum_cnt[(chan.m_atsc_major_channel << 16) |
1508  (chan.m_atsc_minor_channel)] != 1));
1509  }
1510  return false;
1511 }
1512 
1514  const ScanDTVTransportList &transports,
1515  const ChannelImporterBasicStats &info,
1516  ChannelType type, uint &new_chan, uint &old_chan)
1517 {
1518  new_chan = old_chan = 0;
1519  for (size_t i = 0; i < transports.size(); ++i)
1520  {
1521  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
1522  {
1523  ChannelInsertInfo chan = transports[i].m_channels[j];
1524  if (IsType(info, chan, type))
1525  {
1526  if (chan.m_channel_id)
1527  ++old_chan;
1528  else
1529  ++new_chan;
1530  }
1531  }
1532  }
1533 }
1534 
1536  const ScanDTVTransportList &transports)
1537 {
1538  int count = 0;
1539  for (size_t i = 0; i < transports.size(); ++i)
1540  {
1541  count += transports[i].m_channels.size();
1542  }
1543  return count;
1544 }
1545 
1558  const ChannelInsertInfo &chan)
1559 {
1560  static QMutex last_free_lock;
1561  static QMap<uint,uint> last_free_chan_num_map;
1562 
1563  // Suggest existing channel number if non-conflicting
1565  return chan.m_chan_num;
1566 
1567  QString channelFormat = "%1_%2";
1568  QString chan_num = channelFormat
1569  .arg(chan.m_atsc_major_channel)
1570  .arg(chan.m_atsc_minor_channel);
1571 
1572  if (!chan.m_atsc_minor_channel)
1573  {
1574  if (chan.m_si_standard == "dvb")
1575  {
1576  chan_num = QString("%1")
1577  .arg(chan.m_service_id);
1578  }
1579  else if (chan.m_freqid.isEmpty())
1580  {
1581  chan_num = QString("%1-%2")
1582  .arg(chan.m_source_id)
1583  .arg(chan.m_service_id);
1584  }
1585  else
1586  {
1587  chan_num = QString("%1-%2")
1588  .arg(chan.m_freqid)
1589  .arg(chan.m_service_id);
1590  }
1591  }
1592 
1593  if (!ChannelUtil::IsConflicting(chan_num, chan.m_source_id))
1594  return chan_num;
1595 
1596  QMutexLocker locker(&last_free_lock);
1597  uint last_free_chan_num = last_free_chan_num_map[chan.m_source_id];
1598  for (last_free_chan_num++; ; ++last_free_chan_num)
1599  {
1600  chan_num = QString::number(last_free_chan_num);
1601  if (!ChannelUtil::IsConflicting(chan_num, chan.m_source_id))
1602  break;
1603  }
1604  // cppcheck-suppress unreadVariable
1605  last_free_chan_num_map[chan.m_source_id] = last_free_chan_num;
1606 
1607  return chan_num;
1608 }
1609 
1612 {
1613  DeleteAction action = kDeleteAll;
1614  if (m_use_gui)
1615  {
1616  int ret = -1;
1617  do
1618  {
1619  MythScreenStack *popupStack =
1620  GetMythMainWindow()->GetStack("popup stack");
1621  MythDialogBox *deleteDialog =
1622  new MythDialogBox(msg, popupStack, "deletechannels");
1623 
1624  if (deleteDialog->Create())
1625  {
1626  deleteDialog->AddButton(tr("Delete All"));
1627  deleteDialog->AddButton(tr("Set all invisible"));
1628 // deleteDialog->AddButton(tr("Handle manually"));
1629  deleteDialog->AddButton(tr("Ignore All"));
1630  QObject::connect(deleteDialog, &MythDialogBox::Closed,
1631  [&](const QString & /*resultId*/, int result)
1632  {
1633  ret = result;
1634  m_eventLoop.quit();
1635  });
1636  popupStack->AddScreen(deleteDialog);
1637 
1638  m_eventLoop.exec();
1639  }
1640  } while (ret < 0);
1641 
1642  action = (0 == ret) ? kDeleteAll : action;
1643  action = (1 == ret) ? kDeleteInvisibleAll : action;
1644  action = (2 == ret) ? kDeleteIgnoreAll : action;
1645 // action = (2 == m_deleteChannelResult) ? kDeleteManual : action;
1646 // action = (3 == m_deleteChannelResult) ? kDeleteIgnoreAll : action;
1647  }
1648  else if (m_is_interactive)
1649  {
1650  cout << msg.toLatin1().constData()
1651  << endl
1652  << tr("Do you want to:").toLatin1().constData()
1653  << endl
1654  << tr("1. Delete All").toLatin1().constData()
1655  << endl
1656  << tr("2. Set all invisible").toLatin1().constData()
1657  << endl
1658 // cout << "3. Handle manually" << endl;
1659  << tr("4. Ignore All").toLatin1().constData()
1660  << endl;
1661  while (true)
1662  {
1663  string ret;
1664  cin >> ret;
1665  bool ok;
1666  uint val = QString(ret.c_str()).toUInt(&ok);
1667  if (ok && (val == 1 || val == 2 || val == 4))
1668  {
1669  action = (1 == val) ? kDeleteAll : action;
1670  action = (2 == val) ? kDeleteInvisibleAll : action;
1671  //action = (3 == val) ? kDeleteManual : action;
1672  action = (4 == val) ? kDeleteIgnoreAll : action;
1673  break;
1674  }
1675 
1676  //cout << "Please enter either 1, 2, 3 or 4:" << endl;
1677  cout << tr("Please enter either 1, 2 or 4:")
1678  .toLatin1().constData() << endl;//
1679  }
1680  }
1681 
1682  return action;
1683 }
1684 
1687 {
1688  InsertAction action = kInsertAll;
1689  if (m_use_gui)
1690  {
1691  int ret = -1;
1692  do
1693  {
1694  MythScreenStack *popupStack =
1695  GetMythMainWindow()->GetStack("popup stack");
1696  MythDialogBox *insertDialog =
1697  new MythDialogBox(msg, popupStack, "insertchannels");
1698 
1699  if (insertDialog->Create())
1700  {
1701  insertDialog->AddButton(tr("Insert All"));
1702  insertDialog->AddButton(tr("Insert Manually"));
1703  insertDialog->AddButton(tr("Ignore All"));
1704  QObject::connect(insertDialog, &MythDialogBox::Closed,
1705  [&](const QString & /*resultId*/, int result)
1706  {
1707  ret = result;
1708  m_eventLoop.quit();
1709  });
1710 
1711  popupStack->AddScreen(insertDialog);
1712  m_eventLoop.exec();
1713  }
1714  } while (ret < 0);
1715 
1716  action = (0 == ret) ? kInsertAll : action;
1717  action = (1 == ret) ? kInsertManual : action;
1718  action = (2 == ret) ? kInsertIgnoreAll : action;
1719  }
1720  else if (m_is_interactive)
1721  {
1722  cout << msg.toLatin1().constData()
1723  << endl
1724  << tr("Do you want to:").toLatin1().constData()
1725  << endl
1726  << tr("1. Insert All").toLatin1().constData()
1727  << endl
1728  << tr("2. Insert Manually").toLatin1().constData()
1729  << endl
1730  << tr("3. Ignore All").toLatin1().constData()
1731  << endl;
1732  while (true)
1733  {
1734  string ret;
1735  cin >> ret;
1736  bool ok;
1737  uint val = QString(ret.c_str()).toUInt(&ok);
1738  if (ok && (1 <= val) && (val <= 3))
1739  {
1740  action = (1 == val) ? kInsertAll : action;
1741  action = (2 == val) ? kInsertManual : action;
1742  action = (3 == val) ? kInsertIgnoreAll : action;
1743  break;
1744  }
1745 
1746  cout << tr("Please enter either 1, 2, or 3:")
1747  .toLatin1().constData() << endl;
1748  }
1749  }
1750 
1751  return action;
1752 }
1753 
1756 {
1757  UpdateAction action = kUpdateAll;
1758 
1759  if (m_use_gui)
1760  {
1761  int ret = -1;
1762  do
1763  {
1764  MythScreenStack *popupStack =
1765  GetMythMainWindow()->GetStack("popup stack");
1766  MythDialogBox *updateDialog =
1767  new MythDialogBox(msg, popupStack, "updatechannels");
1768 
1769  if (updateDialog->Create())
1770  {
1771  updateDialog->AddButton(tr("Update All"));
1772  updateDialog->AddButton(tr("Ignore All"));
1773  QObject::connect(updateDialog, &MythDialogBox::Closed,
1774  [&](const QString& /*resultId*/, int result)
1775  {
1776  ret = result;
1777  m_eventLoop.quit();
1778  });
1779 
1780  popupStack->AddScreen(updateDialog);
1781  m_eventLoop.exec();
1782  }
1783  } while (ret < 0);
1784 
1785  action = (0 == ret) ? kUpdateAll : action;
1786  action = (1 == ret) ? kUpdateIgnoreAll : action;
1787  }
1788  else if (m_is_interactive)
1789  {
1790  cout << msg.toLatin1().constData()
1791  << endl
1792  << tr("Do you want to:").toLatin1().constData()
1793  << endl
1794  << tr("1. Update All").toLatin1().constData()
1795  << endl
1796  << tr("2. Update Manually").toLatin1().constData()
1797  << endl
1798  << tr("3. Ignore All").toLatin1().constData()
1799  << endl;
1800  while (true)
1801  {
1802  string ret;
1803  cin >> ret;
1804  bool ok;
1805  uint val = QString(ret.c_str()).toUInt(&ok);
1806  if (ok && (1 <= val) && (val <= 3))
1807  {
1808  action = (1 == val) ? kUpdateAll : action;
1809  action = (2 == val) ? kUpdateManual : action;
1810  action = (3 == val) ? kUpdateIgnoreAll : action;
1811  break;
1812  }
1813 
1814  cout << tr("Please enter either 1, 2, or 3:")
1815  .toLatin1().constData() << endl;
1816  }
1817  }
1818 
1819  return action;
1820 }
1821 
1823  MythMainWindow *parent, const QString& title,
1824  const QString& message, QString &text)
1825 {
1826  int dc = -1;
1827  MythScreenStack *popupStack = parent->GetStack("popup stack");
1828  MythDialogBox *popup = new MythDialogBox(title, message, popupStack,
1829  "manualchannelpopup");
1830 
1831  if (popup->Create())
1832  {
1833  popup->AddButton(QCoreApplication::translate("(Common)", "OK"));
1834  popup->AddButton(tr("Edit"));
1835  popup->AddButton(QCoreApplication::translate("(Common)", "Cancel"));
1836  popup->AddButton(QCoreApplication::translate("(Common)", "Cancel All"));
1837  QObject::connect(popup, &MythDialogBox::Closed,
1838  [&](const QString & /*resultId*/, int result)
1839  {
1840  dc = result;
1841  m_eventLoop.quit();
1842  });
1843  popupStack->AddScreen(popup);
1844  m_eventLoop.exec();
1845  }
1846  else
1847  {
1848  delete popup;
1849  popup = nullptr;
1850  }
1851 
1852  // Choice "Edit"
1853  if (1 == dc)
1854  {
1855  MythTextInputDialog *textEdit =
1856  new MythTextInputDialog(popupStack,
1857  tr("Please enter a unique channel number."),
1858  FilterNone, false, text);
1859  if (textEdit->Create())
1860  {
1861  QObject::connect(textEdit, &MythTextInputDialog::haveResult,
1862  [&](QString result)
1863  {
1864  dc = 0;
1865  text = std::move(result);
1866  });
1867  QObject::connect(textEdit, &MythTextInputDialog::Exiting,
1868  [&]()
1869  {
1870  m_eventLoop.quit();
1871  });
1872 
1873  popupStack->AddScreen(textEdit);
1874  m_eventLoop.exec();
1875  }
1876  else
1877  delete textEdit;
1878  }
1879 
1880  OkCancelType rval = kOCTCancel;
1881  switch (dc) {
1882  case 0: rval = kOCTOk; break;
1883  case 1: rval = kOCTCancel; break; // "Edit" is done already
1884  case 2: rval = kOCTCancel; break;
1885  case 3: rval = kOCTCancelAll; break;
1886  }
1887  return rval;
1888 }
1889 
1891  MythMainWindow *parent, const QString& title,
1892  const QString& message, QString &text)
1893 {
1894  int dc = -1;
1895  MythScreenStack *popupStack = parent->GetStack("popup stack");
1896  MythDialogBox *popup = new MythDialogBox(title, message, popupStack,
1897  "resolvechannelpopup");
1898 
1899  if (popup->Create())
1900  {
1901  popup->AddButton(QCoreApplication::translate("(Common)", "OK"));
1902  popup->AddButton(QCoreApplication::translate("(Common)", "OK All"));
1903  popup->AddButton(tr("Edit"));
1904  popup->AddButton(QCoreApplication::translate("(Common)", "Cancel"));
1905  popup->AddButton(QCoreApplication::translate("(Common)", "Cancel All"));
1906  QObject::connect(popup, &MythDialogBox::Closed,
1907  [&](const QString & /*resultId*/, int result)
1908  {
1909  dc = result;
1910  m_eventLoop.quit();
1911  });
1912  popupStack->AddScreen(popup);
1913  m_eventLoop.exec();
1914  }
1915  else
1916  {
1917  delete popup;
1918  popup = nullptr;
1919  }
1920 
1921  // Choice "Edit"
1922  if (2 == dc)
1923  {
1924  MythTextInputDialog *textEdit =
1925  new MythTextInputDialog(popupStack,
1926  tr("Please enter a unique channel number."),
1927  FilterNone, false, text);
1928  if (textEdit->Create())
1929  {
1930  QObject::connect(textEdit, &MythTextInputDialog::haveResult,
1931  [&](QString result)
1932  {
1933  dc = 0;
1934  text = std::move(result);
1935  });
1936  QObject::connect(textEdit, &MythTextInputDialog::Exiting,
1937  [&]()
1938  {
1939  m_eventLoop.quit();
1940  });
1941 
1942  popupStack->AddScreen(textEdit);
1943  m_eventLoop.exec();
1944  }
1945  else
1946  delete textEdit;
1947  }
1948 
1949  OkCancelType rval = kOCTCancel;
1950  switch (dc) {
1951  case 0: rval = kOCTOk; break;
1952  case 1: rval = kOCTOkAll; break;
1953  case 2: rval = kOCTCancel; break; // "Edit" is done already
1954  case 3: rval = kOCTCancel; break;
1955  case 4: rval = kOCTCancelAll; break;
1956  }
1957  return rval;
1958 }
1959 
1961  const ScanDTVTransport &transport,
1962  ChannelInsertInfo &chan)
1963 {
1964  QString msg = tr("Channel %1 has channel number %2 but that is already in use.")
1965  .arg(SimpleFormatChannel(transport, chan))
1966  .arg(chan.m_chan_num);
1967 
1968  OkCancelType ret = kOCTCancel;
1969 
1970  if (m_use_gui)
1971  {
1972  while (true)
1973  {
1974  QString msg2 = msg;
1975  msg2 += "\n";
1976  msg2 += tr("Please enter a unique channel number.");
1977 
1978  QString val = ComputeSuggestedChannelNum(chan);
1979  msg2 += "\n";
1980  msg2 += tr("Default value is %1.").arg(val);
1981  ret = ShowResolveChannelPopup(
1982  GetMythMainWindow(), tr("Channel Importer"),
1983  msg2, val);
1984 
1985  if (kOCTOk != ret && kOCTOkAll != ret)
1986  break; // user canceled..
1987 
1988  bool ok = CheckChannelNumber(val, chan);
1989  if (ok)
1990  {
1991  chan.m_chan_num = val;
1992  break;
1993  }
1994  }
1995  }
1996  else if (m_is_interactive)
1997  {
1998  cout << msg.toLatin1().constData() << endl;
1999 
2000  QString cancelStr = QCoreApplication::translate("(Common)",
2001  "Cancel").toLower();
2002  QString cancelAllStr = QCoreApplication::translate("(Common)",
2003  "Cancel All").toLower();
2004  QString msg2 = tr("Please enter a non-conflicting channel number "
2005  "(or type '%1' to skip, '%2' to skip all):")
2006  .arg(cancelStr).arg(cancelAllStr);
2007 
2008  while (true)
2009  {
2010  cout << msg2.toLatin1().constData() << endl;
2011  string sret;
2012  cin >> sret;
2013  QString val = QString(sret.c_str());
2014  if (val.toLower() == cancelStr)
2015  {
2016  ret = kOCTCancel;
2017  break; // user canceled..
2018  }
2019  if (val.toLower() == cancelAllStr)
2020  {
2021  ret = kOCTCancelAll;
2022  break; // user canceled..
2023  }
2024 
2025  bool ok = CheckChannelNumber(val, chan);
2026  if (ok)
2027  {
2028  chan.m_chan_num = val;
2029  ret = kOCTOk;
2030  break;
2031  }
2032  }
2033  }
2034 
2035  return ret;
2036 }
2037 
2039  const ScanDTVTransport &transport,
2040  ChannelInsertInfo &chan)
2041 {
2042  QString msg = tr("You chose to manually insert channel %1.")
2043  .arg(SimpleFormatChannel(transport, chan));
2044 
2045  OkCancelType ret = kOCTCancel;
2046 
2047  if (m_use_gui)
2048  {
2049  while (true)
2050  {
2051  QString msg2 = msg;
2052  msg2 += " ";
2053  msg2 += tr("Please enter a unique channel number.");
2054 
2055  QString val = ComputeSuggestedChannelNum(chan);
2056  msg2 += " ";
2057  msg2 += tr("Default value is %1").arg(val);
2058  ret = ShowManualChannelPopup(
2059  GetMythMainWindow(), tr("Channel Importer"),
2060  msg2, val);
2061 
2062  if (kOCTOk != ret)
2063  break; // user canceled..
2064 
2065  bool ok = CheckChannelNumber(val, chan);
2066  if (ok)
2067  {
2068  chan.m_chan_num = val;
2069  ret = kOCTOk;
2070  break;
2071  }
2072  }
2073  }
2074  else if (m_is_interactive)
2075  {
2076  cout << msg.toLatin1().constData() << endl;
2077 
2078  QString cancelStr = QCoreApplication::translate("(Common)", "Cancel").toLower();
2079  QString cancelAllStr = QCoreApplication::translate("(Common)", "Cancel All").toLower();
2080 
2081  //: %1 is the translation of "Cancel", %2 of "Cancel All"
2082  QString msg2 = tr("Please enter a non-conflicting channel number "
2083  "(or type '%1' to skip, '%2' to skip all): ")
2084  .arg(cancelStr).arg(cancelAllStr);
2085 
2086  while (true)
2087  {
2088  cout << msg2.toLatin1().constData() << endl;
2089  string sret;
2090  cin >> sret;
2091  QString val = QString(sret.c_str());
2092  if (val.toLower() == cancelStr)
2093  {
2094  ret = kOCTCancel;
2095  break; // user canceled..
2096  }
2097  if (val.toLower() == cancelAllStr)
2098  {
2099  ret = kOCTCancelAll;
2100  break; // user canceled..
2101  }
2102 
2103  bool ok = CheckChannelNumber(val, chan);
2104  if (ok)
2105  {
2106  chan.m_chan_num = val;
2107  ret = kOCTOk;
2108  break;
2109  }
2110  }
2111  }
2112 
2113  return ret;
2114 }
2115 
2116 // ChannelImporter::CheckChannelNumber
2117 //
2118 // Check validity of a new channel number.
2119 // The channel number is not a number but it is a string that starts with a digit.
2120 // The channel number should not yet exist in this video source.
2121 //
2123  const QString &num,
2124  const ChannelInsertInfo &chan)
2125 {
2126  bool ok = (num.length() >= 1);
2127  ok = ok && ((num[0] >= '0') && (num[0] <= '9'));
2128  ok = ok && !ChannelUtil::IsConflicting(
2129  num, chan.m_source_id, chan.m_channel_id);
2130  return ok;
2131 }
QString toString(ChannelType type)
static QString FormatTransport(const ScanDTVTransport &transport)
static QString GetSummary(uint transport_count, const ChannelImporterBasicStats &info, const ChannelImporterUniquenessStats &stats)
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:782
static bool SetVisible(uint channel_id, bool visible)
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, bool hidden, bool hidden_in_guide, const QString &freqid, QString icon=QString(), QString format="Default", QString xmltvid=QString(), QString default_authority=QString())
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:863
static QString ComputeSuggestedChannelNum(const ChannelInsertInfo &chan)
Compute a suggested channel number based on various aspects of the channel information.
uint SaveScan(const ScanDTVTransportList &scan)
Definition: scaninfo.cpp:21
QString m_service_name
Definition: channelinfo.h:205
QMap< QString, uint > m_channum_cnt
static QString map_str(QString str)
MythConfirmationDialog * ShowOkPopup(const QString &message, QObject *parent, const char *slot, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
QString toString(MarkTypes type)
static const int kTunerTypeATSC
static bool IsType(const ChannelImporterBasicStats &info, const ChannelInsertInfo &chan, ChannelType type)
static bool CheckChannelNumber(const QString &num, const ChannelInsertInfo &chan)
OkCancelType ShowResolveChannelPopup(MythMainWindow *parent, const QString &title, const QString &message, QString &text)
DeleteAction QueryUserDelete(const QString &msg)
For multiple channels.
static QString FormatChannels(const ScanDTVTransportList &transports, const ChannelImporterBasicStats *info=nullptr)
QMap< uint, uint > m_prognum_cnt
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
static QString FormatChannel(const ScanDTVTransport &transport, const ChannelInsertInfo &chan, const ChannelImporterBasicStats *info=nullptr)
Basic menu dialog, message and a list of options.
UpdateAction QueryUserUpdate(const QString &msg)
For multiple channels.
vector< ScanDTVTransport > ScanDTVTransportList
Definition: dtvmultiplex.h:141
int size(void) const
Definition: mythdbcon.h:203
static int GetBetterMplexID(int current_mplexid, int transport_id, int network_id)
Returns best match multiplex ID, creating one if needed.
MythScreenStack * GetStack(const QString &stackname)
void FilterServices(ScanDTVTransportList &transports) const
unsigned int uint
Definition: compat.h:140
QMap< uint, uint > m_atscmaj_cnt
QMap< uint, uint > m_atscmin_cnt
static guint32 * tmp
Definition: goom_core.c:35
static const int kTunerTypeDVBS1
static void FixUpOpenCable(ScanDTVTransportList &transports)
static bool SetChannelValue(const QString &field_name, const QString &value, uint sourceid, const QString &channum)
void InsertChannels(const ScanDTVTransportList &, const ChannelImporterBasicStats &)
#define LOC
ChannelInsertInfoList m_channels
Definition: dtvmultiplex.h:139
static QString SimpleFormatChannel(const ScanDTVTransport &transport, const ChannelInsertInfo &chan)
Format channel information into a simple string.
QVariant value(int i) const
Definition: mythdbcon.h:198
void AddButton(const QString &title, QVariant data=0, bool newMenu=false, bool setCurrent=false)
OkCancelType ShowManualChannelPopup(MythMainWindow *parent, const QString &title, const QString &message, QString &text)
bool IsSameChannel(const ChannelInsertInfo &, int relaxed=0) const
bool Create(void) override
static int SimpleCountChannels(const ScanDTVTransportList &transports)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
static const int kTunerTypeDVBS2
DTVModulation m_modulation
Definition: dtvmultiplex.h:100
void haveResult(QString)
bool IsEqual(DTVTunerType type, const DTVMultiplex &other, uint freq_range=0, bool fuzzy=false) const
static void AddChanToCopy(ScanDTVTransport &transport_copy, const ScanDTVTransport &transport, const ChannelInsertInfo &chan)
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
Definition: mythlogging.h:24
static bool IsConflicting(const QString &channum, uint sourceid=0, uint excluded_chanid=0)
Definition: channelutil.h:281
ScanDTVTransportList GetDBTransports(uint sourceid, ScanDTVTransportList &) const
Adds found channel info to transports list, returns channels in DB which were not found in scan in an...
QString toString() const
InsertAction QueryUserInsert(const QString &msg)
For multiple channels.
static void CountChannels(const ScanDTVTransportList &transports, const ChannelImporterBasicStats &info, ChannelType type, uint &new_chan, uint &old_chan)
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
static bool CreateIPTVTuningData(uint channel_id, const IPTVTuningData &tuning)
Definition: channelutil.h:140
static int CreateChanID(uint sourceid, const QString &chan_num)
Creates a unique channel ID for database use.
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, bool hidden, bool hidden_in_guide, const QString &freqid=QString(), const QString &icon=QString(), QString format=QString(), const QString &xmltvid=QString(), const QString &default_authority=QString())
vector< ChannelInsertInfo > ChannelInsertInfoList
Definition: channelinfo.h:240
MythMainWindow * GetMythMainWindow(void)
static bool MarkProcessed(uint scanid)
Definition: scaninfo.cpp:192
void CleanupDuplicates(ScanDTVTransportList &transports) const
ScanDTVTransportList UpdateChannels(const ScanDTVTransportList &transports, const ChannelImporterBasicStats &info, UpdateAction action, ChannelType type, ScanDTVTransportList &updated, ScanDTVTransportList &skipped)
static void UpdateInsertInfoFromDB(ChannelInsertInfo &chan)
Dialog prompting the user to enter a text string.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:807
static ChannelImporterBasicStats CollectStats(const ScanDTVTransportList &transports)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QString m_si_standard
Definition: channelinfo.h:226
uint DeleteChannels(ScanDTVTransportList &)
OkCancelType
static ChannelImporterUniquenessStats CollectUniquenessStats(const ScanDTVTransportList &transports, const ChannelImporterBasicStats &info)
bool FillFromDB(DTVTunerType type, uint mplexid) override
static void UpdateChannelNumberFromDB(ChannelInsertInfo &chan)
static uint CreateMultiplex(int sourceid, QString sistandard, uint64_t frequency, QString modulation, int transport_id=-1, int network_id=-1)
QString m_default_authority
Definition: channelinfo.h:217
static QString FormatTransports(const ScanDTVTransportList &transports_in)
OkCancelType QueryUserResolve(const ScanDTVTransport &transport, ChannelInsertInfo &chan)
For a single channel.
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
QMap< uint, uint > m_atscnum_cnt
static uint GetChannelCount(int sourceid=-1)
static QString GetChanNum(int chan_id)
Returns the channel-number string of the given channel.
void Closed(QString, int)
uint DeleteUnusedTransports(uint sourceid)
bool Create(void) override
static bool DeleteChannel(uint channel_id)
uint64_t m_frequency
Definition: dtvmultiplex.h:94
void Process(const ScanDTVTransportList &, int sourceid=-1)