mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-13 04:37:11 +02:00
Open video from ?t= links.
This commit is contained in:
parent
1e77a3df20
commit
2077f51084
14 changed files with 86 additions and 74 deletions
|
@ -317,6 +317,7 @@ PreviewWrap::PreviewWrap(
|
|||
0, // duration
|
||||
QString(), // author
|
||||
false, // hasLargeMedia
|
||||
false, // photoIsVideoCover
|
||||
0)) // pendingTill
|
||||
, _theme(theme)
|
||||
, _style(style)
|
||||
|
|
|
@ -598,6 +598,8 @@ bool ResolveUsernameOrPhone(
|
|||
const auto threadParam = params.value(u"thread"_q);
|
||||
const auto threadId = topicId ? topicId : threadParam.toInt();
|
||||
const auto gameParam = params.value(u"game"_q);
|
||||
const auto videot = params.value(u"t"_q);
|
||||
|
||||
if (!gameParam.isEmpty() && validDomain(gameParam)) {
|
||||
startToken = gameParam;
|
||||
resolveType = ResolveType::ShareGame;
|
||||
|
@ -614,6 +616,9 @@ bool ResolveUsernameOrPhone(
|
|||
.phone = phone,
|
||||
.messageId = post,
|
||||
.storyId = storyId,
|
||||
.videoTimestamp = (!videot.isEmpty()
|
||||
? ParseVideoTimestamp(videot)
|
||||
: std::optional<TimeId>()),
|
||||
.text = params.value(u"text"_q),
|
||||
.repliesInfo = commentId
|
||||
? Window::RepliesByLinkInfo{
|
||||
|
@ -1747,4 +1752,14 @@ void ResolveAndShowUniqueGift(
|
|||
ResolveAndShowUniqueGift(std::move(show), slug, {});
|
||||
}
|
||||
|
||||
TimeId ParseVideoTimestamp(QStringView value) {
|
||||
const auto kExp = u"^(?:(\\d+)h)?(?:(\\d+)m)?(?:(\\d+)s)?$"_q;
|
||||
const auto m = QRegularExpression(kExp).match(value);
|
||||
return m.hasMatch()
|
||||
? (m.capturedView(1).toInt() * 3600
|
||||
+ m.capturedView(2).toInt() * 60
|
||||
+ m.capturedView(3).toInt())
|
||||
: value.toInt();
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
|
|
@ -50,4 +50,6 @@ void ResolveAndShowUniqueGift(
|
|||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
const QString &slug);
|
||||
|
||||
[[nodiscard]] TimeId ParseVideoTimestamp(QStringView value);
|
||||
|
||||
} // namespace Core
|
||||
|
|
|
@ -3469,6 +3469,7 @@ not_null<WebPageData*> Session::processWebpage(
|
|||
0,
|
||||
QString(),
|
||||
false,
|
||||
false,
|
||||
data.vdate().v
|
||||
? data.vdate().v
|
||||
: (base::unixtime::now() + kDefaultPendingTimeout));
|
||||
|
@ -3496,6 +3497,7 @@ not_null<WebPageData*> Session::webpage(
|
|||
0,
|
||||
QString(),
|
||||
false,
|
||||
false,
|
||||
TimeId(0));
|
||||
}
|
||||
|
||||
|
@ -3516,6 +3518,7 @@ not_null<WebPageData*> Session::webpage(
|
|||
int duration,
|
||||
const QString &author,
|
||||
bool hasLargeMedia,
|
||||
bool photoIsVideoCover,
|
||||
TimeId pendingTill) {
|
||||
const auto result = webpage(id);
|
||||
webpageApplyFields(
|
||||
|
@ -3536,6 +3539,7 @@ not_null<WebPageData*> Session::webpage(
|
|||
duration,
|
||||
author,
|
||||
hasLargeMedia,
|
||||
photoIsVideoCover,
|
||||
pendingTill);
|
||||
return result;
|
||||
}
|
||||
|
@ -3723,6 +3727,7 @@ void Session::webpageApplyFields(
|
|||
data.vduration().value_or_empty(),
|
||||
qs(data.vauthor().value_or_empty()),
|
||||
data.is_has_large_media(),
|
||||
false, // photo_is_video_cover
|
||||
pendingTill);
|
||||
}
|
||||
|
||||
|
@ -3744,6 +3749,7 @@ void Session::webpageApplyFields(
|
|||
int duration,
|
||||
const QString &author,
|
||||
bool hasLargeMedia,
|
||||
bool photoIsVideoCover,
|
||||
TimeId pendingTill) {
|
||||
const auto requestPending = (!page->pendingTill && pendingTill > 0);
|
||||
const auto changed = page->applyChanges(
|
||||
|
@ -3763,6 +3769,7 @@ void Session::webpageApplyFields(
|
|||
duration,
|
||||
author,
|
||||
hasLargeMedia,
|
||||
photoIsVideoCover,
|
||||
pendingTill);
|
||||
if (requestPending) {
|
||||
_session->api().requestWebPageDelayed(page);
|
||||
|
|
|
@ -631,6 +631,7 @@ public:
|
|||
int duration,
|
||||
const QString &author,
|
||||
bool hasLargeMedia,
|
||||
bool photoIsVideoCover,
|
||||
TimeId pendingTill);
|
||||
|
||||
[[nodiscard]] not_null<GameData*> game(GameId id);
|
||||
|
@ -916,6 +917,7 @@ private:
|
|||
int duration,
|
||||
const QString &author,
|
||||
bool hasLargeMedia,
|
||||
bool photoIsVideoCover,
|
||||
TimeId pendingTill);
|
||||
|
||||
void gameApplyFields(
|
||||
|
|
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_photo.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_document.h"
|
||||
#include "core/local_url_handlers.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "iv/iv_data.h"
|
||||
#include "ui/image/image.h"
|
||||
|
@ -228,6 +229,7 @@ bool WebPageData::applyChanges(
|
|||
int newDuration,
|
||||
const QString &newAuthor,
|
||||
bool newHasLargeMedia,
|
||||
bool newPhotoIsVideoCover,
|
||||
int newPendingTill) {
|
||||
if (newPendingTill != 0
|
||||
&& (!url.isEmpty() || failed)
|
||||
|
@ -265,6 +267,9 @@ bool WebPageData::applyChanges(
|
|||
|| (hasSiteName + hasTitle + hasDescription < 2)) {
|
||||
newHasLargeMedia = false;
|
||||
}
|
||||
if (!newDocument || !newDocument->isVideoFile() || !newPhoto) {
|
||||
newPhotoIsVideoCover = false;
|
||||
}
|
||||
|
||||
if (type == newType
|
||||
&& url == resultUrl
|
||||
|
@ -283,6 +288,7 @@ bool WebPageData::applyChanges(
|
|||
&& duration == newDuration
|
||||
&& author == resultAuthor
|
||||
&& hasLargeMedia == (newHasLargeMedia ? 1 : 0)
|
||||
&& photoIsVideoCover == (newPhotoIsVideoCover ? 1 : 0)
|
||||
&& pendingTill == newPendingTill) {
|
||||
return false;
|
||||
}
|
||||
|
@ -291,6 +297,7 @@ bool WebPageData::applyChanges(
|
|||
}
|
||||
type = newType;
|
||||
hasLargeMedia = newHasLargeMedia ? 1 : 0;
|
||||
photoIsVideoCover = newPhotoIsVideoCover ? 1 : 0;
|
||||
url = resultUrl;
|
||||
displayUrl = resultDisplayUrl;
|
||||
siteName = resultSiteName;
|
||||
|
@ -383,14 +390,7 @@ TimeId WebPageData::extractVideoTimestamp() const {
|
|||
const auto parts = params.split('&');
|
||||
for (const auto &part : parts) {
|
||||
if (part.startsWith(u"t="_q)) {
|
||||
const auto value = part.mid(2);
|
||||
const auto kExp = u"^(?:(\\d+)h)?(?:(\\d+)m)?(?:(\\d+)s)?$"_q;
|
||||
const auto m = QRegularExpression(kExp).match(value);
|
||||
return m.hasMatch()
|
||||
? (m.capturedView(1).toInt() * 3600
|
||||
+ m.capturedView(2).toInt() * 60
|
||||
+ m.capturedView(3).toInt())
|
||||
: value.toInt();
|
||||
return Core::ParseVideoTimestamp(part.mid(2));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -106,6 +106,7 @@ struct WebPageData {
|
|||
int newDuration,
|
||||
const QString &newAuthor,
|
||||
bool newHasLargeMedia,
|
||||
bool newPhotoIsVideoCover,
|
||||
int newPendingTill);
|
||||
|
||||
static void ApplyChanges(
|
||||
|
@ -135,7 +136,8 @@ struct WebPageData {
|
|||
std::shared_ptr<Data::UniqueGift> uniqueGift;
|
||||
int duration = 0;
|
||||
TimeId pendingTill = 0;
|
||||
uint32 version : 30 = 0;
|
||||
uint32 version : 29 = 0;
|
||||
uint32 photoIsVideoCover : 1 = 0;
|
||||
uint32 hasLargeMedia : 1 = 0;
|
||||
uint32 failed : 1 = 0;
|
||||
|
||||
|
|
|
@ -726,6 +726,7 @@ HistoryItem::HistoryItem(
|
|||
0,
|
||||
QString(),
|
||||
false,
|
||||
false,
|
||||
0);
|
||||
auto webpageMedia = std::make_unique<Data::MediaWebPage>(
|
||||
this,
|
||||
|
|
|
@ -2207,15 +2207,12 @@ bool HistoryWidget::insideJumpToEndInsteadOfToUnread() const {
|
|||
}
|
||||
|
||||
void HistoryWidget::showHistory(
|
||||
const PeerId &peerId,
|
||||
PeerId peerId,
|
||||
MsgId showAtMsgId,
|
||||
const TextWithEntities &highlightPart,
|
||||
int highlightPartOffsetHint) {
|
||||
|
||||
const Window::SectionShow ¶ms) {
|
||||
_pinnedClickedId = FullMsgId();
|
||||
_minPinnedId = std::nullopt;
|
||||
_showAtMsgHighlightPart = {};
|
||||
_showAtMsgHighlightPartOffsetHint = 0;
|
||||
_showAtMsgParams = {};
|
||||
|
||||
const auto wasState = controller()->dialogsEntryStateCurrent();
|
||||
const auto startBot = (showAtMsgId == ShowAndStartBotMsgId);
|
||||
|
@ -2265,16 +2262,10 @@ void HistoryWidget::showHistory(
|
|||
).arg(_history->inboxReadTillId().bare
|
||||
).arg(Logs::b(_history->loadedAtBottom())
|
||||
).arg(showAtMsgId.bare));
|
||||
delayedShowAt(
|
||||
showAtMsgId,
|
||||
highlightPart,
|
||||
highlightPartOffsetHint);
|
||||
delayedShowAt(showAtMsgId, params);
|
||||
} else if (_showAtMsgId != showAtMsgId) {
|
||||
clearAllLoadRequests();
|
||||
setMsgId(
|
||||
showAtMsgId,
|
||||
highlightPart,
|
||||
highlightPartOffsetHint);
|
||||
setMsgId(showAtMsgId, params);
|
||||
firstLoadMessages();
|
||||
doneShow();
|
||||
}
|
||||
|
@ -2294,10 +2285,7 @@ void HistoryWidget::showHistory(
|
|||
_cornerButtons.skipReplyReturn(skipId);
|
||||
}
|
||||
|
||||
setMsgId(
|
||||
showAtMsgId,
|
||||
highlightPart,
|
||||
highlightPartOffsetHint);
|
||||
setMsgId(showAtMsgId, params);
|
||||
if (_historyInited) {
|
||||
DEBUG_LOG(("JumpToEnd(%1, %2, %3): "
|
||||
"Showing instant at %4."
|
||||
|
@ -2402,8 +2390,7 @@ void HistoryWidget::showHistory(
|
|||
clearInlineBot();
|
||||
|
||||
_showAtMsgId = showAtMsgId;
|
||||
_showAtMsgHighlightPart = highlightPart;
|
||||
_showAtMsgHighlightPartOffsetHint = highlightPartOffsetHint;
|
||||
_showAtMsgParams = params;
|
||||
_historyInited = false;
|
||||
_contactStatus = nullptr;
|
||||
_businessBotStatus = nullptr;
|
||||
|
@ -3642,10 +3629,7 @@ void HistoryWidget::messagesReceived(
|
|||
}
|
||||
|
||||
_delayedShowAtRequest = 0;
|
||||
setMsgId(
|
||||
_delayedShowAtMsgId,
|
||||
_delayedShowAtMsgHighlightPart,
|
||||
_delayedShowAtMsgHighlightPartOffsetHint);
|
||||
setMsgId(_delayedShowAtMsgId, _delayedShowAtMsgParams);
|
||||
historyLoaded();
|
||||
}
|
||||
if (session().supportMode()) {
|
||||
|
@ -3897,15 +3881,11 @@ void HistoryWidget::loadMessagesDown() {
|
|||
|
||||
void HistoryWidget::delayedShowAt(
|
||||
MsgId showAtMsgId,
|
||||
const TextWithEntities &highlightPart,
|
||||
int highlightPartOffsetHint) {
|
||||
const Window::SectionShow ¶ms) {
|
||||
if (!_history) {
|
||||
return;
|
||||
}
|
||||
if (_delayedShowAtMsgHighlightPart != highlightPart) {
|
||||
_delayedShowAtMsgHighlightPart = highlightPart;
|
||||
}
|
||||
_delayedShowAtMsgHighlightPartOffsetHint = highlightPartOffsetHint;
|
||||
_delayedShowAtMsgParams = params;
|
||||
if (_delayedShowAtRequest && _delayedShowAtMsgId == showAtMsgId) {
|
||||
return;
|
||||
}
|
||||
|
@ -4539,12 +4519,8 @@ PeerData *HistoryWidget::peer() const {
|
|||
// Sometimes _showAtMsgId is set directly.
|
||||
void HistoryWidget::setMsgId(
|
||||
MsgId showAtMsgId,
|
||||
const TextWithEntities &highlightPart,
|
||||
int highlightPartOffsetHint) {
|
||||
if (_showAtMsgHighlightPart != highlightPart) {
|
||||
_showAtMsgHighlightPart = highlightPart;
|
||||
}
|
||||
_showAtMsgHighlightPartOffsetHint = highlightPartOffsetHint;
|
||||
const Window::SectionShow ¶ms) {
|
||||
_showAtMsgParams = params;
|
||||
if (_showAtMsgId != showAtMsgId) {
|
||||
_showAtMsgId = showAtMsgId;
|
||||
if (_history) {
|
||||
|
@ -6289,8 +6265,8 @@ int HistoryWidget::countInitialScrollTop() {
|
|||
|
||||
enqueueMessageHighlight({
|
||||
item,
|
||||
base::take(_showAtMsgHighlightPart),
|
||||
base::take(_showAtMsgHighlightPartOffsetHint),
|
||||
base::take(_showAtMsgParams.highlightPart),
|
||||
base::take(_showAtMsgParams.highlightPartOffsetHint),
|
||||
});
|
||||
const auto result = itemTopForHighlight(view);
|
||||
createUnreadBarIfBelowVisibleArea(result);
|
||||
|
@ -6507,6 +6483,22 @@ void HistoryWidget::updateHistoryGeometry(
|
|||
}
|
||||
const auto toY = std::clamp(newScrollTop, 0, _scroll->scrollTopMax());
|
||||
synteticScrollToY(toY);
|
||||
if (initial && _showAtMsgId) {
|
||||
const auto timestamp = base::take(_showAtMsgParams.videoTimestamp);
|
||||
if (timestamp.has_value()) {
|
||||
const auto item = session().data().message(_peer, _showAtMsgId);
|
||||
const auto media = item ? item->media() : nullptr;
|
||||
const auto document = media ? media->document() : nullptr;
|
||||
if (document && document->isVideoFile()) {
|
||||
controller()->openDocument(
|
||||
document,
|
||||
true,
|
||||
{ item->fullId() },
|
||||
nullptr,
|
||||
timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::revealItemsCallback() {
|
||||
|
|
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "chat_helpers/field_characters_count_manager.h"
|
||||
#include "data/data_report.h"
|
||||
#include "window/section_widget.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "ui/widgets/fields/input_field.h"
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
|
@ -86,10 +87,6 @@ namespace Webrtc {
|
|||
enum class RecordAvailability : uchar;
|
||||
} // namespace Webrtc
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
namespace ChatHelpers {
|
||||
class TabbedPanel;
|
||||
class TabbedSelector;
|
||||
|
@ -160,10 +157,7 @@ public:
|
|||
void loadMessages();
|
||||
void loadMessagesDown();
|
||||
void firstLoadMessages();
|
||||
void delayedShowAt(
|
||||
MsgId showAtMsgId,
|
||||
const TextWithEntities &highlightPart,
|
||||
int highlightPartOffsetHint);
|
||||
void delayedShowAt(MsgId showAtMsgId, const Window::SectionShow ¶ms);
|
||||
|
||||
bool updateReplaceMediaButton();
|
||||
void updateFieldPlaceholder();
|
||||
|
@ -176,10 +170,7 @@ public:
|
|||
|
||||
History *history() const;
|
||||
PeerData *peer() const;
|
||||
void setMsgId(
|
||||
MsgId showAtMsgId,
|
||||
const TextWithEntities &highlightPart = {},
|
||||
int highlightPartOffsetHint = 0);
|
||||
void setMsgId(MsgId showAtMsgId, const Window::SectionShow ¶ms = {});
|
||||
MsgId msgId() const;
|
||||
|
||||
bool hasTopBarShadow() const {
|
||||
|
@ -244,10 +235,9 @@ public:
|
|||
bool applyDraft(
|
||||
FieldHistoryAction fieldHistoryAction = FieldHistoryAction::Clear);
|
||||
void showHistory(
|
||||
const PeerId &peer,
|
||||
PeerId peerId,
|
||||
MsgId showAtMsgId,
|
||||
const TextWithEntities &highlightPart = {},
|
||||
int highlightPartOffsetHint = 0);
|
||||
const Window::SectionShow ¶ms = {});
|
||||
void setChooseReportMessagesDetails(
|
||||
Data::ReportInput reportInput,
|
||||
Fn<void(std::vector<MsgId>)> callback);
|
||||
|
@ -728,8 +718,7 @@ private:
|
|||
bool _canSendTexts = false;
|
||||
MsgId _showAtMsgId = ShowAtUnreadMsgId;
|
||||
base::flat_set<MsgId> _topicsRequested;
|
||||
TextWithEntities _showAtMsgHighlightPart;
|
||||
int _showAtMsgHighlightPartOffsetHint = 0;
|
||||
Window::SectionShow _showAtMsgParams;
|
||||
bool _showAndMaybeSendStart = false;
|
||||
|
||||
int _firstLoadRequest = 0; // Not real mtpRequestId.
|
||||
|
@ -737,8 +726,7 @@ private:
|
|||
int _preloadDownRequest = 0; // Not real mtpRequestId.
|
||||
|
||||
MsgId _delayedShowAtMsgId = -1;
|
||||
TextWithEntities _delayedShowAtMsgHighlightPart;
|
||||
int _delayedShowAtMsgHighlightPartOffsetHint = 0;
|
||||
Window::SectionShow _delayedShowAtMsgParams;
|
||||
int _delayedShowAtRequest = 0; // Not real mtpRequestId.
|
||||
|
||||
History *_supportPreloadHistory = nullptr;
|
||||
|
|
|
@ -1449,11 +1449,7 @@ void MainWidget::showHistory(
|
|||
&& way != Way::Forward) {
|
||||
ClearBotStartToken(_history->peer());
|
||||
}
|
||||
_history->showHistory(
|
||||
peerId,
|
||||
showAtMsgId,
|
||||
params.highlightPart,
|
||||
params.highlightPartOffsetHint);
|
||||
_history->showHistory(peerId, showAtMsgId, params);
|
||||
if (alreadyThatPeer && params.reapplyLocalDraft) {
|
||||
_history->applyDraft(HistoryWidget::FieldHistoryAction::NewEntry);
|
||||
}
|
||||
|
|
|
@ -755,6 +755,7 @@ void SessionNavigation::showPeerByLinkResolved(
|
|||
});
|
||||
} else {
|
||||
const auto draft = info.text;
|
||||
params.videoTimestamp = info.videoTimestamp;
|
||||
crl::on_main(this, [=] {
|
||||
if (peer->isUser() && !draft.isEmpty()) {
|
||||
Data::SetChatLinkDraft(peer, { draft });
|
||||
|
@ -2780,19 +2781,21 @@ void SessionController::openDocument(
|
|||
not_null<DocumentData*> document,
|
||||
bool showInMediaView,
|
||||
MessageContext message,
|
||||
const Data::StoriesContext *stories) {
|
||||
const Data::StoriesContext *stories,
|
||||
std::optional<TimeId> videoTimestampOverride) {
|
||||
const auto item = session().data().message(message.id);
|
||||
if (openSharedStory(item) || openFakeItemStory(message.id, stories)) {
|
||||
return;
|
||||
} else if (showInMediaView) {
|
||||
using namespace Media::View;
|
||||
const auto timestamp = item ? ExtractVideoTimestamp(item) : 0;
|
||||
_window->openInMediaView(OpenRequest(
|
||||
this,
|
||||
document,
|
||||
item,
|
||||
message.topicRootId,
|
||||
false,
|
||||
(item ? ExtractVideoTimestamp(item) : 0) * crl::time(1000)));
|
||||
videoTimestampOverride.value_or(timestamp) * crl::time(1000)));
|
||||
return;
|
||||
}
|
||||
Data::ResolveDocument(this, document, item, message.topicRootId);
|
||||
|
|
|
@ -158,6 +158,7 @@ struct SectionShow {
|
|||
|
||||
TextWithEntities highlightPart;
|
||||
int highlightPartOffsetHint = 0;
|
||||
std::optional<TimeId> videoTimestamp;
|
||||
Way way = Way::Forward;
|
||||
anim::type animated = anim::type::normal;
|
||||
anim::activation activation = anim::activation::normal;
|
||||
|
@ -505,7 +506,8 @@ public:
|
|||
not_null<DocumentData*> document,
|
||||
bool showInMediaView,
|
||||
MessageContext message,
|
||||
const Data::StoriesContext *stories = nullptr);
|
||||
const Data::StoriesContext *stories = nullptr,
|
||||
std::optional<TimeId> videoTimestampOverride = {});
|
||||
bool openSharedStory(HistoryItem *item);
|
||||
bool openFakeItemStory(
|
||||
FullMsgId fakeItemId,
|
||||
|
|
|
@ -40,6 +40,7 @@ struct PeerByLinkInfo {
|
|||
QString chatLinkSlug;
|
||||
MsgId messageId = ShowAtUnreadMsgId;
|
||||
StoryId storyId = 0;
|
||||
std::optional<TimeId> videoTimestamp;
|
||||
QString text;
|
||||
RepliesByLinkInfo repliesInfo;
|
||||
ResolveType resolveType = ResolveType::Default;
|
||||
|
|
Loading…
Add table
Reference in a new issue