Implement a rudimentary cover art file writing facility.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
+106
-10
@@ -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 <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;
|
||||
|
||||
+1
-48
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user