From: Mike Brady Date: Tue, 16 Jan 2018 22:22:03 +0000 (+0000) Subject: Implement a rudimentary cover art file writing facility. X-Git-Tag: 3.2d29~115 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4aaa8d19fe27b5de4fb4036a1ce61d2477e9913f;p=thirdparty%2Fshairport-sync.git Implement a rudimentary cover art file writing facility. --- diff --git a/common.c b/common.c index e8cc16ac..29cded7f 100644 --- a/common.c +++ b/common.c @@ -38,6 +38,8 @@ #include #include +#include + #include "common.h" #include @@ -158,6 +160,54 @@ char *base64_enc(uint8_t *input, int length) { return buf; } +// The following two functions are adapted slightly and with thanks from Jonathan Leffler's sample +// code at +// https://stackoverflow.com/questions/675039/how-can-i-create-directory-tree-in-c-linux + +int do_mkdir(const char *path, mode_t mode) { + struct stat st; + int status = 0; + + if (stat(path, &st) != 0) { + /* Directory does not exist. EEXIST for race condition */ + if (mkdir(path, mode) != 0 && errno != EEXIST) + status = -1; + } else if (!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + status = -1; + } + + return (status); +} + +// mkpath - ensure all directories in path exist +// Algorithm takes the pessimistic view and works top-down to ensure +// each directory in path exists, rather than optimistically creating +// the last element and working backwards. + +int mkpath(const char *path, mode_t mode) { + char *pp; + char *sp; + int status; + char *copypath = strdup(path); + + status = 0; + pp = copypath; + while (status == 0 && (sp = strchr(pp, '/')) != 0) { + if (sp != pp) { + /* Neither root nor double slash in path */ + *sp = '\0'; + status = do_mkdir(copypath, mode); + *sp = '/'; + } + pp = sp + 1; + } + if (status == 0) + status = do_mkdir(path, mode); + free(copypath); + return (status); +} + uint8_t *base64_dec(char *input, int *outlen) { // slight problem here is that Apple cut the padding off their challenges. We must restore it // before passing it in to the decoder, it seems diff --git a/common.h b/common.h index 7bdc736c..fc51da20 100644 --- a/common.h +++ b/common.h @@ -129,7 +129,8 @@ typedef struct { int daemonise_store_pid; // don't try to save a PID file char *piddir; char *computed_piddir; // the actual pid directory to create, if any - int logOutputLevel; // log output level + + int logOutputLevel; // log output level int statistics_requested, use_negotiated_latencies; enum playback_mode_type playback_mode; char *cmd_start, *cmd_stop, *cmd_set_volume; @@ -174,6 +175,10 @@ typedef struct { enum dbus_session_type mpris_service_bus_type; #endif +#ifdef HAVE_METADATA_HUB + char *cover_art_cache_dir; +#endif + } shairport_cfg; // true if Shairport Sync is supposed to be sending output to the output device, false otherwise @@ -232,6 +237,8 @@ void command_start(void); void command_stop(void); void command_set_volume(double volume); +int mkpath(const char *path, mode_t mode); + void shairport_shutdown(); // void shairport_startup_complete(void); diff --git a/metadata_hub.c b/metadata_hub.c index 8aa7f6cd..174aca43 100644 --- a/metadata_hub.c +++ b/metadata_hub.c @@ -5,7 +5,7 @@ * then you need a metadata hub, * where everything is stored * This file is part of Shairport Sync. - * Copyright (c) Mike Brady 2017 + * Copyright (c) Mike Brady 2017--2018 * All rights reserved. * * Permission is hereby granted, free of charge, to any person @@ -32,9 +32,30 @@ #include #include +#include +#include +#include +#include +#include + +#include "config.h" + +#include "common.h" #include "dacp.h" #include "metadata_hub.h" +#ifdef HAVE_LIBMBEDTLS +#include +#endif + +#ifdef HAVE_LIBPOLARSSL +#include +#endif + +#ifdef HAVE_LIBSSL +#include +#endif + void metadata_hub_init(void) { debug(1, "Metadata bundle initialisation."); memset(&metadata_store, 0, sizeof(metadata_store)); @@ -61,6 +82,83 @@ void run_metadata_watchers(void) { } } +void metadata_write_image_file(const char *buf, int len) { + + uint8_t img_md5[16]; +// uint8_t ap_md5[16]; + +#ifdef HAVE_LIBSSL + MD5_CTX ctx; + MD5_Init(&ctx); + MD5_Update(&ctx, buf, len); + MD5_Final(img_md5, &ctx); +#endif + +#ifdef HAVE_LIBMBEDTLS + mbedtls_md5_context tctx; + mbedtls_md5_starts(&tctx); + mbedtls_md5_update(&tctx, buf, len); + mbedtls_md5_finish(&tctx, img_md5); +#endif + +#ifdef HAVE_LIBPOLARSSL + md5_context tctx; + md5_starts(&tctx); + md5_update(&tctx, buf, len); + md5_finish(&tctx, img_md5); +#endif + + char img_md5_str[33]; + memset(img_md5_str, 0, sizeof(img_md5_str)); + char *ext; + char png[] = "png"; + char jpg[] = "jpg"; + int i; + for (i = 0; i < 16; i++) + sprintf(&img_md5_str[i * 2], "%02x", (uint8_t)img_md5[i]); + // see if the file is a jpeg or a png + if (strncmp(buf, "\xFF\xD8\xFF", 3) == 0) + ext = jpg; + else if (strncmp(buf, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) == 0) + ext = png; + else { + debug(1, "Unidentified image type of cover art -- jpg extension used."); + ext = jpg; + } + + int result = mkpath(config.cover_art_cache_dir, 0700); + if ((result == 0) || (result == -EEXIST)) { + debug(1, "Cover art cache directory okay"); + // see if the file exists by opening it. + // if it exists, we're done + char *prefix = "cover-"; + + size_t pl = strlen(config.cover_art_cache_dir) + 1 + strlen(prefix) + strlen(img_md5_str) + 1 + + strlen(ext); + + char *path = malloc(pl + 1); + snprintf(path, pl + 1, "%s/%s%s.%s", config.cover_art_cache_dir, prefix, img_md5_str, ext); + int cover_fd = open(path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + if (cover_fd > 0) { + // write the contents + if (write(cover_fd, buf, len) < len) { + warn("Writing cover art file \"%s\" failed!", path); + } + close(cover_fd); + free(path); + } else { + if (errno == EEXIST) + debug(1, "Cover art file \"%s\" already exists!", path); + else + warn("Could not open file \"%s\" for writing cover art", path); + free(path); + } + } else { + debug(1, "Couldn't access or create the cover art cache directory \"%s\".", + config.cover_art_cache_dir); + } +} + void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t length) { // metadata coming in from the audio source or from Shairport Sync itself passes through here // this has more information about tags, which might be relevant: @@ -89,8 +187,7 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin } break; case 'ascm': - if ((metadata_store.comment == NULL) || - (strncmp(metadata_store.comment, data, length) != 0)) { + if ((metadata_store.comment == NULL) || (strncmp(metadata_store.comment, data, length) != 0)) { if (metadata_store.comment) free(metadata_store.comment); metadata_store.comment = strndup(data, length); @@ -100,8 +197,7 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin } break; case 'asgn': - if ((metadata_store.genre == NULL) || - (strncmp(metadata_store.genre, data, length) != 0)) { + if ((metadata_store.genre == NULL) || (strncmp(metadata_store.genre, data, length) != 0)) { if (metadata_store.genre) free(metadata_store.genre); metadata_store.genre = strndup(data, length); @@ -155,8 +251,7 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin } break; case 'assn': - if ((metadata_store.sort_as == NULL) || - (strncmp(metadata_store.sort_as, data, length) != 0)) { + if ((metadata_store.sort_as == NULL) || (strncmp(metadata_store.sort_as, data, length) != 0)) { if (metadata_store.sort_as) free(metadata_store.sort_as); metadata_store.sort_as = strndup(data, length); @@ -167,6 +262,8 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin break; case 'PICT': debug(1, "MH Picture received, length %u bytes.", length); + if (length > 16) + metadata_write_image_file(data, length); break; case 'clip': if ((metadata_store.client_ip == NULL) || @@ -179,10 +276,9 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin metadata_store.changed = 1; } break; - + default: - if (type == 'ssnc') - { + if (type == 'ssnc') { char typestring[5]; *(uint32_t *)typestring = htonl(type); typestring[4] = 0; diff --git a/shairport.c b/shairport.c index fbeda85f..e4b58f6f 100644 --- a/shairport.c +++ b/shairport.c @@ -132,54 +132,6 @@ static void sig_connect_audio_output(int foo, siginfo_t *bar, void *baz) { set_requested_connection_state_to_output(1); } -// The following two functions are adapted slightly and with thanks from Jonathan Leffler's sample -// code at -// https://stackoverflow.com/questions/675039/how-can-i-create-directory-tree-in-c-linux - -int do_mkdir(const char *path, mode_t mode) { - struct stat st; - int status = 0; - - if (stat(path, &st) != 0) { - /* Directory does not exist. EEXIST for race condition */ - if (mkdir(path, mode) != 0 && errno != EEXIST) - status = -1; - } else if (!S_ISDIR(st.st_mode)) { - errno = ENOTDIR; - status = -1; - } - - return (status); -} - -// mkpath - ensure all directories in path exist -// Algorithm takes the pessimistic view and works top-down to ensure -// each directory in path exists, rather than optimistically creating -// the last element and working backwards. - -int mkpath(const char *path, mode_t mode) { - char *pp; - char *sp; - int status; - char *copypath = strdup(path); - - status = 0; - pp = copypath; - while (status == 0 && (sp = strchr(pp, '/')) != 0) { - if (sp != pp) { - /* Neither root nor double slash in path */ - *sp = '\0'; - status = do_mkdir(copypath, mode); - *sp = '/'; - } - pp = sp + 1; - } - if (status == 0) - status = do_mkdir(path, mode); - free(copypath); - return (status); -} - char *get_version_string() { char *version_string = malloc(200); if (version_string) { @@ -438,6 +390,7 @@ int parse_options(int argc, char **argv) { config.airplay_volume = -18.0; // if no volume is ever set, default to initial default value if // nothing else comes in first. config.fixedLatencyOffset = 11025; // this sounds like it works properly. + config.cover_art_cache_dir = "/tmp/shairport-sync/.cache/coverart"; config_setting_t *setting; const char *str = 0;