Sonic ID3 mode is now working properly, but Sonic index mode stopped working

This commit is contained in:
Fufu Fang 2019-10-27 21:21:30 +00:00
parent 4b2ac94fe1
commit ff1d34855c
No known key found for this signature in database
GPG Key ID: 0F6BB5EF6F8BB729
7 changed files with 288 additions and 109 deletions

View File

@ -16,6 +16,8 @@ Jerome Charaoui
### Fixed
- Remove the erroneous error messages when the user supplies wrong command line
options.
- The same cache folder is used, irrespective whether the server root URL ends
with '/'
## [1.1.10] - 2019-09-10
### Added

View File

@ -585,7 +585,7 @@ void Cache_delete(const char *fn)
{
if (CONFIG.sonic_mode) {
Link *link = path_to_Link(fn);
fn = link->sonic_id_str;
fn = link->sonic_song_id_str;
}
char *metafn = path_append(META_DIR, fn);
@ -678,7 +678,7 @@ int Cache_create(const char *path)
fn = curl_easy_unescape(NULL, this_link->f_url + ROOT_LINK_OFFSET, 0,
NULL);
} else {
fn = this_link->sonic_id_str;
fn = this_link->sonic_song_id_str;
}
fprintf(stderr, "Cache_create(): Creating cache files for %s.\n", fn);
@ -774,7 +774,7 @@ Cache *Cache_open(const char *fn)
return NULL;
}
} else {
if (!Cache_exist(link->sonic_id_str)) {
if (!Cache_exist(link->sonic_song_id_str)) {
return NULL;
}
}
@ -788,7 +788,7 @@ Cache *Cache_open(const char *fn)
/* Set the path for the local cache file, if we are in sonic mode */
if (CONFIG.sonic_mode) {
fn = link->sonic_id_str;
fn = link->sonic_song_id_str;
}
cf->path = strndup(fn, MAX_PATH_LEN);

View File

@ -6,6 +6,7 @@
#include <gumbo.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
@ -27,8 +28,15 @@ int ROOT_LINK_OFFSET = 0;
*/
static pthread_mutex_t link_lock;
LinkTable *LinkSystem_init(const char *url)
LinkTable *LinkSystem_init(const char *raw_url)
{
/* Remove excess '/' if it is there */
char *url = strdup(raw_url);
int url_len = strnlen(url, MAX_PATH_LEN) - 1;
if (url[url_len] == '/') {
url[url_len] = '\0';
}
if (pthread_mutex_init(&link_lock, NULL) != 0) {
fprintf(stderr,
"link_system_init(): link_lock initialisation failed!\n");
@ -37,17 +45,7 @@ LinkTable *LinkSystem_init(const char *url)
/* --------- Set the length of the root link ----------- */
/* This is where the '/' should be */
ROOT_LINK_OFFSET = strnlen(url, MAX_PATH_LEN) - 1;
if (url[ROOT_LINK_OFFSET] != '/') {
/*
* If '/' is not there, it is automatically added, so we need to skip 2
* characters
*/
ROOT_LINK_OFFSET += 2;
} else {
/* If '/' is there, we need to skip it */
ROOT_LINK_OFFSET += 1;
}
ROOT_LINK_OFFSET = strnlen(url, MAX_PATH_LEN) + 1;
/* --------------------- Enable cache system -------------------- /
*
@ -68,9 +66,9 @@ LinkTable *LinkSystem_init(const char *url)
} else {
sonic_config_init(url, CONFIG.sonic_username, CONFIG.sonic_password);
if (!CONFIG.sonic_id3) {
ROOT_LINK_TBL = sonic_LinkTable_new_index_mode(0);
ROOT_LINK_TBL = sonic_LinkTable_new_index(0);
} else {
ROOT_LINK_TBL = sonic_LinkTable_new_id3_mode("/");
ROOT_LINK_TBL = sonic_LinkTable_new_id3(0, 0);
}
}
return ROOT_LINK_TBL;
@ -404,6 +402,7 @@ LinkTable *LinkTable_alloc(const char *url)
Link *head_link = Link_new("/", LINK_HEAD);
LinkTable_add(linktbl, head_link);
strncpy(head_link->f_url, url, MAX_PATH_LEN);
assert(linktbl->num == 1);
return linktbl;
}
@ -607,9 +606,10 @@ LinkTable *path_to_Link_LinkTable_new(const char *path)
next_table = LinkTable_new(link->f_url);
} else {
if (!CONFIG.sonic_id3) {
next_table = sonic_LinkTable_new_index_mode(link->sonic_id);
next_table = sonic_LinkTable_new_index(link->sonic_id);
} else {
next_table = sonic_LinkTable_new_id3_mode(link->sonic_id_str);
next_table = sonic_LinkTable_new_id3(link->sonic_depth,
link->sonic_id);
}
}
}
@ -662,11 +662,12 @@ static Link *path_to_Link_recursive(char *path, LinkTable *linktbl)
linktbl->links[i]->f_url);
} else {
if (!CONFIG.sonic_id3) {
next_table = sonic_LinkTable_new_index_mode(
next_table = sonic_LinkTable_new_index(
linktbl->links[i]->sonic_id);
} else {
next_table = sonic_LinkTable_new_id3_mode(
linktbl->links[i]->sonic_id_str);
next_table = sonic_LinkTable_new_id3(
linktbl->links[i]->sonic_depth,
linktbl->links[i]->sonic_id);
}
}
}

View File

@ -69,14 +69,25 @@ struct Link {
/** \brief The pointer associated with the cache file */
Cache *cache_ptr;
/**
* \brief Sonic Music Directory ID
* \details We use linkname to store filename
* \brief Sonic id field
* \details This is used to store the followings:
* - Arist ID
* - Album ID
* - Song ID
* - Sub-directory ID (in the XML response, this is the ID on the "child"
* element)
*/
int sonic_id;
/**
* \brief Sonic Music Directory ID in string format
* \brief Sonic directory depth
* \details This is used exclusively in ID3 mode to store the depth of the
* current directory.
*/
char *sonic_id_str;
int sonic_depth;
/**
* \brief The sonic song's ID in character array format.
*/
char *sonic_song_id_str;
};
struct LinkTable {
@ -97,7 +108,7 @@ extern int ROOT_LINK_OFFSET;
/**
* \brief initialise link sub-system.
*/
LinkTable *LinkSystem_init(const char *url);
LinkTable *LinkSystem_init(const char *raw_url);
/**
* \brief Add a link to the curl multi bundle for querying stats

View File

@ -151,7 +151,7 @@ parse_arg_list(int argc, char **argv, char ***fuse_argv, int *fuse_argc)
{"cache-location", required_argument, NULL, 'L'}, /* 14 */
{"sonic-username", required_argument, NULL, 'L'}, /* 15 */
{"sonic-password", required_argument, NULL, 'L'}, /* 16 */
{"sonic-password", no_argument, NULL, 'L'}, /* 17 */
{"sonic-id3", no_argument, NULL, 'L'}, /* 17 */
{0, 0, 0, 0}
};
while ((c =

View File

@ -6,6 +6,7 @@
#include <expat.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
@ -90,6 +91,30 @@ static char *sonic_getMusicDirectory_link(const int id)
return url;
}
/**
* \brief generate a getArtist request URL
*/
static char *sonic_getArtist_link(const int id)
{
char *first_part = sonic_gen_url_first_part("getArtist");
char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char));
snprintf(url, MAX_PATH_LEN, "%s&id=%d", first_part, id);
free(first_part);
return url;
}
/**
* \brief generate a getAlbum request URL
*/
static char *sonic_getAlbum_link(const int id)
{
char *first_part = sonic_gen_url_first_part("getAlbum");
char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char));
snprintf(url, MAX_PATH_LEN, "%s&id=%d", first_part, id);
free(first_part);
return url;
}
/**
* \brief generate a download request URL
*/
@ -98,7 +123,7 @@ static char *sonic_stream_link(const int id)
char *first_part = sonic_gen_url_first_part("stream");
char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char));
snprintf(url, MAX_PATH_LEN,
"%s&estimateContentLength=true&format=raw&id=%d", first_part, id);
"%s&format=raw&id=%d", first_part, id);
free(first_part);
return url;
}
@ -117,19 +142,15 @@ static char *sonic_stream_link(const int id)
* parser terminates the strings properly, which is a fair assumption,
* considering how mature expat is.
*/
static void XMLCALL XML_parser_index_mode(void *data, const char *elem,
static void XMLCALL XML_parser_index(void *data, const char *elem,
const char **attr)
{
LinkTable *linktbl = (LinkTable *) data;
Link *link;
if (!strcmp(elem, "child")) {
/* Return from getMusicDirectory */
if (!strcmp(elem, "child") || !strcmp(elem, "artist")) {
link = CALLOC(1, sizeof(Link));
/* Initialise to LINK_INVALID, as the link->type is set by isDir */
link->type = LINK_INVALID;
} else if (!strcmp(elem, "artist")){
/* Return from getIndexes */
link = CALLOC(1, sizeof(Link));
link->type = LINK_DIR;
} else {
/* The element does not contain directory structural information */
return;
@ -141,8 +162,8 @@ static void XMLCALL XML_parser_index_mode(void *data, const char *elem,
for (int i = 0; attr[i]; i += 2) {
if (!strcmp("id", attr[i])) {
link->sonic_id = atoi(attr[i+1]);
link->sonic_id_str = calloc(MAX_FILENAME_LEN, sizeof(char));
snprintf(link->sonic_id_str, MAX_FILENAME_LEN, "%d",
link->sonic_song_id_str = calloc(MAX_FILENAME_LEN, sizeof(char));
snprintf(link->sonic_song_id_str, MAX_FILENAME_LEN, "%d",
link->sonic_id);
id_set = 1;
continue;
@ -180,6 +201,9 @@ static void XMLCALL XML_parser_index_mode(void *data, const char *elem,
link->type = LINK_DIR;
} else if (!strcmp("false", attr[i+1])) {
link->type = LINK_FILE;
char *url = sonic_stream_link(link->sonic_id);
strncpy(link->f_url, url, MAX_PATH_LEN);
free(url);
}
continue;
}
@ -199,31 +223,13 @@ static void XMLCALL XML_parser_index_mode(void *data, const char *elem,
}
/* Clean up if linkname is not set */
if (!linkname_set) {
if (!linkname_set || !id_set) {
free(link);
return;
}
/* Clean up if id is not set */
if (!id_set) {
if (linkname_set) {
free(link->linkname);
}
free(link);
return;
}
if (link->type == LINK_DIR) {
char *url = sonic_getMusicDirectory_link(link->sonic_id);
strncpy(link->f_url, url, MAX_PATH_LEN);
free(url);
} else if (link->type == LINK_FILE) {
char *url = sonic_stream_link(link->sonic_id);
strncpy(link->f_url, url, MAX_PATH_LEN);
free(url);
} else {
if (link->type == LINK_INVALID) {
/* Invalid link */
free(link->linkname);
free(link);
return;
}
@ -231,23 +237,15 @@ static void XMLCALL XML_parser_index_mode(void *data, const char *elem,
LinkTable_add(linktbl, link);
}
/**
* \brief The parser for Sonic ID3 mode
* \details Please refer to the details for XML_parser_index_mode()
*/
static void XMLCALL XML_parser_id3_mode(void *data, const char *elem,
const char **attr)
{
LinkTable *linktbl = (LinkTable *) data;
}
/**
* \brief parse a XML string in order to fill in the LinkTable
*/
static LinkTable *sonic_url_to_LinkTable(const char *url,
XML_StartElementHandler handler)
XML_StartElementHandler handler,
int depth)
{
LinkTable *linktbl = LinkTable_alloc(url);
linktbl->links[0]->sonic_depth = depth;
/* start downloading the base URL */
DataStruct xml = Link_to_DataStruct(linktbl->links[0]);
@ -278,7 +276,7 @@ static LinkTable *sonic_url_to_LinkTable(const char *url,
}
LinkTable *sonic_LinkTable_new_index_mode(const int id)
LinkTable *sonic_LinkTable_new_index(const int id)
{
char *url;
if (id > 0) {
@ -286,47 +284,211 @@ LinkTable *sonic_LinkTable_new_index_mode(const int id)
} else {
url = sonic_gen_url_first_part("getIndexes");
}
LinkTable *linktbl = sonic_url_to_LinkTable(url, XML_parser_index_mode);
LinkTable *linktbl = sonic_url_to_LinkTable(url, XML_parser_index, 0);
free(url);
return linktbl;
}
LinkTable *sonic_LinkTable_new_id3_root()
static void XMLCALL XML_parser_id3_root(void *data, const char *elem,
const char **attr)
{
char *url = sonic_gen_url_first_part("getArtists");
LinkTable *linktbl = sonic_url_to_LinkTable(url, XML_parser_index_mode);
free(url);
return linktbl;
LinkTable *root_linktbl = (LinkTable *) data;
LinkTable *this_linktbl = NULL;
/* Set the current linktbl, if we have more than head link. */
if (root_linktbl->num > 1) {
this_linktbl = root_linktbl->links[root_linktbl->num - 1]->next_table;
}
int id_set = 0;
int linkname_set = 0;
Link *link;
if (!strcmp(elem, "index")) {
/* Add a subdirectory */
link = CALLOC(1, sizeof(Link));
link->type = LINK_DIR;
for (int i = 0; attr[i]; i += 2) {
if (!strcmp("name", attr[i])) {
strncpy(link->linkname, attr[i+1], MAX_FILENAME_LEN);
linkname_set = 1;
/* Allocate a new LinkTable */
link->next_table = LinkTable_alloc("/");
}
}
/* Make sure we don't add an empty directory */
if (linkname_set) {
LinkTable_add(root_linktbl, link);
} else {
free(link);
}
return;
} else if (!strcmp(elem, "artist")) {
link = CALLOC(1, sizeof(Link));
link->type = LINK_DIR;
/* This table should be a level 3 album table */
link->sonic_depth = 3;
for (int i = 0; attr[i]; i += 2) {
if (!strcmp("name", attr[i])) {
strncpy(link->linkname, attr[i+1], MAX_FILENAME_LEN);
linkname_set = 1;
continue;
}
if (!strcmp("id", attr[i])) {
link->sonic_id = atoi(attr[i+1]);
id_set = 1;
continue;
}
}
/* Clean up if linkname is not set */
if (!linkname_set || !id_set) {
free(link);
return;
}
LinkTable_add(this_linktbl, link);
}
/* If we reach here, this element does not contain directory structural
* information */
}
LinkTable *sonic_LinkTable_new_id3_mode(char *sonic_id_str)
static void XMLCALL XML_parser_id3(void *data, const char *elem,
const char **attr)
{
/* Count the number of '/' */
int n = 0;
for (char *c = sonic_id_str; *c; c++) {
if (*c == '/') {
n++;
LinkTable *linktbl = (LinkTable *) data;
Link *link;
/*
* Please refer to the documentation at the function prototype of
* sonic_LinkTable_new_id3()
*/
if (!strcmp(elem, "album") && linktbl->links[0]->sonic_depth == 3) {
link = CALLOC(1, sizeof(Link));
link->type = LINK_DIR;
/* This table should be a level 3 album table */
link->sonic_depth = 4;
} else if (!strcmp(elem, "song") && linktbl->links[0]->sonic_depth == 4) {
link = CALLOC(1, sizeof(Link));
link->type = LINK_FILE;
} else {
return;
}
int id_set = 0;
int linkname_set = 0;
int track = 0;
char *title = "";
char *suffix = "";
for (int i = 0; attr[i]; i += 2) {
if (!strcmp("id", attr[i])) {
link->sonic_id = atoi(attr[i+1]);
link->sonic_song_id_str = calloc(MAX_FILENAME_LEN, sizeof(char));
snprintf(link->sonic_song_id_str, MAX_FILENAME_LEN, "%d",
link->sonic_id);
id_set = 1;
continue;
}
if (!strcmp("size", attr[i])) {
link->content_length = atoll(attr[i+1]);
continue;
}
if (!strcmp("created", attr[i])) {
struct tm *tm = calloc(1, sizeof(struct tm));
strptime(attr[i+1], "%Y-%m-%dT%H:%M:%S.000Z", tm);
link->time = mktime(tm);
free(tm);
continue;
}
/* This is used by the album table */
if (!strcmp("name", attr[i])) {
strncpy(link->linkname, attr[i+1], MAX_FILENAME_LEN);
linkname_set = 1;
continue;
}
if (!strcmp("path", attr[i])) {
memset(link->linkname, 0, MAX_FILENAME_LEN);
/* Skip to the last '/' if it exists */
char *s = strrchr(attr[i+1], '/');
if (s) {
strncpy(link->linkname, s + 1, MAX_FILENAME_LEN);
} else {
strncpy(link->linkname, attr[i+1], MAX_FILENAME_LEN);
}
linkname_set = 1;
continue;
}
if (!strcmp("track", attr[i])) {
track = atoi(attr[i+1]);
}
if (!strcmp("title", attr[i])) {
title = (char *) attr[i+1];
}
if (!strcmp("suffix", attr[i])) {
suffix = (char *) attr[i+1];
}
}
char *url;
switch (n) {
/* Root level */
case 1:
break;
/* Index level */
case 2:
break;
/* Artist level */
case 3:
break;
/* Album level*/
case 4:
break;
/* Invalid */
default:
break;
if (!linkname_set && strlen(title) > 0 && strlen(suffix) > 0) {
snprintf(link->linkname, MAX_FILENAME_LEN, "%02d - %s.%s",
track, title, suffix);
linkname_set = 1;
}
return NULL;
if (!linkname_set || !id_set) {
free(link);
return;
}
if (link->type == LINK_FILE) {
char *url = sonic_stream_link(link->sonic_id);
strncpy(link->f_url, url, MAX_PATH_LEN);
free(url);
}
LinkTable_add(linktbl, link);
}
LinkTable *sonic_LinkTable_new_id3(int depth, int id)
{
char *url;
LinkTable *linktbl = ROOT_LINK_TBL;
switch (depth) {
/* Root table */
case 0:
url = sonic_gen_url_first_part("getArtists");
linktbl = sonic_url_to_LinkTable(url, XML_parser_id3_root, 0);
free(url);
break;
/* Album table - get all the albums of an artist */
case 3:
url = sonic_getArtist_link(id);
linktbl = sonic_url_to_LinkTable(url, XML_parser_id3, depth);
free(url);
break;
/* Song table - get all the songs of an album */
case 4:
url = sonic_getAlbum_link(id);
linktbl = sonic_url_to_LinkTable(url, XML_parser_id3, depth);
free(url);
break;
default:
/*
* We shouldn't reach here.
*/
fprintf(stderr, "sonic_LinkTable_new_id3(): case %d.\n", depth);
exit_failure();
break;
}
return linktbl;
}

View File

@ -16,17 +16,20 @@ void sonic_config_init(const char *server, const char *username,
/**
* \brief Create a new Sonic LinkTable in index mode
*/
LinkTable *sonic_LinkTable_new_index_mode(const int id);
LinkTable *sonic_LinkTable_new_index(const int id);
/**
* \brief Create a new Sonic LinkTable in ID3 mode
* \details In this mode, the filesystem effectively has 4 levels of
* directories, which are:
* 1. Root
* 2. Index
* 3. Artists
* 4. Album
* \details In this mode, the filesystem effectively has 5 levels of which are:
* 0. Root table
* 1. Index table
* 2. Artist table
* 3. Album table
* 4. Song table
* 5. Individual song
* \param[in] depth the level of the requested table
* \param[in] id the id of the requested table
*/
LinkTable *sonic_LinkTable_new_id3_mode(char *sonic_id_str);
LinkTable *sonic_LinkTable_new_id3(int depth, int id);
#endif