Initial commit

This commit is contained in:
zvon 2020-05-29 14:21:05 +02:00
commit 2abd02f88f
10 changed files with 433 additions and 0 deletions

24
.clang-format Normal file
View 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
View File

@ -0,0 +1,2 @@
*.o
universalmusiccontroller

16
Makefile Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 );