MythTV  master
livetvchain.cpp
Go to the documentation of this file.
1 #include "livetvchain.h"
2 #include "mythcontext.h"
3 #include "mythdb.h"
4 #include "mythlogging.h"
5 #include "programinfo.h"
6 #include "mythsocket.h"
7 #include "cardutil.h"
8 
9 #define LOC QString("LiveTVChain(%1): ").arg(m_id)
10 
11 static inline void clear(LiveTVChainEntry &entry)
12 {
13  entry.chanid = 0;
14 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
15  entry.starttime.setTime_t(0);
16 #else
17  entry.starttime.setSecsSinceEpoch(0);
18 #endif
19  entry.endtime = QDateTime();
20  entry.discontinuity = true;
21  entry.hostprefix = QString();
22  entry.inputtype = QString();
23  entry.channum = QString();
24  entry.inputname = QString();
25 }
26 
31 {
33  LOG(VB_GENERAL, LOG_DEBUG, LOC + "ctor");
34 }
35 
37 {
38  LOG(VB_GENERAL, LOG_DEBUG, LOC + "dtor");
39 }
40 
41 QString LiveTVChain::InitializeNewChain(const QString &seed)
42 {
43  QDateTime curdt = MythDate::current();
44  m_id = QString("live-%1-%2").arg(seed).arg(curdt.toString(Qt::ISODate));
45  return m_id;
46 }
47 
48 void LiveTVChain::SetHostPrefix(const QString &prefix)
49 {
51 }
52 
53 void LiveTVChain::SetInputType(const QString &type)
54 {
55  m_inputType = type;
56 }
57 
58 void LiveTVChain::LoadFromExistingChain(const QString &id)
59 {
60  m_id = id;
61  ReloadAll();
62 }
63 
64 void LiveTVChain::AppendNewProgram(ProgramInfo *pginfo, const QString& channum,
65  const QString& inputname, bool discont)
66 {
67  QMutexLocker lock(&m_lock);
68 
69  LiveTVChainEntry newent;
70  newent.chanid = pginfo->GetChanID();
71  newent.starttime = pginfo->GetRecordingStartTime();
72  newent.endtime = pginfo->GetRecordingEndTime();
73  newent.discontinuity = discont;
74  newent.hostprefix = m_hostPrefix;
75  newent.inputtype = m_inputType;
76  newent.channum = channum;
77  newent.inputname = inputname;
78 
79  m_chain.append(newent);
80 
82  query.prepare("INSERT INTO tvchain (chanid, starttime, endtime, chainid,"
83  " chainpos, discontinuity, watching, hostprefix, cardtype, "
84  " channame, input) "
85  "VALUES(:CHANID, :START, :END, :CHAINID, :CHAINPOS, "
86  " :DISCONT, :WATCHING, :PREFIX, :INPUTTYPE, :CHANNAME, "
87  " :INPUT );");
88  query.bindValue(":CHANID", pginfo->GetChanID());
89  query.bindValue(":START", pginfo->GetRecordingStartTime());
90  query.bindValue(":END", pginfo->GetRecordingEndTime());
91  query.bindValueNoNull(":CHAINID", m_id);
92  query.bindValue(":CHAINPOS", m_maxPos);
93  query.bindValue(":DISCONT", discont);
94  query.bindValue(":WATCHING", 0);
95  query.bindValue(":PREFIX", m_hostPrefix);
96  query.bindValue(":INPUTTYPE", m_inputType);
97  query.bindValue(":CHANNAME", channum);
98  query.bindValue(":INPUT", inputname);
99 
100  if (!query.exec() || !query.isActive())
101  MythDB::DBError("Chain: AppendNewProgram", query);
102  else
103  {
104  LOG(VB_RECORD, LOG_INFO, QString("Chain: Appended@%3 '%1_%2'")
105  .arg(newent.chanid)
107  .arg(m_maxPos));
108  }
109 
110  m_maxPos++;
111  BroadcastUpdate();
112 }
113 
115 {
116  QMutexLocker lock(&m_lock);
117 
118  MSqlQuery query(MSqlQuery::InitCon());
119  query.prepare("UPDATE tvchain SET endtime = :END "
120  "WHERE chanid = :CHANID AND starttime = :START ;");
121  query.bindValue(":END", pginfo->GetRecordingEndTime());
122  query.bindValue(":CHANID", pginfo->GetChanID());
123  query.bindValue(":START", pginfo->GetRecordingStartTime());
124 
125  if (!query.exec() || !query.isActive())
126  MythDB::DBError("Chain: FinishedRecording", query);
127  else
128  {
129  LOG(VB_RECORD, LOG_INFO,
130  QString("Chain: Updated endtime for '%1_%2' to %3")
131  .arg(pginfo->GetChanID())
134  }
135 
136  QList<LiveTVChainEntry>::iterator it;
137  for (it = m_chain.begin(); it != m_chain.end(); ++it)
138  {
139  if ((*it).chanid == pginfo->GetChanID() &&
140  (*it).starttime == pginfo->GetRecordingStartTime())
141  {
142  (*it).endtime = pginfo->GetRecordingEndTime();
143  }
144  }
145  BroadcastUpdate();
146 }
147 
149 {
150  QMutexLocker lock(&m_lock);
151 
152  for (auto it = m_chain.begin(); it != m_chain.end(); ++it)
153  {
154  if ((*it).chanid == pginfo->GetChanID() &&
155  (*it).starttime == pginfo->GetRecordingStartTime())
156  {
157  auto del = it;
158  ++it;
159 
160  MSqlQuery query(MSqlQuery::InitCon());
161  if (it != m_chain.end())
162  {
163  (*it).discontinuity = true;
164  query.prepare("UPDATE tvchain SET discontinuity = :DISCONT "
165  "WHERE chanid = :CHANID AND starttime = :START "
166  "AND chainid = :CHAINID ;");
167  query.bindValue(":CHANID", (*it).chanid);
168  query.bindValue(":START", (*it).starttime);
169  query.bindValue(":CHAINID", m_id);
170  query.bindValue(":DISCONT", true);
171  if (!query.exec())
172  MythDB::DBError("LiveTVChain::DeleteProgram -- "
173  "discontinuity", query);
174  }
175 
176  query.prepare("DELETE FROM tvchain WHERE chanid = :CHANID "
177  "AND starttime = :START AND chainid = :CHAINID ;");
178  query.bindValue(":CHANID", (*del).chanid);
179  query.bindValue(":START", (*del).starttime);
180  query.bindValue(":CHAINID", m_id);
181  if (!query.exec())
182  MythDB::DBError("LiveTVChain::DeleteProgram -- delete", query);
183 
184  m_chain.erase(del);
185 
186  BroadcastUpdate();
187  break;
188  }
189  }
190 }
191 
193 {
194  QString message = QString("LIVETV_CHAIN UPDATE %1").arg(m_id);
195  MythEvent me(message, entriesToStringList());
196  gCoreContext->dispatch(me);
197 }
198 
200 {
201  QMutexLocker lock(&m_lock);
202 
203  m_chain.clear();
204 
205  MSqlQuery query(MSqlQuery::InitCon());
206  query.prepare("DELETE FROM tvchain WHERE chainid = :CHAINID ;");
207  query.bindValue(":CHAINID", m_id);
208 
209  if (!query.exec())
210  MythDB::DBError("LiveTVChain::DestroyChain", query);
211 }
212 
213 void LiveTVChain::ReloadAll(const QStringList &data)
214 {
215  QMutexLocker lock(&m_lock);
216 
217  int prev_size = m_chain.size();
218  if (data.isEmpty() || !entriesFromStringList(data))
219  {
220  m_chain.clear();
221 
222  MSqlQuery query(MSqlQuery::InitCon());
223  query.prepare("SELECT chanid, starttime, endtime, discontinuity, "
224  "chainpos, hostprefix, cardtype, channame, input "
225  "FROM tvchain "
226  "WHERE chainid = :CHAINID ORDER BY chainpos;");
227  query.bindValue(":CHAINID", m_id);
228 
229  if (query.exec() && query.isActive() && query.size() > 0)
230  {
231  while (query.next())
232  {
233  LiveTVChainEntry entry;
234  entry.chanid = query.value(0).toUInt();
235  entry.starttime =
236  MythDate::as_utc(query.value(1).toDateTime());
237  entry.endtime =
238  MythDate::as_utc(query.value(2).toDateTime());
239  entry.discontinuity = query.value(3).toBool();
240  entry.hostprefix = query.value(5).toString();
241  entry.inputtype = query.value(6).toString();
242  entry.channum = query.value(7).toString();
243  entry.inputname = query.value(8).toString();
244 
245  m_maxPos = query.value(4).toInt() + 1;
246 
247  m_chain.append(entry);
248  }
249  }
250  }
251 
253  if (m_curPos < 0)
254  m_curPos = 0;
255 
256  if (m_switchId >= 0)
258 
259  if (prev_size > m_chain.size())
260  {
261  LOG(VB_PLAYBACK, LOG_INFO, LOC +
262  QString("ReloadAll(): Removed %1 recording(s)")
263  .arg(prev_size - m_chain.size()));
264  LOG(VB_PLAYBACK, LOG_INFO, LOC + toString());
265  }
266  else if (prev_size < m_chain.size())
267  {
268  LOG(VB_PLAYBACK, LOG_INFO, LOC +
269  QString("ReloadAll(): Added %1 recording(s)")
270  .arg(m_chain.size() - prev_size));
271  LOG(VB_PLAYBACK, LOG_INFO, LOC + toString());
272  }
273 }
274 
275 void LiveTVChain::GetEntryAt(int at, LiveTVChainEntry &entry) const
276 {
277  QMutexLocker lock(&m_lock);
278 
279  int size = m_chain.count();
280  int new_at = (size && (at < 0 || at >= size)) ? size - 1 : at;
281 
282  if (size && new_at >= 0 && new_at < size)
283  entry = m_chain[new_at];
284  else
285  {
286  LOG(VB_GENERAL, LOG_ERR, QString("GetEntryAt(%1) failed.").arg(at));
287  if (at == -1)
288  {
289  LOG(VB_GENERAL, LOG_ERR, "It appears that your backend may "
290  "be misconfigured. Check your backend logs to determine "
291  "whether your inputs, lineups, channels, or storage "
292  "configuration are reporting errors. This issue is commonly "
293  "caused by failing to complete all setup steps properly. You "
294  "may wish to review the documentation for mythtv-setup.");
295  }
296  clear(entry);
297  }
298 }
299 
301 {
302  auto *pginfo = new ProgramInfo(entry.chanid, entry.starttime);
303 
304  if (pginfo->GetChanID())
305  {
306  pginfo->SetPathname(entry.hostprefix + pginfo->GetBasename());
307  return pginfo;
308  }
309 
310  LOG(VB_GENERAL, LOG_ERR,
311  QString("EntryToProgram(%1@%2) failed to get pginfo")
312  .arg(entry.chanid).arg(entry.starttime.toString(Qt::ISODate)));
313  delete pginfo;
314  return nullptr;
315 }
316 
325 {
326  LiveTVChainEntry entry;
327  GetEntryAt(at, entry);
328 
329  return EntryToProgram(entry);
330 }
331 
335 int LiveTVChain::ProgramIsAt(uint chanid, const QDateTime &starttime) const
336 {
337  QMutexLocker lock(&m_lock);
338 
339  int count = 0;
340  QList<LiveTVChainEntry>::const_iterator it;
341  for (it = m_chain.begin(); it != m_chain.end(); ++it, ++count)
342  {
343  if ((*it).chanid == chanid &&
344  (*it).starttime == starttime)
345  {
346  return count;
347  }
348  }
349 
350  return -1;
351 }
352 
356 int LiveTVChain::ProgramIsAt(const ProgramInfo &pginfo) const
357 {
358  return ProgramIsAt(pginfo.GetChanID(), pginfo.GetRecordingStartTime());
359 }
360 
365 {
366  return GetLengthAtPos(m_curPos);
367 }
368 
373 {
374  QMutexLocker lock(&m_lock);
375 
376  LiveTVChainEntry entry = m_chain[pos];
377  if (pos == (m_chain.count() - 1))
378  {
379  // We're on live program, it hasn't ended. Use current time as end time
380  return entry.starttime.secsTo(MythDate::current());
381  }
382 
383  // use begin time from the following program, as it's certain to be right
384  // the end time is set as per the EPG, but should playback be interrupted
385  // such as a channel change, the end value wouldn't have reflected the actual
386  // duration of the program
387  LiveTVChainEntry nextentry = m_chain[pos+1];
388  return entry.starttime.secsTo(nextentry.starttime);
389 }
390 
391 int LiveTVChain::TotalSize(void) const
392 {
393  return m_chain.count();
394 }
395 
397 {
398  QMutexLocker lock(&m_lock);
399 
400  m_curChanId = pginfo.GetChanID();
402 
403  m_curPos = ProgramIsAt(pginfo);
404  if (m_curPos < 0)
405  m_curPos = 0;
406  m_switchId = -1;
407 }
408 
409 bool LiveTVChain::HasNext(void) const
410 {
411  return (m_chain.count() - 1 > m_curPos);
412 }
413 
415 {
416  QMutexLocker lock(&m_lock);
417 
418  m_switchId = -1;
419  m_jumpPos = INT_MAX;
420 }
421 
432 ProgramInfo *LiveTVChain::GetSwitchProgram(bool &discont, bool &newtype,
433  int &newid)
434 {
435  ReloadAll();
436  QMutexLocker lock(&m_lock);
437 
438  int id = m_switchId;
440  discont, newtype);
441  if (pginfo)
442  {
443  newid = id;
444  }
445  ClearSwitch();
446 
447  return pginfo;
448 }
449 
450 ProgramInfo *LiveTVChain::DoGetNextProgram(bool up, int curpos, int &newid,
451  bool &discont, bool &newtype)
452 {
453  LiveTVChainEntry oldentry;
454  LiveTVChainEntry entry;
455  ProgramInfo *pginfo = nullptr;
456 
457  GetEntryAt(curpos, oldentry);
458 
459  if (newid < 0 || curpos == newid)
460  {
461  // already on the program
462  entry = oldentry;
463  pginfo = EntryToProgram(entry);
464  newid = curpos;
465  }
466  else
467  {
468  // try to find recordings during first pass
469  // we'll skip dummy and empty recordings
470  while (!pginfo && newid < m_chain.count() && newid >= 0)
471  {
472  GetEntryAt(newid, entry);
473 
474  bool at_last_entry =
475  ((newid > curpos) &&
476  (newid == m_chain.count()-1)) ||
477  ((newid <= curpos) && (newid == 0));
478 
479  // Skip dummy recordings, if possible.
480  if (at_last_entry || (entry.inputtype != "DUMMY"))
481  pginfo = EntryToProgram(entry);
482 
483  // Skip empty recordings, if possible
484  if (pginfo && (0 == pginfo->GetFilesize()) &&
485  newid < m_chain.count()-1)
486  {
487  LOG(VB_GENERAL, LOG_WARNING,
488  QString("Skipping empty program %1")
489  .arg(pginfo->MakeUniqueKey()));
490  delete pginfo;
491  pginfo = nullptr;
492  }
493 
494  if (!pginfo)
495  {
496  newid += up ? 1 : -1;
497  }
498  }
499 
500  if (!pginfo)
501  {
502  // didn't find in first pass, now get back to the next good one
503  // as this is the one we will use
504  do
505  {
506  newid += up ? -1 : 1;
507 
508  GetEntryAt(newid, entry);
509 
510  bool at_last_entry =
511  ((newid > curpos) &&
512  (newid == m_chain.count()-1)) ||
513  ((newid <= curpos) && (newid == 0));
514 
515  // Skip dummy recordings, if possible.
516  if (at_last_entry || (entry.inputtype != "DUMMY"))
517  pginfo = EntryToProgram(entry);
518 
519  // Skip empty recordings, if possible
520  if (pginfo && (0 == pginfo->GetFilesize()) &&
521  newid < m_chain.count()-1)
522  {
523  LOG(VB_GENERAL, LOG_WARNING,
524  QString("Skipping empty program %1")
525  .arg(pginfo->MakeUniqueKey()));
526  delete pginfo;
527  pginfo = nullptr;
528  }
529  }
530  while (!pginfo && newid < m_chain.count() && newid >= 0);
531 
532  if (!pginfo)
533  {
534  // still not found so abort (will never happen once playback has started)
535  return nullptr;
536  }
537  }
538  }
539 
540  discont = true;
541  if (curpos == newid - 1)
542  discont = entry.discontinuity;
543 
544  newtype = (oldentry.inputtype != entry.inputtype);
545 
546  // Some inputs can change their streams dramatically on a channel change...
547  if (discont)
549 
550  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
551  QString("DoGetNextProgram: %1 -> ").arg(newid) + pginfo->toString());
552 
553  return pginfo;
554 }
555 
561 {
562  QMutexLocker lock(&m_lock);
563 
564  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SwitchTo(%1)").arg(num));
565 
566  int size = m_chain.count();
567  if ((num < 0) || (num >= size))
568  num = size - 1;
569 
570  if (m_curPos != num)
571  {
572  m_switchId = num;
574  }
575  else
576  LOG(VB_GENERAL, LOG_ERR, LOC + "SwitchTo() not switching to current");
577 
578  if (VERBOSE_LEVEL_CHECK(VB_PLAYBACK, LOG_DEBUG))
579  {
581  GetEntryAt(num, e);
582  QString msg = QString("%1_%2")
583  .arg(e.chanid)
585  LOG(VB_PLAYBACK, LOG_DEBUG,
586  LOC + QString("Entry@%1: '%2')").arg(num).arg(msg));
587  }
588 }
589 
596 {
597 #if 0
598  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "SwitchToNext("<<(up?"up":"down")<<")");
599 #endif
600  if (up && HasNext())
601  SwitchTo(m_curPos + 1);
602  else if (!up && HasPrev())
603  SwitchTo(m_curPos - 1);
604 }
605 
606 void LiveTVChain::JumpTo(int num, int pos)
607 {
608  m_jumpPos = pos;
609  SwitchTo(num);
610 }
611 
621 void LiveTVChain::JumpToNext(bool up, int pos)
622 {
623  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("JumpToNext: %1 -> %2").arg(up).arg(pos));
624  if (pos >= 0)
625  {
626  m_jumpPos = pos;
627  SwitchToNext(up);
628  }
629  else
630  {
631  QMutexLocker lock(&m_lock);
632 
633  int current = m_curPos;
634  int switchto = m_curPos;
635  bool discont = false;
636  bool newtype = false;
637 
638  while (current >= 0 && current < m_chain.size())
639  {
640  switchto = current + (up ? 1 : -1);
641 
642  ProgramInfo *pginfo = DoGetNextProgram(up, current, switchto,
643  discont, newtype);
644  delete pginfo;
645 
646  if (switchto == current)
647  {
648  // we've reached the end
649  pos = up ? GetLengthAtPos(switchto) : 0;
650  break;
651  }
652 
653  int duration = GetLengthAtPos(switchto);
654 
655  pos += duration;
656 
657  if (pos >= 0)
658  {
659  if (up)
660  {
661  pos = - (pos - duration);
662  }
663  break;
664  }
665 
666  current = switchto;
667  }
668  m_switchId = switchto;
669  m_jumpPos = pos;
671  }
672 }
673 
678 {
679  int ret = m_jumpPos;
680  m_jumpPos = 0;
681  return ret;
682 }
683 
684 QString LiveTVChain::GetChannelName(int pos) const
685 {
686  LiveTVChainEntry entry;
687  GetEntryAt(pos, entry);
688 
689  return entry.channum;
690 }
691 
692 QString LiveTVChain::GetInputName(int pos) const
693 {
694  LiveTVChainEntry entry;
695  GetEntryAt(pos, entry);
696 
697  return entry.inputname;
698 }
699 
700 QString LiveTVChain::GetInputType(int pos) const
701 {
702  LiveTVChainEntry entry;
703  GetEntryAt(pos, entry);
704 
705  return entry.inputtype;
706 }
707 
709 {
710  QMutexLocker lock(&m_sockLock);
711 
712  if (!m_inUseSocks.contains(sock))
713  m_inUseSocks.append(sock);
714 }
715 
717 {
718  QMutexLocker lock(&m_sockLock);
719  return m_inUseSocks.contains(sock);
720 }
721 
723 {
724  QMutexLocker lock(&m_sockLock);
725  return m_inUseSocks.count();
726 }
727 
729 {
730  QMutexLocker lock(&m_sockLock);
731  m_inUseSocks.removeAll(sock);
732 }
733 
734 static QString toString(const LiveTVChainEntry &v)
735 {
736  return QString("%1: %2 (%3 to %4)%5")
737  .arg(v.inputtype,6).arg(v.chanid,4)
738  .arg(v.starttime.time().toString())
739  .arg(v.endtime.time().toString())
740  .arg(v.discontinuity?" discontinuous":"");
741 }
742 
743 QString LiveTVChain::toString() const
744 {
745  QMutexLocker lock(&m_lock);
746  QString ret = QString("LiveTVChain has %1 entries\n").arg(m_chain.size());
747  for (uint i = 0; i < (uint)m_chain.size(); i++)
748  {
749  ret += (QString((i==(uint)m_curPos) ? "* " : " ") +
750  ::toString(m_chain[i]) + "\n");
751  }
752  return ret;
753 }
754 
756 {
757  QMutexLocker lock(&m_lock);
758  QStringList ret;
759  ret << QString::number(m_maxPos);
760  foreach (const auto & entry, m_chain)
761  {
762  ret << QString::number(entry.chanid);
763  ret << entry.starttime.toString(Qt::ISODate);
764  ret << entry.endtime.toString(Qt::ISODate);
765  ret << QString::number(entry.discontinuity);
766  ret << entry.hostprefix;
767  ret << entry.inputtype;
768  ret << entry.channum;
769  ret << entry.inputname;
770  }
771  return ret;
772 }
773 
774 bool LiveTVChain::entriesFromStringList(const QStringList &items)
775 {
776  int numItems = items.size();
777  QList<LiveTVChainEntry> chain;
778  int itemIdx = 0;
779  int maxpos = 0;
780  bool ok = false;
781  if (itemIdx < numItems)
782  maxpos = items[itemIdx++].toInt(&ok);
783  while (ok && itemIdx < numItems)
784  {
785  LiveTVChainEntry entry;
786  if (ok && itemIdx < numItems)
787  entry.chanid = items[itemIdx++].toUInt(&ok);
788  if (ok && itemIdx < numItems)
789  {
790  entry.starttime =
791  QDateTime::fromString(items[itemIdx++], Qt::ISODate);
792  ok = entry.starttime.isValid();
793  }
794  if (ok && itemIdx < numItems)
795  {
796  entry.endtime =
797  QDateTime::fromString(items[itemIdx++], Qt::ISODate);
798  ok = entry.endtime.isValid();
799  }
800  if (ok && itemIdx < numItems)
801  entry.discontinuity = (items[itemIdx++].toInt(&ok) != 0);
802  if (ok && itemIdx < numItems)
803  entry.hostprefix = items[itemIdx++];
804  if (ok && itemIdx < numItems)
805  entry.inputtype = items[itemIdx++];
806  if (ok && itemIdx < numItems)
807  entry.channum = items[itemIdx++];
808  if (ok && itemIdx < numItems)
809  entry.inputname = items[itemIdx++];
810  if (ok)
811  chain.append(entry);
812  }
813  if (ok)
814  {
815  QMutexLocker lock(&m_lock);
816  m_maxPos = maxpos;
817  m_chain = chain;
818  }
819  else
820  {
821  LOG(VB_PLAYBACK, LOG_INFO,
822  QString("Failed to deserialize TVChain - ") + items.join("|"));
823  }
824  return ok;
825 }
int GetJumpPos(void)
Returns the jump position in seconds and clears it.
#define LOC
Definition: livetvchain.cpp:9
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
QString toString() const
void DelHostSocket(MythSocket *sock)
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:864
int TotalSize(void) const
virtual uint64_t GetFilesize(void) const
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
QDateTime endtime
Definition: livetvchain.h:23
void DestroyChain(void)
General purpose reference counter.
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
void BroadcastUpdate()
ProgramInfo * GetProgramAt(int at) const
Returns program at the desired location.
void SetHostSocket(MythSocket *sock)
void SetInputType(const QString &type)
Definition: livetvchain.cpp:53
QString m_id
Definition: livetvchain.h:106
int size(void) const
Definition: mythdbcon.h:203
QString toString(Verbosity v=kLongDescription, const QString &sep=":", const QString &grp="\"") const
void JumpToNext(bool up, int pos)
JumpToNext(bool up, int pos) jump to the next (up == true) or previous (up == false) liveTV program I...
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
bool HasNext(void) const
void SwitchTo(int num)
Sets the recording to switch to.
static ProgramInfo * EntryToProgram(const LiveTVChainEntry &entry)
int GetLengthAtCurPos(void)
QDateTime as_utc(const QDateTime &old_dt)
Returns copy of QDateTime with TimeSpec set to UTC.
Definition: mythdate.cpp:23
static bool IsChannelChangeDiscontinuous(const QString &rawtype)
Definition: cardutil.h:138
void LoadFromExistingChain(const QString &id)
Definition: livetvchain.cpp:58
QDateTime starttime
Definition: livetvchain.h:22
QString inputtype
Definition: livetvchain.h:26
QVariant value(int i) const
Definition: mythdbcon.h:198
QString MakeUniqueKey(void) const
Creates a unique string that can be used to identify an existing recording.
Definition: programinfo.h:333
QString m_inputType
Definition: livetvchain.h:112
static QString toString(const LiveTVChainEntry &v)
Holds information on recordings and videos.
Definition: programinfo.h:67
uint m_curChanId
Definition: livetvchain.h:115
void ReloadAll(const QStringList &data=QStringList())
This class is used as a container for messages.
Definition: mythevent.h:16
QString GetChannelName(int pos=-1) const
QMutex m_sockLock
Definition: livetvchain.h:123
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
Definition: mythlogging.h:24
QList< LiveTVChainEntry > m_chain
Definition: livetvchain.h:107
QDateTime m_curStartTs
Definition: livetvchain.h:116
QString InitializeNewChain(const QString &seed)
Definition: livetvchain.cpp:41
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
uint HostSocketCount(void) const
QString GetInputType(int pos=-1) const
bool isActive(void) const
Definition: mythdbcon.h:204
QString hostprefix
Definition: livetvchain.h:25
void AppendNewProgram(ProgramInfo *pginfo, const QString &channum, const QString &inputname, bool discont)
Definition: livetvchain.cpp:64
void GetEntryAt(int at, LiveTVChainEntry &entry) const
QList< MythSocket * > m_inUseSocks
Definition: livetvchain.h:124
unsigned int uint
Definition: compat.h:140
LiveTVChainEntry m_switchEntry
Definition: livetvchain.h:119
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
void SetProgram(const ProgramInfo &pginfo)
static void clear(LiveTVChainEntry &entry)
Definition: livetvchain.cpp:11
ProgramInfo * DoGetNextProgram(bool up, int curpos, int &newid, bool &discont, bool &newtype)
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:101
bool HasPrev(void) const
Definition: livetvchain.h:61
bool IsHostSocket(MythSocket *sock)
void dispatch(const MythEvent &event)
void SetHostPrefix(const QString &prefix)
Definition: livetvchain.cpp:48
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808
void DeleteProgram(ProgramInfo *pginfo)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void ClearSwitch(void)
QString GetInputName(int pos=-1) const
QMutex m_lock
Definition: livetvchain.h:109
void FinishedRecording(ProgramInfo *pginfo)
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
Definition: mythdbcon.cpp:869
ProgramInfo * GetSwitchProgram(bool &discont, bool &newtype, int &newid)
Returns the recording we should switch to.
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:366
QString m_hostPrefix
Definition: livetvchain.h:111
bool entriesFromStringList(const QStringList &items)
int ProgramIsAt(uint chanid, const QDateTime &starttime) const
~LiveTVChain() override
Definition: livetvchain.cpp:36
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:398
QDateTime GetRecordingEndTime(void) const
Approximate time the recording should have ended, did end, or is intended to end.
Definition: programinfo.h:406
QStringList entriesToStringList() const
Class for communcating between myth backends and frontends.
Definition: mythsocket.h:26
Default UTC.
Definition: mythdate.h:14
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
Default UTC, "yyyyMMddhhmmss".
Definition: mythdate.h:15
void SwitchToNext(bool up)
Sets the recording to switch to.
int GetLengthAtPos(int pos)
void JumpTo(int num, int pos)
QString inputname
Definition: livetvchain.h:28