Initial commit
This commit is contained in:
commit
2abd02f88f
24
.clang-format
Normal file
24
.clang-format
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: 'false'
|
||||||
|
ConstructorInitializerIndentWidth: 8
|
||||||
|
Cpp11BracedListStyle: 'false'
|
||||||
|
IndentWidth: '4'
|
||||||
|
Language: Cpp
|
||||||
|
PointerAlignment: Right
|
||||||
|
SortIncludes: 'false'
|
||||||
|
SpaceBeforeAssignmentOperators: 'true'
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceInEmptyParentheses: 'false'
|
||||||
|
SpacesInAngles: 'true'
|
||||||
|
SpacesInCStyleCastParentheses: 'true'
|
||||||
|
SpacesInContainerLiterals: 'true'
|
||||||
|
SpacesInParentheses: 'true'
|
||||||
|
SpacesInSquareBrackets: 'false'
|
||||||
|
Standard: Cpp11
|
||||||
|
TabWidth: '4'
|
||||||
|
UseTab: Never
|
||||||
|
|
||||||
|
...
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*.o
|
||||||
|
universalmusiccontroller
|
16
Makefile
Normal file
16
Makefile
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
CC ?= clang
|
||||||
|
CFLAGS ?= -O2 -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include/ -Wall -Wextra -g
|
||||||
|
PREFIX ?= /usr/local/bin
|
||||||
|
LDFLAGS ?= -ldbus-1 -lmpdclient
|
||||||
|
|
||||||
|
.PHONY: default
|
||||||
|
default: universalmusiccontroller
|
||||||
|
|
||||||
|
universalmusiccontroller: main.c dbus_client.o mpd_client.o metadata.o
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^ ${LDFLAGS}
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(CFLAGS) -c $^ -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -Rf *.o universalmusiccontroller
|
215
dbus_client.c
Normal file
215
dbus_client.c
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
#include "dbus_client.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void dbusQuery( DBusMessageIter *args, DBusMessage **msg, DBusConnection *conn,
|
||||||
|
const char *target, const char *object, const char *interface,
|
||||||
|
const char *method, const char **params ) {
|
||||||
|
DBusPendingCall *pending;
|
||||||
|
|
||||||
|
// create a new method call and check for errors
|
||||||
|
*msg = dbus_message_new_method_call( target, object, interface, method );
|
||||||
|
|
||||||
|
if ( *msg == NULL ) {
|
||||||
|
fprintf( stderr, "Message is NULL\n" );
|
||||||
|
exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// append arguments
|
||||||
|
if ( params != NULL ) {
|
||||||
|
dbus_message_iter_init_append( *msg, args );
|
||||||
|
// iterate through params
|
||||||
|
for ( const char **param = params; *param; ++param ) {
|
||||||
|
if ( !dbus_message_iter_append_basic( args, DBUS_TYPE_STRING,
|
||||||
|
param ) ) {
|
||||||
|
fprintf( stderr, "Out Of Memory!\n" );
|
||||||
|
exit( 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send message and get a handle for a reply
|
||||||
|
if ( !dbus_connection_send_with_reply( conn, *msg, &pending, -1 ) ) {
|
||||||
|
fprintf( stderr, "Out Of Memory!\n" );
|
||||||
|
exit( 1 );
|
||||||
|
}
|
||||||
|
if ( pending == NULL ) {
|
||||||
|
fprintf( stderr, "Pending Call is NULL\n" );
|
||||||
|
exit( 1 );
|
||||||
|
}
|
||||||
|
dbus_connection_flush( conn );
|
||||||
|
dbus_message_unref( *msg );
|
||||||
|
|
||||||
|
// block until we recieve a reply
|
||||||
|
dbus_pending_call_block( pending );
|
||||||
|
|
||||||
|
// get the reply message
|
||||||
|
*msg = dbus_pending_call_steal_reply( pending );
|
||||||
|
if ( *msg == NULL ) {
|
||||||
|
fprintf( stderr, "Reply Null\n" );
|
||||||
|
exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
dbus_pending_call_unref( pending );
|
||||||
|
dbus_message_iter_init( *msg, args );
|
||||||
|
}
|
||||||
|
|
||||||
|
DBusConnection *dbusConnect() {
|
||||||
|
DBusError err;
|
||||||
|
|
||||||
|
dbus_error_init( &err );
|
||||||
|
DBusConnection *conn = dbus_bus_get( DBUS_BUS_SESSION, &err );
|
||||||
|
if ( dbus_error_is_set( &err ) ) {
|
||||||
|
fprintf( stderr, "Connection Error (%s)\n", err.message );
|
||||||
|
dbus_error_free( &err );
|
||||||
|
exit( 1 );
|
||||||
|
}
|
||||||
|
if ( NULL == conn ) {
|
||||||
|
exit( 1 );
|
||||||
|
}
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dbusDisconnect( DBusConnection *conn ) {
|
||||||
|
dbus_connection_close( conn );
|
||||||
|
}
|
||||||
|
|
||||||
|
char **dbusGetMediaPlayers( DBusConnection *conn ) {
|
||||||
|
DBusMessageIter args;
|
||||||
|
DBusMessageIter string;
|
||||||
|
DBusMessage *msg = NULL;
|
||||||
|
dbusQuery( &args, &msg, conn, "org.freedesktop.DBus",
|
||||||
|
"/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames",
|
||||||
|
NULL );
|
||||||
|
|
||||||
|
char **ret = malloc( sizeof( char * ) );
|
||||||
|
if ( ret == NULL ) {
|
||||||
|
fprintf( stderr, "Out of memory" );
|
||||||
|
exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
ret[0] = NULL;
|
||||||
|
size_t count = 0;
|
||||||
|
char *path = NULL;
|
||||||
|
do {
|
||||||
|
dbus_message_iter_recurse( &args, &string );
|
||||||
|
do {
|
||||||
|
dbus_message_iter_get_basic( &string, &path );
|
||||||
|
if ( strstr( path, "mpris" ) != NULL ) {
|
||||||
|
count++;
|
||||||
|
char **new = realloc( ret, ( count + 1 ) * sizeof( char * ) );
|
||||||
|
if ( new == NULL ) {
|
||||||
|
fprintf( stderr, "Out of memory" );
|
||||||
|
exit( 1 );
|
||||||
|
}
|
||||||
|
ret = new;
|
||||||
|
ret[count - 1] = strdup( path );
|
||||||
|
if ( ret[count - 1] == NULL ) {
|
||||||
|
fprintf( stderr, "Out of memory" );
|
||||||
|
exit( 1 );
|
||||||
|
}
|
||||||
|
ret[count] = NULL;
|
||||||
|
}
|
||||||
|
} while ( dbus_message_iter_next( &string ) );
|
||||||
|
} while ( dbus_message_iter_next( &args ) );
|
||||||
|
|
||||||
|
dbus_message_unref( msg );
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct song_metadata dbusGetSong( DBusConnection *conn, const char *player ) {
|
||||||
|
struct song_metadata ret = { 0 };
|
||||||
|
DBusMessageIter args;
|
||||||
|
DBusMessageIter array;
|
||||||
|
DBusMessageIter dict;
|
||||||
|
DBusMessageIter dict_entry;
|
||||||
|
DBusMessageIter variant;
|
||||||
|
DBusMessageIter artists;
|
||||||
|
DBusMessage *msg = NULL;
|
||||||
|
const char *params[3] = { "org.mpris.MediaPlayer2.Player", "Metadata",
|
||||||
|
NULL };
|
||||||
|
dbusQuery( &args, &msg, conn, player, "/org/mpris/MediaPlayer2",
|
||||||
|
"org.freedesktop.DBus.Properties", "Get", params );
|
||||||
|
|
||||||
|
char *meta_name = NULL;
|
||||||
|
char *meta_val = NULL;
|
||||||
|
dbus_message_iter_recurse( &args, &array );
|
||||||
|
dbus_message_iter_recurse( &array, &dict );
|
||||||
|
do {
|
||||||
|
dbus_message_iter_recurse( &dict, &dict_entry );
|
||||||
|
dbus_message_iter_get_basic( &dict_entry, &meta_name );
|
||||||
|
|
||||||
|
dbus_message_iter_next( &dict_entry );
|
||||||
|
dbus_message_iter_recurse( &dict_entry, &variant );
|
||||||
|
if ( !strcmp( meta_name, "xesam:artist" ) ) {
|
||||||
|
dbus_message_iter_recurse( &variant, &artists );
|
||||||
|
dbus_message_iter_get_basic( &artists, &meta_val );
|
||||||
|
ret.artist = strdup( meta_val );
|
||||||
|
} else {
|
||||||
|
dbus_message_iter_get_basic( &variant, &meta_val );
|
||||||
|
if ( !strcmp( meta_name, "xesam:title" ) )
|
||||||
|
ret.title = strdup( meta_val );
|
||||||
|
else if ( !strcmp( meta_name, "xesam:album" ) )
|
||||||
|
ret.album = strdup( meta_val );
|
||||||
|
else if ( !strcmp( meta_name, "xesam:url" ) )
|
||||||
|
ret.file = strdup( meta_val );
|
||||||
|
else if ( !strcmp( meta_name, "mpris:artUrl" ) )
|
||||||
|
ret.art_uri = strdup( meta_val );
|
||||||
|
}
|
||||||
|
} while ( dbus_message_iter_next( &dict ) );
|
||||||
|
|
||||||
|
dbus_message_unref( msg );
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dbusPlayPause( DBusConnection *conn, const char *player ) {
|
||||||
|
DBusMessageIter args;
|
||||||
|
DBusMessage *msg = NULL;
|
||||||
|
dbusQuery( &args, &msg, conn, player, "/org/mpris/MediaPlayer2",
|
||||||
|
"org.mpris.MediaPlayer2.Player", "PlayPause", NULL );
|
||||||
|
dbus_message_unref( msg );
|
||||||
|
}
|
||||||
|
|
||||||
|
void dbusNext( DBusConnection *conn, const char *player ) {
|
||||||
|
DBusMessageIter args;
|
||||||
|
DBusMessage *msg = NULL;
|
||||||
|
dbusQuery( &args, &msg, conn, player, "/org/mpris/MediaPlayer2",
|
||||||
|
"org.mpris.MediaPlayer2.Player", "Next", NULL );
|
||||||
|
dbus_message_unref( msg );
|
||||||
|
}
|
||||||
|
|
||||||
|
void dbusPrev( DBusConnection *conn, const char *player ) {
|
||||||
|
DBusMessageIter args;
|
||||||
|
DBusMessage *msg = NULL;
|
||||||
|
dbusQuery( &args, &msg, conn, player, "/org/mpris/MediaPlayer2",
|
||||||
|
"org.mpris.MediaPlayer2.Player", "Previous", NULL );
|
||||||
|
dbus_message_unref( msg );
|
||||||
|
}
|
||||||
|
|
||||||
|
void dbusStop( DBusConnection *conn, const char *player ) {
|
||||||
|
DBusMessageIter args;
|
||||||
|
DBusMessage *msg = NULL;
|
||||||
|
dbusQuery( &args, &msg, conn, player, "/org/mpris/MediaPlayer2",
|
||||||
|
"org.mpris.MediaPlayer2.Player", "Stop", NULL );
|
||||||
|
dbus_message_unref( msg );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dbusStatus( DBusConnection *conn, const char *player ) {
|
||||||
|
DBusMessageIter args;
|
||||||
|
DBusMessageIter string;
|
||||||
|
DBusMessage *msg = NULL;
|
||||||
|
const char *params[3] = { "org.mpris.MediaPlayer2.Player", "PlaybackStatus",
|
||||||
|
NULL };
|
||||||
|
dbusQuery( &args, &msg, conn, player, "/org/mpris/MediaPlayer2",
|
||||||
|
"org.freedesktop.DBus.Properties", "Get", params );
|
||||||
|
|
||||||
|
char *meta = NULL;
|
||||||
|
dbus_message_iter_recurse( &args, &string );
|
||||||
|
dbus_message_iter_get_basic( &string, &meta );
|
||||||
|
bool ret = strcmp( meta, "Playing" ) == 0;
|
||||||
|
// free reply
|
||||||
|
dbus_message_unref( msg );
|
||||||
|
return ret;
|
||||||
|
}
|
18
dbus_client.h
Normal file
18
dbus_client.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef DBUS_PLAYER_H
|
||||||
|
#define DBUS_PLAYER_H
|
||||||
|
#include <dbus/dbus.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "metadata.h"
|
||||||
|
|
||||||
|
DBusConnection *dbusConnect();
|
||||||
|
void dbusDisconnect( DBusConnection *conn );
|
||||||
|
char **dbusGetMediaPlayers( DBusConnection *conn );
|
||||||
|
struct song_metadata dbusGetSong( DBusConnection *conn, const char *player );
|
||||||
|
void dbusPlayPause( DBusConnection *conn, const char *player );
|
||||||
|
void dbusNext( DBusConnection *conn, const char *player );
|
||||||
|
void dbusPrev( DBusConnection *conn, const char *player );
|
||||||
|
void dbusStop( DBusConnection *conn, const char *player );
|
||||||
|
bool dbusStatus( DBusConnection *conn, const char *player );
|
||||||
|
|
||||||
|
#endif
|
39
main.c
Normal file
39
main.c
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "metadata.h"
|
||||||
|
#include "dbus_client.h"
|
||||||
|
#include "mpd_client.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
DBusConnection *conn = dbusConnect();
|
||||||
|
|
||||||
|
printf( "Media players on dbus:\n" );
|
||||||
|
char **dbus_players = dbusGetMediaPlayers( conn );
|
||||||
|
if ( dbus_players != NULL ) {
|
||||||
|
for ( char **player = dbus_players; *player; player++ ) {
|
||||||
|
printf( "%s\n", *player );
|
||||||
|
struct song_metadata song = dbusGetSong( conn, *player );
|
||||||
|
printSong( &song );
|
||||||
|
dbusPlayPause( conn, *player );
|
||||||
|
free( *player );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free( dbus_players );
|
||||||
|
dbusDisconnect( conn );
|
||||||
|
|
||||||
|
struct mpd_connection *mpd_connection = mpdConnect( 6600 );
|
||||||
|
|
||||||
|
if ( mpd_connection != NULL ) {
|
||||||
|
printf( "MPD is running!\n" );
|
||||||
|
struct song_metadata song = mpdGetSong( mpd_connection );
|
||||||
|
printSong( &song );
|
||||||
|
mpdPlayPause( mpd_connection );
|
||||||
|
}
|
||||||
|
|
||||||
|
mpdDisconnect( mpd_connection );
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
18
metadata.c
Normal file
18
metadata.c
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#include "metadata.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void printSong( struct song_metadata *song ) {
|
||||||
|
if ( song->title == NULL )
|
||||||
|
return;
|
||||||
|
printf( " TITLE: %s\n ALBUM: %s\n ARTIST: %s\n", song->title,
|
||||||
|
song->album, song->artist );
|
||||||
|
if ( song->year != NULL ) {
|
||||||
|
printf( " YEAR: %s\n", song->year );
|
||||||
|
}
|
||||||
|
printf( " FILE: %s\n", song->file );
|
||||||
|
if ( song->art_uri != NULL ) {
|
||||||
|
printf( " ART: %s\n", song->art_uri );
|
||||||
|
}
|
||||||
|
}
|
15
metadata.h
Normal file
15
metadata.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef METADATA_H
|
||||||
|
#define METADATA_H
|
||||||
|
|
||||||
|
struct song_metadata {
|
||||||
|
const char *title;
|
||||||
|
const char *artist;
|
||||||
|
const char *album;
|
||||||
|
const char *year;
|
||||||
|
const char *file;
|
||||||
|
const char *art_uri;
|
||||||
|
};
|
||||||
|
|
||||||
|
void printSong( struct song_metadata *song );
|
||||||
|
|
||||||
|
#endif
|
73
mpd_client.c
Normal file
73
mpd_client.c
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#include "mpd_client.h"
|
||||||
|
|
||||||
|
#include <mpd/player.h>
|
||||||
|
#include <mpd/song.h>
|
||||||
|
#include <mpd/status.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "metadata.h"
|
||||||
|
|
||||||
|
struct mpd_connection *mpdConnect( int port ) {
|
||||||
|
struct mpd_connection *ret = mpd_connection_new( "localhost", port, 1000 );
|
||||||
|
if ( ret == NULL )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if ( mpd_connection_get_error( ret ) != MPD_ERROR_SUCCESS )
|
||||||
|
return NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mpdDisconnect( struct mpd_connection *mpd_connection ) {
|
||||||
|
if ( mpd_connection != NULL )
|
||||||
|
mpd_connection_free( mpd_connection );
|
||||||
|
}
|
||||||
|
|
||||||
|
struct song_metadata mpdGetSong( struct mpd_connection *mpd_connection ) {
|
||||||
|
struct song_metadata ret = { 0 };
|
||||||
|
struct mpd_song *song = mpd_run_current_song( mpd_connection );
|
||||||
|
if ( song == NULL )
|
||||||
|
goto end;
|
||||||
|
const char *copy = mpd_song_get_tag( song, MPD_TAG_TITLE, 0 );
|
||||||
|
if ( copy == NULL )
|
||||||
|
goto endfree;
|
||||||
|
// TODO check return values
|
||||||
|
ret.title = strdup( copy );
|
||||||
|
copy = mpd_song_get_tag( song, MPD_TAG_ARTIST, 0 );
|
||||||
|
if( copy != NULL )
|
||||||
|
ret.artist = strdup( copy );
|
||||||
|
copy = mpd_song_get_tag( song, MPD_TAG_ALBUM, 0 );
|
||||||
|
if( copy != NULL )
|
||||||
|
ret.album = strdup( copy );
|
||||||
|
copy = mpd_song_get_tag( song, MPD_TAG_DATE, 0 );
|
||||||
|
if( copy != NULL )
|
||||||
|
ret.year = strdup( copy );
|
||||||
|
|
||||||
|
ret.file = strdup( mpd_song_get_uri( song ) );
|
||||||
|
endfree:
|
||||||
|
mpd_song_free( song );
|
||||||
|
end:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mpdPlayPause( struct mpd_connection *mpd_connection ) {
|
||||||
|
mpd_send_toggle_pause( mpd_connection );
|
||||||
|
}
|
||||||
|
|
||||||
|
void mpdNext( struct mpd_connection *mpd_connection ) {
|
||||||
|
mpd_send_next( mpd_connection );
|
||||||
|
}
|
||||||
|
|
||||||
|
void mpdPrev( struct mpd_connection *mpd_connection ) {
|
||||||
|
mpd_send_previous( mpd_connection );
|
||||||
|
}
|
||||||
|
|
||||||
|
void mpdStop( struct mpd_connection *mpd_connection ) {
|
||||||
|
mpd_send_stop( mpd_connection );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mpdStatus( struct mpd_connection *mpd_connection ) {
|
||||||
|
struct mpd_status *status = mpd_recv_status( mpd_connection );
|
||||||
|
enum mpd_state ret = mpd_status_get_state( status );
|
||||||
|
mpd_status_free( status );
|
||||||
|
return ret == MPD_STATE_PLAY;
|
||||||
|
}
|
13
mpd_client.h
Normal file
13
mpd_client.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include <mpd/connection.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "metadata.h"
|
||||||
|
|
||||||
|
struct mpd_connection *mpdConnect( int port );
|
||||||
|
void mpdDisconnect( struct mpd_connection *mpd_connection );
|
||||||
|
struct song_metadata mpdGetSong( struct mpd_connection *mpd_connection );
|
||||||
|
void mpdPlayPause( struct mpd_connection *mpd_connection );
|
||||||
|
void mpdNext( struct mpd_connection *mpd_connection );
|
||||||
|
void mpdPrev( struct mpd_connection *mpd_connection );
|
||||||
|
void mpdStop( struct mpd_connection *mpd_connection );
|
||||||
|
bool mpdStatus( struct mpd_connection *mpd_connection );
|
Loading…
Reference in New Issue
Block a user