Finish implementing basic play, engine, and service.
This commit is contained in:
@@ -13,7 +13,7 @@ include(KDECMakeSettings)
|
||||
include(KDECompilerSettings NO_POLICY_SCOPE)
|
||||
include(FeatureSummary)
|
||||
|
||||
find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Core)
|
||||
find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Core Multimedia)
|
||||
|
||||
find_package(KF5 ${KF_MIN_VERSION} REQUIRED COMPONENTS
|
||||
CoreAddons
|
||||
|
||||
@@ -11,6 +11,7 @@ kcoreaddons_desktop_to_json(plasma_engine_simplecast plasma-dataengine-simplecas
|
||||
|
||||
target_link_libraries( plasma_engine_simplecast
|
||||
Qt5::Core
|
||||
Qt5::Multimedia
|
||||
KF5::Plasma
|
||||
KF5::Service
|
||||
KF5::I18n
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
/*
|
||||
* Simple Media Player that runs off a playlist.
|
||||
* Copyright (C) 2019 Kevin Whitaker <eyecreate@eyecreate.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*ByteArray(jsonTrack)
|
||||
* 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
|
||||
@@ -17,17 +16,202 @@
|
||||
*/
|
||||
|
||||
#include "minimediaplayer.h"
|
||||
#include <QProcess>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
MiniMediaPlayer::MiniMediaPlayer(QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_player = new QMediaPlayer(this,QMediaPlayer::StreamPlayback);
|
||||
connect(m_player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),this,SLOT(mediaStatusChanged(QMediaPlayer::MediaStatus)));
|
||||
connect(m_player,SIGNAL(stateChanged(QMediaPlayer::State)),this,SLOT(mediaStateChanged(QMediaPlayer::State)));
|
||||
connect(m_player,SIGNAL(durationChanged(qint64)),this,SLOT(durationChanged(qint64)));
|
||||
connect(m_player,SIGNAL(positionChanged(qint64)),this,SLOT(positionChanged(qint64)));
|
||||
}
|
||||
|
||||
QMediaPlaylist* MiniMediaPlayer::playlist() const
|
||||
bool MiniMediaPlayer::isPlaying()
|
||||
{
|
||||
return m_playlist;
|
||||
return m_player->state() == QMediaPlayer::State::PlayingState;
|
||||
}
|
||||
|
||||
QMediaPlayer* MiniMediaPlayer::player() const
|
||||
bool MiniMediaPlayer::nextTrack()
|
||||
{
|
||||
return m_player;
|
||||
if(playlist.size() > 1)
|
||||
{
|
||||
playlist.pop_front();
|
||||
m_player->stop();
|
||||
m_player->setMedia(getStreamUrlFromUrl(playlist.first().second));
|
||||
emit playlistChanged(getTrackNames());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MiniMediaPlayer::playPauseTrack(bool playing)
|
||||
{
|
||||
if(m_player->state() == QMediaPlayer::State::PlayingState && !playing)
|
||||
{
|
||||
m_player->pause();
|
||||
return true;
|
||||
}
|
||||
else if(m_player->state() == QMediaPlayer::State::PausedState && playing)
|
||||
{
|
||||
m_player->play();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QList<QString> MiniMediaPlayer::getTrackNames()
|
||||
{
|
||||
QList<QString> playlistTitles;
|
||||
for(QPair<QString,QUrl> item: playlist)
|
||||
{
|
||||
playlistTitles.append(item.first);
|
||||
}
|
||||
return playlistTitles;
|
||||
}
|
||||
|
||||
QMap<QString, qint64> MiniMediaPlayer::currentState()
|
||||
{
|
||||
QMap<QString,qint64> state;
|
||||
state.insert("duration",m_player->duration());
|
||||
state.insert("position",m_player->position());
|
||||
state.insert("state",isPlaying());
|
||||
return state;
|
||||
}
|
||||
|
||||
void MiniMediaPlayer::durationChanged(qint64 length)
|
||||
{
|
||||
emit trackDurationChanged(length);
|
||||
}
|
||||
|
||||
void MiniMediaPlayer::positionChanged(qint64 position)
|
||||
{
|
||||
emit trackPositionChanged(position);
|
||||
}
|
||||
|
||||
bool MiniMediaPlayer::addURLToPlaylist(QUrl mediaUrl)
|
||||
{
|
||||
if(isValidMediaUrl(mediaUrl))
|
||||
{
|
||||
bool shouldStartPlaying = false;
|
||||
if(playlist.size() == 0) shouldStartPlaying = true;
|
||||
QList<QPair<QString,QUrl>> tracks = getStreamTitlesAndUrlsFromUrl(mediaUrl);
|
||||
playlist.append(tracks);
|
||||
emit playlistChanged(getTrackNames());
|
||||
if(shouldStartPlaying && playlist.size() > 0)
|
||||
{
|
||||
m_player->setMedia(getStreamUrlFromUrl(playlist.first().second));
|
||||
}
|
||||
if(tracks.size() > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QUrl MiniMediaPlayer::getStreamUrlFromUrl(QUrl mediaUrl)
|
||||
{
|
||||
//Run youtube-dl on Url and return Url given back.
|
||||
QProcess youtubedl;
|
||||
youtubedl.start("youtube-dl -f bestaudio -g "+mediaUrl.toString());
|
||||
if(youtubedl.waitForFinished(-1))
|
||||
{
|
||||
QByteArray out = youtubedl.readAllStandardOutput();
|
||||
QString outStr = QString::fromStdString(out.toStdString());
|
||||
for(QString track:outStr.split('\n'))
|
||||
{
|
||||
qDebug() << "Found URL stream:" << track;
|
||||
return QUrl(track);
|
||||
}
|
||||
}
|
||||
return QUrl();
|
||||
}
|
||||
|
||||
bool MiniMediaPlayer::isValidMediaUrl(QUrl mediaUrl)
|
||||
{
|
||||
//Only support youtube and bandcamp for now.
|
||||
if(mediaUrl.host().endsWith("youtube.com") || mediaUrl.host().endsWith("bandcamp.com"))
|
||||
{
|
||||
//TODO: check more than host?
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MiniMediaPlayer::mediaStateChanged(QMediaPlayer::State state)
|
||||
{
|
||||
if(state == QMediaPlayer::State::PlayingState)
|
||||
{
|
||||
emit playStateChanged(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit playStateChanged(false);
|
||||
}
|
||||
}
|
||||
|
||||
void MiniMediaPlayer::mediaStatusChanged(QMediaPlayer::MediaStatus status)
|
||||
{
|
||||
//Make sure to load next track once track has ended.
|
||||
if(status == QMediaPlayer::MediaStatus::EndOfMedia)
|
||||
{
|
||||
//Load next track
|
||||
nextTrack();
|
||||
}
|
||||
else if(status == QMediaPlayer::MediaStatus::InvalidMedia)
|
||||
{
|
||||
qWarning() << "Unable to play track.";
|
||||
}
|
||||
else if(status == QMediaPlayer::MediaStatus::LoadedMedia)
|
||||
{
|
||||
//Start playing media
|
||||
m_player->play();
|
||||
}
|
||||
}
|
||||
|
||||
QList<QPair<QString,QUrl>> MiniMediaPlayer::getStreamTitlesAndUrlsFromUrl(QUrl mediaUrl)
|
||||
{
|
||||
QList<QPair<QString,QUrl>> tracks;
|
||||
//Run youtube-dl on Url and return json given back.
|
||||
QProcess youtubedl;
|
||||
youtubedl.start("youtube-dl -f bestaudio -j "+mediaUrl.toString());
|
||||
if(youtubedl.waitForFinished(-1))
|
||||
{
|
||||
//If there is anything in error output, error must have occured.
|
||||
if(youtubedl.readAllStandardError().size()== 0)
|
||||
{
|
||||
QByteArray out = youtubedl.readAllStandardOutput();
|
||||
QString outStr = QString::fromStdString(out.toStdString());
|
||||
for(QString jsonTrack:outStr.split('\n'))
|
||||
{
|
||||
//Parse urls and titles from json list
|
||||
QJsonDocument info = QJsonDocument::fromJson(jsonTrack.toUtf8());
|
||||
if(!info.isNull() && info.isObject())
|
||||
{
|
||||
QPair<QString,QUrl> track;
|
||||
QJsonObject jsonTrackInfo = info.object();
|
||||
track.first = jsonTrackInfo["title"].toString();
|
||||
track.second = QUrl(jsonTrackInfo["webpage_url"].toString());
|
||||
tracks.append(track);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "Url may be invalid.";
|
||||
}
|
||||
}
|
||||
return tracks;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/*
|
||||
* Simple Media Player that runs off a playlist.
|
||||
* Copyright (C) 2019 Kevin Whitaker <eyecreate@eyecreate.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@@ -21,36 +20,49 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QtMultimedia/QMediaPlayer>
|
||||
#include <QtMultimedia/QMediaPlaylist>
|
||||
|
||||
/**
|
||||
* @todo write docs
|
||||
* Mini Media Player is a simple class that wraps some wrappers around Qt's Multimedia classes for adding and playing media from a playlist. Also parses URLs to be added to playlist using youtube-dl and makes sure they are valid.
|
||||
*/
|
||||
class MiniMediaPlayer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QMediaPlaylist playlist READ playlist)
|
||||
Q_PROPERTY(QMediaPlayer player READ player)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @todo write docs
|
||||
*/
|
||||
MiniMediaPlayer(QObject *parent = 0);
|
||||
|
||||
bool addURLToPlaylist(QUrl mediaUrl);
|
||||
|
||||
/**
|
||||
* @return the playlist
|
||||
*/
|
||||
QMediaPlaylist *playlist() const;
|
||||
|
||||
/**
|
||||
* @return the player
|
||||
*/
|
||||
QMediaPlayer *player() const;
|
||||
|
||||
QList<QString> getTrackNames();
|
||||
|
||||
bool isPlaying();
|
||||
|
||||
QMap<QString,qint64> currentState();
|
||||
|
||||
bool nextTrack();
|
||||
|
||||
bool playPauseTrack(bool playing);
|
||||
|
||||
signals:
|
||||
void playStateChanged(bool isPlaying);
|
||||
void playlistChanged(QStringList trackTitles);
|
||||
void trackDurationChanged(qint64 length);
|
||||
void trackPositionChanged(qint64 position);
|
||||
|
||||
private slots:
|
||||
void mediaStatusChanged(QMediaPlayer::MediaStatus status);
|
||||
void mediaStateChanged(QMediaPlayer::State state);
|
||||
void durationChanged(qint64 length);
|
||||
void positionChanged(qint64 position);
|
||||
|
||||
private:
|
||||
QMediaPlaylist *m_playlist;
|
||||
QMediaPlayer *m_player;
|
||||
QList<QPair<QString,QUrl>> playlist;
|
||||
|
||||
bool isValidMediaUrl(QUrl mediaUrl);
|
||||
QUrl getStreamUrlFromUrl(QUrl mediaUrl);
|
||||
QList<QPair<QString,QUrl>> getStreamTitlesAndUrlsFromUrl(QUrl mediaUrl);
|
||||
|
||||
};
|
||||
|
||||
#endif // MINIMEDIAPLAYER_H
|
||||
|
||||
@@ -22,25 +22,55 @@
|
||||
#include "simplecastservice.h"
|
||||
|
||||
SimpleCastEngine::SimpleCastEngine(QObject *parent, const QVariantList &args)
|
||||
: Plasma::DataEngine(parent,args)
|
||||
: Plasma::DataEngine(parent,args), player(new MiniMediaPlayer(this))
|
||||
{
|
||||
Q_UNUSED(args)
|
||||
setMinimumPollingInterval(333);
|
||||
connect(player,SIGNAL(playStateChanged(bool)),this,SLOT(playStateChanged(bool)));
|
||||
connect(player,SIGNAL(trackDurationChanged(qint64)),this,SLOT(durationChanged(qint64)));
|
||||
connect(player,SIGNAL(trackPositionChanged(qint64)),this,SLOT(positionChanged(qint64)));
|
||||
connect(player,SIGNAL(playlistChanged(QStringList)),this,SLOT(playlistChanged(QStringList)));
|
||||
}
|
||||
|
||||
void SimpleCastEngine::durationChanged(qint64 length)
|
||||
{
|
||||
setData("CurrentState","trackDuration",length);
|
||||
}
|
||||
|
||||
void SimpleCastEngine::playStateChanged(bool isPlaying)
|
||||
{
|
||||
setData("CurrentState","playingState",isPlaying);
|
||||
}
|
||||
|
||||
void SimpleCastEngine::positionChanged(qint64 position)
|
||||
{
|
||||
setData("CurrentState","trackPosition",position);
|
||||
}
|
||||
|
||||
bool SimpleCastEngine::sourceRequestEvent(const QString& source)
|
||||
{
|
||||
setData(source, "you found me");
|
||||
return true;
|
||||
//return updateSourceEvent(source);//TODO
|
||||
return updateSourceEvent(source);
|
||||
}
|
||||
|
||||
void SimpleCastEngine::playlistChanged(QStringList playlist)
|
||||
{
|
||||
setData("Playlist",playlist);
|
||||
}
|
||||
|
||||
bool SimpleCastEngine::updateSourceEvent(const QString& source)
|
||||
{
|
||||
//Source for current playlist(contains title and url)
|
||||
//Source for current track time and length, and playing state
|
||||
setData(source, "you found me again");
|
||||
return true;//TODO
|
||||
if(source == "Playlist")
|
||||
{
|
||||
setData(source, QStringList(player->getTrackNames()));
|
||||
}
|
||||
else if(source == "CurrentState")
|
||||
{
|
||||
QMap<QString,qint64> state = player->currentState();
|
||||
setData(source,"trackDuration",state["duration"]);
|
||||
setData(source,"trackPosition",state["position"]);
|
||||
setData(source,"playingState",state["state"]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList SimpleCastEngine::sources() const
|
||||
@@ -52,7 +82,7 @@ Plasma::Service * SimpleCastEngine::serviceForSource(const QString& source)
|
||||
{
|
||||
if(source == "CurrentState")
|
||||
{
|
||||
return new SimpleCastService(source, this);
|
||||
return new SimpleCastService(source, player, this);
|
||||
}
|
||||
return Plasma::DataEngine::serviceForSource(source);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,11 @@
|
||||
|
||||
#include <Plasma/DataEngine>
|
||||
#include <qt5/QtCore/QString>
|
||||
#include "minimediaplayer.h"
|
||||
|
||||
/**
|
||||
* Simple Cast Engine is a Plasma DataEngine that conveys that status on the media player casting to to other code like Plasmoids.
|
||||
*/
|
||||
class SimpleCastEngine : public Plasma::DataEngine
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -33,9 +37,15 @@ public:
|
||||
QStringList sources() const override;
|
||||
Plasma::Service *serviceForSource(const QString &source) override;
|
||||
|
||||
private slots:
|
||||
void durationChanged(qint64 length);
|
||||
void positionChanged(qint64 position);
|
||||
void playStateChanged(bool isPlaying);
|
||||
void playlistChanged(QStringList playlist);
|
||||
protected:
|
||||
bool sourceRequestEvent(const QString &source) override;
|
||||
bool updateSourceEvent(const QString &source) override;
|
||||
MiniMediaPlayer *player;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* <one line to give the program's name and a brief idea of what it does.>
|
||||
* Copyright (C) 2019 <copyright holder> <email>
|
||||
* Copyright (C) 2019 Kevin Whitaker <eyecreate@eyecreate.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,8 +16,42 @@
|
||||
*/
|
||||
|
||||
#include "simplecastservice.h"
|
||||
#include <Plasma/ServiceJob>
|
||||
|
||||
SimpleCastService::SimpleCastService(const QString &destination, QObject *parent) : Plasma::Service(parent)
|
||||
class SimpleCastJob : public Plasma::ServiceJob
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SimpleCastJob(const QString& destination, const QString& operation, MiniMediaPlayer *player, const QVariantMap& parameters, QObject* parent = 0) : Plasma::ServiceJob(destination,operation,parameters,parent),player(player) {}
|
||||
|
||||
virtual void start() override
|
||||
{
|
||||
const auto operation = operationName();
|
||||
const auto paramaters = parameters();
|
||||
if(operation == "changePlayState")
|
||||
{
|
||||
if(paramaters.contains("playing"))
|
||||
{
|
||||
setResult(player->playPauseTrack(paramaters["playing"].toBool()));
|
||||
}
|
||||
}
|
||||
else if(operation == "skipToNextTrack")
|
||||
{
|
||||
setResult(player->nextTrack());
|
||||
}
|
||||
else if(operation == "addURLToPlaylist")
|
||||
{
|
||||
if(paramaters.contains("URL"))
|
||||
{
|
||||
setResult(player->addURLToPlaylist(paramaters["URL"].toUrl()));
|
||||
}
|
||||
}
|
||||
}
|
||||
private:
|
||||
MiniMediaPlayer *player;
|
||||
};
|
||||
|
||||
SimpleCastService::SimpleCastService(const QString &destination,MiniMediaPlayer *player, QObject *parent) : Plasma::Service(parent), player(player)
|
||||
{
|
||||
setName("simplecastcontrol");
|
||||
}
|
||||
@@ -30,7 +63,7 @@ SimpleCastService::~SimpleCastService()
|
||||
|
||||
Plasma::ServiceJob* SimpleCastService::createJob(const QString& operation, QVariantMap& parameters)
|
||||
{
|
||||
return NULL; //TODO
|
||||
return new SimpleCastJob(destination(),operation,player,parameters,this);
|
||||
}
|
||||
|
||||
//K_EXPORT_PLASMA_SERVICE(org.eyecreate.simplecast, SimpleCastService)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* <one line to give the program's name and a brief idea of what it does.>
|
||||
* Copyright (C) 2019 <copyright holder> <email>
|
||||
* Copyright (C) 2019 Kevin Whitaker <eyecreate@eyecreate.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,25 +19,20 @@
|
||||
#define SIMPLECASTSERVICE_H
|
||||
|
||||
#include <Plasma/Service>
|
||||
#include "minimediaplayer.h"
|
||||
|
||||
/**
|
||||
* @todo write docs
|
||||
* Simple Cast Service is a Plasma Service that controls the media player casting to.
|
||||
*/
|
||||
class SimpleCastService : public Plasma::Service
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SimpleCastService(const QString &destination, QObject *parent = 0);
|
||||
SimpleCastService(const QString &destination, MiniMediaPlayer *player, QObject *parent = 0);
|
||||
~SimpleCastService();
|
||||
protected:
|
||||
/**
|
||||
* @todo write docs
|
||||
*
|
||||
* @param operation TODO
|
||||
* @param parameters TODO
|
||||
* @return TODO
|
||||
*/
|
||||
Plasma::ServiceJob* createJob(const QString& operation, QVariantMap& parameters) override;
|
||||
MiniMediaPlayer *player;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user