]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Implement a rudimentary cover art file writing facility.
authorMike Brady <mikebrady@eircom.net>
Tue, 16 Jan 2018 22:22:03 +0000 (22:22 +0000)
committerMike Brady <mikebrady@eircom.net>
Tue, 16 Jan 2018 22:22:03 +0000 (22:22 +0000)
common.c
common.h
metadata_hub.c
shairport.c

index e8cc16ac86ceb4f752b211dd9e77a48a20ec7b58..29cded7f401f3b0bd95610b075a4028651ed5304 100644 (file)
--- a/common.c
+++ b/common.c
@@ -38,6 +38,8 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <sys/stat.h>
+
 #include "common.h"
 #include <assert.h>
 
@@ -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
index 7bdc736cc122562e8305223046e329f4623efcba..fc51da20ac0db6a6653d0395aa508efa125b873d 100644 (file)
--- 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);
 
index 8aa7f6cd29e87b97fa0e7a7e5d80c621283ebe16..174aca43033965b859cc381ee404a67d572ffd4e 100644 (file)
@@ -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
 #include <stdlib.h>
 #include <string.h>
 
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "config.h"
+
+#include "common.h"
 #include "dacp.h"
 #include "metadata_hub.h"
 
+#ifdef HAVE_LIBMBEDTLS
+#include <mbedtls/md5.h>
+#endif
+
+#ifdef HAVE_LIBPOLARSSL
+#include <polarssl/md5.h>
+#endif
+
+#ifdef HAVE_LIBSSL
+#include <openssl/md5.h>
+#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;
index fbeda85f33fa7b2034a380ed10ab9741b4e9251a..e4b58f6f958b9c5800869621d8e21a663cfec5e3 100644 (file)
@@ -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;