MythTV master
tvbrowsehelper.cpp
Go to the documentation of this file.
1// Qt
2#include <QCoreApplication>
3
4// MythTV
7
8#include "cardutil.h"
9#include "channelutil.h"
10#include "playercontext.h"
11#include "recordinginfo.h"
12#include "remoteencoder.h"
13#include "tv_play.h"
14#include "tvbrowsehelper.h"
15
16#define LOC QString("BrowseHelper: ")
17
18static void format_time(int seconds, QString& tMin, QString& tHrsMin)
19{
20 int minutes = seconds / 60;
21 int hours = minutes / 60;
22 int min = minutes % 60;
23
24 tMin = TV::tr("%n minute(s)", "", minutes);
25 tHrsMin = QString("%1:%2").arg(hours).arg(min, 2, 10, QChar('0'));
26}
27
29 : MThread("TVBrowseHelper"),
30 m_parent(Parent)
31{
32}
33
35{
36 BrowseStop();
37 BrowseWait();
38}
39
40void TVBrowseHelper::BrowseInit(std::chrono::seconds BrowseMaxForward, bool BrowseAllTuners,
41 bool UseChannelGroups, const QString &DBChannelOrdering)
42{
43 m_dbBrowseMaxForward = BrowseMaxForward;
45 m_dbUseChannelGroups = UseChannelGroups;
46
47 m_dbAllChannels = ChannelUtil::GetChannels(0, true, "channum, callsign");
48 ChannelUtil::SortChannels(m_dbAllChannels, DBChannelOrdering, false);
49
50 for (const auto & chan : m_dbAllChannels)
51 {
52 m_dbChanidToChannum[chan.m_chanId] = chan.m_chanNum;
53 m_dbChanidToSourceid[chan.m_chanId] = chan.m_sourceId;
54 m_dbChannumToChanids.insert(chan.m_chanNum,chan.m_chanId);
55 }
56
57 start();
58}
59
61{
62 QMutexLocker locker(&m_browseLock);
63 m_browseList.clear();
64 m_browseRun = false;
65 m_browseWait.wakeAll();
66}
67
69{
71}
72
75bool TVBrowseHelper::BrowseStart(bool SkipBrowse)
76{
78 return false;
79
80 QMutexLocker locker(&m_browseLock);
81
83 return true;
84
88 context->LockPlayingInfo(__FILE__, __LINE__);
89 if (context->m_playingInfo)
90 {
94 context->UnlockPlayingInfo(__FILE__, __LINE__);
96
97 if (!SkipBrowse)
98 {
100 locker.unlock();
101 BrowseDispInfo(bi);
102 }
103 return true;
104 }
105 context->UnlockPlayingInfo(__FILE__, __LINE__);
107 return false;
108}
109
115void TVBrowseHelper::BrowseEnd(bool ChangeChannel)
116{
117 if (!gCoreContext->IsUIThread())
118 return;
119
120 QMutexLocker locker(&m_browseLock);
121
122 if (m_browseTimerId)
123 {
125 m_browseTimerId = 0;
126 }
127
128 m_browseList.clear();
129 m_browseWait.wakeAll();
130
131 OSD* osd = m_parent->GetOSDL();
132 if (osd)
135
136 if (ChangeChannel)
138}
139
141{
142 if (!gCoreContext->IsUIThread())
143 return;
144
145 if (!BrowseStart(true))
146 return;
147
150
151 QMutexLocker locker(&m_browseLock);
152 if (BROWSE_SAME == Browseinfo.m_dir)
153 m_browseList.clear();
154 m_browseList.push_back(Browseinfo);
155 m_browseWait.wakeAll();
156}
157
159{
160 BrowseInfo bi(Direction);
161 if (BROWSE_SAME != Direction)
162 BrowseDispInfo(bi);
163}
164
166{
167 if (!gCoreContext->IsUIThread())
168 return;
169
171 {
172 BrowseInfo bi(Channum, 0);
173 BrowseDispInfo(bi);
174 return;
175 }
176
179 if (!context->m_recorder || !context->m_lastCardid)
180 {
182 return;
183 }
184
185 uint inputid = static_cast<uint>(context->m_lastCardid);
187 uint sourceid = CardUtil::GetSourceID(inputid);
188 if (sourceid)
189 {
190 BrowseInfo bi(Channum, sourceid);
191 BrowseDispInfo(bi);
192 }
193}
194
196{
197 QMutexLocker locker(&m_browseLock);
202 return bi;
203}
204
212uint TVBrowseHelper::GetBrowseChanId(const QString& Channum, uint PrefCardid, uint PrefSourceid) const
213{
214 if (PrefSourceid)
215 {
216 auto samesourceid = [&Channum, &PrefSourceid](const ChannelInfo& Chan)
217 { return Chan.m_sourceId == PrefSourceid && Chan.m_chanNum == Channum; };
218 auto chan = std::find_if(m_dbAllChannels.cbegin(), m_dbAllChannels.cend(), samesourceid);
219 if (chan != m_dbAllChannels.cend())
220 return chan->m_chanId;
221 }
222
223 if (PrefCardid)
224 {
225 auto prefcardid = [&Channum, &PrefCardid](const ChannelInfo& Chan)
226 { return Chan.GetInputIds().contains(PrefCardid) && Chan.m_chanNum == Channum; };
227 auto chan = std::find_if(m_dbAllChannels.cbegin(), m_dbAllChannels.cend(), prefcardid);
228 if (chan != m_dbAllChannels.cend())
229 return chan->m_chanId;
230 }
231
233 {
234 auto channelmatch = [&Channum](const ChannelInfo& Chan) { return Chan.m_chanNum == Channum; };
235 auto chan = std::find_if(m_dbAllChannels.cbegin(), m_dbAllChannels.cend(), channelmatch);
236 if (chan != m_dbAllChannels.cend())
237 return chan->m_chanId;
238 }
239
240 return 0;
241}
242
249{
252 if (!context->m_recorder)
253 {
255 return;
256 }
257
258 QString title;
259 QString subtitle;
260 QString desc;
261 QString category;
262 QString endtime;
263 QString callsign;
264 QString iconpath;
265 QDateTime begts;
266 QDateTime endts;
267
268 QString starttime = Infomap["dbstarttime"];
269 QString chanid = Infomap["chanid"];
270 QString channum = Infomap["channum"];
271 QString seriesid = Infomap["seriesid"];
272 QString programid = Infomap["programid"];
273
274 context->m_recorder->GetNextProgram(Direction, title, subtitle, desc, category,
275 starttime, endtime, callsign, iconpath,
276 channum, chanid, seriesid, programid);
278
279 if (!starttime.isEmpty())
280 begts = MythDate::fromString(starttime);
281 else
282 begts = MythDate::fromString(Infomap["dbstarttime"]);
283
284 Infomap["starttime"] = MythDate::toString(begts, MythDate::kTime);
285 Infomap["startdate"] = MythDate::toString(begts, MythDate::kDateFull | MythDate::kSimplify);
286
287 Infomap["endtime"] = Infomap["enddate"] = "";
288 if (!endtime.isEmpty())
289 {
290 endts = MythDate::fromString(endtime);
291 Infomap["endtime"] = MythDate::toString(endts, MythDate::kTime);
292 Infomap["enddate"] = MythDate::toString(endts, MythDate::kDateFull | MythDate::kSimplify);
293 }
294
295 Infomap["lenmins"] = TV::tr("%n minute(s)", "", 0);
296 Infomap["lentime"] = "0:00";
297 if (begts.isValid() && endts.isValid())
298 {
299 QString lenM;
300 QString lenHM;
301 format_time(static_cast<int>(begts.secsTo(endts)), lenM, lenHM);
302 Infomap["lenmins"] = lenM;
303 Infomap["lentime"] = lenHM;
304 }
305
306 Infomap["dbstarttime"] = starttime;
307 Infomap["dbendtime"] = endtime;
308 Infomap["title"] = title;
309 Infomap["subtitle"] = subtitle;
310 Infomap["description"] = desc;
311 Infomap["category"] = category;
312 Infomap["callsign"] = callsign;
313 Infomap["channum"] = channum;
314 Infomap["chanid"] = chanid;
315 Infomap["iconpath"] = iconpath;
316 Infomap["seriesid"] = seriesid;
317 Infomap["programid"] = programid;
318}
319
321{
322 uint chanid = Infomap["chanid"].toUInt();
323 if (!chanid)
324 {
325 LOG(VB_GENERAL, LOG_ERR, LOC + "GetNextProgramDB() requires a chanid");
326 return;
327 }
328
329 int chandir = -1;
330 switch (direction)
331 {
332 case BROWSE_UP: chandir = CHANNEL_DIRECTION_UP; break;
333 case BROWSE_DOWN: chandir = CHANNEL_DIRECTION_DOWN; break;
334 case BROWSE_FAVORITE: chandir = CHANNEL_DIRECTION_FAVORITE; break;
335 case BROWSE_SAME:
336 case BROWSE_LEFT:
337 case BROWSE_RIGHT:
338 case BROWSE_INVALID: break;
339 }
340
341 if (chandir != -1)
342 {
344 chanid,
345 0 /* mplexid_restriction */,
346 0 /* chanid restriction */,
347 static_cast<ChannelChangeDirection>(chandir),
348 true /* skip non visible */,
349 true /* skip_same_channum_and_callsign */,
350 true /* skip_other_sources */);
351 }
352
353 Infomap["chanid"] = QString::number(chanid);
354 Infomap["channum"] = m_dbChanidToChannum[chanid];
355
356 QDateTime nowtime = MythDate::current();
357 static constexpr int64_t kSixHours {6LL * 60 * 60};
358 QDateTime latesttime = nowtime.addSecs(kSixHours);
359 QDateTime browsetime = MythDate::fromString(Infomap["dbstarttime"]);
360
361 MSqlBindings bindings;
362 bindings[":CHANID"] = chanid;
363 bindings[":NOWTS"] = nowtime;
364 bindings[":LATESTTS"] = latesttime;
365 bindings[":BROWSETS"] = browsetime;
366 bindings[":BROWSETS2"] = browsetime;
367
368 QString querystr = " WHERE program.chanid = :CHANID ";
369 switch (direction)
370 {
371 case BROWSE_LEFT:
372 querystr += " AND program.endtime <= :BROWSETS AND program.endtime > :NOWTS ";
373 break;
374 case BROWSE_RIGHT:
375 querystr += " AND program.starttime > :BROWSETS AND program.starttime < :LATESTTS ";
376 break;
377 default:
378 querystr += " AND program.starttime <= :BROWSETS AND program.endtime > :BROWSETS2 ";
379 };
380
381 ProgramList progList;
382 ProgramList dummySched;
383 LoadFromProgram(progList, querystr, bindings, dummySched);
384
385 if (progList.empty())
386 {
387 Infomap["dbstarttime"] = "";
388 return;
389 }
390
391 const ProgramInfo* prog = (direction == BROWSE_LEFT) ?
392 progList[static_cast<uint>(progList.size() - 1)] : progList[0];
393 Infomap["dbstarttime"] = prog->GetScheduledStartTime(MythDate::ISODate);
394}
395
397{
398 RunProlog();
399 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Helper thread starting");
400 QMutexLocker locker(&m_browseLock);
401 while (true)
402 {
403 while (m_browseList.empty() && m_browseRun)
405
406 if (!m_browseRun)
407 break;
408
409 BrowseInfo bi = m_browseList.front();
410 m_browseList.pop_front();
411
412 std::vector<uint> chanids;
413 if (BROWSE_SAME == bi.m_dir)
414 {
415 if (!bi.m_chanId)
416 {
417 std::vector<uint> chanids_extra;
419 QMultiMap<QString,uint>::iterator it;
420 it = m_dbChannumToChanids.lowerBound(bi.m_chanNum);
421 for ( ; (it != m_dbChannumToChanids.end()) &&
422 (it.key() == bi.m_chanNum); ++it)
423 {
424 if (m_dbChanidToSourceid[*it] == sourceid)
425 chanids.push_back(*it);
426 else
427 chanids_extra.push_back(*it);
428 }
429 chanids.insert(chanids.end(),
430 chanids_extra.begin(),
431 chanids_extra.end());
432 }
434 m_browseChanId = (chanids.empty()) ? bi.m_chanId : chanids[0];
436 }
437
438 BrowseDirection direction = bi.m_dir;
439
440 QDateTime lasttime = MythDate::fromString(m_browseStartTime);
441 QDateTime curtime = MythDate::current();
442 if (lasttime < curtime)
443 m_browseStartTime = curtime.toString(Qt::ISODate);
444
445 QDateTime maxtime = curtime.addSecs(m_dbBrowseMaxForward.count());
446 if ((lasttime > maxtime) && (direction == BROWSE_RIGHT))
447 continue;
448
449 m_browseLock.unlock();
450
451 // if browsing channel groups is enabled or
452 // direction if BROWSE_FAVORITES
453 // Then pick the next channel in the channel group list to browse
454 // If channel group is ALL CHANNELS (-1), then bypass picking from
455 // the channel group list
456 if ((m_dbUseChannelGroups || (direction == BROWSE_FAVORITE)) &&
457 (direction != BROWSE_RIGHT) && (direction != BROWSE_LEFT) &&
458 (direction != BROWSE_SAME))
459 {
461 if (m_parent->m_channelGroupId > -1)
462 {
464 if ((direction == BROWSE_UP) || (direction == BROWSE_FAVORITE))
466 else if (direction == BROWSE_DOWN)
468
470 direction = BROWSE_SAME;
471
473
474 m_browseLock.lock();
475 m_browseChanId = chanid;
476 m_browseChanNum.clear();
477 m_browseLock.unlock();
478 }
479 else
480 {
482 }
483 }
484
485 if (direction == BROWSE_FAVORITE)
486 direction = BROWSE_UP;
487
488 InfoMap infoMap;
489 infoMap["dbstarttime"] = m_browseStartTime;
490 infoMap["channum"] = m_browseChanNum;
491 infoMap["chanid"] = QString::number(m_browseChanId);
492
494
496 {
497 GetNextProgram(direction, infoMap);
498 }
499 else
500 {
501 if (!chanids.empty())
502 {
503 auto tunable = [](uint chanid) { return TV::IsTunable(chanid); };
504 auto it = std::find_if(chanids.cbegin(), chanids.cend(), tunable);
505 if (it != chanids.cend())
506 {
507 infoMap["chanid"] = QString::number(*it);
508 GetNextProgramDB(direction, infoMap);
509 }
510 }
511 else
512 {
513 uint orig_chanid = infoMap["chanid"].toUInt();
514 GetNextProgramDB(direction, infoMap);
515 while (!TV::IsTunable(infoMap["chanid"].toUInt()) &&
516 (infoMap["chanid"].toUInt() != orig_chanid))
517 {
518 GetNextProgramDB(direction, infoMap);
519 }
520 }
521 }
523
524 m_browseLock.lock();
525
526 m_browseChanNum = infoMap["channum"];
527 m_browseChanId = infoMap["chanid"].toUInt();
528
529 if (((direction == BROWSE_LEFT) || (direction == BROWSE_RIGHT)) &&
530 !infoMap["dbstarttime"].isEmpty())
531 {
532 m_browseStartTime = infoMap["dbstarttime"];
533 }
534
535 if (!m_browseList.empty())
536 {
537 // send partial info to UI for appearance of responsiveness
538 QCoreApplication::postEvent(m_parent, new UpdateBrowseInfoEvent(infoMap));
539 continue;
540 }
541 m_browseLock.unlock();
542
543 // pull in additional data from the DB...
546 else
547 infoMap["channelgroup"] = QObject::tr("All channels");
548
549 QDateTime startts = MythDate::fromString(m_browseStartTime);
550 RecordingInfo recinfo(m_browseChanId, startts, false);
551 recinfo.ToMap(infoMap);
552 infoMap["iconpath"] = ChannelUtil::GetIcon(recinfo.GetChanID());
553
554 m_browseLock.lock();
555 QCoreApplication::postEvent(m_parent, new UpdateBrowseInfoEvent(infoMap));
556 }
557 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Helper thread exiting");
558 RunEpilog();
559}
bool empty(void) const
size_t size(void) const
QString m_chanNum
QString m_startTime
BrowseDirection m_dir
static uint GetSourceID(uint inputid)
Definition: cardutil.cpp:1948
static QString GetChannelGroupName(int grpid)
static QString GetIcon(uint chanid)
static void SortChannels(ChannelInfoList &list, const QString &order, bool eliminate_duplicates=false)
static uint GetNextChannel(const ChannelInfoList &sorted, uint old_chanid, uint mplexid_restriction, uint chanid_restriction, ChannelChangeDirection direction, bool skip_non_visible=true, bool skip_same_channum_and_callsign=false, bool skip_other_sources=false)
static ChannelInfoList GetChannels(uint sourceid, bool visible_only, const QString &group_by=QString(), uint channel_groupid=0)
Definition: channelutil.h:251
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:49
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:196
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
Definition: osd.h:94
void HideWindow(const QString &Window) override
Definition: osd.cpp:675
void LockPlayingInfo(const char *file, int line) const
int m_lastCardid
CardID of current/last recorder.
RemoteEncoder * m_recorder
void UnlockPlayingInfo(const char *file, int line) const
ProgramInfo * m_playingInfo
Currently playing info.
Holds information on recordings and videos.
Definition: programinfo.h:70
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:375
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:393
QString GetChanNum(void) const
This is the channel "number", in the form 1, 1_2, 1-2, 1#1, etc.
Definition: programinfo.h:379
virtual void ToMap(InfoMap &progMap, bool showrerecord=false, uint star_range=10, uint date_format=0) const
Converts ProgramInfo into QString QHash containing each field in ProgramInfo converted into localized...
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:36
void GetNextProgram(int direction, QString &title, QString &subtitle, QString &desc, QString &category, QString &starttime, QString &endtime, QString &callsign, QString &iconpath, QString &channelname, QString &chanid, QString &seriesid, QString &programid)
Returns information about the program that would be seen if we changed the channel using ChangeChanne...
void GetNextProgramDB(BrowseDirection Direction, InfoMap &Infomap) const
bool m_dbUseChannelGroups
void BrowseEnd(bool ChangeChannel)
Ends channel browsing.
BrowseInfo GetBrowsedInfo() const
ChannelInfoList m_dbAllChannels
QHash< uint, QString > m_dbChanidToChannum
bool m_dbBrowseAllTuners
void BrowseDispInfo(const BrowseInfo &Browseinfo)
void GetNextProgram(BrowseDirection Direction, InfoMap &Infomap) const
Fetches information on the desired program from the backend.
TVBrowseHelper(TV *Parent)
QWaitCondition m_browseWait
QString m_browseStartTime
void BrowseChannel(const QString &Channum)
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
std::chrono::seconds m_dbBrowseMaxForward
~TVBrowseHelper() override
void BrowseInit(std::chrono::seconds BrowseMaxForward, bool BrowseAllTuners, bool UseChannelGroups, const QString &DBChannelOrdering)
QHash< uint, uint > m_dbChanidToSourceid
uint GetBrowseChanId(const QString &Channum, uint PrefCardid, uint PrefSourceid) const
Returns a chanid for the channum, or 0 if none is available.
QString m_browseChanNum
QList< BrowseInfo > m_browseList
bool BrowseStart(bool SkipBrowse=false)
Begins channel browsing.
QMultiMap< QString, uint > m_dbChannumToChanids
Control TV playback.
Definition: tv_play.h:155
OSD * GetOSDL()
Definition: tv_play.cpp:10471
int StartTimer(std::chrono::milliseconds Interval, int Line)
Definition: tv_play.cpp:2694
PlayerContext * GetPlayerContext()
Return a pointer to TV::m_playerContext.
Definition: tv_play.cpp:193
void GetPlayerReadLock() const
Definition: tv_play.cpp:10501
volatile int m_channelGroupId
Definition: tv_play.h:674
static bool IsTunable(uint ChanId)
Definition: tv_play.cpp:6766
void ClearOSD()
Definition: tv_play.cpp:6303
QMutex m_channelGroupLock
Lock necessary when modifying channel group variables.
Definition: tv_play.h:673
void ReturnPlayerLock() const
Definition: tv_play.cpp:10506
void ReturnOSDLock() const
Definition: tv_play.cpp:10489
void KillTimer(int Id)
Definition: tv_play.cpp:2702
static const std::chrono::milliseconds kBrowseTimeout
Definition: tv_play.h:756
void ChangeChannel(const ChannelInfoList &Options)
Definition: tv_play.cpp:6242
ChannelInfoList m_channelGroupChannelList
Definition: tv_play.h:675
unsigned int uint
Definition: compat.h:68
static HostCheckBoxSetting * BrowseAllTuners()
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QMap< QString, QVariant > MSqlBindings
typedef for a map of string -> string bindings for generic queries.
Definition: mythdbcon.h:100
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
@ kSimplify
Do Today/Yesterday/Tomorrow transform.
Definition: mythdate.h:26
@ kDateFull
Default local time.
Definition: mythdate.h:19
@ ISODate
Default UTC.
Definition: mythdate.h:17
@ kTime
Default local time.
Definition: mythdate.h:22
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:39
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
static constexpr const char * OSD_WIN_BROWSE
Definition: osd.h:34
bool LoadFromProgram(ProgramList &destination, const QString &where, const QString &groupBy, const QString &orderBy, const MSqlBindings &bindings, const ProgramList &schedList)
BrowseDirection
Used to request ProgramInfo for channel browsing.
Definition: tv.h:41
@ BROWSE_INVALID
Definition: tv.h:42
@ BROWSE_SAME
Fetch browse information on current channel and time.
Definition: tv.h:43
@ BROWSE_RIGHT
Fetch information on current channel in the future.
Definition: tv.h:47
@ BROWSE_LEFT
Fetch information on current channel in the past.
Definition: tv.h:46
@ BROWSE_UP
Fetch information on previous channel.
Definition: tv.h:44
@ BROWSE_FAVORITE
Fetch information on the next favorite channel.
Definition: tv.h:48
@ BROWSE_DOWN
Fetch information on next channel.
Definition: tv.h:45
ChannelChangeDirection
ChannelChangeDirection is an enumeration of possible channel changing directions.
Definition: tv.h:32
@ CHANNEL_DIRECTION_SAME
Definition: tv.h:36
@ CHANNEL_DIRECTION_DOWN
Definition: tv.h:34
@ CHANNEL_DIRECTION_FAVORITE
Definition: tv.h:35
@ CHANNEL_DIRECTION_UP
Definition: tv.h:33
#define LOC
static void format_time(int seconds, QString &tMin, QString &tHrsMin)