diff --git a/CMakeLists.txt b/CMakeLists.txt index e05c09f..46ecdc9 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 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..f467096 100644 --- a/kokovp.cpp +++ b/kokovp.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,7 @@ #include "config.h" #include "cache.h" #include "helper.h" +#include "program_arg.h" #include "prefs/prefdialog.h" #include "prefs/prefappearance.h" @@ -55,6 +57,7 @@ QString extfolderRewriteRule(const PlayerController::Track &t) return QString(); QFileInfo fI(t.filename); + QString fName = fI.fileName(); if (fI.fileName()==t.title) return QApplication::translate("KokoVP", "[EXT] ")+fI.dir().dirName(); @@ -162,15 +165,13 @@ KokoVP::~KokoVP() fileSettings->saveSettingsFor(player->lastOpenFile(), true); // Always save time-pos on exit } -void KokoVP::handleNewMessage(QString msg) +void KokoVP::handleNewMessage(const QString &msg) { - int del = msg.indexOf(':'); + int del = msg.indexOf(PROGRAM_ARG_DELIMITER); QString cmd = msg.left(del); QStringList args = msg.trimmed().mid(del+1).split(','); - if (cmd=="open") + if (cmd==PROGRAM_ARG_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 } void KokoVP::toggleFullscreen(bool on) @@ -221,6 +222,9 @@ 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); @@ -460,6 +464,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.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..d4674c9 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 QString &msg); private: void toggleFullscreen(bool on); @@ -59,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); diff --git a/main.cpp b/main.cpp index 6a66df5..b886857 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")); @@ -58,7 +60,7 @@ int main(int argc, char *argv[]) { if (inst.connectServer()) { - inst.sendMessage("open:" + parser.positionalArguments().join(',')); + inst.sendMessage(PROGRAM_ARG_OPEN_W_DEL + parser.positionalArguments().join(',')); inst.closeSocket(); return 0; } @@ -68,11 +70,10 @@ int main(int argc, char *argv[]) } KokoVP w; + if (parser.positionalArguments().length()>0) - { - w.handleNewMessage("open:" + parser.positionalArguments().join(',')); - w.handleNewMessage("playlast"); - } + 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 41beb54..cc3e537 100644 --- a/playercontroller.cpp +++ b/playercontroller.cpp @@ -56,9 +56,9 @@ void PlayerController::handleFileEnd() haveFile = false; if (!queuedFile.isEmpty()) { - QUrl f = queuedFile; + QUrl url = queuedFile; queuedFile = QUrl(); - open(f); + open(url); } } @@ -94,7 +94,7 @@ void PlayerController::open(const QUrl &file) } } - p->command(QStringList{"loadfile", file.path()}); + p->command(QStringList{"loadfile", file.toString()}); } void PlayerController::stop() diff --git a/program_arg.h b/program_arg.h new file mode 100644 index 0000000..f715b6f --- /dev/null +++ b/program_arg.h @@ -0,0 +1,8 @@ +#ifndef PROGRAM_ARG_H +#define PROGRAM_ARG_H + +#define 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 a3d88f3..6218af4 100644 --- a/singleinstance.cpp +++ b/singleinstance.cpp @@ -16,8 +16,12 @@ */ #include "singleinstance.h" +#include +#include #include #include +#include + SingleInstance::SingleInstance(QString appName, QObject *parent) : p_appName(appName), QObject{parent} @@ -49,7 +53,7 @@ bool SingleInstance::hostServer() return ret; } -void SingleInstance::sendMessage(QString msg) +void SingleInstance::sendMessage(const QString &msg) { p_socket->write(msg.toUtf8()); p_socket->putChar('\n'); @@ -71,4 +75,5 @@ void SingleInstance::readData() QLocalSocket *sock = static_cast(sender()); while (sock->canReadLine()) emit newMessage(sock->readLine()); + } diff --git a/singleinstance.h b/singleinstance.h index 6ec7a5c..eeb9372 100644 --- a/singleinstance.h +++ b/singleinstance.h @@ -29,11 +29,12 @@ class SingleInstance : public QObject explicit SingleInstance(QString appName, QObject *parent = nullptr); bool connectServer(); void closeSocket(); - void sendMessage(QString msg); + void sendMessage(const QString &msg); bool hostServer(); signals: - void newMessage(QString msg); + void newMessage(const QString &msg); + void handleNewMessage(const QString &msg); private: void handleNewConnection(); void readData();