Ticket #12809: 0001-Use-lastPlayPos-instead-of-bookmark.patch
| File 0001-Use-lastPlayPos-instead-of-bookmark.patch, 81.4 KB (added by , 9 years ago) |
|---|
-
mythtv/libs/libmyth/programinfo.cpp
From 5bafa67ba68e6c37f7e4074262176ada205ae41d Mon Sep 17 00:00:00 2001 From: Roger Siddons <rsiddons@mythtv.org> Date: Fri, 6 May 2016 16:12:19 +0100 Subject: [PATCH 1/3] =?UTF-8?q?Use=20lastPlayPos=20instead=20of=20bookmark?= =?UTF-8?q?=E2=80=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable 'Play from last play position' 38443b8e disabled playing recordings from last play position mark. This patch re-enables it for recordings. Add MConcurrent Provides a simple version of QtConcurrent::run() that uses MThreadPool rather than QThreadPool. Useful for starting background threads in 1 line. Given a class method of: void Class::fn(arg1, arg2...) you can run it in a different thread using: MConcurrent::run("thread name", &Class instance, &Class::fn, arg1, arg2...) Refer to QtConcurrent::run for further details Restrictions: 1. Accepts 0-5 arguments 2. Only class methods are supported (most typical in Myth) 3. Only non-const classes & methods are supported (most typical in Myth) 4. The method must have return type of void (QFuture is not easily ported to MThreadPool. Use signals/events instead) Add UI progress indicator to Watch Recordings Adds theme widget 'progresspercent' to watchrecordings window to show percentage of recording that has been watched. MythUI: Implement progressbar on buttonlist items Allows buttonlists to contain a progressbar. Show play position as a progress bar in Watch recordings Allows recording list to show part-watched recordings using a progressbar. Demo uses Mythcenter-wide theme Move automatic bookmark updates to last play position. Convert bookmarks to a user aid only. They will never be automatically updated/removed by playback. Last play position mark is now used instead. Settings "Clear bookmark on playback" & "Action on playback exit"/"Save position and exit" are removed (from UI only) as they are now redundant. Last play position is never updated within 30 secs of playback start. Thereafter it records position on playback exit (as well as the existing periodic 30 sec update). At end of recording it is reset. Note: 'Automatically Mark watched' is now also actioned by end-of-playback dialog (it wasn't previously) It also will not trigger for first 30 secs of playback. Default recording playback from last play position Playback of recordings now starts from: - last play position, if present - bookmark, if present (so user can override progstart) - program start mark, if present - beginning of file Menu options allow user to start from last play position, bookmark or 'beginning' (prog start/file start) where applicable. Add menu options to Clear bookmark and Clear last play position for recordings Allow user to easily reset last play position and bookmark. Start video playback as per recordings Video playback starts from: - last played position, if present - bookmark, if present - file start Menu options allow user to explicitly select option and reset last played position & bookmark Plays from last played position, then bookmark, then file start Add menu options to Play from bookmark & Play from beginning Add menu options to Clear Bookmark & Clear last played position Preview generator uses last played position Previews are now generated from: - last played position, if present - bookmark, if present, - prog start mark, if present - existing offset, which us unchanged (it does not use the progstart mark.) Add progress bar to Upcoming Recordings. Progress of an active recording is shown by a progress bar and 'progresspercent' theme widget. Both are based on rec start/end and current time. diff --git a/mythtv/libs/libmyth/programinfo.cpp b/mythtv/libs/libmyth/programinfo.cpp index d845a28..866d077 100644a b ProgramInfo::ProgramInfo(void) : 230 230 231 231 // everything below this line is not serialized 232 232 availableStatus(asAvailable), 233 progressPercent(0), 233 234 spread(-1), 234 235 startCol(-1), 235 236 sortTitle(), … … ProgramInfo::ProgramInfo(const ProgramInfo &other) : 314 315 315 316 // everything below this line is not serialized 316 317 availableStatus(other.availableStatus), 318 progressPercent(other.progressPercent), 317 319 spread(other.spread), 318 320 startCol(other.startCol), 319 321 sortTitle(other.sortTitle), … … ProgramInfo::ProgramInfo( 502 504 503 505 // everything below this line is not serialized 504 506 availableStatus(asAvailable), 507 progressPercent(0), 505 508 spread(-1), 506 509 startCol(-1), 507 510 sortTitle(), … … ProgramInfo::ProgramInfo( 620 623 621 624 // everything below this line is not serialized 622 625 availableStatus(asAvailable), 626 progressPercent(0), 623 627 spread(-1), 624 628 startCol(-1), 625 629 sortTitle(), … … ProgramInfo::ProgramInfo( 751 755 752 756 // everything below this line is not serialized 753 757 availableStatus(asAvailable), 758 progressPercent(0), 754 759 spread(-1), 755 760 startCol(-1), 756 761 sortTitle(), … … ProgramInfo::ProgramInfo( 905 910 906 911 // everything below this line is not serialized 907 912 availableStatus(asAvailable), 913 progressPercent(0), 908 914 spread(-1), 909 915 startCol(-1), 910 916 sortTitle(), … … void ProgramInfo::clear(void) 1259 1265 spread = -1; 1260 1266 startCol = -1; 1261 1267 availableStatus = asAvailable; 1268 progressPercent = 0; 1262 1269 1263 1270 // Private 1264 1271 inUseForWhat.clear(); … … void ProgramInfo::ToMap(InfoMap &progMap, 1702 1709 progMap["lentime"] = QObject::tr("%n hour(s)","", hours); 1703 1710 } 1704 1711 1712 progMap["progresspercent"] = 1713 GetProgressPercent() > 0 ? QString::number(GetProgressPercent()) : ""; 1714 1705 1715 progMap["rectypechar"] = toQChar(GetRecordingRuleType()); 1706 1716 progMap["rectype"] = ::toString(GetRecordingRuleType()); 1707 1717 QString tmp_rec = progMap["rectype"]; … … void ProgramInfo::SaveBookmark(uint64_t frame) 2665 2675 2666 2676 set_flag(programflags, FL_BOOKMARK, is_valid); 2667 2677 2678 UpdateMarkTimeStamp(is_valid); 2679 SendUpdateEvent(); 2680 } 2681 2682 void ProgramInfo::SaveLastPlayPos(uint64_t frame, bool notify) 2683 { 2684 LOG(VB_PLAYBACK, LOG_DEBUG, 2685 QString("LastPlayPos frame=%1").arg(frame)); 2686 ClearMarkupMap(MARK_UTIL_LASTPLAYPOS); 2687 2688 if (frame > 0) 2689 { 2690 frm_dir_map_t lastPlayPosMap; 2691 lastPlayPosMap[frame] = MARK_UTIL_LASTPLAYPOS; 2692 SaveMarkupMap(lastPlayPosMap, MARK_UTIL_LASTPLAYPOS); 2693 } 2694 2695 UpdateMarkTimeStamp(IsBookmarkSet()); 2696 2697 if (notify) 2698 SendUpdateEvent(); 2699 } 2700 2701 void ProgramInfo::UpdateMarkTimeStamp(bool bookmarked) 2702 { 2668 2703 if (IsRecording()) 2669 2704 { 2670 2705 MSqlQuery query(MSqlQuery::InitCon()); … … void ProgramInfo::SaveBookmark(uint64_t frame) 2672 2707 "UPDATE recorded " 2673 2708 "SET bookmarkupdate = CURRENT_TIMESTAMP, " 2674 2709 " bookmark = :BOOKMARKFLAG " 2675 "WHERE chanid = :CHANID AND " 2676 " starttime = :STARTTIME"); 2710 "WHERE recordedid = :RECORDEDID"); 2677 2711 2678 query.bindValue(":BOOKMARKFLAG", is_valid); 2679 query.bindValue(":CHANID", chanid); 2680 query.bindValue(":STARTTIME", recstartts); 2712 query.bindValue(":BOOKMARKFLAG", bookmarked); 2713 query.bindValue(":RECORDEDID", recordedid); 2681 2714 2682 2715 if (!query.exec()) 2683 2716 MythDB::DBError("bookmark flag update", query); 2684 2685 SendUpdateEvent();2686 2717 } 2687 2718 } 2688 2719 … … uint64_t ProgramInfo::QueryProgStart(void) const 2769 2800 return (bookmarkmap.isEmpty()) ? 0 : bookmarkmap.begin().key(); 2770 2801 } 2771 2802 2803 uint64_t ProgramInfo::QueryStartMark(void) const 2804 { 2805 uint64_t start = 0; 2806 if ((start = QueryLastPlayPos()) > 0) 2807 LOG(VB_PLAYBACK, LOG_INFO, QString("Using last position @ %1").arg(start)); 2808 else if ((start = QueryBookmark()) > 0) 2809 LOG(VB_PLAYBACK, LOG_INFO, QString("Using bookmark @ %1").arg(start)); 2810 else if (HasCutlist()) 2811 // Disable progstart if the program has a cutlist. 2812 LOG(VB_PLAYBACK, LOG_INFO, "Ignoring progstart as cutlist exists"); 2813 else if ((start = QueryProgStart()) > 0) 2814 LOG(VB_PLAYBACK, LOG_INFO, QString("Using progstart @ %1").arg(start)); 2815 else 2816 LOG(VB_PLAYBACK, LOG_INFO, "Using file start"); 2817 return start; 2818 } 2819 2772 2820 /** \brief Gets any lastplaypos position in database, 2773 2821 * unless the ignore lastplaypos flag is set. 2774 2822 * -
mythtv/libs/libmyth/programinfo.h
diff --git a/mythtv/libs/libmyth/programinfo.h b/mythtv/libs/libmyth/programinfo.h index 2a1cb20..ca7aa91 100644
a b class MPUBLIC ProgramInfo 69 69 public: 70 70 enum CategoryType { kCategoryNone, kCategoryMovie, kCategorySeries, 71 71 kCategorySports, kCategoryTVShow }; 72 72 73 73 /// Null constructor 74 74 ProgramInfo(void); 75 75 /// Copy constructor … … class MPUBLIC ProgramInfo 548 548 void SetPositionMapDBReplacement(PMapDBReplacement *pmap) 549 549 { positionMapDBReplacement = pmap; } 550 550 551 uint GetProgressPercent() const { return progressPercent; } 552 void SetProgressPercent(uint progress) { progressPercent = progress; } 553 551 554 // Slow DB gets 552 555 QString QueryBasename(void) const; 553 556 // uint64_t QueryFilesize(void) const; // TODO Remove … … class MPUBLIC ProgramInfo 556 559 uint64_t QueryBookmark(void) const; 557 560 uint64_t QueryProgStart(void) const; 558 561 uint64_t QueryLastPlayPos(void) const; 562 uint64_t QueryStartMark(void) const; 559 563 CategoryType QueryCategoryType(void) const; 560 564 QStringList QueryDVDBookmark(const QString &serialid) const; 561 565 QStringList QueryBDBookmark(const QString &serialid) const; … … class MPUBLIC ProgramInfo 581 585 582 586 // Slow DB sets 583 587 virtual void SaveFilesize(uint64_t fsize); /// TODO Move to RecordingInfo 588 void SaveLastPlayPos(uint64_t frame, bool notify = true); 584 589 void SaveBookmark(uint64_t frame); 585 590 void SaveDVDBookmark(const QStringList &fields) const; 586 591 void SaveBDBookmark(const QStringList &fields) const; … … class MPUBLIC ProgramInfo 702 707 bool FromStringList(QStringList::const_iterator &it, 703 708 QStringList::const_iterator end); 704 709 710 void UpdateMarkTimeStamp(bool bookmarked); 711 705 712 static void QueryMarkupMap( 706 713 const QString &video_pathname, 707 714 frm_dir_map_t&, MarkTypes type, bool merge = false); … … class MPUBLIC ProgramInfo 782 789 783 790 // everything below this line is not serialized 784 791 uint8_t availableStatus; // only used for playbackbox.cpp 792 uint progressPercent; // only used by UI 793 785 794 public: 786 795 void SetAvailableStatus(AvailableStatusType status, const QString &where); 787 796 AvailableStatusType GetAvailableStatus(void) const -
mythtv/libs/libmythbase/libmythbase.pro
diff --git a/mythtv/libs/libmythbase/libmythbase.pro b/mythtv/libs/libmythbase/libmythbase.pro index 228617a..3e44184 100644
a b INSTALLS = target 10 10 QMAKE_CLEAN += $(TARGET) $(TARGETA) $(TARGETD) $(TARGET0) $(TARGET1) $(TARGET2) 11 11 12 12 # Input 13 HEADERS += mthread.h mthreadpool.h 13 HEADERS += mthread.h mthreadpool.h mconcurrent.h 14 14 HEADERS += mythsocket.h mythsocket_cb.h 15 15 HEADERS += mythbaseexp.h mythdbcon.h mythdb.h mythdbparams.h oldsettings.h 16 16 HEADERS += verbosedefs.h mythversion.h compat.h mythconfig.h -
new file mythtv/libs/libmythbase/mconcurrent.h
diff --git a/mythtv/libs/libmythbase/mconcurrent.h b/mythtv/libs/libmythbase/mconcurrent.h new file mode 100644 index 0000000..5990763
- + 1 #ifndef MCONCURRENT_H 2 #define MCONCURRENT_H 3 4 #include "mthreadpool.h" 5 #include "logging.h" 6 7 8 /// Provides a simple version of QtConcurrent::run() that uses MThreadPool rather 9 /// than QThreadPool. Useful for starting background threads in 1 line. 10 /// 11 /// Given a class method of: 12 /// 13 /// void Class::fn(arg1, arg2...) 14 /// 15 /// you can run it in a different thread using: 16 /// 17 /// MConcurrent::run("thread name", &Class instance, &Class::fn, arg1, arg2...) 18 /// 19 /// Refer to QtConcurrent::run for further details 20 /// 21 /// Restrictions: 22 /// 1. Accepts 0-5 arguments 23 /// 2. Only class methods are supported (most typical in Myth) 24 /// 3. Only non-const classes & methods are supported (most typical in Myth) 25 /// 4. The method must have return type of void (QFuture is not easily ported to 26 /// MThreadPool. Use signals/events instead) 27 /// 28 namespace MConcurrent { 29 30 class RunFunctionTask : public QRunnable 31 { 32 public: 33 void start(QString name) 34 { 35 MThreadPool::globalInstance()->start(this, name, /*m_priority*/ 0); 36 } 37 38 virtual void runFunctor() = 0; 39 40 void run() 41 { 42 try 43 { 44 this->runFunctor(); 45 } 46 catch (...) 47 { 48 LOG(VB_GENERAL, LOG_ERR, "An exception occurred"); 49 } 50 } 51 }; 52 53 template <typename Class> 54 class VoidStoredMemberFunctionPointerCall0 : public RunFunctionTask 55 { 56 public: 57 VoidStoredMemberFunctionPointerCall0(void (Class::*_fn)() , Class *_object) 58 : fn(_fn), object(_object) { } 59 60 void runFunctor() { (object->*fn)(); } 61 private: 62 void (Class::*fn)(); 63 Class *object; 64 }; 65 66 template <typename Class, typename Param1, typename Arg1> 67 class VoidStoredMemberFunctionPointerCall1 : public RunFunctionTask 68 { 69 public: 70 VoidStoredMemberFunctionPointerCall1(void (Class::*_fn)(Param1) , Class *_object, const Arg1 &_arg1) 71 : fn(_fn), object(_object), arg1(_arg1){ } 72 73 void runFunctor() { (object->*fn)(arg1); } 74 private: 75 void (Class::*fn)(Param1); 76 Class *object; 77 Arg1 arg1; 78 }; 79 80 template <typename Class, typename Param1, typename Arg1, typename Param2, typename Arg2> 81 class VoidStoredMemberFunctionPointerCall2 : public RunFunctionTask 82 { 83 public: 84 VoidStoredMemberFunctionPointerCall2(void (Class::*_fn)(Param1, Param2) , Class *_object, const Arg1 &_arg1, const Arg2 &_arg2) 85 : fn(_fn), object(_object), arg1(_arg1), arg2(_arg2){ } 86 87 void runFunctor() { (object->*fn)(arg1, arg2); } 88 private: 89 void (Class::*fn)(Param1, Param2); 90 Class *object; 91 Arg1 arg1; Arg2 arg2; 92 }; 93 94 template <typename Class, typename Param1, typename Arg1, typename Param2, typename Arg2, typename Param3, typename Arg3> 95 class VoidStoredMemberFunctionPointerCall3 : public RunFunctionTask 96 { 97 public: 98 VoidStoredMemberFunctionPointerCall3(void (Class::*_fn)(Param1, Param2, Param3) , Class *_object, const Arg1 &_arg1, const Arg2 &_arg2, const Arg3 &_arg3) 99 : fn(_fn), object(_object), arg1(_arg1), arg2(_arg2), arg3(_arg3){ } 100 101 void runFunctor() { (object->*fn)(arg1, arg2, arg3); } 102 private: 103 void (Class::*fn)(Param1, Param2, Param3); 104 Class *object; 105 Arg1 arg1; Arg2 arg2; Arg3 arg3; 106 }; 107 108 template <typename Class, typename Param1, typename Arg1, typename Param2, typename Arg2, typename Param3, typename Arg3, typename Param4, typename Arg4> 109 class VoidStoredMemberFunctionPointerCall4 : public RunFunctionTask 110 { 111 public: 112 VoidStoredMemberFunctionPointerCall4(void (Class::*_fn)(Param1, Param2, Param3, Param4) , Class *_object, const Arg1 &_arg1, const Arg2 &_arg2, const Arg3 &_arg3, const Arg4 &_arg4) 113 : fn(_fn), object(_object), arg1(_arg1), arg2(_arg2), arg3(_arg3), arg4(_arg4){ } 114 115 void runFunctor() { (object->*fn)(arg1, arg2, arg3, arg4); } 116 private: 117 void (Class::*fn)(Param1, Param2, Param3, Param4); 118 Class *object; 119 Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; 120 }; 121 122 template <typename Class, typename Param1, typename Arg1, typename Param2, typename Arg2, typename Param3, typename Arg3, typename Param4, typename Arg4, typename Param5, typename Arg5> 123 class VoidStoredMemberFunctionPointerCall5 : public RunFunctionTask 124 { 125 public: 126 VoidStoredMemberFunctionPointerCall5(void (Class::*_fn)(Param1, Param2, Param3, Param4, Param5) , Class *_object, const Arg1 &_arg1, const Arg2 &_arg2, const Arg3 &_arg3, const Arg4 &_arg4, const Arg5 &_arg5) 127 : fn(_fn), object(_object), arg1(_arg1), arg2(_arg2), arg3(_arg3), arg4(_arg4), arg5(_arg5){ } 128 129 void runFunctor() { (object->*fn)(arg1, arg2, arg3, arg4, arg5); } 130 private: 131 void (Class::*fn)(Param1, Param2, Param3, Param4, Param5); 132 Class *object; 133 Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; Arg5 arg5; 134 }; 135 136 template <typename Class> 137 void run(const QString &name, Class *object, void (Class::*fn)()) 138 { 139 (new VoidStoredMemberFunctionPointerCall0<Class>(fn, object))->start(name); 140 } 141 142 template <typename Class, typename Param1, typename Arg1> 143 void run(const QString &name, Class *object, void (Class::*fn)(Param1), const Arg1 &arg1) 144 { 145 (new VoidStoredMemberFunctionPointerCall1<Class, Param1, Arg1>(fn, object, arg1))->start(name); 146 } 147 148 template <typename Class, typename Param1, typename Arg1, typename Param2, typename Arg2> 149 void run(const QString &name, Class *object, void (Class::*fn)(Param1, Param2), const Arg1 &arg1, const Arg2 &arg2) 150 { 151 (new VoidStoredMemberFunctionPointerCall2<Class, Param1, Arg1, Param2, Arg2>(fn, object, arg1, arg2))->start(name); 152 } 153 154 template <typename Class, typename Param1, typename Arg1, typename Param2, typename Arg2, typename Param3, typename Arg3> 155 void run(const QString &name, Class *object, void (Class::*fn)(Param1, Param2, Param3), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3) 156 { 157 (new VoidStoredMemberFunctionPointerCall3<Class, Param1, Arg1, Param2, Arg2, Param3, Arg3>(fn, object, arg1, arg2, arg3))->start(name); 158 } 159 160 template <typename Class, typename Param1, typename Arg1, typename Param2, typename Arg2, typename Param3, typename Arg3, typename Param4, typename Arg4> 161 void run(const QString &name, Class *object, void (Class::*fn)(Param1, Param2, Param3, Param4), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4) 162 { 163 (new VoidStoredMemberFunctionPointerCall4<Class, Param1, Arg1, Param2, Arg2, Param3, Arg3, Param4, Arg4>(fn, object, arg1, arg2, arg3, arg4))->start(name); 164 } 165 166 template <typename Class, typename Param1, typename Arg1, typename Param2, typename Arg2, typename Param3, typename Arg3, typename Param4, typename Arg4, typename Param5, typename Arg5> 167 void run(const QString &name, Class *object, void (Class::*fn)(Param1, Param2, Param3, Param4, Param5), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg4 &arg5) 168 { 169 (new VoidStoredMemberFunctionPointerCall5<Class, Param1, Arg1, Param2, Arg2, Param3, Arg3, Param4, Arg4, Param5, Arg5>(fn, object, arg1, arg2, arg3, arg4, arg5))->start(name); 170 } 171 172 173 } //namespace QtConcurrent 174 175 #endif // MCONCURRENT_H 176 -
mythtv/libs/libmythtv/mythplayer.cpp
diff --git a/mythtv/libs/libmythtv/mythplayer.cpp b/mythtv/libs/libmythtv/mythplayer.cpp index f953e11..c0bbab0 100644
a b MythPlayer::MythPlayer(PlayerFlags flags) 238 238 captionsEnabledbyDefault = gCoreContext->GetNumSetting("DefaultCCMode"); 239 239 decode_extra_audio = gCoreContext->GetNumSetting("DecodeExtraAudio", 0); 240 240 itvEnabled = gCoreContext->GetNumSetting("EnableMHEG", 0); 241 clearSavedPosition = gCoreContext->GetNumSetting("ClearSavedPosition", 1);242 241 endExitPrompt = gCoreContext->GetNumSetting("EndOfRecordingExitPrompt"); 243 242 pip_default_loc = (PIPLocation)gCoreContext->GetNumSetting("PIPLocation", kPIPTopLeft); 244 243 … … void MythPlayer::InitialSeek(void) 2901 2900 if (bookmarkseek > 30) 2902 2901 { 2903 2902 DoJumpToFrame(bookmarkseek, kInaccuracyNone); 2904 if (clearSavedPosition && !player_ctx->IsPIP())2905 SetBookmark(true);2906 2903 } 2907 2904 } 2908 2905 … … uint64_t MythPlayer::GetBookmark(void) 3666 3663 player_ctx->LockPlayingInfo(__FILE__, __LINE__); 3667 3664 if (const ProgramInfo *pi = player_ctx->playingInfo) 3668 3665 { 3669 bookmark = pi->QueryBookmark(); 3670 // Disable progstart if the program has a cutlist. 3671 if (bookmark == 0 && !pi->HasCutlist()) 3672 bookmark = pi->QueryProgStart(); 3673 if (bookmark == 0) 3674 bookmark = pi->QueryLastPlayPos(); 3666 bookmark = pi->QueryStartMark(); 3675 3667 } 3676 3668 player_ctx->UnlockPlayingInfo(__FILE__, __LINE__); 3677 3669 } -
mythtv/libs/libmythtv/mythplayer.h
diff --git a/mythtv/libs/libmythtv/mythplayer.h b/mythtv/libs/libmythtv/mythplayer.h index bfdac87..667c6d4 100644
a b class MTV_PUBLIC MythPlayer 684 684 685 685 // Bookmark stuff 686 686 uint64_t bookmarkseek; 687 int clearSavedPosition;688 687 int endExitPrompt; 689 688 690 689 // Seek -
mythtv/libs/libmythtv/previewgenerator.cpp
diff --git a/mythtv/libs/libmythtv/previewgenerator.cpp b/mythtv/libs/libmythtv/previewgenerator.cpp index c138f5e..a2ec613 100644
a b bool PreviewGenerator::SavePreview(const QString &filename, 618 618 bool PreviewGenerator::LocalPreviewRun(void) 619 619 { 620 620 m_programInfo.MarkAsInUse(true, kPreviewGeneratorInUseID); 621 m_programInfo.SetIgnoreProgStart(true); 622 m_programInfo.SetAllowLastPlayPos(false); 621 m_programInfo.SetIgnoreProgStart(false); 622 m_programInfo.SetIgnoreBookmark(false); 623 m_programInfo.SetAllowLastPlayPos(true); 623 624 624 625 float aspect = 0; 625 626 int width, height, sz; … … bool PreviewGenerator::LocalPreviewRun(void) 632 633 633 634 if (captime < 0) 634 635 { 635 captime = m_programInfo.QueryBookmark(); 636 if (captime > 0) 636 uint64_t markFrame = m_programInfo.QueryStartMark(); 637 LOG(VB_GENERAL, LOG_INFO, 638 QString("Preview from start mark (frame %1)").arg(markFrame)); 639 if (markFrame > 0) 637 640 { 638 641 m_timeInSeconds = false; 639 LOG(VB_GENERAL, LOG_INFO, 640 QString("Preview from bookmark (frame %1)").arg(captime)); 642 captime = markFrame; 641 643 } 642 else643 captime = -1;644 644 } 645 645 646 646 if (captime <= 0) -
mythtv/libs/libmythtv/tv_play.cpp
diff --git a/mythtv/libs/libmythtv/tv_play.cpp b/mythtv/libs/libmythtv/tv_play.cpp index 9b9511b..dbc5d39 100644
a b using namespace std; 35 35 #include "compat.h" 36 36 #include "mythdirs.h" 37 37 #include "mythmedia.h" 38 #include "mconcurrent.h" 38 39 39 40 // libmyth 40 41 #include "programinfo.h" … … TV::TV(void) 1013 1014 db_playback_exit_prompt(0), db_autoexpire_default(0), 1014 1015 db_auto_set_watched(false), db_end_of_rec_exit_prompt(false), 1015 1016 db_jump_prefer_osd(true), db_use_gui_size_for_tv(false), 1016 db_clear_saved_position(false),1017 1017 db_toggle_bookmark(false), 1018 1018 db_run_jobs_on_remote(false), db_continue_embedded(false), 1019 1019 db_use_fixed_size(true), db_browse_always(false), … … TV::TV(void) 1038 1038 requestDelete(false), allowRerecord(false), 1039 1039 doSmartForward(false), 1040 1040 queuedTranscode(false), 1041 savePosOnExit(false), 1041 1042 adjustingPicture(kAdjustingPicture_None), 1042 1043 adjustingPictureAttribute(kPictureAttribute_None), 1043 1044 askAllowLock(QMutex::Recursive), … … void TV::InitFromDB(void) 1121 1122 kv["EndOfRecordingExitPrompt"] = "0"; 1122 1123 kv["JumpToProgramOSD"] = "1"; 1123 1124 kv["GuiSizeForTV"] = "0"; 1124 kv["ClearSavedPosition"] = "1";1125 1125 kv["AltClearSavedPosition"] = "1"; 1126 1126 kv["JobsRunOnRecordHost"] = "0"; 1127 1127 kv["ContinueEmbeddedTVPlay"] = "0"; … … void TV::InitFromDB(void) 1172 1172 db_end_of_rec_exit_prompt = kv["EndOfRecordingExitPrompt"].toInt(); 1173 1173 db_jump_prefer_osd = kv["JumpToProgramOSD"].toInt(); 1174 1174 db_use_gui_size_for_tv = kv["GuiSizeForTV"].toInt(); 1175 db_clear_saved_position= kv["ClearSavedPosition"].toInt();1176 1175 db_toggle_bookmark = kv["AltClearSavedPosition"].toInt(); 1177 1176 db_run_jobs_on_remote = kv["JobsRunOnRecordHost"].toInt(); 1178 1177 db_continue_embedded = kv["ContinueEmbeddedTVPlay"].toInt(); … … bool TV::Init(bool createWindow) 1358 1357 speedChangeTimerId = StartTimer(kSpeedChangeCheckFrequency, __LINE__); 1359 1358 saveLastPlayPosTimerId = StartTimer(kSaveLastPlayPosTimeout, __LINE__); 1360 1359 1360 savePosOnExit = false; 1361 1361 1362 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "-- end"); 1362 1363 return true; 1363 1364 } … … void TV::PrepToSwitchToRecordedProgram(PlayerContext *ctx, 3348 3349 SetExitPlayer(true, true); 3349 3350 } 3350 3351 3351 void TV::PrepareToExitPlayer(PlayerContext *ctx, int line , BookmarkAction bookmark)3352 void TV::PrepareToExitPlayer(PlayerContext *ctx, int line) 3352 3353 { 3353 bool bm_allowed = IsBookmarkAllowed(ctx);3354 3354 ctx->LockDeletePlayer(__FILE__, line); 3355 if ( ctx->player)3355 if (savePosOnExit && ctx->player && ctx->playingInfo) 3356 3356 { 3357 if (bm_allowed) 3358 { 3359 // If we're exiting in the middle of the recording, we 3360 // automatically save a bookmark when "Action on playback 3361 // exit" is set to "Save position and exit". 3362 bool allow_set_before_end = 3363 (bookmark == kBookmarkAlways || 3364 (bookmark == kBookmarkAuto && 3365 db_playback_exit_prompt == 2)); 3366 // If we're exiting at the end of the recording, we 3367 // automatically clear the bookmark when "Action on 3368 // playback exit" is set to "Save position and exit" and 3369 // "Clear bookmark on playback" is set to true. 3370 bool allow_clear_at_end = 3371 (bookmark == kBookmarkAlways || 3372 (bookmark == kBookmarkAuto && 3373 db_playback_exit_prompt == 2 && 3374 db_clear_saved_position)); 3375 // Whether to set/clear a bookmark depends on whether we're 3376 // exiting at the end of a recording. 3377 bool at_end = (ctx->player->IsNearEnd() || getEndOfRecording()); 3378 // Don't consider ourselves at the end if the recording is 3379 // in-progress. 3380 at_end &= !StateIsRecording(GetState(ctx)); 3381 bool clear_lastplaypos = false; 3382 if (at_end && allow_clear_at_end) 3383 { 3384 SetBookmark(ctx, true); 3385 // Tidy up the lastplaypos mark only when we clear the 3386 // bookmark due to exiting at the end. 3387 clear_lastplaypos = true; 3388 } 3389 else if (!at_end && allow_set_before_end) 3390 { 3391 SetBookmark(ctx, false); 3392 } 3393 if (clear_lastplaypos && ctx->playingInfo) 3394 ctx->playingInfo->ClearMarkupMap(MARK_UTIL_LASTPLAYPOS); 3395 } 3357 // Clear last play position when we're at the end of a recording. 3358 // unless the recording is in-progress. 3359 bool at_end = !StateIsRecording(GetState(ctx)) && 3360 (getEndOfRecording() || ctx->player->IsNearEnd()); 3361 3362 // Clear/Save play position without notification 3363 // The change must be broadcast when file is no longer in use 3364 // to update previews, ie. with the MarkNotInUse notification 3365 uint64_t frame = at_end ? 0 : ctx->player->GetFramesPlayed(); 3366 ctx->playingInfo->SaveLastPlayPos(frame, false); 3367 3396 3368 if (db_auto_set_watched) 3397 3369 ctx->player->SetWatched(); 3398 3370 } … … bool TV::ActivePostQHandleAction(PlayerContext *ctx, const QStringList &actions) 5079 5051 { 5080 5052 NormalSpeed(ctx); 5081 5053 StopFFRew(ctx); 5082 SetBookmark(ctx);5054 PrepareToExitPlayer(ctx, __LINE__); 5083 5055 ShowOSDPromptDeleteRecording(ctx, tr("Are you sure you want to delete:")); 5084 5056 } 5085 5057 else if (has_action(ACTION_JUMPTODVDROOTMENU, actions) && isdisc) … … void TV::ProcessNetworkControlCommand(PlayerContext *ctx, 5301 5273 } 5302 5274 else if (tokens.size() == 2 && tokens[1] == "STOP") 5303 5275 { 5304 SetBookmark(ctx); 5305 ctx->LockDeletePlayer(__FILE__, __LINE__); 5306 if (ctx->player && db_auto_set_watched) 5307 ctx->player->SetWatched(); 5308 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 5276 PrepareToExitPlayer(ctx, __LINE__); 5309 5277 SetExitPlayer(true, true); 5310 5278 } 5311 5279 else if (tokens.size() >= 3 && tokens[1] == "SEEK" && ctx->HasPlayer()) … … void TV::customEvent(QEvent *e) 9502 9470 for (uint i = 0; mctx && (i < player.size()); i++) 9503 9471 { 9504 9472 PlayerContext *ctx = GetPlayer(mctx, i); 9505 PrepareToExitPlayer(ctx, __LINE__ , kBookmarkAuto);9473 PrepareToExitPlayer(ctx, __LINE__); 9506 9474 } 9507 9475 9508 9476 SetExitPlayer(true, true); … … void TV::ShowOSDStopWatchingRecording(PlayerContext *ctx) 13255 13223 osd->DialogShow(OSD_DLG_VIDEOEXIT, 13256 13224 tr("You are exiting %1").arg(videotype)); 13257 13225 13258 if (IsBookmarkAllowed(ctx)) 13259 { 13260 osd->DialogAddButton(tr("Save this position and go to the menu"), 13261 "DIALOG_VIDEOEXIT_SAVEPOSITIONANDEXIT_0"); 13262 osd->DialogAddButton(tr("Do not save, just exit to the menu"), 13263 ACTION_STOP); 13264 } 13265 else 13266 osd->DialogAddButton(tr("Exit %1").arg(videotype), 13267 ACTION_STOP); 13226 osd->DialogAddButton(tr("Exit %1").arg(videotype), 13227 ACTION_STOP); 13268 13228 13269 13229 if (IsDeleteAllowed(ctx)) 13270 13230 osd->DialogAddButton(tr("Delete this recording"), … … bool TV::HandleOSDVideoExit(PlayerContext *ctx, QString action) 13409 13369 13410 13370 bool hide = true; 13411 13371 bool delete_ok = IsDeleteAllowed(ctx); 13412 bool bookmark_ok = IsBookmarkAllowed(ctx);13413 13372 13414 13373 ctx->LockDeletePlayer(__FILE__, __LINE__); 13415 13374 bool near_end = ctx->player && ctx->player->IsNearEnd(); … … bool TV::HandleOSDVideoExit(PlayerContext *ctx, QString action) 13419 13378 { 13420 13379 allowRerecord = true; 13421 13380 requestDelete = true; 13381 PrepareToExitPlayer(ctx, __LINE__); 13422 13382 SetExitPlayer(true, true); 13423 13383 } 13424 13384 else if (action == "JUSTDELETE" && delete_ok) 13425 13385 { 13426 13386 requestDelete = true; 13387 PrepareToExitPlayer(ctx, __LINE__); 13427 13388 SetExitPlayer(true, true); 13428 13389 } 13429 13390 else if (action == "CONFIRMDELETE") … … bool TV::HandleOSDVideoExit(PlayerContext *ctx, QString action) 13432 13393 ShowOSDPromptDeleteRecording(ctx, tr("Are you sure you want to delete:"), 13433 13394 true); 13434 13395 } 13435 else if (action == "SAVEPOSITIONANDEXIT" && bookmark_ok)13436 {13437 PrepareToExitPlayer(ctx, __LINE__, kBookmarkAlways);13438 SetExitPlayer(true, true);13439 }13440 13396 else if (action == "KEEPWATCHING" && !near_end) 13441 13397 { 13442 13398 DoTogglePause(ctx, true); … … bool TV::HandleOSDVideoExit(PlayerContext *ctx, QString action) 13447 13403 13448 13404 void TV::HandleSaveLastPlayPosEvent(void) 13449 13405 { 13450 // Helper class to save the latest playback position (in a background thread13451 // to avoid playback glitches). The ctor makes a copy of the ProgramInfo13452 // struct to avoid race conditions if playback ends and deletes objects13453 // before or while the background thread runs.13454 class PositionSaver : public QRunnable13455 {13456 public:13457 PositionSaver(const ProgramInfo &pginfo, uint64_t frame) :13458 m_pginfo(pginfo), m_frame(frame) {}13459 virtual void run(void)13460 {13461 LOG(VB_PLAYBACK, LOG_DEBUG,13462 QString("PositionSaver frame=%1").arg(m_frame));13463 frm_dir_map_t lastPlayPosMap;13464 lastPlayPosMap[m_frame] = MARK_UTIL_LASTPLAYPOS;13465 m_pginfo.ClearMarkupMap(MARK_UTIL_LASTPLAYPOS);13466 m_pginfo.SaveMarkupMap(lastPlayPosMap, MARK_UTIL_LASTPLAYPOS);13467 }13468 private:13469 const ProgramInfo m_pginfo;13470 const uint64_t m_frame;13471 };13472 13473 13406 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 13474 13407 for (uint i = 0; mctx && i < player.size(); ++i) 13475 13408 { … … void TV::HandleSaveLastPlayPosEvent(void) 13478 13411 bool playing = ctx->player && !ctx->player->IsPaused(); 13479 13412 if (playing) // Don't bother saving lastplaypos while paused 13480 13413 { 13414 // Save the latest playback position in a background thread 13415 // to avoid playback glitches. 13481 13416 uint64_t framesPlayed = ctx->player->GetFramesPlayed(); 13482 MThreadPool::globalInstance()-> 13483 start(new PositionSaver(*ctx->playingInfo, framesPlayed), 13484 "PositionSaver"); 13417 MConcurrent::run("PositionSaver", ctx->playingInfo, 13418 &ProgramInfo::SaveLastPlayPos, framesPlayed, true); 13485 13419 } 13486 13420 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 13487 13421 } … … void TV::HandleSaveLastPlayPosEvent(void) 13490 13424 QMutexLocker locker(&timerIdLock); 13491 13425 KillTimer(saveLastPlayPosTimerId); 13492 13426 saveLastPlayPosTimerId = StartTimer(kSaveLastPlayPosTimeout, __LINE__); 13427 13428 savePosOnExit = true; 13493 13429 } 13494 13430 13495 13431 void TV::SetLastProgram(const ProgramInfo *rcinfo) -
mythtv/libs/libmythtv/tv_play.h
diff --git a/mythtv/libs/libmythtv/tv_play.h b/mythtv/libs/libmythtv/tv_play.h index 0678cbb..b026781 100644
a b class MTV_PUBLIC TV : public QObject, public MenuItemDisplayer 366 366 bool Init(bool createWindow = true); 367 367 void InitFromDB(void); 368 368 QList<QKeyEvent> ConvertScreenPressKeyMap(const QString& keyList); 369 369 370 370 // Top level playback methods 371 371 bool LiveTV(bool showDialogs, const ChannelInfoList &selection); 372 372 int Playback(const ProgramInfo &rcinfo); … … class MTV_PUBLIC TV : public QObject, public MenuItemDisplayer 375 375 // Private event handling 376 376 bool ProcessKeypressOrGesture(PlayerContext*, QEvent *e); 377 377 bool TranslateKeyPressOrGesture(const QString &context, QEvent *e, 378 QStringList &actions, bool isLiveTV, 378 QStringList &actions, bool isLiveTV, 379 379 bool allowJumps = true); 380 380 bool TranslateGesture(const QString &context, MythGestureEvent *e, 381 381 QStringList &actions, bool isLiveTV); … … class MTV_PUBLIC TV : public QObject, public MenuItemDisplayer 483 483 kBookmarkNever, 484 484 kBookmarkAuto // set iff db_playback_exit_prompt==2 485 485 }; 486 void PrepareToExitPlayer(PlayerContext*, int line, 487 BookmarkAction bookmark = kBookmarkAuto); 486 void PrepareToExitPlayer(PlayerContext*, int line); 488 487 void SetExitPlayer(bool set_it, bool wants_to); 489 488 490 489 bool RequestNextRecorder(PlayerContext *, bool, … … class MTV_PUBLIC TV : public QObject, public MenuItemDisplayer 795 794 bool db_jump_prefer_osd; 796 795 bool db_use_gui_size_for_tv; 797 796 bool db_start_in_guide; 798 bool db_clear_saved_position;799 797 bool db_toggle_bookmark; 800 798 bool db_run_jobs_on_remote; 801 799 bool db_continue_embedded; … … class MTV_PUBLIC TV : public QObject, public MenuItemDisplayer 839 837 bool allowRerecord; ///< User wants to rerecord the last video if deleted 840 838 bool doSmartForward; 841 839 bool queuedTranscode; 840 bool savePosOnExit; ///< False until first timer event 842 841 /// Picture attribute type to modify. 843 842 PictureAdjustType adjustingPicture; 844 843 /// Picture attribute to modify (on arrow left or right) … … class MTV_PUBLIC TV : public QObject, public MenuItemDisplayer 899 898 static const int screenPressRegionCount = 12; 900 899 QList<QKeyEvent> screenPressKeyMapPlayback; 901 900 QList<QKeyEvent> screenPressKeyMapLiveTV; 902 901 903 902 // Channel changing timeout notification variables 904 903 QTime lockTimer; 905 904 bool lockTimerOn; -
mythtv/libs/libmythui/mythuibuttonlist.cpp
diff --git a/mythtv/libs/libmythui/mythuibuttonlist.cpp b/mythtv/libs/libmythui/mythuibuttonlist.cpp index 9c25ff4..1e013d0 100644
a b 21 21 #include "mythuigroup.h" 22 22 #include "mythuiimage.h" 23 23 #include "mythgesture.h" 24 #include "mythuiprogressbar.h" 24 25 25 26 #define LOC QString("MythUIButtonList(%1): ").arg(objectName()) 26 27 … … MythUIButtonListItem::MythUIButtonListItem(MythUIButtonList *lbtype, 3133 3134 m_showArrow = showArrow; 3134 3135 m_data = 0; 3135 3136 m_isVisible = false; 3137 m_progress = 0; 3138 m_progressStart = 0; 3139 m_progressTotal = 0; 3136 3140 3137 3141 if (state >= NotChecked) 3138 3142 m_checkable = true; … … MythUIButtonListItem::MythUIButtonListItem(MythUIButtonList *lbtype, 3158 3162 m_state = CantCheck; 3159 3163 m_showArrow = false; 3160 3164 m_isVisible = false; 3165 m_progress = 0; 3166 m_progressStart = 0; 3167 m_progressTotal = 0; 3161 3168 3162 3169 if (m_parent) 3163 3170 m_parent->InsertItem(this, listPosition); … … QString MythUIButtonListItem::GetImageFilename(const QString &name) const 3413 3420 return QString(); 3414 3421 } 3415 3422 3423 void MythUIButtonListItem::SetProgress(int start, int total, int used) 3424 { 3425 m_progress = used; 3426 m_progressStart = start; 3427 m_progressTotal = total; 3428 3429 if (m_parent && m_isVisible) 3430 m_parent->Update(); 3431 } 3432 3416 3433 void MythUIButtonListItem::DisplayState(const QString &state, 3417 3434 const QString &name) 3418 3435 { … … void MythUIButtonListItem::SetToRealButton(MythUIStateType *button, bool selecte 3707 3724 // There is no need to check the return value here, since we already 3708 3725 // checked that the state exists with GetState() earlier 3709 3726 button->DisplayState(state); 3727 3728 MythUIProgressBar *progress = dynamic_cast<MythUIProgressBar *> 3729 (buttonstate->GetChild("buttonprogress")); 3730 if (progress) 3731 progress->Set(m_progressStart, m_progressTotal, m_progress); 3710 3732 } 3711 3733 3712 3734 //--------------------------------------------------------- -
mythtv/libs/libmythui/mythuibuttonlist.h
diff --git a/mythtv/libs/libmythui/mythuibuttonlist.h b/mythtv/libs/libmythui/mythuibuttonlist.h index 3838fc6..2efc533 100644
a b class MUI_PUBLIC MythUIButtonListItem 81 81 void SetImageFromMap(const InfoMap &imageMap); 82 82 QString GetImageFilename(const QString &name="") const; 83 83 84 void SetProgress(int start, int total, int used); 85 84 86 void DisplayState(const QString &state, const QString &name); 85 87 void SetStatesFromMap(const InfoMap &stateMap); 86 88 … … class MUI_PUBLIC MythUIButtonListItem 113 115 QVariant m_data; 114 116 bool m_showArrow; 115 117 bool m_isVisible; 118 int m_progress; 119 int m_progressStart; 120 int m_progressTotal; 116 121 117 122 QMap<QString, TextProperties> m_strings; 118 123 QMap<QString, MythImage*> m_images; -
mythtv/libs/libmythui/mythuiprogressbar.cpp
diff --git a/mythtv/libs/libmythui/mythuiprogressbar.cpp b/mythtv/libs/libmythui/mythuiprogressbar.cpp index 4a7fdf4..2942c47 100644
a b bool MythUIProgressBar::ParseElement( 58 58 return true; 59 59 } 60 60 61 void MythUIProgressBar::Set(int start, int total, int used) 62 { 63 if (used != m_current || start != m_start || total != m_total) 64 { 65 m_start = start; 66 m_total = total; 67 SetUsed(used); 68 } 69 } 70 61 71 void MythUIProgressBar::SetStart(int value) 62 72 { 63 73 m_start = value; -
mythtv/libs/libmythui/mythuiprogressbar.h
diff --git a/mythtv/libs/libmythui/mythuiprogressbar.h b/mythtv/libs/libmythui/mythuiprogressbar.h index fea6eb5..dfa4ac7 100644
a b class MUI_PUBLIC MythUIProgressBar : public MythUIType 20 20 enum LayoutType { LayoutVertical, LayoutHorizontal }; 21 21 enum EffectType { EffectReveal, EffectSlide, EffectAnimate }; 22 22 23 void Set(int start, int total, int used); 23 24 void SetStart(int); 24 25 void SetUsed(int); 25 26 void SetTotal(int); -
mythtv/programs/mythfrontend/globalsettings.cpp
diff --git a/mythtv/programs/mythfrontend/globalsettings.cpp b/mythtv/programs/mythfrontend/globalsettings.cpp index f07e4fe..4a8a5b3 100644
a b static HostCheckBox *BrowseAllTuners() 1656 1656 return gc; 1657 1657 } 1658 1658 1659 static HostCheckBox *ClearSavedPosition()1660 {1661 HostCheckBox *gc = new HostCheckBox("ClearSavedPosition");1662 1663 gc->setLabel(PlaybackSettings::tr("Clear bookmark on playback"));1664 1665 gc->setValue(true);1666 1667 gc->setHelpText(PlaybackSettings::tr("If enabled, automatically clear the "1668 "bookmark on a recording when the "1669 "recording is played back. If "1670 "disabled, you can mark the "1671 "beginning with rewind then save "1672 "position."));1673 return gc;1674 }1675 1676 1659 static HostCheckBox *AltClearSavedPosition() 1677 1660 { 1678 1661 HostCheckBox *gc = new HostCheckBox("AltClearSavedPosition"); … … static HostComboBox *PlaybackExitPrompt() 1697 1680 gc->setLabel(PlaybackSettings::tr("Action on playback exit")); 1698 1681 1699 1682 gc->addSelection(PlaybackSettings::tr("Just exit"), "0"); 1700 gc->addSelection(PlaybackSettings::tr("Save position and exit"), "2");1701 1683 gc->addSelection(PlaybackSettings::tr("Always prompt (excluding Live TV)"), 1702 1684 "1"); 1703 1685 gc->addSelection(PlaybackSettings::tr("Always prompt (including Live TV)"), … … static HostComboBox *PlaybackExitPrompt() 1707 1689 gc->setHelpText(PlaybackSettings::tr("If set to prompt, a menu will be " 1708 1690 "displayed when you exit playback " 1709 1691 "mode. The options available will " 1710 "allow you to save your position, " 1711 "delete the recording, or continue " 1712 "watching.")); 1692 "allow you to delete the recording, " 1693 "continue watching or exit.")); 1713 1694 return gc; 1714 1695 } 1715 1696 … … PlaybackSettings::PlaybackSettings() 3924 3905 3925 3906 VerticalConfigurationGroup *column2 = 3926 3907 new VerticalConfigurationGroup(false, false, true, true); 3927 column2->addChild(ClearSavedPosition());3928 3908 column2->addChild(AltClearSavedPosition()); 3929 3909 column2->addChild(AutomaticSetWatched()); 3930 3910 column2->addChild(ContinueEmbeddedTVPlay()); -
mythtv/programs/mythfrontend/main.cpp
diff --git a/mythtv/programs/mythfrontend/main.cpp b/mythtv/programs/mythfrontend/main.cpp index 62ba4b2..a6f6f15 100644
a b namespace 194 194 Q_DECLARE_TR_FUNCTIONS(BookmarkDialog) 195 195 196 196 public: 197 BookmarkDialog(ProgramInfo *pginfo, MythScreenStack *parent) : 197 BookmarkDialog(ProgramInfo *pginfo, MythScreenStack *parent, 198 bool bookmarkPresent, bool lastPlayPresent) : 198 199 MythScreenType(parent, "bookmarkdialog"), 199 pgi(pginfo) 200 pgi(pginfo), 201 bookmarked(bookmarkPresent), 202 lastPlayed(lastPlayPresent), 203 btnPlayBookmark(tr("Play from bookmark")), 204 btnClearBookmark(tr("Clear bookmark")), 205 btnPlayBegin(tr("Play from beginning")), 206 btnPlayLast(tr("Play from last played position")), 207 btnClearLastPlay(tr("Clear last played position")) 200 208 { 201 209 } 202 210 203 211 bool Create() 204 212 { 205 213 QString msg = tr("DVD/Video contains a bookmark"); 206 QString btn0msg = tr("Play from bookmark");207 QString btn1msg = tr("Play from beginning");208 214 209 215 MythDialogBox *popup = new MythDialogBox(msg, GetScreenStack(), "bookmarkdialog"); 210 216 if (!popup->Create()) … … namespace 216 222 GetScreenStack()->AddScreen(popup); 217 223 218 224 popup->SetReturnEvent(this, "bookmarkdialog"); 219 popup->AddButton(btn0msg); 220 popup->AddButton(btn1msg); 225 if (bookmarked) 226 { 227 popup->AddButton(btnPlayBookmark); 228 popup->AddButton(btnClearBookmark); 229 } 230 231 popup->AddButton(btnPlayBegin); 232 233 if (lastPlayed) 234 { 235 popup->AddButton(btnPlayLast); 236 popup->AddButton(btnClearLastPlay); 237 } 221 238 return true; 222 239 } 223 240 … … namespace 226 243 if (event->type() == DialogCompletionEvent::kEventType) 227 244 { 228 245 DialogCompletionEvent *dce = (DialogCompletionEvent*)(event); 229 int buttonnum = dce->GetResult();246 QString buttonText = dce->GetResultText(); 230 247 231 248 if (dce->GetId() == "bookmarkdialog") 232 249 { 233 uint flags = kStartTVNoFlags; 234 if (buttonnum == 1) 250 if (buttonText == btnPlayBookmark) 235 251 { 236 flags |= kStartTVIgnoreBookmark;252 TV::StartTV(pgi, kStartTVNoFlags ); 237 253 } 238 else if (button num != 0)254 else if (buttonText == btnPlayBegin) 239 255 { 240 delete pgi; 241 return; 256 TV::StartTV(pgi, kStartTVNoFlags | kStartTVIgnoreBookmark); 257 } 258 else if (buttonText == btnPlayLast) 259 { 260 TV::StartTV(pgi, kStartTVNoFlags | kStartTVAllowLastPlayPos); 261 } 262 else if (buttonText == btnClearBookmark) 263 { 264 pgi->SaveBookmark(0); 265 } 266 else if (buttonText == btnClearLastPlay) 267 { 268 pgi->SaveLastPlayPos(0); 242 269 } 243 244 TV::StartTV(pgi, flags);245 270 246 271 delete pgi; 247 272 } … … namespace 250 275 251 276 private: 252 277 ProgramInfo* pgi; 278 bool bookmarked, lastPlayed; 279 QString btnPlayBookmark, btnClearBookmark; 280 QString btnPlayBegin; 281 QString btnPlayLast, btnClearLastPlay; 253 282 }; 254 283 255 284 void cleanup() … … static int internal_play_media(const QString &mrl, const QString &plot, 1174 1203 pginfo->SetProgramInfoType(pginfo->DiscoverProgramInfoType()); 1175 1204 1176 1205 bool bookmarkPresent = false; 1206 bool lastPlayPresent = false; 1177 1207 1178 1208 if (pginfo->IsVideoDVD()) 1179 1209 { … … static int internal_play_media(const QString &mrl, const QString &plot, 1224 1254 return res; 1225 1255 } 1226 1256 } 1227 else if (pginfo->IsVideo()) 1228 bookmarkPresent = (pginfo->QueryBookmark() > 0); 1257 else if (useBookmark && pginfo->IsVideo()) 1258 { 1259 pginfo->SetAllowLastPlayPos(true); 1260 pginfo->SetIgnoreBookmark(false); 1261 bookmarkPresent = pginfo->QueryBookmark() > 0; 1262 lastPlayPresent = pginfo->QueryLastPlayPos() > 0; 1263 } 1229 1264 1230 if (useBookmark && bookmarkPresent)1265 if (useBookmark && (bookmarkPresent || lastPlayPresent)) 1231 1266 { 1232 1267 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack(); 1233 BookmarkDialog *bookmarkdialog = new BookmarkDialog(pginfo, mainStack); 1268 BookmarkDialog *bookmarkdialog = new BookmarkDialog(pginfo, mainStack, 1269 bookmarkPresent, 1270 lastPlayPresent); 1234 1271 if (!bookmarkdialog->Create()) 1235 1272 { 1236 1273 delete bookmarkdialog; … … static bool WasAutomaticStart(void) 1655 1692 } 1656 1693 1657 1694 // from https://www.raspberrypi.org/forums/viewtopic.php?f=33&t=16897 1658 // The old way of revoking root with setuid(getuid()) 1695 // The old way of revoking root with setuid(getuid()) 1659 1696 // causes system hang in certain cases on raspberry pi 1660 1697 1661 1698 static int revokeRoot (void) -
mythtv/programs/mythfrontend/playbackbox.cpp
diff --git a/mythtv/programs/mythfrontend/playbackbox.cpp b/mythtv/programs/mythfrontend/playbackbox.cpp index 34815cc..fe71685 100644
a b 40 40 #include "mythdb.h" 41 41 #include "mythdate.h" 42 42 #include "tv.h" 43 #include "mconcurrent.h" 43 44 44 45 #ifdef _MSC_VER 45 46 # include "compat.h" // for random … … bool PlaybackBox::Create() 562 563 connect(m_recordingList, SIGNAL(itemSelected(MythUIButtonListItem*)), 563 564 SLOT(ItemSelected(MythUIButtonListItem*))); 564 565 connect(m_recordingList, SIGNAL(itemClicked(MythUIButtonListItem*)), 565 SLOT(PlayFrom BookmarkOrProgStart(MythUIButtonListItem*)));566 SLOT(PlayFromAnyMark(MythUIButtonListItem*))); 566 567 connect(m_recordingList, SIGNAL(itemVisible(MythUIButtonListItem*)), 567 568 SLOT(ItemVisible(MythUIButtonListItem*))); 568 569 connect(m_recordingList, SIGNAL(itemLoaded(MythUIButtonListItem*)), … … void PlaybackBox::ItemVisible(MythUIButtonListItem *item) 1011 1012 // Flagging status (queued, running, no, yes) 1012 1013 item->DisplayState(extract_commflag_state(*pginfo), "commflagged"); 1013 1014 1015 item->SetProgress(0, 100, pginfo->GetProgressPercent()); 1016 1014 1017 MythUIButtonListItem *sel_item = item->parent()->GetItemCurrent(); 1015 1018 if ((item != sel_item) && item->GetImageFilename("preview").isEmpty() && 1016 1019 (asAvailable == pginfo->GetAvailableStatus())) … … void PlaybackBox::playSelectedPlaylist(bool _random) 2222 2225 this, new MythEvent("PLAY_PLAYLIST")); 2223 2226 } 2224 2227 2225 void PlaybackBox::PlayFrom BookmarkOrProgStart(MythUIButtonListItem *item)2228 void PlaybackBox::PlayFromAnyMark(MythUIButtonListItem *item) 2226 2229 { 2227 2230 if (!item) 2228 2231 item = m_recordingList->GetItemCurrent(); … … void PlaybackBox::PlayFromBookmarkOrProgStart(MythUIButtonListItem *item) 2234 2237 2235 2238 const bool ignoreBookmark = false; 2236 2239 const bool ignoreProgStart = false; 2237 const bool ignoreLastPlayPos = true;2240 const bool ignoreLastPlayPos = false; 2238 2241 const bool underNetworkControl = false; 2239 2242 if (pginfo) 2240 2243 PlayX(*pginfo, ignoreBookmark, ignoreProgStart, ignoreLastPlayPos, … … void PlaybackBox::PlayX(const ProgramInfo &pginfo, 2323 2326 Close(); 2324 2327 } 2325 2328 2329 void PlaybackBox::ClearBookmark() 2330 { 2331 ProgramInfo *pginfo = GetCurrentProgram(); 2332 if (pginfo) 2333 pginfo->SaveBookmark(0); 2334 } 2335 2336 void PlaybackBox::ClearLastPlayPos() 2337 { 2338 ProgramInfo *pginfo = GetCurrentProgram(); 2339 if (pginfo) 2340 pginfo->SaveLastPlayPos(0); 2341 } 2342 2326 2343 void PlaybackBox::StopSelected(void) 2327 2344 { 2328 2345 ProgramInfo *pginfo = GetCurrentProgram(); … … void PlaybackBox::selected(MythUIButtonListItem *item) 2401 2418 if (!item) 2402 2419 return; 2403 2420 2404 PlayFrom BookmarkOrProgStart(item);2421 PlayFromAnyMark(item); 2405 2422 } 2406 2423 2407 2424 void PlaybackBox::popupClosed(QString which, int result) … … bool PlaybackBox::Play( 2536 2553 QCoreApplication::postEvent( 2537 2554 this, new MythEvent("PLAY_PLAYLIST")); 2538 2555 } 2539 else2540 {2541 // User may have saved or deleted a bookmark2542 // requiring update of bookmark icon..2543 ProgramInfo *pginfo = m_programInfoCache.GetRecordingInfo(tvrec.GetRecordingID());2544 if (pginfo)2545 UpdateUIListItem(pginfo, true);2546 }2547 2556 2548 2557 if (m_needUpdate) 2549 2558 ScheduleUpdateUIList(); … … MythMenu* PlaybackBox::createPlayFromMenu() 2993 3002 MythMenu *menu = new MythMenu(title, this, "slotmenu"); 2994 3003 2995 3004 if (pginfo->IsBookmarkSet()) 3005 { 2996 3006 menu->AddItem(tr("Play from bookmark"), SLOT(PlayFromBookmark())); 3007 menu->AddItem(tr("Clear bookmark"), SLOT(ClearBookmark())); 3008 } 2997 3009 menu->AddItem(tr("Play from beginning"), SLOT(PlayFromBeginning())); 2998 3010 if (pginfo->QueryLastPlayPos()) 3011 { 2999 3012 menu->AddItem(tr("Play from last played position"), 3000 3013 SLOT(PlayFromLastPlayPos())); 3014 menu->AddItem(tr("Clear last played position"), 3015 SLOT(ClearLastPlayPos())); 3016 } 3001 3017 3002 3018 return menu; 3003 3019 } … … void PlaybackBox::ShowActionPopup(const ProgramInfo &pginfo) 3237 3253 m_popupMenu->AddItem(tr("Play from..."), NULL, createPlayFromMenu()); 3238 3254 else 3239 3255 m_popupMenu->AddItem(tr("Play"), 3240 SLOT(PlayFrom BookmarkOrProgStart()));3256 SLOT(PlayFromAnyMark())); 3241 3257 } 3242 3258 3243 3259 if (!m_player) … … bool PlaybackBox::keyPressEvent(QKeyEvent *event) 3966 3982 if (action == "DELETE") 3967 3983 deleteSelected(m_recordingList->GetItemCurrent()); 3968 3984 else if (action == ACTION_PLAYBACK) 3969 PlayFrom BookmarkOrProgStart();3985 PlayFromAnyMark(); 3970 3986 else if (action == "DETAILS" || action == "INFO") 3971 3987 ShowDetails(); 3972 3988 else if (action == "CUSTOMEDIT") … … void PlaybackBox::customEvent(QEvent *event) 4022 4038 { 4023 4039 ProgramInfo evinfo(me->ExtraDataList()); 4024 4040 if (evinfo.HasPathname() || evinfo.GetChanID()) 4025 HandleUpdateProgramInfoEvent(evinfo); 4041 { 4042 uint32_t flags = m_programInfoCache.Update(evinfo); 4043 if (flags != PIC_NO_ACTION) 4044 HandleUpdateItemEvent(evinfo.GetRecordingID(), flags); 4045 } 4026 4046 } 4027 4047 else if (recordingID && (tokens[1] == "ADD")) 4028 4048 { … … void PlaybackBox::customEvent(QEvent *event) 4073 4093 else if (message.startsWith("UPDATE_FILE_SIZE")) 4074 4094 { 4075 4095 QStringList tokens = message.simplified().split(" "); 4076 bool ok = false;4077 uint recordingID = 0;4078 uint64_t filesize = 0ULL;4079 4096 if (tokens.size() >= 3) 4080 4097 { 4081 recordingID = tokens[1].toUInt(); 4082 filesize = tokens[2].toLongLong(&ok); 4083 } 4084 if (recordingID && ok) 4085 { 4086 4087 HandleUpdateProgramInfoFileSizeEvent(recordingID, filesize); 4098 bool ok = false; 4099 uint recordingID = tokens[1].toUInt(); 4100 uint64_t filesize = tokens[2].toLongLong(&ok); 4101 if (ok) 4102 { 4103 // Delegate to background thread 4104 MConcurrent::run("UpdateFileSize", &m_programInfoCache, 4105 &ProgramInfoCache::UpdateFileSize, 4106 recordingID, filesize, PIC_NONE); 4107 } 4088 4108 } 4089 4109 } 4090 4110 else if (message == "UPDATE_UI_LIST") … … void PlaybackBox::customEvent(QEvent *event) 4097 4117 m_helper.ForceFreeSpaceUpdate(); 4098 4118 } 4099 4119 } 4120 else if (message.startsWith("UPDATE_UI_ITEM")) 4121 { 4122 QStringList tokens = message.simplified().split(" "); 4123 if (tokens.size() < 3) 4124 return; 4125 4126 uint recordingID = tokens[1].toUInt(); 4127 UpdateState flags = static_cast<UpdateState>(tokens[2].toUInt()); 4128 4129 if (flags != PIC_NO_ACTION) 4130 HandleUpdateItemEvent(recordingID, flags); 4131 } 4100 4132 else if (message == "UPDATE_USAGE_UI") 4101 4133 { 4102 4134 UpdateUsageUI(); … … void PlaybackBox::HandleRecordingAddEvent(const ProgramInfo &evinfo) 4415 4447 ScheduleUpdateUIList(); 4416 4448 } 4417 4449 4418 void PlaybackBox::HandleUpdate ProgramInfoEvent(const ProgramInfo &evinfo)4450 void PlaybackBox::HandleUpdateItemEvent(uint recordingId, uint flags) 4419 4451 { 4420 QString old_recgroup = m_programInfoCache.GetRecGroup( 4421 evinfo.GetRecordingID()); 4422 4423 if (!m_programInfoCache.Update(evinfo)) 4424 return; 4425 4426 // If the recording group has changed, reload lists from the recently 4427 // updated cache; if not, only update UI for the updated item 4428 if (evinfo.GetRecordingGroup() == old_recgroup) 4452 // Changing recording group full reload 4453 if (flags & PIC_RECGROUP_CHANGED) 4429 4454 { 4430 ProgramInfo *dst = FindProgramInUILists(evinfo); 4431 if (dst) 4432 UpdateUIListItem(dst, true); 4433 return; 4455 ScheduleUpdateUIList(); 4456 } 4457 else 4458 { 4459 ProgramInfo *pginfo = FindProgramInUILists(recordingId); 4460 if (pginfo) 4461 { 4462 bool genPreview = (flags & PIC_MARK_CHANGED); 4463 UpdateUIListItem(pginfo, genPreview); 4464 } 4434 4465 } 4435 4436 ScheduleUpdateUIList();4437 }4438 4439 void PlaybackBox::HandleUpdateProgramInfoFileSizeEvent(uint recordingID,4440 uint64_t filesize)4441 {4442 m_programInfoCache.UpdateFileSize(recordingID, filesize);4443 4444 ProgramInfo *dst = FindProgramInUILists(recordingID);4445 if (dst)4446 UpdateUIListItem(dst, false);4447 4466 } 4448 4467 4449 4468 void PlaybackBox::ScheduleUpdateUIList(void) -
mythtv/programs/mythfrontend/playbackbox.h
diff --git a/mythtv/programs/mythfrontend/playbackbox.h b/mythtv/programs/mythfrontend/playbackbox.h index 6b7e144..280a516 100644
a b class PlaybackBox : public ScheduleCommon 141 141 void ItemVisible(MythUIButtonListItem *item); 142 142 void ItemLoaded(MythUIButtonListItem *item); 143 143 void selected(MythUIButtonListItem *item); 144 void PlayFrom BookmarkOrProgStart(MythUIButtonListItem *item = NULL);144 void PlayFromAnyMark(MythUIButtonListItem *item = NULL); 145 145 void PlayFromBookmark(MythUIButtonListItem *item = NULL); 146 146 void PlayFromBeginning(MythUIButtonListItem *item = NULL); 147 147 void PlayFromLastPlayPos(MythUIButtonListItem *item = NULL); 148 148 void deleteSelected(MythUIButtonListItem *item); 149 149 void ClearBookmark(); 150 void ClearLastPlayPos(); 150 151 void SwitchList(void); 151 152 152 153 void ShowGroupPopup(void); … … class PlaybackBox : public ScheduleCommon 321 322 void HandlePreviewEvent(const QStringList &list); 322 323 void HandleRecordingRemoveEvent(uint recordingID); 323 324 void HandleRecordingAddEvent(const ProgramInfo &evinfo); 324 void HandleUpdateProgramInfoEvent(const ProgramInfo &evinfo); 325 void HandleUpdateProgramInfoFileSizeEvent(uint recordingID, uint64_t filesize); 325 void HandleUpdateItemEvent(uint recordingId, uint flags); 326 326 327 327 void ScheduleUpdateUIList(void); 328 328 void ShowMenu(void); -
mythtv/programs/mythfrontend/programinfocache.cpp
diff --git a/mythtv/programs/mythfrontend/programinfocache.cpp b/mythtv/programs/mythfrontend/programinfocache.cpp index 920190f..5a8c59f 100644
a b 10 10 #include "programinfo.h" 11 11 #include "remoteutil.h" 12 12 #include "mythevent.h" 13 #include "mythdb.h" 14 #include "mconcurrent.h" 13 15 14 16 #include <QCoreApplication> 15 17 #include <QRunnable> … … void ProgramInfoCache::ScheduleLoad(const bool updateUI) 73 75 } 74 76 } 75 77 78 void ProgramInfoCache::CalculateProgress(ProgramInfo &pg, int pos) 79 { 80 uint lastPlayPercent = 0; 81 if (pos > 0) 82 { 83 int total = 0; 84 85 switch (pg.GetRecordingStatus()) 86 { 87 case RecStatus::Recorded: 88 total = pg.QueryTotalFrames(); 89 break; 90 case RecStatus::Recording: 91 // Active recordings won't have total frames set yet. 92 total = pg.QueryLastFrameInPosMap(); 93 break; 94 default: 95 break; 96 } 97 98 lastPlayPercent = (total > pos) ? (100 * pos) / total : 0; 99 100 LOG(VB_GUI, LOG_DEBUG, QString("%1 %2 %3/%4 = %5%") 101 .arg(pg.GetRecordingID()).arg(pg.GetTitle()) 102 .arg(pos).arg(total).arg(lastPlayPercent)); 103 } 104 pg.SetProgressPercent(lastPlayPercent); 105 } 106 76 107 void ProgramInfoCache::Load(const bool updateUI) 77 108 { 78 109 QMutexLocker locker(&m_lock); … … void ProgramInfoCache::Load(const bool updateUI) 84 115 // we sort the list later anyway. 85 116 vector<ProgramInfo*> *tmp = RemoteGetRecordedList(0); 86 117 /**/ 118 119 // Calculate play positions for UI 120 if (tmp) 121 { 122 // Played progress 123 typedef QPair<uint, QDateTime> ProgId; 124 QHash<ProgId, uint> lastPlayFrames; 125 126 // Get all lastplaypos marks in a single lookup 127 MSqlQuery query(MSqlQuery::InitCon()); 128 query.prepare("SELECT chanid, starttime, mark " 129 "FROM recordedmarkup " 130 "WHERE type = :TYPE "); 131 query.bindValue(":TYPE", MARK_UTIL_LASTPLAYPOS); 132 133 if (query.exec()) 134 { 135 while (query.next()) 136 { 137 ProgId id = qMakePair(query.value(0).toUInt(), 138 MythDate::as_utc(query.value(1).toDateTime())); 139 lastPlayFrames[id] = query.value(2).toUInt(); 140 } 141 142 // Determine progress of each prog 143 foreach (ProgramInfo* pg, *tmp) 144 { 145 // Enable last play pos for all recordings 146 pg->SetAllowLastPlayPos(true); 147 148 ProgId id = qMakePair(pg->GetChanID(), 149 pg->GetRecordingStartTime()); 150 CalculateProgress(*pg, lastPlayFrames.value(id)); 151 } 152 } 153 else 154 MythDB::DBError("Watched progress", query); 155 } 156 87 157 locker.relock(); 88 158 89 159 free_vec(m_next_cache); … … void ProgramInfoCache::Refresh(void) 157 227 158 228 /** \brief Updates a ProgramInfo in the cache. 159 229 * \note This must only be called from the UI thread. 160 * \return True iff the ProgramInfo was in the cache and was updated.230 * \return Flags indicating the result of the update 161 231 */ 162 bool ProgramInfoCache::Update(const ProgramInfo &pginfo)232 uint32_t ProgramInfoCache::Update(const ProgramInfo& pginfo) 163 233 { 164 234 QMutexLocker locker(&m_lock); 165 235 166 Cache::iterator it = m_cache.find(pginfo.GetRecordingID()); 236 uint recordingId = pginfo.GetRecordingID(); 237 Cache::iterator it = m_cache.find(recordingId); 167 238 168 if (it != m_cache.end())169 (*it)->clone(pginfo, true);239 if (it == m_cache.end()) 240 return PIC_NO_ACTION; 170 241 171 return it != m_cache.end();172 } 242 ProgramInfo& pg = **it; 243 uint32_t flags = PIC_NONE; 173 244 174 /** \brief Updates a ProgramInfo in the cache. 175 * \note This must only be called from the UI thread. 176 * \return True iff the ProgramInfo was in the cache and was updated. 177 */ 178 bool ProgramInfoCache::UpdateFileSize(uint recordingID, uint64_t filesize) 179 { 180 QMutexLocker locker(&m_lock); 245 if (pginfo.GetBookmarkUpdate() != pg.GetBookmarkUpdate()) 246 flags |= PIC_MARK_CHANGED; 181 247 182 Cache::iterator it = m_cache.find(recordingID); 248 if (pginfo.GetRecordingGroup() != pg.GetRecordingGroup()) 249 flags |= PIC_RECGROUP_CHANGED; 183 250 184 if (it != m_cache.end()) 251 pg.clone(pginfo, true); 252 pg.SetAllowLastPlayPos(true); 253 254 if (flags & PIC_MARK_CHANGED) 185 255 { 186 (*it)->SetFilesize(filesize); 187 if (filesize) 188 (*it)->SetAvailableStatus(asAvailable, "PIC::UpdateFileSize"); 256 // Delegate this update to a background task 257 MConcurrent::run("UpdateProg", this, &ProgramInfoCache::UpdateFileSize, 258 recordingId, 0, flags); 259 // Ignore this update 260 flags = PIC_NO_ACTION; 189 261 } 190 262 191 return it != m_cache.end(); 263 LOG(VB_GUI, LOG_DEBUG, QString("Pg %1 %2 update state %3") 264 .arg(recordingId).arg(pg.GetTitle()).arg(flags)); 265 return flags; 192 266 } 193 267 194 /** \brief Returns the ProgramInfo::recgroup or an empty string if not found. 195 * \note This must only be called from the UI thread. 268 /** \brief Updates file size calculations of a ProgramInfo in the cache. 269 * \note This should only be run by a non-UI thread as it contains multiple 270 * Db queries 271 * \return True iff the ProgramInfo was in the cache and was updated. 196 272 */ 197 QString ProgramInfoCache::GetRecGroup(uint recordingID) const 273 void ProgramInfoCache::UpdateFileSize(uint recordingId, uint64_t filesize, 274 uint32_t flags) 198 275 { 199 276 QMutexLocker locker(&m_lock); 200 277 201 Cache::const_iterator it = m_cache.find(recordingID); 278 Cache::iterator it = m_cache.find(recordingId); 279 if (it == m_cache.end()) 280 return; 202 281 203 QString recgroup;204 if (it != m_cache.end()) 205 recgroup = (*it)->GetRecordingGroup();282 ProgramInfo& pg = **it; 283 284 CalculateProgress(pg, pg.QueryLastPlayPos()); 206 285 207 return recgroup; 286 if (filesize > 0) 287 { 288 // Filesize update 289 pg.SetFilesize(filesize); 290 pg.SetAvailableStatus(asAvailable, "PIC::UpdateFileSize"); 291 } 292 else // Info update 293 { 294 // Don't keep regenerating previews of files being played 295 QString byWhom; 296 if (pg.QueryIsInUse(byWhom) && byWhom.contains(QObject::tr("Playing"))) 297 flags &= ~PIC_MARK_CHANGED; 298 } 299 300 QString mesg = QString("UPDATE_UI_ITEM %1 %2").arg(recordingId).arg(flags); 301 QCoreApplication::postEvent(m_listener, new MythEvent(mesg)); 302 303 LOG(VB_GUI, LOG_DEBUG, mesg); 208 304 } 209 305 210 306 /** \brief Adds a ProgramInfo to the cache. … … QString ProgramInfoCache::GetRecGroup(uint recordingID) const 212 308 */ 213 309 void ProgramInfoCache::Add(const ProgramInfo &pginfo) 214 310 { 215 if (!pginfo.GetRecordingID() || Update(pginfo) )311 if (!pginfo.GetRecordingID() || Update(pginfo) != PIC_NO_ACTION) 216 312 return; 217 313 218 m_cache[pginfo.GetRecordingID()] = new ProgramInfo(pginfo); 314 QMutexLocker locker(&m_lock); 315 316 ProgramInfo* pg = new ProgramInfo(pginfo); 317 pg->SetAllowLastPlayPos(true); 318 m_cache[pginfo.GetRecordingID()] = pg; 219 319 } 220 320 221 321 /** \brief Marks a ProgramInfo in the cache for deletion on the next -
mythtv/programs/mythfrontend/programinfocache.h
diff --git a/mythtv/programs/mythfrontend/programinfocache.h b/mythtv/programs/mythfrontend/programinfocache.h index 3b95a74..4c8d5a4 100644
a b class ProgramInfoLoader; 20 20 class ProgramInfo; 21 21 class QObject; 22 22 23 typedef enum { 24 PIC_NONE = 0x00, 25 PIC_MARK_CHANGED = 0x01, 26 PIC_RECGROUP_CHANGED = 0x02, 27 PIC_NO_ACTION = 0x80, 28 } UpdateState; 29 23 30 class ProgramInfoCache 24 31 { 25 32 friend class ProgramInfoLoader; … … class ProgramInfoCache 35 42 void Refresh(void); 36 43 void Add(const ProgramInfo&); 37 44 bool Remove(uint recordingID); 38 bool Update(const ProgramInfo&); 39 bool UpdateFileSize(uint recordingID, uint64_t filesize); 40 QString GetRecGroup(uint recordingID) const; 45 uint32_t Update(const ProgramInfo& pginfo); 46 void UpdateFileSize(uint recordingID, uint64_t filesize, uint32_t flags); 41 47 void GetOrdered(vector<ProgramInfo*> &list, bool newest_first = false); 42 48 /// \note This must only be called from the UI thread. 43 49 bool empty(void) const { return m_cache.empty(); } … … class ProgramInfoCache 46 52 private: 47 53 void Load(const bool updateUI = true); 48 54 void Clear(void); 55 void CalculateProgress(ProgramInfo &pg, int playPos); 56 void LoadProgressMarks(); 49 57 50 58 private: 51 59 // NOTE: Hash would be faster for lookups and updates, but we need a sorted -
mythtv/programs/mythfrontend/viewscheduled.cpp
diff --git a/mythtv/programs/mythfrontend/viewscheduled.cpp b/mythtv/programs/mythfrontend/viewscheduled.cpp index 63f7881..81109fe 100644
a b void ViewScheduled::LoadList(bool useExistingData) 282 282 if (!useExistingData) 283 283 LoadFromScheduler(m_recList, m_conflictBool); 284 284 285 ProgramList::iterator pit = m_recList.begin();286 QString currentDate;287 285 m_recgroupList[m_defaultGroup] = ProgramList(false); 288 286 m_recgroupList[m_defaultGroup].setAutoDelete(false); 287 288 ProgramList::iterator pit = m_recList.begin(); 289 289 while (pit != m_recList.end()) 290 290 { 291 291 ProgramInfo *pginfo = *pit; 292 293 CalcRecordedPercent(*pginfo); 294 292 295 const RecStatus::Type recstatus = pginfo->GetRecordingStatus(); 293 296 if ((pginfo->GetRecordingEndTime() >= now || 294 297 pginfo->GetScheduledEndTime() >= now || … … void ViewScheduled::ChangeGroup(MythUIButtonListItem* item) 392 395 FillList(); 393 396 } 394 397 398 void ViewScheduled::UpdateUIListItem(MythUIButtonListItem* item, 399 const ProgramInfo &pginfo) 400 { 401 QString state; 402 403 const RecStatus::Type recstatus = pginfo.GetRecordingStatus(); 404 if (recstatus == RecStatus::Recording || 405 recstatus == RecStatus::Tuning) 406 state = "running"; 407 else if (recstatus == RecStatus::Conflict || 408 recstatus == RecStatus::Offline || 409 recstatus == RecStatus::TunerBusy || 410 recstatus == RecStatus::Failed || 411 recstatus == RecStatus::Failing || 412 recstatus == RecStatus::Aborted || 413 recstatus == RecStatus::Missed) 414 state = "error"; 415 else if (recstatus == RecStatus::WillRecord) 416 { 417 if (m_curinput == 0 || pginfo.GetInputID() == m_curinput) 418 { 419 if (pginfo.GetRecordingPriority2() < 0) 420 state = "warning"; 421 else 422 state = "normal"; 423 } 424 } 425 else if (recstatus == RecStatus::Repeat || 426 recstatus == RecStatus::NeverRecord || 427 recstatus == RecStatus::DontRecord || 428 (recstatus != RecStatus::DontRecord && 429 recstatus <= RecStatus::EarlierShowing)) 430 state = "disabled"; 431 else 432 state = "warning"; 433 434 InfoMap infoMap; 435 pginfo.ToMap(infoMap); 436 437 infoMap["progresspercent"] = ProgressString(pginfo); 438 439 item->SetTextFromMap(infoMap, state); 440 441 if (!infoMap["progresspercent"].isEmpty()) 442 item->SetProgress(0, 100, pginfo.GetProgressPercent()); 443 444 QString rating = QString::number(pginfo.GetStars(10)); 445 item->DisplayState(rating, "ratingstate"); 446 item->DisplayState(state, "status"); 447 } 448 395 449 void ViewScheduled::FillList() 396 450 { 397 451 m_schedulesList->Reset(); … … void ViewScheduled::FillList() 415 469 ProgramList::iterator pit = plist.begin(); 416 470 while (pit != plist.end()) 417 471 { 418 ProgramInfo *pginfo = *pit;419 if ( !pginfo)472 ProgramInfo* pginfo = *pit; 473 if (pginfo) 420 474 { 421 ++pit; 422 continue; 475 MythUIButtonListItem *item = 476 new MythUIButtonListItem(m_schedulesList,"", 477 qVariantFromValue(pginfo)); 478 UpdateUIListItem(item, *pginfo); 423 479 } 424 425 QString state;426 427 const RecStatus::Type recstatus = pginfo->GetRecordingStatus();428 if (recstatus == RecStatus::Recording ||429 recstatus == RecStatus::Tuning)430 state = "running";431 else if (recstatus == RecStatus::Conflict ||432 recstatus == RecStatus::Offline ||433 recstatus == RecStatus::TunerBusy ||434 recstatus == RecStatus::Failed ||435 recstatus == RecStatus::Failing ||436 recstatus == RecStatus::Aborted ||437 recstatus == RecStatus::Missed)438 state = "error";439 else if (recstatus == RecStatus::WillRecord)440 {441 if (m_curinput == 0 || pginfo->GetInputID() == m_curinput)442 {443 if (pginfo->GetRecordingPriority2() < 0)444 state = "warning";445 else446 state = "normal";447 }448 }449 else if (recstatus == RecStatus::Repeat ||450 recstatus == RecStatus::NeverRecord ||451 recstatus == RecStatus::DontRecord ||452 (recstatus != RecStatus::DontRecord &&453 recstatus <= RecStatus::EarlierShowing))454 state = "disabled";455 else456 state = "warning";457 458 MythUIButtonListItem *item =459 new MythUIButtonListItem(m_schedulesList,"",460 qVariantFromValue(pginfo));461 462 InfoMap infoMap;463 pginfo->ToMap(infoMap);464 item->SetTextFromMap(infoMap, state);465 466 QString rating = QString::number(pginfo->GetStars(10));467 item->DisplayState(rating, "ratingstate");468 item->DisplayState(state, "status");469 470 480 ++pit; 471 481 } 472 482 … … void ViewScheduled::FillList() 510 520 } 511 521 } 512 522 523 QString ViewScheduled::ProgressString(const ProgramInfo &pg) 524 { 525 // Only show progress value for active recordings 526 return pg.GetRecordingStatus() == RecStatus::Recording 527 ? QString::number(pg.GetProgressPercent()) : ""; 528 } 529 513 530 void ViewScheduled::updateInfo(MythUIButtonListItem *item) 514 531 { 515 532 if (!item) … … void ViewScheduled::updateInfo(MythUIButtonListItem *item) 520 537 { 521 538 InfoMap infoMap; 522 539 pginfo->ToMap(infoMap); 540 541 infoMap["progresspercent"] = ProgressString(*pginfo); 542 523 543 SetTextFromMap(infoMap); 524 544 525 545 MythUIStateType *ratingState = dynamic_cast<MythUIStateType*> … … void ViewScheduled::customEvent(QEvent *event) 600 620 MythEvent *me = (MythEvent *)event; 601 621 QString message = me->Message(); 602 622 603 if (message != "SCHEDULE_CHANGE") 604 return; 623 if (message == "SCHEDULE_CHANGE") 624 { 625 m_needFill = true; 605 626 606 m_needFill = true; 627 if (m_inEvent) 628 return; 607 629 608 if (m_inEvent) 609 return; 630 m_inEvent = true; 610 631 611 m_inEvent = true;632 LoadList(); 612 633 613 LoadList(); 634 m_inEvent = false; 635 } 636 else if (message.startsWith("UPDATE_FILE_SIZE")) 637 { 638 QStringList tokens = message.simplified().split(" "); 639 if (tokens.size() < 3) 640 return; 614 641 615 m_inEvent = false; 642 bool ok; 643 uint recordingID = tokens[1].toUInt(); 644 uint64_t filesize = tokens[2].toLongLong(&ok); 645 646 // Locate program 647 ProgramList::iterator pit = m_recList.begin(); 648 while (pit != m_recList.end()) 649 { 650 ProgramInfo* pginfo = *pit; 651 if (pginfo && pginfo->GetRecordingID() == recordingID) 652 { 653 // Update size & progress 654 pginfo->SetFilesize(filesize); 655 uint current = pginfo->GetProgressPercent(); 656 CalcRecordedPercent(*pginfo); 657 if (pginfo->GetProgressPercent() != current) 658 { 659 // Update display, if it's shown 660 MythUIButtonListItem *item = 661 m_schedulesList-> 662 GetItemByData(qVariantFromValue(pginfo)); 663 if (item) 664 { 665 UpdateUIListItem(item, *pginfo); 666 667 // Update selected item if necessary 668 MythUIButtonListItem *selected = 669 m_schedulesList->GetItemCurrent(); 670 if (item == selected) 671 updateInfo(selected); 672 } 673 } 674 break; 675 } 676 ++pit; 677 } 678 } 616 679 } 617 680 else if (event->type() == DialogCompletionEvent::kEventType) 618 681 { … … void ViewScheduled::customEvent(QEvent *event) 694 757 } 695 758 } 696 759 760 void ViewScheduled::CalcRecordedPercent(ProgramInfo &pg) 761 { 762 QDateTime start = pg.GetRecordingStartTime(); 763 int current = start.secsTo(MythDate::current()); 764 uint recordedPercent = 0; 765 int duration = 0; 766 if (current > 0) 767 { 768 // Recording stops at end of the final minute 769 duration = start.secsTo(pg.GetRecordingEndTime()) + 60; 770 recordedPercent = duration > current ? current * 100 / duration : 100; 771 } 772 pg.SetProgressPercent(recordedPercent); 773 LOG(VB_GUI, LOG_DEBUG, QString("%1 %2/%3 = %4%") 774 .arg(pg.GetTitle()).arg(current).arg(duration).arg(recordedPercent)); 775 } 776 697 777 ProgramInfo *ViewScheduled::GetCurrentProgram(void) const 698 778 { 699 779 MythUIButtonListItem *item = m_schedulesList->GetItemCurrent(); -
mythtv/programs/mythfrontend/viewscheduled.h
diff --git a/mythtv/programs/mythfrontend/viewscheduled.h b/mythtv/programs/mythfrontend/viewscheduled.h index 5d2e6a2..ce2fa6c 100644
a b class ViewScheduled : public ScheduleCommon 59 59 60 60 void EmbedTVWindow(void); 61 61 62 void CalcRecordedPercent(ProgramInfo &pg); 63 void UpdateUIListItem(MythUIButtonListItem* item, 64 const ProgramInfo &pginfo); 65 QString ProgressString(const ProgramInfo &pg); 66 62 67 bool m_conflictBool; 63 68 QDate m_conflictDate; 64 69 -
mythtv/themes/MythCenter-wide/recordings-ui.xml
diff --git a/mythtv/themes/MythCenter-wide/recordings-ui.xml b/mythtv/themes/MythCenter-wide/recordings-ui.xml index f5729a6..569e204 100644
a b 180 180 <statetype name="buttonitem"> 181 181 <state name="active"> 182 182 <area>0,0,880,30</area> 183 <progressbar name="buttonprogress"> 184 <area>0,0,100%,100%</area> 185 <layout>horizontal</layout> 186 <style>reveal</style> 187 <shape name="progressimage"> 188 <area>0,1,100%,100%-1</area> 189 <type>box</type> 190 <fill color="#000000" alpha="128"/> 191 </shape> 192 </progressbar> 183 193 <statetype name="status"> 184 194 <position>3,2</position> 185 195 <state name="disabled"> … … 212 222 <textarea name="titlesubtitle" from="buttontext"> 213 223 <area>32,2,656,28</area> 214 224 <align>vcenter</align> 225 <template>%titlesubtitle%% (|progresspercent|%)%</template> 215 226 </textarea> 216 <textarea name="shortstartdate" from=" titlesubtitle">227 <textarea name="shortstartdate" from="buttontext"> 217 228 <area>634,2,120,28</area> 218 229 <align>right,vcenter</align> 219 230 </textarea> … … 249 260 <shape name="selectbar"> 250 261 <area>0,0,100%,30</area> 251 262 </shape> 252 <textarea name="titlesubtitle" from="buttontext"> 253 <area>32,2,656,28</area> 263 <textarea name="fonts" from="buttontext"> 254 264 <font>basesmall_normal_selected</font> 255 265 <font state="disabled">basesmall_disabled_selected</font> 256 266 <font state="error">basesmall_error_selected</font> … … 259 269 <font state="running">basesmall_running_selected</font> 260 270 <align>vcenter</align> 261 271 </textarea> 262 <textarea name="shortstartdate" from="titlesubtitle"> 272 <textarea name="titlesubtitle" from="fonts"> 273 <area>32,2,656,28</area> 274 <template>%titlesubtitle%% (|progresspercent|%)%</template> 275 </textarea> 276 <textarea name="shortstartdate" from="fonts"> 263 277 <area>634,2,120,28</area> 264 278 <align>right,vcenter</align> 265 279 </textarea> 266 280 <textarea name="starttime" from="shortstartdate"> 267 281 <area>760,2,114,28</area> 268 <align>right,vcenter</align>269 282 </textarea> 270 283 </state> 271 284 </statetype> … … 382 395 <font>baselarge</font> 383 396 <cutdown>yes</cutdown> 384 397 <align>vcenter</align> 398 <template>%title%% (|progresspercent|%)%</template> 385 399 </textarea> 386 400 387 401 <textarea name="channel" from="basetextarea"> -
mythtv/themes/MythCenter-wide/schedule-ui.xml
diff --git a/mythtv/themes/MythCenter-wide/schedule-ui.xml b/mythtv/themes/MythCenter-wide/schedule-ui.xml index 9b1ff20..a6c00c6 100644
a b 513 513 <statetype name="buttonitem"> 514 514 <area>0,0,1200,24</area> 515 515 <state name="active"> 516 <progressbar name="buttonprogress"> 517 <area>10,2,1200,24</area> 518 <layout>horizontal</layout> 519 <style>reveal</style> 520 <shape name="progressimage"> 521 <area>0,0,100%,100%</area> 522 <type>box</type> 523 <fill color="#000000" alpha="128"/> 524 </shape> 525 </progressbar> 516 526 <textarea name="shortstarttimedate" from="buttontext"> 517 527 <area>10,2,250,24</area> 518 528 </textarea> … … 521 531 </textarea> 522 532 <textarea name="title" from="shortstarttimedate"> 523 533 <area>480,2,655,24</area> 534 <template>%title%% (|progresspercent|%)%</template> 524 535 </textarea> 525 536 <textarea name="card" from="shortstarttimedate"> 526 537 <area>1145,2,40,24</area> … … 542 553 </textarea> 543 554 <textarea name="title" from="shortstarttimedate"> 544 555 <area>480,2,655,24</area> 556 <template>%title%% (|progresspercent|%)%</template> 545 557 </textarea> 546 558 <textarea name="card" from="shortstarttimedate"> 547 559 <area>1145,2,40,24</area> … … 562 574 </buttonlist> 563 575 564 576 <textarea name="title" from="basetextarea"> 565 <area>30,454,1 200,50</area>577 <area>30,454,1000,50</area> 566 578 <font>baselarge</font> 567 579 </textarea> 568 580 581 <textarea name="progresspercent" from="basetextarea"> 582 <area>1180,454,70,50</area> 583 <align>right,vcenter</align> 584 <template>%progresspercent|%%</template> 585 </textarea> 586 569 587 <textarea name="channel" from="basetextarea"> 570 588 <area>30,494,360,30</area> 571 589 </textarea>
