1 | // -*- Mode: c++ -*- |
---|
2 | |
---|
3 | #include <algorithm> |
---|
4 | #include <cstdint> |
---|
5 | #include <set> |
---|
6 | using namespace std; |
---|
7 | |
---|
8 | #include <QRegExp> |
---|
9 | #include <QImage> |
---|
10 | #include <QFile> |
---|
11 | #include <QReadWriteLock> |
---|
12 | #include <QHash> |
---|
13 | |
---|
14 | #include "channelutil.h" |
---|
15 | #include "mythdb.h" |
---|
16 | #include "dvbtables.h" |
---|
17 | #include "mythmiscutil.h" |
---|
18 | #include "HLSReader.h" |
---|
19 | |
---|
20 | #define LOC QString("ChanUtil: ") |
---|
21 | |
---|
22 | const QString ChannelUtil::kATSCSeparators = "(_|-|#|\\.)"; |
---|
23 | |
---|
24 | static uint get_dtv_multiplex(uint db_source_id, QString sistandard, |
---|
25 | uint64_t frequency, |
---|
26 | // DVB specific |
---|
27 | uint transport_id, |
---|
28 | // tsid exists with other sistandards, |
---|
29 | // but we only trust it in dvb-land. |
---|
30 | uint network_id, |
---|
31 | // must check polarity for dvb-s |
---|
32 | signed char polarity) |
---|
33 | { |
---|
34 | QString qstr = |
---|
35 | "SELECT mplexid " |
---|
36 | "FROM dtv_multiplex " |
---|
37 | "WHERE sourceid = :SOURCEID " |
---|
38 | " AND sistandard = :SISTANDARD "; |
---|
39 | |
---|
40 | if (sistandard.toLower() != "dvb") |
---|
41 | qstr += "AND frequency = :FREQUENCY "; |
---|
42 | else |
---|
43 | { |
---|
44 | qstr += "AND transportid = :TRANSPORTID "; |
---|
45 | qstr += "AND networkid = :NETWORKID "; |
---|
46 | qstr += "AND polarity = :POLARITY "; |
---|
47 | } |
---|
48 | |
---|
49 | |
---|
50 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
51 | query.prepare(qstr); |
---|
52 | |
---|
53 | query.bindValue(":SOURCEID", db_source_id); |
---|
54 | query.bindValue(":SISTANDARD", sistandard); |
---|
55 | |
---|
56 | if (sistandard.toLower() != "dvb") |
---|
57 | query.bindValue(":FREQUENCY", QString::number(frequency)); |
---|
58 | else |
---|
59 | { |
---|
60 | query.bindValue(":TRANSPORTID", transport_id); |
---|
61 | query.bindValue(":NETWORKID", network_id); |
---|
62 | query.bindValue(":POLARITY", QString(polarity)); |
---|
63 | } |
---|
64 | |
---|
65 | if (!query.exec() || !query.isActive()) |
---|
66 | { |
---|
67 | MythDB::DBError("get_dtv_multiplex", query); |
---|
68 | return 0; |
---|
69 | } |
---|
70 | |
---|
71 | if (query.next()) |
---|
72 | return query.value(0).toUInt(); |
---|
73 | |
---|
74 | return 0; |
---|
75 | } |
---|
76 | |
---|
77 | static uint insert_dtv_multiplex( |
---|
78 | int db_source_id, QString sistandard, |
---|
79 | uint64_t frequency, QString modulation, |
---|
80 | // DVB specific |
---|
81 | int transport_id, int network_id, |
---|
82 | int symbol_rate, signed char bandwidth, |
---|
83 | signed char polarity, signed char inversion, |
---|
84 | signed char trans_mode, |
---|
85 | QString inner_FEC, QString constellation, |
---|
86 | signed char hierarchy, QString hp_code_rate, |
---|
87 | QString lp_code_rate, QString guard_interval, |
---|
88 | QString mod_sys, QString rolloff) |
---|
89 | { |
---|
90 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
91 | |
---|
92 | // If transport is already present, skip insert |
---|
93 | uint mplex = get_dtv_multiplex( |
---|
94 | db_source_id, sistandard, frequency, |
---|
95 | // DVB specific |
---|
96 | transport_id, network_id, polarity); |
---|
97 | |
---|
98 | LOG(VB_CHANSCAN, LOG_INFO, QString( |
---|
99 | "insert_dtv_multiplex(db_source_id: %1, sistandard: '%2', " |
---|
100 | "frequency: %3, modulation: %4, transport_id: %5, " |
---|
101 | "network_id: %6, polarity: %7...) mplexid:%8") |
---|
102 | .arg(db_source_id).arg(sistandard) |
---|
103 | .arg(frequency).arg(modulation) |
---|
104 | .arg(transport_id).arg(network_id) |
---|
105 | .arg(polarity).arg(mplex)); |
---|
106 | |
---|
107 | bool isDVB = (sistandard.toLower() == "dvb"); |
---|
108 | |
---|
109 | QString updateStr = |
---|
110 | "UPDATE dtv_multiplex " |
---|
111 | "SET frequency = :FREQUENCY1, "; |
---|
112 | |
---|
113 | updateStr += (!modulation.isNull()) ? |
---|
114 | "modulation = :MODULATION, " : ""; |
---|
115 | updateStr += (symbol_rate >= 0) ? |
---|
116 | "symbolrate = :SYMBOLRATE, " : ""; |
---|
117 | updateStr += (bandwidth >= 0) ? |
---|
118 | "bandwidth = :BANDWIDTH, " : ""; |
---|
119 | updateStr += (polarity >= 0) ? |
---|
120 | "polarity = :POLARITY, " : ""; |
---|
121 | updateStr += (inversion >= 0) ? |
---|
122 | "inversion = :INVERSION, " : ""; |
---|
123 | updateStr += (trans_mode >= 0) ? |
---|
124 | "transmission_mode= :TRANS_MODE, " : ""; |
---|
125 | updateStr += (!inner_FEC.isNull()) ? |
---|
126 | "fec = :INNER_FEC, " : ""; |
---|
127 | updateStr += (!constellation.isNull()) ? |
---|
128 | "constellation = :CONSTELLATION, " : ""; |
---|
129 | updateStr += (hierarchy >= 0) ? |
---|
130 | "hierarchy = :HIERARCHY, " : ""; |
---|
131 | updateStr += (!hp_code_rate.isNull()) ? |
---|
132 | "hp_code_rate = :HP_CODE_RATE, " : ""; |
---|
133 | updateStr += (!lp_code_rate.isNull()) ? |
---|
134 | "lp_code_rate = :LP_CODE_RATE, " : ""; |
---|
135 | updateStr += (!guard_interval.isNull()) ? |
---|
136 | "guard_interval = :GUARD_INTERVAL, " : ""; |
---|
137 | updateStr += (!mod_sys.isNull()) ? |
---|
138 | "mod_sys = :MOD_SYS, " : ""; |
---|
139 | updateStr += (symbol_rate >= 0) ? |
---|
140 | "rolloff = :ROLLOFF, " : ""; |
---|
141 | updateStr += (transport_id && !isDVB) ? |
---|
142 | "transportid = :TRANSPORTID, " : ""; |
---|
143 | |
---|
144 | updateStr = updateStr.left(updateStr.length()-2) + " "; |
---|
145 | |
---|
146 | updateStr += |
---|
147 | "WHERE sourceid = :SOURCEID AND " |
---|
148 | " sistandard = :SISTANDARD AND "; |
---|
149 | |
---|
150 | updateStr += (isDVB) ? |
---|
151 | " polarity = :WHEREPOLARITY AND " |
---|
152 | " transportid = :TRANSPORTID AND networkid = :NETWORKID " : |
---|
153 | " frequency = :FREQUENCY2 "; |
---|
154 | |
---|
155 | QString insertStr = |
---|
156 | "INSERT INTO dtv_multiplex " |
---|
157 | " (sourceid, sistandard, frequency, "; |
---|
158 | |
---|
159 | insertStr += (!modulation.isNull()) ? "modulation, " : ""; |
---|
160 | insertStr += (transport_id || isDVB) ? "transportid, " : ""; |
---|
161 | insertStr += (isDVB) ? "networkid, " : ""; |
---|
162 | insertStr += (symbol_rate >= 0) ? "symbolrate, " : ""; |
---|
163 | insertStr += (bandwidth >= 0) ? "bandwidth, " : ""; |
---|
164 | insertStr += (polarity >= 0) ? "polarity, " : ""; |
---|
165 | insertStr += (inversion >= 0) ? "inversion, " : ""; |
---|
166 | insertStr += (trans_mode >= 0) ? "transmission_mode, " : ""; |
---|
167 | insertStr += (!inner_FEC.isNull()) ? "fec, " : ""; |
---|
168 | insertStr += (!constellation.isNull()) ? "constellation, " : ""; |
---|
169 | insertStr += (hierarchy >= 0) ? "hierarchy, " : ""; |
---|
170 | insertStr += (!hp_code_rate.isNull()) ? "hp_code_rate, " : ""; |
---|
171 | insertStr += (!lp_code_rate.isNull()) ? "lp_code_rate, " : ""; |
---|
172 | insertStr += (!guard_interval.isNull()) ? "guard_interval, " : ""; |
---|
173 | insertStr += (!mod_sys.isNull()) ? "mod_sys, " : ""; |
---|
174 | insertStr += (!rolloff.isNull()) ? "rolloff, " : ""; |
---|
175 | insertStr = insertStr.left(insertStr.length()-2) + ") "; |
---|
176 | |
---|
177 | insertStr += |
---|
178 | "VALUES " |
---|
179 | " (:SOURCEID, :SISTANDARD, :FREQUENCY1, "; |
---|
180 | insertStr += (!modulation.isNull()) ? ":MODULATION, " : ""; |
---|
181 | insertStr += (transport_id || isDVB) ? ":TRANSPORTID, " : ""; |
---|
182 | insertStr += (isDVB) ? ":NETWORKID, " : ""; |
---|
183 | insertStr += (symbol_rate >= 0) ? ":SYMBOLRATE, " : ""; |
---|
184 | insertStr += (bandwidth >= 0) ? ":BANDWIDTH, " : ""; |
---|
185 | insertStr += (polarity >= 0) ? ":POLARITY, " : ""; |
---|
186 | insertStr += (inversion >= 0) ? ":INVERSION, " : ""; |
---|
187 | insertStr += (trans_mode >= 0) ? ":TRANS_MODE, " : ""; |
---|
188 | insertStr += (!inner_FEC.isNull()) ? ":INNER_FEC, " : ""; |
---|
189 | insertStr += (!constellation.isNull()) ? ":CONSTELLATION, " : ""; |
---|
190 | insertStr += (hierarchy >= 0) ? ":HIERARCHY, " : ""; |
---|
191 | insertStr += (!hp_code_rate.isNull()) ? ":HP_CODE_RATE, " : ""; |
---|
192 | insertStr += (!lp_code_rate.isNull()) ? ":LP_CODE_RATE, " : ""; |
---|
193 | insertStr += (!guard_interval.isNull()) ? ":GUARD_INTERVAL, " : ""; |
---|
194 | insertStr += (!mod_sys.isNull()) ? ":MOD_SYS, " : ""; |
---|
195 | insertStr += (!rolloff.isNull()) ? ":ROLLOFF, " : ""; |
---|
196 | insertStr = insertStr.left(insertStr.length()-2) + ");"; |
---|
197 | |
---|
198 | query.prepare((mplex) ? updateStr : insertStr); |
---|
199 | |
---|
200 | query.bindValue(":SOURCEID", db_source_id); |
---|
201 | query.bindValue(":SISTANDARD", sistandard); |
---|
202 | query.bindValue(":FREQUENCY1", QString::number(frequency)); |
---|
203 | |
---|
204 | if (mplex) |
---|
205 | { |
---|
206 | if (isDVB) |
---|
207 | { |
---|
208 | query.bindValue(":TRANSPORTID", transport_id); |
---|
209 | query.bindValue(":NETWORKID", network_id); |
---|
210 | query.bindValue(":WHEREPOLARITY", QString(polarity)); |
---|
211 | } |
---|
212 | else |
---|
213 | { |
---|
214 | query.bindValue(":FREQUENCY2", QString::number(frequency)); |
---|
215 | if (transport_id) |
---|
216 | query.bindValue(":TRANSPORTID", transport_id); |
---|
217 | } |
---|
218 | } |
---|
219 | else |
---|
220 | { |
---|
221 | if (transport_id || isDVB) |
---|
222 | query.bindValue(":TRANSPORTID", transport_id); |
---|
223 | if (isDVB) |
---|
224 | query.bindValue(":NETWORKID", network_id); |
---|
225 | } |
---|
226 | |
---|
227 | if (!modulation.isNull()) |
---|
228 | query.bindValue(":MODULATION", modulation); |
---|
229 | |
---|
230 | if (symbol_rate >= 0) |
---|
231 | query.bindValue(":SYMBOLRATE", symbol_rate); |
---|
232 | if (bandwidth >= 0) |
---|
233 | query.bindValue(":BANDWIDTH", QString("%1").arg((char)bandwidth)); |
---|
234 | if (polarity >= 0) |
---|
235 | query.bindValue(":POLARITY", QString("%1").arg((char)polarity)); |
---|
236 | if (inversion >= 0) |
---|
237 | query.bindValue(":INVERSION", QString("%1").arg((char)inversion)); |
---|
238 | if (trans_mode >= 0) |
---|
239 | query.bindValue(":TRANS_MODE", QString("%1").arg((char)trans_mode)); |
---|
240 | |
---|
241 | if (!inner_FEC.isNull()) |
---|
242 | query.bindValue(":INNER_FEC", inner_FEC); |
---|
243 | if (!constellation.isNull()) |
---|
244 | query.bindValue(":CONSTELLATION", constellation); |
---|
245 | if (hierarchy >= 0) |
---|
246 | query.bindValue(":HIERARCHY", QString("%1").arg((char)hierarchy)); |
---|
247 | if (!hp_code_rate.isNull()) |
---|
248 | query.bindValue(":HP_CODE_RATE", hp_code_rate); |
---|
249 | if (!lp_code_rate.isNull()) |
---|
250 | query.bindValue(":LP_CODE_RATE", lp_code_rate); |
---|
251 | if (!guard_interval.isNull()) |
---|
252 | query.bindValue(":GUARD_INTERVAL",guard_interval); |
---|
253 | if (!mod_sys.isNull()) |
---|
254 | query.bindValue(":MOD_SYS", mod_sys); |
---|
255 | if (!rolloff.isNull()) |
---|
256 | query.bindValue(":ROLLOFF", rolloff); |
---|
257 | |
---|
258 | if (!query.exec() || !query.isActive()) |
---|
259 | { |
---|
260 | MythDB::DBError("Adding transport to Database.", query); |
---|
261 | return 0; |
---|
262 | } |
---|
263 | |
---|
264 | if (mplex) |
---|
265 | return mplex; |
---|
266 | |
---|
267 | mplex = get_dtv_multiplex( |
---|
268 | db_source_id, sistandard, frequency, |
---|
269 | // DVB specific |
---|
270 | transport_id, network_id, polarity); |
---|
271 | |
---|
272 | LOG(VB_CHANSCAN, LOG_INFO, QString("insert_dtv_multiplex -- ") + |
---|
273 | QString("inserted %1").arg(mplex)); |
---|
274 | |
---|
275 | return mplex; |
---|
276 | } |
---|
277 | |
---|
278 | static void handle_transport_desc(vector<uint> &muxes, |
---|
279 | const MPEGDescriptor &desc, |
---|
280 | uint sourceid, uint tsid, uint netid) |
---|
281 | { |
---|
282 | uint tag = desc.DescriptorTag(); |
---|
283 | |
---|
284 | if (tag == DescriptorID::terrestrial_delivery_system) |
---|
285 | { |
---|
286 | const TerrestrialDeliverySystemDescriptor cd(desc); |
---|
287 | uint64_t freq = cd.FrequencyHz(); |
---|
288 | |
---|
289 | // Use the frequency we already have for this mplex |
---|
290 | // as it may be one of the other_frequencies for this mplex |
---|
291 | int mux = ChannelUtil::GetMplexID(sourceid, tsid, netid); |
---|
292 | if (mux > 0) |
---|
293 | { |
---|
294 | QString dummy_mod; |
---|
295 | QString dummy_sistd; |
---|
296 | uint dummy_tsid, dummy_netid; |
---|
297 | ChannelUtil::GetTuningParams(mux, dummy_mod, freq, |
---|
298 | dummy_tsid, dummy_netid, dummy_sistd); |
---|
299 | } |
---|
300 | |
---|
301 | mux = ChannelUtil::CreateMultiplex( |
---|
302 | (int)sourceid, "dvb", |
---|
303 | freq, QString(), |
---|
304 | // DVB specific |
---|
305 | (int)tsid, (int)netid, |
---|
306 | -1, cd.BandwidthString()[0].toLatin1(), |
---|
307 | -1, 'a', |
---|
308 | cd.TransmissionModeString()[0].toLatin1(), |
---|
309 | QString(), cd.ConstellationString(), |
---|
310 | cd.HierarchyString()[0].toLatin1(), cd.CodeRateHPString(), |
---|
311 | cd.CodeRateLPString(), cd.GuardIntervalString(), |
---|
312 | QString(), QString()); |
---|
313 | |
---|
314 | if (mux) |
---|
315 | muxes.push_back(mux); |
---|
316 | |
---|
317 | /* unused |
---|
318 | HighPriority() |
---|
319 | IsTimeSlicingIndicatorUsed() |
---|
320 | IsMPE_FECUsed() |
---|
321 | NativeInterleaver() |
---|
322 | Alpha() |
---|
323 | */ |
---|
324 | } |
---|
325 | else if (tag == DescriptorID::satellite_delivery_system) |
---|
326 | { |
---|
327 | const SatelliteDeliverySystemDescriptor cd(desc); |
---|
328 | |
---|
329 | uint mux = ChannelUtil::CreateMultiplex( |
---|
330 | sourceid, "dvb", |
---|
331 | cd.FrequencyHz(), cd.ModulationString(), |
---|
332 | // DVB specific |
---|
333 | tsid, netid, |
---|
334 | cd.SymbolRateHz(), -1, |
---|
335 | cd.PolarizationString()[0].toLatin1(), 'a', |
---|
336 | -1, |
---|
337 | cd.FECInnerString(), QString(), |
---|
338 | -1, QString(), |
---|
339 | QString(), QString(), |
---|
340 | cd.ModulationSystemString(), cd.RollOffString()); |
---|
341 | |
---|
342 | if (mux) |
---|
343 | muxes.push_back(mux); |
---|
344 | |
---|
345 | /* unused |
---|
346 | OrbitalPositionString() == OrbitalLocation |
---|
347 | */ |
---|
348 | } |
---|
349 | else if (tag == DescriptorID::cable_delivery_system) |
---|
350 | { |
---|
351 | const CableDeliverySystemDescriptor cd(desc); |
---|
352 | |
---|
353 | uint mux = ChannelUtil::CreateMultiplex( |
---|
354 | sourceid, "dvb", |
---|
355 | cd.FrequencyHz(), cd.ModulationString(), |
---|
356 | // DVB specific |
---|
357 | tsid, netid, |
---|
358 | cd.SymbolRateHz(), -1, |
---|
359 | -1, 'a', |
---|
360 | -1, |
---|
361 | cd.FECInnerString(), QString(), |
---|
362 | -1, QString(), |
---|
363 | QString(), QString(), |
---|
364 | QString(), QString()); |
---|
365 | |
---|
366 | if (mux) |
---|
367 | muxes.push_back(mux); |
---|
368 | } |
---|
369 | } |
---|
370 | |
---|
371 | uint ChannelUtil::CreateMultiplex(int sourceid, QString sistandard, |
---|
372 | uint64_t frequency, QString modulation, |
---|
373 | int transport_id, int network_id) |
---|
374 | { |
---|
375 | return CreateMultiplex( |
---|
376 | sourceid, sistandard, |
---|
377 | frequency, modulation, |
---|
378 | transport_id, network_id, |
---|
379 | -1, -1, |
---|
380 | -1, -1, |
---|
381 | -1, |
---|
382 | QString(), QString(), |
---|
383 | -1, QString(), |
---|
384 | QString(), QString(), |
---|
385 | QString(), QString()); |
---|
386 | } |
---|
387 | |
---|
388 | uint ChannelUtil::CreateMultiplex( |
---|
389 | int sourceid, QString sistandard, |
---|
390 | uint64_t freq, QString modulation, |
---|
391 | // DVB specific |
---|
392 | int transport_id, int network_id, |
---|
393 | int symbol_rate, signed char bandwidth, |
---|
394 | signed char polarity, signed char inversion, |
---|
395 | signed char trans_mode, |
---|
396 | QString inner_FEC, QString constellation, |
---|
397 | signed char hierarchy, QString hp_code_rate, |
---|
398 | QString lp_code_rate, QString guard_interval, |
---|
399 | QString mod_sys, QString rolloff) |
---|
400 | { |
---|
401 | return insert_dtv_multiplex( |
---|
402 | sourceid, sistandard, |
---|
403 | freq, modulation, |
---|
404 | // DVB specific |
---|
405 | transport_id, network_id, |
---|
406 | symbol_rate, bandwidth, |
---|
407 | polarity, inversion, |
---|
408 | trans_mode, |
---|
409 | inner_FEC, constellation, |
---|
410 | hierarchy, hp_code_rate, |
---|
411 | lp_code_rate, guard_interval, |
---|
412 | mod_sys, rolloff); |
---|
413 | } |
---|
414 | |
---|
415 | uint ChannelUtil::CreateMultiplex(uint sourceid, const DTVMultiplex &mux, |
---|
416 | int transport_id, int network_id) |
---|
417 | { |
---|
418 | return insert_dtv_multiplex( |
---|
419 | sourceid, mux.sistandard, |
---|
420 | mux.frequency, mux.modulation.toString(), |
---|
421 | // DVB specific |
---|
422 | transport_id, network_id, |
---|
423 | mux.symbolrate, mux.bandwidth.toChar().toLatin1(), |
---|
424 | mux.polarity.toChar().toLatin1(), mux.inversion.toChar().toLatin1(), |
---|
425 | mux.trans_mode.toChar().toLatin1(), |
---|
426 | mux.fec.toString(), mux.modulation.toString(), |
---|
427 | mux.hierarchy.toChar().toLatin1(), mux.hp_code_rate.toString(), |
---|
428 | mux.lp_code_rate.toString(), mux.guard_interval.toString(), |
---|
429 | mux.mod_sys.toString(), mux.rolloff.toString()); |
---|
430 | } |
---|
431 | |
---|
432 | |
---|
433 | /** \fn ChannelUtil::CreateMultiplexes(int, const NetworkInformationTable*) |
---|
434 | * |
---|
435 | */ |
---|
436 | vector<uint> ChannelUtil::CreateMultiplexes( |
---|
437 | int sourceid, const NetworkInformationTable *nit) |
---|
438 | { |
---|
439 | vector<uint> muxes; |
---|
440 | |
---|
441 | if (sourceid <= 0) |
---|
442 | return muxes; |
---|
443 | |
---|
444 | for (uint i = 0; i < nit->TransportStreamCount(); ++i) |
---|
445 | { |
---|
446 | const desc_list_t& list = |
---|
447 | MPEGDescriptor::Parse(nit->TransportDescriptors(i), |
---|
448 | nit->TransportDescriptorsLength(i)); |
---|
449 | |
---|
450 | uint tsid = nit->TSID(i); |
---|
451 | uint netid = nit->OriginalNetworkID(i); |
---|
452 | for (uint j = 0; j < list.size(); ++j) |
---|
453 | { |
---|
454 | const MPEGDescriptor desc(list[j]); |
---|
455 | handle_transport_desc(muxes, desc, sourceid, tsid, netid); |
---|
456 | } |
---|
457 | } |
---|
458 | return muxes; |
---|
459 | } |
---|
460 | |
---|
461 | uint ChannelUtil::GetMplexID(uint sourceid, const QString &channum) |
---|
462 | { |
---|
463 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
464 | /* See if mplexid is already in the database */ |
---|
465 | query.prepare( |
---|
466 | "SELECT mplexid " |
---|
467 | "FROM channel " |
---|
468 | "WHERE sourceid = :SOURCEID AND " |
---|
469 | " channum = :CHANNUM"); |
---|
470 | |
---|
471 | query.bindValue(":SOURCEID", sourceid); |
---|
472 | query.bindValue(":CHANNUM", channum); |
---|
473 | |
---|
474 | if (!query.exec() || !query.isActive()) |
---|
475 | MythDB::DBError("GetMplexID 0", query); |
---|
476 | else if (query.next()) |
---|
477 | return query.value(0).toInt(); |
---|
478 | |
---|
479 | return 0; |
---|
480 | } |
---|
481 | |
---|
482 | int ChannelUtil::GetMplexID(uint sourceid, uint64_t frequency) |
---|
483 | { |
---|
484 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
485 | /* See if mplexid is already in the database */ |
---|
486 | query.prepare( |
---|
487 | "SELECT mplexid " |
---|
488 | "FROM dtv_multiplex " |
---|
489 | "WHERE sourceid = :SOURCEID AND " |
---|
490 | " frequency = :FREQUENCY"); |
---|
491 | |
---|
492 | query.bindValue(":SOURCEID", sourceid); |
---|
493 | query.bindValue(":FREQUENCY", QString::number(frequency)); |
---|
494 | |
---|
495 | if (!query.exec() || !query.isActive()) |
---|
496 | { |
---|
497 | MythDB::DBError("GetMplexID 1", query); |
---|
498 | return -1; |
---|
499 | } |
---|
500 | |
---|
501 | if (query.next()) |
---|
502 | return query.value(0).toInt(); |
---|
503 | |
---|
504 | return -1; |
---|
505 | } |
---|
506 | |
---|
507 | int ChannelUtil::GetMplexID(uint sourceid, uint64_t frequency, |
---|
508 | uint transport_id, uint network_id) |
---|
509 | { |
---|
510 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
511 | // See if transport already in database |
---|
512 | query.prepare( |
---|
513 | "SELECT mplexid " |
---|
514 | "FROM dtv_multiplex " |
---|
515 | "WHERE networkid = :NETWORKID AND " |
---|
516 | " transportid = :TRANSPORTID AND " |
---|
517 | " frequency = :FREQUENCY AND " |
---|
518 | " sourceid = :SOURCEID"); |
---|
519 | |
---|
520 | query.bindValue(":SOURCEID", sourceid); |
---|
521 | query.bindValue(":NETWORKID", network_id); |
---|
522 | query.bindValue(":TRANSPORTID", transport_id); |
---|
523 | query.bindValue(":FREQUENCY", QString::number(frequency)); |
---|
524 | |
---|
525 | if (!query.exec() || !query.isActive()) |
---|
526 | { |
---|
527 | MythDB::DBError("GetMplexID 2", query); |
---|
528 | return -1; |
---|
529 | } |
---|
530 | |
---|
531 | if (query.next()) |
---|
532 | return query.value(0).toInt(); |
---|
533 | |
---|
534 | return -1; |
---|
535 | } |
---|
536 | |
---|
537 | int ChannelUtil::GetMplexID(uint sourceid, |
---|
538 | uint transport_id, uint network_id) |
---|
539 | { |
---|
540 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
541 | // See if transport already in database |
---|
542 | query.prepare( |
---|
543 | "SELECT mplexid " |
---|
544 | "FROM dtv_multiplex " |
---|
545 | "WHERE networkid = :NETWORKID AND " |
---|
546 | " transportid = :TRANSPORTID AND " |
---|
547 | " sourceid = :SOURCEID"); |
---|
548 | |
---|
549 | query.bindValue(":SOURCEID", sourceid); |
---|
550 | query.bindValue(":NETWORKID", network_id); |
---|
551 | query.bindValue(":TRANSPORTID", transport_id); |
---|
552 | |
---|
553 | if (!query.exec() || !query.isActive()) |
---|
554 | { |
---|
555 | MythDB::DBError("GetMplexID 3", query); |
---|
556 | return -1; |
---|
557 | } |
---|
558 | |
---|
559 | if (query.next()) |
---|
560 | return query.value(0).toInt(); |
---|
561 | |
---|
562 | return -1; |
---|
563 | } |
---|
564 | |
---|
565 | uint ChannelUtil::GetMplexID(uint chanid) |
---|
566 | { |
---|
567 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
568 | /* See if mplexid is already in the database */ |
---|
569 | query.prepare( |
---|
570 | "SELECT mplexid " |
---|
571 | "FROM channel " |
---|
572 | "WHERE chanid = :CHANID"); |
---|
573 | |
---|
574 | query.bindValue(":CHANID", chanid); |
---|
575 | |
---|
576 | if (!query.exec()) |
---|
577 | MythDB::DBError("GetMplexID 4", query); |
---|
578 | else if (query.next()) |
---|
579 | return query.value(0).toInt(); |
---|
580 | |
---|
581 | return 0; |
---|
582 | } |
---|
583 | |
---|
584 | /** \fn ChannelUtil::GetBetterMplexID(int, int, int) |
---|
585 | * \brief Returns best match multiplex ID, creating one if needed. |
---|
586 | * |
---|
587 | * First, see if you can get an exact match based on the current |
---|
588 | * mplexid's sourceID and the NetworkID/TransportID. |
---|
589 | * |
---|
590 | * Next, see if current one is NULL, if so update those |
---|
591 | * values and return current mplexid. |
---|
592 | * |
---|
593 | * Next, if values were set, see where you can find this |
---|
594 | * NetworkID/TransportID. If we get an exact match just return it, |
---|
595 | * since there is no question what mplexid this NetworkId/TransportId |
---|
596 | * is for. If we get many matches, return CurrentMplexID. |
---|
597 | * |
---|
598 | * Next, try to repeat query without currentmplexid as source id. |
---|
599 | * If we get a singe match return it, if we get many matches we |
---|
600 | * return the first one. |
---|
601 | * |
---|
602 | * If none of these work return -1. |
---|
603 | * \return mplexid on success, -1 on failure. |
---|
604 | */ |
---|
605 | |
---|
606 | // current_mplexid always exists in scanner, see ScanTranport() |
---|
607 | // |
---|
608 | int ChannelUtil::GetBetterMplexID(int current_mplexid, |
---|
609 | int transport_id, |
---|
610 | int network_id) |
---|
611 | { |
---|
612 | LOG(VB_CHANSCAN, LOG_INFO, |
---|
613 | QString("GetBetterMplexID(mplexId %1, tId %2, netId %3)") |
---|
614 | .arg(current_mplexid).arg(transport_id).arg(network_id)); |
---|
615 | |
---|
616 | int q_networkid = 0, q_transportid = 0; |
---|
617 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
618 | |
---|
619 | query.prepare("SELECT networkid, transportid " |
---|
620 | "FROM dtv_multiplex " |
---|
621 | "WHERE mplexid = :MPLEX_ID"); |
---|
622 | |
---|
623 | query.bindValue(":MPLEX_ID", current_mplexid); |
---|
624 | |
---|
625 | if (!query.exec()) |
---|
626 | MythDB::DBError("Getting mplexid global search", query); |
---|
627 | else if (query.next()) |
---|
628 | { |
---|
629 | q_networkid = query.value(0).toInt(); |
---|
630 | q_transportid = query.value(1).toInt(); |
---|
631 | } |
---|
632 | |
---|
633 | // Got a match, return it. |
---|
634 | if ((q_networkid == network_id) && (q_transportid == transport_id)) |
---|
635 | { |
---|
636 | LOG(VB_CHANSCAN, LOG_INFO, |
---|
637 | QString("GetBetterMplexID(): Returning perfect match %1") |
---|
638 | .arg(current_mplexid)); |
---|
639 | return current_mplexid; |
---|
640 | } |
---|
641 | |
---|
642 | // Not in DB at all, insert it |
---|
643 | if (!q_networkid && !q_transportid) |
---|
644 | { |
---|
645 | int qsize = query.size(); |
---|
646 | query.prepare("UPDATE dtv_multiplex " |
---|
647 | "SET networkid = :NETWORK_ID, " |
---|
648 | " transportid = :TRANSPORT_ID " |
---|
649 | "WHERE mplexid = :MPLEX_ID"); |
---|
650 | |
---|
651 | query.bindValue(":NETWORK_ID", network_id); |
---|
652 | query.bindValue(":TRANSPORT_ID", transport_id); |
---|
653 | query.bindValue(":MPLEX_ID", current_mplexid); |
---|
654 | |
---|
655 | if (!query.exec()) |
---|
656 | MythDB::DBError("Getting mplexid global search", query); |
---|
657 | |
---|
658 | LOG(VB_CHANSCAN, LOG_INFO, |
---|
659 | QString("GetBetterMplexID(): net id and transport id " |
---|
660 | "are null, qsize(%1), Returning %2") |
---|
661 | .arg(qsize).arg(current_mplexid)); |
---|
662 | return current_mplexid; |
---|
663 | } |
---|
664 | |
---|
665 | // We have a partial match, so we try to do better... |
---|
666 | QString theQueries[2] = |
---|
667 | { |
---|
668 | QString("SELECT a.mplexid " |
---|
669 | "FROM dtv_multiplex a, dtv_multiplex b " |
---|
670 | "WHERE a.networkid = :NETWORK_ID AND " |
---|
671 | " a.transportid = :TRANSPORT_ID AND " |
---|
672 | " a.sourceid = b.sourceid AND " |
---|
673 | " b.mplexid = :MPLEX_ID"), |
---|
674 | |
---|
675 | QString("SELECT mplexid " |
---|
676 | "FROM dtv_multiplex " |
---|
677 | "WHERE networkid = :NETWORK_ID AND " |
---|
678 | " transportid = :TRANSPORT_ID"), |
---|
679 | }; |
---|
680 | |
---|
681 | for (uint i=0; i<2; i++) |
---|
682 | { |
---|
683 | query.prepare(theQueries[i]); |
---|
684 | |
---|
685 | query.bindValue(":NETWORK_ID", network_id); |
---|
686 | query.bindValue(":TRANSPORT_ID", transport_id); |
---|
687 | if (i == 0) |
---|
688 | query.bindValue(":MPLEX_ID", current_mplexid); |
---|
689 | |
---|
690 | if (!query.exec() || !query.isActive()) |
---|
691 | MythDB::DBError("Finding matching mplexid", query); |
---|
692 | |
---|
693 | if (query.size() == 1 && query.next()) |
---|
694 | { |
---|
695 | LOG(VB_CHANSCAN, LOG_INFO, |
---|
696 | QString("GetBetterMplexID(): query#%1 qsize(%2) " |
---|
697 | "Returning %3") |
---|
698 | .arg(i).arg(query.size()).arg(current_mplexid)); |
---|
699 | return query.value(0).toInt(); |
---|
700 | } |
---|
701 | |
---|
702 | if (query.next()) |
---|
703 | { |
---|
704 | int ret = (i==0) ? current_mplexid : query.value(0).toInt(); |
---|
705 | LOG(VB_CHANSCAN, LOG_INFO, |
---|
706 | QString("GetBetterMplexID(): query#%1 qsize(%2) " |
---|
707 | "Returning %3") |
---|
708 | .arg(i).arg(query.size()).arg(ret)); |
---|
709 | return ret; |
---|
710 | } |
---|
711 | } |
---|
712 | |
---|
713 | // If you still didn't find this combo return -1 (failure) |
---|
714 | LOG(VB_CHANSCAN, LOG_INFO, "GetBetterMplexID(): Returning -1"); |
---|
715 | return -1; |
---|
716 | } |
---|
717 | |
---|
718 | bool ChannelUtil::GetTuningParams(uint mplexid, |
---|
719 | QString &modulation, |
---|
720 | uint64_t &frequency, |
---|
721 | uint &dvb_transportid, |
---|
722 | uint &dvb_networkid, |
---|
723 | QString &si_std) |
---|
724 | { |
---|
725 | if (!mplexid || (mplexid == 32767)) /* 32767 deals with old lineups */ |
---|
726 | return false; |
---|
727 | |
---|
728 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
729 | query.prepare( |
---|
730 | "SELECT transportid, networkid, frequency, modulation, sistandard " |
---|
731 | "FROM dtv_multiplex " |
---|
732 | "WHERE mplexid = :MPLEXID"); |
---|
733 | query.bindValue(":MPLEXID", mplexid); |
---|
734 | |
---|
735 | if (!query.exec()) |
---|
736 | { |
---|
737 | MythDB::DBError("GetTuningParams failed ", query); |
---|
738 | return false; |
---|
739 | } |
---|
740 | |
---|
741 | if (!query.next()) |
---|
742 | return false; |
---|
743 | |
---|
744 | dvb_transportid = query.value(0).toUInt(); |
---|
745 | dvb_networkid = query.value(1).toUInt(); |
---|
746 | frequency = query.value(2).toULongLong(); |
---|
747 | modulation = query.value(3).toString(); |
---|
748 | si_std = query.value(4).toString(); |
---|
749 | |
---|
750 | return true; |
---|
751 | } |
---|
752 | |
---|
753 | QString ChannelUtil::GetChannelStringField(int chan_id, const QString &field) |
---|
754 | { |
---|
755 | if (chan_id < 0) |
---|
756 | return QString(); |
---|
757 | |
---|
758 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
759 | query.prepare(QString("SELECT %1 FROM channel " |
---|
760 | "WHERE chanid = :CHANID").arg(field)); |
---|
761 | query.bindValue(":CHANID", chan_id); |
---|
762 | if (!query.exec()) |
---|
763 | { |
---|
764 | MythDB::DBError("Selecting channel/dtv_multiplex 1", query); |
---|
765 | return QString(); |
---|
766 | } |
---|
767 | |
---|
768 | if (!query.next()) |
---|
769 | return QString(); |
---|
770 | |
---|
771 | return query.value(0).toString(); |
---|
772 | } |
---|
773 | |
---|
774 | QString ChannelUtil::GetChanNum(int chan_id) |
---|
775 | { |
---|
776 | return GetChannelStringField(chan_id, QString("channum")); |
---|
777 | } |
---|
778 | |
---|
779 | int ChannelUtil::GetTimeOffset(int chan_id) |
---|
780 | { |
---|
781 | return GetChannelStringField(chan_id, QString("tmoffset")).toInt(); |
---|
782 | } |
---|
783 | |
---|
784 | int ChannelUtil::GetSourceID(int db_mplexid) |
---|
785 | { |
---|
786 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
787 | |
---|
788 | query.prepare("SELECT sourceid " |
---|
789 | "FROM dtv_multiplex " |
---|
790 | "WHERE mplexid = :MPLEXID"); |
---|
791 | query.bindValue(":MPLEXID", db_mplexid); |
---|
792 | if (!query.exec()) |
---|
793 | { |
---|
794 | MythDB::DBError("Selecting channel/dtv_multiplex", query); |
---|
795 | return -1; |
---|
796 | } |
---|
797 | |
---|
798 | if (query.next()) |
---|
799 | return query.value(0).toInt(); |
---|
800 | |
---|
801 | return -1; |
---|
802 | } |
---|
803 | |
---|
804 | uint ChannelUtil::GetSourceIDForChannel(uint chanid) |
---|
805 | { |
---|
806 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
807 | |
---|
808 | query.prepare( |
---|
809 | "SELECT sourceid " |
---|
810 | "FROM channel " |
---|
811 | "WHERE chanid = :CHANID"); |
---|
812 | query.bindValue(":CHANID", chanid); |
---|
813 | |
---|
814 | if (!query.exec()) |
---|
815 | MythDB::DBError("Selecting channel/dtv_multiplex", query); |
---|
816 | else if (query.next()) |
---|
817 | return query.value(0).toUInt(); |
---|
818 | |
---|
819 | return 0; |
---|
820 | } |
---|
821 | |
---|
822 | QStringList ChannelUtil::GetInputTypes(uint chanid) |
---|
823 | { |
---|
824 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
825 | query.prepare("SELECT cardtype " |
---|
826 | "FROM capturecard, channel " |
---|
827 | "WHERE channel.chanid = :CHANID AND " |
---|
828 | " channel.sourceid = capturecard.sourceid " |
---|
829 | "GROUP BY cardtype"); |
---|
830 | query.bindValue(":CHANID", chanid); |
---|
831 | |
---|
832 | QStringList list; |
---|
833 | if (!query.exec()) |
---|
834 | { |
---|
835 | MythDB::DBError("ChannelUtil::GetInputTypes", query); |
---|
836 | return list; |
---|
837 | } |
---|
838 | while (query.next()) |
---|
839 | list.push_back(query.value(0).toString()); |
---|
840 | return list; |
---|
841 | } |
---|
842 | |
---|
843 | static bool lt_pidcache( |
---|
844 | const pid_cache_item_t &a, const pid_cache_item_t &b) |
---|
845 | { |
---|
846 | return a.GetPID() < b.GetPID(); |
---|
847 | } |
---|
848 | |
---|
849 | /** \brief Returns cached MPEG PIDs when given a Channel ID. |
---|
850 | * |
---|
851 | * \param chanid Channel ID to fetch cached pids for. |
---|
852 | * \param pid_cache List of PIDs with their TableID |
---|
853 | * types is returned in pid_cache. |
---|
854 | */ |
---|
855 | bool ChannelUtil::GetCachedPids(uint chanid, |
---|
856 | pid_cache_t &pid_cache) |
---|
857 | { |
---|
858 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
859 | query.prepare("SELECT pid, tableid FROM pidcache " |
---|
860 | "WHERE chanid = :CHANID"); |
---|
861 | |
---|
862 | query.bindValue(":CHANID", chanid); |
---|
863 | |
---|
864 | if (!query.exec()) |
---|
865 | { |
---|
866 | MythDB::DBError("GetCachedPids: fetching pids", query); |
---|
867 | return false; |
---|
868 | } |
---|
869 | |
---|
870 | while (query.next()) |
---|
871 | { |
---|
872 | int pid = query.value(0).toInt(), tid = query.value(1).toInt(); |
---|
873 | if ((pid >= 0) && (tid >= 0)) |
---|
874 | pid_cache.push_back(pid_cache_item_t(pid, tid)); |
---|
875 | } |
---|
876 | stable_sort(pid_cache.begin(), pid_cache.end(), lt_pidcache); |
---|
877 | |
---|
878 | return true; |
---|
879 | } |
---|
880 | |
---|
881 | /** \brief Saves PIDs for PSIP tables to database. |
---|
882 | * |
---|
883 | * \param chanid Channel ID to fetch cached pids for. |
---|
884 | * \param _pid_cache List of PIDs with their TableID types to be saved. |
---|
885 | * \param delete_all If true delete both permanent and transient pids first. |
---|
886 | */ |
---|
887 | bool ChannelUtil::SaveCachedPids(uint chanid, |
---|
888 | const pid_cache_t &_pid_cache, |
---|
889 | bool delete_all) |
---|
890 | { |
---|
891 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
892 | |
---|
893 | /// delete |
---|
894 | if (delete_all) |
---|
895 | query.prepare("DELETE FROM pidcache WHERE chanid = :CHANID"); |
---|
896 | else |
---|
897 | query.prepare( |
---|
898 | "DELETE FROM pidcache " |
---|
899 | "WHERE chanid = :CHANID AND tableid < 65536"); |
---|
900 | |
---|
901 | query.bindValue(":CHANID", chanid); |
---|
902 | |
---|
903 | if (!query.exec()) |
---|
904 | { |
---|
905 | MythDB::DBError("GetCachedPids -- delete", query); |
---|
906 | return false; |
---|
907 | } |
---|
908 | |
---|
909 | pid_cache_t old_cache; |
---|
910 | GetCachedPids(chanid, old_cache); |
---|
911 | pid_cache_t pid_cache = _pid_cache; |
---|
912 | stable_sort(pid_cache.begin(), pid_cache.end(), lt_pidcache); |
---|
913 | |
---|
914 | /// insert |
---|
915 | query.prepare( |
---|
916 | "INSERT INTO pidcache " |
---|
917 | "SET chanid = :CHANID, pid = :PID, tableid = :TABLEID"); |
---|
918 | query.bindValue(":CHANID", chanid); |
---|
919 | |
---|
920 | bool ok = true; |
---|
921 | pid_cache_t::const_iterator ito = old_cache.begin(); |
---|
922 | pid_cache_t::const_iterator itn = pid_cache.begin(); |
---|
923 | for (; itn != pid_cache.end(); ++itn) |
---|
924 | { |
---|
925 | // if old pid smaller than current new pid, skip this old pid |
---|
926 | for (; ito != old_cache.end() && ito->GetPID() < itn->GetPID(); ++ito); |
---|
927 | |
---|
928 | // if already in DB, skip DB insert |
---|
929 | if (ito != old_cache.end() && ito->GetPID() == itn->GetPID()) |
---|
930 | continue; |
---|
931 | |
---|
932 | query.bindValue(":PID", itn->GetPID()); |
---|
933 | query.bindValue(":TABLEID", itn->GetComposite()); |
---|
934 | |
---|
935 | if (!query.exec()) |
---|
936 | { |
---|
937 | MythDB::DBError("GetCachedPids -- insert", query); |
---|
938 | ok = false; |
---|
939 | } |
---|
940 | } |
---|
941 | |
---|
942 | return ok; |
---|
943 | } |
---|
944 | |
---|
945 | QString ChannelUtil::GetChannelValueStr(const QString &channel_field, |
---|
946 | uint sourceid, |
---|
947 | const QString &channum) |
---|
948 | { |
---|
949 | QString retval; |
---|
950 | |
---|
951 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
952 | |
---|
953 | query.prepare( |
---|
954 | QString( |
---|
955 | "SELECT channel.%1 " |
---|
956 | "FROM channel " |
---|
957 | "WHERE channum = :CHANNUM AND " |
---|
958 | " sourceid = :SOURCEID") |
---|
959 | .arg(channel_field)); |
---|
960 | |
---|
961 | query.bindValue(":SOURCEID", sourceid); |
---|
962 | query.bindValue(":CHANNUM", channum); |
---|
963 | |
---|
964 | if (!query.exec() || !query.isActive()) |
---|
965 | MythDB::DBError("getchannelvalue", query); |
---|
966 | else if (query.next()) |
---|
967 | retval = query.value(0).toString(); |
---|
968 | |
---|
969 | return retval; |
---|
970 | } |
---|
971 | |
---|
972 | int ChannelUtil::GetChannelValueInt(const QString &channel_field, |
---|
973 | uint sourceid, |
---|
974 | const QString &channum) |
---|
975 | { |
---|
976 | QString val = GetChannelValueStr(channel_field, sourceid, channum); |
---|
977 | |
---|
978 | int retval = 0; |
---|
979 | if (!val.isEmpty()) |
---|
980 | retval = val.toInt(); |
---|
981 | |
---|
982 | return (retval) ? retval : -1; |
---|
983 | } |
---|
984 | |
---|
985 | bool ChannelUtil::IsOnSameMultiplex(uint srcid, |
---|
986 | const QString &new_channum, |
---|
987 | const QString &old_channum) |
---|
988 | { |
---|
989 | if (new_channum.isEmpty() || old_channum.isEmpty()) |
---|
990 | return false; |
---|
991 | |
---|
992 | if (new_channum == old_channum) |
---|
993 | return true; |
---|
994 | |
---|
995 | uint old_mplexid = GetMplexID(srcid, old_channum); |
---|
996 | if (!old_mplexid) |
---|
997 | return false; |
---|
998 | |
---|
999 | uint new_mplexid = GetMplexID(srcid, new_channum); |
---|
1000 | if (!new_mplexid) |
---|
1001 | return false; |
---|
1002 | |
---|
1003 | LOG(VB_CHANNEL, LOG_INFO, QString("IsOnSameMultiplex? %1==%2 -> %3") |
---|
1004 | .arg(old_mplexid).arg(new_mplexid) |
---|
1005 | .arg(old_mplexid == new_mplexid)); |
---|
1006 | |
---|
1007 | return old_mplexid == new_mplexid; |
---|
1008 | } |
---|
1009 | |
---|
1010 | /** \fn get_valid_recorder_list(uint) |
---|
1011 | * \brief Returns list of the recorders that have chanid in their sources. |
---|
1012 | * \param chanid Channel ID of channel we are querying recorders for. |
---|
1013 | * \return List of inputid's for recorders with channel. |
---|
1014 | */ |
---|
1015 | static QStringList get_valid_recorder_list(uint chanid) |
---|
1016 | { |
---|
1017 | QStringList reclist; |
---|
1018 | |
---|
1019 | // Query the database to determine which source is being used currently. |
---|
1020 | // set the EPG so that it only displays the channels of the current source |
---|
1021 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1022 | // We want to get the current source id for this recorder |
---|
1023 | query.prepare( |
---|
1024 | "SELECT capturecard.cardid " |
---|
1025 | "FROM channel " |
---|
1026 | "LEFT JOIN capturecard ON channel.sourceid = capturecard.sourceid " |
---|
1027 | "WHERE channel.chanid = :CHANID AND " |
---|
1028 | " capturecard.livetvorder > 0 " |
---|
1029 | "ORDER BY capturecard.livetvorder, capturecard.cardid"); |
---|
1030 | query.bindValue(":CHANID", chanid); |
---|
1031 | |
---|
1032 | if (!query.exec() || !query.isActive()) |
---|
1033 | { |
---|
1034 | MythDB::DBError("get_valid_recorder_list ChanID", query); |
---|
1035 | return reclist; |
---|
1036 | } |
---|
1037 | |
---|
1038 | while (query.next()) |
---|
1039 | reclist << query.value(0).toString(); |
---|
1040 | |
---|
1041 | return reclist; |
---|
1042 | } |
---|
1043 | |
---|
1044 | /** \fn get_valid_recorder_list(const QString&) |
---|
1045 | * \brief Returns list of the recorders that have channum in their sources. |
---|
1046 | * \param channum Channel "number" we are querying recorders for. |
---|
1047 | * \return List of inputid's for recorders with channel. |
---|
1048 | */ |
---|
1049 | static QStringList get_valid_recorder_list(const QString &channum) |
---|
1050 | { |
---|
1051 | QStringList reclist; |
---|
1052 | |
---|
1053 | // Query the database to determine which source is being used currently. |
---|
1054 | // set the EPG so that it only displays the channels of the current source |
---|
1055 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1056 | // We want to get the current source id for this recorder |
---|
1057 | query.prepare( |
---|
1058 | "SELECT capturecard.cardid " |
---|
1059 | "FROM channel " |
---|
1060 | "LEFT JOIN capturecard ON channel.sourceid = capturecard.sourceid " |
---|
1061 | "WHERE channel.channum = :CHANNUM AND " |
---|
1062 | " capturecard.livetvorder > 0 " |
---|
1063 | "ORDER BY capturecard.livetvorder, capturecard.cardid"); |
---|
1064 | query.bindValue(":CHANNUM", channum); |
---|
1065 | |
---|
1066 | if (!query.exec() || !query.isActive()) |
---|
1067 | { |
---|
1068 | MythDB::DBError("get_valid_recorder_list ChanNum", query); |
---|
1069 | return reclist; |
---|
1070 | } |
---|
1071 | |
---|
1072 | while (query.next()) |
---|
1073 | reclist << query.value(0).toString(); |
---|
1074 | |
---|
1075 | return reclist; |
---|
1076 | } |
---|
1077 | |
---|
1078 | /** |
---|
1079 | * \brief Returns list of the recorders that have chanid or channum |
---|
1080 | * in their sources. |
---|
1081 | * \param chanid Channel ID of channel we are querying recorders for. |
---|
1082 | * \param channum Channel "number" we are querying recorders for. |
---|
1083 | * \return List of inputid's for recorders with channel. |
---|
1084 | */ |
---|
1085 | QStringList ChannelUtil::GetValidRecorderList( |
---|
1086 | uint chanid, const QString &channum) |
---|
1087 | { |
---|
1088 | if (chanid) |
---|
1089 | return get_valid_recorder_list(chanid); |
---|
1090 | else if (!channum.isEmpty()) |
---|
1091 | return get_valid_recorder_list(channum); |
---|
1092 | return QStringList(); |
---|
1093 | } |
---|
1094 | |
---|
1095 | |
---|
1096 | vector<uint> ChannelUtil::GetConflicting(const QString &channum, uint sourceid) |
---|
1097 | { |
---|
1098 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1099 | vector<uint> conflicting; |
---|
1100 | |
---|
1101 | if (sourceid) |
---|
1102 | { |
---|
1103 | query.prepare( |
---|
1104 | "SELECT chanid from channel " |
---|
1105 | "WHERE sourceid = :SOURCEID AND " |
---|
1106 | " channum = :CHANNUM"); |
---|
1107 | query.bindValue(":SOURCEID", sourceid); |
---|
1108 | } |
---|
1109 | else |
---|
1110 | { |
---|
1111 | query.prepare( |
---|
1112 | "SELECT chanid from channel " |
---|
1113 | "WHERE channum = :CHANNUM"); |
---|
1114 | } |
---|
1115 | |
---|
1116 | query.bindValue(":CHANNUM", channum); |
---|
1117 | if (!query.exec()) |
---|
1118 | { |
---|
1119 | MythDB::DBError("IsConflicting", query); |
---|
1120 | conflicting.push_back(0); |
---|
1121 | return conflicting; |
---|
1122 | } |
---|
1123 | |
---|
1124 | while (query.next()) |
---|
1125 | conflicting.push_back(query.value(0).toUInt()); |
---|
1126 | |
---|
1127 | return conflicting; |
---|
1128 | } |
---|
1129 | |
---|
1130 | bool ChannelUtil::SetChannelValue(const QString &field_name, |
---|
1131 | QString value, |
---|
1132 | uint sourceid, |
---|
1133 | const QString &channum) |
---|
1134 | { |
---|
1135 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1136 | |
---|
1137 | query.prepare( |
---|
1138 | QString("UPDATE channel SET channel.%1=:VALUE " |
---|
1139 | "WHERE channel.channum = :CHANNUM AND " |
---|
1140 | " channel.sourceid = :SOURCEID").arg(field_name)); |
---|
1141 | |
---|
1142 | query.bindValue(":VALUE", value); |
---|
1143 | query.bindValue(":CHANNUM", channum); |
---|
1144 | query.bindValue(":SOURCEID", sourceid); |
---|
1145 | |
---|
1146 | return query.exec(); |
---|
1147 | } |
---|
1148 | |
---|
1149 | bool ChannelUtil::SetChannelValue(const QString &field_name, |
---|
1150 | QString value, |
---|
1151 | int chanid) |
---|
1152 | { |
---|
1153 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1154 | |
---|
1155 | query.prepare( |
---|
1156 | QString("UPDATE channel SET channel.%1=:VALUE " |
---|
1157 | "WHERE channel.chanid = :CHANID").arg(field_name)); |
---|
1158 | |
---|
1159 | query.bindValue(":VALUE", value); |
---|
1160 | query.bindValue(":CHANID", chanid); |
---|
1161 | |
---|
1162 | return query.exec(); |
---|
1163 | } |
---|
1164 | |
---|
1165 | /** Returns the DVB default authority for the chanid given. */ |
---|
1166 | QString ChannelUtil::GetDefaultAuthority(uint chanid) |
---|
1167 | { |
---|
1168 | static QReadWriteLock channel_default_authority_map_lock; |
---|
1169 | static QMap<uint,QString> channel_default_authority_map; |
---|
1170 | static bool run_init = true; |
---|
1171 | |
---|
1172 | channel_default_authority_map_lock.lockForRead(); |
---|
1173 | |
---|
1174 | if (run_init) |
---|
1175 | { |
---|
1176 | channel_default_authority_map_lock.unlock(); |
---|
1177 | channel_default_authority_map_lock.lockForWrite(); |
---|
1178 | if (run_init) |
---|
1179 | { |
---|
1180 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1181 | query.prepare( |
---|
1182 | "SELECT chanid, m.default_authority " |
---|
1183 | "FROM channel c " |
---|
1184 | "LEFT JOIN dtv_multiplex m " |
---|
1185 | "ON (c.mplexid = m.mplexid)"); |
---|
1186 | if (query.exec()) |
---|
1187 | { |
---|
1188 | while (query.next()) |
---|
1189 | { |
---|
1190 | if (!query.value(1).toString().isEmpty()) |
---|
1191 | { |
---|
1192 | channel_default_authority_map[query.value(0).toUInt()] = |
---|
1193 | query.value(1).toString(); |
---|
1194 | } |
---|
1195 | } |
---|
1196 | run_init = false; |
---|
1197 | } |
---|
1198 | else |
---|
1199 | { |
---|
1200 | MythDB::DBError("GetDefaultAuthority 1", query); |
---|
1201 | } |
---|
1202 | |
---|
1203 | query.prepare( |
---|
1204 | "SELECT chanid, default_authority " |
---|
1205 | "FROM channel"); |
---|
1206 | if (query.exec()) |
---|
1207 | { |
---|
1208 | while (query.next()) |
---|
1209 | { |
---|
1210 | if (!query.value(1).toString().isEmpty()) |
---|
1211 | { |
---|
1212 | channel_default_authority_map[query.value(0).toUInt()] = |
---|
1213 | query.value(1).toString(); |
---|
1214 | } |
---|
1215 | } |
---|
1216 | run_init = false; |
---|
1217 | } |
---|
1218 | else |
---|
1219 | { |
---|
1220 | MythDB::DBError("GetDefaultAuthority 2", query); |
---|
1221 | } |
---|
1222 | |
---|
1223 | } |
---|
1224 | } |
---|
1225 | |
---|
1226 | QMap<uint,QString>::iterator it = channel_default_authority_map.find(chanid); |
---|
1227 | QString ret; |
---|
1228 | if (it != channel_default_authority_map.end()) |
---|
1229 | ret = *it; |
---|
1230 | channel_default_authority_map_lock.unlock(); |
---|
1231 | |
---|
1232 | return ret; |
---|
1233 | } |
---|
1234 | |
---|
1235 | QString ChannelUtil::GetIcon(uint chanid) |
---|
1236 | { |
---|
1237 | static QReadWriteLock channel_icon_map_lock; |
---|
1238 | static QHash<uint,QString> channel_icon_map; |
---|
1239 | static bool run_init = true; |
---|
1240 | |
---|
1241 | channel_icon_map_lock.lockForRead(); |
---|
1242 | |
---|
1243 | QString ret(channel_icon_map.value(chanid, "_cold_")); |
---|
1244 | |
---|
1245 | channel_icon_map_lock.unlock(); |
---|
1246 | |
---|
1247 | if (ret != "_cold_") |
---|
1248 | return ret; |
---|
1249 | |
---|
1250 | channel_icon_map_lock.lockForWrite(); |
---|
1251 | |
---|
1252 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1253 | QString iconquery = "SELECT chanid, icon FROM channel"; |
---|
1254 | |
---|
1255 | if (run_init) |
---|
1256 | iconquery += " WHERE visible = 1"; |
---|
1257 | else |
---|
1258 | iconquery += " WHERE chanid = :CHANID"; |
---|
1259 | |
---|
1260 | query.prepare(iconquery); |
---|
1261 | |
---|
1262 | if (!run_init) |
---|
1263 | query.bindValue(":CHANID", chanid); |
---|
1264 | |
---|
1265 | if (query.exec()) |
---|
1266 | { |
---|
1267 | if (run_init) |
---|
1268 | { |
---|
1269 | channel_icon_map.reserve(query.size()); |
---|
1270 | while (query.next()) |
---|
1271 | { |
---|
1272 | channel_icon_map[query.value(0).toUInt()] = |
---|
1273 | query.value(1).toString(); |
---|
1274 | } |
---|
1275 | run_init = false; |
---|
1276 | } |
---|
1277 | else |
---|
1278 | { |
---|
1279 | channel_icon_map[chanid] = (query.next()) ? |
---|
1280 | query.value(1).toString() : ""; |
---|
1281 | } |
---|
1282 | } |
---|
1283 | else |
---|
1284 | { |
---|
1285 | MythDB::DBError("GetIcon", query); |
---|
1286 | } |
---|
1287 | |
---|
1288 | ret = channel_icon_map.value(chanid, ""); |
---|
1289 | |
---|
1290 | channel_icon_map_lock.unlock(); |
---|
1291 | |
---|
1292 | return ret; |
---|
1293 | } |
---|
1294 | |
---|
1295 | QString ChannelUtil::GetUnknownCallsign(void) |
---|
1296 | { |
---|
1297 | return tr("UNKNOWN", "Synthesized callsign"); |
---|
1298 | } |
---|
1299 | |
---|
1300 | int ChannelUtil::GetChanID(int mplexid, int service_transport_id, |
---|
1301 | int major_channel, int minor_channel, |
---|
1302 | int program_number) |
---|
1303 | { |
---|
1304 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1305 | |
---|
1306 | // find source id, so we can find manually inserted ATSC channels |
---|
1307 | query.prepare("SELECT sourceid " |
---|
1308 | "FROM dtv_multiplex " |
---|
1309 | "WHERE mplexid = :MPLEXID"); |
---|
1310 | query.bindValue(":MPLEXID", mplexid); |
---|
1311 | if (!query.exec()) |
---|
1312 | { |
---|
1313 | MythDB::DBError("Selecting channel/dtv_multiplex 2", query); |
---|
1314 | return -1; |
---|
1315 | } |
---|
1316 | if (!query.next()) |
---|
1317 | return -1; |
---|
1318 | |
---|
1319 | int source_id = query.value(0).toInt(); |
---|
1320 | |
---|
1321 | // find a proper ATSC channel |
---|
1322 | query.prepare("SELECT chanid FROM channel,dtv_multiplex " |
---|
1323 | "WHERE channel.sourceid = :SOURCEID AND " |
---|
1324 | " atsc_major_chan = :MAJORCHAN AND " |
---|
1325 | " atsc_minor_chan = :MINORCHAN AND " |
---|
1326 | " dtv_multiplex.transportid = :TRANSPORTID AND " |
---|
1327 | " dtv_multiplex.mplexid = :MPLEXID AND " |
---|
1328 | " dtv_multiplex.sourceid = channel.sourceid AND " |
---|
1329 | " dtv_multiplex.mplexid = channel.mplexid"); |
---|
1330 | |
---|
1331 | query.bindValue(":SOURCEID", source_id); |
---|
1332 | query.bindValue(":MAJORCHAN", major_channel); |
---|
1333 | query.bindValue(":MINORCHAN", minor_channel); |
---|
1334 | query.bindValue(":TRANSPORTID", service_transport_id); |
---|
1335 | query.bindValue(":MPLEXID", mplexid); |
---|
1336 | |
---|
1337 | if (query.exec() && query.next()) |
---|
1338 | return query.value(0).toInt(); |
---|
1339 | |
---|
1340 | // Find manually inserted/edited channels in order of scariness. |
---|
1341 | // find renamed channel, where atsc is valid |
---|
1342 | query.prepare("SELECT chanid FROM channel " |
---|
1343 | "WHERE sourceid = :SOURCEID AND " |
---|
1344 | "atsc_major_chan = :MAJORCHAN AND " |
---|
1345 | "atsc_minor_chan = :MINORCHAN"); |
---|
1346 | |
---|
1347 | query.bindValue(":SOURCEID", source_id); |
---|
1348 | query.bindValue(":MAJORCHAN", major_channel); |
---|
1349 | query.bindValue(":MINORCHAN", minor_channel); |
---|
1350 | |
---|
1351 | if (query.exec() && query.next()) |
---|
1352 | return query.value(0).toInt(); |
---|
1353 | |
---|
1354 | // find based on mpeg program number and mplexid alone |
---|
1355 | query.prepare("SELECT chanid FROM channel " |
---|
1356 | "WHERE sourceid = :SOURCEID AND " |
---|
1357 | "serviceID = :SERVICEID AND " |
---|
1358 | "mplexid = :MPLEXID"); |
---|
1359 | |
---|
1360 | query.bindValue(":SOURCEID", source_id); |
---|
1361 | query.bindValue(":SERVICEID", program_number); |
---|
1362 | query.bindValue(":MPLEXID", mplexid); |
---|
1363 | |
---|
1364 | if (query.exec() && query.next()) |
---|
1365 | return query.value(0).toInt(); |
---|
1366 | |
---|
1367 | return -1; |
---|
1368 | } |
---|
1369 | |
---|
1370 | uint ChannelUtil::FindChannel(uint sourceid, const QString &freqid) |
---|
1371 | { |
---|
1372 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1373 | query.prepare("SELECT chanid " |
---|
1374 | "FROM channel " |
---|
1375 | "WHERE sourceid = :SOURCEID AND " |
---|
1376 | " freqid = :FREQID"); |
---|
1377 | |
---|
1378 | query.bindValue(":SOURCEID", sourceid); |
---|
1379 | query.bindValue(":FREQID", freqid); |
---|
1380 | |
---|
1381 | if (!query.exec() || !query.isActive()) |
---|
1382 | MythDB::DBError("FindChannel", query); |
---|
1383 | else if (query.next()) |
---|
1384 | return query.value(0).toUInt(); |
---|
1385 | |
---|
1386 | return 0; |
---|
1387 | } |
---|
1388 | |
---|
1389 | |
---|
1390 | static uint get_max_chanid(uint sourceid) |
---|
1391 | { |
---|
1392 | QString qstr = "SELECT MAX(chanid) FROM channel "; |
---|
1393 | qstr += (sourceid) ? "WHERE sourceid = :SOURCEID" : ""; |
---|
1394 | |
---|
1395 | MSqlQuery query(MSqlQuery::DDCon()); |
---|
1396 | query.prepare(qstr); |
---|
1397 | |
---|
1398 | if (sourceid) |
---|
1399 | query.bindValue(":SOURCEID", sourceid); |
---|
1400 | |
---|
1401 | if (!query.exec() || !query.isActive()) |
---|
1402 | MythDB::DBError("Getting chanid for new channel (2)", query); |
---|
1403 | else if (!query.next()) |
---|
1404 | LOG(VB_GENERAL, LOG_ERR, "Error getting chanid for new channel."); |
---|
1405 | else |
---|
1406 | return query.value(0).toUInt(); |
---|
1407 | |
---|
1408 | return 0; |
---|
1409 | } |
---|
1410 | |
---|
1411 | static bool chanid_available(uint chanid) |
---|
1412 | { |
---|
1413 | MSqlQuery query(MSqlQuery::DDCon()); |
---|
1414 | query.prepare( |
---|
1415 | "SELECT chanid " |
---|
1416 | "FROM channel " |
---|
1417 | "WHERE chanid = :CHANID"); |
---|
1418 | query.bindValue(":CHANID", chanid); |
---|
1419 | |
---|
1420 | if (!query.exec() || !query.isActive()) |
---|
1421 | MythDB::DBError("is_chan_id_available", query); |
---|
1422 | else if (query.size() == 0) |
---|
1423 | return true; |
---|
1424 | |
---|
1425 | return false; |
---|
1426 | } |
---|
1427 | |
---|
1428 | /** \fn ChannelUtil::CreateChanID(uint, const QString&) |
---|
1429 | * \brief Creates a unique channel ID for database use. |
---|
1430 | * \return chanid if successful, -1 if not |
---|
1431 | */ |
---|
1432 | int ChannelUtil::CreateChanID(uint sourceid, const QString &chan_num) |
---|
1433 | { |
---|
1434 | // first try to base it on the channel number for human readability |
---|
1435 | uint chanid = 0; |
---|
1436 | int chansep = chan_num.indexOf(QRegExp("\\D")); |
---|
1437 | if (chansep > 0) |
---|
1438 | { |
---|
1439 | chanid = |
---|
1440 | sourceid * 10000 + |
---|
1441 | chan_num.left(chansep).toInt() * 100 + |
---|
1442 | chan_num.right(chan_num.length() - chansep - 1).toInt(); |
---|
1443 | } |
---|
1444 | else |
---|
1445 | { |
---|
1446 | chanid = sourceid * 10000 + chan_num.toInt(); |
---|
1447 | } |
---|
1448 | |
---|
1449 | if ((chanid > sourceid * 10000) && (chanid_available(chanid))) |
---|
1450 | return chanid; |
---|
1451 | |
---|
1452 | // try to at least base it on the sourceid for human readability |
---|
1453 | chanid = max(get_max_chanid(sourceid) + 1, sourceid * 10000); |
---|
1454 | |
---|
1455 | if (chanid_available(chanid)) |
---|
1456 | return chanid; |
---|
1457 | |
---|
1458 | // just get a chanid we know should work |
---|
1459 | chanid = get_max_chanid(0) + 1; |
---|
1460 | |
---|
1461 | if (chanid_available(chanid)) |
---|
1462 | return chanid; |
---|
1463 | |
---|
1464 | // failure |
---|
1465 | return -1; |
---|
1466 | } |
---|
1467 | |
---|
1468 | bool ChannelUtil::CreateChannel(uint db_mplexid, |
---|
1469 | uint db_sourceid, |
---|
1470 | uint new_channel_id, |
---|
1471 | const QString &callsign, |
---|
1472 | const QString &service_name, |
---|
1473 | const QString &chan_num, |
---|
1474 | uint service_id, |
---|
1475 | uint atsc_major_channel, |
---|
1476 | uint atsc_minor_channel, |
---|
1477 | bool use_on_air_guide, |
---|
1478 | bool hidden, |
---|
1479 | bool hidden_in_guide, |
---|
1480 | const QString &freqid, |
---|
1481 | QString icon, |
---|
1482 | QString format, |
---|
1483 | QString xmltvid, |
---|
1484 | QString default_authority) |
---|
1485 | { |
---|
1486 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1487 | |
---|
1488 | QString chanNum = (chan_num == "-1") ? |
---|
1489 | QString::number(service_id) : chan_num; |
---|
1490 | |
---|
1491 | QString qstr = |
---|
1492 | "INSERT INTO channel " |
---|
1493 | " (chanid, channum, sourceid, " |
---|
1494 | " callsign, name, serviceid, "; |
---|
1495 | qstr += (db_mplexid > 0) ? "mplexid, " : ""; |
---|
1496 | qstr += (!freqid.isEmpty()) ? "freqid, " : ""; |
---|
1497 | qstr += |
---|
1498 | " atsc_major_chan, atsc_minor_chan, " |
---|
1499 | " useonairguide, visible, tvformat, " |
---|
1500 | " icon, xmltvid, default_authority) " |
---|
1501 | "VALUES " |
---|
1502 | " (:CHANID, :CHANNUM, :SOURCEID, " |
---|
1503 | " :CALLSIGN, :NAME, :SERVICEID, "; |
---|
1504 | qstr += (db_mplexid > 0) ? ":MPLEXID, " : ""; |
---|
1505 | qstr += (!freqid.isEmpty()) ? ":FREQID, " : ""; |
---|
1506 | qstr += |
---|
1507 | " :MAJORCHAN, :MINORCHAN, " |
---|
1508 | " :USEOAG, :VISIBLE, :TVFORMAT, " |
---|
1509 | " :ICON, :XMLTVID, :AUTHORITY) "; |
---|
1510 | |
---|
1511 | query.prepare(qstr); |
---|
1512 | |
---|
1513 | query.bindValue(":CHANID", new_channel_id); |
---|
1514 | query.bindValue(":CHANNUM", chanNum); |
---|
1515 | query.bindValue(":SOURCEID", db_sourceid); |
---|
1516 | query.bindValue(":CALLSIGN", callsign); |
---|
1517 | query.bindValue(":NAME", service_name); |
---|
1518 | |
---|
1519 | if (db_mplexid > 0) |
---|
1520 | query.bindValue(":MPLEXID", db_mplexid); |
---|
1521 | |
---|
1522 | query.bindValue(":SERVICEID", service_id); |
---|
1523 | query.bindValue(":MAJORCHAN", atsc_major_channel); |
---|
1524 | query.bindValue(":MINORCHAN", atsc_minor_channel); |
---|
1525 | query.bindValue(":USEOAG", use_on_air_guide); |
---|
1526 | query.bindValue(":VISIBLE", !hidden); |
---|
1527 | (void) hidden_in_guide; // MythTV can't hide the channel in just the guide. |
---|
1528 | |
---|
1529 | if (!freqid.isEmpty()) |
---|
1530 | query.bindValue(":FREQID", freqid); |
---|
1531 | |
---|
1532 | QString tvformat = (atsc_minor_channel > 0) ? "ATSC" : format; |
---|
1533 | tvformat = tvformat.isNull() ? "" : tvformat; |
---|
1534 | query.bindValue(":TVFORMAT", tvformat); |
---|
1535 | |
---|
1536 | icon = (icon.isNull()) ? "" : icon; |
---|
1537 | query.bindValue(":ICON", icon); |
---|
1538 | |
---|
1539 | xmltvid = (xmltvid.isNull()) ? "" : xmltvid; |
---|
1540 | query.bindValue(":XMLTVID", xmltvid); |
---|
1541 | |
---|
1542 | default_authority = (default_authority.isNull()) ? "" : default_authority; |
---|
1543 | query.bindValue(":AUTHORITY", default_authority); |
---|
1544 | |
---|
1545 | if (!query.exec() || !query.isActive()) |
---|
1546 | { |
---|
1547 | MythDB::DBError("Adding Service", query); |
---|
1548 | return false; |
---|
1549 | } |
---|
1550 | return true; |
---|
1551 | } |
---|
1552 | |
---|
1553 | bool ChannelUtil::UpdateChannel(uint db_mplexid, |
---|
1554 | uint source_id, |
---|
1555 | uint channel_id, |
---|
1556 | const QString &callsign, |
---|
1557 | const QString &service_name, |
---|
1558 | const QString &chan_num, |
---|
1559 | uint service_id, |
---|
1560 | uint atsc_major_channel, |
---|
1561 | uint atsc_minor_channel, |
---|
1562 | bool use_on_air_guide, |
---|
1563 | bool hidden, |
---|
1564 | bool hidden_in_guide, |
---|
1565 | QString freqid, |
---|
1566 | QString icon, |
---|
1567 | QString format, |
---|
1568 | QString xmltvid, |
---|
1569 | QString default_authority) |
---|
1570 | { |
---|
1571 | if (!channel_id) |
---|
1572 | return false; |
---|
1573 | |
---|
1574 | QString tvformat = (atsc_minor_channel > 0) ? "ATSC" : format; |
---|
1575 | bool set_channum = !chan_num.isEmpty() && chan_num != "-1"; |
---|
1576 | QString qstr = QString( |
---|
1577 | "UPDATE channel " |
---|
1578 | "SET %1 %2 %3 %4 %5 %6" |
---|
1579 | " mplexid = :MPLEXID, serviceid = :SERVICEID, " |
---|
1580 | " atsc_major_chan = :MAJORCHAN, atsc_minor_chan = :MINORCHAN, " |
---|
1581 | " callsign = :CALLSIGN, name = :NAME, " |
---|
1582 | " sourceid = :SOURCEID, useonairguide = :USEOAG " |
---|
1583 | "WHERE chanid=:CHANID") |
---|
1584 | .arg((!set_channum) ? "" : "channum = :CHANNUM, ") |
---|
1585 | .arg((freqid.isEmpty()) ? "" : "freqid = :FREQID, ") |
---|
1586 | .arg((icon.isEmpty()) ? "" : "icon = :ICON, ") |
---|
1587 | .arg((tvformat.isEmpty()) ? "" : "tvformat = :TVFORMAT, ") |
---|
1588 | .arg((xmltvid.isEmpty()) ? "" : "xmltvid = :XMLTVID, ") |
---|
1589 | .arg((default_authority.isEmpty()) ? |
---|
1590 | "" : "default_authority = :AUTHORITY,"); |
---|
1591 | |
---|
1592 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1593 | query.prepare(qstr); |
---|
1594 | |
---|
1595 | query.bindValue(":CHANID", channel_id); |
---|
1596 | |
---|
1597 | if (set_channum) |
---|
1598 | query.bindValue(":CHANNUM", chan_num); |
---|
1599 | |
---|
1600 | query.bindValue(":SOURCEID", source_id); |
---|
1601 | query.bindValue(":CALLSIGN", callsign); |
---|
1602 | query.bindValue(":NAME", service_name); |
---|
1603 | |
---|
1604 | query.bindValue(":MPLEXID", db_mplexid); |
---|
1605 | |
---|
1606 | query.bindValue(":SERVICEID", service_id); |
---|
1607 | query.bindValue(":MAJORCHAN", atsc_major_channel); |
---|
1608 | query.bindValue(":MINORCHAN", atsc_minor_channel); |
---|
1609 | query.bindValue(":USEOAG", use_on_air_guide); |
---|
1610 | |
---|
1611 | (void) hidden; // Do not change the current value of "visible", trac #12332 |
---|
1612 | (void) hidden_in_guide; // MythTV can't hide the channel in just the guide. |
---|
1613 | |
---|
1614 | if (!freqid.isEmpty()) |
---|
1615 | query.bindValue(":FREQID", freqid); |
---|
1616 | |
---|
1617 | if (!tvformat.isEmpty()) |
---|
1618 | query.bindValue(":TVFORMAT", tvformat); |
---|
1619 | |
---|
1620 | if (!icon.isEmpty()) |
---|
1621 | query.bindValue(":ICON", icon); |
---|
1622 | if (!xmltvid.isEmpty()) |
---|
1623 | query.bindValue(":XMLTVID", xmltvid); |
---|
1624 | if (!default_authority.isEmpty()) |
---|
1625 | query.bindValue(":AUTHORITY", default_authority); |
---|
1626 | |
---|
1627 | if (!query.exec()) |
---|
1628 | { |
---|
1629 | MythDB::DBError("Updating Service", query); |
---|
1630 | return false; |
---|
1631 | } |
---|
1632 | return true; |
---|
1633 | } |
---|
1634 | |
---|
1635 | void ChannelUtil::UpdateInsertInfoFromDB(ChannelInsertInfo &chan) |
---|
1636 | { |
---|
1637 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1638 | query.prepare( |
---|
1639 | "SELECT xmltvid, useonairguide " |
---|
1640 | "FROM channel " |
---|
1641 | "WHERE chanid = :ID"); |
---|
1642 | query.bindValue(":ID", chan.channel_id); |
---|
1643 | |
---|
1644 | if (!query.exec()) |
---|
1645 | { |
---|
1646 | MythDB::DBError("UpdateInsertInfoFromDB", query); |
---|
1647 | return; |
---|
1648 | } |
---|
1649 | |
---|
1650 | if (query.next()) |
---|
1651 | { |
---|
1652 | QString xmltvid = query.value(0).toString(); |
---|
1653 | bool useeit = query.value(1).toInt(); |
---|
1654 | |
---|
1655 | if (!xmltvid.isEmpty()) |
---|
1656 | { |
---|
1657 | if (useeit) |
---|
1658 | LOG(VB_GENERAL, LOG_ERR, |
---|
1659 | "Using EIT and xmltv for the same channel " |
---|
1660 | "is a unsupported configuration."); |
---|
1661 | chan.xmltvid = xmltvid; |
---|
1662 | chan.use_on_air_guide = useeit; |
---|
1663 | } |
---|
1664 | } |
---|
1665 | } |
---|
1666 | |
---|
1667 | bool ChannelUtil::UpdateIPTVTuningData( |
---|
1668 | uint channel_id, const IPTVTuningData &tuning) |
---|
1669 | { |
---|
1670 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1671 | |
---|
1672 | query.prepare( |
---|
1673 | "DELETE FROM iptv_channel " |
---|
1674 | "WHERE chanid=:CHANID"); |
---|
1675 | query.bindValue(":CHANID", channel_id); |
---|
1676 | |
---|
1677 | if (!query.exec()) |
---|
1678 | { |
---|
1679 | MythDB::DBError("UpdateIPTVTuningData -- delete", query); |
---|
1680 | return false; |
---|
1681 | } |
---|
1682 | |
---|
1683 | query.prepare( |
---|
1684 | "INSERT INTO iptv_channel (chanid, url, type, bitrate) " |
---|
1685 | "VALUES (:CHANID, :URL, :TYPE, :BITRATE)"); |
---|
1686 | query.bindValue(":CHANID", channel_id); |
---|
1687 | |
---|
1688 | query.bindValue(":URL", tuning.GetDataURL().toString()); |
---|
1689 | query.bindValue(":TYPE", tuning.GetFECTypeString(0)); |
---|
1690 | query.bindValue(":BITRATE", tuning.GetBitrate(0)); |
---|
1691 | |
---|
1692 | if (!query.exec()) |
---|
1693 | { |
---|
1694 | MythDB::DBError("UpdateIPTVTuningData -- data", query); |
---|
1695 | return false; |
---|
1696 | } |
---|
1697 | |
---|
1698 | if (tuning.GetFECURL0().port() >= 0) |
---|
1699 | { |
---|
1700 | query.bindValue(":URL", tuning.GetFECURL0().toString()); |
---|
1701 | query.bindValue(":TYPE", tuning.GetFECTypeString(1)); |
---|
1702 | query.bindValue(":BITRATE", tuning.GetBitrate(1)); |
---|
1703 | if (!query.exec()) |
---|
1704 | { |
---|
1705 | MythDB::DBError("UpdateIPTVTuningData -- fec 0", query); |
---|
1706 | return false; |
---|
1707 | } |
---|
1708 | } |
---|
1709 | |
---|
1710 | if (tuning.GetFECURL1().port() >= 0) |
---|
1711 | { |
---|
1712 | query.bindValue(":URL", tuning.GetFECURL1().toString()); |
---|
1713 | query.bindValue(":TYPE", tuning.GetFECTypeString(2)); |
---|
1714 | query.bindValue(":BITRATE", tuning.GetBitrate(2)); |
---|
1715 | if (!query.exec()) |
---|
1716 | { |
---|
1717 | MythDB::DBError("UpdateIPTVTuningData -- fec 1", query); |
---|
1718 | return false; |
---|
1719 | } |
---|
1720 | } |
---|
1721 | |
---|
1722 | return true; |
---|
1723 | } |
---|
1724 | |
---|
1725 | bool ChannelUtil::DeleteChannel(uint channel_id) |
---|
1726 | { |
---|
1727 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1728 | query.prepare( |
---|
1729 | "DELETE FROM channel " |
---|
1730 | "WHERE chanid = :ID"); |
---|
1731 | query.bindValue(":ID", channel_id); |
---|
1732 | |
---|
1733 | if (!query.exec()) |
---|
1734 | { |
---|
1735 | MythDB::DBError("Delete Channel", query); |
---|
1736 | return false; |
---|
1737 | } |
---|
1738 | |
---|
1739 | query.prepare( |
---|
1740 | "DELETE FROM iptv_channel " |
---|
1741 | "WHERE chanid = :ID"); |
---|
1742 | query.bindValue(":ID", channel_id); |
---|
1743 | |
---|
1744 | if (!query.exec()) |
---|
1745 | { |
---|
1746 | MythDB::DBError("Delete Channel 2", query); |
---|
1747 | return false; |
---|
1748 | } |
---|
1749 | |
---|
1750 | return true; |
---|
1751 | } |
---|
1752 | |
---|
1753 | bool ChannelUtil::SetVisible(uint channel_id, bool visible) |
---|
1754 | { |
---|
1755 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1756 | query.prepare( |
---|
1757 | "UPDATE channel " |
---|
1758 | "SET visible = :VISIBLE " |
---|
1759 | "WHERE chanid = :ID"); |
---|
1760 | query.bindValue(":ID", channel_id); |
---|
1761 | query.bindValue(":VISIBLE", visible); |
---|
1762 | |
---|
1763 | if (!query.exec()) |
---|
1764 | { |
---|
1765 | MythDB::DBError("ChannelUtil::SetVisible", query); |
---|
1766 | return false; |
---|
1767 | } |
---|
1768 | |
---|
1769 | return true; |
---|
1770 | } |
---|
1771 | |
---|
1772 | bool ChannelUtil::SetServiceVersion(int mplexid, int version) |
---|
1773 | { |
---|
1774 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1775 | |
---|
1776 | query.prepare("UPDATE dtv_multiplex " |
---|
1777 | "SET serviceversion = :VERSION " |
---|
1778 | "WHERE mplexid = :MPLEXID"); |
---|
1779 | |
---|
1780 | query.bindValue(":VERSION", version); |
---|
1781 | query.bindValue(":MPLEXID", mplexid); |
---|
1782 | |
---|
1783 | if (!query.exec()) |
---|
1784 | { |
---|
1785 | MythDB::DBError("Selecting channel/dtv_multiplex", query); |
---|
1786 | return false; |
---|
1787 | } |
---|
1788 | return true; |
---|
1789 | } |
---|
1790 | |
---|
1791 | int ChannelUtil::GetServiceVersion(int mplexid) |
---|
1792 | { |
---|
1793 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1794 | |
---|
1795 | query.prepare("SELECT serviceversion " |
---|
1796 | "FROM dtv_multiplex " |
---|
1797 | "WHERE mplexid = :MPLEXID"); |
---|
1798 | |
---|
1799 | query.bindValue(":MPLEXID", mplexid); |
---|
1800 | |
---|
1801 | if (!query.exec()) |
---|
1802 | { |
---|
1803 | MythDB::DBError("Selecting channel/dtv_multiplex", query); |
---|
1804 | return false; |
---|
1805 | } |
---|
1806 | |
---|
1807 | if (query.next()) |
---|
1808 | return query.value(0).toInt(); |
---|
1809 | |
---|
1810 | return -1; |
---|
1811 | } |
---|
1812 | |
---|
1813 | bool ChannelUtil::GetATSCChannel(uint sourceid, const QString &channum, |
---|
1814 | uint &major, uint &minor) |
---|
1815 | { |
---|
1816 | major = minor = 0; |
---|
1817 | |
---|
1818 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1819 | query.prepare( |
---|
1820 | "SELECT atsc_major_chan, atsc_minor_chan " |
---|
1821 | "FROM channel " |
---|
1822 | "WHERE channum = :CHANNUM AND " |
---|
1823 | " sourceid = :SOURCEID"); |
---|
1824 | |
---|
1825 | query.bindValue(":SOURCEID", sourceid); |
---|
1826 | query.bindValue(":CHANNUM", channum); |
---|
1827 | |
---|
1828 | if (!query.exec() || !query.isActive()) |
---|
1829 | MythDB::DBError("getatscchannel", query); |
---|
1830 | else if (query.next()) |
---|
1831 | { |
---|
1832 | major = query.value(0).toUInt(); |
---|
1833 | minor = query.value(1).toUInt(); |
---|
1834 | return true; |
---|
1835 | } |
---|
1836 | |
---|
1837 | return false; |
---|
1838 | } |
---|
1839 | |
---|
1840 | bool ChannelUtil::GetChannelData( |
---|
1841 | uint sourceid, |
---|
1842 | uint &chanid, const QString &channum, |
---|
1843 | QString &tvformat, QString &modulation, |
---|
1844 | QString &freqtable, QString &freqid, |
---|
1845 | int &finetune, uint64_t &frequency, |
---|
1846 | QString &dtv_si_std, int &mpeg_prog_num, |
---|
1847 | uint &atsc_major, uint &atsc_minor, |
---|
1848 | uint &dvb_transportid, uint &dvb_networkid, |
---|
1849 | uint &mplexid, |
---|
1850 | bool &commfree) |
---|
1851 | { |
---|
1852 | chanid = 0; |
---|
1853 | tvformat.clear(); |
---|
1854 | modulation.clear(); |
---|
1855 | freqtable.clear();; |
---|
1856 | freqid.clear(); |
---|
1857 | dtv_si_std.clear(); |
---|
1858 | finetune = 0; |
---|
1859 | frequency = 0; |
---|
1860 | mpeg_prog_num = -1; |
---|
1861 | atsc_major = atsc_minor = mplexid = 0; |
---|
1862 | dvb_networkid = dvb_transportid = 0; |
---|
1863 | commfree = false; |
---|
1864 | |
---|
1865 | int found = 0; |
---|
1866 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1867 | query.prepare( |
---|
1868 | "SELECT finetune, freqid, tvformat, freqtable, " |
---|
1869 | " commmethod, mplexid, " |
---|
1870 | " atsc_major_chan, atsc_minor_chan, serviceid, " |
---|
1871 | " chanid, visible " |
---|
1872 | "FROM channel, videosource " |
---|
1873 | "WHERE videosource.sourceid = channel.sourceid AND " |
---|
1874 | " channum = :CHANNUM AND " |
---|
1875 | " channel.sourceid = :SOURCEID"); |
---|
1876 | query.bindValue(":CHANNUM", channum); |
---|
1877 | query.bindValue(":SOURCEID", sourceid); |
---|
1878 | |
---|
1879 | if (!query.exec() || !query.isActive()) |
---|
1880 | { |
---|
1881 | MythDB::DBError("GetChannelData", query); |
---|
1882 | return false; |
---|
1883 | } |
---|
1884 | |
---|
1885 | while (query.next()) |
---|
1886 | { |
---|
1887 | found += query.value(10).toInt(); |
---|
1888 | if (found) |
---|
1889 | { |
---|
1890 | finetune = query.value(0).toInt(); |
---|
1891 | freqid = query.value(1).toString(); |
---|
1892 | tvformat = query.value(2).toString(); |
---|
1893 | freqtable = query.value(3).toString(); |
---|
1894 | commfree = (query.value(4).toInt() == -2); |
---|
1895 | mplexid = query.value(5).toUInt(); |
---|
1896 | atsc_major = query.value(6).toUInt(); |
---|
1897 | atsc_minor = query.value(7).toUInt(); |
---|
1898 | mpeg_prog_num = (query.value(8).isNull()) ? -1 |
---|
1899 | : query.value(8).toInt(); |
---|
1900 | chanid = query.value(9).toUInt(); |
---|
1901 | } |
---|
1902 | } |
---|
1903 | |
---|
1904 | if (!found) |
---|
1905 | { |
---|
1906 | LOG(VB_GENERAL, LOG_INFO, |
---|
1907 | QString("No visible channels for %1").arg(channum)); |
---|
1908 | } |
---|
1909 | |
---|
1910 | if (found > 1) |
---|
1911 | { |
---|
1912 | LOG(VB_GENERAL, LOG_WARNING, |
---|
1913 | QString("Found multiple visible channels for %1").arg(channum)); |
---|
1914 | } |
---|
1915 | |
---|
1916 | if (!chanid) |
---|
1917 | { |
---|
1918 | LOG(VB_GENERAL, LOG_ERR, |
---|
1919 | QString("GetChannelData() failed because it could not\n" |
---|
1920 | "\t\t\tfind channel number '%1' in DB for source '%2'.") |
---|
1921 | .arg(channum).arg(sourceid)); |
---|
1922 | return false; |
---|
1923 | } |
---|
1924 | |
---|
1925 | if (!mplexid || (mplexid == 32767)) /* 32767 deals with old lineups */ |
---|
1926 | return true; |
---|
1927 | |
---|
1928 | return GetTuningParams(mplexid, modulation, frequency, |
---|
1929 | dvb_transportid, dvb_networkid, dtv_si_std); |
---|
1930 | } |
---|
1931 | |
---|
1932 | IPTVTuningData ChannelUtil::GetIPTVTuningData(uint chanid) |
---|
1933 | { |
---|
1934 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1935 | query.prepare( |
---|
1936 | "SELECT type+0, url, bitrate " |
---|
1937 | "FROM iptv_channel " |
---|
1938 | "WHERE chanid = :CHANID " |
---|
1939 | "ORDER BY type+0"); |
---|
1940 | query.bindValue(":CHANID", chanid); |
---|
1941 | |
---|
1942 | if (!query.exec()) |
---|
1943 | { |
---|
1944 | MythDB::DBError("GetChannelData -- iptv", query); |
---|
1945 | return IPTVTuningData(); |
---|
1946 | } |
---|
1947 | |
---|
1948 | QString data_url, fec_url0, fec_url1; |
---|
1949 | IPTVTuningData::FECType fec_type = IPTVTuningData::kNone; |
---|
1950 | uint bitrate[3] = { 0, 0, 0, }; |
---|
1951 | while (query.next()) |
---|
1952 | { |
---|
1953 | IPTVTuningData::IPTVType type = (IPTVTuningData::IPTVType) |
---|
1954 | query.value(0).toUInt(); |
---|
1955 | switch (type) |
---|
1956 | { |
---|
1957 | case IPTVTuningData::kData: |
---|
1958 | data_url = query.value(1).toString(); |
---|
1959 | bitrate[0] = query.value(2).toUInt(); |
---|
1960 | break; |
---|
1961 | case IPTVTuningData::kRFC2733_1: |
---|
1962 | case IPTVTuningData::kRFC5109_1: |
---|
1963 | case IPTVTuningData::kSMPTE2022_1: |
---|
1964 | fec_url0 = query.value(1).toString(); |
---|
1965 | bitrate[1] = query.value(2).toUInt(); |
---|
1966 | break; |
---|
1967 | case IPTVTuningData::kRFC2733_2: |
---|
1968 | case IPTVTuningData::kRFC5109_2: |
---|
1969 | case IPTVTuningData::kSMPTE2022_2: |
---|
1970 | fec_url1 = query.value(1).toString(); |
---|
1971 | bitrate[2] = query.value(2).toUInt(); |
---|
1972 | break; |
---|
1973 | } |
---|
1974 | switch (type) |
---|
1975 | { |
---|
1976 | case IPTVTuningData::kData: |
---|
1977 | break; |
---|
1978 | case IPTVTuningData::kRFC2733_1: |
---|
1979 | fec_type = IPTVTuningData::kRFC2733; |
---|
1980 | break; |
---|
1981 | case IPTVTuningData::kRFC5109_1: |
---|
1982 | fec_type = IPTVTuningData::kRFC5109; |
---|
1983 | break; |
---|
1984 | case IPTVTuningData::kSMPTE2022_1: |
---|
1985 | fec_type = IPTVTuningData::kSMPTE2022; |
---|
1986 | break; |
---|
1987 | case IPTVTuningData::kRFC2733_2: |
---|
1988 | case IPTVTuningData::kRFC5109_2: |
---|
1989 | case IPTVTuningData::kSMPTE2022_2: |
---|
1990 | break; // will be handled by type of first FEC stream |
---|
1991 | } |
---|
1992 | } |
---|
1993 | |
---|
1994 | IPTVTuningData tuning(data_url, bitrate[0], fec_type, |
---|
1995 | fec_url0, bitrate[1], fec_url1, bitrate[2]); |
---|
1996 | LOG(VB_GENERAL, LOG_INFO, QString("Loaded %1 for %2") |
---|
1997 | .arg(tuning.GetDeviceName()).arg(chanid)); |
---|
1998 | return tuning; |
---|
1999 | } |
---|
2000 | |
---|
2001 | // TODO This should be modified to load a complete channelinfo object including |
---|
2002 | // all fields from the database |
---|
2003 | /** |
---|
2004 | * \deprecated Use ChannelInfo::LoadChannels() instead |
---|
2005 | */ |
---|
2006 | ChannelInfoList ChannelUtil::GetChannelsInternal( |
---|
2007 | uint sourceid, bool vis_only, bool include_disconnected, |
---|
2008 | const QString &grp, uint changrpid) |
---|
2009 | { |
---|
2010 | ChannelInfoList list; |
---|
2011 | |
---|
2012 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
2013 | |
---|
2014 | QString qstr = QString( |
---|
2015 | "SELECT channum, callsign, channel.chanid, " |
---|
2016 | " atsc_major_chan, atsc_minor_chan, " |
---|
2017 | " name, icon, mplexid, visible, " |
---|
2018 | " channel.sourceid, GROUP_CONCAT(DISTINCT capturecard.cardid), " |
---|
2019 | " GROUP_CONCAT(DISTINCT channelgroup.grpid), " |
---|
2020 | " xmltvid " |
---|
2021 | "FROM channel " |
---|
2022 | "LEFT JOIN channelgroup ON channel.chanid = channelgroup.chanid " |
---|
2023 | " %1 JOIN capturecard ON capturecard.sourceid = channel.sourceid ") |
---|
2024 | .arg((include_disconnected) ? "LEFT" : ""); |
---|
2025 | |
---|
2026 | QString cond = " WHERE "; |
---|
2027 | |
---|
2028 | if (sourceid) |
---|
2029 | { |
---|
2030 | qstr += QString("WHERE channel.sourceid='%1' ").arg(sourceid); |
---|
2031 | cond = " AND "; |
---|
2032 | } |
---|
2033 | |
---|
2034 | // Select only channels from the specified channel group |
---|
2035 | if (changrpid > 0) |
---|
2036 | { |
---|
2037 | qstr += QString("%1 channelgroup.grpid = '%2' ") |
---|
2038 | .arg(cond).arg(changrpid); |
---|
2039 | cond = " AND "; |
---|
2040 | } |
---|
2041 | |
---|
2042 | if (vis_only) |
---|
2043 | { |
---|
2044 | qstr += QString("%1 visible=1 ").arg(cond); |
---|
2045 | cond = " AND "; |
---|
2046 | } |
---|
2047 | |
---|
2048 | qstr += " GROUP BY chanid"; |
---|
2049 | |
---|
2050 | if (!grp.isEmpty()) |
---|
2051 | qstr += QString(", %1").arg(grp); |
---|
2052 | |
---|
2053 | query.prepare(qstr); |
---|
2054 | if (!query.exec()) |
---|
2055 | { |
---|
2056 | MythDB::DBError("ChannelUtil::GetChannels()", query); |
---|
2057 | return list; |
---|
2058 | } |
---|
2059 | |
---|
2060 | while (query.next()) |
---|
2061 | { |
---|
2062 | if (query.value(0).toString().isEmpty() || !query.value(2).toUInt()) |
---|
2063 | continue; // skip if channum blank, or chanid empty |
---|
2064 | |
---|
2065 | ChannelInfo chan( |
---|
2066 | query.value(0).toString(), /* channum */ |
---|
2067 | query.value(1).toString(), /* callsign */ |
---|
2068 | query.value(2).toUInt(), /* chanid */ |
---|
2069 | query.value(3).toUInt(), /* ATSC major */ |
---|
2070 | query.value(4).toUInt(), /* ATSC minor */ |
---|
2071 | query.value(7).toUInt(), /* mplexid */ |
---|
2072 | query.value(8).toBool(), /* visible */ |
---|
2073 | query.value(5).toString(), /* name */ |
---|
2074 | query.value(6).toString(), /* icon */ |
---|
2075 | query.value(9).toUInt()); /* sourceid */ |
---|
2076 | |
---|
2077 | chan.xmltvid = query.value(12).toString(); /* xmltvid */ |
---|
2078 | |
---|
2079 | QStringList inputIDs = query.value(11).toString().split(","); |
---|
2080 | QString inputid; |
---|
2081 | while (!inputIDs.isEmpty()) |
---|
2082 | chan.AddInputId(inputIDs.takeFirst().toUInt()); |
---|
2083 | |
---|
2084 | QStringList groupIDs = query.value(10).toString().split(","); |
---|
2085 | QString groupid; |
---|
2086 | while (!groupIDs.isEmpty()) |
---|
2087 | chan.AddGroupId(groupIDs.takeFirst().toUInt()); |
---|
2088 | |
---|
2089 | list.push_back(chan); |
---|
2090 | |
---|
2091 | } |
---|
2092 | |
---|
2093 | return list; |
---|
2094 | } |
---|
2095 | |
---|
2096 | vector<uint> ChannelUtil::GetChanIDs(int sourceid, bool onlyVisible) |
---|
2097 | { |
---|
2098 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
2099 | |
---|
2100 | QString select = "SELECT chanid FROM channel "; |
---|
2101 | // Yes, this a little ugly |
---|
2102 | if (onlyVisible || sourceid > 0) |
---|
2103 | { |
---|
2104 | select += "WHERE "; |
---|
2105 | if (onlyVisible) |
---|
2106 | select += "visible = 1 "; |
---|
2107 | if (sourceid > 0) |
---|
2108 | { |
---|
2109 | if (onlyVisible) |
---|
2110 | select += "AND "; |
---|
2111 | select += "sourceid=" + QString::number(sourceid); |
---|
2112 | } |
---|
2113 | } |
---|
2114 | |
---|
2115 | vector<uint> list; |
---|
2116 | if (!query.exec(select)) |
---|
2117 | { |
---|
2118 | MythDB::DBError("SourceUtil::GetChanIDs()", query); |
---|
2119 | return list; |
---|
2120 | } |
---|
2121 | |
---|
2122 | while (query.next()) |
---|
2123 | list.push_back(query.value(0).toUInt()); |
---|
2124 | |
---|
2125 | return list; |
---|
2126 | } |
---|
2127 | |
---|
2128 | inline bool lt_callsign(const ChannelInfo &a, const ChannelInfo &b) |
---|
2129 | { |
---|
2130 | return naturalCompare(a.callsign, b.callsign) < 0; |
---|
2131 | } |
---|
2132 | |
---|
2133 | inline bool lt_smart(const ChannelInfo &a, const ChannelInfo &b) |
---|
2134 | { |
---|
2135 | static QMutex sepExprLock; |
---|
2136 | static const QRegExp sepExpr(ChannelUtil::kATSCSeparators); |
---|
2137 | |
---|
2138 | int cmp = 0; |
---|
2139 | |
---|
2140 | bool isIntA, isIntB; |
---|
2141 | int a_int = a.channum.toUInt(&isIntA); |
---|
2142 | int b_int = b.channum.toUInt(&isIntB); |
---|
2143 | int a_major = a.atsc_major_chan; |
---|
2144 | int b_major = b.atsc_major_chan; |
---|
2145 | int a_minor = a.atsc_minor_chan; |
---|
2146 | int b_minor = b.atsc_minor_chan; |
---|
2147 | |
---|
2148 | // Extract minor and major numbers from channum.. |
---|
2149 | bool tmp1, tmp2; |
---|
2150 | int idxA, idxB; |
---|
2151 | { |
---|
2152 | QMutexLocker locker(&sepExprLock); |
---|
2153 | idxA = a.channum.indexOf(sepExpr); |
---|
2154 | idxB = b.channum.indexOf(sepExpr); |
---|
2155 | } |
---|
2156 | if (idxA >= 0) |
---|
2157 | { |
---|
2158 | int major = a.channum.left(idxA).toUInt(&tmp1); |
---|
2159 | int minor = a.channum.mid(idxA+1).toUInt(&tmp2); |
---|
2160 | if (tmp1 && tmp2) |
---|
2161 | (a_major = major), (a_minor = minor), (isIntA = false); |
---|
2162 | } |
---|
2163 | |
---|
2164 | if (idxB >= 0) |
---|
2165 | { |
---|
2166 | int major = b.channum.left(idxB).toUInt(&tmp1); |
---|
2167 | int minor = b.channum.mid(idxB+1).toUInt(&tmp2); |
---|
2168 | if (tmp1 && tmp2) |
---|
2169 | (b_major = major), (b_minor = minor), (isIntB = false); |
---|
2170 | } |
---|
2171 | |
---|
2172 | // If ATSC channel has been renumbered, sort by new channel number |
---|
2173 | if ((a_minor > 0) && isIntA) |
---|
2174 | { |
---|
2175 | int atsc_int = (QString("%1%2").arg(a_major).arg(a_minor)).toInt(); |
---|
2176 | a_minor = (atsc_int == a_int) ? a_minor : 0; |
---|
2177 | } |
---|
2178 | |
---|
2179 | if ((b_minor > 0) && isIntB) |
---|
2180 | { |
---|
2181 | int atsc_int = (QString("%1%2").arg(b_major).arg(b_minor)).toInt(); |
---|
2182 | b_minor = (atsc_int == b_int) ? b_minor : 0; |
---|
2183 | } |
---|
2184 | |
---|
2185 | // one of the channels is an ATSC channel, and the other |
---|
2186 | // is either ATSC or is numeric. |
---|
2187 | if ((a_minor || b_minor) && |
---|
2188 | (a_minor || isIntA) && (b_minor || isIntB)) |
---|
2189 | { |
---|
2190 | int a_maj = (!a_minor && isIntA) ? a_int : a_major; |
---|
2191 | int b_maj = (!b_minor && isIntB) ? b_int : b_major; |
---|
2192 | if ((cmp = a_maj - b_maj)) |
---|
2193 | return cmp < 0; |
---|
2194 | |
---|
2195 | if ((cmp = a_minor - b_minor)) |
---|
2196 | return cmp < 0; |
---|
2197 | } |
---|
2198 | |
---|
2199 | if (isIntA && isIntB) |
---|
2200 | { |
---|
2201 | // both channels have a numeric channum |
---|
2202 | cmp = a_int - b_int; |
---|
2203 | if (cmp) |
---|
2204 | return cmp < 0; |
---|
2205 | } |
---|
2206 | else if (isIntA ^ isIntB) |
---|
2207 | { |
---|
2208 | // if only one is channel numeric always consider it less than |
---|
2209 | return isIntA; |
---|
2210 | } |
---|
2211 | else |
---|
2212 | { |
---|
2213 | // neither of channels have a numeric channum |
---|
2214 | cmp = naturalCompare(a.channum, b.channum); |
---|
2215 | if (cmp) |
---|
2216 | return cmp < 0; |
---|
2217 | } |
---|
2218 | |
---|
2219 | return lt_callsign(a,b); |
---|
2220 | } |
---|
2221 | |
---|
2222 | uint ChannelUtil::GetChannelCount(int sourceid) |
---|
2223 | { |
---|
2224 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
2225 | QString select; |
---|
2226 | |
---|
2227 | |
---|
2228 | select = "SELECT chanid FROM channel"; |
---|
2229 | if (sourceid >= 0) |
---|
2230 | select += " WHERE sourceid=" + QString::number(sourceid); |
---|
2231 | select += ';'; |
---|
2232 | |
---|
2233 | query.prepare(select); |
---|
2234 | |
---|
2235 | if (!query.exec() || !query.isActive()) |
---|
2236 | return 0; |
---|
2237 | |
---|
2238 | return query.size(); |
---|
2239 | } |
---|
2240 | |
---|
2241 | void ChannelUtil::SortChannels(ChannelInfoList &list, const QString &order, |
---|
2242 | bool eliminate_duplicates) |
---|
2243 | { |
---|
2244 | bool cs = order.toLower() == "callsign"; |
---|
2245 | if (cs) |
---|
2246 | stable_sort(list.begin(), list.end(), lt_callsign); |
---|
2247 | else /* if (sortorder == "channum") */ |
---|
2248 | stable_sort(list.begin(), list.end(), lt_smart); |
---|
2249 | |
---|
2250 | if (eliminate_duplicates && !list.empty()) |
---|
2251 | { |
---|
2252 | ChannelInfoList tmp; |
---|
2253 | tmp.push_back(list[0]); |
---|
2254 | for (uint i = 1; i < list.size(); i++) |
---|
2255 | { |
---|
2256 | if ((cs && lt_callsign(tmp.back(), list[i])) || |
---|
2257 | (!cs && lt_smart(tmp.back(), list[i]))) |
---|
2258 | { |
---|
2259 | tmp.push_back(list[i]); |
---|
2260 | } |
---|
2261 | } |
---|
2262 | |
---|
2263 | list = tmp; |
---|
2264 | } |
---|
2265 | } |
---|
2266 | |
---|
2267 | // Return the array index of the best matching channel. An exact |
---|
2268 | // match is the best match. Otherwise, find the closest numerical |
---|
2269 | // value greater than channum. E.g., if the channel list is {2_1, |
---|
2270 | // 2_2, 4_1, 4_2, 300} then input 3 returns 2_2, input 4 returns 2_2, |
---|
2271 | // and input 5 returns 4_2. |
---|
2272 | // |
---|
2273 | // The list does not need to be sorted. |
---|
2274 | int ChannelUtil::GetNearestChannel(const ChannelInfoList &list, |
---|
2275 | const QString &channum) |
---|
2276 | { |
---|
2277 | ChannelInfo target; |
---|
2278 | target.channum = channum; |
---|
2279 | int b = -1; // index of best seen so far |
---|
2280 | for (int i = 0; i < (int)list.size(); ++i) |
---|
2281 | { |
---|
2282 | // Index i is a better result if any of the following hold: |
---|
2283 | // i is the first element seen |
---|
2284 | // i < target < best (i.e., i is the first one less than the target) |
---|
2285 | // best < i < target |
---|
2286 | // target < i < best |
---|
2287 | if ((b < 0) || |
---|
2288 | (lt_smart(list[i], target) && lt_smart(target, list[b])) || |
---|
2289 | (lt_smart(list[b], list[i]) && lt_smart(list[i], target)) || |
---|
2290 | (lt_smart(target, list[i]) && lt_smart(list[i], list[b]))) |
---|
2291 | { |
---|
2292 | b = i; |
---|
2293 | } |
---|
2294 | } |
---|
2295 | return b; |
---|
2296 | } |
---|
2297 | |
---|
2298 | uint ChannelUtil::GetNextChannel( |
---|
2299 | const ChannelInfoList &sorted, |
---|
2300 | uint old_chanid, |
---|
2301 | uint mplexid_restriction, |
---|
2302 | uint chanid_restriction, |
---|
2303 | ChannelChangeDirection direction, |
---|
2304 | bool skip_non_visible, |
---|
2305 | bool skip_same_channum_and_callsign) |
---|
2306 | { |
---|
2307 | ChannelInfoList::const_iterator it = |
---|
2308 | find(sorted.begin(), sorted.end(), old_chanid); |
---|
2309 | |
---|
2310 | if (it == sorted.end()) |
---|
2311 | it = sorted.begin(); // not in list, pretend we are on first channel |
---|
2312 | |
---|
2313 | if (it == sorted.end()) |
---|
2314 | return 0; // no channels.. |
---|
2315 | |
---|
2316 | ChannelInfoList::const_iterator start = it; |
---|
2317 | |
---|
2318 | if (CHANNEL_DIRECTION_DOWN == direction) |
---|
2319 | { |
---|
2320 | do |
---|
2321 | { |
---|
2322 | if (it == sorted.begin()) |
---|
2323 | { |
---|
2324 | it = find(sorted.begin(), sorted.end(), |
---|
2325 | sorted.rbegin()->chanid); |
---|
2326 | if (it == sorted.end()) |
---|
2327 | { |
---|
2328 | --it; |
---|
2329 | } |
---|
2330 | } |
---|
2331 | else |
---|
2332 | --it; |
---|
2333 | } |
---|
2334 | while ((it != start) && |
---|
2335 | ((skip_non_visible && !it->visible) || |
---|
2336 | (skip_same_channum_and_callsign && |
---|
2337 | it->channum == start->channum && |
---|
2338 | it->callsign == start->callsign) || |
---|
2339 | (mplexid_restriction && |
---|
2340 | (mplexid_restriction != it->mplexid)) || |
---|
2341 | (chanid_restriction && |
---|
2342 | (chanid_restriction != it->chanid)))); |
---|
2343 | } |
---|
2344 | else if ((CHANNEL_DIRECTION_UP == direction) || |
---|
2345 | (CHANNEL_DIRECTION_FAVORITE == direction)) |
---|
2346 | { |
---|
2347 | do |
---|
2348 | { |
---|
2349 | ++it; |
---|
2350 | if (it == sorted.end()) |
---|
2351 | it = sorted.begin(); |
---|
2352 | } |
---|
2353 | while ((it != start) && |
---|
2354 | ((skip_non_visible && !it->visible) || |
---|
2355 | (skip_same_channum_and_callsign && |
---|
2356 | it->channum == start->channum && |
---|
2357 | it->callsign == start->callsign) || |
---|
2358 | (mplexid_restriction && |
---|
2359 | (mplexid_restriction != it->mplexid)) || |
---|
2360 | (chanid_restriction && |
---|
2361 | (chanid_restriction != it->chanid)))); |
---|
2362 | } |
---|
2363 | |
---|
2364 | return it->chanid; |
---|
2365 | } |
---|
2366 | |
---|
2367 | ChannelInfoList ChannelUtil::LoadChannels(uint startIndex, uint count, |
---|
2368 | uint &totalAvailable, |
---|
2369 | bool ignoreHidden, |
---|
2370 | ChannelUtil::OrderBy orderBy, |
---|
2371 | ChannelUtil::GroupBy groupBy, |
---|
2372 | uint sourceID, |
---|
2373 | uint channelGroupID) |
---|
2374 | { |
---|
2375 | ChannelInfoList channelList; |
---|
2376 | |
---|
2377 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
2378 | QString sql = "SELECT %1 channum, freqid, channel.sourceid, " |
---|
2379 | "callsign, name, icon, finetune, videofilters, xmltvid, " |
---|
2380 | "channel.recpriority, channel.contrast, channel.brightness, " |
---|
2381 | "channel.colour, channel.hue, tvformat, " |
---|
2382 | "visible, outputfilters, useonairguide, mplexid, " |
---|
2383 | "serviceid, atsc_major_chan, atsc_minor_chan, last_record, " |
---|
2384 | "default_authority, commmethod, tmoffset, iptvid, " |
---|
2385 | "channel.chanid, " |
---|
2386 | "GROUP_CONCAT(DISTINCT channelgroup.grpid), " // Creates a CSV list of channel groupids for this channel |
---|
2387 | "GROUP_CONCAT(DISTINCT capturecard.cardid) " // Creates a CSV list of inputids for this channel |
---|
2388 | "FROM channel " |
---|
2389 | "LEFT JOIN channelgroup ON channel.chanid = channelgroup.chanid " |
---|
2390 | "LEFT JOIN capturecard ON capturecard.sourceid = channel.sourceid "; |
---|
2391 | |
---|
2392 | QStringList cond; |
---|
2393 | if (ignoreHidden) |
---|
2394 | cond << "channel.visible = :VISIBLE "; |
---|
2395 | |
---|
2396 | if (channelGroupID > 0) |
---|
2397 | cond << "channelgroup.grpid = :CHANGROUPID "; |
---|
2398 | |
---|
2399 | if (sourceID > 0) |
---|
2400 | cond << "channel.sourceid = :SOURCEID "; |
---|
2401 | |
---|
2402 | if (!cond.isEmpty()) |
---|
2403 | sql += QString("WHERE %1").arg(cond.join("AND ")); |
---|
2404 | |
---|
2405 | if (groupBy == kChanGroupByCallsign) |
---|
2406 | sql += "GROUP BY channel.callsign "; |
---|
2407 | else |
---|
2408 | sql += "GROUP BY channel.chanid "; // We must always group for this query |
---|
2409 | |
---|
2410 | if (orderBy == kChanOrderByName) |
---|
2411 | sql += "ORDER BY channel.name "; |
---|
2412 | else // kChanOrderByChanNum |
---|
2413 | { |
---|
2414 | // Natural sorting including subchannels e.g. 2_4, 1.3 |
---|
2415 | sql += "ORDER BY LPAD(CAST(channel.channum AS UNSIGNED), 10, 0), " |
---|
2416 | " LPAD(channel.channum, 10, 0) "; |
---|
2417 | } |
---|
2418 | |
---|
2419 | if (count > 0) |
---|
2420 | sql += "LIMIT :LIMIT "; |
---|
2421 | |
---|
2422 | if (startIndex > 0) |
---|
2423 | sql += "OFFSET :STARTINDEX "; |
---|
2424 | |
---|
2425 | |
---|
2426 | if (startIndex > 0 || count > 0) |
---|
2427 | sql = sql.arg("SQL_CALC_FOUND_ROWS"); |
---|
2428 | else |
---|
2429 | sql = sql.arg(""); // remove place holder |
---|
2430 | |
---|
2431 | query.prepare(sql); |
---|
2432 | |
---|
2433 | if (ignoreHidden) |
---|
2434 | query.bindValue(":VISIBLE", 1); |
---|
2435 | |
---|
2436 | if (channelGroupID > 0) |
---|
2437 | query.bindValue(":CHANGROUPID", channelGroupID); |
---|
2438 | |
---|
2439 | if (sourceID > 0) |
---|
2440 | query.bindValue(":SOURCEID", sourceID); |
---|
2441 | |
---|
2442 | if (count > 0) |
---|
2443 | query.bindValue(":LIMIT", count); |
---|
2444 | |
---|
2445 | if (startIndex > 0) |
---|
2446 | query.bindValue(":STARTINDEX", startIndex); |
---|
2447 | |
---|
2448 | if (!query.exec()) |
---|
2449 | { |
---|
2450 | MythDB::DBError("ChannelInfo::Load()", query); |
---|
2451 | return channelList; |
---|
2452 | } |
---|
2453 | |
---|
2454 | while (query.next()) |
---|
2455 | { |
---|
2456 | ChannelInfo channelInfo; |
---|
2457 | channelInfo.channum = query.value(0).toString(); |
---|
2458 | channelInfo.freqid = query.value(1).toString(); |
---|
2459 | channelInfo.sourceid = query.value(2).toUInt(); |
---|
2460 | channelInfo.callsign = query.value(3).toString(); |
---|
2461 | channelInfo.name = query.value(4).toString(); |
---|
2462 | channelInfo.icon = query.value(5).toString(); |
---|
2463 | channelInfo.finetune = query.value(6).toInt(); |
---|
2464 | channelInfo.videofilters = query.value(7).toString(); |
---|
2465 | channelInfo.xmltvid = query.value(8).toString(); |
---|
2466 | channelInfo.recpriority = query.value(9).toInt(); |
---|
2467 | channelInfo.contrast = query.value(10).toUInt(); |
---|
2468 | channelInfo.brightness = query.value(11).toUInt(); |
---|
2469 | channelInfo.colour = query.value(12).toUInt(); |
---|
2470 | channelInfo.hue = query.value(13).toUInt(); |
---|
2471 | channelInfo.tvformat = query.value(14).toString(); |
---|
2472 | channelInfo.visible = query.value(15).toBool(); |
---|
2473 | channelInfo.outputfilters = query.value(16).toString(); |
---|
2474 | channelInfo.useonairguide = query.value(17).toBool(); |
---|
2475 | channelInfo.mplexid = query.value(18).toUInt(); |
---|
2476 | channelInfo.serviceid = query.value(19).toUInt(); |
---|
2477 | channelInfo.atsc_major_chan = query.value(20).toUInt(); |
---|
2478 | channelInfo.atsc_minor_chan = query.value(21).toUInt(); |
---|
2479 | channelInfo.last_record = query.value(22).toDateTime(); |
---|
2480 | channelInfo.default_authority = query.value(23).toString(); |
---|
2481 | channelInfo.commmethod = query.value(24).toUInt(); |
---|
2482 | channelInfo.tmoffset = query.value(25).toUInt(); |
---|
2483 | channelInfo.iptvid = query.value(26).toUInt(); |
---|
2484 | channelInfo.chanid = query.value(27).toUInt(); |
---|
2485 | |
---|
2486 | QStringList groupIDs = query.value(28).toString().split(","); |
---|
2487 | QString groupid; |
---|
2488 | while (!groupIDs.isEmpty()) |
---|
2489 | channelInfo.AddGroupId(groupIDs.takeFirst().toUInt()); |
---|
2490 | |
---|
2491 | QStringList inputIDs = query.value(29).toString().split(","); |
---|
2492 | QString inputid; |
---|
2493 | while (!inputIDs.isEmpty()) |
---|
2494 | channelInfo.AddInputId(inputIDs.takeFirst().toUInt()); |
---|
2495 | |
---|
2496 | channelList.push_back(channelInfo); |
---|
2497 | } |
---|
2498 | |
---|
2499 | if ((startIndex > 0 || count > 0) && |
---|
2500 | query.exec("SELECT FOUND_ROWS()") && query.next()) |
---|
2501 | totalAvailable = query.value(0).toUInt(); |
---|
2502 | else |
---|
2503 | totalAvailable = query.size(); |
---|
2504 | |
---|
2505 | return channelList; |
---|
2506 | } |
---|
2507 | |
---|
2508 | /* vim: set expandtab tabstop=4 shiftwidth=4: */ |
---|