diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index 9f57aff83a..6898b7f18e 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -30,20 +30,47 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/widgets/input_fields.h" #include "styles/style_history.h" #include "styles/style_boxes.h" +#include "media/media_clip_reader.h" namespace { constexpr auto kMinPreviewWidth = 20; +bool ValidatePhotoDimensions(int width, int height) { + return (width > 0) && (height > 0) && (width < 20 * height) && (height < 20 * width); +} + } // namespace -SendFilesBox::SendFilesBox(QWidget*, const QString &filepath, QImage image, CompressConfirm compressed, bool animated) -: _files(filepath) -, _image(image) +SendFilesBox::SendFilesBox(QWidget*, QImage image, CompressConfirm compressed) +: _image(image) , _compressConfirm(compressed) -, _animated(image.isNull() ? false : animated) , _caption(this, st::confirmCaptionArea, lang(lng_photo_caption)) { - if (!image.isNull()) { + _files.push_back(QString()); + prepareSingleFileLayout(); +} + +SendFilesBox::SendFilesBox(QWidget*, const QStringList &files, CompressConfirm compressed) +: _files(files) +, _compressConfirm(compressed) +, _caption(this, st::confirmCaptionArea, lang(_files.size() > 1 ? lng_photos_comment : lng_photo_caption)) { + if (_files.size() == 1) { + prepareSingleFileLayout(); + } +} + +void SendFilesBox::prepareSingleFileLayout() { + t_assert(_files.size() == 1); + if (!_files.front().isEmpty()) { + tryToReadSingleFile(); + } + + if (_image.isNull() || !ValidatePhotoDimensions(_image.width(), _image.height()) || _animated) { + _compressConfirm = CompressConfirm::None; + } + + if (!_image.isNull()) { + auto image = _image; if (!_animated && _compressConfirm == CompressConfirm::None) { auto originalWidth = image.width(); auto originalHeight = image.height(); @@ -99,27 +126,51 @@ SendFilesBox::SendFilesBox(QWidget*, const QString &filepath, QImage image, Comp } } if (_preview.isNull()) { - if (filepath.isEmpty()) { - auto filename = filedialogDefaultName(qsl("image"), qsl(".png"), QString(), true); - _nameText.setText(st::semiboldTextStyle, filename, _textNameOptions); - _statusText = qsl("%1x%2").arg(_image.width()).arg(_image.height()); - _statusWidth = qMax(_nameText.maxWidth(), st::normalFont->width(_statusText)); - _fileIsImage = true; - } else { - auto fileinfo = QFileInfo(filepath); - auto filename = fileinfo.fileName(); - _nameText.setText(st::semiboldTextStyle, filename, _textNameOptions); - _statusText = formatSizeText(fileinfo.size()); - _statusWidth = qMax(_nameText.maxWidth(), st::normalFont->width(_statusText)); - _fileIsImage = fileIsImage(filename, mimeTypeForFile(fileinfo).name()); - } + prepareDocumentLayout(); } } -SendFilesBox::SendFilesBox(QWidget*, const QStringList &files, CompressConfirm compressed) -: _files(files) -, _compressConfirm(compressed) -, _caption(this, st::confirmCaptionArea, lang(lng_photos_comment)) { +void SendFilesBox::prepareDocumentLayout() { + auto filepath = _files.front(); + if (filepath.isEmpty()) { + auto filename = filedialogDefaultName(qsl("image"), qsl(".png"), QString(), true); + _nameText.setText(st::semiboldTextStyle, filename, _textNameOptions); + _statusText = qsl("%1x%2").arg(_image.width()).arg(_image.height()); + _statusWidth = qMax(_nameText.maxWidth(), st::normalFont->width(_statusText)); + _fileIsImage = true; + } else { + auto fileinfo = QFileInfo(filepath); + auto filename = fileinfo.fileName(); + _fileIsImage = fileIsImage(filename, mimeTypeForFile(fileinfo).name()); + + auto songTitle = QString(); + auto songPerformer = QString(); + if (_information) { + if (auto song = base::get_if(&_information->media)) { + songTitle = song->title; + songPerformer = song->performer; + _fileIsAudio = true; + } + } + + auto nameString = DocumentData::composeNameString(filename, songTitle, songPerformer); + _nameText.setText(st::semiboldTextStyle, nameString, _textNameOptions); + _statusText = formatSizeText(fileinfo.size()); + _statusWidth = qMax(_nameText.maxWidth(), st::normalFont->width(_statusText)); + } +} + +void SendFilesBox::tryToReadSingleFile() { + auto filepath = _files.front(); + auto filemime = mimeTypeForFile(QFileInfo(filepath)).name(); + _information = FileLoadTask::ReadMediaInformation(_files.front(), QByteArray(), filemime); + if (auto image = base::get_if(&_information->media)) { + _image = image->data; + _animated = image->animated; + } else if (auto video = base::get_if(&_information->media)) { + _image = video->thumbnail; + _animated = true; + } } SendFilesBox::SendFilesBox(QWidget*, const QString &phone, const QString &firstname, const QString &lastname) @@ -278,7 +329,7 @@ void SendFilesBox::paintEvent(QPaintEvent *e) { p.drawEllipse(inner); } - auto &icon = _fileIsImage ? st::historyFileOutImage : st::historyFileOutDocument; + auto &icon = _fileIsAudio ? st::historyFileOutPlay : _fileIsImage ? st::historyFileOutImage : st::historyFileOutDocument; icon.paintInCenter(p, inner); } else { _contactPhotoEmpty.paint(p, x + st::msgFilePadding.left(), y + st::msgFilePadding.top(), width(), st::msgFileSize); @@ -333,7 +384,7 @@ void SendFilesBox::onSend(bool ctrlShiftEnter) { if (_confirmedCallback) { auto compressed = _compressed ? _compressed->checked() : false; auto caption = _caption ? prepareText(_caption->getLastText(), true) : QString(); - _confirmedCallback(_files, compressed, caption, ctrlShiftEnter); + _confirmedCallback(_files, _animated ? QImage() : _image, std::move(_information), compressed, caption, ctrlShiftEnter); } closeBox(); } @@ -403,11 +454,12 @@ EditCaptionBox::EditCaptionBox(QWidget*, HistoryItem *msg) if (doc->voice()) { _name.setText(st::semiboldTextStyle, lang(lng_media_audio), _textNameOptions); } else { - _name.setText(st::semiboldTextStyle, documentName(doc), _textNameOptions); + _name.setText(st::semiboldTextStyle, doc->composeNameString(), _textNameOptions); } _status = formatSizeText(doc->size); _statusw = qMax(_name.maxWidth(), st::normalFont->width(_status)); _isImage = doc->isImage(); + _isAudio = (doc->voice() || doc->song()); } } else { int32 maxW = 0, maxH = 0; @@ -579,7 +631,7 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) { p.drawEllipse(inner); } - auto icon = &(_isImage ? st::historyFileInImage : st::historyFileInDocument); + auto icon = &(_isAudio ? st::historyFileInPlay : _isImage ? st::historyFileInImage : st::historyFileInDocument); icon->paintInCenter(p, inner); } p.setFont(st::semiboldFont); diff --git a/Telegram/SourceFiles/boxes/send_files_box.h b/Telegram/SourceFiles/boxes/send_files_box.h index 65d9e4831e..0f1fe50f4f 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.h +++ b/Telegram/SourceFiles/boxes/send_files_box.h @@ -33,11 +33,11 @@ class SendFilesBox : public BoxContent { Q_OBJECT public: - SendFilesBox(QWidget*, const QString &filepath, QImage image, CompressConfirm compressed, bool animated = false); + SendFilesBox(QWidget*, QImage image, CompressConfirm compressed); SendFilesBox(QWidget*, const QStringList &files, CompressConfirm compressed); SendFilesBox(QWidget*, const QString &phone, const QString &firstname, const QString &lastname); - void setConfirmedCallback(base::lambda callback) { + void setConfirmedCallback(base::lambda information, bool compressed, const QString &caption, bool ctrlShiftEnter)> callback) { _confirmedCallback = std::move(callback); } void setCancelledCallback(base::lambda callback) { @@ -63,6 +63,10 @@ private slots: } private: + void prepareSingleFileLayout(); + void prepareDocumentLayout(); + void tryToReadSingleFile(); + void updateTitleText(); void updateBoxSize(); void updateControlsGeometry(); @@ -70,7 +74,8 @@ private: QString _titleText; QStringList _files; - const QImage _image; + QImage _image; + std::unique_ptr _information; CompressConfirm _compressConfirm = CompressConfirm::None; bool _animated = false; @@ -82,6 +87,7 @@ private: QPixmap _fileThumb; Text _nameText; + bool _fileIsAudio = false; bool _fileIsImage = false; QString _statusText; int _statusWidth = 0; @@ -91,7 +97,7 @@ private: QString _contactLastName; EmptyUserpic _contactPhotoEmpty; - base::lambda _confirmedCallback; + base::lambda information, bool compressed, const QString &caption, bool ctrlShiftEnter)> _confirmedCallback; base::lambda _cancelledCallback; bool _confirmed = false; @@ -145,6 +151,7 @@ private: Text _name; QString _status; int _statusw = 0; + bool _isAudio = false; bool _isImage = false; bool _previewCancelled = false; diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index a578b0e4a5..05e3e91f22 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -1010,7 +1010,7 @@ void HistoryDocument::createComponents(bool caption) { } void HistoryDocument::fillNamedFromData(HistoryDocumentNamed *named) { - auto name = named->_name = documentName(_data); + auto name = named->_name = _data->composeNameString(); named->_namew = st::semiboldFont->width(name); } diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 5fa17a572a..5aaa813415 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -6609,14 +6609,14 @@ bool HistoryWidget::showSendFilesBox(object_ptr box, const QString App::wnd()->activateWindow(); auto withComment = (addedComment != nullptr); - box->setConfirmedCallback(base::lambda_guarded(this, [this, withComment, sendCallback = std::move(callback)](const QStringList &files, bool compressed, const QString &caption, bool ctrlShiftEnter) { + box->setConfirmedCallback(base::lambda_guarded(this, [this, withComment, sendCallback = std::move(callback)](const QStringList &files, const QImage &image, std::unique_ptr information, bool compressed, const QString &caption, bool ctrlShiftEnter) { if (!canWriteMessage()) return; auto replyTo = replyToId(); if (withComment) { onSend(ctrlShiftEnter, replyTo); } - sendCallback(files, compressed, caption, replyTo); + sendCallback(files, image, std::move(information), compressed, caption, replyTo); })); if (withComment) { @@ -6662,21 +6662,12 @@ bool HistoryWidget::confirmSendingFiles(const QStringList &files, CompressConfir bool HistoryWidget::confirmSendingFiles(const SendingFilesLists &lists, CompressConfirm compressed, const QString *addedComment) { return validateSendingFiles(lists, [this, &lists, compressed, addedComment](const QStringList &files) { - auto image = QImage(); auto insertTextOnCancel = QString(); - auto box = ([this, &files, &lists, compressed, &image] { - if (files.size() > 1) { - return Box(files, lists.allFilesForCompress ? compressed : CompressConfirm::None); - } - auto filepath = files.front(); - auto animated = false; - image = App::readImage(filepath, nullptr, false, &animated); - return Box(filepath, image, imageCompressConfirm(image, compressed, animated), animated); - })(); - auto sendCallback = [this, image](const QStringList &files, bool compressed, const QString &caption, MsgId replyTo) { + auto sendCallback = [this](const QStringList &files, const QImage &image, std::unique_ptr information, bool compressed, const QString &caption, MsgId replyTo) { auto type = compressed ? SendMediaType::Photo : SendMediaType::File; - uploadFilesAfterConfirmation(files, image, QByteArray(), type, caption); + uploadFilesAfterConfirmation(files, QByteArray(), image, std::move(information), type, caption); }; + auto box = Box(files, lists.allFilesForCompress ? compressed : CompressConfirm::None); return showSendFilesBox(std::move(box), insertTextOnCancel, addedComment, std::move(sendCallback)); }); } @@ -6685,12 +6676,11 @@ bool HistoryWidget::confirmSendingFiles(const QImage &image, const QByteArray &c if (!canWriteMessage() || image.isNull()) return false; App::wnd()->activateWindow(); - auto animated = false; - auto sendCallback = [this, content, image](const QStringList &files, bool compressed, const QString &caption, MsgId replyTo) { + auto sendCallback = [this, content](const QStringList &files, const QImage &image, std::unique_ptr information, bool compressed, const QString &caption, MsgId replyTo) { auto type = compressed ? SendMediaType::Photo : SendMediaType::File; - uploadFilesAfterConfirmation(files, image, content, type, caption); + uploadFilesAfterConfirmation(files, content, image, std::move(information), type, caption); }; - auto box = Box(QString(), image, imageCompressConfirm(image, compressed), animated); + auto box = Box(image, compressed); return showSendFilesBox(std::move(box), insertTextOnCancel, nullptr, std::move(sendCallback)); } @@ -6722,7 +6712,7 @@ bool HistoryWidget::confirmShareContact(const QString &phone, const QString &fna if (!canWriteMessage()) return false; auto box = Box(phone, fname, lname); - auto sendCallback = [this, phone, fname, lname](const QStringList &files, bool compressed, const QString &caption, MsgId replyTo) { + auto sendCallback = [this, phone, fname, lname](const QStringList &files, const QImage &image, std::unique_ptr information, bool compressed, const QString &caption, MsgId replyTo) { shareContact(_peer->id, phone, fname, lname, replyTo); }; auto insertTextOnCancel = QString(); @@ -6779,26 +6769,14 @@ void HistoryWidget::getSendingLocalFileInfo(SendingFilesLists &result, const QSt } } -CompressConfirm HistoryWidget::imageCompressConfirm(const QImage &image, CompressConfirm compressed, bool animated) { - if (animated || image.isNull()) { - return CompressConfirm::None; - } - auto imageWidth = image.width(); - auto imageHeight = image.height(); - if (imageWidth >= 20 * imageHeight || imageHeight >= 20 * imageWidth) { - return CompressConfirm::None; - } - return compressed; -} - void HistoryWidget::uploadFiles(const QStringList &files, SendMediaType type) { if (!canWriteMessage()) return; auto caption = QString(); - uploadFilesAfterConfirmation(files, QImage(), QByteArray(), type, caption); + uploadFilesAfterConfirmation(files, QByteArray(), QImage(), std::unique_ptr(), type, caption); } -void HistoryWidget::uploadFilesAfterConfirmation(const QStringList &files, const QImage &image, const QByteArray &content, SendMediaType type, QString caption) { +void HistoryWidget::uploadFilesAfterConfirmation(const QStringList &files, const QByteArray &content, const QImage &image, std::unique_ptr information, SendMediaType type, QString caption) { t_assert(canWriteMessage()); auto to = FileLoadTo(_peer->id, _silent->checked(), replyToId()); @@ -6818,7 +6796,7 @@ void HistoryWidget::uploadFilesAfterConfirmation(const QStringList &files, const if (filepath.isEmpty() && (!image.isNull() || !content.isNull())) { tasks.push_back(MakeShared(content, image, type, to, caption)); } else { - tasks.push_back(MakeShared(filepath, image, type, to, caption)); + tasks.push_back(MakeShared(filepath, std::move(information), type, to, caption)); } } _fileLoader.addTasks(tasks); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 34d9b58df5..7c03132c1b 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -868,10 +868,9 @@ private: bool validateSendingFiles(const SendingFilesLists &lists, Callback callback); template bool showSendFilesBox(object_ptr box, const QString &insertTextOnCancel, const QString *addedComment, SendCallback callback); - CompressConfirm imageCompressConfirm(const QImage &image, CompressConfirm compressed, bool animated = false); // If an empty filepath is found we upload (possible) "image" with (possible) "content". - void uploadFilesAfterConfirmation(const QStringList &files, const QImage &image, const QByteArray &content, SendMediaType type, QString caption); + void uploadFilesAfterConfirmation(const QStringList &files, const QByteArray &content, const QImage &image, std::unique_ptr information, SendMediaType type, QString caption); void itemRemoved(HistoryItem *item); diff --git a/Telegram/SourceFiles/layout.cpp b/Telegram/SourceFiles/layout.cpp index 03776da174..515ae22805 100644 --- a/Telegram/SourceFiles/layout.cpp +++ b/Telegram/SourceFiles/layout.cpp @@ -139,33 +139,6 @@ QString formatPlayedText(qint64 played, qint64 duration) { return lng_duration_played(lt_played, formatDurationText(played), lt_duration, formatDurationText(duration)); } -QString documentName(DocumentData *document) { - auto song = document->song(); - if (!song || (song->title.isEmpty() && song->performer.isEmpty())) { - return document->name.isEmpty() ? qsl("Unknown File") : document->name; - } - - if (song->performer.isEmpty()) return song->title; - - return song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + (song->title.isEmpty() ? qsl("Unknown Track") : song->title); -} - -TextWithEntities documentNameWithEntities(DocumentData *document) { - TextWithEntities result; - auto song = document->song(); - if (!song || (song->title.isEmpty() && song->performer.isEmpty())) { - result.text = document->name.isEmpty() ? qsl("Unknown File") : document->name; - result.entities.push_back({ EntityInTextBold, 0, result.text.size() }); - } else if (song->performer.isEmpty()) { - result.text = song->title; - result.entities.push_back({ EntityInTextBold, 0, result.text.size() }); - } else { - result.text = song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + (song->title.isEmpty() ? qsl("Unknown Track") : song->title); - result.entities.push_back({ EntityInTextBold, 0, song->performer.size() }); - } - return result; -} - int32 documentColorIndex(DocumentData *document, QString &ext) { int32 colorIndex = 0; diff --git a/Telegram/SourceFiles/layout.h b/Telegram/SourceFiles/layout.h index 16e2e86ede..ad315f4b40 100644 --- a/Telegram/SourceFiles/layout.h +++ b/Telegram/SourceFiles/layout.h @@ -81,8 +81,6 @@ QString formatDurationAndSizeText(qint64 duration, qint64 size); QString formatGifAndSizeText(qint64 size); QString formatPlayedText(qint64 played, qint64 duration); -QString documentName(DocumentData *document); -TextWithEntities documentNameWithEntities(DocumentData *document); int32 documentColorIndex(DocumentData *document, QString &ext); style::color documentColor(int colorIndex); style::color documentDarkColor(int colorIndex); diff --git a/Telegram/SourceFiles/media/media_audio.cpp b/Telegram/SourceFiles/media/media_audio.cpp index 7d9c9b1b94..a9cf6a29aa 100644 --- a/Telegram/SourceFiles/media/media_audio.cpp +++ b/Telegram/SourceFiles/media/media_audio.cpp @@ -1656,24 +1656,27 @@ private: }; -MTPDocumentAttribute audioReadSongAttributes(const QString &fname, const QByteArray &data, QImage &cover, QByteArray &coverBytes, QByteArray &coverFormat) { +namespace Media { +namespace Player { + +FileLoadTask::Song PrepareForSending(const QString &fname, const QByteArray &data) { + auto result = FileLoadTask::Song(); FFMpegAttributesReader reader(FileLocation(fname), data); qint64 position = 0; - if (reader.open(position)) { - int32 duration = reader.duration() / reader.frequency(); - if (reader.duration() > 0) { - cover = reader.cover(); - coverBytes = reader.coverBytes(); - coverFormat = reader.coverFormat(); - return MTP_documentAttributeAudio(MTP_flags(MTPDdocumentAttributeAudio::Flag::f_title | MTPDdocumentAttributeAudio::Flag::f_performer), MTP_int(duration), MTP_string(reader.title()), MTP_string(reader.performer()), MTPstring()); - } + if (reader.open(position) && reader.duration() > 0) { + result.duration = reader.duration() / reader.frequency(); + result.title = reader.title(); + result.performer = reader.performer(); + result.cover = reader.cover(); } - return MTP_documentAttributeFilename(MTP_string(fname)); + return result; } +} // namespace Player +} // namespace Media + class FFMpegWaveformCounter : public FFMpegLoader { public: - FFMpegWaveformCounter(const FileLocation &file, const QByteArray &data) : FFMpegLoader(file, data) { } diff --git a/Telegram/SourceFiles/media/media_audio.h b/Telegram/SourceFiles/media/media_audio.h index 6bb7236b8e..0de0397435 100644 --- a/Telegram/SourceFiles/media/media_audio.h +++ b/Telegram/SourceFiles/media/media_audio.h @@ -20,6 +20,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once +#include "storage/localimageloader.h" + struct VideoSoundData; struct VideoSoundPart; @@ -278,6 +280,8 @@ private: }; +FileLoadTask::Song PrepareForSending(const QString &fname, const QByteArray &data); + } // namespace Player } // namespace Media @@ -291,5 +295,4 @@ bool CheckAudioDeviceConnected(); } // namespace internal -MTPDocumentAttribute audioReadSongAttributes(const QString &fname, const QByteArray &data, QImage &cover, QByteArray &coverBytes, QByteArray &coverFormat); VoiceWaveform audioCountWaveform(const FileLocation &file, const QByteArray &data); diff --git a/Telegram/SourceFiles/media/media_clip_reader.cpp b/Telegram/SourceFiles/media/media_clip_reader.cpp index 09c3104360..b9e6477f26 100644 --- a/Telegram/SourceFiles/media/media_clip_reader.cpp +++ b/Telegram/SourceFiles/media/media_clip_reader.cpp @@ -839,8 +839,8 @@ Manager::~Manager() { clear(); } -SendData PrepareForSending(const QString &fname, const QByteArray &data) { - auto result = SendData(); +FileLoadTask::Video PrepareForSending(const QString &fname, const QByteArray &data) { + auto result = FileLoadTask::Video(); auto localLocation = FileLocation(fname); auto localData = QByteArray(data); @@ -859,14 +859,14 @@ SendData PrepareForSending(const QString &fname, const QByteArray &data) { auto hasAlpha = false; auto readResult = reader->readFramesTill(-1, getms()); auto readFrame = (readResult == internal::ReaderImplementation::ReadResult::Success); - if (readFrame && reader->renderFrame(result.cover, hasAlpha, QSize())) { + if (readFrame && reader->renderFrame(result.thumbnail, hasAlpha, QSize())) { if (hasAlpha) { auto cacheForResize = QImage(); auto request = FrameRequest(); - request.framew = request.outerw = result.cover.width(); - request.frameh = request.outerh = result.cover.height(); + request.framew = request.outerw = result.thumbnail.width(); + request.frameh = request.outerh = result.thumbnail.height(); request.factor = 1; - result.cover = PrepareFrameImage(request, result.cover, hasAlpha, cacheForResize); + result.thumbnail = PrepareFrameImage(request, result.thumbnail, hasAlpha, cacheForResize); } result.duration = static_cast(durationMs / 1000); } diff --git a/Telegram/SourceFiles/media/media_clip_reader.h b/Telegram/SourceFiles/media/media_clip_reader.h index 9d31bcc6b2..6b9cd3e473 100644 --- a/Telegram/SourceFiles/media/media_clip_reader.h +++ b/Telegram/SourceFiles/media/media_clip_reader.h @@ -20,6 +20,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once +#include "storage/localimageloader.h" + class FileLocation; namespace Media { @@ -243,12 +245,7 @@ private: }; -struct SendData { - QImage cover; - int duration = 0; - bool isGifv = false; -}; -SendData PrepareForSending(const QString &fname, const QByteArray &data); +FileLoadTask::Video PrepareForSending(const QString &fname, const QByteArray &data); void Finish(); diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index c65aff81d7..acb46082f5 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -47,6 +47,22 @@ TextParseOptions _documentNameOptions = { Qt::LayoutDirectionAuto, // dir }; +TextWithEntities ComposeNameWithEntities(DocumentData *document) { + TextWithEntities result; + auto song = document->song(); + if (!song || (song->title.isEmpty() && song->performer.isEmpty())) { + result.text = document->name.isEmpty() ? qsl("Unknown File") : document->name; + result.entities.push_back({ EntityInTextBold, 0, result.text.size() }); + } else if (song->performer.isEmpty()) { + result.text = song->title; + result.entities.push_back({ EntityInTextBold, 0, result.text.size() }); + } else { + result.text = song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + (song->title.isEmpty() ? qsl("Unknown Track") : song->title); + result.entities.push_back({ EntityInTextBold, 0, song->performer.size() }); + } + return result; +} + } // namespace void ItemBase::clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) { @@ -672,7 +688,7 @@ Document::Document(DocumentData *document, HistoryItem *parent, const style::Ove , _date(langDateTime(date(_data->date))) , _datew(st::normalFont->width(_date)) , _colorIndex(documentColorIndex(_data, _ext)) { - _name.setMarkedText(st::defaultTextStyle, documentNameWithEntities(_data), _documentNameOptions); + _name.setMarkedText(st::defaultTextStyle, ComposeNameWithEntities(_data), _documentNameOptions); AddComponents(Info::Bit()); diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index 5281110080..1510eeb84b 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -179,10 +179,10 @@ void TaskQueueWorker::onTaskAdded() { _inTaskAdded = false; } -FileLoadTask::FileLoadTask(const QString &filepath, const QImage &image, SendMediaType type, const FileLoadTo &to, const QString &caption) : _id(rand_value()) +FileLoadTask::FileLoadTask(const QString &filepath, std::unique_ptr information, SendMediaType type, const FileLoadTo &to, const QString &caption) : _id(rand_value()) , _to(to) , _filepath(filepath) -, _image(image) +, _information(std::move(information)) , _type(type) , _caption(caption) { } @@ -204,6 +204,115 @@ FileLoadTask::FileLoadTask(const QByteArray &voice, int32 duration, const VoiceW , _caption(caption) { } +std::unique_ptr FileLoadTask::ReadMediaInformation(const QString &filepath, const QByteArray &content, const QString &filemime) { + auto result = std::make_unique(); + result->filemime = filemime; + + if (CheckForSong(filepath, content, result)) { + return result; + } else if (CheckForVideo(filepath, content, result)) { + return result; + } else if (CheckForImage(filepath, content, result)) { + return result; + } + return result; +} + +template +bool FileLoadTask::CheckMimeOrExtensions(const QString &filepath, const QString &filemime, Mimes &mimes, Extensions &extensions) { + if (std::find(std::begin(mimes), std::end(mimes), filemime) != std::end(mimes)) { + return true; + } + if (std::find_if(std::begin(extensions), std::end(extensions), [&filepath](auto &extension) { + return filepath.endsWith(extension, Qt::CaseInsensitive); + }) != std::end(extensions)) { + return true; + } + return false; +} + +bool FileLoadTask::CheckForSong(const QString &filepath, const QByteArray &content, std::unique_ptr &result) { + static const auto mimes = { + qstr("audio/mp3"), + qstr("audio/m4a"), + qstr("audio/aac"), + qstr("audio/ogg"), + qstr("audio/flac"), + }; + static const auto extensions = { + qstr(".mp3"), + qstr(".m4a"), + qstr(".aac"), + qstr(".ogg"), + qstr(".flac"), + }; + if (!CheckMimeOrExtensions(filepath, result->filemime, mimes, extensions)) { + return false; + } + + auto media = Media::Player::PrepareForSending(filepath, content); + if (media.duration <= 0) { + return false; + } + if (!ValidateThumbDimensions(media.cover.width(), media.cover.height())) { + media.cover = QImage(); + } + result->media = std::move(media); + return true; +} + +bool FileLoadTask::CheckForVideo(const QString &filepath, const QByteArray &content, std::unique_ptr &result) { + static const auto mimes = { + qstr("video/mp4"), + qstr("video/quicktime"), + }; + static const auto extensions = { + qstr(".mp4"), + qstr(".mov"), + }; + if (!CheckMimeOrExtensions(filepath, result->filemime, mimes, extensions)) { + return false; + } + + auto media = Media::Clip::PrepareForSending(filepath, content); + if (media.duration <= 0) { + return false; + } + + auto coverWidth = media.thumbnail.width(); + auto coverHeight = media.thumbnail.height(); + if (!ValidateThumbDimensions(coverWidth, coverHeight)) { + return false; + } + + if (filepath.endsWith(qstr(".mp4"), Qt::CaseInsensitive)) { + result->filemime = qstr("video/mp4"); + } + result->media = std::move(media); + return true; +} + +bool FileLoadTask::CheckForImage(const QString &filepath, const QByteArray &content, std::unique_ptr &result) { + auto animated = false; + auto image = ([&filepath, &content, &animated] { + if (!content.isEmpty()) { + return App::readImage(content, nullptr, false, &animated); + } else if (!filepath.isEmpty()) { + return App::readImage(filepath, nullptr, false, &animated); + } + return QImage(); + })(); + + if (image.isNull()) { + return false; + } + auto media = Image(); + media.data = std::move(image); + media.animated = animated; + result->media = media; + return true; +} + void FileLoadTask::process() { const QString stickerMime = qsl("image/webp"); @@ -217,10 +326,11 @@ void FileLoadTask::process() { QString thumbname = "thumb.jpg"; QByteArray thumbdata; - auto animated = false; - auto song = false; - auto video = false; - auto voice = (_type == SendMediaType::Audio); + auto isAnimation = false; + auto isSong = false; + auto isVideo = false; + auto isVoice = (_type == SendMediaType::Audio); + auto fullimage = base::take(_image); auto info = _filepath.isEmpty() ? QFileInfo() : QFileInfo(_filepath); if (info.exists()) { @@ -228,14 +338,28 @@ void FileLoadTask::process() { _result->filesize = -1; return; } + + // Voice sending is supported only from memory for now. + // Because for voice we force mime type and don't read MediaInformation. + // For a real file we always read mime type and read MediaInformation. + t_assert(!isVoice); + filesize = info.size(); - filemime = mimeTypeForFile(info).name(); filename = info.fileName(); - auto opaque = (filemime != stickerMime); - fullimage = App::readImage(_filepath, 0, opaque, &animated); + if (!_information) { + _information = readMediaInformation(mimeTypeForFile(info).name()); + } + filemime = _information->filemime; + if (auto image = base::get_if(&_information->media)) { + fullimage = base::take(image->data); + if (auto opaque = (filemime != stickerMime)) { + fullimage = Images::prepareOpaque(std::move(fullimage)); + } + isAnimation = image->animated; + } } else if (!_content.isEmpty()) { filesize = _content.size(); - if (voice) { + if (isVoice) { filename = filedialogDefaultName(qsl("audio"), qsl(".ogg"), QString(), true); filemime = "audio/ogg"; } else { @@ -294,81 +418,65 @@ void FileLoadTask::process() { MTPPhoto photo(MTP_photoEmpty(MTP_long(0))); MTPDocument document(MTP_documentEmpty(MTP_long(0))); - if (!voice) { - if (filemime == qstr("audio/mp3") || filemime == qstr("audio/m4a") || filemime == qstr("audio/aac") || filemime == qstr("audio/ogg") || filemime == qstr("audio/flac") || - filename.endsWith(qstr(".mp3"), Qt::CaseInsensitive) || filename.endsWith(qstr(".m4a"), Qt::CaseInsensitive) || - filename.endsWith(qstr(".aac"), Qt::CaseInsensitive) || filename.endsWith(qstr(".ogg"), Qt::CaseInsensitive) || - filename.endsWith(qstr(".flac"), Qt::CaseInsensitive)) { - QImage cover; - QByteArray coverBytes, coverFormat; - auto audioAttribute = audioReadSongAttributes(_filepath, _content, cover, coverBytes, coverFormat); - if (audioAttribute.type() == mtpc_documentAttributeAudio) { - attributes.push_back(audioAttribute); - song = true; - if (!cover.isNull()) { // cover to thumb - auto coverWidth = cover.width(); - auto coverHeight = cover.height(); - if (ValidateThumbDimensions(coverWidth, coverHeight)) { - auto full = (coverWidth > 90 || coverHeight > 90) ? App::pixmapFromImageInPlace(cover.scaled(90, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation)) : App::pixmapFromImageInPlace(std::move(cover)); - { - auto thumbFormat = QByteArray("JPG"); - auto thumbQuality = 87; - - QBuffer buffer(&thumbdata); - full.save(&buffer, thumbFormat, thumbQuality); - } - - thumb = full; - thumbSize = MTP_photoSize(MTP_string(""), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0)); - - thumbId = rand_value(); - } - } - } + if (!isVoice) { + if (!_information) { + _information = readMediaInformation(filemime); + filemime = _information->filemime; } - if (filemime == qstr("video/mp4") || filemime == qstr("video/quicktime") - || filename.endsWith(qstr(".mp4"), Qt::CaseInsensitive) || filename.endsWith(qstr(".mov"), Qt::CaseInsensitive)) { - auto sendVideoData = Media::Clip::PrepareForSending(_filepath, _content); - if (sendVideoData.duration > 0) { - auto coverWidth = sendVideoData.cover.width(); - auto coverHeight = sendVideoData.cover.height(); - if (ValidateThumbDimensions(coverWidth, coverHeight)) { - if (sendVideoData.isGifv) { - attributes.push_back(MTP_documentAttributeAnimated()); - } - attributes.push_back(MTP_documentAttributeVideo(MTP_int(sendVideoData.duration), MTP_int(coverWidth), MTP_int(coverHeight))); - video = true; + if (auto song = base::get_if(&_information->media)) { + isSong = true; + auto flags = MTPDdocumentAttributeAudio::Flag::f_title | MTPDdocumentAttributeAudio::Flag::f_performer; + attributes.push_back(MTP_documentAttributeAudio(MTP_flags(flags), MTP_int(song->duration), MTP_string(song->title), MTP_string(song->performer), MTPstring())); + if (!song->cover.isNull()) { // cover to thumb + auto coverWidth = song->cover.width(); + auto coverHeight = song->cover.height(); + auto full = (coverWidth > 90 || coverHeight > 90) ? App::pixmapFromImageInPlace(song->cover.scaled(90, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation)) : App::pixmapFromImageInPlace(std::move(song->cover)); + { + auto thumbFormat = QByteArray("JPG"); + auto thumbQuality = 87; - auto cover = (coverWidth > 90 || coverHeight > 90) - ? sendVideoData.cover.scaled(90, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation) - : std::move(sendVideoData.cover); - { - auto thumbFormat = QByteArray("JPG"); - auto thumbQuality = 87; - - QBuffer buffer(&thumbdata); - cover.save(&buffer, thumbFormat, thumbQuality); - } - - thumb = App::pixmapFromImageInPlace(std::move(cover)); - thumbSize = MTP_photoSize(MTP_string(""), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0)); - - thumbId = rand_value(); - - if (filename.endsWith(qstr(".mp4"), Qt::CaseInsensitive)) { - filemime = qstr("video/mp4"); - } + QBuffer buffer(&thumbdata); + full.save(&buffer, thumbFormat, thumbQuality); } + + thumb = full; + thumbSize = MTP_photoSize(MTP_string(""), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0)); + + thumbId = rand_value(); } + } else if (auto video = base::get_if