commit 2abd02f88fc996c8b4a804c8bc1d7eda38c9586e Author: zvon Date: Fri May 29 14:21:05 2020 +0200 Initial commit diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..8dad6e1 --- /dev/null +++ b/.clang-format @@ -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 + +... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a86796 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +universalmusiccontroller diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9cf0ff4 --- /dev/null +++ b/Makefile @@ -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 diff --git a/dbus_client.c b/dbus_client.c new file mode 100644 index 0000000..9a4b72e --- /dev/null +++ b/dbus_client.c @@ -0,0 +1,215 @@ +#include "dbus_client.h" + +#include +#include +#include + +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; +} diff --git a/dbus_client.h b/dbus_client.h new file mode 100644 index 0000000..10d0995 --- /dev/null +++ b/dbus_client.h @@ -0,0 +1,18 @@ +#ifndef DBUS_PLAYER_H +#define DBUS_PLAYER_H +#include +#include + +#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 diff --git a/main.c b/main.c new file mode 100644 index 0000000..eba2fc0 --- /dev/null +++ b/main.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +#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; +} diff --git a/metadata.c b/metadata.c new file mode 100644 index 0000000..45b87c1 --- /dev/null +++ b/metadata.c @@ -0,0 +1,18 @@ +#include "metadata.h" + +#include +#include + +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 ); + } +} diff --git a/metadata.h b/metadata.h new file mode 100644 index 0000000..7a931f7 --- /dev/null +++ b/metadata.h @@ -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 diff --git a/mpd_client.c b/mpd_client.c new file mode 100644 index 0000000..703bc45 --- /dev/null +++ b/mpd_client.c @@ -0,0 +1,73 @@ +#include "mpd_client.h" + +#include +#include +#include +#include + +#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; +} diff --git a/mpd_client.h b/mpd_client.h new file mode 100644 index 0000000..0c50622 --- /dev/null +++ b/mpd_client.h @@ -0,0 +1,13 @@ +#include +#include + +#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 );