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>
29 #include <sys/statfs.h>
38 #include "upnpglobalvars.h"
51 sqlite3_int64 objectID
;
58 is_external_path(const char * path
)
62 if (statfs(path
, &sf
) == 0)
64 /* if it returns squashfs or tmpfs type, assume it's not mounted */
65 return (sf
.f_type
!= 0x73717368 && sf
.f_type
!= 0x1021994);
73 /* This could be a directory in tmpfs.
74 * Mounting a USB drive on this directory can take some time,
75 * so let's wait up to 5 seconds and hope that mount will complete.
76 * If not, just proceed with scanning - after all we may not mount
77 * anything on this directory.
80 wait_for_mount(const char * path
)
83 while ( ((r
= is_external_path(path
)) == 0) && (n
-- > 0) )
91 get_next_available_id(const char * table
, const char * parentID
)
94 sqlite_int64 objectID
= 0;
96 ret
= sql_get_text_field(db
, "SELECT OBJECT_ID from %s where ID = "
97 "(SELECT max(ID) from %s where PARENT_ID = '%s')",
98 table
, table
, parentID
);
101 base
= strrchr(ret
, '$');
103 objectID
= strtoll(base
+1, NULL
, 16) + 1;
111 insert_container(const char * item
, const char * rootParent
, const char * refID
, const char *class,
112 const char *artist
, const char *genre
, const char *album_art
, sqlite3_int64
*objectID
, sqlite3_int64
*parentID
)
117 sqlite_int64 detailID
= 0;
119 result
= sql_get_text_field(db
, "SELECT OBJECT_ID from OBJECTS o "
120 "left join DETAILS d on (o.DETAIL_ID = d.ID)"
121 " where o.PARENT_ID = '%s'"
122 " and o.NAME like '%q'"
123 " and d.ARTIST %s %Q"
124 " and o.CLASS = 'container.%s' limit 1",
125 rootParent
, item
, artist
?"like":"is", artist
, class);
128 base
= strrchr(result
, '$');
130 *parentID
= strtoll(base
+1, NULL
, 16);
133 *objectID
= get_next_available_id("OBJECTS", result
);
138 *parentID
= get_next_available_id("OBJECTS", rootParent
);
141 result
= sql_get_text_field(db
, "SELECT DETAIL_ID from OBJECTS where OBJECT_ID = %Q", refID
);
143 detailID
= strtoll(result
, NULL
, 10);
147 detailID
= GetFolderMetadata(item
, NULL
, artist
, genre
, (album_art
? strtoll(album_art
, NULL
, 10) : 0));
149 ret
= sql_exec(db
, "INSERT into OBJECTS"
150 " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
152 " ('%s$%"PRIX64
"', '%s', %Q, %"PRId64
", 'container.%s', '%q')",
153 rootParent
, *parentID
, rootParent
, refID
, detailID
, class, item
);
155 sqlite3_free(result
);
161 insert_containers(const char * name
, const char *path
, const char * refID
, const char * class, sqlite3_int64 detailID
)
167 sqlite_int64 objectID
, parentID
;
169 if( strstr(class, "imageItem") )
171 char *date
= NULL
, *cam
= NULL
;
172 char date_taken
[13], camera
[64];
173 static struct virtual_item last_date
;
174 static struct virtual_item last_cam
;
175 static struct virtual_item last_camdate
;
176 static sqlite_int64 last_all_objectID
= 0;
178 snprintf(sql
, sizeof(sql
), "SELECT DATE, CREATOR from DETAILS where ID = %lld", detailID
);
179 ret
= sql_get_table(db
, sql
, &result
, &row
, &cols
);
180 if( ret
== SQLITE_OK
)
188 strncpy(date_taken
, date
, 10);
189 date_taken
[10] = '\0';
193 strcpy(date_taken
, _("Unknown Date"));
195 if( valid_cache
&& strcmp(last_date
.name
, date_taken
) == 0 )
197 last_date
.objectID
++;
198 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last date item: %s/%s/%X\n", last_date.name, last_date.parentID, last_date.objectID);
202 insert_container(date_taken
, IMAGE_DATE_ID
, NULL
, "album.photoAlbum", NULL
, NULL
, NULL
, &objectID
, &parentID
);
203 sprintf(last_date
.parentID
, IMAGE_DATE_ID
"$%"PRIX64
, parentID
);
204 last_date
.objectID
= objectID
;
205 strcpy(last_date
.name
, date_taken
);
206 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached date item: %s/%s/%X\n", last_date.name, last_date.parentID, last_date.objectID);
208 sql_exec(db
, "INSERT into OBJECTS"
209 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
211 " ('%s$%"PRIX64
"', '%s', '%s', '%s', %"PRId64
", %Q)",
212 last_date
.parentID
, last_date
.objectID
, last_date
.parentID
, refID
, class, detailID
, name
);
216 strncpy(camera
, cam
, 63);
221 strcpy(camera
, _("Unknown Camera"));
223 if( !valid_cache
|| strcmp(camera
, last_cam
.name
) != 0 )
225 insert_container(camera
, IMAGE_CAMERA_ID
, NULL
, "storageFolder", NULL
, NULL
, NULL
, &objectID
, &parentID
);
226 sprintf(last_cam
.parentID
, IMAGE_CAMERA_ID
"$%"PRIX64
, parentID
);
227 strncpy(last_cam
.name
, camera
, 255);
228 last_camdate
.name
[0] = '\0';
230 if( valid_cache
&& strcmp(last_camdate
.name
, date_taken
) == 0 )
232 last_camdate
.objectID
++;
233 //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);
237 insert_container(date_taken
, last_cam
.parentID
, NULL
, "album.photoAlbum", NULL
, NULL
, NULL
, &objectID
, &parentID
);
238 sprintf(last_camdate
.parentID
, "%s$%"PRIX64
, last_cam
.parentID
, parentID
);
239 last_camdate
.objectID
= objectID
;
240 strcpy(last_camdate
.name
, date_taken
);
241 //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);
243 sql_exec(db
, "INSERT into OBJECTS"
244 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
246 " ('%s$%"PRIX64
"', '%s', '%s', '%s', %"PRId64
", %Q)",
247 last_camdate
.parentID
, last_camdate
.objectID
, last_camdate
.parentID
, refID
, class, detailID
, name
);
249 if( !last_all_objectID
)
251 last_all_objectID
= get_next_available_id("OBJECTS", IMAGE_ALL_ID
);
253 sql_exec(db
, "INSERT into OBJECTS"
254 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
256 " ('"IMAGE_ALL_ID
"$%"PRIX64
"', '"IMAGE_ALL_ID
"', '%s', '%s', %"PRId64
", %Q)",
257 last_all_objectID
++, refID
, class, detailID
, name
);
259 else if( strstr(class, "audioItem") )
261 snprintf(sql
, sizeof(sql
), "SELECT ALBUM, ARTIST, GENRE, ALBUM_ART from DETAILS where ID = %lld", detailID
);
262 ret
= sql_get_table(db
, sql
, &result
, &row
, &cols
);
263 if( ret
!= SQLITE_OK
)
267 sqlite3_free_table(result
);
270 char *album
= result
[4], *artist
= result
[5], *genre
= result
[6];
271 char *album_art
= result
[7];
272 static struct virtual_item last_album
;
273 static struct virtual_item last_artist
;
274 static struct virtual_item last_artistAlbum
;
275 static struct virtual_item last_artistAlbumAll
;
276 static struct virtual_item last_genre
;
277 static struct virtual_item last_genreArtist
;
278 static struct virtual_item last_genreArtistAll
;
279 static sqlite_int64 last_all_objectID
= 0;
283 if( valid_cache
&& strcmp(album
, last_album
.name
) == 0 )
285 last_album
.objectID
++;
286 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last album item: %s/%s/%X\n", last_album.name, last_album.parentID, last_album.objectID);
290 strcpy(last_album
.name
, album
);
291 insert_container(album
, MUSIC_ALBUM_ID
, NULL
, "album.musicAlbum", artist
, genre
, album_art
, &objectID
, &parentID
);
292 sprintf(last_album
.parentID
, MUSIC_ALBUM_ID
"$%llX", parentID
);
293 last_album
.objectID
= objectID
;
294 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached album item: %s/%s/%X\n", last_album.name, last_album.parentID, last_album.objectID);
296 sql_exec(db
, "INSERT into OBJECTS"
297 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
299 " ('%s$%"PRIX64
"', '%s', '%s', '%s', %"PRId64
", %Q)",
300 last_album
.parentID
, last_album
.objectID
, last_album
.parentID
, refID
, class, detailID
, name
);
304 if( !valid_cache
|| strcmp(artist
, last_artist
.name
) != 0 )
306 insert_container(artist
, MUSIC_ARTIST_ID
, NULL
, "person.musicArtist", NULL
, genre
, NULL
, &objectID
, &parentID
);
307 sprintf(last_artist
.parentID
, MUSIC_ARTIST_ID
"$%"PRIX64
, parentID
);
308 strcpy(last_artist
.name
, artist
);
309 last_artistAlbum
.name
[0] = '\0';
310 /* Add this file to the "- All Albums -" container as well */
311 insert_container(_("- All Albums -"), last_artist
.parentID
, NULL
, "album", artist
, genre
, NULL
, &objectID
, &parentID
);
312 sprintf(last_artistAlbumAll
.parentID
, "%s$%"PRIX64
, last_artist
.parentID
, parentID
);
313 last_artistAlbumAll
.objectID
= objectID
;
317 last_artistAlbumAll
.objectID
++;
319 if( valid_cache
&& strcmp(album
?album
:_("Unknown Album"), last_artistAlbum
.name
) == 0 )
321 last_artistAlbum
.objectID
++;
322 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last artist/album item: %s/%s/%X\n", last_artist.name, last_artist.parentID, last_artist.objectID);
326 insert_container(album
?album
:_("Unknown Album"), last_artist
.parentID
, album
?last_album
.parentID
:NULL
,
327 "album.musicAlbum", artist
, genre
, album_art
, &objectID
, &parentID
);
328 sprintf(last_artistAlbum
.parentID
, "%s$%"PRIX64
, last_artist
.parentID
, parentID
);
329 last_artistAlbum
.objectID
= objectID
;
330 strcpy(last_artistAlbum
.name
, album
?album
:_("Unknown Album"));
331 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached artist/album item: %s/%s/%X\n", last_artist.name, last_artist.parentID, last_artist.objectID);
333 sql_exec(db
, "INSERT into OBJECTS"
334 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
336 " ('%s$%"PRIX64
"', '%s', '%s', '%s', %"PRId64
", %Q)",
337 last_artistAlbum
.parentID
, last_artistAlbum
.objectID
, last_artistAlbum
.parentID
, refID
, class, detailID
, name
);
338 sql_exec(db
, "INSERT into OBJECTS"
339 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
341 " ('%s$%"PRIX64
"', '%s', '%s', '%s', %"PRId64
", %Q)",
342 last_artistAlbumAll
.parentID
, last_artistAlbumAll
.objectID
, last_artistAlbumAll
.parentID
, refID
, class, detailID
, name
);
346 if( !valid_cache
|| strcmp(genre
, last_genre
.name
) != 0 )
348 insert_container(genre
, MUSIC_GENRE_ID
, NULL
, "genre.musicGenre", NULL
, NULL
, NULL
, &objectID
, &parentID
);
349 sprintf(last_genre
.parentID
, MUSIC_GENRE_ID
"$%"PRIX64
, parentID
);
350 strcpy(last_genre
.name
, genre
);
351 last_genreArtist
.name
[0] = '\0';
352 /* Add this file to the "- All Artists -" container as well */
353 insert_container(_("- All Artists -"), last_genre
.parentID
, NULL
, "person", NULL
, genre
, NULL
, &objectID
, &parentID
);
354 sprintf(last_genreArtistAll
.parentID
, "%s$%"PRIX64
, last_genre
.parentID
, parentID
);
355 last_genreArtistAll
.objectID
= objectID
;
359 last_genreArtistAll
.objectID
++;
361 if( valid_cache
&& strcmp(artist
?artist
:_("Unknown Artist"), last_genreArtist
.name
) == 0 )
363 last_genreArtist
.objectID
++;
367 insert_container(artist
?artist
:_("Unknown Artist"), last_genre
.parentID
, artist
?last_artist
.parentID
:NULL
,
368 "person.musicArtist", NULL
, genre
, NULL
, &objectID
, &parentID
);
369 sprintf(last_genreArtist
.parentID
, "%s$%"PRIX64
, last_genre
.parentID
, parentID
);
370 last_genreArtist
.objectID
= objectID
;
371 strcpy(last_genreArtist
.name
, artist
?artist
:_("Unknown Artist"));
372 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached genre/artist item: %s/%s/%X\n", last_genreArtist.name, last_genreArtist.parentID, last_genreArtist.objectID);
374 sql_exec(db
, "INSERT into OBJECTS"
375 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
377 " ('%s$%"PRIX64
"', '%s', '%s', '%s', %"PRId64
", %Q)",
378 last_genreArtist
.parentID
, last_genreArtist
.objectID
, last_genreArtist
.parentID
, refID
, class, detailID
, name
);
379 sql_exec(db
, "INSERT into OBJECTS"
380 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
382 " ('%s$%"PRIX64
"', '%s', '%s', '%s', %"PRId64
", %Q)",
383 last_genreArtistAll
.parentID
, last_genreArtistAll
.objectID
, last_genreArtistAll
.parentID
, refID
, class, detailID
, name
);
386 if( !last_all_objectID
)
388 last_all_objectID
= get_next_available_id("OBJECTS", MUSIC_ALL_ID
);
390 sql_exec(db
, "INSERT into OBJECTS"
391 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
393 " ('"MUSIC_ALL_ID
"$%"PRIX64
"', '"MUSIC_ALL_ID
"', '%s', '%s', %"PRId64
", %Q)",
394 last_all_objectID
++, refID
, class, detailID
, name
);
396 else if( strstr(class, "videoItem") )
398 static sqlite_int64 last_all_objectID
= 0;
401 if( !last_all_objectID
)
403 last_all_objectID
= get_next_available_id("OBJECTS", VIDEO_ALL_ID
);
405 sql_exec(db
, "INSERT into OBJECTS"
406 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
408 " ('"VIDEO_ALL_ID
"$%"PRIX64
"', '"VIDEO_ALL_ID
"', '%s', '%s', %"PRId64
", %Q)",
409 last_all_objectID
++, refID
, class, detailID
, name
);
416 sqlite3_free_table(result
);
421 insert_directory(const char * name
, const char * path
, const char * base
, const char * parentID
, int objectID
)
424 sqlite_int64 detailID
= 0;
426 char class[] = "container.storageFolder";
428 static char last_found
[256] = "-1";
430 if( strcmp(base
, BROWSEDIR_ID
) != 0 )
432 if( asprintf(&refID
, "%s%s$%X", BROWSEDIR_ID
, parentID
, objectID
) == -1 )
438 char id_buf
[64], parent_buf
[64];
440 dir_buf
= strdup(path
);
441 dir
= dirname(dir_buf
);
442 snprintf(id_buf
, sizeof(id_buf
), "%s%s$%X", base
, parentID
, objectID
);
443 snprintf(parent_buf
, sizeof(parent_buf
), "%s%s", base
, parentID
);
446 if( valid_cache
&& strcmp(id_buf
, last_found
) == 0 )
448 if( sql_get_int_field(db
, "SELECT count(*) from OBJECTS where OBJECT_ID = '%s'", id_buf
) > 0 )
450 strcpy(last_found
, id_buf
);
453 /* Does not exist. Need to create, and may need to create parents also */
454 result
= sql_get_text_field(db
, "SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s'", refID
);
457 detailID
= strtoll(result
, NULL
, 10);
458 sqlite3_free(result
);
460 sql_exec(db
, "INSERT into OBJECTS"
461 " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
463 " ('%s', '%s', %Q, %"PRId64
", '%s', '%q')",
464 id_buf
, parent_buf
, refID
, detailID
, class, strrchr(dir
, '/')+1);
465 if( (p
= strrchr(id_buf
, '$')) )
467 if( (p
= strrchr(parent_buf
, '$')) )
469 if( (p
= strrchr(refID
, '$')) )
478 detailID
= GetFolderMetadata(name
, path
, NULL
, NULL
, find_album_art(path
, NULL
, 0));
479 sql_exec(db
, "INSERT into OBJECTS"
480 " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
482 " ('%s%s$%X', '%s%s', %Q, %"PRId64
", '%s', '%q')",
483 base
, parentID
, objectID
, base
, parentID
, refID
, detailID
, class, name
);
491 insert_file(char * name
, const char * path
, const char * parentID
, int object
)
495 sqlite3_int64 detailID
= 0;
497 char * typedir_parentID
;
498 int typedir_objectID
;
500 char * orig_name
= NULL
;
504 if( is_album_art(name
) )
506 strcpy(base
, IMAGE_DIR_ID
);
507 strcpy(class, "item.imageItem.photo");
508 detailID
= GetImageMetadata(path
, name
);
510 else if( is_video(name
) )
512 orig_name
= strdup(name
);
513 strcpy(base
, VIDEO_DIR_ID
);
514 strcpy(class, "item.videoItem");
515 detailID
= GetVideoMetadata(path
, name
);
517 strcpy(name
, orig_name
);
519 else if( is_playlist(name
) )
521 if( insert_playlist(path
, name
) == 0 )
524 if( !detailID
&& is_audio(name
) )
526 strcpy(base
, MUSIC_DIR_ID
);
527 strcpy(class, "item.audioItem.musicTrack");
528 detailID
= GetAudioMetadata(path
, name
);
534 DPRINTF(E_WARN
, L_SCANNER
, "Unsuccessful getting details for %s!\n", path
);
538 sprintf(objectID
, "%s%s$%X", BROWSEDIR_ID
, parentID
, object
);
540 sql_exec(db
, "INSERT into OBJECTS"
541 " (OBJECT_ID, PARENT_ID, CLASS, DETAIL_ID, NAME) "
543 " ('%s', '%s%s', '%s', %"PRId64
", '%q')",
544 objectID
, BROWSEDIR_ID
, parentID
, class, detailID
, name
);
548 typedir_objectID
= 0;
549 typedir_parentID
= strdup(parentID
);
550 baseid
= strrchr(typedir_parentID
, '$');
553 typedir_objectID
= strtol(baseid
+1, NULL
, 16);
556 insert_directory(name
, path
, base
, typedir_parentID
, typedir_objectID
);
557 free(typedir_parentID
);
559 sql_exec(db
, "INSERT into OBJECTS"
560 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
562 " ('%s%s$%X', '%s%s', '%s', '%s', %"PRId64
", '%q')",
563 base
, parentID
, object
, base
, parentID
, objectID
, class, detailID
, name
);
565 insert_containers(name
, path
, objectID
, class, detailID
);
573 const char * containers
[] = { "0","-1", "root",
574 MUSIC_ID
, "0", _("Music"),
575 MUSIC_ALL_ID
, MUSIC_ID
, _("All Music"),
576 MUSIC_GENRE_ID
, MUSIC_ID
, _("Genre"),
577 MUSIC_ARTIST_ID
, MUSIC_ID
, _("Artist"),
578 MUSIC_ALBUM_ID
, MUSIC_ID
, _("Album"),
579 MUSIC_DIR_ID
, MUSIC_ID
, _("Folders"),
580 MUSIC_PLIST_ID
, MUSIC_ID
, _("Playlists"),
582 VIDEO_ID
, "0", _("Video"),
583 VIDEO_ALL_ID
, VIDEO_ID
, _("All Video"),
584 VIDEO_DIR_ID
, VIDEO_ID
, _("Folders"),
586 IMAGE_ID
, "0", _("Pictures"),
587 IMAGE_ALL_ID
, IMAGE_ID
, _("All Pictures"),
588 IMAGE_DATE_ID
, IMAGE_ID
, _("Date Taken"),
589 IMAGE_CAMERA_ID
, IMAGE_ID
, _("Camera"),
590 IMAGE_DIR_ID
, IMAGE_ID
, _("Folders"),
592 BROWSEDIR_ID
, "0", _("Browse Folders"),
595 ret
= sql_exec(db
, "CREATE TABLE OBJECTS ( "
596 "ID INTEGER PRIMARY KEY AUTOINCREMENT, "
597 "OBJECT_ID TEXT UNIQUE NOT NULL, "
598 "PARENT_ID TEXT NOT NULL, "
599 "REF_ID TEXT DEFAULT NULL, "
600 "CLASS TEXT NOT NULL, "
601 "DETAIL_ID INTEGER DEFAULT NULL, "
602 "NAME TEXT DEFAULT NULL"
604 if( ret
!= SQLITE_OK
)
606 ret
= sql_exec(db
, "CREATE TABLE DETAILS ( "
607 "ID INTEGER PRIMARY KEY AUTOINCREMENT, "
608 "PATH TEXT DEFAULT NULL, "
610 "TIMESTAMP INTEGER, "
611 "TITLE TEXT COLLATE NOCASE, "
614 "SAMPLERATE INTEGER, "
615 "CREATOR TEXT COLLATE NOCASE, "
616 "ARTIST TEXT COLLATE NOCASE, "
617 "ALBUM TEXT COLLATE NOCASE, "
618 "GENRE TEXT COLLATE NOCASE, "
625 "THUMBNAIL BOOL DEFAULT 0, "
626 "ALBUM_ART INTEGER DEFAULT 0, "
631 if( ret
!= SQLITE_OK
)
633 ret
= sql_exec(db
, "CREATE TABLE ALBUM_ART ( "
634 "ID INTEGER PRIMARY KEY AUTOINCREMENT, "
637 if( ret
!= SQLITE_OK
)
639 ret
= sql_exec(db
, "CREATE TABLE CAPTIONS ("
640 "ID INTEGER PRIMARY KEY, "
643 if( ret
!= SQLITE_OK
)
645 ret
= sql_exec(db
, "CREATE TABLE BOOKMARKS ("
646 "ID INTEGER PRIMARY KEY, "
649 if( ret
!= SQLITE_OK
)
651 ret
= sql_exec(db
, "CREATE TABLE PLAYLISTS ("
652 "ID INTEGER PRIMARY KEY AUTOINCREMENT, "
653 "NAME TEXT NOT NULL, "
654 "PATH TEXT NOT NULL, "
655 "ITEMS INTEGER DEFAULT 0, "
656 "FOUND INTEGER DEFAULT 0"
658 if( ret
!= SQLITE_OK
)
660 ret
= sql_exec(db
, "CREATE TABLE SETTINGS ("
661 "UPDATE_ID INTEGER PRIMARY KEY DEFAULT 0, "
662 "FLAGS INTEGER DEFAULT 0"
664 if( ret
!= SQLITE_OK
)
666 ret
= sql_exec(db
, "INSERT into SETTINGS values (0, 0)");
667 if( ret
!= SQLITE_OK
)
669 for( i
=0; containers
[i
]; i
=i
+3 )
671 ret
= sql_exec(db
, "INSERT into OBJECTS (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME)"
673 "('%s', '%s', %lld, 'container.storageFolder', '%q')",
674 containers
[i
], containers
[i
+1], GetFolderMetadata(containers
[i
+2], NULL
, NULL
, NULL
, 0), containers
[i
+2]);
675 if( ret
!= SQLITE_OK
)
678 sql_exec(db
, "create INDEX IDX_OBJECTS_OBJECT_ID ON OBJECTS(OBJECT_ID);");
679 sql_exec(db
, "create INDEX IDX_OBJECTS_PARENT_ID ON OBJECTS(PARENT_ID);");
680 sql_exec(db
, "create INDEX IDX_OBJECTS_DETAIL_ID ON OBJECTS(DETAIL_ID);");
681 sql_exec(db
, "create INDEX IDX_OBJECTS_CLASS ON OBJECTS(CLASS);");
682 sql_exec(db
, "create INDEX IDX_DETAILS_PATH ON DETAILS(PATH);");
683 sql_exec(db
, "create INDEX IDX_DETAILS_ID ON DETAILS(ID);");
684 sql_exec(db
, "create INDEX IDX_ALBUM_ART ON ALBUM_ART(ID);");
685 sql_exec(db
, "create INDEX IDX_SCANNER_OPT ON OBJECTS(PARENT_ID, NAME, OBJECT_ID);");
688 if( ret
!= SQLITE_OK
)
689 fprintf(stderr
, "Error creating SQLite3 database!\n");
690 return (ret
!= SQLITE_OK
);
694 filter_audio(const struct dirent
*d
)
696 return ( (*d
->d_name
!= '.') &&
697 ((d
->d_type
== DT_DIR
) ||
698 (d
->d_type
== DT_LNK
) ||
699 (d
->d_type
== DT_UNKNOWN
) ||
700 ((d
->d_type
== DT_REG
) &&
701 (is_audio(d
->d_name
) ||
702 is_playlist(d
->d_name
)
708 filter_video(const struct dirent
*d
)
710 return ( (*d
->d_name
!= '.') &&
711 ((d
->d_type
== DT_DIR
) ||
712 (d
->d_type
== DT_LNK
) ||
713 (d
->d_type
== DT_UNKNOWN
) ||
714 ((d
->d_type
== DT_REG
) &&
715 is_video(d
->d_name
) )
720 filter_images(const struct dirent
*d
)
722 return ( (*d
->d_name
!= '.') &&
723 ((d
->d_type
== DT_DIR
) ||
724 (d
->d_type
== DT_LNK
) ||
725 (d
->d_type
== DT_UNKNOWN
) ||
726 ((d
->d_type
== DT_REG
) &&
727 is_image(d
->d_name
) )
732 filter_media(const struct dirent
*d
)
734 return ( (*d
->d_name
!= '.') &&
735 ((d
->d_type
== DT_DIR
) ||
736 (d
->d_type
== DT_LNK
) ||
737 (d
->d_type
== DT_UNKNOWN
) ||
738 ((d
->d_type
== DT_REG
) &&
739 (is_image(d
->d_name
) ||
740 is_audio(d
->d_name
) ||
741 is_video(d
->d_name
) ||
742 is_playlist(d
->d_name
)
748 ScanDirectory(const char * dir
, const char * parent
, enum media_types dir_type
)
750 struct dirent
**namelist
;
752 char parent_id
[PATH_MAX
];
753 char full_path
[PATH_MAX
];
755 static long long unsigned int fileno
= 0;
756 enum file_types type
;
758 setlocale(LC_COLLATE
, "");
759 if( chdir(dir
) != 0 )
762 if ( wait_for_mount(dir
) < 0 )
765 DPRINTF(parent
?E_INFO
:E_WARN
, L_SCANNER
, _("Scanning %s\n"), dir
);
769 n
= scandir(".", &namelist
, filter_media
, alphasort
);
772 n
= scandir(".", &namelist
, filter_audio
, alphasort
);
775 n
= scandir(".", &namelist
, filter_video
, alphasort
);
778 n
= scandir(".", &namelist
, filter_images
, alphasort
);
785 fprintf(stderr
, "Error scanning %s [scandir]\n", dir
);
791 startID
= get_next_available_id("OBJECTS", BROWSEDIR_ID
);
794 for (i
=0; i
< n
; i
++)
801 sprintf(full_path
, "%s/%s", dir
, namelist
[i
]->d_name
);
802 name
= escape_tag(namelist
[i
]->d_name
, 1);
803 if( namelist
[i
]->d_type
== DT_DIR
)
807 else if( namelist
[i
]->d_type
== DT_REG
)
813 type
= resolve_unknown_type(full_path
, dir_type
);
815 if( (type
== TYPE_DIR
) && (access(full_path
, R_OK
|X_OK
) == 0) )
817 insert_directory(name
, full_path
, BROWSEDIR_ID
, (parent
? parent
:""), i
+startID
);
818 sprintf(parent_id
, "%s$%X", (parent
? parent
:""), i
+startID
);
819 ScanDirectory(full_path
, parent_id
, dir_type
);
821 else if( type
== TYPE_FILE
&& (access(full_path
, R_OK
) == 0) )
823 if( insert_file(name
, full_path
, (parent
? parent
:""), i
+startID
) == 0 )
832 chdir(dirname((char*)dir
));
836 DPRINTF(E_WARN
, L_SCANNER
, _("Scanning %s finished (%llu files)!\n"), dir
, fileno
);
843 struct media_dir_s
* media_path
= media_dirs
;
846 if (setpriority(PRIO_PROCESS
, 0, 15) == -1)
847 DPRINTF(E_WARN
, L_INOTIFY
, "Failed to reduce scanner thread priority\n");
850 freopen("/dev/null", "a", stderr
);
853 strncpy(name
, media_path
->path
, sizeof(name
));
854 GetFolderMetadata(basename(name
), media_path
->path
, NULL
, NULL
, 0);
855 ScanDirectory(media_path
->path
, NULL
, media_path
->type
);
856 media_path
= media_path
->next
;
858 freopen("/proc/self/fd/2", "a", stderr
);
861 /* Create this index after scanning, so it doesn't slow down the scanning process.
862 * This index is very useful for large libraries used with an XBox360 (or any
863 * client that uses UPnPSearch on large containers). */
864 sql_exec(db
, "create INDEX IDX_SEARCH_OPT ON OBJECTS(OBJECT_ID, CLASS, DETAIL_ID);");
866 if( GETFLAG(NO_PLAYLIST_MASK
) )
868 DPRINTF(E_WARN
, L_SCANNER
, "Playlist creation disabled\n");
875 DPRINTF(E_DEBUG
, L_SCANNER
, "Initial file scan completed\n", DB_VERSION
);
876 //JM: Set up a db version number, so we know if we need to rebuild due to a new structure.
877 sql_exec(db
, "pragma user_version = %d;", DB_VERSION
);