/* This file is part of Telegram Desktop, the official desktop version of Telegram messaging app, see https://telegram.org Telegram Desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library. Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" #include "playerwidget.h" #include "shortcuts.h" #include "lang.h" #include "boxes/addcontactbox.h" #include "application.h" #include "mainwindow.h" #include "playerwidget.h" #include "mainwidget.h" #include "localstorage.h" #include "media/media_audio.h" #include "history/history_media_types.h" PlayerWidget::PlayerWidget(QWidget *parent) : TWidget(parent) , _a_state(animation(this, &PlayerWidget::step_state)) , _a_progress(animation(this, &PlayerWidget::step_progress)) { resize(st::wndMinWidth, st::playerHeight); setMouseTracking(true); memset(_stateHovers, 0, sizeof(_stateHovers)); } void PlayerWidget::paintEvent(QPaintEvent *e) { Painter p(this); QRect r(e->rect()), checkr(myrtlrect(r)); p.fillRect(r, st::playerBg->b); if (!_playbackRect.contains(checkr)) { if (_fullAvailable && checkr.intersects(_prevRect)) { if (_prevAvailable) { float64 o = _stateHovers[OverPrev]; p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity); } else { p.setOpacity(st::playerUnavailableOpacity); } p.drawSpriteCenterLeft(_prevRect, width(), st::playerPrev); } if (checkr.intersects(_playRect)) { float64 o = _stateHovers[OverPlay]; p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity); p.drawSpriteCenterLeft(_playRect, width(), (_showPause || _down == OverPlayback) ? st::playerPause : st::playerPlay); } if (_fullAvailable && checkr.intersects(_nextRect)) { if (_nextAvailable) { float64 o = _stateHovers[OverNext]; p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity); } else { p.setOpacity(st::playerUnavailableOpacity); } p.drawSpriteCenterLeft(_nextRect, width(), st::playerNext); } if (checkr.intersects(_closeRect)) { float64 o = _stateHovers[OverClose]; p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity); p.drawSpriteCenterLeft(_closeRect, width(), st::playerClose); } if (checkr.intersects(_volumeRect)) { float64 o = _stateHovers[OverVolume]; p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity); int32 top = _volumeRect.y() + (_volumeRect.height() - st::playerVolume.pxHeight()) / 2; int32 left = _volumeRect.x() + (_volumeRect.width() - st::playerVolume.pxWidth()) / 2; int32 mid = left + qRound(st::playerVolume.pxWidth() * Global::SongVolume()); int32 right = left + st::playerVolume.pxWidth(); if (rtl()) { left = width() - left; mid = width() - mid; right = width() - right; if (mid < left) { p.drawPixmap(QRect(mid, top, left - mid, st::playerVolume.pxHeight()), App::sprite(), QRect(st::playerVolume.rect().x() + (mid - right) * cIntRetinaFactor(), st::playerVolume.rect().y(), (left - mid) * cIntRetinaFactor(), st::playerVolume.pxHeight() * cIntRetinaFactor())); } if (right < mid) { p.setOpacity(st::playerUnavailableOpacity); p.drawPixmap(QRect(right, top, mid - right, st::playerVolume.pxHeight()), App::sprite(), QRect(st::playerVolume.rect().x(), st::playerVolume.rect().y(), (mid - right) * cIntRetinaFactor(), st::playerVolume.pxHeight() * cIntRetinaFactor())); } } else { if (mid > left) { p.drawPixmap(QRect(left, top, mid - left, st::playerVolume.pxHeight()), App::sprite(), QRect(st::playerVolume.rect().x(), st::playerVolume.rect().y(), (mid - left) * cIntRetinaFactor(), st::playerVolume.pxHeight() * cIntRetinaFactor())); } if (right > mid) { p.setOpacity(st::playerUnavailableOpacity); p.drawPixmap(QRect(mid, top, right - mid, st::playerVolume.pxHeight()), App::sprite(), QRect(st::playerVolume.rect().x() + (mid - left) * cIntRetinaFactor(), st::playerVolume.rect().y(), (right - mid) * cIntRetinaFactor(), st::playerVolume.pxHeight() * cIntRetinaFactor())); } } } if (_fullAvailable && checkr.intersects(_fullRect)) { float64 o = _stateHovers[OverFull]; p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity); p.drawSpriteCenterLeft(_fullRect, width(), st::playerFull); } if (checkr.intersects(_repeatRect)) { float64 o = _stateHovers[OverRepeat]; p.setOpacity(_repeat ? 1. : (o * st::playerInactiveOpacity + (1. - o) * st::playerUnavailableOpacity)); p.drawSpriteCenterLeft(_repeatRect, width(), st::playerRepeat); } p.setOpacity(1.); p.setPen(st::playerTimeFg->p); p.setFont(st::linkFont->f); p.drawTextLeft(_infoRect.x() + _infoRect.width() - _timeWidth, _infoRect.y() + (_infoRect.height() - st::linkFont->height) / 2, width(), _time, _timeWidth); textstyleSet(&st::playerNameStyle); p.setPen(st::playerFg->p); _name.drawElided(p, _infoRect.x() + (rtl() ? (_timeWidth + st::playerSkip) : 0), _infoRect.y() + (_infoRect.height() - st::linkFont->height) / 2, _infoRect.width() - _timeWidth - st::playerSkip); textstyleRestore(); } if (_duration) { float64 prg = (_down == OverPlayback) ? _downProgress : a_progress.current(); int32 from = _playbackRect.x(), mid = qRound(_playbackRect.x() + prg * _playbackRect.width()), end = _playbackRect.x() + _playbackRect.width(); if (mid > from) { p.fillRect(rtl() ? (width() - mid) : from, height() - st::playerLineHeight, mid - from, _playbackRect.height(), st::playerLineActive->b); } if (end > mid) { p.fillRect(rtl() ? (width() - end) : mid, height() - st::playerLineHeight, end - mid, st::playerLineHeight, st::playerLineInactive->b); } if (_stateHovers[OverPlayback] > 0) { p.setOpacity(_stateHovers[OverPlayback]); int32 x = mid - (st::playerMoverSize.width() / 2); p.fillRect(rtl() ? (width() - x - st::playerMoverSize.width()) : x, height() - st::playerMoverSize.height(), st::playerMoverSize.width(), st::playerMoverSize.height(), st::playerLineActive->b); } } else if (a_loadProgress.current() > 0) { int32 from = _playbackRect.x(), mid = qRound(_playbackRect.x() + a_loadProgress.current() * _playbackRect.width()); if (mid > from) { p.fillRect(rtl() ? (width() - mid) : from, height() - st::playerLineHeight, mid - from, _playbackRect.height(), st::playerLineInactive->b); } } } void PlayerWidget::mousePressEvent(QMouseEvent *e) { QPoint pos(myrtlpoint(e->pos())); if (e->button() == Qt::LeftButton) { _down = OverNone; if (_song && _over == OverPlay) { playPausePressed(); return; } else if (_over == OverPrev) { prevPressed(); } else if (_over == OverNext) { nextPressed(); } else if (_over == OverClose) { _down = OverClose; } else if (_over == OverVolume) { _down = OverVolume; _downCoord = pos.x() - _volumeRect.x(); Global::SetSongVolume(snap((_downCoord - ((_volumeRect.width() - st::playerVolume.pxWidth()) / 2)) / float64(st::playerVolume.pxWidth()), 0., 1.)); emit audioPlayer()->songVolumeChanged(); rtlupdate(_volumeRect); } else if (_over == OverPlayback) { AudioMsgId playing; auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song); if (playing == _song && playbackState.duration) { if (playbackState.state == AudioPlayerPlaying || playbackState.state == AudioPlayerStarting || playbackState.state == AudioPlayerResuming) { audioPlayer()->pauseresume(AudioMsgId::Type::Song); } _down = OverPlayback; _downProgress = snap((pos.x() - _playbackRect.x()) / float64(_playbackRect.width()), 0., 1.); _downDuration = playbackState.duration; _downFrequency = (playbackState.frequency ? playbackState.frequency : AudioVoiceMsgFrequency); rtlupdate(_playbackRect); updateDownTime(); } } else if (_over == OverFull && _song) { if (HistoryItem *item = App::histItemById(_song.contextId())) { App::main()->showMediaOverview(item->history()->peer, OverviewMusicFiles); } } else if (_over == OverRepeat) { _repeat = !_repeat; updateOverRect(OverRepeat); } } } void PlayerWidget::updateDownTime() { QString time = formatDurationText(qRound(_downDuration * _downProgress) / _downFrequency); if (time != _time) { _time = time; _timeWidth = st::linkFont->width(_time); rtlupdate(_infoRect); } } void PlayerWidget::updateOverState(OverState newState) { bool result = true; if (_over != newState) { updateOverRect(_over); updateOverRect(newState); if (_over != OverNone) { _stateAnimations.remove(_over); _stateAnimations[-_over] = getms() - ((1. - _stateHovers[_over]) * st::playerDuration); if (!_a_state.animating()) _a_state.start(); } else { result = false; } _over = newState; if (newState != OverNone) { _stateAnimations.remove(-_over); _stateAnimations[_over] = getms() - (_stateHovers[_over] * st::playerDuration); if (!_a_state.animating()) _a_state.start(); setCursor(style::cur_pointer); } else { setCursor(style::cur_default); } } } void PlayerWidget::updateOverRect(OverState state) { switch (state) { case OverPrev: rtlupdate(_prevRect); break; case OverPlay: rtlupdate(_playRect); break; case OverNext: rtlupdate(_nextRect); break; case OverClose: rtlupdate(_closeRect); break; case OverVolume: rtlupdate(_volumeRect); break; case OverFull: rtlupdate(_fullRect); break; case OverRepeat: rtlupdate(_repeatRect); break; case OverPlayback: rtlupdate(_playbackRect); break; } } void PlayerWidget::updateControls() { _fullAvailable = (_index >= 0); History *history = _msgmigrated ? _migrated : _history; _prevAvailable = _fullAvailable && ((_index > 0) || (_index == 0 && _migrated && !_msgmigrated && !_migrated->overview[OverviewMusicFiles].isEmpty())); _nextAvailable = _fullAvailable && ((_index < history->overview[OverviewMusicFiles].size() - 1) || (_msgmigrated && _index == _migrated->overview[OverviewMusicFiles].size() - 1 && _history->overviewLoaded(OverviewMusicFiles) && _history->overviewCount(OverviewMusicFiles) > 0)); resizeEvent(0); update(); if (_index >= 0 && _index < MediaOverviewStartPerPage) { if (!_history->overviewLoaded(OverviewMusicFiles) || (_migrated && !_migrated->overviewLoaded(OverviewMusicFiles))) { if (App::main()) { if (_msgmigrated || (_migrated && _index == 0 && _history->overviewLoaded(OverviewMusicFiles))) { App::main()->loadMediaBack(_migrated->peer, OverviewMusicFiles); } else { App::main()->loadMediaBack(_history->peer, OverviewMusicFiles); if (_migrated && _index == 0 && _migrated->overview[OverviewMusicFiles].isEmpty() && !_migrated->overviewLoaded(OverviewMusicFiles)) { App::main()->loadMediaBack(_migrated->peer, OverviewMusicFiles); } } if (_msgmigrated && !_history->overviewCountLoaded(OverviewMusicFiles)) { App::main()->preloadOverview(_history->peer, OverviewMusicFiles); } } } } } void PlayerWidget::findCurrent() { _index = -1; if (!_history || !_song.contextId().msg) return; const History::MediaOverview *o = &(_msgmigrated ? _migrated : _history)->overview[OverviewMusicFiles]; if ((_msgmigrated ? _migrated : _history)->channelId() == _song.contextId().channel) { for (int i = 0, l = o->size(); i < l; ++i) { if (o->at(i) == _song.contextId().msg) { _index = i; break; } } } preloadNext(); } void PlayerWidget::preloadNext() { if (_index < 0) return; History *history = _msgmigrated ? _migrated : _history; const History::MediaOverview *o = &history->overview[OverviewMusicFiles]; HistoryItem *next = 0; if (_index < o->size() - 1) { next = App::histItemById(history->channelId(), o->at(_index + 1)); } else if (_msgmigrated && _index == o->size() - 1 && _history->overviewLoaded(OverviewMusicFiles) && _history->overviewCount(OverviewMusicFiles) > 0) { next = App::histItemById(_history->channelId(), _history->overview[OverviewMusicFiles].at(0)); } else if (_msgmigrated && _index == o->size() - 1 && !_history->overviewCountLoaded(OverviewMusicFiles)) { if (App::main()) App::main()->preloadOverview(_history->peer, OverviewMusicFiles); } if (next) { if (HistoryDocument *document = static_cast(next->getMedia())) { DocumentData *d = document->getDocument(); if (!d->loaded(DocumentData::FilePathResolveSaveFromDataSilent)) { DocumentOpenClickHandler::doOpen(d, ActionOnLoadNone); } } } } void PlayerWidget::startPlay(const FullMsgId &msgId) { if (HistoryItem *item = App::histItemById(msgId)) { if (HistoryDocument *doc = static_cast(item->getMedia())) { audioPlayer()->play(AudioMsgId(doc->getDocument(), item->fullId())); updateState(); } } } void PlayerWidget::clearSelection() { for (StateAnimations::const_iterator i = _stateAnimations.cbegin(); i != _stateAnimations.cend(); ++i) { _stateHovers[qAbs(i.key())] = 0; } _stateAnimations.clear(); } void PlayerWidget::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) { if (_history && (_history->peer == peer || (_migrated && _migrated->peer == peer)) && type == OverviewMusicFiles) { _index = -1; History *history = _msgmigrated ? _migrated : _history; if (history->channelId() == _song.contextId().channel && _song.contextId().msg) { for (int i = 0, l = history->overview[OverviewMusicFiles].size(); i < l; ++i) { if (history->overview[OverviewMusicFiles].at(i) == _song.contextId().msg) { _index = i; preloadNext(); break; } } } updateControls(); } } bool PlayerWidget::seekingSong(const AudioMsgId &song) const { return (_down == OverPlayback) && (song == _song); } void PlayerWidget::openPlayer() { _playerOpened = true; Shortcuts::enableMediaShortcuts(); } bool PlayerWidget::isOpened() const { return _playerOpened; } void PlayerWidget::closePlayer() { _playerOpened = false; Shortcuts::disableMediaShortcuts(); } void PlayerWidget::showPlayer() { TWidget::show(); } void PlayerWidget::hidePlayer() { clearSelection(); TWidget::hide(); } void PlayerWidget::step_state(uint64 ms, bool timer) { for (StateAnimations::iterator i = _stateAnimations.begin(); i != _stateAnimations.cend();) { int32 over = qAbs(i.key()); updateOverRect(OverState(over)); float64 dt = float64(ms - i.value()) / st::playerDuration; if (dt >= 1) { _stateHovers[over] = (i.key() > 0) ? 1 : 0; i = _stateAnimations.erase(i); } else { _stateHovers[over] = (i.key() > 0) ? dt : (1 - dt); ++i; } } if (_stateAnimations.isEmpty()) { _a_state.stop(); } } void PlayerWidget::mouseMoveEvent(QMouseEvent *e) { _lastMousePos = e->globalPos(); updateSelected(); } void PlayerWidget::leaveEvent(QEvent *e) { _lastMousePos = QCursor::pos(); updateSelected(); } void PlayerWidget::updateSelected() { QPoint pos(myrtlpoint(mapFromGlobal(_lastMousePos))); if (_down == OverVolume) { int32 delta = (pos.x() - _volumeRect.x()) - _downCoord; float64 startFrom = snap((_downCoord - ((_volumeRect.width() - st::playerVolume.pxWidth()) / 2)) / float64(st::playerVolume.pxWidth()), 0., 1.); float64 add = delta / float64(4 * st::playerVolume.pxWidth()), result = snap(startFrom + add, 0., 1.); if (result != Global::SongVolume()) { Global::SetSongVolume(result); emit audioPlayer()->songVolumeChanged(); rtlupdate(_volumeRect); } } else if (_down == OverPlayback) { _downProgress = snap((pos.x() - _playbackRect.x()) / float64(_playbackRect.width()), 0., 1.); rtlupdate(_playbackRect); updateDownTime(); } else if (_down == OverNone) { bool inInfo = ((pos.x() >= _infoRect.x()) && (pos.x() < _fullRect.x() + _fullRect.width()) && (pos.y() >= _playRect.y()) && (pos.y() <= _playRect.y() + _playRect.height())); if (_prevAvailable && _prevRect.contains(pos)) { updateOverState(OverPrev); } else if (_nextAvailable && _nextRect.contains(pos)) { updateOverState(OverNext); } else if (_playRect.contains(pos)) { updateOverState(OverPlay); } else if (_closeRect.contains(pos)) { updateOverState(OverClose); } else if (_volumeRect.contains(pos)) { updateOverState(OverVolume); } else if (_repeatRect.contains(pos)) { updateOverState(OverRepeat); } else if (_duration && _playbackRect.contains(pos)) { updateOverState(OverPlayback); } else if (_fullAvailable && inInfo) { updateOverState(OverFull); } else if (_over != OverNone) { updateOverState(OverNone); } } } void PlayerWidget::mouseReleaseEvent(QMouseEvent *e) { if (_down == OverVolume) { mouseMoveEvent(e); Local::writeUserSettings(); } else if (_down == OverPlayback) { mouseMoveEvent(e); AudioMsgId playing; auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song); if (playing == _song && playbackState.duration) { _downDuration = playbackState.duration; audioPlayer()->seek(qRound(_downProgress * _downDuration)); _showPause = true; a_progress = anim::fvalue(_downProgress, _downProgress); _a_progress.stop(); } update(); } else if (_down == OverClose && _over == OverClose) { closePressed(); } _down = OverNone; } void PlayerWidget::playPressed() { if (!_song || isHidden()) return; AudioMsgId playing; auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song); if (playing == _song && !(playbackState.state & AudioPlayerStoppedMask)) { if (playbackState.state == AudioPlayerPausing || playbackState.state == AudioPlayerPaused || playbackState.state == AudioPlayerPausedAtEnd) { audioPlayer()->pauseresume(AudioMsgId::Type::Song); } } else { audioPlayer()->play(_song); if (App::main()) App::main()->audioPlayProgress(_song); } } void PlayerWidget::pausePressed() { if (!_song || isHidden()) return; AudioMsgId playing; auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song); if (playing == _song && !(playbackState.state & AudioPlayerStoppedMask)) { if (playbackState.state == AudioPlayerStarting || playbackState.state == AudioPlayerResuming || playbackState.state == AudioPlayerPlaying || playbackState.state == AudioPlayerFinishing) { audioPlayer()->pauseresume(AudioMsgId::Type::Song); } } } void PlayerWidget::playPausePressed() { if (!_song || isHidden()) return; AudioMsgId playing; auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song); if (playing == _song && !(playbackState.state & AudioPlayerStoppedMask)) { audioPlayer()->pauseresume(AudioMsgId::Type::Song); } else { audioPlayer()->play(_song); if (App::main()) App::main()->audioPlayProgress(_song); } } void PlayerWidget::prevPressed() { if (isHidden()) return; History *history = _msgmigrated ? _migrated : _history; const History::MediaOverview *o = history ? &history->overview[OverviewMusicFiles] : 0; if (audioPlayer() && o && _index > 0 && _index <= o->size() && !o->isEmpty()) { startPlay(FullMsgId(history->channelId(), o->at(_index - 1))); } else if (!_index && _history && _migrated && !_msgmigrated) { o = &_migrated->overview[OverviewMusicFiles]; if (!o->isEmpty()) { startPlay(FullMsgId(_migrated->channelId(), o->at(o->size() - 1))); } } } void PlayerWidget::nextPressed() { if (isHidden()) return; History *history = _msgmigrated ? _migrated : _history; const History::MediaOverview *o = history ? &history->overview[OverviewMusicFiles] : 0; if (audioPlayer() && o && _index >= 0 && _index < o->size() - 1) { startPlay(FullMsgId(history->channelId(), o->at(_index + 1))); } else if (o && (_index == o->size() - 1) && _msgmigrated && _history->overviewLoaded(OverviewMusicFiles)) { o = &_history->overview[OverviewMusicFiles]; if (!o->isEmpty()) { startPlay(FullMsgId(_history->channelId(), o->at(0))); } } } void PlayerWidget::stopPressed() { if (!_song || isHidden()) return; audioPlayer()->stop(AudioMsgId::Type::Song); } void PlayerWidget::closePressed() { stopPressed(); if (App::main()) App::main()->closePlayer(); } void PlayerWidget::resizeEvent(QResizeEvent *e) { int32 availh = (height() - st::playerLineHeight); int32 ch = st::playerPlay.pxHeight() + st::playerSkip, ct = (availh - ch) / 2; _playbackRect = QRect(Adaptive::OneColumn() ? 0 : st::lineWidth, height() - st::playerMoverSize.height(), width() - (Adaptive::OneColumn() ? 0 : st::lineWidth), st::playerMoverSize.height()); _prevRect = _fullAvailable ? QRect(st::playerSkip / 2, ct, st::playerPrev.pxWidth() + st::playerSkip, ch) : QRect(); _playRect = QRect(_fullAvailable ? (_prevRect.x() + _prevRect.width()) : (st::playerSkip / 2), ct, st::playerPlay.pxWidth() + st::playerSkip, ch); _nextRect = _fullAvailable ? QRect(_playRect.x() + _playRect.width(), ct, st::playerNext.pxWidth() + st::playerSkip, ch) : QRect(); _closeRect = QRect(width() - st::playerSkip / 2 - st::playerClose.pxWidth() - st::playerSkip, ct, st::playerClose.pxWidth() + st::playerSkip, ch); _volumeRect = QRect(_closeRect.x() - st::playerVolume.pxWidth() - st::playerSkip, ct, st::playerVolume.pxWidth() + st::playerSkip, ch); _repeatRect = QRect(_volumeRect.x() - st::playerRepeat.pxWidth() - st::playerSkip, ct, st::playerRepeat.pxWidth() + st::playerSkip, ch); _fullRect = _fullAvailable ? QRect(_repeatRect.x() - st::playerFull.pxWidth() - st::playerSkip, ct, st::playerFull.pxWidth() + st::playerSkip, ch) : QRect(); int32 infoLeft = (_fullAvailable ? (_nextRect.x() + _nextRect.width()) : (_playRect.x() + _playRect.width())); _infoRect = QRect(infoLeft + st::playerSkip / 2, 0, (_fullAvailable ? _fullRect.x() : _repeatRect.x()) - infoLeft - st::playerSkip, availh); update(); } void PlayerWidget::step_progress(float64 ms, bool timer) { float64 dt = ms / (2 * AudioVoiceMsgUpdateView); if (_duration && dt >= 1) { _a_progress.stop(); a_progress.finish(); a_loadProgress.finish(); } else { a_progress.update(qMin(dt, 1.), anim::linear); a_loadProgress.update(1. - (st::radialDuration / (st::radialDuration + ms)), anim::linear); } if (timer) rtlupdate(_playbackRect); } void PlayerWidget::updateState() { updateState(AudioMsgId(), AudioPlaybackState()); } void PlayerWidget::updateState(AudioMsgId playing, AudioPlaybackState playbackState) { if (!playing) { playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song); } bool songChanged = false; if (playing && _song != playing) { songChanged = true; _song = playing; if (HistoryItem *item = App::histItemById(_song.contextId())) { _history = item->history(); if (_history->peer->migrateFrom()) { _migrated = App::history(_history->peer->migrateFrom()->id); _msgmigrated = false; } else if (_history->peer->migrateTo()) { _migrated = _history; _history = App::history(_migrated->peer->migrateTo()->id); _msgmigrated = true; } findCurrent(); } else { _history = nullptr; _msgmigrated = false; _index = -1; } auto song = _song.audio()->song(); if (song->performer.isEmpty()) { _name.setText(st::linkFont, song->title.isEmpty() ? (_song.audio()->name.isEmpty() ? qsl("Unknown Track") : _song.audio()->name) : song->title, _textNameOptions); } else { TextCustomTagsMap custom; custom.insert(QChar('c'), qMakePair(textcmdStartLink(1), textcmdStopLink())); _name.setRichText(st::linkFont, QString::fromUtf8("[c]%1[/c] \xe2\x80\x93 %2").arg(textRichPrepare(song->performer)).arg(song->title.isEmpty() ? qsl("Unknown Track") : textRichPrepare(song->title)), _textNameOptions, custom); } updateControls(); } qint64 position = 0, duration = 0, display = 0; if (playing == _song) { if (!(playbackState.state & AudioPlayerStoppedMask) && playbackState.state != AudioPlayerFinishing) { display = position = playbackState.position; duration = playbackState.duration; } else { display = playbackState.duration; } display = display / (playbackState.frequency ? playbackState.frequency : AudioVoiceMsgFrequency); } else if (_song) { display = _song.audio()->song()->duration; } bool showPause = false, stopped = ((playbackState.state & AudioPlayerStoppedMask) || playbackState.state == AudioPlayerFinishing); bool wasPlaying = (_duration != 0); if (!stopped) { showPause = (playbackState.state == AudioPlayerPlaying || playbackState.state == AudioPlayerResuming || playbackState.state == AudioPlayerStarting); } QString time; float64 progress = 0.; int32 loaded; float64 loadProgress = 1.; if (duration || !_song || !_song.audio() || !_song.audio()->loading()) { time = (_down == OverPlayback) ? _time : formatDurationText(display); progress = duration ? snap(float64(position) / duration, 0., 1.) : 0.; loaded = duration ? _song.audio()->size : 0; } else { loaded = _song.audio()->loading() ? _song.audio()->loadOffset() : 0; time = formatDownloadText(loaded, _song.audio()->size); loadProgress = snap(float64(loaded) / qMax(_song.audio()->size, 1), 0., 1.); } if (time != _time || showPause != _showPause) { if (_time != time) { _time = time; _timeWidth = st::linkFont->width(_time); } _showPause = showPause; if (duration != _duration || position != _position || loaded != _loaded) { if (!songChanged && ((!stopped && duration && _duration) || (!duration && _loaded != loaded))) { a_progress.start(progress); a_loadProgress.start(loadProgress); _a_progress.start(); } else { a_progress = anim::fvalue(progress, progress); a_loadProgress = anim::fvalue(loadProgress, loadProgress); _a_progress.stop(); } _position = position; _duration = duration; _loaded = loaded; } update(); } else if (duration != _duration || position != _position || loaded != _loaded) { if (!songChanged && ((!stopped && duration && _duration) || (!duration && _loaded != loaded))) { a_progress.start(progress); a_loadProgress.start(loadProgress); _a_progress.start(); } else { a_progress = anim::fvalue(progress, progress); a_loadProgress = anim::fvalue(loadProgress, loadProgress); _a_progress.stop(); } _position = position; _duration = duration; _loaded = loaded; } if (wasPlaying && playbackState.state == AudioPlayerStoppedAtEnd) { if (_repeat) { if (_song.audio()) { audioPlayer()->play(_song, OverviewMusicFiles); updateState(); } } else { nextPressed(); } } if (songChanged) { emit playerSongChanged(_song.contextId()); } }