kde-extraapps/kdeplasma-addons/runners/audioplayercontrol/audioplayercontrolrunner.cpp
Ivailo Monev ebfdcdee1c kdeplasma-addons: deal with TODOs related to audio player control runner
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2021-07-27 15:20:58 +03:00

310 lines
12 KiB
C++

/*
* Copyright (C) 2008 Bruno Virlet <bvirlet@kdemail.net>
* Copyright (C) 2009 Ryan P. Bitanga <ryan.bitanga@gmail.com>
* Copyright (C) 2009 Jan G. Marker <jangerrit@weiler-marker.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License version 2 as
* published by the Free Software Foundation
*
* This program 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
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "audioplayercontrolrunner.h"
#include "imageiconengine.h"
#include "audioplayercontrolconfigkeys.h"
#include <QtCore/QElapsedTimer>
#include <QtCore/QCoreApplication>
#include <QtDBus/QDBusInterface>
#include <QtDBus/QDBusMetaType>
#include <QtDBus/QDBusPendingReply>
#include <QtDBus/qdbuspendingcall.h>
#include <QtDBus/QDBusMessage>
#include <KMessageBox>
#include <KDebug>
#include <KIcon>
#include <KRun>
#include <KUrl>
/** The variable PLAY contains the action label for play */
static const QString PLAY(QLatin1String("play"));
/** The variable APPEND contains the action label for append */
static const QString APPEND(QLatin1String("append"));
/** The variable NONE says that no action is needed */
static const QString NONE(QLatin1String("none"));
// for reference:
// https://specifications.freedesktop.org/mpris-spec/latest/
typedef QList<QDBusObjectPath> TrackListType;
AudioPlayerControlRunner::AudioPlayerControlRunner(QObject *parent, const QVariantList& args)
: Plasma::AbstractRunner(parent, args)
{
Q_UNUSED(args);
setObjectName(QLatin1String("Audio Player Control Runner"));
setSpeed(AbstractRunner::SlowSpeed);
connect(this, SIGNAL(prepare()), this, SLOT(prep()));
reloadConfiguration();
}
AudioPlayerControlRunner::~AudioPlayerControlRunner()
{
}
void AudioPlayerControlRunner::prep()
{
m_running = false;
m_canPlay = false;
m_canGoNext = false;
m_canGoPrevious = false;
m_playbackStatus = QString();
m_identity = m_player;
QDBusInterface player(QString::fromLatin1("org.mpris.MediaPlayer2.%1").arg(m_player),
QLatin1String("/org/mpris/MediaPlayer2"), QLatin1String("org.mpris.MediaPlayer2.Player"));
m_running = player.isValid();
if (m_running) {
QDBusInterface mediaplayer2(QString::fromLatin1("org.mpris.MediaPlayer2.%1").arg(m_player),
QLatin1String("/org/mpris/MediaPlayer2"), QLatin1String("org.mpris.MediaPlayer2"));
m_identity = mediaplayer2.property("Identity").toString();
m_playbackStatus = player.property("PlaybackStatus").toString();
// NOTE: Bogus values set by Audacious for the properties bellow
m_canPlay = player.property("CanPlay").toBool();
m_canGoNext = player.property("CanGoNext").toBool();
m_canGoPrevious = player.property("CanGoPrevious").toBool();
}
// qDebug() << Q_FUNC_INFO << m_running << m_canPlay << m_canGoNext << m_canGoPrevious << m_playbackStatus;
}
void AudioPlayerControlRunner::match(Plasma::RunnerContext &context)
{
if (context.query().length() < 3) {
return;
}
const QString term = context.query();
QList<Plasma::QueryMatch> matches;
if (m_useCommands && context.isValid() ) {
QVariantList playcontrol;
playcontrol << QLatin1String("/org/mpris/MediaPlayer2") << QLatin1String("org.mpris.MediaPlayer2.Player");
// Play
if (m_comPlay.startsWith(term, Qt::CaseInsensitive) &&
(!m_running || (m_canPlay && m_playbackStatus != QLatin1String("Playing")))) {
QVariantList data = playcontrol;
data << QLatin1String("Play") << NONE << QLatin1String("start");
matches << createMatch(this, i18n("Start playing"), i18n("Audio player control"),
QLatin1String("play"), KIcon(QLatin1String("media-playback-start")), data, 1.0);
}
if (!m_running) {
// The interface of the player is not availalbe, so the rest of the commands is not needed
context.addMatches(term,matches);
return;
}
// Next song
if (m_comNext.startsWith(term, Qt::CaseInsensitive) && m_canGoNext) {
QVariantList data = playcontrol;
data << QLatin1String("Next") << NONE << QLatin1String("nostart");
matches << createMatch(this, i18n("Play next song"), i18n("Audio player control"),
QLatin1String("next"), KIcon(QLatin1String("media-skip-forward")), data, 1.0);
}
// Previous song
if (m_comPrev.startsWith(term, Qt::CaseInsensitive) && m_canGoPrevious) {
QVariantList data = playcontrol;
data << QLatin1String("Previous") << NONE << QLatin1String("nostart");
matches << createMatch(this, i18n("Play previous song"), i18n("Audio player control") ,
QLatin1String("previous"), KIcon(QLatin1String("media-skip-backward")), data, 1.0);
}
// Pause
if (m_comPause.startsWith(term, Qt::CaseInsensitive) && m_playbackStatus == QLatin1String("Playing")) {
QVariantList data = playcontrol;
data << QLatin1String("Pause") << NONE << QLatin1String("nostart");
matches << createMatch(this, i18n("Pause playing"), i18n("Audio player control"),
QLatin1String("pause"), KIcon(QLatin1String("media-playback-pause")), data, 1.0);
}
// Stop
if (m_comStop.startsWith(term, Qt::CaseInsensitive) && m_playbackStatus == QLatin1String("Playing")) {
QVariantList data = playcontrol;
data << QLatin1String("Stop") << NONE << QLatin1String("nostart");
matches << createMatch(this, i18n("Stop playing"), i18n("Audio player control"),
QLatin1String("stop"), KIcon(QLatin1String("media-playback-stop")), data, 1.0);
}
// Set volume to
QRegExp volumeRegxp(m_comVolume + QLatin1String(" \\d{1,2}0{0,1}"));
volumeRegxp.setCaseSensitivity(Qt::CaseInsensitive);
if (volumeRegxp.exactMatch(term)) {
QVariantList data = playcontrol;
int newVolume = getNumber(term , ' ');
data << QLatin1String("Volume") << NONE << QLatin1String("nostart") << (newVolume / 100.0);
matches << createMatch(this, i18n("Set volume to %1%" , newVolume),
QLatin1String("volume"), i18n("Audio player control"), KIcon(QLatin1String("audio-volume-medium")), data, 1.0);
}
// Quit player
if (m_comQuit.startsWith(term, Qt::CaseInsensitive)) {
QVariantList data;
data << QLatin1String("/org/mpris/MediaPlayer2") << QLatin1String("org.mpris.MediaPlayer2")
<< QLatin1String("Quit") << NONE << QLatin1String("nostart");
matches << createMatch(this, i18n("Quit %1", m_identity), QLatin1String(""),
QLatin1String("quit"), KIcon(QLatin1String("application-exit")), data, 1.0);
}
}
context.addMatches(term, matches);
}
void AudioPlayerControlRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match)
{
Q_UNUSED(context)
QVariantList data = match.data().value<QVariantList>();
if (data[4].toString().compare(QLatin1String("start")) == 0) {
// The players's interface isn't available but it should be started
if (!startPlayer()) {
return;
}
}
// Special case for properties
if (data[2].toString().compare(QLatin1String("Volume")) == 0) {
QDBusInterface player(QString::fromLatin1("org.mpris.MediaPlayer2.%1").arg(m_player),
data[0].toString(), data[1].toString());
player.setProperty(data[2].toByteArray(), data[5]);
return;
}
QDBusMessage msg = QDBusMessage::createMethodCall(QString::fromLatin1("org.mpris.MediaPlayer2.%1").arg(m_player),
data[0].toString(), data[1].toString(), data[2].toString());
kDebug() << msg;
QVariantList args;
for (int i = 5; data.length() > i;++i) {
args << data[i];
}
msg.setArguments(args);
QDBusConnection::sessionBus().call(msg, QDBus::NoBlock);
}
QList<QAction*> AudioPlayerControlRunner::actionsForMatch(const Plasma::QueryMatch &match)
{
QList<QAction*> ret;
QVariantList data = match.data().value<QVariantList>();
if (data.length() > 3 && data[3].toString().compare(NONE)) {
if (!action(PLAY)) {
addAction(PLAY, KIcon(QLatin1String("media-playback-start")), i18n("Play"));
addAction(APPEND, KIcon(QLatin1String("media-track-add-amarok")), i18n("Append to playlist"));
}
const QStringList actions = data[3].toString().split(QLatin1Char( ',' ));
for (int i = 0; i < actions.length(); ++i) {
ret << action(actions[i]);
}
}
return ret;
}
void AudioPlayerControlRunner::reloadConfiguration()
{
KConfigGroup grp = config();
m_player = grp.readEntry(CONFIG_PLAYER, "vlc");
m_useCommands = grp.readEntry(CONFIG_COMMANDS, true);
m_comPlay = grp.readEntry(CONFIG_PLAY, i18n("play"));
m_comAppend = grp.readEntry(CONFIG_APPEND, i18n("append"));
m_comPause = grp.readEntry(CONFIG_PAUSE, i18n("pause"));
m_comNext = grp.readEntry(CONFIG_NEXT, i18n("next"));
m_comPrev = grp.readEntry(CONFIG_PREV, i18n("prev"));
m_comStop = grp.readEntry(CONFIG_STOP, i18n("stop"));
m_comVolume = grp.readEntry(CONFIG_VOLUME, i18n("volume"));
m_comQuit = grp.readEntry(CONFIG_QUIT, i18n("quit"));
// Adding the syntaxes for helping the user
QList<Plasma::RunnerSyntax> syntaxes;
syntaxes << Plasma::RunnerSyntax(m_comPlay, i18n("Plays a song from playlist"));
syntaxes << Plasma::RunnerSyntax(m_comPause,i18n("Pauses the playing"));
syntaxes << Plasma::RunnerSyntax(m_comNext, i18n("Plays the next song in the playlist if one is available"));
syntaxes << Plasma::RunnerSyntax(m_comPrev, i18n("Plays the previous song if one is available"));
syntaxes << Plasma::RunnerSyntax(m_comStop, i18n("Stops the playing"));
syntaxes << Plasma::RunnerSyntax(m_comVolume + QLatin1String(" :q:"), i18n("Sets the volume to :q:"));
syntaxes << Plasma::RunnerSyntax(m_comQuit, i18n("Quits the player"));
setSyntaxes(syntaxes);
}
Plasma::QueryMatch AudioPlayerControlRunner::createMatch(Plasma::AbstractRunner* runner,
const QString &title, const QString &subtext, const QString &id,
const KIcon &icon, const QVariantList &data, const float &relevance)
{
Plasma::QueryMatch match(runner);
match.setText(title);
match.setSubtext(subtext);
match.setId(id);
match.setIcon(icon);
match.setData(data);
match.setRelevance(relevance);
return match;
}
bool AudioPlayerControlRunner::startPlayer()
{
if (m_running) {
return true;
}
if (!KRun::run(m_player, KUrl::List(), Q_NULLPTR)) {
// We couldn't start the player
KMessageBox::error(Q_NULLPTR, i18n("%1 not found", m_player),
i18n("%1 was not found so the runner is unable to work.", m_player));
return false;
}
QElapsedTimer waitTimeout;
waitTimeout.start();
while (!m_running && waitTimeout.elapsed() < 30000) {
// Waiting for the player's interface to appear
QDBusInterface player(QString::fromLatin1("org.mpris.MediaPlayer2.%1").arg(m_player),
QLatin1String("/org/mpris/MediaPlayer2"), QLatin1String("org.mpris.MediaPlayer2.Player"));
m_running = player.isValid();
QCoreApplication::processEvents();
}
if (m_running) {
prep();
return true;
}
return false;
}
int AudioPlayerControlRunner::getNumber(const QString& term, const char character)
{
return term.section(QLatin1Char(character), 1, 1).toInt();
}
#include "moc_audioplayercontrolrunner.cpp"