diff --git a/packaging/arch/PKGBUILD b/packaging/arch/PKGBUILD index 0d8bce7..220de87 100644 --- a/packaging/arch/PKGBUILD +++ b/packaging/arch/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: local pkgname=qemby -pkgver=0.0.6 +pkgver=0.0.7 pkgrel=1 pkgdesc="Desktop client for Emby and Jellyfin media servers" arch=('x86_64') diff --git a/src/qEmbyApp/components/mpvcontroller.cpp b/src/qEmbyApp/components/mpvcontroller.cpp index 16c49a2..cf06c16 100644 --- a/src/qEmbyApp/components/mpvcontroller.cpp +++ b/src/qEmbyApp/components/mpvcontroller.cpp @@ -131,7 +131,7 @@ bool MpvController::init() { mpv_set_option_string(m_mpv, "sub-hinting", "none"); mpv_set_option_string(m_mpv, "sub-ass-hinting", "none"); - mpv_request_log_messages(m_mpv, "info"); + mpv_request_log_messages(m_mpv, "warn"); @@ -223,6 +223,10 @@ bool MpvController::init() { observeProperty("track-list", MPV_FORMAT_NODE); observeProperty("sid", MPV_FORMAT_NODE); observeProperty("secondary-sid", MPV_FORMAT_NODE); + observeProperty("video-params/dw", MPV_FORMAT_DOUBLE); + observeProperty("video-params/dh", MPV_FORMAT_DOUBLE); + observeProperty("width", MPV_FORMAT_INT64); + observeProperty("height", MPV_FORMAT_INT64); observeProperty("paused-for-cache", MPV_FORMAT_FLAG); @@ -289,7 +293,10 @@ void MpvController::eventHandler() { auto *msg = static_cast(event->data); if (msg) { - qDebug().noquote() << QString("[MPV %1] %2").arg(msg->prefix, QString::fromUtf8(msg->text).trimmed()); + qWarning().noquote() + << QString("[MPV %1] %2") + .arg(msg->prefix, + QString::fromUtf8(msg->text).trimmed()); } break; } diff --git a/src/qEmbyApp/components/nativedanmakuoverlay.cpp b/src/qEmbyApp/components/nativedanmakuoverlay.cpp index 3640b27..b9bc97e 100644 --- a/src/qEmbyApp/components/nativedanmakuoverlay.cpp +++ b/src/qEmbyApp/components/nativedanmakuoverlay.cpp @@ -215,6 +215,7 @@ void NativeDanmakuOverlay::setRenderOptions(DanmakuRenderOptions options) const bool appearanceChanged = !renderAppearanceEqual(m_options, options); m_options = options; + markSurfaceRectDirty(); requestScheduleRebuild(false, appearanceChanged); } @@ -274,6 +275,7 @@ void NativeDanmakuOverlay::setBottomSubtitleProtected(bool enabled) } m_bottomSubtitleProtected = enabled; + markSurfaceRectDirty(); requestScheduleRebuild(false, false); } @@ -291,7 +293,7 @@ void NativeDanmakuOverlay::paintEvent(QPaintEvent *event) painter.setRenderHint(QPainter::SmoothPixmapTransform, false); const qreal positionMs = currentPositionMsPrecise(); - const QRectF surfaceRect = effectiveSurfaceRect(); + const QRectF surfaceRect = cachedEffectiveSurfaceRect(); const qreal dpr = std::max(1.0, devicePixelRatioF()); const auto alignToDevicePixel = [dpr](qreal value) { return std::round(value * dpr) / dpr; @@ -336,6 +338,7 @@ void NativeDanmakuOverlay::paintEvent(QPaintEvent *event) void NativeDanmakuOverlay::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); + markSurfaceRectDirty(); requestScheduleRebuild(false, false); updateScreenRefreshBinding(); } @@ -390,7 +393,7 @@ void NativeDanmakuOverlay::rebuildScheduleNow() return; } - const QRectF surfaceRect = effectiveSurfaceRect(); + const QRectF surfaceRect = cachedEffectiveSurfaceRect(); const qreal viewportScale = viewportScaleForSize(size()); const QStringList blockedKeywords = normalizeBlockedKeywords(m_options.blockedKeywords); @@ -738,6 +741,48 @@ void NativeDanmakuOverlay::updateScreenRefreshBinding() syncFrameTimer(); } +void NativeDanmakuOverlay::markSurfaceRectDirty() +{ + m_surfaceRectDirty = true; +} + +QRectF NativeDanmakuOverlay::cachedEffectiveSurfaceRect() const +{ + if (m_surfaceRectDirty) { + m_cachedSurfaceRect = effectiveSurfaceRect(); + m_surfaceRectDirty = false; + } + return m_cachedSurfaceRect; +} + +void NativeDanmakuOverlay::updateVideoDisplaySize(const QString &property, + const QVariant &value) +{ + qreal *target = nullptr; + if (property == QLatin1String("video-params/dw")) { + target = &m_videoParamsDisplayWidth; + } else if (property == QLatin1String("video-params/dh")) { + target = &m_videoParamsDisplayHeight; + } else if (property == QLatin1String("width")) { + target = &m_videoFallbackWidth; + } else if (property == QLatin1String("height")) { + target = &m_videoFallbackHeight; + } + + if (!target) { + return; + } + + const qreal newValue = value.toDouble(); + if (std::abs(*target - newValue) <= 0.5) { + return; + } + + *target = newValue; + markSurfaceRectDirty(); + requestScheduleRebuild(false, false); +} + void NativeDanmakuOverlay::refreshBasePosition(qint64 positionMs) { m_basePositionMs = std::max(0, positionMs); @@ -769,20 +814,14 @@ QRectF NativeDanmakuOverlay::effectiveSurfaceRect() const return surface; } - if (m_controller) { - qreal displayWidth = - m_controller->getProperty(QStringLiteral("video-params/dw")).toDouble(); - qreal displayHeight = - m_controller->getProperty(QStringLiteral("video-params/dh")).toDouble(); - if (displayWidth <= 0.0 || displayHeight <= 0.0) { - displayWidth = - m_controller->getProperty(QStringLiteral("width")).toDouble(); - displayHeight = - m_controller->getProperty(QStringLiteral("height")).toDouble(); - } - - if (displayWidth > 0.0 && displayHeight > 0.0 && - surface.width() > 0.0 && surface.height() > 0.0) { + const qreal displayWidth = + m_videoParamsDisplayWidth > 0.0 ? m_videoParamsDisplayWidth + : m_videoFallbackWidth; + const qreal displayHeight = + m_videoParamsDisplayHeight > 0.0 ? m_videoParamsDisplayHeight + : m_videoFallbackHeight; + if (displayWidth > 0.0 && displayHeight > 0.0) { + if (surface.width() > 0.0 && surface.height() > 0.0) { const qreal videoAspect = displayWidth / displayHeight; const qreal viewportAspect = surface.width() / surface.height(); if (videoAspect > 0.0 && viewportAspect > 0.0) { @@ -810,7 +849,7 @@ QRectF NativeDanmakuOverlay::effectiveSurfaceRect() const qreal NativeDanmakuOverlay::laneTop(const ScheduledItem &item) const { - const QRectF surfaceRect = effectiveSurfaceRect(); + const QRectF surfaceRect = cachedEffectiveSurfaceRect(); if (item.type == ItemType::Bottom) { return surfaceRect.bottom() - m_bottomMargin - (item.laneIndex + 1) * m_lineHeight; @@ -901,10 +940,7 @@ void NativeDanmakuOverlay::handleControllerPropertyChanged(const QString &proper const qint64 positionMs = currentPositionMs(); m_playbackSpeed = qBound(0.1, value.toDouble(), 16.0); refreshBasePosition(positionMs); - } else if (property == QLatin1String("video-params/dw") || - property == QLatin1String("video-params/dh") || - property == QLatin1String("width") || - property == QLatin1String("height")) { - requestScheduleRebuild(false, false); + } else { + updateVideoDisplaySize(property, value); } } diff --git a/src/qEmbyApp/components/nativedanmakuoverlay.h b/src/qEmbyApp/components/nativedanmakuoverlay.h index fbc6403..690b4ba 100644 --- a/src/qEmbyApp/components/nativedanmakuoverlay.h +++ b/src/qEmbyApp/components/nativedanmakuoverlay.h @@ -73,6 +73,10 @@ private: int targetFrameIntervalMs() const; QScreen *targetScreen() const; void updateScreenRefreshBinding(); + void markSurfaceRectDirty(); + QRectF cachedEffectiveSurfaceRect() const; + void updateVideoDisplaySize(const QString &property, + const QVariant &value); void refreshBasePosition(qint64 positionMs); qreal currentPositionMsPrecise() const; qint64 currentPositionMs() const; @@ -111,11 +115,17 @@ private: qreal m_outlineSize = 0.0; qreal m_shadowOffset = 0.0; qreal m_spritePadding = 0.0; + mutable QRectF m_cachedSurfaceRect; + qreal m_videoParamsDisplayWidth = 0.0; + qreal m_videoParamsDisplayHeight = 0.0; + qreal m_videoFallbackWidth = 0.0; + qreal m_videoFallbackHeight = 0.0; bool m_visibleRequested = false; bool m_playbackPaused = true; bool m_bottomSubtitleProtected = false; bool m_scheduleDirty = false; bool m_clearSpriteCacheOnNextRebuild = false; + mutable bool m_surfaceRectDirty = true; }; #endif diff --git a/src/qEmbyApp/mainwindow.cpp b/src/qEmbyApp/mainwindow.cpp index 8db4c22..b069399 100644 --- a/src/qEmbyApp/mainwindow.cpp +++ b/src/qEmbyApp/mainwindow.cpp @@ -101,23 +101,8 @@ MainWindow::MainWindow(QWidget *parent) }); } }); - connect(m_trayManager, &TrayManager::quitRequested, this, [this]() { - m_realQuit = true; - - if (auto *player = m_homeView->activePlayerView()) { - player->stopAndReport(); - - QElapsedTimer wait; - wait.start(); - while (wait.elapsed() < 500) { - QCoreApplication::processEvents(QEventLoop::AllEvents, 50); - } - } - if(m_core && m_core->authService()) { - m_core->authService()->logout(); - } - qApp->quit(); - }); + connect(m_trayManager, &TrayManager::quitRequested, this, + &MainWindow::requestApplicationQuit); @@ -1045,6 +1030,46 @@ void MainWindow::navigateToLogin() { fadeIn->start(QAbstractAnimation::DeleteWhenStopped); } +void MainWindow::requestApplicationQuit() +{ + if (m_quitPending) { + return; + } + + m_quitPending = true; + m_realQuit = true; + const bool stoppedPlayback = stopActivePlaybackForQuit(); + if (stoppedPlayback) { + QTimer::singleShot(500, this, &MainWindow::finishApplicationQuit); + return; + } + + finishApplicationQuit(); +} + +bool MainWindow::stopActivePlaybackForQuit() +{ + if (!m_homeView) { + return false; + } + + auto *player = m_homeView->activePlayerView(); + if (!player) { + return false; + } + + player->stopAndReport(); + return true; +} + +void MainWindow::finishApplicationQuit() +{ + if (m_core && m_core->authService()) { + m_core->authService()->logout(); + } + qApp->quit(); +} + bool MainWindow::eventFilter(QObject *watched, QEvent *event) { if (watched == qApp && event->type() == QEvent::ApplicationStateChange) { @@ -1172,16 +1197,11 @@ void MainWindow::closeEvent(QCloseEvent *event) hide(); event->ignore(); } else { - - if (auto *player = m_homeView->activePlayerView()) { - player->stopAndReport(); - - QElapsedTimer wait; - wait.start(); - while (wait.elapsed() < 500) { - QCoreApplication::processEvents(QEventLoop::AllEvents, 50); - } + if (!m_quitPending) { + requestApplicationQuit(); + event->ignore(); + return; } - event->accept(); + event->accept(); } } diff --git a/src/qEmbyApp/mainwindow.h b/src/qEmbyApp/mainwindow.h index 90a3660..32ddfe6 100644 --- a/src/qEmbyApp/mainwindow.h +++ b/src/qEmbyApp/mainwindow.h @@ -33,6 +33,9 @@ protected: void closeEvent(QCloseEvent *event) override; private: + void requestApplicationQuit(); + bool stopActivePlaybackForQuit(); + void finishApplicationQuit(); void setupGlobalSearchHistory(); void hideGlobalSearchTransientUi(); void updateGlobalSearchCompleter(const QString &text = QString()); @@ -59,6 +62,7 @@ private: bool m_themeAnimating{false}; bool m_wasPausedByTray{false}; bool m_viewTransitionInProgress{false}; + bool m_quitPending{false}; }; #endif