From e9620af6fbfa2fe6b0d0411ac8c89aff9db7c3ea Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 25 Dec 2019 18:19:52 +0300 Subject: [PATCH] Save last playback position for long videos. --- Telegram/SourceFiles/main/main_settings.cpp | 47 +++++++++++++++++++ Telegram/SourceFiles/main/main_settings.h | 4 ++ .../media/view/media_view_overlay_widget.cpp | 21 ++++++++- 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/main/main_settings.cpp b/Telegram/SourceFiles/main/main_settings.cpp index 65e2dca24..acb3c1d04 100644 --- a/Telegram/SourceFiles/main/main_settings.cpp +++ b/Telegram/SourceFiles/main/main_settings.cpp @@ -21,6 +21,7 @@ constexpr auto kAutoLockTimeoutLateMs = crl::time(3000); constexpr auto kLegacyCallsPeerToPeerNobody = 4; constexpr auto kVersionTag = -1; constexpr auto kVersion = 1; +constexpr auto kMaxSavedPlaybackPositions = 16; } // namespace @@ -93,6 +94,10 @@ QByteArray Settings::serialize() const { stream << qint32(_variables.suggestEmoji ? 1 : 0); stream << qint32(_variables.suggestStickersByEmoji ? 1 : 0); stream << qint32(_variables.spellcheckerEnabled.current() ? 1 : 0); + stream << qint32(_variables.mediaLastPlaybackPosition.size()); + for (const auto &[id, time] : _variables.mediaLastPlaybackPosition) { + stream << quint64(id) << qint64(time); + } } return result; } @@ -142,6 +147,7 @@ void Settings::constructFromSerialized(const QByteArray &serialized) { qint32 suggestEmoji = _variables.suggestEmoji ? 1 : 0; qint32 suggestStickersByEmoji = _variables.suggestStickersByEmoji ? 1 : 0; qint32 spellcheckerEnabled = _variables.spellcheckerEnabled.current() ? 1 : 0; + std::vector> mediaLastPlaybackPosition; stream >> versionTag; if (versionTag == kVersionTag) { @@ -250,6 +256,18 @@ void Settings::constructFromSerialized(const QByteArray &serialized) { if (!stream.atEnd()) { stream >> spellcheckerEnabled; } + if (!stream.atEnd()) { + auto count = qint32(0); + stream >> count; + if (stream.status() == QDataStream::Ok) { + for (auto i = 0; i != count; ++i) { + quint64 documentId; + qint64 time; + stream >> documentId >> time; + mediaLastPlaybackPosition.emplace_back(documentId, time); + } + } + } if (stream.status() != QDataStream::Ok) { LOG(("App Error: " "Bad data for Main::Settings::constructFromSerialized()")); @@ -336,6 +354,7 @@ void Settings::constructFromSerialized(const QByteArray &serialized) { _variables.suggestEmoji = (suggestEmoji == 1); _variables.suggestStickersByEmoji = (suggestStickersByEmoji == 1); _variables.spellcheckerEnabled = (spellcheckerEnabled == 1); + _variables.mediaLastPlaybackPosition = std::move(mediaLastPlaybackPosition); } void Settings::setSupportChatsTimeSlice(int slice) { @@ -430,6 +449,34 @@ rpl::producer Settings::thirdColumnWidthChanges() const { return _variables.thirdColumnWidth.changes(); } +void Settings::setMediaLastPlaybackPosition(DocumentId id, crl::time time) { + auto &map = _variables.mediaLastPlaybackPosition; + const auto i = ranges::find( + map, + id, + &std::pair::first); + if (i != map.end()) { + if (time > 0) { + i->second = time; + } else { + map.erase(i); + } + } else if (time > 0) { + if (map.size() >= kMaxSavedPlaybackPositions) { + map.erase(map.begin()); + } + map.emplace_back(id, time); + } +} + +crl::time Settings::mediaLastPlaybackPosition(DocumentId id) const { + const auto i = ranges::find( + _variables.mediaLastPlaybackPosition, + id, + &std::pair::first); + return (i != _variables.mediaLastPlaybackPosition.end()) ? i->second : 0; +} + void Settings::setArchiveCollapsed(bool collapsed) { _variables.archiveCollapsed = collapsed; } diff --git a/Telegram/SourceFiles/main/main_settings.h b/Telegram/SourceFiles/main/main_settings.h index f7baaf450..4cf55aa8c 100644 --- a/Telegram/SourceFiles/main/main_settings.h +++ b/Telegram/SourceFiles/main/main_settings.h @@ -153,6 +153,9 @@ public: _variables.groupStickersSectionHidden.remove(peerId); } + void setMediaLastPlaybackPosition(DocumentId id, crl::time time); + [[nodiscard]] crl::time mediaLastPlaybackPosition(DocumentId id) const; + [[nodiscard]] Data::AutoDownload::Full &autoDownload() { return _variables.autoDownload; } @@ -277,6 +280,7 @@ private: bool suggestEmoji = true; bool suggestStickersByEmoji = true; rpl::variable spellcheckerEnabled = true; + std::vector> mediaLastPlaybackPosition; static constexpr auto kDefaultSupportChatsLimitSlice = 7 * 24 * 60 * 60; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index fb9c44d66..d6c9572f8 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -80,6 +80,8 @@ constexpr auto kIdsLimit = 48; // Preload next messages if we went further from current than that. constexpr auto kIdsPreloadAfter = 28; +constexpr auto kMinLengthForSavePosition = 20 * TimeId(60); // 20 minutes. + Images::Options VideoThumbOptions(not_null document) { const auto result = Images::Option::Smooth | Images::Option::Blurred; return (document && document->isVideoMessage()) @@ -435,6 +437,20 @@ bool OverlayWidget::documentBubbleShown() const { } void OverlayWidget::clearStreaming() { + if (_streamed && _doc) { + const auto state = _streamed->instance.player().prepareLegacyState(); + const auto time = (state.position == kTimeUnknown + || state.length == kTimeUnknown) + ? TimeId(0) + : (state.length >= kMinLengthForSavePosition * state.frequency) + ? (state.position / state.frequency) * crl::time(1000) + : TimeId(0); + auto &session = _doc->session(); + if (session.settings().mediaLastPlaybackPosition(_doc->id) != time) { + session.settings().setMediaLastPlaybackPosition(_doc->id, time); + session.saveSettingsDelayed(); + } + } _fullScreenVideo = false; _streamed = nullptr; } @@ -2018,7 +2034,10 @@ void OverlayWidget::startStreamingPlayer() { if (!_streamed->withSound && _streamed->instance.player().playing()) { return; } - restartAtSeekPosition(0); + const auto position = _doc + ? _doc->session().settings().mediaLastPlaybackPosition(_doc->id) + : 0; + restartAtSeekPosition(position); } void OverlayWidget::initStreamingThumbnail() {