* Fix some possible memory leaks.
* Pay attention to the specified port number. * Add support for multiple media directories, and allow content filtering on the directories.
This commit is contained in:
parent
3f454a5762
commit
0324818d86
@ -222,6 +222,7 @@ find_album_art(const char * path)
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3_free(sql);
|
||||
sql = sqlite3_mprintf( "INSERT into ALBUM_ART"
|
||||
" (PATH, EMBEDDED) "
|
||||
"VALUES"
|
||||
@ -231,6 +232,7 @@ find_album_art(const char * path)
|
||||
if( sql_exec(db, sql) == SQLITE_OK )
|
||||
ret = sqlite3_last_insert_rowid(db);
|
||||
}
|
||||
sqlite3_free_table(result);
|
||||
sqlite3_free(sql);
|
||||
}
|
||||
if( album_art )
|
||||
|
35
getifaddr.c
35
getifaddr.c
@ -59,17 +59,34 @@ getifaddr(const char * ifname, char * buf, int len)
|
||||
int
|
||||
getsysaddr(char * buf, int len)
|
||||
{
|
||||
char hn[256];
|
||||
struct in_addr *addr;
|
||||
struct hostent *host;
|
||||
int i;
|
||||
int s = socket(PF_INET, SOCK_STREAM, 0);
|
||||
|
||||
memset(buf, '\0', len);
|
||||
gethostname(hn, sizeof(hn));
|
||||
host = gethostbyname(hn);
|
||||
if( !host )
|
||||
for (i=1; i > 0; i++)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
struct sockaddr_in *addr = (struct sockaddr_in *) &ifr.ifr_addr;
|
||||
|
||||
ifr.ifr_ifindex = i;
|
||||
if( ioctl(s, SIOCGIFNAME, &ifr) < 0 )
|
||||
break;
|
||||
if(ioctl(s, SIOCGIFADDR, &ifr, sizeof(struct ifreq)) < 0)
|
||||
{
|
||||
syslog(LOG_ERR, "ioctl(s, SIOCGIFADDR, ...): %m");
|
||||
close(s);
|
||||
return -1;
|
||||
addr = (struct in_addr*)host->h_addr;
|
||||
strncpy(buf, inet_ntoa(*addr), len);
|
||||
}
|
||||
if(strncmp(inet_ntoa(addr->sin_addr), "127.", 4) == 0)
|
||||
continue;
|
||||
if(!inet_ntop(AF_INET, &addr->sin_addr, buf, len))
|
||||
{
|
||||
syslog(LOG_ERR, "inet_ntop(): %m");
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
close(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
165
minidlna.c
165
minidlna.c
@ -139,17 +139,6 @@ set_startup_time(int sysuptime)
|
||||
}
|
||||
}
|
||||
|
||||
/* structure containing variables used during "main loop"
|
||||
* that are filled during the init */
|
||||
struct runtime_vars {
|
||||
/* LAN IP addresses for SSDP traffic and HTTP */
|
||||
/* moved to global vars */
|
||||
/*int n_lan_addr;*/
|
||||
/*struct lan_addr_s lan_addr[MAX_LAN_ADDR];*/
|
||||
int port; /* HTTP Port */
|
||||
int notify_interval; /* seconds between SSDP announces */
|
||||
};
|
||||
|
||||
/* parselanaddr()
|
||||
* parse address with mask
|
||||
* ex: 192.168.1.1/24
|
||||
@ -224,7 +213,7 @@ getfriendlyname(char * buf, int len)
|
||||
* 7) compute presentation URL
|
||||
* 8) set signal handlers */
|
||||
static int
|
||||
init(int argc, char * * argv, struct runtime_vars * v)
|
||||
init(int argc, char * * argv)
|
||||
{
|
||||
int i;
|
||||
int pid;
|
||||
@ -261,7 +250,6 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
|
||||
getfriendlyname(friendly_name, FRIENDLYNAME_MAX_LEN);
|
||||
|
||||
/*v->n_lan_addr = 0;*/
|
||||
char ext_ip_addr[INET_ADDRSTRLEN];
|
||||
if( (getsysaddr(ext_ip_addr, INET_ADDRSTRLEN) < 0) &&
|
||||
(getifaddr("eth0", ext_ip_addr, INET_ADDRSTRLEN) < 0) &&
|
||||
@ -272,8 +260,8 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
}
|
||||
if( parselanaddr(&lan_addr[n_lan_addr], ext_ip_addr) == 0 )
|
||||
n_lan_addr++;
|
||||
v->port = -1;
|
||||
v->notify_interval = 30; /* seconds between SSDP announces */
|
||||
runtime_vars.port = -1;
|
||||
runtime_vars.notify_interval = 30; /* seconds between SSDP announces */
|
||||
|
||||
/* read options file first since
|
||||
* command line arguments have final say */
|
||||
@ -290,12 +278,11 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
switch(ary_options[i].id)
|
||||
{
|
||||
case UPNPLISTENING_IP:
|
||||
if(n_lan_addr < MAX_LAN_ADDR)/* if(v->n_lan_addr < MAX_LAN_ADDR)*/
|
||||
if(n_lan_addr < MAX_LAN_ADDR)
|
||||
{
|
||||
/*if(parselanaddr(&v->lan_addr[v->n_lan_addr],*/
|
||||
if(parselanaddr(&lan_addr[n_lan_addr],
|
||||
ary_options[i].value) == 0)
|
||||
n_lan_addr++; /*v->n_lan_addr++; */
|
||||
n_lan_addr++;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -304,13 +291,13 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
}
|
||||
break;
|
||||
case UPNPPORT:
|
||||
v->port = atoi(ary_options[i].value);
|
||||
runtime_vars.port = atoi(ary_options[i].value);
|
||||
break;
|
||||
case UPNPPRESENTATIONURL:
|
||||
presurl = ary_options[i].value;
|
||||
break;
|
||||
case UPNPNOTIFY_INTERVAL:
|
||||
v->notify_interval = atoi(ary_options[i].value);
|
||||
runtime_vars.notify_interval = atoi(ary_options[i].value);
|
||||
break;
|
||||
case UPNPSYSTEM_UPTIME:
|
||||
if(strcmp(ary_options[i].value, "yes") == 0)
|
||||
@ -337,8 +324,54 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
friendly_name[FRIENDLYNAME_MAX_LEN-1] = '\0';
|
||||
break;
|
||||
case UPNPMEDIADIR:
|
||||
strncpy(media_dir, ary_options[i].value, MEDIADIR_MAX_LEN);
|
||||
media_dir[MEDIADIR_MAX_LEN-1] = '\0';
|
||||
usleep(1);
|
||||
enum media_types type = ALL_MEDIA;
|
||||
char * myval = NULL;
|
||||
switch( ary_options[i].value[0] )
|
||||
{
|
||||
case 'A':
|
||||
case 'a':
|
||||
if( ary_options[i].value[0] == 'A' || ary_options[i].value[0] == 'a' )
|
||||
type = AUDIO_ONLY;
|
||||
case 'V':
|
||||
case 'v':
|
||||
if( ary_options[i].value[0] == 'V' || ary_options[i].value[0] == 'v' )
|
||||
type = VIDEO_ONLY;
|
||||
case 'P':
|
||||
case 'p':
|
||||
if( ary_options[i].value[0] == 'P' || ary_options[i].value[0] == 'p' )
|
||||
type = IMAGES_ONLY;
|
||||
myval = index(ary_options[i].value, '/');
|
||||
case '/':
|
||||
usleep(1);
|
||||
char * path = realpath(myval ? myval:ary_options[i].value, NULL);
|
||||
if( access(path, F_OK) != 0 )
|
||||
{
|
||||
fprintf(stderr, "Media directory not accessible! [%s]\n",
|
||||
path);
|
||||
free(path);
|
||||
break;
|
||||
}
|
||||
struct media_dir_s * this_dir = calloc(1, sizeof(struct media_dir_s));
|
||||
this_dir->path = path;
|
||||
this_dir->type = type;
|
||||
if( !media_dirs )
|
||||
{
|
||||
media_dirs = this_dir;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct media_dir_s * all_dirs = media_dirs;
|
||||
while( all_dirs->next )
|
||||
all_dirs = all_dirs->next;
|
||||
all_dirs->next = this_dir;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Media directory entry not understood! [%s]\n",
|
||||
ary_options[i].value);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown option in file %s\n",
|
||||
@ -358,7 +391,7 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
{
|
||||
case 't':
|
||||
if(i+1 < argc)
|
||||
v->notify_interval = atoi(argv[++i]);
|
||||
runtime_vars.notify_interval = atoi(argv[++i]);
|
||||
else
|
||||
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
||||
break;
|
||||
@ -392,7 +425,7 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
break;
|
||||
case 'p':
|
||||
if(i+1 < argc)
|
||||
v->port = atoi(argv[++i]);
|
||||
runtime_vars.port = atoi(argv[++i]);
|
||||
else
|
||||
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
||||
break;
|
||||
@ -417,22 +450,19 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
int address_already_there = 0;
|
||||
int j;
|
||||
i++;
|
||||
for(j=0; j<n_lan_addr; j++)/* for(j=0; j<v->n_lan_addr; j++)*/
|
||||
for(j=0; j<n_lan_addr; j++)
|
||||
{
|
||||
struct lan_addr_s tmpaddr;
|
||||
parselanaddr(&tmpaddr, argv[i]);
|
||||
/*if(0 == strcmp(v->lan_addr[j].str, tmpaddr.str))*/
|
||||
if(0 == strcmp(lan_addr[j].str, tmpaddr.str))
|
||||
address_already_there = 1;
|
||||
}
|
||||
if(address_already_there)
|
||||
break;
|
||||
if(n_lan_addr < MAX_LAN_ADDR) /*if(v->n_lan_addr < MAX_LAN_ADDR)*/
|
||||
if(n_lan_addr < MAX_LAN_ADDR)
|
||||
{
|
||||
/*v->lan_addr[v->n_lan_addr++] = argv[i];*/
|
||||
/*if(parselanaddr(&v->lan_addr[v->n_lan_addr], argv[i]) == 0)*/
|
||||
if(parselanaddr(&lan_addr[n_lan_addr], argv[i]) == 0)
|
||||
n_lan_addr++; /*v->n_lan_addr++;*/
|
||||
n_lan_addr++;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -450,7 +480,7 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
fprintf(stderr, "Unknown option: %s\n", argv[i]);
|
||||
}
|
||||
}
|
||||
if( (/*v->*/n_lan_addr==0) || (v->port<=0) )
|
||||
if( (n_lan_addr==0) || (runtime_vars.port<=0) )
|
||||
{
|
||||
fprintf(stderr, "Usage:\n\t"
|
||||
"%s [-f config_file] [-i ext_ifname] [-o ext_ip]\n"
|
||||
@ -521,8 +551,6 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
{
|
||||
snprintf(presentationurl, PRESENTATIONURL_MAX_LEN,
|
||||
"http://%s/", lan_addr[0].str);
|
||||
/*"http://%s:%d/", lan_addr[0].str, 80);*/
|
||||
/*"http://%s:%d/", v->lan_addr[0].str, 80);*/
|
||||
}
|
||||
|
||||
/* set signal handler */
|
||||
@ -566,19 +594,36 @@ main(int argc, char * * argv)
|
||||
#endif
|
||||
struct timeval timeout, timeofday, lasttimeofday = {0, 0};
|
||||
int max_fd = -1;
|
||||
struct runtime_vars v;
|
||||
|
||||
if(init(argc, argv, &v) != 0)
|
||||
if(init(argc, argv) != 0)
|
||||
return 1;
|
||||
|
||||
LIST_INIT(&upnphttphead);
|
||||
|
||||
if( access(DB_PATH, F_OK) )
|
||||
{
|
||||
struct media_dir_s * media_path = media_dirs;
|
||||
sqlite3_open(DB_PATH, &db);
|
||||
freopen("/dev/null", "a", stderr);
|
||||
ScanDirectory(media_dir, NULL);
|
||||
if( CreateDatabase() != 0 )
|
||||
{
|
||||
fprintf(stderr, "Error creating database!\n");
|
||||
return -1;
|
||||
}
|
||||
#if USE_FORK
|
||||
pid_t newpid = fork();
|
||||
if( newpid )
|
||||
goto fork_done;
|
||||
#endif
|
||||
while( media_path )
|
||||
{
|
||||
ScanDirectory(media_path->path, NULL, media_path->type);
|
||||
media_path = media_path->next;
|
||||
}
|
||||
freopen("/proc/self/fd/2", "a", stderr);
|
||||
#if USE_FORK
|
||||
_exit(0);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -588,21 +633,40 @@ main(int argc, char * * argv)
|
||||
if( sqlite3_get_table(db, "pragma user_version", &result, &rows, 0, 0) == SQLITE_OK )
|
||||
{
|
||||
if( atoi(result[1]) != DB_VERSION ) {
|
||||
struct media_dir_s * media_path = media_dirs;
|
||||
printf("Database version mismatch; need to recreate...\n");
|
||||
sqlite3_close(db);
|
||||
unlink(DB_PATH);
|
||||
sqlite3_open(DB_PATH, &db);
|
||||
freopen("/dev/null", "a", stderr);
|
||||
ScanDirectory(media_dir, NULL);
|
||||
if( CreateDatabase() != 0 )
|
||||
{
|
||||
fprintf(stderr, "Error creating database!\n");
|
||||
return -1;
|
||||
}
|
||||
#if USE_FORK
|
||||
pid_t newpid = fork();
|
||||
if( newpid )
|
||||
goto fork_done;
|
||||
#endif
|
||||
while( media_path )
|
||||
{
|
||||
ScanDirectory(media_path->path, NULL, media_path->type);
|
||||
media_path = media_path->next;
|
||||
}
|
||||
ScanDirectory(media_dirs->path, NULL, media_dirs->type);
|
||||
freopen("/proc/self/fd/2", "a", stderr);
|
||||
#if USE_FORK
|
||||
_exit(0);
|
||||
#endif
|
||||
}
|
||||
sqlite3_free_table(result);
|
||||
}
|
||||
}
|
||||
#if USE_FORK
|
||||
fork_done:
|
||||
#endif
|
||||
|
||||
|
||||
/* open socket for SSDP connections */
|
||||
/*sudp = OpenAndConfSSDPReceiveSocket(v.n_lan_addr, v.lan_addr);*/
|
||||
sudp = OpenAndConfSSDPReceiveSocket(n_lan_addr, lan_addr);
|
||||
if(sudp < 0)
|
||||
{
|
||||
@ -610,13 +674,13 @@ main(int argc, char * * argv)
|
||||
return 1;
|
||||
}
|
||||
/* open socket for HTTP connections. Listen on the 1st LAN address */
|
||||
shttpl = OpenAndConfHTTPSocket(v.port);
|
||||
shttpl = OpenAndConfHTTPSocket(runtime_vars.port);
|
||||
if(shttpl < 0)
|
||||
{
|
||||
syslog(LOG_ERR, "Failed to open socket for HTTP. EXITING");
|
||||
return 1;
|
||||
}
|
||||
syslog(LOG_NOTICE, "HTTP listening on port %d", v.port);
|
||||
syslog(LOG_NOTICE, "HTTP listening on port %d", runtime_vars.port);
|
||||
|
||||
/* open socket for sending notifications */
|
||||
if(OpenAndConfSSDPNotifySockets(snotify) < 0)
|
||||
@ -636,24 +700,24 @@ main(int argc, char * * argv)
|
||||
if(gettimeofday(&timeofday, 0) < 0)
|
||||
{
|
||||
syslog(LOG_ERR, "gettimeofday(): %m");
|
||||
timeout.tv_sec = v.notify_interval;
|
||||
timeout.tv_sec = runtime_vars.notify_interval;
|
||||
timeout.tv_usec = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* the comparaison is not very precise but who cares ? */
|
||||
if(timeofday.tv_sec >= (lasttimeofday.tv_sec + v.notify_interval))
|
||||
if(timeofday.tv_sec >= (lasttimeofday.tv_sec + runtime_vars.notify_interval))
|
||||
{
|
||||
SendSSDPNotifies2(snotify,
|
||||
(unsigned short)v.port,
|
||||
(v.notify_interval << 1)+10);
|
||||
(unsigned short)runtime_vars.port,
|
||||
(runtime_vars.notify_interval << 1)+10);
|
||||
memcpy(&lasttimeofday, &timeofday, sizeof(struct timeval));
|
||||
timeout.tv_sec = v.notify_interval;
|
||||
timeout.tv_sec = runtime_vars.notify_interval;
|
||||
timeout.tv_usec = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
timeout.tv_sec = lasttimeofday.tv_sec + v.notify_interval
|
||||
timeout.tv_sec = lasttimeofday.tv_sec + runtime_vars.notify_interval
|
||||
- timeofday.tv_sec;
|
||||
if(timeofday.tv_usec > lasttimeofday.tv_usec)
|
||||
{
|
||||
@ -724,8 +788,7 @@ main(int argc, char * * argv)
|
||||
if(sudp >= 0 && FD_ISSET(sudp, &readset))
|
||||
{
|
||||
/*syslog(LOG_INFO, "Received UDP Packet");*/
|
||||
/*ProcessSSDPRequest(sudp, v.lan_addr, v.n_lan_addr,*/
|
||||
ProcessSSDPRequest(sudp, (unsigned short)v.port);
|
||||
ProcessSSDPRequest(sudp, (unsigned short)runtime_vars.port);
|
||||
}
|
||||
/* process active HTTP connections */
|
||||
/* LIST_FOREACH macro is not available under linux */
|
||||
@ -802,7 +865,7 @@ shutdown:
|
||||
{
|
||||
syslog(LOG_ERR, "Failed to broadcast good-bye notifications");
|
||||
}
|
||||
for(i=0; i<n_lan_addr; i++)/* for(i=0; i<v.n_lan_addr; i++)*/
|
||||
for(i=0; i<n_lan_addr; i++)
|
||||
close(snotify[i]);
|
||||
|
||||
sqlite3_close(db);
|
||||
|
@ -4,6 +4,13 @@ port=5555
|
||||
# enable UPNP support (default is yes)
|
||||
enable_upnp=yes
|
||||
|
||||
# set this to the directory you want scanned.
|
||||
# * if have multiple directories, you can have multiple media_dir= lines
|
||||
# * if you want to restrict a media_dir to a specific content type, you
|
||||
# can prepend the type, followed by a comma, to the directory:
|
||||
# + "A" for audio (eg. media_dir=A,/home/jmaggard/Music)
|
||||
# + "V" for video (eg. media_dir=V,/home/jmaggard/Videos)
|
||||
# + "P" for images (eg. media_dir=P,/home/jmaggard/Pictures)
|
||||
media_dir=/opt
|
||||
|
||||
# set this if you want to customize the name that shows up on your clients
|
||||
|
@ -16,4 +16,22 @@ struct lan_addr_s {
|
||||
struct in_addr addr, mask; /* ip/mask */
|
||||
};
|
||||
|
||||
struct runtime_vars_s {
|
||||
int port; /* HTTP Port */
|
||||
int notify_interval; /* seconds between SSDP announces */
|
||||
};
|
||||
|
||||
enum media_types {
|
||||
ALL_MEDIA,
|
||||
AUDIO_ONLY,
|
||||
VIDEO_ONLY,
|
||||
IMAGES_ONLY
|
||||
};
|
||||
|
||||
struct media_dir_s {
|
||||
char * path; /* Base path */
|
||||
enum media_types type; /* type of files to scan */
|
||||
struct media_dir_s * next;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
148
scanner.c
148
scanner.c
@ -57,6 +57,26 @@ is_image(const char * file)
|
||||
return (ends_with(file, ".jpg") || ends_with(file, ".jpeg"));
|
||||
}
|
||||
|
||||
sqlite_int64
|
||||
get_next_available_id(const char * table, const char * parentID)
|
||||
{
|
||||
char * sql;
|
||||
char **result;
|
||||
int ret;
|
||||
sqlite_int64 objectID = 0;
|
||||
|
||||
asprintf(&sql, "SELECT OBJECT_ID, max(ID) from %s where PARENT_ID = '%s'", table, parentID);
|
||||
ret = sql_get_table(db, sql, &result, NULL, NULL);
|
||||
if( result[2] && (sscanf(rindex(result[2], '$')+1, "%llX", &objectID) == 1) )
|
||||
{
|
||||
objectID++;
|
||||
}
|
||||
sqlite3_free_table(result);
|
||||
free(sql);
|
||||
|
||||
return objectID;
|
||||
}
|
||||
|
||||
long long int
|
||||
insert_container(const char * tmpTable, const char * item, const char * rootParent, const char *subParent,
|
||||
const char *class, const char *artist, const char *genre, const char *album_art)
|
||||
@ -73,29 +93,13 @@ insert_container(const char * tmpTable, const char * item, const char * rootPare
|
||||
if( cols )
|
||||
{
|
||||
sscanf(result[4], "%X", &parentID);
|
||||
sqlite3_free_table(result);
|
||||
sql = sqlite3_mprintf("SELECT OBJECT_ID, max(ID) from OBJECTS where PARENT_ID = '%s$%X'", rootParent, parentID);
|
||||
ret = sql_get_table(db, sql, &result, 0, &cols);
|
||||
sqlite3_free(sql);
|
||||
if( result[2] && (sscanf(rindex(result[2], '$')+1, "%X", &objectID) == 1) )
|
||||
{
|
||||
objectID++;
|
||||
}
|
||||
asprintf(&sql, "%s$%X", rootParent, parentID);
|
||||
objectID = get_next_available_id("OBJECTS", sql);
|
||||
free(sql);
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3_free_table(result);
|
||||
sql = sqlite3_mprintf("SELECT OBJECT_ID, max(ID) from OBJECTS where PARENT_ID = '%s'", rootParent);
|
||||
sql_get_table(db, sql, &result, &rows, &cols);
|
||||
sqlite3_free(sql);
|
||||
if( result[2] && (sscanf(rindex(result[2], '$')+1, "%X", &parentID) == 1) )
|
||||
{
|
||||
parentID++;
|
||||
}
|
||||
else
|
||||
{
|
||||
parentID = 0;
|
||||
}
|
||||
parentID = get_next_available_id("OBJECTS", rootParent);
|
||||
detailID = GetFolderMetadata(item, artist, genre, album_art);
|
||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||
" (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME) "
|
||||
@ -301,13 +305,14 @@ insert_directory(const char * name, const char * path, const char * base, const
|
||||
char * id_buf = NULL;
|
||||
char * parent_buf = NULL;
|
||||
char **result;
|
||||
char *dir = strdup(path);
|
||||
char *dir = NULL;
|
||||
|
||||
if( strcmp(base, BROWSEDIR_ID) != 0 )
|
||||
asprintf(&refID, "%s%s$%X", BROWSEDIR_ID, parentID, objectID);
|
||||
|
||||
if( refID )
|
||||
{
|
||||
dir = strdup(path);
|
||||
dir = dirname(dir);
|
||||
asprintf(&id_buf, "%s%s$%X", base, parentID, objectID);
|
||||
asprintf(&parent_buf, "%s%s", base, parentID);
|
||||
@ -315,19 +320,28 @@ insert_directory(const char * name, const char * path, const char * base, const
|
||||
{
|
||||
sql = sqlite3_mprintf("SELECT count(OBJECT_ID) from OBJECTS where OBJECT_ID = '%s'", id_buf);
|
||||
if( (sql_get_table(db, sql, &result, NULL, NULL) == SQLITE_OK) && atoi(result[1]) )
|
||||
{
|
||||
sqlite3_free_table(result);
|
||||
sqlite3_free(sql);
|
||||
break;
|
||||
}
|
||||
sqlite3_free_table(result);
|
||||
sqlite3_free(sql);
|
||||
/* Does not exist. Need to create, and may need to create parents also */
|
||||
sql = sqlite3_mprintf("SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s'", refID);
|
||||
if( (sql_get_table(db, sql, &result, NULL, NULL) == SQLITE_OK) && atoi(result[1]) )
|
||||
{
|
||||
detailID = atoi(result[1]);
|
||||
}
|
||||
sqlite3_free_table(result);
|
||||
sqlite3_free(sql);
|
||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
|
||||
"VALUES"
|
||||
" ('%s', '%s', %Q, '%lld', '%s', '%q')",
|
||||
id_buf, parent_buf, refID, detailID, class, rindex(dir, '/')+1);
|
||||
sql_exec(db, sql);
|
||||
sqlite3_free(sql);
|
||||
if( rindex(id_buf, '$') )
|
||||
*rindex(id_buf, '$') = '\0';
|
||||
if( rindex(parent_buf, '$') )
|
||||
@ -336,8 +350,10 @@ insert_directory(const char * name, const char * path, const char * base, const
|
||||
*rindex(refID, '$') = '\0';
|
||||
dir = dirname(dir);
|
||||
}
|
||||
sqlite3_free(sql);
|
||||
free(refID);
|
||||
free(parent_buf);
|
||||
free(id_buf);
|
||||
free(dir);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -365,16 +381,12 @@ insert_file(char * name, const char * path, const char * parentID, int object)
|
||||
unsigned long int detailID = 0;
|
||||
char base[8];
|
||||
char * typedir_parentID;
|
||||
int typedir_objectID;
|
||||
int typedir_objectID = 0;
|
||||
char * baseid;
|
||||
|
||||
static long unsigned int fileno = 0;
|
||||
printf("Scanned %lu files...\r", fileno++); fflush(stdout);
|
||||
|
||||
sprintf(objectID, "%s%s$%X", BROWSEDIR_ID, parentID, object);
|
||||
typedir_parentID = strdup(parentID);
|
||||
sscanf(rindex(typedir_parentID, '$')+1, "%X", &typedir_objectID);
|
||||
*rindex(typedir_parentID, '$') = '\0';
|
||||
|
||||
if( is_image(name) )
|
||||
{
|
||||
strcpy(base, IMAGE_DIR_ID);
|
||||
@ -397,6 +409,15 @@ insert_file(char * name, const char * path, const char * parentID, int object)
|
||||
if( !detailID )
|
||||
return -1;
|
||||
|
||||
sprintf(objectID, "%s%s$%X", BROWSEDIR_ID, parentID, object);
|
||||
typedir_parentID = strdup(parentID);
|
||||
baseid = rindex(typedir_parentID, '$');
|
||||
if( baseid )
|
||||
{
|
||||
sscanf(baseid+1, "%X", &typedir_objectID);
|
||||
*baseid = '\0';
|
||||
}
|
||||
|
||||
insert_directory(name, path, base, typedir_parentID, typedir_objectID);
|
||||
|
||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||
@ -406,6 +427,7 @@ insert_file(char * name, const char * path, const char * parentID, int object)
|
||||
objectID, BROWSEDIR_ID, parentID, class, detailID, name);
|
||||
//DEBUG printf("SQL: %s\n", sql);
|
||||
sql_exec(db, sql);
|
||||
sqlite3_free(sql);
|
||||
|
||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
||||
@ -416,12 +438,14 @@ insert_file(char * name, const char * path, const char * parentID, int object)
|
||||
sql_exec(db, sql);
|
||||
sqlite3_free(sql);
|
||||
|
||||
free(typedir_parentID);
|
||||
|
||||
insert_containers(name, path, objectID, class, detailID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
create_database(void)
|
||||
CreateDatabase(void)
|
||||
{
|
||||
int ret, i;
|
||||
char sql_buf[512];
|
||||
@ -569,42 +593,52 @@ filter_media(const struct dirent *d)
|
||||
}
|
||||
|
||||
void
|
||||
ScanDirectory(const char * dir, const char * parent)
|
||||
ScanDirectory(const char * dir, const char * parent, enum media_types type)
|
||||
{
|
||||
struct dirent **namelist;
|
||||
int n, i;
|
||||
int n, i, startID = 0;
|
||||
char parent_id[PATH_MAX];
|
||||
char full_path[PATH_MAX];
|
||||
char * name;
|
||||
#if USE_FORK
|
||||
pid_t newpid;
|
||||
#endif
|
||||
|
||||
if( !parent )
|
||||
{
|
||||
if( create_database() != 0 )
|
||||
{
|
||||
fprintf(stderr, "Error creating database!\n");
|
||||
return;
|
||||
}
|
||||
#if USE_FORK
|
||||
newpid = fork();
|
||||
if( newpid )
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
char * name = NULL;
|
||||
|
||||
setlocale(LC_COLLATE, "");
|
||||
if( chdir(dir) != 0 )
|
||||
return;
|
||||
|
||||
printf("\nScanning %s\n", dir);
|
||||
switch( type )
|
||||
{
|
||||
case ALL_MEDIA:
|
||||
n = scandir(".", &namelist, filter_media, alphasort);
|
||||
break;
|
||||
case AUDIO_ONLY:
|
||||
n = scandir(".", &namelist, filter_audio, alphasort);
|
||||
break;
|
||||
case VIDEO_ONLY:
|
||||
n = scandir(".", &namelist, filter_video, alphasort);
|
||||
break;
|
||||
case IMAGES_ONLY:
|
||||
n = scandir(".", &namelist, filter_images, alphasort);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (n < 0) {
|
||||
fprintf(stderr, "Error scanning %s [scandir]\n", dir);
|
||||
return;
|
||||
}
|
||||
|
||||
/* sql = sqlite3_mprintf("SELECT OBJECT_ID, max(ID) from OBJECTS where PARENT_ID = '%s$%X'", rootParent, parentID);
|
||||
ret = sql_get_table(db, sql, &result, 0, &cols);
|
||||
if( result[2] && (sscanf(rindex(result[2], '$')+1, "%X", &objectID) == 1) )
|
||||
{
|
||||
objectID++;
|
||||
}
|
||||
*/
|
||||
if( !parent )
|
||||
startID = get_next_available_id("OBJECTS", BROWSEDIR_ID);
|
||||
|
||||
for (i=0; i < n; i++) {
|
||||
name = NULL;
|
||||
sprintf(full_path, "%s/%s", dir, namelist[i]->d_name);
|
||||
if( index(namelist[i]->d_name, '&') )
|
||||
{
|
||||
@ -612,16 +646,19 @@ ScanDirectory(const char * dir, const char * parent)
|
||||
}
|
||||
if( namelist[i]->d_type == DT_DIR )
|
||||
{
|
||||
insert_directory(name?name:namelist[i]->d_name, full_path, BROWSEDIR_ID, (parent ? parent:""), i);
|
||||
sprintf(parent_id, "%s$%X", (parent ? parent:""), i);
|
||||
ScanDirectory(full_path, parent_id);
|
||||
insert_directory(name?name:namelist[i]->d_name, full_path, BROWSEDIR_ID, (parent ? parent:""), i+startID);
|
||||
sprintf(parent_id, "%s$%X", (parent ? parent:""), i+startID);
|
||||
ScanDirectory(full_path, parent_id, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
insert_file(name?name:namelist[i]->d_name, full_path, (parent ? parent:""), i);
|
||||
insert_file(name?name:namelist[i]->d_name, full_path, (parent ? parent:""), i+startID);
|
||||
}
|
||||
if( name )
|
||||
{
|
||||
free(name);
|
||||
name = NULL;
|
||||
}
|
||||
free(namelist[i]);
|
||||
}
|
||||
free(namelist);
|
||||
@ -632,8 +669,5 @@ ScanDirectory(const char * dir, const char * parent)
|
||||
else
|
||||
{
|
||||
printf("Scanning \"%s\" finished!\n", dir);
|
||||
#if USE_FORK
|
||||
_exit(0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,10 @@
|
||||
#define VIDEO_DIR_ID "2$21"
|
||||
#define IMAGE_DIR_ID "3$22"
|
||||
|
||||
int
|
||||
CreateDatabase(void);
|
||||
|
||||
void
|
||||
ScanDirectory(const char * dir, const char * parent);
|
||||
ScanDirectory(const char * dir, const char * parent, enum media_types type);
|
||||
|
||||
#endif
|
||||
|
@ -26,6 +26,7 @@ int logpackets = 0;
|
||||
|
||||
#endif
|
||||
|
||||
struct runtime_vars_s runtime_vars;
|
||||
int runtime_flags = 0;
|
||||
|
||||
const char * pidfilename = "/var/run/minidlna.pid";
|
||||
@ -44,5 +45,5 @@ struct lan_addr_s lan_addr[MAX_LAN_ADDR];
|
||||
|
||||
/* UPnP-A/V [DLNA] */
|
||||
sqlite3 *db;
|
||||
char media_dir[MEDIADIR_MAX_LEN];
|
||||
struct media_dir_s * media_dirs = NULL;
|
||||
char friendly_name[FRIENDLYNAME_MAX_LEN];
|
||||
|
@ -47,6 +47,7 @@
|
||||
/* statup time */
|
||||
extern time_t startup_time;
|
||||
|
||||
extern struct runtime_vars_s runtime_vars;
|
||||
/* runtime boolean flags */
|
||||
extern int runtime_flags;
|
||||
#define LOGPACKETSMASK 0x0001
|
||||
@ -81,7 +82,7 @@ extern struct lan_addr_s lan_addr[];
|
||||
/* UPnP-A/V [DLNA] */
|
||||
extern sqlite3 *db;
|
||||
#define MEDIADIR_MAX_LEN (256)
|
||||
extern char media_dir[];
|
||||
extern struct media_dir_s * media_dirs;
|
||||
#define FRIENDLYNAME_MAX_LEN (64)
|
||||
extern char friendly_name[];
|
||||
|
||||
|
10
upnphttp.c
10
upnphttp.c
@ -1178,6 +1178,9 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
|
||||
int sendfh;
|
||||
#if USE_FORK
|
||||
pid_t newpid = 0;
|
||||
newpid = fork();
|
||||
if( newpid )
|
||||
return;
|
||||
#endif
|
||||
|
||||
memset(header, 0, 1500);
|
||||
@ -1192,15 +1195,10 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
|
||||
sqlite3_free_table(result);
|
||||
return;
|
||||
}
|
||||
#if USE_FORK
|
||||
newpid = fork();
|
||||
if( newpid )
|
||||
return;
|
||||
#endif
|
||||
|
||||
path = result[3];
|
||||
mime = result[4];
|
||||
dlna = result[5];
|
||||
|
||||
printf("Serving DetailID: %s [%s]\n", object, path);
|
||||
|
||||
if( h->reqflags & FLAG_XFERSTREAMING )
|
||||
|
18
upnpsoap.c
18
upnpsoap.c
@ -270,10 +270,10 @@ static int callback(void *args, int argc, char **argv, char **azColName)
|
||||
}
|
||||
if( album_art && atoi(album_art) && (!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI")) ) {
|
||||
sprintf(str_buf, "<upnp:albumArtURI %s"
|
||||
">http://%s:5555/AlbumArt/%s.jpg</upnp:albumArtURI>",
|
||||
">http://%s:%d/AlbumArt/%s.jpg</upnp:albumArtURI>",
|
||||
(!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI@dlna:profileID")) ?
|
||||
"dlna:profileID=\"JPEG_TN\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\"" : "",
|
||||
lan_addr[0].str, album_art);
|
||||
lan_addr[0].str, runtime_vars.port, album_art);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( !passed_args->filter || strstr(passed_args->filter, "res") ) {
|
||||
@ -303,26 +303,26 @@ static int callback(void *args, int argc, char **argv, char **azColName)
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\">"
|
||||
"http://%s:5555/MediaItems/%s.dat"
|
||||
"http://%s:%d/MediaItems/%s.dat"
|
||||
"</res>",
|
||||
mime, dlna_buf, lan_addr[0].str, detailID);
|
||||
mime, dlna_buf, lan_addr[0].str, runtime_vars.port, detailID);
|
||||
#if 0 //JPEG_RESIZE
|
||||
if( dlna_pn && (strncmp(dlna_pn, "JPEG_LRG", 8) == 0) ) {
|
||||
strcat(passed_args->resp, str_buf);
|
||||
sprintf(str_buf, "<res "
|
||||
"protocolInfo=\"http-get:*:%s:%s\">"
|
||||
"http://%s:5555/Resized/%s"
|
||||
"http://%s:%d/Resized/%s"
|
||||
"</res>",
|
||||
mime, "DLNA.ORG_PN=JPEG_SM", lan_addr[0].str, id);
|
||||
mime, "DLNA.ORG_PN=JPEG_SM", lan_addr[0].str, runtime_vars.port, id);
|
||||
}
|
||||
#endif
|
||||
if( tn && atoi(tn) && dlna_pn ) {
|
||||
strcat(passed_args->resp, str_buf);
|
||||
strcat(passed_args->resp, "<res ");
|
||||
sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\">"
|
||||
"http://%s:5555/Thumbnails/%s.dat"
|
||||
"http://%s:%d/Thumbnails/%s.dat"
|
||||
"</res>",
|
||||
mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, detailID);
|
||||
mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, runtime_vars.port, detailID);
|
||||
}
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
@ -369,7 +369,7 @@ static int callback(void *args, int argc, char **argv, char **azColName)
|
||||
}
|
||||
if( album_art && atoi(album_art) && (!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI")) ) {
|
||||
sprintf(str_buf, "<upnp:albumArtURI dlna:profileID=\"JPEG_TN\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\""
|
||||
">http://%s:5555/AlbumArt/%s.jpg</upnp:albumArtURI>", lan_addr[0].str, album_art);
|
||||
">http://%s:%d/AlbumArt/%s.jpg</upnp:albumArtURI>", lan_addr[0].str, runtime_vars.port, album_art);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
sprintf(str_buf, "</container>");
|
||||
|
Loading…
x
Reference in New Issue
Block a user