MythTV master
tvbrowsehelper.cpp
Go to the documentation of this file.
1// C++ headers
2#include <algorithm>
3
4// Qt
5#include <QCoreApplication>
6
7// MythTV
10
11#include "cardutil.h"
12#include "channelutil.h"
13#include "playercontext.h"
14#include "recordinginfo.h"
15#include "remoteencoder.h"
16#include "tv_play.h"
17#include "tvbrowsehelper.h"
18
19#define LOC QString("BrowseHelper: ")
20
21static void format_time(int seconds, QString& tMin, QString& tHrsMin)
22{
23 int minutes = seconds / 60;
24 int hours = minutes / 60;
25 int min = minutes % 60;
26
27 tMin = TV::tr("%n minute(s)", "", minutes);
28 tHrsMin = QString("%1:%2").arg(hours).arg(min, 2, 10, QChar('0'));
29}
30
32 : MThread("TVBrowseHelper"),
33 m_parent(Parent)
34{
35}
36
38{
39 BrowseStop();
40 BrowseWait();
41}
42
43void TVBrowseHelper::BrowseInit(std::chrono::seconds BrowseMaxForward, bool BrowseAllTuners,
44 bool UseChannelGroups, const QString &DBChannelOrdering)
45{
46 m_dbBrowseMaxForward = BrowseMaxForward;
48 m_dbUseChannelGroups = UseChannelGroups;
49
50 m_dbAllChannels = ChannelUtil::GetChannels(0, true, "channum, callsign");
51 ChannelUtil::SortChannels(m_dbAllChannels, DBChannelOrdering, false);
52
53 for (const auto & chan : m_dbAllChannels)
54 {
55 m_dbChanidToChannum[chan.m_chanId] = chan.m_chanNum;
56 m_dbChanidToSourceid[chan.m_chanId] = chan.m_sourceId;
57 m_dbChannumToChanids.insert(chan.m_chanNum,chan.m_chanId);
58 }
59
60 start();
61}
62
64{
65 QMutexLocker locker(&m_browseLock);
66 m_browseList.clear();
67 m_browseRun = false;
68 m_browseWait.wakeAll();
69}
70
72{
74}
75
78bool TVBrowseHelper::BrowseStart(bool SkipBrowse)
79{
81 return false;
82
83 QMutexLocker locker(&m_browseLock);
84
86 return true;
87
91 context->LockPlayingInfo(__FILE__, __LINE__);
92 if (context->m_playingInfo)
93 {
97 context->UnlockPlayingInfo(__FILE__, __LINE__);
99
100 if (!SkipBrowse)
101 {
103 locker.unlock();
104 BrowseDispInfo(bi);
105 }
106 return true;
107 }
108 context->UnlockPlayingInfo(__FILE__, __LINE__);
110 return false;
111}
112
118void TVBrowseHelper::BrowseEnd(bool ChangeChannel)
119{
120 if (!gCoreContext->IsUIThread())
121 return;
122
123 QMutexLocker locker(&m_browseLock);
124
125 if (m_browseTimerId)
126 {
128 m_browseTimerId = 0;
129 }
130
131 m_browseList.clear();
132 m_browseWait.wakeAll();
133
134 OSD* osd = m_parent->GetOSDL();
135 if (osd)
138
139 if (ChangeChannel)
141}
142
144{
145 if (!gCoreContext->IsUIThread())
146 return;
147
148 if (!BrowseStart(true))
149 return;
150
153
154 QMutexLocker locker(&m_browseLock);
155 if (BROWSE_SAME == Browseinfo.m_dir)
156 m_browseList.clear();
157 m_browseList.push_back(Browseinfo);
158 m_browseWait.wakeAll();
159}
160
162{
163 BrowseInfo bi(Direction);
164 if (BROWSE_SAME != Direction)
165 BrowseDispInfo(bi);
166}
167
169{
170 if (!gCoreContext->IsUIThread())
171 return;
172
174 {
175 BrowseInfo bi(Channum, 0);
176 BrowseDispInfo(bi);
177 return;
178 }
179
182 if (!context->m_recorder || !context->m_lastCardid)
183 {
185 return;
186 }
187
188 uint inputid = static_cast<uint>(context->m_lastCardid);
190 uint sourceid = CardUtil::GetSourceID(inputid);
191 if (sourceid)
192 {
193 BrowseInfo bi(Channum, sourceid);
194 BrowseDispInfo(bi);
195 }
196}
197
199{
200 QMutexLocker locker(&m_browseLock);
205 return bi;
206}
207
215uint TVBrowseHelper::GetBrowseChanId(const QString& Channum, uint PrefCardid, uint PrefSourceid) const
216{
217 if (PrefSourceid)
218 {
219 auto samesourceid = [&Channum, &PrefSourceid](const ChannelInfo& Chan)
220 { return Chan.m_sourceId == PrefSourceid && Chan.m_chanNum == Channum; };
221 auto chan = std::ranges::find_if(std::as_const(m_dbAllChannels), samesourceid);
222 if (chan != m_dbAllChannels.cend())
223 return chan->m_chanId;
224 }
225
226 if (PrefCardid)
227 {
228 auto prefcardid = [&Channum, &PrefCardid](const ChannelInfo& Chan)
229 { return Chan.GetInputIds().contains(PrefCardid) && Chan.m_chanNum == Channum; };
230 auto chan = std::ranges::find_if(std::as_const(m_dbAllChannels), prefcardid);
231 if (chan != m_dbAllChannels.cend())
232 return chan->m_chanId;
233 }
234
236 {
237 auto chan = std::ranges::find(std::as_const(m_dbAllChannels), Channum,
239 if (chan != m_dbAllChannels.cend())
240 return chan->m_chanId;
241 }
242
243 return 0;
244}
245
252{
255 if (!context->m_recorder)
256 {
258 return;
259 }
260
261 QString title;
262 QString subtitle;
263 QString desc;
264 QString category;
265 QString endtime;
266 QString callsign;
267 QString iconpath;
268 QDateTime begts;
269 QDateTime endts;
270
271 QString starttime = Infomap["dbstarttime"];
272 QString chanid = Infomap["chanid"];
273 QString channum = Infomap["channum"];
274 QString seriesid = Infomap["seriesid"];
275 QString programid = Infomap["programid"];
276
277 context->m_recorder->GetNextProgram(Direction, title, subtitle, desc, category,
278 starttime, endtime, callsign, iconpath,
279 channum, chanid, seriesid, programid);
281
282 if (!starttime.isEmpty())
283 begts = MythDate::fromString(starttime);
284 else
285 begts = MythDate::fromString(Infomap["dbstarttime"]);
286
287 Infomap["starttime"] = MythDate::toString(begts, MythDate::kTime);
288 Infomap["startdate"] = MythDate::toString(begts, MythDate::kDateFull | MythDate::kSimplify);
289
290 Infomap["endtime"] = Infomap["enddate"] = "";
291 if (!endtime.isEmpty())
292 {
293 endts = MythDate::fromString(endtime);
294 Infomap["endtime"] = MythDate::toString(endts, MythDate::kTime);
295 Infomap["enddate"] = MythDate::toString(endts, MythDate::kDateFull | MythDate::kSimplify);
296 }
297
298 Infomap["lenmins"] = TV::tr("%n minute(s)", "", 0);
299 Infomap["lentime"] = "0:00";
300 if (begts.isValid() && endts.isValid())
301 {
302 QString lenM;
303 QString lenHM;
304 format_time(static_cast<int>(begts.secsTo(endts)), lenM, lenHM);
305 Infomap["lenmins"] = lenM;
306 Infomap["lentime"] = lenHM;
307 }
308
309 Infomap["dbstarttime"] = starttime;
310 Infomap["dbendtime"] = endtime;
311 Infomap["title"] = title;
312 Infomap["subtitle"] = subtitle;
313 Infomap["description"] = desc;
314 Infomap["category"] = category;
315 Infomap["callsign"] = callsign;
316 Infomap["channum"] = channum;
317 Infomap["chanid"] = chanid;
318 Infomap["iconpath"] = iconpath;
319 Infomap["seriesid"] = seriesid;
320 Infomap["programid"] = programid;
321}
322
324{
325 uint chanid = Infomap["chanid"].toUInt();
326 if (!chanid)
327 {
328 LOG(VB_GENERAL, LOG_ERR, LOC + "GetNextProgramDB() requires a chanid");
329 return;
330 }
331
332 int chandir = -1;
333 switch (direction)
334 {
335 case BROWSE_UP: chandir = CHANNEL_DIRECTION_UP; break;
336 case BROWSE_DOWN: chandir = CHANNEL_DIRECTION_DOWN; break;
337 case BROWSE_FAVORITE: chandir = CHANNEL_DIRECTION_FAVORITE; break;
338 case BROWSE_SAME:
339 case BROWSE_LEFT:
340 case BROWSE_RIGHT:
341 case BROWSE_INVALID: break;
342 }
343
344 if (chandir != -1)
345 {
347 chanid,
348 0 /* mplexid_restriction */,
349 0 /* chanid restriction */,
350 static_cast<ChannelChangeDirection>(chandir),
351 true /* skip non visible */,
352 true /* skip_same_channum_and_callsign */,
353 true /* skip_other_sources */);
354 }
355
356 Infomap["chanid"] = QString::number(chanid);
357 Infomap["channum"] = m_dbChanidToChannum[chanid];
358
359 QDateTime nowtime = MythDate::current();
360 static constexpr int64_t kSixHours {6LL * 60 * 60};
361 QDateTime latesttime = nowtime.addSecs(kSixHours);
362 QDateTime browsetime = MythDate::fromString(Infomap["dbstarttime"]);
363
364 MSqlBindings bindings;
365 bindings[":CHANID"] = chanid;
366 bindings[":NOWTS"] = nowtime;
367 bindings[":LATESTTS"] = latesttime;
368 bindings[":BROWSETS"] = browsetime;
369 bindings[":BROWSETS2"] = browsetime;
370
371 QString querystr = " WHERE program.chanid = :CHANID ";
372 switch (direction)
373 {
374 case BROWSE_LEFT:
375 querystr += " AND program.endtime <= :BROWSETS AND program.endtime > :NOWTS ";
376 break;
377 case BROWSE_RIGHT:
378 querystr += " AND program.starttime > :BROWSETS AND program.starttime < :LATESTTS ";
379 break;
380 default:
381 querystr += " AND program.starttime <= :BROWSETS AND program.endtime > :BROWSETS2 ";
382 };
383
384 ProgramList progList;
385 ProgramList dummySched;
386 LoadFromProgram(progList, querystr, bindings, dummySched);
387
388 if (progList.empty())
389 {
390 Infomap["dbstarttime"] = "";
391 return;
392 }
393
394 const ProgramInfo* prog = (direction == BROWSE_LEFT) ?
395 progList[static_cast<uint>(progList.size() - 1)] : progList[0];
396 Infomap["dbstarttime"] = prog->GetScheduledStartTime(MythDate::ISODate);
397}
398
400{
401 RunProlog();
402 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Helper thread starting");
403 QMutexLocker locker(&m_browseLock);
404 while (true)
405 {
406 while (m_browseList.empty() && m_browseRun)
408
409 if (!m_browseRun)
410 break;
411
412 BrowseInfo bi = m_browseList.front();
413 m_browseList.pop_front();
414
415 std::vector<uint> chanids;
416 if (BROWSE_SAME == bi.m_dir)
417 {
418 if (!bi.m_chanId)
419 {
420 std::vector<uint> chanids_extra;
422 QMultiMap<QString,uint>::iterator it;
423 it = m_dbChannumToChanids.lowerBound(bi.m_chanNum);
424 for ( ; (it != m_dbChannumToChanids.end()) &&
425 (it.key() == bi.m_chanNum); ++it)
426 {
427 if (m_dbChanidToSourceid[*it] == sourceid)
428 chanids.push_back(*it);
429 else
430 chanids_extra.push_back(*it);
431 }
432 chanids.insert(chanids.end(),
433 chanids_extra.begin(),
434 chanids_extra.end());
435 }
437 m_browseChanId = (chanids.empty()) ? bi.m_chanId : chanids[0];
439 }
440
441 BrowseDirection direction = bi.m_dir;
442
443 QDateTime lasttime = MythDate::fromString(m_browseStartTime);
444 QDateTime curtime = MythDate::current();
445 if (lasttime < curtime)
446 m_browseStartTime = curtime.toString(Qt::ISODate);
447
448 QDateTime maxtime = curtime.addSecs(m_dbBrowseMaxForward.count());
449 if ((lasttime > maxtime) && (direction == BROWSE_RIGHT))
450 continue;
451
452 m_browseLock.unlock();
453
454 // if browsing channel groups is enabled or
455 // direction if BROWSE_FAVORITES
456 // Then pick the next channel in the channel group list to browse
457 // If channel group is ALL CHANNELS (-1), then bypass picking from
458 // the channel group list
459 if ((m_dbUseChannelGroups || (direction == BROWSE_FAVORITE)) &&
460 (direction != BROWSE_RIGHT) && (direction != BROWSE_LEFT) &&
461 (direction != BROWSE_SAME))
462 {
464 if (m_parent->m_channelGroupId > -1)
465 {
467 if ((direction == BROWSE_UP) || (direction == BROWSE_FAVORITE))
469 else if (direction == BROWSE_DOWN)
471
473 direction = BROWSE_SAME;
474
476
477 m_browseLock.lock();
478 m_browseChanId = chanid;
479 m_browseChanNum.clear();
480 m_browseLock.unlock();
481 }
482 else
483 {
485 }
486 }
487
488 if (direction == BROWSE_FAVORITE)
489 direction = BROWSE_UP;
490
491 InfoMap infoMap;
492 infoMap["dbstarttime"] = m_browseStartTime;
493 infoMap["channum"] = m_browseChanNum;
494 infoMap["chanid"] = QString::number(m_browseChanId);
495
497
499 {
500 GetNextProgram(direction, infoMap);
501 }
502 else
503 {
504 if (!chanids.empty())
505 {
506 auto tunable = [](uint chanid) { return TV::IsTunable(chanid); };
507 auto it = std::ranges::find_if(std::as_const(chanids), tunable);
508 if (it != chanids.cend())
509 {
510 infoMap["chanid"] = QString::number(*it);
511 GetNextProgramDB(direction, infoMap);
512 }
513 }
514 else
515 {
516 uint orig_chanid = infoMap["chanid"].toUInt();
517 GetNextProgramDB(direction, infoMap);
518 while (!TV::IsTunable(infoMap["chanid"].toUInt()) &&
519 (infoMap["chanid"].toUInt() != orig_chanid))
520 {
521 GetNextProgramDB(direction, infoMap);
522 }
523 }
524 }
526
527 m_browseLock.lock();
528
529 m_browseChanNum = infoMap["channum"];
530 m_browseChanId = infoMap["chanid"].toUInt();
531
532 if (((direction == BROWSE_LEFT) || (direction == BROWSE_RIGHT)) &&
533 !infoMap["dbstarttime"].isEmpty())
534 {
535 m_browseStartTime = infoMap["dbstarttime"];
536 }
537
538 if (!m_browseList.empty())
539 {
540 // send partial info to UI for appearance of responsiveness
541 QCoreApplication::postEvent(m_parent, new UpdateBrowseInfoEvent(infoMap));
542 continue;
543 }
544 m_browseLock.unlock();
545
546 // pull in additional data from the DB...
549 else
550 infoMap["channelgroup"] = QObject::tr("All channels");
551
552 QDateTime startts = MythDate::fromString(m_browseStartTime);
553 RecordingInfo recinfo(m_browseChanId, startts, false);
554 recinfo.ToMap(infoMap);
555 infoMap["iconpath"] = ChannelUtil::GetIcon(recinfo.GetChanID());
556
557 m_browseLock.lock();
558 QCoreApplication::postEvent(m_parent, new UpdateBrowseInfoEvent(infoMap));
559 }
560 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Helper thread exiting");
561 RunEpilog();
562}
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:1960
static QString GetChannelGroupName(int grpid)
QString m_chanNum
Definition: channelinfo.h:85
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:252
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:180
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:267
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:193
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:284
Definition: osd.h:94
void HideWindow(const QString &Window) override
Definition: osd.cpp:690
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:74
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:380
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:398
QString GetChanNum(void) const
This is the channel "number", in the form 1, 1_2, 1-2, 1#1, etc.
Definition: programinfo.h:384
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:157
OSD * GetOSDL()
Definition: tv_play.cpp:10515
int StartTimer(std::chrono::milliseconds Interval, int Line)
Definition: tv_play.cpp:2711
PlayerContext * GetPlayerContext()
Return a pointer to TV::m_playerContext.
Definition: tv_play.cpp:196
void GetPlayerReadLock() const
Definition: tv_play.cpp:10545
volatile int m_channelGroupId
Definition: tv_play.h:684
static bool IsTunable(uint ChanId)
Definition: tv_play.cpp:6772
void ClearOSD()
Definition: tv_play.cpp:6306
QMutex m_channelGroupLock
Lock necessary when modifying channel group variables.
Definition: tv_play.h:683
void ReturnPlayerLock() const
Definition: tv_play.cpp:10550
void ReturnOSDLock() const
Definition: tv_play.cpp:10533
void KillTimer(int Id)
Definition: tv_play.cpp:2719
static const std::chrono::milliseconds kBrowseTimeout
Definition: tv_play.h:766
void ChangeChannel(const ChannelInfoList &Options)
Definition: tv_play.cpp:6245
ChannelInfoList m_channelGroupChannelList
Definition: tv_play.h:685
unsigned int uint
Definition: compat.h:60
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
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)