From c251dca27b5d0bafb2c68e60a55d71822d8162e4 Mon Sep 17 00:00:00 2001 From: Fedor Kozhevnikov Date: Mon, 29 Aug 2011 00:07:31 -0400 Subject: [PATCH] MiniDLNA 1.0.22: cvs 2011-08-25 --- release/src/router/minidlna/NEWS | 9 ++ release/src/router/minidlna/albumart.c | 48 +++++---- release/src/router/minidlna/codelength.h | 24 +++++ release/src/router/minidlna/image_utils.c | 2 +- release/src/router/minidlna/inotify.c | 6 +- release/src/router/minidlna/metadata.c | 34 ++++--- release/src/router/minidlna/metadata.h | 2 +- release/src/router/minidlna/minidlna.c | 18 ++-- release/src/router/minidlna/minidlna.conf | 3 + release/src/router/minidlna/minidlnatypes.h | 1 + release/src/router/minidlna/minissdp.c | 121 ++++++++++++++++++---- release/src/router/minidlna/minissdp.h | 3 + release/src/router/minidlna/options.c | 2 +- release/src/router/minidlna/options.h | 1 + release/src/router/minidlna/playlist.c | 4 +- release/src/router/minidlna/po/LINGUAS | 2 +- release/src/router/minidlna/po/pl.po | 121 ++++++++++++++++++++++ release/src/router/minidlna/scanner.c | 15 ++- release/src/router/minidlna/sql.c | 31 +++++- release/src/router/minidlna/sql.h | 5 +- release/src/router/minidlna/upnpglobalvars.c | 3 + release/src/router/minidlna/upnpglobalvars.h | 6 +- release/src/router/minidlna/upnphttp.c | 2 +- release/src/router/minidlna/upnphttp.h | 15 +-- release/src/router/minidlna/upnpreplyparse.c | 13 ++- release/src/router/minidlna/upnpreplyparse.h | 2 +- release/src/router/minidlna/upnpsoap.c | 147 +++++++++++++++++++-------- 27 files changed, 503 insertions(+), 137 deletions(-) create mode 100644 release/src/router/minidlna/codelength.h create mode 100644 release/src/router/minidlna/po/pl.po diff --git a/release/src/router/minidlna/NEWS b/release/src/router/minidlna/NEWS index 089411edfc..b98702b775 100644 --- a/release/src/router/minidlna/NEWS +++ b/release/src/router/minidlna/NEWS @@ -1,3 +1,12 @@ +1.0.22 - Released 24-Aug-2011 +-------------------------------- +- Add bookmark support for some Samsung TV's. +- Fix a memory leak when certain model Samsung TV's or Roku devices are on the network. +- Fix detection of Samsung Series D models. +- Add WAV MIME workaround for Marantz Receivers and Roku SoundBridge. +- Fix bitrate displayed on Microsoft PFS devices. +- Fix a scanner crash when trying to scan image files with no read access. + 1.0.21 - Released 18-July-2011 -------------------------------- - Fix a few issues with new libav/ffmpeg versions. diff --git a/release/src/router/minidlna/albumart.c b/release/src/router/minidlna/albumart.c index 9ba5c8f81b..f184377ac4 100644 --- a/release/src/router/minidlna/albumart.c +++ b/release/src/router/minidlna/albumart.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -268,16 +269,16 @@ end_art: } char * -check_for_album_file(char * dir, const char * path) +check_for_album_file(const char * dir, const char * path) { - char * file = malloc(PATH_MAX); + char file[MAXPATHLEN]; struct album_art_name_s * album_art_name; image_s * imsrc = NULL; int width=0, height=0; char * art_file; /* First look for file-specific cover art */ - sprintf(file, "%s.cover.jpg", path); + snprintf(file, sizeof(file), "%s.cover.jpg", path); if( access(file, R_OK) == 0 ) { if( art_cache_exists(file, &art_file) ) @@ -287,9 +288,10 @@ check_for_album_file(char * dir, const char * path) if( imsrc ) goto found_file; } - sprintf(file, "%s", path); - strip_ext(file); - strcat(file, ".jpg"); + snprintf(file, sizeof(file), "%s", path); + art_file = strrchr(file, '.'); + if( art_file ) + strcpy(art_file, ".jpg"); if( access(file, R_OK) == 0 ) { if( art_cache_exists(file, &art_file) ) @@ -303,13 +305,12 @@ check_for_album_file(char * dir, const char * path) /* Then fall back to possible generic cover art file names */ for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next ) { - sprintf(file, "%s/%s", dir, album_art_name->name); + snprintf(file, sizeof(file), "%s/%s", dir, album_art_name->name); if( access(file, R_OK) == 0 ) { if( art_cache_exists(file, &art_file) ) { existing_file: - free(file); return art_file; } free(art_file); @@ -320,16 +321,13 @@ found_file: width = imsrc->width; height = imsrc->height; if( width > 160 || height > 160 ) - { - art_file = file; - file = save_resized_album_art(imsrc, art_file); - free(art_file); - } + art_file = save_resized_album_art(imsrc, file); + else + art_file = strdup(file); image_free(imsrc); - return(file); + return(art_file); } } - free(file); return NULL; } @@ -341,10 +339,23 @@ find_album_art(const char * path, const char * image_data, int image_size) char ** result; int cols, rows; sqlite_int64 ret = 0; - char * mypath = strdup(path); + char * mypath; + const char * dir; + struct stat st; + + if( stat(path, &st) == 0 && S_ISDIR(st.st_mode) ) + { + mypath = NULL; + dir = path; + } + else + { + mypath = strdup(path); + dir = dirname(mypath); + } if( (image_size && (album_art = check_embedded_art(path, image_data, image_size))) || - (album_art = check_for_album_file(dirname(mypath), path)) ) + (album_art = check_for_album_file(dir, path)) ) { sql = sqlite3_mprintf("SELECT ID from ALBUM_ART where PATH = '%q'", album_art ? album_art : path); if( (sql_get_table(db, sql, &result, &rows, &cols) == SQLITE_OK) && rows ) @@ -359,8 +370,7 @@ find_album_art(const char * path, const char * image_data, int image_size) sqlite3_free_table(result); sqlite3_free(sql); } - if( album_art ) - free(album_art); + free(album_art); free(mypath); return ret; diff --git a/release/src/router/minidlna/codelength.h b/release/src/router/minidlna/codelength.h new file mode 100644 index 0000000000..ab7f9b4c0b --- /dev/null +++ b/release/src/router/minidlna/codelength.h @@ -0,0 +1,24 @@ +/* $Id: codelength.h,v 1.1 2011/07/23 18:38:46 jmaggard Exp $ */ +/* Project : miniupnp + * Author : Thomas BERNARD + * copyright (c) 2005-2008 Thomas Bernard + * This software is subjet to the conditions detailed in the + * provided LICENCE file. */ +#ifndef __CODELENGTH_H__ +#define __CODELENGTH_H__ + +/* Encode length by using 7bit per Byte : + * Most significant bit of each byte specifies that the + * following byte is part of the code */ +#define DECODELENGTH(n, p) n = 0; \ + do { n = (n << 7) | (*p & 0x7f); } \ + while(*(p++)&0x80); + +#define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \ + if(n>=2097152) *(p++) = (n >> 21) | 0x80; \ + if(n>=16384) *(p++) = (n >> 14) | 0x80; \ + if(n>=128) *(p++) = (n >> 7) | 0x80; \ + *(p++) = n & 0x7f; + +#endif + diff --git a/release/src/router/minidlna/image_utils.c b/release/src/router/minidlna/image_utils.c index 79b4625180..c6b9abd0e2 100644 --- a/release/src/router/minidlna/image_utils.c +++ b/release/src/router/minidlna/image_utils.c @@ -482,7 +482,7 @@ image_new_from_jpeg(const char * path, int is_file, const char * buf, int size, if(cinfo.output_components == 3) { ofs = 0; - if((ptr = (unsigned char *)malloc(w * 3 * cinfo.rec_outbuf_height + 8)) == NULL) + if((ptr = (unsigned char *)malloc(w * 3 * cinfo.rec_outbuf_height + 16)) == NULL) { DPRINTF(E_WARN, L_METADATA, "malloc failed\n"); image_free(vimage); diff --git a/release/src/router/minidlna/inotify.c b/release/src/router/minidlna/inotify.c index 51ab35feed..13074fc777 100644 --- a/release/src/router/minidlna/inotify.c +++ b/release/src/router/minidlna/inotify.c @@ -510,9 +510,9 @@ int inotify_remove_file(const char * path) { char * sql; - char **result; char * art_cache; char * ptr; + char **result; sqlite_int64 detailID; int rows, playlist; @@ -528,9 +528,9 @@ inotify_remove_file(const char * path) { sql_exec(db, "DELETE from PLAYLISTS where ID = %lld", detailID); sql_exec(db, "DELETE from DETAILS where ID =" - " (SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s$%lld')", + " (SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s$%llX')", MUSIC_PLIST_ID, detailID); - sql_exec(db, "DELETE from OBJECTS where OBJECT_ID = '%s$%lld' or PARENT_ID = '%s$%lld'", + sql_exec(db, "DELETE from OBJECTS where OBJECT_ID = '%s$%llX' or PARENT_ID = '%s$%llX'", MUSIC_PLIST_ID, detailID, MUSIC_PLIST_ID, detailID); } else diff --git a/release/src/router/minidlna/metadata.c b/release/src/router/minidlna/metadata.c index 986d6c558e..0f698d0701 100644 --- a/release/src/router/minidlna/metadata.c +++ b/release/src/router/minidlna/metadata.c @@ -274,7 +274,7 @@ free_metadata(metadata_t * m, uint32_t flags) } sqlite_int64 -GetFolderMetadata(const char * name, const char * path, const char * artist, const char * genre, const char * album_art) +GetFolderMetadata(const char * name, const char * path, const char * artist, const char * genre, sqlite3_int64 album_art) { int ret; @@ -282,8 +282,7 @@ GetFolderMetadata(const char * name, const char * path, const char * artist, con " (TITLE, PATH, CREATOR, ARTIST, GENRE, ALBUM_ART) " "VALUES" " ('%q', %Q, %Q, %Q, %Q, %lld);", - name, path, artist, artist, genre, - album_art ? strtoll(album_art, NULL, 10) : 0); + name, path, artist, artist, genre, album_art); if( ret != SQLITE_OK ) ret = 0; else @@ -593,19 +592,22 @@ no_exifdata: if( image_get_jpeg_resolution(path, &width, &height) != 0 || !width || !height ) { infile = fopen(path, "r"); - cinfo.err = jpeg_std_error(&jerr); - jerr.error_exit = libjpeg_error_handler; - jpeg_create_decompress(&cinfo); - if( setjmp(setjmp_buffer) ) - goto error; - jpeg_stdio_src(&cinfo, infile); - jpeg_read_header(&cinfo, TRUE); - jpeg_start_decompress(&cinfo); - width = cinfo.output_width; - height = cinfo.output_height; - error: - jpeg_destroy_decompress(&cinfo); - fclose(infile); + if( infile ) + { + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = libjpeg_error_handler; + jpeg_create_decompress(&cinfo); + if( setjmp(setjmp_buffer) ) + goto error; + jpeg_stdio_src(&cinfo, infile); + jpeg_read_header(&cinfo, TRUE); + jpeg_start_decompress(&cinfo); + width = cinfo.output_width; + height = cinfo.output_height; + error: + jpeg_destroy_decompress(&cinfo); + fclose(infile); + } } //DEBUG DPRINTF(E_DEBUG, L_METADATA, " * resolution: %dx%d\n", width, height); diff --git a/release/src/router/minidlna/metadata.h b/release/src/router/minidlna/metadata.h index 7a01105d94..60e9b6784f 100644 --- a/release/src/router/minidlna/metadata.h +++ b/release/src/router/minidlna/metadata.h @@ -88,7 +88,7 @@ void check_for_captions(const char * path, sqlite_int64 detailID); sqlite_int64 -GetFolderMetadata(const char * name, const char * path, const char * artist, const char * genre, const char * album_art); +GetFolderMetadata(const char * name, const char * path, const char * artist, const char * genre, sqlite3_int64 album_art); sqlite_int64 GetAudioMetadata(const char * path, char * name); diff --git a/release/src/router/minidlna/minidlna.c b/release/src/router/minidlna/minidlna.c index 11800747e7..1ce0d52554 100644 --- a/release/src/router/minidlna/minidlna.c +++ b/release/src/router/minidlna/minidlna.c @@ -588,6 +588,9 @@ init(int argc, char * * argv) break; } break; + case UPNPMINISSDPDSOCKET: + minissdpdsocketpath = ary_options[i].value; + break; default: fprintf(stderr, "Unknown option in file %s\n", optionsfile); @@ -868,7 +871,6 @@ main(int argc, char * * argv) time_t lastupdatetime = 0; int max_fd = -1; int last_changecnt = 0; - short int new_db = 0; pid_t scanner_pid = 0; pthread_t inotify_thread = 0; struct media_dir_s *media_path, *last_path; @@ -908,14 +910,14 @@ main(int argc, char * * argv) #endif LIST_INIT(&upnphttphead); - new_db = open_db(); - if( !new_db ) + if( open_db() == 0 ) { updateID = sql_get_int_field(db, "SELECT UPDATE_ID from SETTINGS"); } - if( sql_get_int_field(db, "pragma user_version") != DB_VERSION ) + i = db_upgrade(db); + if( i != 0 ) { - if( new_db ) + if( i < 0 ) { DPRINTF(E_WARN, L_GENERAL, "Creating new database...\n"); } @@ -974,7 +976,11 @@ main(int argc, char * * argv) sudp = OpenAndConfSSDPReceiveSocket(n_lan_addr, lan_addr); if(sudp < 0) { - DPRINTF(E_FATAL, L_GENERAL, "Failed to open socket for receiving SSDP. EXITING\n"); + DPRINTF(E_INFO, L_GENERAL, "Failed to open socket for receiving SSDP. Trying to use MiniSSDPd\n"); + if(SubmitServicesToMiniSSDPD(lan_addr[0].str, runtime_vars.port) < 0) { + DPRINTF(E_FATAL, L_GENERAL, "Failed to connect to MiniSSDPd. EXITING"); + return 1; + } } /* open socket for HTTP connections. Listen on the 1st LAN address */ shttpl = OpenAndConfHTTPSocket(runtime_vars.port); diff --git a/release/src/router/minidlna/minidlna.conf b/release/src/router/minidlna/minidlna.conf index da55668ace..377e9b28d8 100644 --- a/release/src/router/minidlna/minidlna.conf +++ b/release/src/router/minidlna/minidlna.conf @@ -49,6 +49,9 @@ notify_interval=900 serial=12345678 model_number=1 +# specify the path to the MiniSSDPd socket +#minissdpdsocket=/var/run/minissdpd.sock + # use different container as root of the tree # possible values: # + "." - use standard container (this is the default) diff --git a/release/src/router/minidlna/minidlnatypes.h b/release/src/router/minidlna/minidlnatypes.h index 8277a50e3e..31d25dc4e5 100644 --- a/release/src/router/minidlna/minidlnatypes.h +++ b/release/src/router/minidlna/minidlnatypes.h @@ -81,6 +81,7 @@ enum client_types { ENetgearEVA2000, ESamsungSeriesA, ESamsungSeriesB, + EMarantzDMP, EStandardDLNA150 = 100 }; diff --git a/release/src/router/minidlna/minissdp.c b/release/src/router/minidlna/minissdp.c index c484d6cbc7..320711bc60 100644 --- a/release/src/router/minidlna/minissdp.c +++ b/release/src/router/minidlna/minissdp.c @@ -1,5 +1,7 @@ -/* $Id: minissdp.c,v 1.20 2011/07/11 20:01:42 jmaggard Exp $ */ -/* MiniUPnP project +/* MiniDLNA media server + * This file is part of MiniDLNA. + * + * The code herein is based on the MiniUPnP Project. * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * * Copyright (c) 2006, Thomas Bernard @@ -32,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +47,7 @@ #include "upnpreplyparse.h" #include "getifaddr.h" #include "minissdp.h" +#include "codelength.h" #include "utils.h" #include "log.h" @@ -52,7 +56,7 @@ #define SSDP_MCAST_ADDR ("239.255.255.250") static int -AddMulticastMembership(int s, in_addr_t ifaddr/*const char * ifaddr*/) +AddMulticastMembership(int s, in_addr_t ifaddr) { struct ip_mreq imr; /* Ip multicast membership */ @@ -70,6 +74,8 @@ AddMulticastMembership(int s, in_addr_t ifaddr/*const char * ifaddr*/) return 0; } +/* Open and configure the socket listening for + * SSDP udp packets sent on 239.255.255.250 port 1900 */ int OpenAndConfSSDPReceiveSocket() { @@ -242,9 +248,9 @@ SendSSDPAnnounce2(int s, struct sockaddr_in sockname, int st_no, { int l, n; char buf[512]; - /* TODO : + /* * follow guideline from document "UPnP Device Architecture 1.0" - * put in uppercase. + * uppercase is recommended. * DATE: is recommended * SERVER: OS/ver UPnP/1.0 minidlna/1.0 * - check what to put in the 'Cache-Control' header @@ -360,7 +366,7 @@ ParseUPnPClient(char *location) int client; enum client_types type = 0; uint32_t flags = 0; - char *model, *serial; + char *model, *serial, *name; if (strncmp(location, "http://", 7) != 0) return; @@ -444,13 +450,16 @@ close: ParseNameValue(off, nread, &xml); model = GetValueFromNameValueList(&xml, "modelName"); serial = GetValueFromNameValueList(&xml, "serialNumber"); + name = GetValueFromNameValueList(&xml, "friendlyName"); if( model ) { DPRINTF(E_DEBUG, L_SSDP, "Model: %s\n", model); if( strstr(model, "Roku SoundBridge") ) { type = ERokuSoundBridge; + flags |= FLAG_MS_PFS; flags |= FLAG_AUDIO_ONLY; + flags |= FLAG_MIME_WAV_WAV; } else if( strcmp(model, "Samsung DTV DMR") == 0 && serial ) { @@ -464,11 +473,21 @@ close: flags |= FLAG_NO_RESIZE; } } + else + { + if( name && (strcmp(name, "marantz DMP") == 0) ) + { + type = EMarantzDMP; + flags |= FLAG_DLNA; + flags |= FLAG_MIME_WAV_WAV; + } + } } + ClearNameValueList(&xml); if( !type ) return; - client = SearchClientCache(dest.sin_addr, 1); /* Add this client to the cache if it's not there already. */ + client = SearchClientCache(dest.sin_addr, 1); if( client < 0 ) { for( client=0; client 0) + l++; + CODELENGTH(l, p); + memcpy(p, known_service_types[i], l); + if(i > 0) + p[l-1] = '1'; + p += l; + l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s", + uuidvalue, known_service_types[i], (i==0)?"":"1"); + CODELENGTH(l, p); + memcpy(p, strbuf, l); + p += l; + l = (int)strlen(MINIDLNA_SERVER_STRING); + CODELENGTH(l, p); + memcpy(p, MINIDLNA_SERVER_STRING, l); + p += l; + l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH, + host, (unsigned int)port); + CODELENGTH(l, p); + memcpy(p, strbuf, l); + p += l; + if(write(s, buffer, p - buffer) < 0) { + DPRINTF(E_ERROR, L_SSDP, "write(): %s", strerror(errno)); + return -1; + } + } + close(s); + return 0; +} + diff --git a/release/src/router/minidlna/minissdp.h b/release/src/router/minidlna/minissdp.h index 7f79130ae1..bcc7ff4a25 100644 --- a/release/src/router/minidlna/minissdp.h +++ b/release/src/router/minidlna/minissdp.h @@ -62,5 +62,8 @@ ProcessSSDPRequest(int s, unsigned short port); int SendSSDPGoodbye(int * sockets, int n); +int +SubmitServicesToMiniSSDPD(const char * host, unsigned short port); + #endif diff --git a/release/src/router/minidlna/options.c b/release/src/router/minidlna/options.c index 9a6464d180..11fa6802f9 100644 --- a/release/src/router/minidlna/options.c +++ b/release/src/router/minidlna/options.c @@ -1,4 +1,3 @@ -/* $Id: options.c,v 1.15 2011/06/25 00:39:45 jmaggard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * author: Ryan Wagoner @@ -58,6 +57,7 @@ static const struct { { UPNPINOTIFY, "inotify" }, { UPNPDBDIR, "db_dir" }, { UPNPLOGDIR, "log_dir" }, + { UPNPMINISSDPDSOCKET, "minissdpdsocket"}, { ENABLE_TIVO, "enable_tivo" }, { ENABLE_DLNA_STRICT, "strict_dlna" }, { ROOT_CONTAINER, "root_container" } diff --git a/release/src/router/minidlna/options.h b/release/src/router/minidlna/options.h index b208542b9f..bce407042b 100644 --- a/release/src/router/minidlna/options.h +++ b/release/src/router/minidlna/options.h @@ -51,6 +51,7 @@ enum upnpconfigoptions { UPNPINOTIFY, /* enable inotify on the media directories */ UPNPDBDIR, /* base directory to store the database and album art cache */ UPNPLOGDIR, /* base directory to store the log file */ + UPNPMINISSDPDSOCKET, /* minissdpdsocket */ ENABLE_TIVO, /* enable support for streaming images and music to TiVo */ ENABLE_DLNA_STRICT, /* strictly adhere to DLNA specs */ ROOT_CONTAINER /* root ObjectID (instead of "0") */ diff --git a/release/src/router/minidlna/playlist.c b/release/src/router/minidlna/playlist.c index fadd4e3d0e..1d7f4869d6 100644 --- a/release/src/router/minidlna/playlist.c +++ b/release/src/router/minidlna/playlist.c @@ -95,7 +95,7 @@ fill_playlists() struct stat file; char type[4]; sqlite_int64 plID, detailID; - char sql_buf[1024] = "SELECT ID, NAME, PATH from PLAYLISTS where ITEMS > FOUND"; + char sql_buf[] = "SELECT ID, NAME, PATH from PLAYLISTS where ITEMS > FOUND"; if( sql_get_table(db, sql_buf, &result, &rows, NULL) != SQLITE_OK ) return -1; @@ -121,7 +121,7 @@ fill_playlists() if( sql_get_int_field(db, "SELECT ID from OBJECTS where PARENT_ID = '"MUSIC_PLIST_ID"'" " and NAME = '%q'", plname) <= 0 ) { - detailID = GetFolderMetadata(plname, NULL, NULL, NULL, NULL); + detailID = GetFolderMetadata(plname, NULL, NULL, NULL, 0); sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME) " "VALUES" diff --git a/release/src/router/minidlna/po/LINGUAS b/release/src/router/minidlna/po/LINGUAS index 78420991a2..db2513ed02 100644 --- a/release/src/router/minidlna/po/LINGUAS +++ b/release/src/router/minidlna/po/LINGUAS @@ -1 +1 @@ -da de es fr it ja nb nl ru sl sv +da de es fr it ja nb nl pl ru sl sv diff --git a/release/src/router/minidlna/po/pl.po b/release/src/router/minidlna/po/pl.po new file mode 100644 index 0000000000..cca06bdf2d --- /dev/null +++ b/release/src/router/minidlna/po/pl.po @@ -0,0 +1,121 @@ +# MiniDLNA translation template file +# Justin Maggard , 2010. +# +# MiniDLNA media server +# Copyright (C) 2010 NETGEAR +# +# This file is part of MiniDLNA. +# +# MiniDLNA is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# MiniDLNA is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with MiniDLNA. If not, see . +# +msgid "" +msgstr "" +"Project-Id-Version: minidlna 1.0.18\n" +"Report-Msgid-Bugs-To: jmaggard@users.sourceforge.net\n" +"POT-Creation-Date: 2010-07-19 10:50-0700\n" +"PO-Revision-Date: 2010-10-19 11:00-0800\n" +"Last-Translator: Tomasz.Matuszewski \n" +"Language-Team: Polski\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: scanner.c:163 +msgid "Unknown Date" +msgstr "Nieznana Data" + +#: scanner.c:191 +msgid "Unknown Camera" +msgstr "Nieznana Kamera" + +#: scanner.c:282 +msgid "- All Albums -" +msgstr "- Wszystkie Albumy -" + +#: scanner.c:290 scanner.c:297 scanner.c:300 +msgid "Unknown Album" +msgstr "Nieznany Album" + +#: scanner.c:323 +msgid "- All Artists -" +msgstr "- Wszyscy Wykonawcy -" + +#: scanner.c:331 scanner.c:337 scanner.c:340 +msgid "Unknown Artist" +msgstr "Nieznany Wykonawca" + +#: scanner.c:543 +msgid "Music" +msgstr "Muzyka" + +#: scanner.c:544 +msgid "All Music" +msgstr "Wszystkie Utwory" + +#: scanner.c:545 +msgid "Genre" +msgstr "Rodzaj" + +#: scanner.c:546 +msgid "Artist" +msgstr "Wykonawca" + +#: scanner.c:547 +msgid "Album" +msgstr "Album" + +#: scanner.c:548 scanner.c:552 scanner.c:557 +msgid "Folders" +msgstr "Folder" + +#: scanner.c:549 +msgid "Playlists" +msgstr "Lista Utworow" + +#: scanner.c:550 +msgid "Video" +msgstr "Filmy" + +#: scanner.c:551 +msgid "All Video" +msgstr "Wszystkie Filmy" + +#: scanner.c:553 +msgid "Pictures" +msgstr "Obrazy" + +#: scanner.c:554 +msgid "All Pictures" +msgstr "Wszystkie Obrazy" + +#: scanner.c:555 +msgid "Date Taken" +msgstr "Data Wykonania" + +#: scanner.c:556 +msgid "Camera" +msgstr "Kamera" + +#: scanner.c:558 +msgid "Browse Folders" +msgstr "Przegladaj Foldery" + +#: scanner.c:721 +#, c-format +msgid "Scanning %s\n" +msgstr "Skanowanie %s\n" + +#: scanner.c:789 +#, c-format +msgid "Scanning %s finished (%llu files)!\n" +msgstr "Skanowanie %s zakonczone (%llu files)!\n" diff --git a/release/src/router/minidlna/scanner.c b/release/src/router/minidlna/scanner.c index 84f0b4c3f7..7360ba7cc2 100644 --- a/release/src/router/minidlna/scanner.c +++ b/release/src/router/minidlna/scanner.c @@ -40,6 +40,7 @@ #include "utils.h" #include "sql.h" #include "scanner.h" +#include "albumart.h" #include "log.h" int valid_cache = 0; @@ -106,7 +107,7 @@ insert_container(const char * item, const char * rootParent, const char * refID, } if( !detailID ) { - detailID = GetFolderMetadata(item, NULL, artist, genre, album_art); + detailID = GetFolderMetadata(item, NULL, artist, genre, (album_art ? strtoll(album_art, NULL, 10) : 0)); } ret = sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) " @@ -439,7 +440,7 @@ insert_directory(const char * name, const char * path, const char * base, const return 1; } - detailID = GetFolderMetadata(name, path, NULL, NULL, NULL); + detailID = GetFolderMetadata(name, path, NULL, NULL, find_album_art(path, NULL, 0)); sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) " "VALUES" @@ -605,6 +606,12 @@ CreateDatabase(void) ")"); if( ret != SQLITE_OK ) goto sql_failed; + ret = sql_exec(db, "CREATE TABLE BOOKMARKS (" + "ID INTEGER PRIMARY KEY, " + "SEC INTEGER" + ")"); + if( ret != SQLITE_OK ) + goto sql_failed; ret = sql_exec(db, "CREATE TABLE PLAYLISTS (" "ID INTEGER PRIMARY KEY AUTOINCREMENT, " "NAME TEXT NOT NULL, " @@ -628,7 +635,7 @@ CreateDatabase(void) ret = sql_exec(db, "INSERT into OBJECTS (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME)" " values " "('%s', '%s', %lld, 'container.storageFolder', '%q')", - containers[i], containers[i+1], GetFolderMetadata(containers[i+2], NULL, NULL, NULL, NULL), containers[i+2]); + containers[i], containers[i+1], GetFolderMetadata(containers[i+2], NULL, NULL, NULL, 0), containers[i+2]); if( ret != SQLITE_OK ) goto sql_failed; } @@ -772,7 +779,7 @@ ScanDirectory(const char * dir, const char * parent, enum media_types dir_type) sprintf(parent_id, "%s$%X", (parent ? parent:""), i+startID); ScanDirectory(full_path, parent_id, dir_type); } - else if( type == TYPE_FILE ) + else if( type == TYPE_FILE && (access(full_path, R_OK) == 0) ) { if( insert_file(name, full_path, (parent ? parent:""), i+startID) == 0 ) fileno++; diff --git a/release/src/router/minidlna/sql.c b/release/src/router/minidlna/sql.c index 6311e1ef10..e8bdebde1b 100644 --- a/release/src/router/minidlna/sql.c +++ b/release/src/router/minidlna/sql.c @@ -20,6 +20,7 @@ #include #include "sql.h" +#include "upnpglobalvars.h" #include "log.h" int @@ -124,7 +125,7 @@ sql_get_int_field(sqlite3 *db, const char *fmt, ...) } char * -sql_get_text_field(void *db, const char *fmt, ...) +sql_get_text_field(sqlite3 *db, const char *fmt, ...) { va_list ap; int counter, result, len; @@ -198,3 +199,31 @@ sql_get_text_field(void *db, const char *fmt, ...) sqlite3_finalize(stmt); return str; } + +int +db_upgrade(sqlite3 *db) +{ + int db_vers; + int ret; + + db_vers = sql_get_int_field(db, "PRAGMA user_version"); + + if (db_vers == DB_VERSION) + return 0; + if (db_vers < 1) + return -1; + if (db_vers < 5) + return 5; + if (db_vers < 6) + { + DPRINTF(E_WARN, L_DB_SQL, "Updating DB version to v%d.\n", DB_VERSION); + ret = sql_exec(db, "CREATE TABLE BOOKMARKS (" + "ID INTEGER PRIMARY KEY, " + "SEC INTEGER)"); + if( ret != SQLITE_OK ) + return 6; + } + sql_exec(db, "PRAGMA user_version = %d", DB_VERSION); + + return 0; +} diff --git a/release/src/router/minidlna/sql.h b/release/src/router/minidlna/sql.h index e2e966154e..b3742ca086 100644 --- a/release/src/router/minidlna/sql.h +++ b/release/src/router/minidlna/sql.h @@ -36,6 +36,9 @@ int sql_get_int_field(sqlite3 *db, const char *fmt, ...); char * -sql_get_text_field(void *dbh, const char *fmt, ...); +sql_get_text_field(sqlite3 *db, const char *fmt, ...); + +int +db_upgrade(sqlite3 *db); #endif diff --git a/release/src/router/minidlna/upnpglobalvars.c b/release/src/router/minidlna/upnpglobalvars.c index d059daaea1..3fe5cf162c 100644 --- a/release/src/router/minidlna/upnpglobalvars.c +++ b/release/src/router/minidlna/upnpglobalvars.c @@ -80,6 +80,9 @@ char presentationurl[PRESENTATIONURL_MAX_LEN]; int n_lan_addr = 0; struct lan_addr_s lan_addr[MAX_LAN_ADDR]; +/* Path of the Unix socket used to communicate with MiniSSDPd */ +const char * minissdpdsocketpath = "/var/run/minissdpd.sock"; + /* UPnP-A/V [DLNA] */ sqlite3 * db; char dlna_no_conv[] = "DLNA.ORG_OP=01;DLNA.ORG_CI=0"; diff --git a/release/src/router/minidlna/upnpglobalvars.h b/release/src/router/minidlna/upnpglobalvars.h index eaf4de2843..3562e1adf9 100644 --- a/release/src/router/minidlna/upnpglobalvars.h +++ b/release/src/router/minidlna/upnpglobalvars.h @@ -56,7 +56,7 @@ #include -#define MINIDLNA_VERSION "1.0.21" +#define MINIDLNA_VERSION "1.0.22" #ifdef NETGEAR # define SERVER_NAME "ReadyDLNA" @@ -66,7 +66,7 @@ #define CLIENT_CACHE_SLOTS 20 #define USE_FORK 1 -#define DB_VERSION 5 +#define DB_VERSION 6 #ifdef ENABLE_NLS #define _(string) gettext(string) @@ -211,6 +211,8 @@ extern char pnpx_hwid[]; extern int n_lan_addr; extern struct lan_addr_s lan_addr[]; +extern const char * minissdpdsocketpath; + /* UPnP-A/V [DLNA] */ extern sqlite3 *db; extern char dlna_no_conv[]; diff --git a/release/src/router/minidlna/upnphttp.c b/release/src/router/minidlna/upnphttp.c index 49a9a3de74..d65bf2258a 100644 --- a/release/src/router/minidlna/upnphttp.c +++ b/release/src/router/minidlna/upnphttp.c @@ -302,7 +302,7 @@ intervening space) by either an integer or the keyword "infinite". */ h->reqflags |= FLAG_DLNA; h->reqflags |= FLAG_MIME_AVI_DIVX; } - else if(strncmp(p, "SEC_HHP_", 8)==0) + else if(strstrc(p, "SEC_HHP_", '\r')) { h->req_client = ESamsungTV; h->reqflags |= FLAG_SAMSUNG; diff --git a/release/src/router/minidlna/upnphttp.h b/release/src/router/minidlna/upnphttp.h index 95711cb309..2998c22ca0 100644 --- a/release/src/router/minidlna/upnphttp.h +++ b/release/src/router/minidlna/upnphttp.h @@ -109,13 +109,14 @@ struct upnphttp { #define FLAG_MIME_AVI_DIVX 0x00200000 #define FLAG_MIME_AVI_AVI 0x00400000 #define FLAG_MIME_FLAC_FLAC 0x00800000 -#define FLAG_NO_RESIZE 0x01000000 -#define FLAG_MS_PFS 0x02000000 // Microsoft PlaysForSure client -#define FLAG_SAMSUNG 0x04000000 -#define FLAG_AUDIO_ONLY 0x08000000 - -#define FLAG_FREE_OBJECT_ID 0x00000001 -#define FLAG_ROOT_CONTAINER 0x00000002 +#define FLAG_MIME_WAV_WAV 0x01000000 +#define FLAG_NO_RESIZE 0x02000000 +#define FLAG_MS_PFS 0x04000000 // Microsoft PlaysForSure client +#define FLAG_SAMSUNG 0x08000000 +#define FLAG_AUDIO_ONLY 0x10000000 + +#define FLAG_FREE_OBJECT_ID 0x00000001 +#define FLAG_ROOT_CONTAINER 0x00000002 /* New_upnphttp() */ struct upnphttp * diff --git a/release/src/router/minidlna/upnpreplyparse.c b/release/src/router/minidlna/upnpreplyparse.c index dd53be5841..419dbf5811 100644 --- a/release/src/router/minidlna/upnpreplyparse.c +++ b/release/src/router/minidlna/upnpreplyparse.c @@ -1,4 +1,3 @@ -/* $Id: upnpreplyparse.c,v 1.6 2010/11/11 23:48:13 jmaggard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * @@ -47,11 +46,11 @@ NameValueParserStartElt(void * d, const char * name, int l) if(!data->head.lh_first) { struct NameValue * nv; - nv = malloc(sizeof(struct NameValue)); + nv = malloc(sizeof(struct NameValue)+l+1); strcpy(nv->name, "rootElement"); memcpy(nv->value, name, l); nv->value[l] = '\0'; - LIST_INSERT_HEAD( &(data->head), nv, entries); + LIST_INSERT_HEAD(&(data->head), nv, entries); } } @@ -60,14 +59,14 @@ NameValueParserGetData(void * d, const char * datas, int l) { struct NameValueParserData * data = (struct NameValueParserData *)d; struct NameValue * nv; - nv = malloc(sizeof(struct NameValue)); - if(l>511) - l = 511; + if(l>1975) + l = 1975; + nv = malloc(sizeof(struct NameValue)+l+1); strncpy(nv->name, data->curelt, 64); nv->name[63] = '\0'; memcpy(nv->value, datas, l); nv->value[l] = '\0'; - LIST_INSERT_HEAD( &(data->head), nv, entries); + LIST_INSERT_HEAD(&(data->head), nv, entries); } void diff --git a/release/src/router/minidlna/upnpreplyparse.h b/release/src/router/minidlna/upnpreplyparse.h index 34f0473945..f887046bf4 100644 --- a/release/src/router/minidlna/upnpreplyparse.h +++ b/release/src/router/minidlna/upnpreplyparse.h @@ -38,7 +38,7 @@ extern "C" { struct NameValue { LIST_ENTRY(NameValue) entries; char name[64]; - char value[512]; + char value[]; }; struct NameValueParserData { diff --git a/release/src/router/minidlna/upnpsoap.c b/release/src/router/minidlna/upnpsoap.c index 42df6a3da5..2b2e8af07d 100644 --- a/release/src/router/minidlna/upnpsoap.c +++ b/release/src/router/minidlna/upnpsoap.c @@ -188,6 +188,7 @@ GetSearchCapabilities(struct upnphttp * h, const char * action) "" "" "dc:creator," + "dc:date," "dc:title," "upnp:album," "upnp:actor," @@ -344,6 +345,9 @@ mime_to_ext(const char * mime, char * buf) #define FILTER_UPNP_GENRE 0x00040000 #define FILTER_UPNP_ORIGINALTRACKNUMBER 0x00080000 #define FILTER_UPNP_SEARCHCLASS 0x00100000 +#define FILTER_SEC 0x00200000 +#define FILTER_SEC_CAPTION_INFO 0x00400000 +#define FILTER_SEC_CAPTION_INFO_EX 0x00800000 static u_int32_t set_filter_flags(char * filter, struct upnphttp *h) @@ -467,6 +471,16 @@ set_filter_flags(char * filter, struct upnphttp *h) flags |= FILTER_RES; flags |= FILTER_RES_SIZE; } + else if( strcmp(item, "sec:CaptionInfo") == 0) + { + flags |= FILTER_SEC; + flags |= FILTER_SEC_CAPTION_INFO; + } + else if( strcmp(item, "sec:CaptionInfoEx") == 0) + { + flags |= FILTER_SEC; + flags |= FILTER_SEC_CAPTION_INFO_EX; + } item = strtok_r(NULL, ",", &saveptr); } @@ -591,7 +605,10 @@ add_res(char *size, char *duration, char *bitrate, char *sampleFrequency, strcatf(args->str, "duration=\"%s\" ", duration); } if( bitrate && (args->filter & FILTER_RES_BITRATE) ) { - strcatf(args->str, "bitrate=\"%s\" ", bitrate); + int br = atoi(bitrate); + if(args->flags & FLAG_MS_PFS) + br /= 8; + strcatf(args->str, "bitrate=\"%d\" ", br); } if( sampleFrequency && (args->filter & FILTER_RES_SAMPLEFREQUENCY) ) { strcatf(args->str, "sampleFrequency=\"%s\" ", sampleFrequency); @@ -723,6 +740,13 @@ callback(void *args, int argc, char **argv, char **azColName) strcpy(mime+6, "flac"); } } + else if( strcmp(mime+6, "x-wav") == 0 ) + { + if( passed_args->flags & FLAG_MIME_WAV_WAV ) + { + strcpy(mime+6, "wav"); + } + } } ret = strcatf(str, "<item id=\"%s\" parentID=\"%s\" restricted=\"1\"", id, parent); @@ -742,6 +766,11 @@ callback(void *args, int argc, char **argv, char **azColName) if( date && (passed_args->filter & FILTER_DC_DATE) ) { ret = strcatf(str, "<dc:date>%s</dc:date>", date); } + if( (passed_args->flags & FLAG_SAMSUNG) && (passed_args->filter & FILTER_SEC_CAPTION_INFO_EX) ) { + /* Get bookmark */ + ret = strcatf(str, "<sec:dcmInfo>CREATIONDATE=0,FOLDER=%s,BM=%d</sec:dcmInfo>", + title, sql_get_int_field(db, "SELECT SEC from BOOKMARKS where ID = '%s'", detailID)); + } if( artist ) { if( (*mime == 'v') && (passed_args->filter & FILTER_UPNP_ACTOR) ) { ret = strcatf(str, "<upnp:actor>%s</upnp:actor>", artist); @@ -964,22 +993,28 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) "" "<DIDL-Lite" CONTENT_DIRECTORY_SCHEMAS; - char *zErrMsg = 0; + char *zErrMsg = NULL; char *sql, *ptr; - int ret; struct Response args; struct string_s str; int totalMatches; + int ret; + char *ObjectID, *Filter, *BrowseFlag, *SortCriteria; + char *orderBy = NULL; struct NameValueParserData data; - - ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); - char * ObjectID = GetValueFromNameValueList(&data, "ObjectID"); - char * Filter = GetValueFromNameValueList(&data, "Filter"); - char * BrowseFlag = GetValueFromNameValueList(&data, "BrowseFlag"); - char * SortCriteria = GetValueFromNameValueList(&data, "SortCriteria"); - char * orderBy = NULL; int RequestedCount = 0; int StartingIndex = 0; + + memset(&args, 0, sizeof(args)); + memset(&str, 0, sizeof(str)); + + ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); + + ObjectID = GetValueFromNameValueList(&data, "ObjectID"); + Filter = GetValueFromNameValueList(&data, "Filter"); + BrowseFlag = GetValueFromNameValueList(&data, "BrowseFlag"); + SortCriteria = GetValueFromNameValueList(&data, "SortCriteria"); + if( (ptr = GetValueFromNameValueList(&data, "RequestedCount")) ) RequestedCount = atoi(ptr); if( !RequestedCount ) @@ -996,8 +1031,6 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) SoapError(h, 701, "No such object error"); goto browse_error; } - memset(&args, 0, sizeof(args)); - memset(&str, 0, sizeof(str)); str.data = malloc(DEFAULT_RESP_SIZE); str.size = DEFAULT_RESP_SIZE; @@ -1152,25 +1185,29 @@ SearchContentDirectory(struct upnphttp * h, const char * action) "" "<DIDL-Lite" CONTENT_DIRECTORY_SCHEMAS; - char *zErrMsg = 0; + char *zErrMsg = NULL; char *sql, *ptr; - char **result; struct Response args; struct string_s str; - int totalMatches = 0; + int totalMatches; int ret; - - struct NameValueParserData data; - ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); - char * ContainerID = GetValueFromNameValueList(&data, "ContainerID"); - char * Filter = GetValueFromNameValueList(&data, "Filter"); - char * SearchCriteria = GetValueFromNameValueList(&data, "SearchCriteria"); - char * SortCriteria = GetValueFromNameValueList(&data, "SortCriteria"); - char * newSearchCriteria = NULL; - char * orderBy = NULL; + char *ContainerID, *Filter, *SearchCriteria, *SortCriteria; + char *newSearchCriteria = NULL, *orderBy = NULL; char groupBy[] = "group by DETAIL_ID"; + struct NameValueParserData data; int RequestedCount = 0; int StartingIndex = 0; + + memset(&args, 0, sizeof(args)); + memset(&str, 0, sizeof(str)); + + ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); + + ContainerID = GetValueFromNameValueList(&data, "ContainerID"); + Filter = GetValueFromNameValueList(&data, "Filter"); + SearchCriteria = GetValueFromNameValueList(&data, "SearchCriteria"); + SortCriteria = GetValueFromNameValueList(&data, "SortCriteria"); + if( (ptr = GetValueFromNameValueList(&data, "RequestedCount")) ) RequestedCount = atoi(ptr); if( !RequestedCount ) @@ -1185,8 +1222,6 @@ SearchContentDirectory(struct upnphttp * h, const char * action) goto search_error; } } - memset(&args, 0, sizeof(args)); - memset(&str, 0, sizeof(str)); str.data = malloc(DEFAULT_RESP_SIZE); str.size = DEFAULT_RESP_SIZE; @@ -1257,10 +1292,13 @@ SearchContentDirectory(struct upnphttp * h, const char * action) { SearchCriteria = modifyString(SearchCriteria, """, "\"", 0); SearchCriteria = modifyString(SearchCriteria, "'", "'", 0); + SearchCriteria = modifyString(SearchCriteria, "<", "<", 0); + SearchCriteria = modifyString(SearchCriteria, ">", ">", 0); SearchCriteria = modifyString(SearchCriteria, "\\\"", "\"\"", 0); SearchCriteria = modifyString(SearchCriteria, "object.", "", 0); SearchCriteria = modifyString(SearchCriteria, "derivedfrom", "like", 1); SearchCriteria = modifyString(SearchCriteria, "contains", "like", 2); + SearchCriteria = modifyString(SearchCriteria, "dc:date", "d.DATE", 0); SearchCriteria = modifyString(SearchCriteria, "dc:title", "d.TITLE", 0); SearchCriteria = modifyString(SearchCriteria, "dc:creator", "d.CREATOR", 0); SearchCriteria = modifyString(SearchCriteria, "upnp:class", "o.CLASS", 0); @@ -1295,22 +1333,14 @@ SearchContentDirectory(struct upnphttp * h, const char * action) } DPRINTF(E_DEBUG, L_HTTP, "Translated SearchCriteria: %s\n", SearchCriteria); - sql = sqlite3_mprintf("SELECT (select count(distinct DETAIL_ID)" - " from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)" - " where (OBJECT_ID glob '%s$*') and (%s))" - " + " - "(select count(*) from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)" - " where (OBJECT_ID = '%s') and (%s))", - ContainerID, SearchCriteria, ContainerID, SearchCriteria); - //DEBUG DPRINTF(E_DEBUG, L_HTTP, "Count SQL: %s\n", sql); - ret = sql_get_table(db, sql, &result, NULL, NULL); - sqlite3_free(sql); - if( ret == SQLITE_OK ) - { - totalMatches = atoi(result[1]); - sqlite3_free_table(result); - } - else + totalMatches = sql_get_int_field(db, "SELECT (select count(distinct DETAIL_ID)" + " from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)" + " where (OBJECT_ID glob '%s$*') and (%s))" + " + " + "(select count(*) from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)" + " where (OBJECT_ID = '%s') and (%s))", + ContainerID, SearchCriteria, ContainerID, SearchCriteria); + if( totalMatches < 0 ) { /* Must be invalid SQL, so most likely bad or unhandled search criteria. */ SoapError(h, 708, "Unsupported or invalid search criteria"); @@ -1440,7 +1470,37 @@ SamsungGetFeatureList(struct upnphttp * h, const char * action) "</Feature>" ""; - BuildSendAndCloseSoapResp(h, resp, sizeof(resp)); + BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1); +} + +static void +SamsungSetBookmark(struct upnphttp * h, const char * action) +{ + static const char resp[] = + "" + ""; + + struct NameValueParserData data; + char *ObjectID, *PosSecond; + int ret; + + ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); + ObjectID = GetValueFromNameValueList(&data, "ObjectID"); + PosSecond = GetValueFromNameValueList(&data, "PosSecond"); + if( ObjectID && PosSecond ) + { + ret = sql_exec(db, "INSERT OR REPLACE into BOOKMARKS" + " VALUES " + "((select DETAIL_ID from OBJECTS where OBJECT_ID = '%s'), %s)", ObjectID, PosSecond); + if( ret != SQLITE_OK ) + DPRINTF(E_WARN, L_METADATA, "Error setting bookmark %s on ObjectID='%s'\n", PosSecond, ObjectID); + BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1); + } + else + SoapError(h, 402, "Invalid Args"); + + ClearNameValueList(&data); } static const struct @@ -1462,6 +1522,7 @@ soapMethods[] = { "IsAuthorized", IsAuthorizedValidated}, { "IsValidated", IsAuthorizedValidated}, { "X_GetFeatureList", SamsungGetFeatureList}, + { "X_SetBookmark", SamsungSetBookmark}, { 0, 0 } }; -- 2.11.4.GIT