This commit adds the basic functionality including: - local/remote video playback - subtitles support - Kaffeine playlist integration What is missing: - sound controls Signed-off-by: Mavridis Philippe <mavridisf@gmail.com>
parent
06028cccb1
commit
54545a0913
@ -0,0 +1,50 @@
|
||||
include_directories(
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${TDE_INCLUDE_DIR}
|
||||
${TQT_INCLUDE_DIRS}
|
||||
${GSTREAMER_INCLUDE_DIRS}
|
||||
${GSTREAMER_PLUGIN_INCLUDE_DIRS}
|
||||
${GSTREAMER_VIDEO_INCLUDE_DIRS}
|
||||
${CMAKE_SOURCE_DIR}/kaffeine/src/player-parts/kaffeine-part
|
||||
${CMAKE_SOURCE_DIR}/kaffeine/src
|
||||
)
|
||||
|
||||
link_directories(
|
||||
${TQT_LIBRARY_DIRS}
|
||||
${TDE_LIB_DIR}
|
||||
#${MPV_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
|
||||
##### libmpvpart (kpart)
|
||||
|
||||
tde_add_kpart( libmpvpart AUTOMOC
|
||||
|
||||
SOURCES
|
||||
libmpv_part.cpp libmpv_event.cpp
|
||||
LINK
|
||||
tdecore-shared
|
||||
tdeui-shared
|
||||
tdeio-shared
|
||||
tdeparts-shared
|
||||
kaffeinepart-shared
|
||||
${MPV_LIBRARIES}
|
||||
|
||||
DESTINATION ${PLUGIN_INSTALL_DIR}
|
||||
)
|
||||
|
||||
|
||||
##### other data
|
||||
|
||||
tde_create_translated_desktop(
|
||||
SOURCE libmpv_part.desktop
|
||||
DESTINATION ${SERVICES_INSTALL_DIR}
|
||||
PO_DIR kaffeine-desktops
|
||||
)
|
||||
|
||||
install(
|
||||
FILES libmpv_part.rc
|
||||
DESTINATION ${DATA_INSTALL_DIR}/libmpvpart
|
||||
)
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Kaffeine libmpv part
|
||||
* Copyright (C) 2023 Mavridis Philippe <mavridisf@gmail.com>
|
||||
*
|
||||
* Based on Kaffeine dummy part
|
||||
* Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net>
|
||||
*
|
||||
* 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 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
|
||||
*/
|
||||
|
||||
// TQt
|
||||
#include <tqapplication.h>
|
||||
|
||||
// TDE
|
||||
#include <tdelocale.h>
|
||||
#include <kdebug.h>
|
||||
|
||||
// Part
|
||||
#include "libmpv_event.h"
|
||||
#include "libmpv_part.h"
|
||||
|
||||
MpvEventThread::MpvEventThread(MpvPart *part) {
|
||||
m_part = part;
|
||||
initPropertyObservers();
|
||||
}
|
||||
|
||||
void MpvEventThread::initPropertyObservers() {
|
||||
mpv_observe_property(m_part->m_mpv, 0, "time-pos", MPV_FORMAT_DOUBLE);
|
||||
mpv_observe_property(m_part->m_mpv, 0, "duration", MPV_FORMAT_DOUBLE);
|
||||
mpv_observe_property(m_part->m_mpv, 0, "media-title", MPV_FORMAT_STRING);
|
||||
|
||||
// "The advantage over using this instead of calculating it out of other
|
||||
// properties is that it properly falls back to estimating the playback
|
||||
// position from the byte position, if the file duration is not known."
|
||||
mpv_observe_property(m_part->m_mpv, 0, "percent-pos", MPV_FORMAT_DOUBLE);
|
||||
|
||||
//mpv_observe_property(m_mpv, 0, "track-list", MPV_FORMAT_NODE);
|
||||
//mpv_observe_property(m_mpv, 0, "chapter-list", MPV_FORMAT_NODE);
|
||||
}
|
||||
|
||||
void MpvEventThread::run() {
|
||||
while (m_part->m_mpv) {
|
||||
mpv_event *event = mpv_wait_event(m_part->m_mpv, 0);
|
||||
if (event->event_id != MPV_EVENT_NONE) {
|
||||
processEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MpvEventThread::processEvent(mpv_event *event) {
|
||||
switch (event->event_id) {
|
||||
case MPV_EVENT_PROPERTY_CHANGE: {
|
||||
mpv_event_property *prop = (mpv_event_property *)event->data;
|
||||
MpvPropertyChangeEvent *pe = new MpvPropertyChangeEvent(
|
||||
prop->name, prop->format, prop->data);
|
||||
TQApplication::postEvent(m_part, pe);
|
||||
break;
|
||||
}
|
||||
|
||||
case MPV_EVENT_LOG_MESSAGE: {
|
||||
struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data;
|
||||
kdDebug() << "[mpv " << msg->prefix << "] " << msg->level << ": "
|
||||
<< msg->text << endl;
|
||||
break;
|
||||
}
|
||||
|
||||
case MPV_EVENT_END_FILE: {
|
||||
struct mpv_event_end_file *end = (struct mpv_event_end_file *)event->data;
|
||||
TQString error;
|
||||
if (end->reason == MPV_END_FILE_REASON_ERROR) {
|
||||
error = TQString(mpv_error_string(end->error));
|
||||
}
|
||||
MpvEOFEvent *eofe = new MpvEOFEvent(end->reason, error);
|
||||
TQApplication::postEvent(m_part, eofe);
|
||||
}
|
||||
|
||||
default: break; // ignore other events
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Kaffeine libmpv part
|
||||
* Copyright (C) 2023 Mavridis Philippe <mavridisf@gmail.com>
|
||||
*
|
||||
* Based on Kaffeine dummy part
|
||||
* Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net>
|
||||
*
|
||||
* 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 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
|
||||
*/
|
||||
|
||||
// TQt
|
||||
#include <tqthread.h>
|
||||
#include <tqevent.h>
|
||||
#include <tqdatetime.h>
|
||||
|
||||
// libmpv
|
||||
#include <mpv/client.h>
|
||||
|
||||
#define MPVPART_EVENT_PROPERTY_CHANGE 65890
|
||||
#define MPVPART_EVENT_EOF 65891
|
||||
|
||||
class MpvPart;
|
||||
|
||||
class MpvPropertyChangeEvent : public TQCustomEvent {
|
||||
public:
|
||||
MpvPropertyChangeEvent(TQCString property, mpv_format format, void *data)
|
||||
: TQCustomEvent(MPVPART_EVENT_PROPERTY_CHANGE),
|
||||
_property(property), _format(format), _data(data)
|
||||
{}
|
||||
|
||||
TQCString property() { return _property; }
|
||||
mpv_format format() { return _format; }
|
||||
void *data() { return _data; }
|
||||
|
||||
double toDouble() { return *(double *)_data; }
|
||||
TQTime toTime() { return TQTime().addMSecs(toDouble() * 1000); }
|
||||
|
||||
private:
|
||||
TQCString _property;
|
||||
mpv_format _format;
|
||||
void *_data;
|
||||
};
|
||||
|
||||
class MpvEOFEvent : public TQCustomEvent {
|
||||
public:
|
||||
MpvEOFEvent(mpv_end_file_reason reason, TQString error = TQString::null)
|
||||
: TQCustomEvent(MPVPART_EVENT_EOF), _reason(reason), _error(error)
|
||||
{}
|
||||
|
||||
mpv_end_file_reason reason() { return _reason; }
|
||||
TQString error() { return _error; }
|
||||
|
||||
private:
|
||||
mpv_end_file_reason _reason;
|
||||
TQString _error;
|
||||
};
|
||||
|
||||
class MpvEventThread : public TQThread {
|
||||
public:
|
||||
MpvEventThread(MpvPart *part);
|
||||
virtual void run();
|
||||
|
||||
private:
|
||||
void initPropertyObservers();
|
||||
void processEvent(mpv_event *event);
|
||||
|
||||
private:
|
||||
MpvPart *m_part;
|
||||
};
|
@ -0,0 +1,401 @@
|
||||
/*
|
||||
* Kaffeine libmpv part
|
||||
* Copyright (C) 2023 Mavridis Philippe <mavridisf@gmail.com>
|
||||
*
|
||||
* Based on Kaffeine dummy part
|
||||
* Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net>
|
||||
*
|
||||
* 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 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
|
||||
*/
|
||||
|
||||
// TQt
|
||||
#include <tqtooltip.h>
|
||||
#include <tqslider.h>
|
||||
#include <tqpushbutton.h>
|
||||
#include <tqfile.h>
|
||||
|
||||
// TDE
|
||||
#include <tdeparts/genericfactory.h>
|
||||
#include <tdeglobalsettings.h>
|
||||
#include <tdeio/netaccess.h>
|
||||
#include <tdeaction.h>
|
||||
#include <kmimetype.h>
|
||||
#include <tdemessagebox.h>
|
||||
#include <kxmlguifactory.h>
|
||||
#include <tdetoolbar.h>
|
||||
#include <kdebug.h>
|
||||
|
||||
// Kaffeine
|
||||
#include "playlistimport.h"
|
||||
|
||||
// Part
|
||||
#include "libmpv_part.h"
|
||||
#include "libmpv_event.h"
|
||||
|
||||
typedef KParts::GenericFactory<MpvPart> MpvPartFactory;
|
||||
K_EXPORT_COMPONENT_FACTORY (libmpvpart, MpvPartFactory);
|
||||
|
||||
MpvPart::MpvPart(TQWidget* parentWidget, const char* widgetName, TQObject* parent, const char* name, const TQStringList& /*args*/)
|
||||
: KaffeinePart(parent, name ? name : "MpvPart"),
|
||||
m_current(0),
|
||||
m_seeking(false)
|
||||
{
|
||||
// Create an instance of this class
|
||||
setInstance(MpvPartFactory::instance());
|
||||
|
||||
// Create container widget
|
||||
m_player = new TQWidget(parentWidget);
|
||||
m_player->setBackgroundColor(TQt::black);
|
||||
m_player->setFocusPolicy(TQ_ClickFocus);
|
||||
setWidget(m_player);
|
||||
|
||||
// Initialize and embed mpv
|
||||
if (!initMpv()) {
|
||||
kdError() << "libmpvpart: initialization of mpv failed!" << endl;
|
||||
emit canceled(i18n("MPV initialization failed!"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize GUI
|
||||
setXMLFile("libmpv_part.rc");
|
||||
initActions();
|
||||
emit setStatusBarText(i18n("Ready"));
|
||||
}
|
||||
|
||||
MpvPart::~MpvPart()
|
||||
{
|
||||
if (m_mpv) {
|
||||
mpv_terminate_destroy(m_mpv);
|
||||
m_mpv = nullptr;
|
||||
}
|
||||
|
||||
if (m_eventThread) {
|
||||
// the event thread doesn't do I/O, so we just kill it
|
||||
m_eventThread->terminate();
|
||||
}
|
||||
}
|
||||
|
||||
TDEAboutData *MpvPart::createAboutData() {
|
||||
TDEAboutData* aboutData = new TDEAboutData(
|
||||
"libmpvpart", I18N_NOOP("MpvPart"),
|
||||
"0.1", "A Kaffeine player part based on libmpv",
|
||||
TDEAboutData::License_GPL,
|
||||
"(c) 2023 Trinity Desktop Project", 0);
|
||||
aboutData->addAuthor("Mavridis Philippe", I18N_NOOP("Developer"), "mavridisf@gmail.com");
|
||||
aboutData->addAuthor("Jürgen Kofler", I18N_NOOP("Original Kaffeine developer"), "kaffeine@gmx.net");
|
||||
return aboutData;
|
||||
}
|
||||
|
||||
bool MpvPart::initMpv() {
|
||||
// Create mpv instance
|
||||
m_mpv = mpv_create();
|
||||
if (!m_mpv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Embed mpv into container widget
|
||||
int64_t wid = static_cast<int64_t>(m_player->winId());
|
||||
mpv_set_option(m_mpv, "wid", MPV_FORMAT_INT64, &wid);
|
||||
|
||||
// Get log messages with minimal level "info"
|
||||
mpv_request_log_messages(m_mpv, "info");
|
||||
|
||||
// Start mpv event thread
|
||||
m_eventThread = new MpvEventThread(this);
|
||||
m_eventThread->start(TQThread::LowPriority);
|
||||
|
||||
return (mpv_initialize(m_mpv) >= 0);
|
||||
}
|
||||
|
||||
void MpvPart::initActions()
|
||||
{
|
||||
new TDEAction(i18n("Play"), "media-playback-start", 0, this, SLOT(slotPlay()), actionCollection(), "player_play");
|
||||
new TDEAction(i18n("Pause"), "media-playback-pause", Key_Space, this, SLOT(slotTogglePause()), actionCollection(), "player_pause");
|
||||
new TDEAction(i18n("Stop"), "media-playback-stop", Key_Backspace, this, SLOT(slotStop()), actionCollection(), "player_stop");
|
||||
new TDEAction(i18n("&Previous"), "media-skip-backward", Key_PageUp, this, SLOT(slotPrevious()), actionCollection(), "player_previous");
|
||||
new TDEAction(i18n("&Next"), "media-skip-forward", Key_PageDown, this, SLOT(slotNext()), actionCollection(), "player_next");
|
||||
|
||||
// Important: we have a max of 1000 instead of 100 for better precision; multiply/divide your percentages by 10
|
||||
m_position = new TQSlider(0, 1000, 10, 0, TQt::Horizontal, 0);
|
||||
TQToolTip::add(m_position, i18n("Position"));
|
||||
m_position->setTracking(false);
|
||||
m_position->setFocusPolicy(TQ_NoFocus);
|
||||
m_position->setMinimumWidth(100);
|
||||
connect(m_position, SIGNAL(sliderPressed()), this, SLOT(slotStartSeeking()));
|
||||
connect(m_position, SIGNAL(sliderMoved(int)), this, SLOT(slotSetSeekingPos(int)));
|
||||
connect(m_position, SIGNAL(sliderReleased()), this, SLOT(slotStopSeeking()));
|
||||
KWidgetAction *posAction = new KWidgetAction(m_position, i18n("Position"), 0, 0, 0, actionCollection(), "player_position");
|
||||
posAction->setAutoSized(true);
|
||||
|
||||
m_playtime = new TQPushButton(0);
|
||||
TQFontMetrics met(TDEGlobalSettings::generalFont());
|
||||
m_playtime->setFixedWidth(met.width("-88:88:88") + 6);
|
||||
m_playtime->setSizePolicy(TQSizePolicy (TQSizePolicy::Fixed, TQSizePolicy::Fixed));
|
||||
m_playtime->setFocusPolicy(TQ_NoFocus);
|
||||
new KWidgetAction(m_playtime, i18n("Playtime"), 0, 0, 0, actionCollection(), "player_playtime");
|
||||
|
||||
resetTime();
|
||||
}
|
||||
|
||||
// Custom events dispatched from mpv event thread are handled here
|
||||
void MpvPart::customEvent(TQCustomEvent *event) {
|
||||
if (event->type() == MPVPART_EVENT_PROPERTY_CHANGE) {
|
||||
MpvPropertyChangeEvent *pe = (MpvPropertyChangeEvent *)event;
|
||||
if (pe->property() == "time-pos" && pe->format() == MPV_FORMAT_DOUBLE) {
|
||||
m_time = pe->toTime();
|
||||
m_playtime->setText(m_time.toString());
|
||||
}
|
||||
|
||||
else if (pe->property() == "percent-pos" && pe->format() == MPV_FORMAT_DOUBLE) {
|
||||
if (!m_seeking) {
|
||||
m_percent = pe->toDouble();
|
||||
m_position->setValue(m_percent * 10);
|
||||
}
|
||||
}
|
||||
|
||||
else if (pe->property() == "duration" && pe->format() == MPV_FORMAT_DOUBLE) {
|
||||
MRL mrl = m_playlist[m_current];
|
||||
TQTime length = TQTime().addMSecs(pe->toDouble() * 1000);
|
||||
if (!length.isNull()) {
|
||||
mrl.setLength(length);
|
||||
emit signalNewMeta(m_mrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (event->type() == MPVPART_EVENT_EOF) {
|
||||
resetTime();
|
||||
|
||||
MpvEOFEvent *eofe = (MpvEOFEvent *)event;
|
||||
if (eofe->reason() == MPV_END_FILE_REASON_ERROR) {
|
||||
KMessageBox::detailedError(nullptr, i18n("Cannot play file."), eofe->error());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MpvPart::openURL(const MRL& mrl) {
|
||||
/* Note: we do not use the internal playlist feature of mpv.
|
||||
* Instead we rely on the playlist feature of Kaffeine.
|
||||
*/
|
||||
|
||||
if (!m_mpv) return false;
|
||||
|
||||
m_mrl = mrl;
|
||||
m_playlist.clear();
|
||||
m_current = 0;
|
||||
|
||||
/* Playlist handling code taken from gstreamer_part.cpp */
|
||||
bool playlist = false;
|
||||
TQString ext = m_mrl.kurl().fileName();
|
||||
ext = ext.remove( 0 , ext.findRev('.')+1 ).lower();
|
||||
|
||||
if ( m_mrl.mime().isNull() ) {
|
||||
KMimeType::Ptr mime = KMimeType::findByURL( m_mrl.kurl().path() );
|
||||
m_mrl.setMime( mime->name() );
|
||||
}
|
||||
|
||||
if ( (m_mrl.mime() == "text/plain") || (m_mrl.mime() == "text/xml") || (m_mrl.mime() == "application/x-kaffeine")
|
||||
|| (m_mrl.mime() == "audio/x-scpls") || (m_mrl.mime() == "audio/x-mpegurl") || (m_mrl.mime() == "audio/mpegurl")
|
||||
|| (ext == "asx") || (ext == "asf") || (ext == "wvx") || (ext == "wax") ) /* windows meta files */
|
||||
{
|
||||
kdDebug() << "libmpvpart: Check for kaffeine/noatun/m3u/pls/asx playlist\n";
|
||||
TQString localFile;
|
||||
if ( TDEIO::NetAccess::download(m_mrl.kurl(), localFile, widget()) ) {
|
||||
TQFile file( localFile );
|
||||
file.open( IO_ReadOnly );
|
||||
TQTextStream stream( &file );
|
||||
TQString firstLine = stream.readLine();
|
||||
TQString secondLine = stream.readLine();
|
||||
file.close();
|
||||
|
||||
if ( secondLine.contains("kaffeine", false) ) {
|
||||
kdDebug() << "libmpvpart: Try loading kaffeine playlist\n";
|
||||
playlist = PlaylistImport::kaffeine( localFile, m_playlist );
|
||||
}
|
||||
if ( secondLine.contains("noatun", false) ) {
|
||||
kdDebug() << "libmpvpart: Try loading noatun playlist\n";
|
||||
playlist = PlaylistImport::noatun( localFile, m_playlist);
|
||||
}
|
||||
if ( firstLine.contains("asx", false) ) {
|
||||
kdDebug() << "libmpvpart: Try loading asx playlist\n";
|
||||
playlist = PlaylistImport::asx( localFile, m_playlist );
|
||||
}
|
||||
if ( firstLine.contains("[playlist]", false) ) {
|
||||
kdDebug() << "libmpvpart: Try loading pls playlist\n";
|
||||
playlist = PlaylistImport::pls( localFile, m_playlist );
|
||||
}
|
||||
if (ext == "m3u" || ext == "m3u8") { //indentify by extension
|
||||
kdDebug() << "libmpvpart: Try loading m3u playlist\n";
|
||||
playlist = PlaylistImport::m3u( localFile, m_playlist );
|
||||
}
|
||||
}
|
||||
else
|
||||
kdError() << "libmpvpart: " << TDEIO::NetAccess::lastErrorString() << endl;
|
||||
}
|
||||
// check for ram playlist
|
||||
if ( (ext == "ra") || (ext == "rm") || (ext == "ram") || (ext == "lsc") || (ext == "pl") ) {
|
||||
kdDebug() << "libmpvpart: Try loading ram playlist\n";
|
||||
playlist = PlaylistImport::ram( m_mrl, m_playlist, widget() );
|
||||
}
|
||||
|
||||
if (!playlist)
|
||||
{
|
||||
kdDebug() << "libmpvpart: Got single track" << endl;
|
||||
m_playlist.append( m_mrl );
|
||||
}
|
||||
|
||||
slotPlay();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MpvPart::closeURL() {
|
||||
if (!m_mpv) return false;
|
||||
|
||||
const char *args[] = {"playlist-remove", "current", nullptr};
|
||||
mpv_command_async(m_mpv, 0, args);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MpvPart::slotPlay() {
|
||||
if (!m_mpv) return;
|
||||
|
||||
if (isPaused()) {
|
||||
int value = 0;
|
||||
mpv_set_property(m_mpv, "pause", MPV_FORMAT_FLAG, &value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_playlist.count() > 0) {
|
||||
emit setStatusBarText( i18n("Opening...") );
|
||||
MRL curMRL = m_playlist[m_current];
|
||||
|
||||
const char *args[] = {"loadfile", curMRL.url().local8Bit(), nullptr};
|
||||
mpv_command_async(m_mpv, 0, args);
|
||||
|
||||
// Add subtitles
|
||||
if ((!curMRL.subtitleFiles().isEmpty()) && (curMRL.currentSubtitle() > -1)) {
|
||||
kdDebug() << "adding subs" << endl;
|
||||
const char *args[] = {"sub-add", curMRL.subtitleFiles()[curMRL.currentSubtitle()].local8Bit(), nullptr};
|
||||
mpv_command_async(m_mpv, 0, args);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
emit signalRequestCurrentTrack();
|
||||
}
|
||||
}
|
||||
|
||||
void MpvPart::slotPause(bool pause) {
|
||||
int value = pause ? 1 : 0;
|
||||
mpv_set_property(m_mpv, "pause", MPV_FORMAT_FLAG, &value);
|
||||
}
|
||||
|
||||
void MpvPart::slotTogglePause() {
|
||||
if (!m_mpv) return;
|
||||
slotPause(!isPaused());
|
||||
}
|
||||
|
||||
bool MpvPart::isPlaying()
|
||||
{
|
||||
if (!m_mpv) return false;
|
||||
|
||||
int result;
|
||||
if (mpv_get_property(m_mpv, "core-idle", MPV_FORMAT_FLAG, &result) < 0) {
|
||||
return false;
|
||||
}
|
||||
return !(bool)result;
|
||||
}
|
||||
|
||||
bool MpvPart::isPaused() {
|
||||
if (!m_mpv) return false;
|
||||
|
||||
int result;
|
||||
if (mpv_get_property(m_mpv, "pause", MPV_FORMAT_FLAG, &result) < 0) {
|
||||
return false;
|
||||
}
|
||||
return (bool)result;
|
||||
}
|
||||
|
||||
void MpvPart::resetTime() {
|
||||
m_playtime->setText("0:00:00");
|
||||
m_position->setValue(0);
|
||||
}
|
||||
|
||||
void MpvPart::slotStartSeeking() {
|
||||
m_seeking = true;
|
||||
slotPause(true);
|
||||
}
|
||||
|
||||
void MpvPart::slotStopSeeking() {
|
||||
slotSetPosition(m_percent);
|
||||
slotPause(false);
|
||||
m_seeking = false;
|
||||
}
|
||||
|
||||
void MpvPart::slotSetSeekingPos(int pos) {
|
||||
m_percent = (uint)pos / 10;
|
||||
}
|
||||
|
||||
uint MpvPart::volume() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint MpvPart::position() const {
|
||||
return m_percent;
|
||||
}
|
||||
|
||||
void MpvPart::slotSetVolume(uint) {
|
||||
|
||||
}
|
||||
|
||||
void MpvPart::slotSetPosition(uint position) {
|
||||
if (!m_mpv) return;
|
||||
|
||||
int64_t value = (int64_t)position;
|
||||
mpv_set_property(m_mpv, "percent-pos", MPV_FORMAT_INT64, &value);
|
||||
}
|
||||
|
||||
void MpvPart::slotPrevious() {
|
||||
if ( m_current > 0 ) {
|
||||
m_current--;
|
||||
slotPlay();
|
||||
}
|
||||
else {
|
||||
emit signalRequestPreviousTrack();
|
||||
}
|
||||
}
|
||||
|
||||
void MpvPart::slotNext() {
|
||||
if ((m_playlist.count() > 0) && (m_current < m_playlist.count()-1)) {
|
||||
m_current++;
|
||||
slotPlay();
|
||||
}
|
||||
else {
|
||||
emit signalRequestNextTrack();
|
||||
}
|
||||
}
|
||||
|
||||
void MpvPart::slotStop() {
|
||||
if (isPlaying()) {
|
||||
const char *args[] = {"stop", nullptr};
|
||||
mpv_command_async(m_mpv, 0, args);
|
||||
}
|
||||
}
|
||||
|
||||
void MpvPart::slotMute() {
|
||||
|
||||
}
|
||||
|
||||
#include "libmpv_part.moc"
|
@ -0,0 +1,10 @@
|
||||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Icon=mpv
|
||||
Name=Kaffeine-MPV
|
||||
Description=A Kaffeine engine based on libmpv
|
||||
MimeType=application/x-ogg;audio/basic;audio/vnd.rn-realaudio;audio/x-aiff;audio/x-mp3;audio/x-mpeg;audio/x-mpegurl;audio/x-ms-wma;audio/x-ogg;audio/x-pn-realaudio;audio/x-pn-realaudio-plugin;audio/x-scpls;audio/x-wav;audio/x-flac;video/x-matroska;audio/x-matroska;video/mpeg;video/msvideo;video/quicktime;video/vnd.rn-realvideo;video/x-avi;video/x-fli;video/x-flic;video/x-ms-asf;video/x-ms-wmv;video/x-msvideo;application/x-mplayer2;application/smil;application/x-kaffeine;audio/x-musepack;
|
||||
X-TDE-ServiceTypes=KParts/ReadOnlyPart,KaffeinePart
|
||||
Type=Service
|
||||
X-TDE-Library=libmpvpart
|
||||
X-TDE-InitialPreference=10
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Kaffeine libmpv part
|
||||
* Copyright (C) 2023 Mavridis Philippe <mavridisf@gmail.com>
|
||||
*
|
||||
* Based on Kaffeine dummy part
|
||||
* Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net>
|
||||
*
|
||||
* 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 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
|
||||
*/
|
||||
|
||||
#ifndef __LIBMPVPART_H__
|
||||
#define __LIBMPVPART_H__
|
||||
|
||||
// TQt
|
||||
#include <tqwidget.h>
|
||||
|
||||
// TDE
|
||||
#include <tdeparts/factory.h>
|
||||
|
||||
// Kaffeine
|
||||
#include "kaffeinepart.h"
|
||||
|
||||
// libmpv
|
||||
#include <mpv/client.h>
|
||||
|
||||
class TQSlider;
|
||||
class TQPushButton;
|
||||
class MpvEventThread;
|
||||
|
||||
/**
|
||||
* MpvPart -- A KaffeinePart which uses libmpv as backend
|
||||
* @author Mavridis Philippe <mavridisf@gmail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
class MpvPart : public KaffeinePart
|
||||
{
|
||||
TQ_OBJECT
|
||||
|
||||
friend class MpvEventThread;
|
||||
|
||||
public:
|
||||
MpvPart(TQWidget*, const char*, TQObject*, const char*, const TQStringList&);
|
||||
virtual ~MpvPart();
|
||||
|
||||
/*
|
||||
* Reimplement from KaffeinePart
|
||||
*/
|
||||
uint volume() const; /* percent */
|
||||
uint position() const; /* percent */
|
||||
bool isPlaying();
|
||||
bool isPaused();
|
||||
|
||||
bool closeURL();
|
||||
static TDEAboutData* createAboutData();
|
||||
|
||||
public slots:
|
||||
/*
|
||||
* Reimplement from KaffeinePart
|
||||
*/
|
||||
bool openURL(const MRL& mrl);
|
||||
void slotPlay();
|
||||
void slotPause(bool pause);
|
||||
void slotTogglePause();
|
||||
void slotSetVolume(uint); /* percent */
|
||||
void slotSetPosition(uint); /* percent */
|
||||
void slotStop();
|
||||
void slotMute();
|
||||
void slotPrevious();
|
||||
void slotNext();
|
||||
|
||||
signals:
|
||||
void mpvEvents();
|
||||
|
||||
protected:
|
||||
mpv_handle *m_mpv;
|
||||
|
||||
private slots:
|
||||
void slotStartSeeking();
|
||||
void slotStopSeeking();
|
||||
void slotSetSeekingPos(int pos);
|
||||
|
||||
private:
|
||||
bool initMpv();
|
||||
void initActions();
|
||||
void resetTime();
|
||||
void customEvent(TQCustomEvent *event);
|
||||
|
||||
private:
|
||||
TQWidget *m_player;
|
||||
MpvEventThread *m_eventThread;
|
||||
TQSlider *m_position;
|
||||
TQPushButton *m_playtime;
|
||||
|
||||
MRL m_mrl;
|
||||
TQValueList<MRL> m_playlist;
|
||||
uint m_current;
|
||||
|
||||
TQTime m_time;
|
||||
uint m_percent;
|
||||
bool m_seeking;
|
||||
};
|
||||
|
||||
#endif /* __LIBMPVPART_H__ */
|
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
|
||||
<kpartgui name="libmpv_part" version="1">
|
||||
<MenuBar>
|
||||
<Menu name="player"><text>&Player</text>
|
||||
<Action name="player_play"/>
|
||||
<Action name="player_pause"/>
|
||||
<Action name="player_stop"/>
|
||||
<Separator/>
|
||||
<Action name="player_previous"/>
|
||||
<Action name="player_next"/>
|
||||
</Menu>
|
||||
</MenuBar>
|
||||
<ToolBar name="controls" position="Bottom"><text>Controls Toolbar</text>
|
||||
<Action name="player_previous"/>
|
||||
<Action name="player_play"/>
|
||||
<Action name="player_pause"/>
|
||||
<Action name="player_stop"/>
|
||||
<Action name="player_next"/>
|
||||
</ToolBar>
|
||||
<ToolBar name="position" fullWidth="true" position="Bottom"><text>Position Toolbar</text>
|
||||
<Action name="player_position"/>
|
||||
<Separator/>
|
||||
<Action name="player_playtime"/>
|
||||
</ToolBar>
|
||||
<StatusBar/>
|
||||
</kpartgui>
|
Loading…
Reference in new issue