1 /* MiniDLNA media server
2 * Copyright (C) 2008-2009 Justin Maggard
4 * This file is part of MiniDLNA.
6 * MiniDLNA is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * MiniDLNA is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
28 #include <sys/resource.h>
37 #include "upnpglobalvars.h"
50 sqlite3_int64 objectID
;
56 get_next_available_id(const char * table
, const char * parentID
)
59 sqlite_int64 objectID
= 0;
61 ret
= sql_get_text_field(db
, "SELECT OBJECT_ID from %s where ID = "
62 "(SELECT max(ID) from %s where PARENT_ID = '%s')",
63 table
, table
, parentID
);
66 base
= strrchr(ret
, '$');
68 objectID
= strtoll(base
+1, NULL
, 16) + 1;
76 insert_container(const char * item
, const char * rootParent
, const char * refID
, const char *class,
77 const char *artist
, const char *genre
, const char *album_art
, sqlite3_int64
*objectID
, sqlite3_int64
*parentID
)
82 sqlite_int64 detailID
= 0;
84 result
= sql_get_text_field(db
, "SELECT OBJECT_ID from OBJECTS"
85 " where PARENT_ID = '%s'"
87 " and CLASS = 'container.%s' limit 1",
88 rootParent
, item
, class);
91 base
= strrchr(result
, '$');
93 *parentID
= strtoll(base
+1, NULL
, 16);
96 *objectID
= get_next_available_id("OBJECTS", result
);
101 *parentID
= get_next_available_id("OBJECTS", rootParent
);
104 result
= sql_get_text_field(db
, "SELECT DETAIL_ID from OBJECTS where OBJECT_ID = %Q", refID
);
106 detailID
= strtoll(result
, NULL
, 10);
110 detailID
= GetFolderMetadata(item
, NULL
, artist
, genre
, (album_art
? strtoll(album_art
, NULL
, 10) : 0));
112 ret
= sql_exec(db
, "INSERT into OBJECTS"
113 " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
115 " ('%s$%"PRIX64
"', '%s', %Q, %"PRId64
", 'container.%s', '%q')",
116 rootParent
, *parentID
, rootParent
, refID
, detailID
, class, item
);
118 sqlite3_free(result
);
124 insert_containers(const char * name
, const char *path
, const char * refID
, const char * class, sqlite3_int64 detailID
)
130 sqlite_int64 objectID
, parentID
;
132 if( strstr(class, "imageItem") )
134 char *date
= NULL
, *cam
= NULL
;
135 char date_taken
[13], camera
[64];
136 static struct virtual_item last_date
;
137 static struct virtual_item last_cam
;
138 static struct virtual_item last_camdate
;
139 static sqlite_int64 last_all_objectID
= 0;
141 asprintf(&sql
, "SELECT DATE, CREATOR from DETAILS where ID = %"PRId64
, detailID
);
142 ret
= sql_get_table(db
, sql
, &result
, &row
, &cols
);
144 if( ret
== SQLITE_OK
)
152 strncpy(date_taken
, date
, 10);
153 date_taken
[10] = '\0';
157 strcpy(date_taken
, _("Unknown Date"));
159 if( valid_cache
&& strcmp(last_date
.name
, date_taken
) == 0 )
161 last_date
.objectID
++;
162 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last date item: %s/%s/%X\n", last_date.name, last_date.parentID, last_date.objectID);
166 insert_container(date_taken
, IMAGE_DATE_ID
, NULL
, "album.photoAlbum", NULL
, NULL
, NULL
, &objectID
, &parentID
);
167 sprintf(last_date
.parentID
, IMAGE_DATE_ID
"$%"PRIX64
, parentID
);
168 last_date
.objectID
= objectID
;
169 strcpy(last_date
.name
, date_taken
);
170 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached date item: %s/%s/%X\n", last_date.name, last_date.parentID, last_date.objectID);
172 sql_exec(db
, "INSERT into OBJECTS"
173 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
175 " ('%s$%"PRIX64
"', '%s', '%s', '%s', %"PRId64
", %Q)",
176 last_date
.parentID
, last_date
.objectID
, last_date
.parentID
, refID
, class, detailID
, name
);
180 strncpy(camera
, cam
, 63);
185 strcpy(camera
, _("Unknown Camera"));
187 if( !valid_cache
|| strcmp(camera
, last_cam
.name
) != 0 )
189 insert_container(camera
, IMAGE_CAMERA_ID
, NULL
, "storageFolder", NULL
, NULL
, NULL
, &objectID
, &parentID
);
190 sprintf(last_cam
.parentID
, IMAGE_CAMERA_ID
"$%"PRIX64
, parentID
);
191 strncpy(last_cam
.name
, camera
, 255);
192 last_camdate
.name
[0] = '\0';
194 if( valid_cache
&& strcmp(last_camdate
.name
, date_taken
) == 0 )
196 last_camdate
.objectID
++;
197 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last camdate item: %s/%s/%s/%X\n", camera, last_camdate.name, last_camdate.parentID, last_camdate.objectID);
201 insert_container(date_taken
, last_cam
.parentID
, NULL
, "album.photoAlbum", NULL
, NULL
, NULL
, &objectID
, &parentID
);
202 sprintf(last_camdate
.parentID
, "%s$%"PRIX64
, last_cam
.parentID
, parentID
);
203 last_camdate
.objectID
= objectID
;
204 strcpy(last_camdate
.name
, date_taken
);
205 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached camdate item: %s/%s/%s/%X\n", camera, last_camdate.name, last_camdate.parentID, last_camdate.objectID);
207 sql_exec(db
, "INSERT into OBJECTS"
208 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
210 " ('%s$%"PRIX64
"', '%s', '%s', '%s', %"PRId64
", %Q)",
211 last_camdate
.parentID
, last_camdate
.objectID
, last_camdate
.parentID
, refID
, class, detailID
, name
);
213 if( !last_all_objectID
)
215 last_all_objectID
= get_next_available_id("OBJECTS", IMAGE_ALL_ID
);
217 sql_exec(db
, "INSERT into OBJECTS"
218 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
220 " ('"IMAGE_ALL_ID
"$%"PRIX64
"', '"IMAGE_ALL_ID
"', '%s', '%s', %"PRId64
", %Q)",
221 last_all_objectID
++, refID
, class, detailID
, name
);
223 else if( strstr(class, "audioItem") )
225 asprintf(&sql
, "SELECT ALBUM, ARTIST, GENRE, ALBUM_ART from DETAILS where ID = %"PRId64
, detailID
);
226 ret
= sql_get_table(db
, sql
, &result
, &row
, &cols
);
228 if( ret
!= SQLITE_OK
)
232 sqlite3_free_table(result
);
235 char *album
= result
[4], *artist
= result
[5], *genre
= result
[6];
236 char *album_art
= result
[7];
237 static struct virtual_item last_album
;
238 static struct virtual_item last_artist
;
239 static struct virtual_item last_artistAlbum
;
240 static struct virtual_item last_artistAlbumAll
;
241 static struct virtual_item last_genre
;
242 static struct virtual_item last_genreArtist
;
243 static struct virtual_item last_genreArtistAll
;
244 static sqlite_int64 last_all_objectID
= 0;
248 if( valid_cache
&& strcmp(album
, last_album
.name
) == 0 )
250 last_album
.objectID
++;
251 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last album item: %s/%s/%X\n", last_album.name, last_album.parentID, last_album.objectID);
255 strcpy(last_album
.name
, album
);
256 insert_container(album
, MUSIC_ALBUM_ID
, NULL
, "album.musicAlbum", artist
, genre
, album_art
, &objectID
, &parentID
);
257 sprintf(last_album
.parentID
, MUSIC_ALBUM_ID
"$%llX", parentID
);
258 last_album
.objectID
= objectID
;
259 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached album item: %s/%s/%X\n", last_album.name, last_album.parentID, last_album.objectID);
261 sql_exec(db
, "INSERT into OBJECTS"
262 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
264 " ('%s$%"PRIX64
"', '%s', '%s', '%s', %"PRId64
", %Q)",
265 last_album
.parentID
, last_album
.objectID
, last_album
.parentID
, refID
, class, detailID
, name
);
269 if( !valid_cache
|| strcmp(artist
, last_artist
.name
) != 0 )
271 insert_container(artist
, MUSIC_ARTIST_ID
, NULL
, "person.musicArtist", NULL
, genre
, NULL
, &objectID
, &parentID
);
272 sprintf(last_artist
.parentID
, MUSIC_ARTIST_ID
"$%"PRIX64
, parentID
);
273 strcpy(last_artist
.name
, artist
);
274 last_artistAlbum
.name
[0] = '\0';
275 /* Add this file to the "- All Albums -" container as well */
276 insert_container(_("- All Albums -"), last_artist
.parentID
, NULL
, "album", artist
, genre
, NULL
, &objectID
, &parentID
);
277 sprintf(last_artistAlbumAll
.parentID
, "%s$%"PRIX64
, last_artist
.parentID
, parentID
);
278 last_artistAlbumAll
.objectID
= objectID
;
282 last_artistAlbumAll
.objectID
++;
284 if( valid_cache
&& strcmp(album
?album
:_("Unknown Album"), last_artistAlbum
.name
) == 0 )
286 last_artistAlbum
.objectID
++;
287 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last artist/album item: %s/%s/%X\n", last_artist.name, last_artist.parentID, last_artist.objectID);
291 insert_container(album
?album
:_("Unknown Album"), last_artist
.parentID
, album
?last_album
.parentID
:NULL
,
292 "album.musicAlbum", artist
, genre
, album_art
, &objectID
, &parentID
);
293 sprintf(last_artistAlbum
.parentID
, "%s$%"PRIX64
, last_artist
.parentID
, parentID
);
294 last_artistAlbum
.objectID
= objectID
;
295 strcpy(last_artistAlbum
.name
, album
?album
:_("Unknown Album"));
296 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached artist/album item: %s/%s/%X\n", last_artist.name, last_artist.parentID, last_artist.objectID);
298 sql_exec(db
, "INSERT into OBJECTS"
299 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
301 " ('%s$%"PRIX64
"', '%s', '%s', '%s', %"PRId64
", %Q)",
302 last_artistAlbum
.parentID
, last_artistAlbum
.objectID
, last_artistAlbum
.parentID
, refID
, class, detailID
, name
);
303 sql_exec(db
, "INSERT into OBJECTS"
304 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
306 " ('%s$%"PRIX64
"', '%s', '%s', '%s', %"PRId64
", %Q)",
307 last_artistAlbumAll
.parentID
, last_artistAlbumAll
.objectID
, last_artistAlbumAll
.parentID
, refID
, class, detailID
, name
);
311 if( !valid_cache
|| strcmp(genre
, last_genre
.name
) != 0 )
313 insert_container(genre
, MUSIC_GENRE_ID
, NULL
, "genre.musicGenre", NULL
, NULL
, NULL
, &objectID
, &parentID
);
314 sprintf(last_genre
.parentID
, MUSIC_GENRE_ID
"$%"PRIX64
, parentID
);
315 strcpy(last_genre
.name
, genre
);
316 last_genreArtist
.name
[0] = '\0';
317 /* Add this file to the "- All Artists -" container as well */
318 insert_container(_("- All Artists -"), last_genre
.parentID
, NULL
, "person", NULL
, genre
, NULL
, &objectID
, &parentID
);
319 sprintf(last_genreArtistAll
.parentID
, "%s$%"PRIX64
, last_genre
.parentID
, parentID
);
320 last_genreArtistAll
.objectID
= objectID
;
324 last_genreArtistAll
.objectID
++;
326 if( valid_cache
&& strcmp(artist
?artist
:_("Unknown Artist"), last_genreArtist
.name
) == 0 )
328 last_genreArtist
.objectID
++;
332 insert_container(artist
?artist
:_("Unknown Artist"), last_genre
.parentID
, artist
?last_artist
.parentID
:NULL
,
333 "person.musicArtist", NULL
, genre
, NULL
, &objectID
, &parentID
);
334 sprintf(last_genreArtist
.parentID
, "%s$%"PRIX64
, last_genre
.parentID
, parentID
);
335 last_genreArtist
.objectID
= objectID
;
336 strcpy(last_genreArtist
.name
, artist
?artist
:_("Unknown Artist"));
337 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached genre/artist item: %s/%s/%X\n", last_genreArtist.name, last_genreArtist.parentID, last_genreArtist.objectID);
339 sql_exec(db
, "INSERT into OBJECTS"
340 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
342 " ('%s$%"PRIX64
"', '%s', '%s', '%s', %"PRId64
", %Q)",
343 last_genreArtist
.parentID
, last_genreArtist
.objectID
, last_genreArtist
.parentID
, refID
, class, detailID
, name
);
344 sql_exec(db
, "INSERT into OBJECTS"
345 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
347 " ('%s$%"PRIX64
"', '%s', '%s', '%s', %"PRId64
", %Q)",
348 last_genreArtistAll
.parentID
, last_genreArtistAll
.objectID
, last_genreArtistAll
.parentID
, refID
, class, detailID
, name
);
351 if( !last_all_objectID
)
353 last_all_objectID
= get_next_available_id("OBJECTS", MUSIC_ALL_ID
);
355 sql_exec(db
, "INSERT into OBJECTS"
356 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
358 " ('"MUSIC_ALL_ID
"$%"PRIX64
"', '"MUSIC_ALL_ID
"', '%s', '%s', %"PRId64
", %Q)",
359 last_all_objectID
++, refID
, class, detailID
, name
);
361 else if( strstr(class, "videoItem") )
363 static sqlite_int64 last_all_objectID
= 0;
366 if( !last_all_objectID
)
368 last_all_objectID
= get_next_available_id("OBJECTS", VIDEO_ALL_ID
);
370 sql_exec(db
, "INSERT into OBJECTS"
371 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
373 " ('"VIDEO_ALL_ID
"$%"PRIX64
"', '"VIDEO_ALL_ID
"', '%s', '%s', %"PRId64
", %Q)",
374 last_all_objectID
++, refID
, class, detailID
, name
);
381 sqlite3_free_table(result
);
386 insert_directory(const char * name
, const char * path
, const char * base
, const char * parentID
, int objectID
)
389 sqlite_int64 detailID
= 0;
391 char class[] = "container.storageFolder";
392 char * id_buf
= NULL
;
393 char * parent_buf
= NULL
;
396 static char last_found
[256] = "-1";
398 if( strcmp(base
, BROWSEDIR_ID
) != 0 )
399 asprintf(&refID
, "%s%s$%X", BROWSEDIR_ID
, parentID
, objectID
);
403 dir_buf
= strdup(path
);
404 dir
= dirname(dir_buf
);
405 asprintf(&id_buf
, "%s%s$%X", base
, parentID
, objectID
);
406 asprintf(&parent_buf
, "%s%s", base
, parentID
);
409 if( strcmp(id_buf
, last_found
) == 0 )
411 if( sql_get_int_field(db
, "SELECT count(*) from OBJECTS where OBJECT_ID = '%s'", id_buf
) > 0 )
413 strcpy(last_found
, id_buf
);
416 /* Does not exist. Need to create, and may need to create parents also */
417 result
= sql_get_text_field(db
, "SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s'", refID
);
420 detailID
= strtoll(result
, NULL
, 10);
421 sqlite3_free(result
);
423 sql_exec(db
, "INSERT into OBJECTS"
424 " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
426 " ('%s', '%s', %Q, %"PRId64
", '%s', '%q')",
427 id_buf
, parent_buf
, refID
, detailID
, class, strrchr(dir
, '/')+1);
428 if( (p
= strrchr(id_buf
, '$')) )
430 if( (p
= strrchr(parent_buf
, '$')) )
432 if( (p
= strrchr(refID
, '$')) )
443 detailID
= GetFolderMetadata(name
, path
, NULL
, NULL
, find_album_art(path
, NULL
, 0));
444 sql_exec(db
, "INSERT into OBJECTS"
445 " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
447 " ('%s%s$%X', '%s%s', %Q, %"PRId64
", '%s', '%q')",
448 base
, parentID
, objectID
, base
, parentID
, refID
, detailID
, class, name
);
456 insert_file(char * name
, const char * path
, const char * parentID
, int object
)
460 sqlite3_int64 detailID
= 0;
462 char * typedir_parentID
;
463 int typedir_objectID
;
465 char * orig_name
= NULL
;
469 if( is_album_art(name
) )
471 strcpy(base
, IMAGE_DIR_ID
);
472 strcpy(class, "item.imageItem.photo");
473 detailID
= GetImageMetadata(path
, name
);
475 else if( is_video(name
) )
477 orig_name
= strdup(name
);
478 strcpy(base
, VIDEO_DIR_ID
);
479 strcpy(class, "item.videoItem");
480 detailID
= GetVideoMetadata(path
, name
);
482 strcpy(name
, orig_name
);
484 else if( is_playlist(name
) )
486 if( insert_playlist(path
, name
) == 0 )
489 if( !detailID
&& is_audio(name
) )
491 strcpy(base
, MUSIC_DIR_ID
);
492 strcpy(class, "item.audioItem.musicTrack");
493 detailID
= GetAudioMetadata(path
, name
);
499 DPRINTF(E_WARN
, L_SCANNER
, "Unsuccessful getting details for %s!\n", path
);
503 sprintf(objectID
, "%s%s$%X", BROWSEDIR_ID
, parentID
, object
);
505 sql_exec(db
, "INSERT into OBJECTS"
506 " (OBJECT_ID, PARENT_ID, CLASS, DETAIL_ID, NAME) "
508 " ('%s', '%s%s', '%s', %"PRId64
", '%q')",
509 objectID
, BROWSEDIR_ID
, parentID
, class, detailID
, name
);
513 typedir_objectID
= 0;
514 typedir_parentID
= strdup(parentID
);
515 baseid
= strrchr(typedir_parentID
, '$');
518 typedir_objectID
= strtol(baseid
+1, NULL
, 16);
521 insert_directory(name
, path
, base
, typedir_parentID
, typedir_objectID
);
522 free(typedir_parentID
);
524 sql_exec(db
, "INSERT into OBJECTS"
525 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
527 " ('%s%s$%X', '%s%s', '%s', '%s', %"PRId64
", '%q')",
528 base
, parentID
, object
, base
, parentID
, objectID
, class, detailID
, name
);
530 insert_containers(name
, path
, objectID
, class, detailID
);
538 const char * containers
[] = { "0","-1", "root",
539 MUSIC_ID
, "0", _("Music"),
540 MUSIC_ALL_ID
, MUSIC_ID
, _("All Music"),
541 MUSIC_GENRE_ID
, MUSIC_ID
, _("Genre"),
542 MUSIC_ARTIST_ID
, MUSIC_ID
, _("Artist"),
543 MUSIC_ALBUM_ID
, MUSIC_ID
, _("Album"),
544 MUSIC_DIR_ID
, MUSIC_ID
, _("Folders"),
545 MUSIC_PLIST_ID
, MUSIC_ID
, _("Playlists"),
547 VIDEO_ID
, "0", _("Video"),
548 VIDEO_ALL_ID
, VIDEO_ID
, _("All Video"),
549 VIDEO_DIR_ID
, VIDEO_ID
, _("Folders"),
551 IMAGE_ID
, "0", _("Pictures"),
552 IMAGE_ALL_ID
, IMAGE_ID
, _("All Pictures"),
553 IMAGE_DATE_ID
, IMAGE_ID
, _("Date Taken"),
554 IMAGE_CAMERA_ID
, IMAGE_ID
, _("Camera"),
555 IMAGE_DIR_ID
, IMAGE_ID
, _("Folders"),
557 BROWSEDIR_ID
, "0", _("Browse Folders"),
560 ret
= sql_exec(db
, "CREATE TABLE OBJECTS ( "
561 "ID INTEGER PRIMARY KEY AUTOINCREMENT, "
562 "OBJECT_ID TEXT UNIQUE NOT NULL, "
563 "PARENT_ID TEXT NOT NULL, "
564 "REF_ID TEXT DEFAULT NULL, "
565 "CLASS TEXT NOT NULL, "
566 "DETAIL_ID INTEGER DEFAULT NULL, "
567 "NAME TEXT DEFAULT NULL"
569 if( ret
!= SQLITE_OK
)
571 ret
= sql_exec(db
, "CREATE TABLE DETAILS ( "
572 "ID INTEGER PRIMARY KEY AUTOINCREMENT, "
573 "PATH TEXT DEFAULT NULL, "
575 "TITLE TEXT COLLATE NOCASE, "
578 "SAMPLERATE INTEGER, "
579 "ARTIST TEXT COLLATE NOCASE, "
580 "ALBUM TEXT COLLATE NOCASE, "
581 "GENRE TEXT COLLATE NOCASE, "
587 "THUMBNAIL BOOL DEFAULT 0, "
588 "CREATOR TEXT COLLATE NOCASE, "
591 "ALBUM_ART INTEGER DEFAULT 0, "
595 if( ret
!= SQLITE_OK
)
597 ret
= sql_exec(db
, "CREATE TABLE ALBUM_ART ( "
598 "ID INTEGER PRIMARY KEY AUTOINCREMENT, "
601 if( ret
!= SQLITE_OK
)
603 ret
= sql_exec(db
, "CREATE TABLE CAPTIONS ("
604 "ID INTEGER PRIMARY KEY, "
607 if( ret
!= SQLITE_OK
)
609 ret
= sql_exec(db
, "CREATE TABLE BOOKMARKS ("
610 "ID INTEGER PRIMARY KEY, "
613 if( ret
!= SQLITE_OK
)
615 ret
= sql_exec(db
, "CREATE TABLE PLAYLISTS ("
616 "ID INTEGER PRIMARY KEY AUTOINCREMENT, "
617 "NAME TEXT NOT NULL, "
618 "PATH TEXT NOT NULL, "
619 "ITEMS INTEGER DEFAULT 0, "
620 "FOUND INTEGER DEFAULT 0"
622 if( ret
!= SQLITE_OK
)
624 ret
= sql_exec(db
, "CREATE TABLE SETTINGS ("
625 "UPDATE_ID INTEGER PRIMARY KEY DEFAULT 0, "
626 "FLAGS INTEGER DEFAULT 0"
628 if( ret
!= SQLITE_OK
)
630 ret
= sql_exec(db
, "INSERT into SETTINGS values (0, 0)");
631 if( ret
!= SQLITE_OK
)
633 for( i
=0; containers
[i
]; i
=i
+3 )
635 ret
= sql_exec(db
, "INSERT into OBJECTS (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME)"
637 "('%s', '%s', %lld, 'container.storageFolder', '%q')",
638 containers
[i
], containers
[i
+1], GetFolderMetadata(containers
[i
+2], NULL
, NULL
, NULL
, 0), containers
[i
+2]);
639 if( ret
!= SQLITE_OK
)
642 sql_exec(db
, "create INDEX IDX_OBJECTS_OBJECT_ID ON OBJECTS(OBJECT_ID);");
643 sql_exec(db
, "create INDEX IDX_OBJECTS_PARENT_ID ON OBJECTS(PARENT_ID);");
644 sql_exec(db
, "create INDEX IDX_OBJECTS_DETAIL_ID ON OBJECTS(DETAIL_ID);");
645 sql_exec(db
, "create INDEX IDX_OBJECTS_CLASS ON OBJECTS(CLASS);");
646 sql_exec(db
, "create INDEX IDX_DETAILS_PATH ON DETAILS(PATH);");
647 sql_exec(db
, "create INDEX IDX_DETAILS_ID ON DETAILS(ID);");
648 sql_exec(db
, "create INDEX IDX_ALBUM_ART ON ALBUM_ART(ID);");
649 sql_exec(db
, "create INDEX IDX_SCANNER_OPT ON OBJECTS(PARENT_ID, NAME, OBJECT_ID);");
652 if( ret
!= SQLITE_OK
)
653 fprintf(stderr
, "Error creating SQLite3 database!\n");
654 return (ret
!= SQLITE_OK
);
658 filter_audio(const struct dirent
*d
)
660 return ( (*d
->d_name
!= '.') &&
661 ((d
->d_type
== DT_DIR
) ||
662 (d
->d_type
== DT_LNK
) ||
663 (d
->d_type
== DT_UNKNOWN
) ||
664 ((d
->d_type
== DT_REG
) &&
665 (is_audio(d
->d_name
) ||
666 is_playlist(d
->d_name
)
672 filter_video(const struct dirent
*d
)
674 return ( (*d
->d_name
!= '.') &&
675 ((d
->d_type
== DT_DIR
) ||
676 (d
->d_type
== DT_LNK
) ||
677 (d
->d_type
== DT_UNKNOWN
) ||
678 ((d
->d_type
== DT_REG
) &&
679 is_video(d
->d_name
) )
684 filter_images(const struct dirent
*d
)
686 return ( (*d
->d_name
!= '.') &&
687 ((d
->d_type
== DT_DIR
) ||
688 (d
->d_type
== DT_LNK
) ||
689 (d
->d_type
== DT_UNKNOWN
) ||
690 ((d
->d_type
== DT_REG
) &&
691 is_image(d
->d_name
) )
696 filter_media(const struct dirent
*d
)
698 return ( (*d
->d_name
!= '.') &&
699 ((d
->d_type
== DT_DIR
) ||
700 (d
->d_type
== DT_LNK
) ||
701 (d
->d_type
== DT_UNKNOWN
) ||
702 ((d
->d_type
== DT_REG
) &&
703 (is_image(d
->d_name
) ||
704 is_audio(d
->d_name
) ||
705 is_video(d
->d_name
) ||
706 is_playlist(d
->d_name
)
712 ScanDirectory(const char * dir
, const char * parent
, enum media_types dir_type
)
714 struct dirent
**namelist
;
716 char parent_id
[PATH_MAX
];
717 char full_path
[PATH_MAX
];
719 static long long unsigned int fileno
= 0;
720 enum file_types type
;
722 setlocale(LC_COLLATE
, "");
723 if( chdir(dir
) != 0 )
726 DPRINTF(parent
?E_INFO
:E_WARN
, L_SCANNER
, _("Scanning %s\n"), dir
);
730 n
= scandir(".", &namelist
, filter_media
, alphasort
);
733 n
= scandir(".", &namelist
, filter_audio
, alphasort
);
736 n
= scandir(".", &namelist
, filter_video
, alphasort
);
739 n
= scandir(".", &namelist
, filter_images
, alphasort
);
746 fprintf(stderr
, "Error scanning %s [scandir]\n", dir
);
752 startID
= get_next_available_id("OBJECTS", BROWSEDIR_ID
);
755 for (i
=0; i
< n
; i
++)
762 sprintf(full_path
, "%s/%s", dir
, namelist
[i
]->d_name
);
763 name
= escape_tag(namelist
[i
]->d_name
, 1);
764 if( namelist
[i
]->d_type
== DT_DIR
)
768 else if( namelist
[i
]->d_type
== DT_REG
)
774 type
= resolve_unknown_type(full_path
, dir_type
);
776 if( (type
== TYPE_DIR
) && (access(full_path
, R_OK
|X_OK
) == 0) )
778 insert_directory(name
, full_path
, BROWSEDIR_ID
, (parent
? parent
:""), i
+startID
);
779 sprintf(parent_id
, "%s$%X", (parent
? parent
:""), i
+startID
);
780 ScanDirectory(full_path
, parent_id
, dir_type
);
782 else if( type
== TYPE_FILE
&& (access(full_path
, R_OK
) == 0) )
784 if( insert_file(name
, full_path
, (parent
? parent
:""), i
+startID
) == 0 )
793 chdir(dirname((char*)dir
));
797 DPRINTF(E_WARN
, L_SCANNER
, _("Scanning %s finished (%llu files)!\n"), dir
, fileno
);
804 struct media_dir_s
* media_path
= media_dirs
;
806 if (setpriority(PRIO_PROCESS
, 0, 15) == -1)
807 DPRINTF(E_WARN
, L_INOTIFY
, "Failed to reduce scanner thread priority\n");
810 FILE * flag
= fopen("/ramfs/.upnp-av_scan", "w");
814 freopen("/dev/null", "a", stderr
);
817 ScanDirectory(media_path
->path
, NULL
, media_path
->type
);
818 media_path
= media_path
->next
;
820 freopen("/proc/self/fd/2", "a", stderr
);
822 if( access("/ramfs/.rescan_done", F_OK
) == 0 )
823 system("/bin/sh /ramfs/.rescan_done");
824 unlink("/ramfs/.upnp-av_scan");
826 /* Create this index after scanning, so it doesn't slow down the scanning process.
827 * This index is very useful for large libraries used with an XBox360 (or any
828 * client that uses UPnPSearch on large containers). */
829 sql_exec(db
, "create INDEX IDX_SEARCH_OPT ON OBJECTS(OBJECT_ID, CLASS, DETAIL_ID);");
833 //JM: Set up a db version number, so we know if we need to rebuild due to a new structure.
834 sql_exec(db
, "pragma user_version = %d;", DB_VERSION
);