From 3c02ef2fc8bc661b19aa8b9c8b1c050b9e5698e1 Mon Sep 17 00:00:00 2001 From: "Oskar Roesler (bionade24)" Date: Sun, 15 Sep 2024 21:17:36 +0200 Subject: [PATCH 01/10] Support URLs as command line argument. This makes it possible to use smtube with kokovp instead of smplayer. --- CMakeLists.txt | 2 ++ helper.cpp | 17 +++++++++++++---- kokovp.cpp | 29 ++++++++++++++--------------- kokovp.desktop | 2 +- kokovp.h | 3 ++- main.cpp | 6 ++++-- playercontroller.cpp | 40 ++++++++++++++++++++-------------------- playercontroller.h | 22 +++++++++++----------- program_arg.cpp | 17 +++++++++++++++++ program_arg.h | 19 +++++++++++++++++++ singleinstance.cpp | 23 ++++++++++++++++++----- singleinstance.h | 6 ++++-- 12 files changed, 125 insertions(+), 61 deletions(-) create mode 100644 program_arg.cpp create mode 100644 program_arg.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e05c09f..f9ba69a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,7 @@ qt_wrap_cpp(MOC_FILES mpvwidget.h playerwidget.h playercontroller.h + program_arg.h tracksmenu.h autohidewidget.h # From SMPlayer singleinstance.h @@ -76,6 +77,7 @@ set(MAIN_SRC mpvwidget.cpp playerwidget.cpp playercontroller.cpp + program_arg.cpp tracksmenu.cpp autohidewidget.cpp # From SMPlayer config.cpp diff --git a/helper.cpp b/helper.cpp index 74aa1cb..99fb227 100644 --- a/helper.cpp +++ b/helper.cpp @@ -121,11 +121,20 @@ void Helper::searchWithMaxDepth(QStringList &outList, const QStringList &filter, const QList Helper::pathsToUrls(const QStringList &paths) { QList ret; - for (auto &arg : paths) + for (const auto &arg : paths) { - QFileInfo f(arg); - if (f.exists()) - ret.append(QUrl::fromLocalFile(f.absoluteFilePath())); + // FIXME: URLs with spaces don't work + QUrl url(arg); + //QUrl::isLocalFile() is not reliable if the file doesn't exist + if (url.isValid() && !url.isLocalFile() && !url.host().isEmpty()) + ret.append(url); + else { + QFileInfo f(arg); + if (f.exists()) + ret.append(QUrl(f.absoluteFilePath())); + else + qDebug() << "Path " << f.path() << "does not exist, skipping";; + } } return ret; } diff --git a/kokovp.cpp b/kokovp.cpp index 19e79e0..07224eb 100644 --- a/kokovp.cpp +++ b/kokovp.cpp @@ -43,6 +43,7 @@ #include "config.h" #include "cache.h" #include "helper.h" +#include "program_arg.h" #include "prefs/prefdialog.h" #include "prefs/prefappearance.h" @@ -54,7 +55,8 @@ QString extfolderRewriteRule(const PlayerController::Track &t) if (!t.isExternal) return QString(); - QFileInfo fI(t.filename); + QFileInfo fI(t.mediaUrl); + QString fName = fI.fileName(); if (fI.fileName()==t.title) return QApplication::translate("KokoVP", "[EXT] ")+fI.dir().dirName(); @@ -82,8 +84,8 @@ KokoVP::KokoVP(QWidget *parent) createPlaylistDock(); connect(player, &PlayerController::tracksUpdated, this, &KokoVP::handleTracks); - connect(player, &PlayerController::fileMetaUpdated, playlist, &Playlist::setCurrentRowMetainfo); - connect(player, &PlayerController::endFile, this, &KokoVP::handleEOF); + connect(player, &PlayerController::mediaMetaUpdated, playlist, &Playlist::setCurrentRowMetainfo); + connect(player, &PlayerController::endMediaRessource, this, &KokoVP::handleEOF); connect(playerWidget, &PlayerWidget::draggedURLS, playlist, &Playlist::addURLs); connect(playlist, &Playlist::playRequest, this, qOverload(&KokoVP::playFile)); @@ -159,18 +161,15 @@ KokoVP::KokoVP(QWidget *parent) KokoVP::~KokoVP() { - fileSettings->saveSettingsFor(player->lastOpenFile(), true); // Always save time-pos on exit + fileSettings->saveSettingsFor(player->lastOpenMediaUrl(), true); // Always save time-pos on exit } -void KokoVP::handleNewMessage(QString msg) +void KokoVP::handleNewMessage(const ProgramArgument &msg) { - int del = msg.indexOf(':'); - QString cmd = msg.left(del); - QStringList args = msg.trimmed().mid(del+1).split(','); - if (cmd=="open") - playlist->addURLs(Helper::pathsToUrls(args)); - else if (cmd=="playlast") - QTimer::singleShot(100, playlist, &Playlist::playLast); // Workaroun to wait until Qt event loop and libmpv will be ready + if (msg.cmd=="open") + playlist->addURLs(Helper::pathsToUrls(msg.args)); + else if (msg.cmd=="playlast") + QTimer::singleShot(100, playlist, &Playlist::playLast); // Workaround to wait until Qt event loop and libmpv will be ready } void KokoVP::toggleFullscreen(bool on) @@ -499,7 +498,7 @@ void KokoVP::handleTracks() } if (Config::i().get("play_mode/keep_props", true).toBool()) - fileSettings->loadSettingsFor(player->currentFile(), Config::i().get("play_mode/keep_timepos", true).toBool()); + fileSettings->loadSettingsFor(player->currentMediaUrl(), Config::i().get("play_mode/keep_timepos", true).toBool()); player->setProp("pause", false); } @@ -509,7 +508,7 @@ void KokoVP::handleEOF(bool wasStopped) setWindowTitle("KokoVP"); if (Config::i().get("play_mode/keep_props", true).toBool()) - fileSettings->saveSettingsFor(player->lastOpenFile(), wasStopped); // If file is ended, then time-pos shouldn't be saved + fileSettings->saveSettingsFor(player->lastOpenMediaUrl(), wasStopped); // If file is ended, then time-pos shouldn't be saved // It's better to reset tracks to auto on EOF instead of load event, to ensure, that tracks' ids events will be generated in time if (wasStopped || !Config::i().get("play_mode/keep_tracknums_for_next", false).toBool()) @@ -579,7 +578,7 @@ void KokoVP::setAudioDevice(QAction *audioDeviceAction) void KokoVP::tryPlayCurrent() { - if (player->currentFile().isEmpty()) + if (player->currentMediaUrl().isEmpty()) playlist->playCurrent(); } diff --git a/kokovp.desktop b/kokovp.desktop index 6a62ea7..d8a4994 100644 --- a/kokovp.desktop +++ b/kokovp.desktop @@ -1,7 +1,7 @@ [Desktop Entry] Categories=Qt;AudioVideo;Player;Video; Comment=A modern mpv-based player -Exec=kokovp %F +Exec=kokovp %U GenericName=Media Player MimeType=application/ogg;application/x-ogg;application/mxf;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;audio/aac;audio/x-aac;audio/vnd.dolby.heaac.1;audio/vnd.dolby.heaac.2;audio/aiff;audio/x-aiff;audio/m4a;audio/x-m4a;application/x-extension-m4a;audio/mp1;audio/x-mp1;audio/mp2;audio/x-mp2;audio/mp3;audio/x-mp3;audio/mpeg;audio/mpeg2;audio/mpeg3;audio/mpg;audio/x-mpg;audio/rn-mpeg;audio/x-musepack;audio/ogg;audio/vnd.rn-realaudio;audio/wav;audio/x-pn-wav;audio/x-pn-windows-pcm;audio/x-realaudio;audio/x-pn-realaudio;audio/x-ms-wma;audio/x-wav;video/mpeg;video/x-mpeg2;video/x-mpeg3;video/mp4v-es;video/x-m4v;video/mp4;application/x-extension-mp4;video/divx;video/vnd.divx;video/msvideo;video/x-msvideo;video/ogg;video/quicktime;video/vnd.rn-realvideo;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvxvideo;video/x-avi;video/avi;video/x-flic;video/fli;video/x-flc;video/flv;video/x-flv;video/x-theora;video/x-theora+ogg;video/x-matroska;video/mkv;audio/x-matroska;application/x-matroska;video/webm;audio/webm;audio/vorbis;audio/x-vorbis;audio/x-vorbis+ogg;video/x-ogm;video/x-ogm+ogg;application/x-ogm;application/x-ogm-audio;application/x-ogm-video;application/x-shorten;audio/x-shorten;audio/x-ape;audio/x-wavpack;audio/x-tta;audio/AMR;audio/ac3;audio/eac3;audio/amr-wb;video/mp2t;audio/flac;audio/mp4;audio/x-pn-au;video/3gp;video/3gpp;video/3gpp2;audio/3gpp;audio/3gpp2;video/dv;audio/dv;audio/opus;audio/vnd.dts;audio/vnd.dts.hd;audio/x-adpcm;audio/vnd.wave;video/vnd.avi; Name=KokoVP diff --git a/kokovp.h b/kokovp.h index 8bfc6d0..0d25046 100644 --- a/kokovp.h +++ b/kokovp.h @@ -27,6 +27,7 @@ class Playlist; class TracksMenu; class AutohideWidget; class FileSettingsHash; +struct ProgramArgument; class QTableView; using QActionMap = QMap; @@ -40,7 +41,7 @@ class KokoVP : public QMainWindow ~KokoVP(); static KokoVP *i() { return inst; } QActionMap actionsMap() const { return p_actionsMap; } - void handleNewMessage(QString msg); + void handleNewMessage(const ProgramArgument &msg); private: void toggleFullscreen(bool on); diff --git a/main.cpp b/main.cpp index 6a66df5..499df28 100644 --- a/main.cpp +++ b/main.cpp @@ -23,6 +23,7 @@ #include #include +#include "program_arg.h" #include "singleinstance.h" int main(int argc, char *argv[]) @@ -36,6 +37,7 @@ int main(int argc, char *argv[]) parser.addHelpOption(); parser.addVersionOption(); parser.addPositionalArgument("media", QCoreApplication::translate("KokoVP", "Media files to play")); + parser.addPositionalArgument("urls", QCoreApplication::translate("KokoVP", "URLs to play")); // A boolean option with a single name (-p) QCommandLineOption newInstanceOption(QStringList() << "n" << "new-instance", QCoreApplication::translate("KokoVP", "Force open in new instance")); @@ -70,8 +72,8 @@ int main(int argc, char *argv[]) KokoVP w; if (parser.positionalArguments().length()>0) { - w.handleNewMessage("open:" + parser.positionalArguments().join(',')); - w.handleNewMessage("playlast"); + w.handleNewMessage(ProgramArgument("open", parser.positionalArguments())); + w.handleNewMessage(ProgramArgument("playlast")); } QObject::connect(&inst, &SingleInstance::newMessage, &w, &KokoVP::handleNewMessage); w.showNormal(); diff --git a/playercontroller.cpp b/playercontroller.cpp index 41beb54..8f3cf30 100644 --- a/playercontroller.cpp +++ b/playercontroller.cpp @@ -26,9 +26,9 @@ PlayerController::PlayerController(PlayerWidget *parent) prop("volume")->set(50); prop("pause")->set(true); p->setProp("audio-file-auto-exts", Extensions.audio()); - connect(p, &PlayerWidget::fileLoaded, this, &PlayerController::handleFileLoad); - connect(p, &PlayerWidget::endFile, this, &PlayerController::endFile); - connect(p, &PlayerWidget::endFile, this, &PlayerController::handleFileEnd); + connect(p, &PlayerWidget::fileLoaded, this, &PlayerController::handleMediaLoad); + connect(p, &PlayerWidget::endFile, this, &PlayerController::endMediaRessource); + connect(p, &PlayerWidget::endFile, this, &PlayerController::handleMediaEnd); } PropertyObserver *PlayerController::prop(QString name) @@ -51,31 +51,31 @@ void PlayerController::setOption(const QString &name, const QVariant &value) p->setOption(name, value); } -void PlayerController::handleFileEnd() +void PlayerController::handleMediaEnd() { - haveFile = false; - if (!queuedFile.isEmpty()) + haveMediaUrl = false; + if (!queuedMediaUrl.isEmpty()) { - QUrl f = queuedFile; - queuedFile = QUrl(); - open(f); + QUrl url = queuedMediaUrl; + queuedMediaUrl = QUrl(); + open(url); } } -void PlayerController::open(const QUrl &file) +void PlayerController::open(const QUrl &url) { - if (haveFile) + if (haveMediaUrl) { - queuedFile = file; + queuedMediaUrl = url; return stop(); } p_tracks.clear(); // Here we need to scan siblings folder for possible external subtitles and audio //, then set it to sub-file-paths and audio-file-paths OPTIONs (not properties) - if (file.isLocalFile()) + if (url.isLocalFile()) { - QDir mediaDir = QFileInfo(file.toLocalFile()).absoluteDir(); + QDir mediaDir = QFileInfo(url.toLocalFile()).absoluteDir(); if (p_extSubMaxDepth>=0 && p_extSubMode!="no") { @@ -94,7 +94,7 @@ void PlayerController::open(const QUrl &file) } } - p->command(QStringList{"loadfile", file.path()}); + p->command(QStringList{"loadfile", url.toString()}); } void PlayerController::stop() @@ -164,10 +164,10 @@ bool PlayerController::isPlaying() return !getProp("pause").toBool(); } -void PlayerController::handleFileLoad() +void PlayerController::handleMediaLoad() { - lastFile = currentFile(); - haveFile = true; + lastMediaUrl = currentMediaUrl(); + haveMediaUrl = true; bool ok; int tracksCount = getProp("track-list/count").toInt(&ok); assert(ok); @@ -188,12 +188,12 @@ void PlayerController::handleFileLoad() t.type = Track::TRACK_TYPE_SUB; if (t.isExternal) - t.filename = p->getProp(trackAddr + "external-filename").toString(); + t.mediaUrl = p->getProp(trackAddr + "external-mediaUrl").toString(); p_tracks.append(t); } //p->command(QVariantList({"vf", "clr", ""})); //SVP emit tracksUpdated(); - emit fileMetaUpdated(p->getProp("media-title").toString(), prop("duration")->get().toDouble()); + emit mediaMetaUpdated(p->getProp("media-title").toString(), prop("duration")->get().toDouble()); } diff --git a/playercontroller.h b/playercontroller.h index e72574c..3f8967d 100644 --- a/playercontroller.h +++ b/playercontroller.h @@ -40,14 +40,14 @@ class PlayerController : public QObject TrackType type; QString title; QString lang; - QString filename; + QString mediaUrl; bool isExternal; }; explicit PlayerController(PlayerWidget *parent = nullptr); - QString currentFile() { return getProp("path").toString(); } - QString lastOpenFile() { return lastFile; } + QString currentMediaUrl() { return getProp("path").toString(); } + QString lastOpenMediaUrl() { return lastMediaUrl; } PropertyObserver *prop(QString name); void setProp(const QString& name, const QVariant& value); @@ -58,7 +58,7 @@ class PlayerController : public QObject void setExtSubOptions(QString mode, int depth) { p_extSubMode = mode; p_extSubMaxDepth = depth; } void setExtAudioOptions(QString mode, int depth) { p_extAudioMode = mode; p_extAudioMaxDepth = depth; } - void open(const QUrl &file); + void open(const QUrl &url); void stop(); void togglePlayback(); @@ -82,11 +82,11 @@ class PlayerController : public QObject signals: void tracksUpdated(); - void fileMetaUpdated(QString label, double duration); - void endFile(bool wasStopped); + void mediaMetaUpdated(QString label, double duration); + void endMediaRessource(bool wasStopped); private: - void handleFileEnd(); - void handleFileLoad(); + void handleMediaEnd(); + void handleMediaLoad(); PlayerWidget *p; @@ -98,9 +98,9 @@ class PlayerController : public QObject QList p_tracks; - QUrl queuedFile; - QString lastFile; - bool haveFile = false; + QUrl queuedMediaUrl; + QString lastMediaUrl; + bool haveMediaUrl = false; }; #endif // PLAYERCONTROLLER_H diff --git a/program_arg.cpp b/program_arg.cpp new file mode 100644 index 0000000..32bbe7f --- /dev/null +++ b/program_arg.cpp @@ -0,0 +1,17 @@ +#include + +#include "program_arg.h" + +ProgramArgument::ProgramArgument(const QString &cmd, const QStringList &args) + : cmd{cmd}, args{args} +{} + +QDataStream &operator>>(QDataStream &istream, ProgramArgument &pa) { + istream >> pa.cmd >> pa.args; + return istream; +} + +QDataStream &operator<<(QDataStream &ostream, const ProgramArgument &pa) { + ostream << pa.cmd << pa.args; + return ostream; +} diff --git a/program_arg.h b/program_arg.h new file mode 100644 index 0000000..702ef39 --- /dev/null +++ b/program_arg.h @@ -0,0 +1,19 @@ +#ifndef PROGRAM_ARG_H +#define PROGRAM_ARG_H + +#include +#include + +class QDataStream; + +struct ProgramArgument { + ProgramArgument(const QString &cmd = QString(), const QStringList &args = QStringList()); + + QString cmd; // TODO: Make cmd enum + QStringList args; + + friend QDataStream &operator>>(QDataStream &istream, ProgramArgument &pa); + friend QDataStream &operator<<(QDataStream &ostream, const ProgramArgument &pa); +}; + +#endif diff --git a/singleinstance.cpp b/singleinstance.cpp index a3d88f3..a5aa0dd 100644 --- a/singleinstance.cpp +++ b/singleinstance.cpp @@ -16,8 +16,13 @@ */ #include "singleinstance.h" +#include +#include #include #include +#include + +#include "program_arg.h" SingleInstance::SingleInstance(QString appName, QObject *parent) : p_appName(appName), QObject{parent} @@ -49,10 +54,12 @@ bool SingleInstance::hostServer() return ret; } -void SingleInstance::sendMessage(QString msg) +void SingleInstance::sendMessage(const ProgramArgument &msg) { - p_socket->write(msg.toUtf8()); - p_socket->putChar('\n'); + QByteArray data; + QDataStream ostream(&data, QIODevice::WriteOnly); + ostream << msg; + p_socket->write(data); p_socket->waitForBytesWritten(100); } @@ -69,6 +76,12 @@ void SingleInstance::handleNewConnection() void SingleInstance::readData() { QLocalSocket *sock = static_cast(sender()); - while (sock->canReadLine()) - emit newMessage(sock->readLine()); + QByteArray bytes = sock->readAll(); + QDataStream istream(&bytes, QIODevice::ReadOnly); + + while (!istream.atEnd()) { + ProgramArgument msg; + istream >> msg; + emit newMessage(msg); + } } diff --git a/singleinstance.h b/singleinstance.h index 6ec7a5c..bfeb7da 100644 --- a/singleinstance.h +++ b/singleinstance.h @@ -19,6 +19,7 @@ #include +struct ProgramArgument; class QLocalSocket; class QLocalServer; @@ -29,11 +30,12 @@ class SingleInstance : public QObject explicit SingleInstance(QString appName, QObject *parent = nullptr); bool connectServer(); void closeSocket(); - void sendMessage(QString msg); + void sendMessage(const ProgramArgument &msg); bool hostServer(); signals: - void newMessage(QString msg); + void newMessage(const ProgramArgument &msg); + void handleNewMessage(const ProgramArgument &msg); private: void handleNewConnection(); void readData(); From 0b2a41de980a47cc6d8e4a8b55eddfbc1fabe812 Mon Sep 17 00:00:00 2001 From: "Oskar Roesler (bionade24)" Date: Mon, 16 Sep 2024 12:23:04 +0200 Subject: [PATCH 02/10] Formatting --- program_arg.cpp | 9 ++++++--- program_arg.h | 5 +++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/program_arg.cpp b/program_arg.cpp index 32bbe7f..2aef890 100644 --- a/program_arg.cpp +++ b/program_arg.cpp @@ -4,14 +4,17 @@ ProgramArgument::ProgramArgument(const QString &cmd, const QStringList &args) : cmd{cmd}, args{args} -{} +{ +} -QDataStream &operator>>(QDataStream &istream, ProgramArgument &pa) { +QDataStream &operator>>(QDataStream &istream, ProgramArgument &pa) +{ istream >> pa.cmd >> pa.args; return istream; } -QDataStream &operator<<(QDataStream &ostream, const ProgramArgument &pa) { +QDataStream &operator<<(QDataStream &ostream, const ProgramArgument &pa) +{ ostream << pa.cmd << pa.args; return ostream; } diff --git a/program_arg.h b/program_arg.h index 702ef39..3fd0a7b 100644 --- a/program_arg.h +++ b/program_arg.h @@ -6,14 +6,15 @@ class QDataStream; -struct ProgramArgument { +struct ProgramArgument +{ ProgramArgument(const QString &cmd = QString(), const QStringList &args = QStringList()); QString cmd; // TODO: Make cmd enum QStringList args; friend QDataStream &operator>>(QDataStream &istream, ProgramArgument &pa); - friend QDataStream &operator<<(QDataStream &ostream, const ProgramArgument &pa); + friend QDataStream &operator<<(QDataStream &ostream, const ProgramArgument &pa); }; #endif From 9c2a54b64bd0bcedd4a4089f358956cc1eaf3402 Mon Sep 17 00:00:00 2001 From: "Oskar Roesler (bionade24)" Date: Fri, 27 Sep 2024 20:40:21 +0200 Subject: [PATCH 03/10] Improvement, to be squashed. --- kokovp.cpp | 4 ++-- main.cpp | 6 +++--- program_arg.cpp | 2 +- program_arg.h | 11 +++++++++-- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/kokovp.cpp b/kokovp.cpp index 07224eb..b7eef2c 100644 --- a/kokovp.cpp +++ b/kokovp.cpp @@ -166,9 +166,9 @@ KokoVP::~KokoVP() void KokoVP::handleNewMessage(const ProgramArgument &msg) { - if (msg.cmd=="open") + if (msg.cmd==ProgramCmd::OPEN) playlist->addURLs(Helper::pathsToUrls(msg.args)); - else if (msg.cmd=="playlast") + else if (msg.cmd==ProgramCmd::PLAYLAST) QTimer::singleShot(100, playlist, &Playlist::playLast); // Workaround to wait until Qt event loop and libmpv will be ready } diff --git a/main.cpp b/main.cpp index 499df28..07685a9 100644 --- a/main.cpp +++ b/main.cpp @@ -60,7 +60,7 @@ int main(int argc, char *argv[]) { if (inst.connectServer()) { - inst.sendMessage("open:" + parser.positionalArguments().join(',')); + inst.sendMessage(ProgramArgument(ProgramCmd::OPEN, parser.positionalArguments())); inst.closeSocket(); return 0; } @@ -72,8 +72,8 @@ int main(int argc, char *argv[]) KokoVP w; if (parser.positionalArguments().length()>0) { - w.handleNewMessage(ProgramArgument("open", parser.positionalArguments())); - w.handleNewMessage(ProgramArgument("playlast")); + w.handleNewMessage(ProgramArgument(ProgramCmd::OPEN, parser.positionalArguments())); + w.handleNewMessage(ProgramArgument(ProgramCmd::PLAYLAST)); } QObject::connect(&inst, &SingleInstance::newMessage, &w, &KokoVP::handleNewMessage); w.showNormal(); diff --git a/program_arg.cpp b/program_arg.cpp index 2aef890..a5db460 100644 --- a/program_arg.cpp +++ b/program_arg.cpp @@ -2,7 +2,7 @@ #include "program_arg.h" -ProgramArgument::ProgramArgument(const QString &cmd, const QStringList &args) +ProgramArgument::ProgramArgument(const ProgramCmd &cmd, const QStringList &args) : cmd{cmd}, args{args} { } diff --git a/program_arg.h b/program_arg.h index 3fd0a7b..3cd550f 100644 --- a/program_arg.h +++ b/program_arg.h @@ -6,11 +6,18 @@ class QDataStream; +enum class ProgramCmd +{ + NONE, + OPEN, + PLAYLAST, +}; + struct ProgramArgument { - ProgramArgument(const QString &cmd = QString(), const QStringList &args = QStringList()); + explicit ProgramArgument(const ProgramCmd &cmd = ProgramCmd::NONE, const QStringList &args = QStringList()); - QString cmd; // TODO: Make cmd enum + ProgramCmd cmd; QStringList args; friend QDataStream &operator>>(QDataStream &istream, ProgramArgument &pa); From 58267d133ecc6ccb29cfa7940d81efa11585cad8 Mon Sep 17 00:00:00 2001 From: "Oskar Roesler (bionade24)" Date: Sun, 6 Apr 2025 19:58:26 +0200 Subject: [PATCH 04/10] Add missing header in singleinstance.cpp --- singleinstance.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/singleinstance.cpp b/singleinstance.cpp index a5aa0dd..5d91e45 100644 --- a/singleinstance.cpp +++ b/singleinstance.cpp @@ -17,6 +17,7 @@ #include "singleinstance.h" #include +#include #include #include #include From 7ec1d055b69b8655eb3b06c0a1f49b23532727ee Mon Sep 17 00:00:00 2001 From: "Oskar Roesler (bionade24)" Date: Sun, 6 Apr 2025 20:52:06 +0200 Subject: [PATCH 05/10] Add Open URL button to Open menu. --- kokovp.cpp | 23 ++++++++++++++++++++++- kokovp.h | 1 + 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/kokovp.cpp b/kokovp.cpp index b7eef2c..6d461e2 100644 --- a/kokovp.cpp +++ b/kokovp.cpp @@ -220,8 +220,11 @@ void KokoVP::populateMenu() ActionWrapper *openDirectoryAct = new ActionWrapper(tr("Directory..."), QKeySequence(), openMenu, "openDirectory", QIcon::fromTheme("folder-open")); connect(openDirectoryAct, &QAction::triggered, this, qOverload<>(&KokoVP::openDirectory)); + ActionWrapper *openUrlAct = new ActionWrapper(tr("URL"), QKeySequence(), openMenu, "openUrl", QIcon::fromTheme("gnumeric-link-url")); + connect(openUrlAct, &QAction::triggered, this, qOverload<>(&KokoVP::openUrl)); + ActionWrapper *exitAct = new ActionWrapper(tr("Exit"), QKeySequence("Ctrl+Q"), openMenu, "exit", QIcon::fromTheme("application-exit")); - connect(exitAct, &QAction::triggered, qApp, &QApplication::exit); + connect(exitAct, &QAction::triggered, qApp, &QApplication::exit); connect(exitAct, &QAction::triggered, qApp, &QApplication::exit); // --- Play --- BistableAction *playPauseAct = new BistableAction(Qt::Key_Space, playMenu, "play_pause"); @@ -459,6 +462,24 @@ void KokoVP::openDirectory() playlist->playFirst(); } +void KokoVP::openUrl() +{ + auto dialog = QInputDialog(this); + dialog.setWindowTitle(tr("Open URL")); + dialog.setOkButtonText(tr("Open")); + dialog.setInputMode(QInputDialog::TextInput); + dialog.setTextValue("https://"); + if (dialog.exec() == QDialog::Accepted) { + auto url = QUrl{dialog.textValue()}; + if (url.isValid() && !url.host().isEmpty()) { + playlist->addURLs(QList{url}); + playlist->playLast(); + } else { + QMessageBox::critical(this, tr("Error"), tr("Invalid URL")); + } + } +} + void KokoVP::videoScreenshot() { player->screenshot(QString(), false); //TODO: maybe frontend will format filenames, instead of mpv itself diff --git a/kokovp.h b/kokovp.h index 0d25046..446bb43 100644 --- a/kokovp.h +++ b/kokovp.h @@ -60,6 +60,7 @@ class KokoVP : public QMainWindow void videoSubScreenshot(); void openFiles(); void openDirectory(); + void openUrl(); void playFile(QUrl file); void handleTracks(); void handleEOF(bool wasStopped); From b374372ebdd637111488ac7d5fc5f1daced6b5d3 Mon Sep 17 00:00:00 2001 From: "Oskar Roesler (bionade24)" Date: Wed, 25 Jun 2025 23:58:38 +0200 Subject: [PATCH 06/10] Revert renaming from file to media(Ressource) To be squashed. --- kokovp.cpp | 14 +++++++------- playercontroller.cpp | 30 +++++++++++++++--------------- playercontroller.h | 20 ++++++++++---------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/kokovp.cpp b/kokovp.cpp index 6d461e2..1b33c4e 100644 --- a/kokovp.cpp +++ b/kokovp.cpp @@ -55,7 +55,7 @@ QString extfolderRewriteRule(const PlayerController::Track &t) if (!t.isExternal) return QString(); - QFileInfo fI(t.mediaUrl); + QFileInfo fI(t.filename); QString fName = fI.fileName(); if (fI.fileName()==t.title) @@ -84,8 +84,8 @@ KokoVP::KokoVP(QWidget *parent) createPlaylistDock(); connect(player, &PlayerController::tracksUpdated, this, &KokoVP::handleTracks); - connect(player, &PlayerController::mediaMetaUpdated, playlist, &Playlist::setCurrentRowMetainfo); - connect(player, &PlayerController::endMediaRessource, this, &KokoVP::handleEOF); + connect(player, &PlayerController::fileMetaUpdated, playlist, &Playlist::setCurrentRowMetainfo); + connect(player, &PlayerController::endFile, this, &KokoVP::handleEOF); connect(playerWidget, &PlayerWidget::draggedURLS, playlist, &Playlist::addURLs); connect(playlist, &Playlist::playRequest, this, qOverload(&KokoVP::playFile)); @@ -161,7 +161,7 @@ KokoVP::KokoVP(QWidget *parent) KokoVP::~KokoVP() { - fileSettings->saveSettingsFor(player->lastOpenMediaUrl(), true); // Always save time-pos on exit + fileSettings->saveSettingsFor(player->lastOpenFile(), true); // Always save time-pos on exit } void KokoVP::handleNewMessage(const ProgramArgument &msg) @@ -519,7 +519,7 @@ void KokoVP::handleTracks() } if (Config::i().get("play_mode/keep_props", true).toBool()) - fileSettings->loadSettingsFor(player->currentMediaUrl(), Config::i().get("play_mode/keep_timepos", true).toBool()); + fileSettings->loadSettingsFor(player->currentFile(), Config::i().get("play_mode/keep_timepos", true).toBool()); player->setProp("pause", false); } @@ -529,7 +529,7 @@ void KokoVP::handleEOF(bool wasStopped) setWindowTitle("KokoVP"); if (Config::i().get("play_mode/keep_props", true).toBool()) - fileSettings->saveSettingsFor(player->lastOpenMediaUrl(), wasStopped); // If file is ended, then time-pos shouldn't be saved + fileSettings->saveSettingsFor(player->lastOpenFile(), wasStopped); // If file is ended, then time-pos shouldn't be saved // It's better to reset tracks to auto on EOF instead of load event, to ensure, that tracks' ids events will be generated in time if (wasStopped || !Config::i().get("play_mode/keep_tracknums_for_next", false).toBool()) @@ -599,7 +599,7 @@ void KokoVP::setAudioDevice(QAction *audioDeviceAction) void KokoVP::tryPlayCurrent() { - if (player->currentMediaUrl().isEmpty()) + if (player->currentFile().isEmpty()) playlist->playCurrent(); } diff --git a/playercontroller.cpp b/playercontroller.cpp index 8f3cf30..1405948 100644 --- a/playercontroller.cpp +++ b/playercontroller.cpp @@ -26,9 +26,9 @@ PlayerController::PlayerController(PlayerWidget *parent) prop("volume")->set(50); prop("pause")->set(true); p->setProp("audio-file-auto-exts", Extensions.audio()); - connect(p, &PlayerWidget::fileLoaded, this, &PlayerController::handleMediaLoad); - connect(p, &PlayerWidget::endFile, this, &PlayerController::endMediaRessource); - connect(p, &PlayerWidget::endFile, this, &PlayerController::handleMediaEnd); + connect(p, &PlayerWidget::fileLoaded, this, &PlayerController::handleFileLoad); + connect(p, &PlayerWidget::endFile, this, &PlayerController::endFile); + connect(p, &PlayerWidget::endFile, this, &PlayerController::handleFileEnd); } PropertyObserver *PlayerController::prop(QString name) @@ -51,22 +51,22 @@ void PlayerController::setOption(const QString &name, const QVariant &value) p->setOption(name, value); } -void PlayerController::handleMediaEnd() +void PlayerController::handleFileEnd() { - haveMediaUrl = false; - if (!queuedMediaUrl.isEmpty()) + haveFile = false; + if (!queuedFile.isEmpty()) { - QUrl url = queuedMediaUrl; - queuedMediaUrl = QUrl(); + QUrl url = queuedFile; + queuedFile = QUrl(); open(url); } } void PlayerController::open(const QUrl &url) { - if (haveMediaUrl) + if (haveFile) { - queuedMediaUrl = url; + queuedFile = url; return stop(); } p_tracks.clear(); @@ -164,10 +164,10 @@ bool PlayerController::isPlaying() return !getProp("pause").toBool(); } -void PlayerController::handleMediaLoad() +void PlayerController::handleFileLoad() { - lastMediaUrl = currentMediaUrl(); - haveMediaUrl = true; + lastFile = currentFile(); + haveFile = true; bool ok; int tracksCount = getProp("track-list/count").toInt(&ok); assert(ok); @@ -188,12 +188,12 @@ void PlayerController::handleMediaLoad() t.type = Track::TRACK_TYPE_SUB; if (t.isExternal) - t.mediaUrl = p->getProp(trackAddr + "external-mediaUrl").toString(); + t.filename = p->getProp(trackAddr + "external-filename").toString(); p_tracks.append(t); } //p->command(QVariantList({"vf", "clr", ""})); //SVP emit tracksUpdated(); - emit mediaMetaUpdated(p->getProp("media-title").toString(), prop("duration")->get().toDouble()); + emit fileMetaUpdated(p->getProp("media-title").toString(), prop("duration")->get().toDouble()); } diff --git a/playercontroller.h b/playercontroller.h index 3f8967d..3a1c446 100644 --- a/playercontroller.h +++ b/playercontroller.h @@ -40,14 +40,14 @@ class PlayerController : public QObject TrackType type; QString title; QString lang; - QString mediaUrl; + QString filename; bool isExternal; }; explicit PlayerController(PlayerWidget *parent = nullptr); - QString currentMediaUrl() { return getProp("path").toString(); } - QString lastOpenMediaUrl() { return lastMediaUrl; } + QString currentFile() { return getProp("path").toString(); } + QString lastOpenFile() { return lastFile; } PropertyObserver *prop(QString name); void setProp(const QString& name, const QVariant& value); @@ -82,11 +82,11 @@ class PlayerController : public QObject signals: void tracksUpdated(); - void mediaMetaUpdated(QString label, double duration); - void endMediaRessource(bool wasStopped); + void fileMetaUpdated(QString label, double duration); + void endFile(bool wasStopped); private: - void handleMediaEnd(); - void handleMediaLoad(); + void handleFileEnd(); + void handleFileLoad(); PlayerWidget *p; @@ -98,9 +98,9 @@ class PlayerController : public QObject QList p_tracks; - QUrl queuedMediaUrl; - QString lastMediaUrl; - bool haveMediaUrl = false; + QUrl queuedFile; + QString lastFile; + bool haveFile = false; }; #endif // PLAYERCONTROLLER_H From 21936c79c3883cafcc66bffa023567e7f072d5c6 Mon Sep 17 00:00:00 2001 From: "Oskar Roesler (bionade24)" Date: Wed, 28 May 2025 15:17:58 +0200 Subject: [PATCH 07/10] Conditionally start playback of opened media in existing instance. If something is already currently playing on the existing instance, the media gets added to the playlist, like before. If nothing is playing, start playback of the added media. --- CMakeLists.txt | 1 - kokovp.cpp | 11 ++++++----- kokovp.h | 2 +- main.cpp | 9 ++++----- program_arg.cpp | 20 -------------------- program_arg.h | 24 ++---------------------- singleinstance.cpp | 17 +++++------------ singleinstance.h | 7 +++---- 8 files changed, 21 insertions(+), 70 deletions(-) delete mode 100644 program_arg.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f9ba69a..46ecdc9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,6 @@ set(MAIN_SRC mpvwidget.cpp playerwidget.cpp playercontroller.cpp - program_arg.cpp tracksmenu.cpp autohidewidget.cpp # From SMPlayer config.cpp diff --git a/kokovp.cpp b/kokovp.cpp index 1b33c4e..e1b04eb 100644 --- a/kokovp.cpp +++ b/kokovp.cpp @@ -164,12 +164,13 @@ KokoVP::~KokoVP() fileSettings->saveSettingsFor(player->lastOpenFile(), true); // Always save time-pos on exit } -void KokoVP::handleNewMessage(const ProgramArgument &msg) +void KokoVP::handleNewMessage(const QString &msg) { - if (msg.cmd==ProgramCmd::OPEN) - playlist->addURLs(Helper::pathsToUrls(msg.args)); - else if (msg.cmd==ProgramCmd::PLAYLAST) - QTimer::singleShot(100, playlist, &Playlist::playLast); // Workaround to wait until Qt event loop and libmpv will be ready + int del = msg.indexOf(PROGRAM_ARG_DELIMITER); + QString cmd = msg.left(del); + QStringList args = msg.trimmed().mid(del+1).split(','); + if (cmd==PROGRAM_ARG_OPEN) + playlist->addURLs(Helper::pathsToUrls(args)); } void KokoVP::toggleFullscreen(bool on) diff --git a/kokovp.h b/kokovp.h index 446bb43..d4674c9 100644 --- a/kokovp.h +++ b/kokovp.h @@ -41,7 +41,7 @@ class KokoVP : public QMainWindow ~KokoVP(); static KokoVP *i() { return inst; } QActionMap actionsMap() const { return p_actionsMap; } - void handleNewMessage(const ProgramArgument &msg); + void handleNewMessage(const QString &msg); private: void toggleFullscreen(bool on); diff --git a/main.cpp b/main.cpp index 07685a9..e6f72b9 100644 --- a/main.cpp +++ b/main.cpp @@ -60,7 +60,7 @@ int main(int argc, char *argv[]) { if (inst.connectServer()) { - inst.sendMessage(ProgramArgument(ProgramCmd::OPEN, parser.positionalArguments())); + inst.sendMessage(QString(PROGRAM_ARG_OPEN) + parser.positionalArguments().join(',')); inst.closeSocket(); return 0; } @@ -70,11 +70,10 @@ int main(int argc, char *argv[]) } KokoVP w; + if (parser.positionalArguments().length()>0) - { - w.handleNewMessage(ProgramArgument(ProgramCmd::OPEN, parser.positionalArguments())); - w.handleNewMessage(ProgramArgument(ProgramCmd::PLAYLAST)); - } + w.handleNewMessage(QString(PROGRAM_ARG_OPEN) + parser.positionalArguments().join(',')); + QObject::connect(&inst, &SingleInstance::newMessage, &w, &KokoVP::handleNewMessage); w.showNormal(); diff --git a/program_arg.cpp b/program_arg.cpp deleted file mode 100644 index a5db460..0000000 --- a/program_arg.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include - -#include "program_arg.h" - -ProgramArgument::ProgramArgument(const ProgramCmd &cmd, const QStringList &args) - : cmd{cmd}, args{args} -{ -} - -QDataStream &operator>>(QDataStream &istream, ProgramArgument &pa) -{ - istream >> pa.cmd >> pa.args; - return istream; -} - -QDataStream &operator<<(QDataStream &ostream, const ProgramArgument &pa) -{ - ostream << pa.cmd << pa.args; - return ostream; -} diff --git a/program_arg.h b/program_arg.h index 3cd550f..5b451b8 100644 --- a/program_arg.h +++ b/program_arg.h @@ -1,27 +1,7 @@ #ifndef PROGRAM_ARG_H #define PROGRAM_ARG_H -#include -#include - -class QDataStream; - -enum class ProgramCmd -{ - NONE, - OPEN, - PLAYLAST, -}; - -struct ProgramArgument -{ - explicit ProgramArgument(const ProgramCmd &cmd = ProgramCmd::NONE, const QStringList &args = QStringList()); - - ProgramCmd cmd; - QStringList args; - - friend QDataStream &operator>>(QDataStream &istream, ProgramArgument &pa); - friend QDataStream &operator<<(QDataStream &ostream, const ProgramArgument &pa); -}; +#define PROGRAM_ARG_DELIMITER ":" +#define PROGRAM_ARG_OPEN "OPEN" PROGRAM_ARG_DELIMITER #endif diff --git a/singleinstance.cpp b/singleinstance.cpp index 5d91e45..e41eef0 100644 --- a/singleinstance.cpp +++ b/singleinstance.cpp @@ -55,12 +55,10 @@ bool SingleInstance::hostServer() return ret; } -void SingleInstance::sendMessage(const ProgramArgument &msg) +void SingleInstance::sendMessage(const QString &msg) { - QByteArray data; - QDataStream ostream(&data, QIODevice::WriteOnly); - ostream << msg; - p_socket->write(data); + p_socket->write(msg.toUtf8()); + p_socket->putChar('\n'); p_socket->waitForBytesWritten(100); } @@ -77,12 +75,7 @@ void SingleInstance::handleNewConnection() void SingleInstance::readData() { QLocalSocket *sock = static_cast(sender()); - QByteArray bytes = sock->readAll(); - QDataStream istream(&bytes, QIODevice::ReadOnly); + while (sock->canReadLine()) + emit newMessage(sock->readLine()); - while (!istream.atEnd()) { - ProgramArgument msg; - istream >> msg; - emit newMessage(msg); - } } diff --git a/singleinstance.h b/singleinstance.h index bfeb7da..eeb9372 100644 --- a/singleinstance.h +++ b/singleinstance.h @@ -19,7 +19,6 @@ #include -struct ProgramArgument; class QLocalSocket; class QLocalServer; @@ -30,12 +29,12 @@ class SingleInstance : public QObject explicit SingleInstance(QString appName, QObject *parent = nullptr); bool connectServer(); void closeSocket(); - void sendMessage(const ProgramArgument &msg); + void sendMessage(const QString &msg); bool hostServer(); signals: - void newMessage(const ProgramArgument &msg); - void handleNewMessage(const ProgramArgument &msg); + void newMessage(const QString &msg); + void handleNewMessage(const QString &msg); private: void handleNewConnection(); void readData(); From 557ffb300618fe52a1d7e14c6aa43bd1f0577a05 Mon Sep 17 00:00:00 2001 From: "Oskar Roesler (bionade24)" Date: Thu, 26 Jun 2025 00:52:27 +0200 Subject: [PATCH 08/10] Add missing QMessageBox header --- kokovp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/kokovp.cpp b/kokovp.cpp index e1b04eb..115df06 100644 --- a/kokovp.cpp +++ b/kokovp.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include From 5f4f1fa8938fb101b852f6338c3a4211e5d6e80d Mon Sep 17 00:00:00 2001 From: "Oskar Roesler (bionade24)" Date: Thu, 26 Jun 2025 03:17:00 +0200 Subject: [PATCH 09/10] More cleanup To be squashed. --- main.cpp | 4 ++-- playercontroller.cpp | 10 +++++----- playercontroller.h | 2 +- program_arg.h | 3 ++- singleinstance.cpp | 2 -- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/main.cpp b/main.cpp index e6f72b9..b886857 100644 --- a/main.cpp +++ b/main.cpp @@ -60,7 +60,7 @@ int main(int argc, char *argv[]) { if (inst.connectServer()) { - inst.sendMessage(QString(PROGRAM_ARG_OPEN) + parser.positionalArguments().join(',')); + inst.sendMessage(PROGRAM_ARG_OPEN_W_DEL + parser.positionalArguments().join(',')); inst.closeSocket(); return 0; } @@ -72,7 +72,7 @@ int main(int argc, char *argv[]) KokoVP w; if (parser.positionalArguments().length()>0) - w.handleNewMessage(QString(PROGRAM_ARG_OPEN) + parser.positionalArguments().join(',')); + w.handleNewMessage(PROGRAM_ARG_OPEN_W_DEL + parser.positionalArguments().join(',')); QObject::connect(&inst, &SingleInstance::newMessage, &w, &KokoVP::handleNewMessage); w.showNormal(); diff --git a/playercontroller.cpp b/playercontroller.cpp index 1405948..cc3e537 100644 --- a/playercontroller.cpp +++ b/playercontroller.cpp @@ -62,20 +62,20 @@ void PlayerController::handleFileEnd() } } -void PlayerController::open(const QUrl &url) +void PlayerController::open(const QUrl &file) { if (haveFile) { - queuedFile = url; + queuedFile = file; return stop(); } p_tracks.clear(); // Here we need to scan siblings folder for possible external subtitles and audio //, then set it to sub-file-paths and audio-file-paths OPTIONs (not properties) - if (url.isLocalFile()) + if (file.isLocalFile()) { - QDir mediaDir = QFileInfo(url.toLocalFile()).absoluteDir(); + QDir mediaDir = QFileInfo(file.toLocalFile()).absoluteDir(); if (p_extSubMaxDepth>=0 && p_extSubMode!="no") { @@ -94,7 +94,7 @@ void PlayerController::open(const QUrl &url) } } - p->command(QStringList{"loadfile", url.toString()}); + p->command(QStringList{"loadfile", file.toString()}); } void PlayerController::stop() diff --git a/playercontroller.h b/playercontroller.h index 3a1c446..e72574c 100644 --- a/playercontroller.h +++ b/playercontroller.h @@ -58,7 +58,7 @@ class PlayerController : public QObject void setExtSubOptions(QString mode, int depth) { p_extSubMode = mode; p_extSubMaxDepth = depth; } void setExtAudioOptions(QString mode, int depth) { p_extAudioMode = mode; p_extAudioMaxDepth = depth; } - void open(const QUrl &url); + void open(const QUrl &file); void stop(); void togglePlayback(); diff --git a/program_arg.h b/program_arg.h index 5b451b8..f715b6f 100644 --- a/program_arg.h +++ b/program_arg.h @@ -2,6 +2,7 @@ #define PROGRAM_ARG_H #define PROGRAM_ARG_DELIMITER ":" -#define PROGRAM_ARG_OPEN "OPEN" PROGRAM_ARG_DELIMITER +#define PROGRAM_ARG_OPEN "OPEN" +#define PROGRAM_ARG_OPEN_W_DEL PROGRAM_ARG_OPEN PROGRAM_ARG_DELIMITER #endif diff --git a/singleinstance.cpp b/singleinstance.cpp index e41eef0..6218af4 100644 --- a/singleinstance.cpp +++ b/singleinstance.cpp @@ -16,14 +16,12 @@ */ #include "singleinstance.h" -#include #include #include #include #include #include -#include "program_arg.h" SingleInstance::SingleInstance(QString appName, QObject *parent) : p_appName(appName), QObject{parent} From fde410556ce86f77d9540dc169d8a51006dd49c8 Mon Sep 17 00:00:00 2001 From: "Oskar Roesler (bionade24)" Date: Fri, 26 Sep 2025 21:12:21 +0200 Subject: [PATCH 10/10] Cleanup, to be squashed --- kokovp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kokovp.cpp b/kokovp.cpp index 115df06..f467096 100644 --- a/kokovp.cpp +++ b/kokovp.cpp @@ -226,7 +226,7 @@ void KokoVP::populateMenu() connect(openUrlAct, &QAction::triggered, this, qOverload<>(&KokoVP::openUrl)); ActionWrapper *exitAct = new ActionWrapper(tr("Exit"), QKeySequence("Ctrl+Q"), openMenu, "exit", QIcon::fromTheme("application-exit")); - connect(exitAct, &QAction::triggered, qApp, &QApplication::exit); connect(exitAct, &QAction::triggered, qApp, &QApplication::exit); + connect(exitAct, &QAction::triggered, qApp, &QApplication::exit); // --- Play --- BistableAction *playPauseAct = new BistableAction(Qt::Key_Space, playMenu, "play_pause");