mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-07-25 15:03:03 +02:00
Track shown sponsored in video.
This commit is contained in:
parent
e5ca9e4c39
commit
284cbda7c0
8 changed files with 345 additions and 32 deletions
|
@ -1257,6 +1257,8 @@ PRIVATE
|
|||
media/view/media_view_playback_controls.h
|
||||
media/view/media_view_playback_progress.cpp
|
||||
media/view/media_view_playback_progress.h
|
||||
media/view/media_view_playback_sponsored.cpp
|
||||
media/view/media_view_playback_sponsored.h
|
||||
media/system_media_controls_manager.h
|
||||
media/system_media_controls_manager.cpp
|
||||
menu/menu_antispam_validator.cpp
|
||||
|
|
|
@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Data {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMs = crl::time(1000);
|
||||
constexpr auto kRequestTimeLimit = 5 * 60 * crl::time(1000);
|
||||
|
||||
const auto kFlaggedPreload = ((MediaPreload*)quintptr(0x01));
|
||||
|
@ -288,7 +289,7 @@ void SponsoredMessages::request(not_null<History*> history, Fn<void()> done) {
|
|||
}).send();
|
||||
}
|
||||
|
||||
void SponsoredMessages::request(
|
||||
void SponsoredMessages::requestForVideo(
|
||||
not_null<HistoryItem*> item,
|
||||
Fn<void(SponsoredForVideo)> done) {
|
||||
Expects(done != nullptr);
|
||||
|
@ -297,14 +298,23 @@ void SponsoredMessages::request(
|
|||
done({});
|
||||
return;
|
||||
}
|
||||
const auto id = item->fullId();
|
||||
auto &request = _requestsForVideo[id];
|
||||
const auto peer = item->history()->peer;
|
||||
auto &request = _requestsForVideo[peer];
|
||||
if (TooEarlyForRequest(request.lastReceived)) {
|
||||
auto prepared = prepareForVideo(peer);
|
||||
if (prepared.list.empty()
|
||||
|| prepared.state.itemIndex < prepared.list.size()
|
||||
|| prepared.state.leftTillShow > 0) {
|
||||
done(std::move(prepared));
|
||||
return;
|
||||
}
|
||||
}
|
||||
request.callbacks.push_back(std::move(done));
|
||||
if (request.requestId) {
|
||||
done(prepareForVideo(id));
|
||||
return;
|
||||
}
|
||||
{
|
||||
const auto it = _dataForVideo.find(id);
|
||||
const auto it = _dataForVideo.find(peer);
|
||||
if (it != end(_dataForVideo)) {
|
||||
auto &list = it->second;
|
||||
// Don't rebuild currently displayed messages.
|
||||
|
@ -316,21 +326,41 @@ void SponsoredMessages::request(
|
|||
}
|
||||
}
|
||||
}
|
||||
const auto finish = [=] {
|
||||
const auto i = _requestsForVideo.find(peer);
|
||||
if (i != end(_requestsForVideo)) {
|
||||
for (const auto &callback : base::take(i->second.callbacks)) {
|
||||
callback(prepareForVideo(peer));
|
||||
}
|
||||
}
|
||||
};
|
||||
using Flag = MTPmessages_GetSponsoredMessages::Flag;
|
||||
request.requestId = _session->api().request(
|
||||
MTPmessages_GetSponsoredMessages(
|
||||
MTP_flags(Flag::f_msg_id),
|
||||
item->history()->peer->input,
|
||||
peer->input,
|
||||
MTP_int(item->id.bare))
|
||||
).done([=](const MTPmessages_sponsoredMessages &result) {
|
||||
parse(id, result);
|
||||
done(prepareForVideo(id));
|
||||
parseForVideo(peer, result);
|
||||
finish();
|
||||
}).fail([=] {
|
||||
_requestsForVideo.remove(id);
|
||||
done({});
|
||||
_requestsForVideo.remove(peer);
|
||||
finish();
|
||||
}).send();
|
||||
}
|
||||
|
||||
void SponsoredMessages::updateForVideo(
|
||||
FullMsgId itemId,
|
||||
SponsoredForVideoState state) {
|
||||
if (state.initial()) {
|
||||
return;
|
||||
}
|
||||
const auto i = _dataForVideo.find(_session->data().peer(itemId.peer));
|
||||
if (i != end(_dataForVideo)) {
|
||||
i->second.state = state;
|
||||
}
|
||||
}
|
||||
|
||||
void SponsoredMessages::parse(
|
||||
not_null<History*> history,
|
||||
const MTPmessages_sponsoredMessages &list) {
|
||||
|
@ -366,10 +396,10 @@ void SponsoredMessages::parse(
|
|||
});
|
||||
}
|
||||
|
||||
void SponsoredMessages::parse(
|
||||
FullMsgId itemId,
|
||||
void SponsoredMessages::parseForVideo(
|
||||
not_null<PeerData*> peer,
|
||||
const MTPmessages_sponsoredMessages &list) {
|
||||
auto &request = _requestsForVideo[itemId];
|
||||
auto &request = _requestsForVideo[peer];
|
||||
request.lastReceived = crl::now();
|
||||
request.requestId = 0;
|
||||
if (!_clearTimer.isActive()) {
|
||||
|
@ -380,24 +410,25 @@ void SponsoredMessages::parse(
|
|||
_session->data().processUsers(data.vusers());
|
||||
_session->data().processChats(data.vchats());
|
||||
|
||||
const auto history = _session->data().history(itemId.peer);
|
||||
const auto history = _session->data().history(peer);
|
||||
const auto &messages = data.vmessages().v;
|
||||
auto &list = _dataForVideo.emplace(itemId).first->second;
|
||||
auto &list = _dataForVideo.emplace(peer).first->second;
|
||||
list.entries.clear();
|
||||
list.received = crl::now();
|
||||
list.startDelay = data.vstart_delay().value_or_empty();
|
||||
list.betweenDelay = data.vbetween_delay().value_or_empty();
|
||||
list.startDelay = 2000;AssertIsDebug()// data.vstart_delay().value_or_empty() * kMs;
|
||||
list.betweenDelay = 3000;//data.vbetween_delay().value_or_empty() * kMs;
|
||||
for (const auto &message : messages) {
|
||||
append([=] {
|
||||
return &_dataForVideo[itemId].entries;
|
||||
return &_dataForVideo[peer].entries;
|
||||
}, history, message);
|
||||
}
|
||||
}, [](const MTPDmessages_sponsoredMessagesEmpty &) {
|
||||
});
|
||||
}
|
||||
|
||||
SponsoredForVideo SponsoredMessages::prepareForVideo(FullMsgId itemId) {
|
||||
const auto i = _dataForVideo.find(itemId);
|
||||
SponsoredForVideo SponsoredMessages::prepareForVideo(
|
||||
not_null<PeerData*> peer) {
|
||||
const auto i = _dataForVideo.find(peer);
|
||||
if (i == end(_dataForVideo) || i->second.entries.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
@ -407,6 +438,7 @@ SponsoredForVideo SponsoredMessages::prepareForVideo(FullMsgId itemId) {
|
|||
) | ranges::to_vector,
|
||||
.startDelay = i->second.startDelay,
|
||||
.betweenDelay = i->second.betweenDelay,
|
||||
.state = i->second.state,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -535,6 +567,8 @@ void SponsoredMessages::append(
|
|||
.link = from.link,
|
||||
.sponsorInfo = std::move(sponsorInfo),
|
||||
.additionalInfo = std::move(additionalInfo),
|
||||
.durationMin = 3000, AssertIsDebug()//data.vmin_display_duration().value_or_empty() * kMs,
|
||||
.durationMax = 5000,//data.vmax_display_duration().value_or_empty() * kMs,
|
||||
};
|
||||
const auto itemId = FullMsgId(
|
||||
history->peer->id,
|
||||
|
|
|
@ -71,6 +71,8 @@ struct SponsoredMessage {
|
|||
QString link;
|
||||
TextWithEntities sponsorInfo;
|
||||
TextWithEntities additionalInfo;
|
||||
crl::time durationMin = 0;
|
||||
crl::time durationMax = 0;
|
||||
};
|
||||
|
||||
struct SponsoredMessageDetails {
|
||||
|
@ -92,10 +94,21 @@ struct SponsoredReportAction {
|
|||
Fn<void(Data::SponsoredReportResult)>)> callback;
|
||||
};
|
||||
|
||||
struct SponsoredForVideoState {
|
||||
int itemIndex = 0;
|
||||
crl::time leftTillShow = 0;
|
||||
|
||||
[[nodiscard]] bool initial() const {
|
||||
return !itemIndex && !leftTillShow;
|
||||
}
|
||||
};
|
||||
|
||||
struct SponsoredForVideo {
|
||||
std::vector<SponsoredMessage> list;
|
||||
crl::time startDelay = 0;
|
||||
crl::time betweenDelay = 0;
|
||||
|
||||
SponsoredForVideoState state;
|
||||
};
|
||||
|
||||
class SponsoredMessages final {
|
||||
|
@ -120,9 +133,12 @@ public:
|
|||
[[nodiscard]] bool canHaveFor(not_null<HistoryItem*> item) const;
|
||||
[[nodiscard]] bool isTopBarFor(not_null<History*> history) const;
|
||||
void request(not_null<History*> history, Fn<void()> done);
|
||||
void request(
|
||||
void requestForVideo(
|
||||
not_null<HistoryItem*> item,
|
||||
Fn<void(SponsoredForVideo)> done);
|
||||
void updateForVideo(
|
||||
FullMsgId itemId,
|
||||
SponsoredForVideoState state);
|
||||
void clearItems(not_null<History*> history);
|
||||
[[nodiscard]] Details lookupDetails(const FullMsgId &fullId) const;
|
||||
[[nodiscard]] Details lookupDetails(
|
||||
|
@ -181,23 +197,30 @@ private:
|
|||
crl::time received = 0;
|
||||
crl::time startDelay = 0;
|
||||
crl::time betweenDelay = 0;
|
||||
SponsoredForVideoState state;
|
||||
};
|
||||
struct Request {
|
||||
mtpRequestId requestId = 0;
|
||||
crl::time lastReceived = 0;
|
||||
};
|
||||
struct RequestForVideo {
|
||||
std::vector<Fn<void(SponsoredForVideo)>> callbacks;
|
||||
mtpRequestId requestId = 0;
|
||||
crl::time lastReceived = 0;
|
||||
};
|
||||
|
||||
void parse(
|
||||
not_null<History*> history,
|
||||
const MTPmessages_sponsoredMessages &list);
|
||||
void parse(
|
||||
FullMsgId itemId,
|
||||
void parseForVideo(
|
||||
not_null<PeerData*> peer,
|
||||
const MTPmessages_sponsoredMessages &list);
|
||||
void append(
|
||||
Fn<not_null<std::vector<Entry>*>()> entries,
|
||||
not_null<History*> history,
|
||||
const MTPSponsoredMessage &message);
|
||||
[[nodiscard]] SponsoredForVideo prepareForVideo(FullMsgId itemId);
|
||||
[[nodiscard]] SponsoredForVideo prepareForVideo(
|
||||
not_null<PeerData*> peer);
|
||||
void clearOldRequests();
|
||||
|
||||
const Entry *find(const FullMsgId &fullId) const;
|
||||
|
@ -209,8 +232,8 @@ private:
|
|||
base::flat_map<not_null<History*>, Request> _requests;
|
||||
base::flat_map<RandomId, Request> _viewRequests;
|
||||
|
||||
base::flat_map<FullMsgId, ListForVideo> _dataForVideo;
|
||||
base::flat_map<FullMsgId, Request> _requestsForVideo;
|
||||
base::flat_map<not_null<PeerData*>, ListForVideo> _dataForVideo;
|
||||
base::flat_map<not_null<PeerData*>, RequestForVideo> _requestsForVideo;
|
||||
|
||||
rpl::event_stream<FullMsgId> _itemRemoved;
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "media/view/media_view_pip.h"
|
||||
#include "media/view/media_view_overlay_raster.h"
|
||||
#include "media/view/media_view_overlay_opengl.h"
|
||||
#include "media/view/media_view_playback_sponsored.h"
|
||||
#include "media/stories/media_stories_share.h"
|
||||
#include "media/stories/media_stories_view.h"
|
||||
#include "media/streaming/media_streaming_document.h"
|
||||
|
@ -335,6 +336,7 @@ struct OverlayWidget::Streamed {
|
|||
|
||||
Streaming::Instance instance;
|
||||
std::unique_ptr<PlaybackControls> controls;
|
||||
std::unique_ptr<PlaybackSponsored> sponsored;
|
||||
std::unique_ptr<base::PowerSaveBlocker> powerSaveBlocker;
|
||||
|
||||
bool ready = false;
|
||||
|
@ -4069,6 +4071,9 @@ void OverlayWidget::initStreamingThumbnail() {
|
|||
|
||||
void OverlayWidget::streamingReady(Streaming::Information &&info) {
|
||||
_streamed->ready = true;
|
||||
if (const auto sponsored = _streamed->sponsored.get()) {
|
||||
sponsored->start();
|
||||
}
|
||||
if (videoShown()) {
|
||||
applyVideoSize();
|
||||
_streamedQualityChangeFrame = QImage();
|
||||
|
@ -4090,6 +4095,7 @@ void OverlayWidget::applyVideoSize() {
|
|||
|
||||
bool OverlayWidget::createStreamingObjects() {
|
||||
Expects(_photo || _document);
|
||||
Expects(!_streamed);
|
||||
|
||||
const auto origin = fileOrigin();
|
||||
const auto callback = [=] { waitingAnimationCallback(); };
|
||||
|
@ -4122,6 +4128,15 @@ bool OverlayWidget::createStreamingObjects() {
|
|||
_body,
|
||||
static_cast<PlaybackControls::Delegate*>(this));
|
||||
_streamed->controls->show();
|
||||
_streamed->sponsored = PlaybackSponsored::Has(_message)
|
||||
? std::make_unique<PlaybackSponsored>(_body, _message)
|
||||
: nullptr;
|
||||
if (const auto sponsored = _streamed->sponsored.get()) {
|
||||
_layerBg->layerShownValue(
|
||||
) | rpl::start_with_next([=](bool shown) {
|
||||
sponsored->setPaused(shown);
|
||||
}, sponsored->lifetime());
|
||||
}
|
||||
refreshClipControllerGeometry();
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -78,6 +78,7 @@ struct ContentLayout;
|
|||
|
||||
namespace Media::View {
|
||||
|
||||
class PlaybackSponsored;
|
||||
class GroupThumbs;
|
||||
class Pip;
|
||||
|
||||
|
|
|
@ -19,14 +19,13 @@ class MediaSlider;
|
|||
class PopupMenu;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Media {
|
||||
namespace Player {
|
||||
namespace Media::Player {
|
||||
struct TrackState;
|
||||
class SettingsButton;
|
||||
class SpeedController;
|
||||
} // namespace Player
|
||||
} // namespace Media::Player
|
||||
|
||||
namespace View {
|
||||
namespace Media::View {
|
||||
|
||||
class PlaybackProgress;
|
||||
|
||||
|
@ -131,5 +130,4 @@ private:
|
|||
|
||||
};
|
||||
|
||||
} // namespace View
|
||||
} // namespace Media
|
||||
} // namespace Media::View
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "media/view/media_view_playback_sponsored.h"
|
||||
|
||||
#include "data/components/sponsored_messages.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
namespace Media::View {
|
||||
namespace {
|
||||
|
||||
constexpr auto kStartDelayMin = crl::time(1000);
|
||||
constexpr auto kDurationMin = 5 * crl::time(1000);
|
||||
|
||||
} // namespace
|
||||
|
||||
PlaybackSponsored::PlaybackSponsored(
|
||||
QWidget *parent,
|
||||
not_null<HistoryItem*> item)
|
||||
: _parent(parent)
|
||||
, _session(&item->history()->session())
|
||||
, _itemId(item->fullId())
|
||||
, _timer([=] { update(); }) {
|
||||
_session->sponsoredMessages().requestForVideo(item, crl::guard(this, [=](
|
||||
Data::SponsoredForVideo data) {
|
||||
if (data.list.empty()) {
|
||||
return;
|
||||
}
|
||||
_data = std::move(data);
|
||||
if (_data->state.initial()
|
||||
|| (_data->state.itemIndex > _data->list.size())
|
||||
|| (_data->state.itemIndex == _data->list.size()
|
||||
&& _data->state.leftTillShow <= 0)) {
|
||||
_data->state.itemIndex = 0;
|
||||
_data->state.leftTillShow = std::max(
|
||||
_data->startDelay,
|
||||
kStartDelayMin);
|
||||
}
|
||||
update();
|
||||
}));
|
||||
}
|
||||
|
||||
PlaybackSponsored::~PlaybackSponsored() {
|
||||
saveState();
|
||||
}
|
||||
|
||||
void PlaybackSponsored::start() {
|
||||
_started = true;
|
||||
if (!_paused) {
|
||||
_start = crl::now();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void PlaybackSponsored::setPaused(bool paused) {
|
||||
if (_paused == paused) {
|
||||
return;
|
||||
}
|
||||
_paused = paused;
|
||||
if (!_started) {
|
||||
return;
|
||||
} else if (_paused) {
|
||||
update();
|
||||
const auto state = computeState();
|
||||
_start = 0;
|
||||
_timer.cancel();
|
||||
} else {
|
||||
_start = crl::now();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void PlaybackSponsored::finish() {
|
||||
_timer.cancel();
|
||||
if (_data) {
|
||||
saveState();
|
||||
_data = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
void PlaybackSponsored::update() {
|
||||
if (!_data || !_start) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto [now, state] = computeState();
|
||||
const auto message = (_data->state.itemIndex < _data->list.size())
|
||||
? &_data->list[state.itemIndex]
|
||||
: nullptr;
|
||||
const auto duration = message
|
||||
? std::max(
|
||||
message->durationMin + kDurationMin,
|
||||
message->durationMax)
|
||||
: crl::time(0);
|
||||
if (_data->state.leftTillShow > 0 && state.leftTillShow <= 0) {
|
||||
_data->state.leftTillShow = 0;
|
||||
if (duration) {
|
||||
show(*message);
|
||||
|
||||
_start = now;
|
||||
_timer.callOnce(duration);
|
||||
saveState();
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
} else if (_data->state.leftTillShow <= 0
|
||||
&& state.leftTillShow <= -duration) {
|
||||
hide();
|
||||
|
||||
++_data->state.itemIndex;
|
||||
_data->state.leftTillShow = std::max(
|
||||
_data->betweenDelay,
|
||||
kStartDelayMin);
|
||||
_start = now;
|
||||
_timer.callOnce(_data->state.leftTillShow);
|
||||
saveState();
|
||||
} else {
|
||||
if (state.leftTillShow <= 0 && duration && !_widget) {
|
||||
show(*message);
|
||||
}
|
||||
_data->state = state;
|
||||
_timer.callOnce((state.leftTillShow > 0)
|
||||
? state.leftTillShow
|
||||
: (state.leftTillShow + duration));
|
||||
}
|
||||
}
|
||||
|
||||
void PlaybackSponsored::show(const Data::SponsoredMessage &data) {
|
||||
_widget = std::make_unique<Ui::RpWidget>(_parent);
|
||||
_widget->setGeometry(_parent->rect());
|
||||
_widget->paintRequest() | rpl::start_with_next([=] {
|
||||
auto p = QPainter(_widget.get());
|
||||
p.fillRect(_widget->rect(), QColor(0, 128, 0, 128));
|
||||
}, _widget->lifetime());
|
||||
_widget->show();
|
||||
}
|
||||
|
||||
void PlaybackSponsored::hide() {
|
||||
_widget = nullptr;
|
||||
}
|
||||
|
||||
void PlaybackSponsored::saveState() {
|
||||
_session->sponsoredMessages().updateForVideo(
|
||||
_itemId,
|
||||
computeState().data);
|
||||
}
|
||||
|
||||
PlaybackSponsored::State PlaybackSponsored::computeState() const {
|
||||
auto result = State{ crl::now() };
|
||||
if (!_data) {
|
||||
return result;
|
||||
}
|
||||
result.data = _data->state;
|
||||
if (!_start) {
|
||||
return result;
|
||||
}
|
||||
const auto elapsed = result.now - _start;
|
||||
result.data.leftTillShow -= elapsed;
|
||||
return result;
|
||||
}
|
||||
|
||||
rpl::lifetime &PlaybackSponsored::lifetime() {
|
||||
return _lifetime;
|
||||
}
|
||||
|
||||
bool PlaybackSponsored::Has(HistoryItem *item) {
|
||||
return item
|
||||
&& item->history()->session().sponsoredMessages().canHaveFor(item);
|
||||
}
|
||||
|
||||
} // namespace Media::View
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/timer.h"
|
||||
#include "base/weak_ptr.h"
|
||||
#include "data/components/sponsored_messages.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Media::View {
|
||||
|
||||
class PlaybackSponsored final : public base::has_weak_ptr {
|
||||
public:
|
||||
PlaybackSponsored(QWidget *parent, not_null<HistoryItem*> item);
|
||||
~PlaybackSponsored();
|
||||
|
||||
void start();
|
||||
void setPaused(bool paused);
|
||||
|
||||
[[nodiscard]] rpl::lifetime &lifetime();
|
||||
|
||||
[[nodiscard]] static bool Has(HistoryItem *item);
|
||||
|
||||
private:
|
||||
struct State {
|
||||
crl::time now = 0;
|
||||
Data::SponsoredForVideoState data;
|
||||
};
|
||||
|
||||
void update();
|
||||
void finish();
|
||||
void show(const Data::SponsoredMessage &data);
|
||||
void hide();
|
||||
[[nodiscard]] State computeState() const;
|
||||
void saveState();
|
||||
|
||||
const not_null<QWidget*> _parent;
|
||||
const not_null<Main::Session*> _session;
|
||||
const FullMsgId _itemId;
|
||||
|
||||
std::unique_ptr<Ui::RpWidget> _widget;
|
||||
|
||||
crl::time _start = 0;
|
||||
bool _started = false;
|
||||
bool _paused = false;
|
||||
base::Timer _timer;
|
||||
|
||||
std::optional<Data::SponsoredForVideo> _data;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Media::View
|
Loading…
Add table
Reference in a new issue