feat: complete QML shell migration and ship 0.0.8 Arch package
Replace the Widgets main shell with AppShellHost + QML routes, add a QML independent player window, and remove the legacy player/page Widgets stack. Also wire settings cache/proxy actions, fix quit-time engine teardown, improve list/home scroll performance, and update Arch packaging to 0.0.8.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
#include <config/configstore.h>
|
||||
#include <config/config_keys.h>
|
||||
#include <models/media/playbackinfo.h>
|
||||
#include "../components/playerwindow.h"
|
||||
#include "../quick/independentplayerhost.h"
|
||||
#include "../components/moderntoast.h"
|
||||
#include "../utils/logredactionutils.h"
|
||||
#include "../utils/playerpreferenceutils.h"
|
||||
@@ -39,16 +39,21 @@ void PlaybackManager::init(QEmbyCore* core)
|
||||
m_core = core;
|
||||
}
|
||||
|
||||
void PlaybackManager::setEmbeddedHostAvailable(bool available)
|
||||
{
|
||||
m_embeddedHostAvailable = available;
|
||||
}
|
||||
|
||||
void PlaybackManager::startPlayback(const QString& mediaId, const QString& title,
|
||||
const QString& streamUrl, long long startPositionTicks,
|
||||
const QVariant& extraData)
|
||||
{
|
||||
if (!m_core) return;
|
||||
|
||||
|
||||
if (m_independentWindow) {
|
||||
m_independentWindow->close();
|
||||
|
||||
// Drop any existing independent window before starting a new session
|
||||
// (embedded, external, or a replacement independent play).
|
||||
if (m_independentHost) {
|
||||
m_independentHost->close();
|
||||
}
|
||||
|
||||
qDebug() << "[PlaybackManager] ===== startPlayback =====";
|
||||
@@ -88,6 +93,13 @@ void PlaybackManager::startPlayback(const QString& mediaId, const QString& title
|
||||
|
||||
|
||||
|
||||
// No embedded host: fall back to QML IndependentPlayerHost.
|
||||
if (!m_embeddedHostAvailable) {
|
||||
qDebug() << "[PlaybackManager] → Mode: Independent window (no embedded host)";
|
||||
launchIndependentWindow(mediaId, title, streamUrl, startPositionTicks, extraData);
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "[PlaybackManager] → Mode: Embedded player";
|
||||
Q_EMIT requestEmbeddedPlay(mediaId, title, streamUrl, startPositionTicks, extraData);
|
||||
}
|
||||
@@ -98,22 +110,25 @@ void PlaybackManager::startInternalPlayback(const QString& mediaId, const QStrin
|
||||
{
|
||||
if (!m_core) return;
|
||||
|
||||
|
||||
if (m_independentWindow) {
|
||||
m_independentWindow->close();
|
||||
if (m_independentHost) {
|
||||
m_independentHost->close();
|
||||
}
|
||||
|
||||
qDebug() << "[PlaybackManager] ===== startInternalPlayback =====";
|
||||
qDebug() << "[PlaybackManager] mediaId:" << mediaId << "title:" << title;
|
||||
qDebug() << "[PlaybackManager] streamUrl:" << LogRedactionUtils::url(streamUrl);
|
||||
|
||||
|
||||
bool independent = ConfigStore::instance()->get<bool>(ConfigKeys::PlayerIndependentWindow, false);
|
||||
if (independent) {
|
||||
qDebug() << "[PlaybackManager] → Mode: Independent window (internal)";
|
||||
launchIndependentWindow(mediaId, title, streamUrl, startPositionTicks, extraData);
|
||||
return;
|
||||
}
|
||||
if (!m_embeddedHostAvailable) {
|
||||
qDebug() << "[PlaybackManager] → Mode: Independent window (internal, no embedded host)";
|
||||
launchIndependentWindow(mediaId, title, streamUrl, startPositionTicks, extraData);
|
||||
return;
|
||||
}
|
||||
qDebug() << "[PlaybackManager] → Mode: Embedded player (internal)";
|
||||
Q_EMIT requestEmbeddedPlay(mediaId, title, streamUrl, startPositionTicks, extraData);
|
||||
}
|
||||
@@ -127,6 +142,35 @@ void PlaybackManager::stopExternalPlayer()
|
||||
cleanupExternalPlayerState();
|
||||
}
|
||||
|
||||
bool PlaybackManager::stopActivePlaybackForQuit()
|
||||
{
|
||||
bool stopped = false;
|
||||
|
||||
// Independent QML player: close() deactivates PlayerViewModel (STOP report)
|
||||
// then destroys the secondary engine. Host emits closed → playbackFinished.
|
||||
if (m_independentHost) {
|
||||
m_independentHost->close();
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
// External player process: kill it and flush the final stop report. This
|
||||
// mirrors the process-exit handler's reportPlaybackStopped, but synchronously
|
||||
// on the quit path where we cannot wait for the async exit signal.
|
||||
if (m_extProcess && m_extProcess->state() != QProcess::NotRunning) {
|
||||
long long finalTicks = m_ipc ? m_ipc->currentPositionTicks() : 0;
|
||||
if (m_core && m_core->mediaService() && !m_currentMediaId.isEmpty() &&
|
||||
!m_currentPlaySessionId.isEmpty()) {
|
||||
m_core->mediaService()->reportPlaybackStopped(
|
||||
m_currentMediaId, m_currentMediaSourceId, finalTicks,
|
||||
m_currentPlaySessionId);
|
||||
}
|
||||
stopExternalPlayer();
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
return stopped;
|
||||
}
|
||||
|
||||
void PlaybackManager::cleanupExternalPlayerState()
|
||||
{
|
||||
if (m_progressTimer) {
|
||||
@@ -640,31 +684,16 @@ void PlaybackManager::launchIndependentWindow(const QString& mediaId, const QStr
|
||||
qDebug() << "[PlaybackManager] startPositionTicks:" << startPositionTicks
|
||||
<< "(" << ticksToTimeString(startPositionTicks) << ")";
|
||||
|
||||
|
||||
if (m_independentWindow) {
|
||||
qDebug() << "[PlaybackManager] Reusing existing independent window";
|
||||
m_independentWindow->playMedia(mediaId, title, streamUrl, startPositionTicks, extraData);
|
||||
m_independentWindow->raise();
|
||||
m_independentWindow->activateWindow();
|
||||
return;
|
||||
if (!m_independentHost) {
|
||||
qDebug() << "[PlaybackManager] Creating IndependentPlayerHost";
|
||||
m_independentHost = new IndependentPlayerHost(m_core, this);
|
||||
connect(m_independentHost, &IndependentPlayerHost::closed, this, [this]() {
|
||||
qDebug() << "[PlaybackManager] Independent player window closed";
|
||||
Q_EMIT playbackFinished();
|
||||
});
|
||||
} else {
|
||||
qDebug() << "[PlaybackManager] Reusing IndependentPlayerHost";
|
||||
}
|
||||
|
||||
qDebug() << "[PlaybackManager] Creating new independent window";
|
||||
|
||||
auto* playerWindow = new PlayerWindow(m_core);
|
||||
playerWindow->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
playerWindow->setMinimumSize(960, 540);
|
||||
playerWindow->resize(1280, 720);
|
||||
|
||||
|
||||
connect(playerWindow, &QObject::destroyed, this, [this]() {
|
||||
m_independentWindow = nullptr;
|
||||
qDebug() << "[PlaybackManager] Independent window destroyed";
|
||||
Q_EMIT playbackFinished();
|
||||
});
|
||||
|
||||
m_independentWindow = playerWindow;
|
||||
|
||||
playerWindow->show();
|
||||
playerWindow->playMedia(mediaId, title, streamUrl, startPositionTicks, extraData);
|
||||
m_independentHost->play(mediaId, title, streamUrl, startPositionTicks, extraData);
|
||||
}
|
||||
|
||||
@@ -9,48 +9,47 @@
|
||||
|
||||
class QEmbyCore;
|
||||
class QProcess;
|
||||
class PlayerWindow;
|
||||
class IndependentPlayerHost;
|
||||
class QTimer;
|
||||
struct MediaSourceInfo;
|
||||
|
||||
|
||||
|
||||
|
||||
class PlaybackManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static PlaybackManager* instance();
|
||||
|
||||
|
||||
void init(QEmbyCore* core);
|
||||
|
||||
|
||||
// Whether an in-shell embedded player host exists to receive
|
||||
// requestEmbeddedPlay (AppShellHost + PlayerRouteController). When false,
|
||||
// the embedded branch falls back to IndependentPlayerHost (QML window).
|
||||
void setEmbeddedHostAvailable(bool available);
|
||||
|
||||
void startPlayback(const QString& mediaId, const QString& title,
|
||||
const QString& streamUrl, long long startPositionTicks,
|
||||
const QVariant& extraData = QVariant());
|
||||
|
||||
|
||||
void startExternalPlayback(const QString& playerPath,
|
||||
const QString& mediaId, const QString& title,
|
||||
const QString& streamUrl, long long startPositionTicks,
|
||||
const QVariant& extraData = QVariant());
|
||||
|
||||
|
||||
void startInternalPlayback(const QString& mediaId, const QString& title,
|
||||
const QString& streamUrl, long long startPositionTicks,
|
||||
const QVariant& extraData = QVariant());
|
||||
|
||||
|
||||
void stopExternalPlayer();
|
||||
|
||||
// Stop independent QML player window and/or external process before quit.
|
||||
// Returns true if something was stopped (caller may defer quit briefly).
|
||||
bool stopActivePlaybackForQuit();
|
||||
|
||||
signals:
|
||||
|
||||
void requestEmbeddedPlay(const QString& mediaId, const QString& title,
|
||||
const QString& streamUrl, long long startPositionTicks,
|
||||
const QVariant& extraData);
|
||||
|
||||
|
||||
void playbackFinished();
|
||||
|
||||
private:
|
||||
@@ -66,11 +65,8 @@ private:
|
||||
const QString& streamUrl, long long startPositionTicks,
|
||||
const QVariant& extraData);
|
||||
|
||||
|
||||
|
||||
static PlayerType identifyPlayerType(const QString& playerPath);
|
||||
|
||||
|
||||
static QStringList buildPlayerArgs(PlayerType type,
|
||||
long long startPositionTicks,
|
||||
int audioStreamIndex,
|
||||
@@ -78,34 +74,27 @@ private:
|
||||
bool subtitleDisabled,
|
||||
const MediaSourceInfo* sourceInfo);
|
||||
|
||||
|
||||
static QString ticksToTimeString(long long ticks);
|
||||
|
||||
|
||||
static double ticksToSeconds(long long ticks);
|
||||
|
||||
|
||||
static int computeRelativeStreamIndex(const MediaSourceInfo& source,
|
||||
const QString& streamType,
|
||||
int globalIndex);
|
||||
|
||||
|
||||
static QString generateIpcEndpoint(PlayerType type);
|
||||
|
||||
|
||||
void cleanupExternalPlayerState();
|
||||
|
||||
|
||||
QEmbyCore* m_core = nullptr;
|
||||
QPointer<QProcess> m_extProcess;
|
||||
QPointer<PlayerWindow> m_independentWindow;
|
||||
bool m_embeddedHostAvailable = true;
|
||||
QPointer<QProcess> m_extProcess;
|
||||
QPointer<IndependentPlayerHost> m_independentHost;
|
||||
|
||||
|
||||
ExternalPlayerIPC* m_ipc = nullptr;
|
||||
QTimer* m_progressTimer = nullptr;
|
||||
QString m_currentMediaId;
|
||||
QString m_currentMediaSourceId;
|
||||
QString m_currentPlaySessionId;
|
||||
ExternalPlayerIPC* m_ipc = nullptr;
|
||||
QTimer* m_progressTimer = nullptr;
|
||||
QString m_currentMediaId;
|
||||
QString m_currentMediaSourceId;
|
||||
QString m_currentPlaySessionId;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user