minidlna support now Samsung TV C550/C650 (thx amir909)
[tomato.git] / release / src / router / minidlna / scanner.c
blobbc7b034ef6e224191ea1e30e6f56034ed9edd9ff
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/>.
18 #include <stdio.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <dirent.h>
23 #include <locale.h>
24 #include <libgen.h>
25 #include <inttypes.h>
26 #include <sys/stat.h>
27 #include <sys/time.h>
28 #include <sys/resource.h>
29 #include <sys/statfs.h>
31 #include "config.h"
33 #ifdef ENABLE_NLS
34 #include <libintl.h>
35 #endif
36 #include <sqlite3.h>
38 #include "upnpglobalvars.h"
39 #include "metadata.h"
40 #include "playlist.h"
41 #include "utils.h"
42 #include "sql.h"
43 #include "scanner.h"
44 #include "log.h"
46 int valid_cache = 0;
48 struct virtual_item
50 sqlite3_int64 objectID;
51 char parentID[64];
52 char name[256];
55 // Tomato
56 static int
57 is_external_path(const char * path)
59 struct statfs sf;
61 if (statfs(path, &sf) == 0)
63 /* if it returns squashfs or tmpfs type, assume it's not mounted */
64 return (sf.f_type != 0x73717368 && sf.f_type != 0x1021994);
66 else
68 return -1;
72 /* This could be a directory in tmpfs.
73 * Mounting a USB drive on this directory can take some time,
74 * so let's wait up to 5 seconds and hope that mount will complete.
75 * If not, just proceed with scanning - after all we may not mount
76 * anything on this directory.
78 int
79 wait_for_mount(const char * path)
81 int r, n = 50;
82 while ( ((r = is_external_path(path)) == 0) && (n-- > 0) )
84 usleep(100 * 1000);
86 return r;
90 sqlite_int64
91 get_next_available_id(const char * table, const char * parentID)
93 char *ret, *base;
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);
99 if( ret )
101 base = strrchr(ret, '$');
102 if( base )
103 objectID = strtoll(base+1, NULL, 16) + 1;
104 sqlite3_free(ret);
107 return objectID;
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)
114 char *result;
115 char *base;
116 int ret = 0;
117 sqlite_int64 detailID = 0;
119 result = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS"
120 " where PARENT_ID = '%s'"
121 " and NAME = '%q'"
122 " and CLASS = 'container.%s' limit 1",
123 rootParent, item, class);
124 if( result )
126 base = strrchr(result, '$');
127 if( base )
128 *parentID = strtoll(base+1, NULL, 16);
129 else
130 *parentID = 0;
131 *objectID = get_next_available_id("OBJECTS", result);
133 else
135 *objectID = 0;
136 *parentID = get_next_available_id("OBJECTS", rootParent);
137 if( refID )
139 result = sql_get_text_field(db, "SELECT DETAIL_ID from OBJECTS where OBJECT_ID = %Q", refID);
140 if( result )
141 detailID = strtoll(result, NULL, 10);
143 if( !detailID )
145 detailID = GetFolderMetadata(item, NULL, artist, genre, album_art);
147 ret = sql_exec(db, "INSERT into OBJECTS"
148 " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
149 "VALUES"
150 " ('%s$%"PRIX64"', '%s', %Q, %"PRId64", 'container.%s', '%q')",
151 rootParent, *parentID, rootParent, refID, detailID, class, item);
153 sqlite3_free(result);
155 return ret;
158 static void
159 insert_containers(const char * name, const char *path, const char * refID, const char * class, sqlite3_int64 detailID)
161 char *sql;
162 char **result;
163 int ret;
164 int cols, row;
165 sqlite_int64 objectID, parentID;
167 if( strstr(class, "imageItem") )
169 char *date = NULL, *cam = NULL;
170 char date_taken[13], camera[64];
171 static struct virtual_item last_date;
172 static struct virtual_item last_cam;
173 static struct virtual_item last_camdate;
174 static sqlite_int64 last_all_objectID = 0;
176 asprintf(&sql, "SELECT DATE, CREATOR from DETAILS where ID = %"PRId64, detailID);
177 ret = sql_get_table(db, sql, &result, &row, &cols);
178 free(sql);
179 if( ret == SQLITE_OK )
181 date = result[2];
182 cam = result[3];
185 if( date )
187 strncpy(date_taken, date, 10);
188 date_taken[10] = '\0';
190 else
192 strcpy(date_taken, _("Unknown Date"));
194 if( valid_cache && strcmp(last_date.name, date_taken) == 0 )
196 last_date.objectID++;
197 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last date item: %s/%s/%X\n", last_date.name, last_date.parentID, last_date.objectID);
199 else
201 insert_container(date_taken, IMAGE_DATE_ID, NULL, "album.photoAlbum", NULL, NULL, NULL, &objectID, &parentID);
202 sprintf(last_date.parentID, IMAGE_DATE_ID"$%"PRIX64, parentID);
203 last_date.objectID = objectID;
204 strcpy(last_date.name, date_taken);
205 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached date item: %s/%s/%X\n", last_date.name, last_date.parentID, last_date.objectID);
207 sql_exec(db, "INSERT into OBJECTS"
208 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
209 "VALUES"
210 " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
211 last_date.parentID, last_date.objectID, last_date.parentID, refID, class, detailID, name);
213 if( cam )
215 strncpy(camera, cam, 63);
216 camera[63] = '\0';
218 else
220 strcpy(camera, _("Unknown Camera"));
222 if( !valid_cache || strcmp(camera, last_cam.name) != 0 )
224 insert_container(camera, IMAGE_CAMERA_ID, NULL, "storageFolder", NULL, NULL, NULL, &objectID, &parentID);
225 sprintf(last_cam.parentID, IMAGE_CAMERA_ID"$%"PRIX64, parentID);
226 strncpy(last_cam.name, camera, 255);
227 last_camdate.name[0] = '\0';
229 if( valid_cache && strcmp(last_camdate.name, date_taken) == 0 )
231 last_camdate.objectID++;
232 //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);
234 else
236 insert_container(date_taken, last_cam.parentID, NULL, "album.photoAlbum", NULL, NULL, NULL, &objectID, &parentID);
237 sprintf(last_camdate.parentID, "%s$%"PRIX64, last_cam.parentID, parentID);
238 last_camdate.objectID = objectID;
239 strcpy(last_camdate.name, date_taken);
240 //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);
242 sql_exec(db, "INSERT into OBJECTS"
243 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
244 "VALUES"
245 " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
246 last_camdate.parentID, last_camdate.objectID, last_camdate.parentID, refID, class, detailID, name);
247 /* All Images */
248 if( !last_all_objectID )
250 last_all_objectID = get_next_available_id("OBJECTS", IMAGE_ALL_ID);
252 sql_exec(db, "INSERT into OBJECTS"
253 " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
254 "VALUES"
255 " ('"IMAGE_ALL_ID"$%"PRIX64"', '"IMAGE_ALL_ID"', '%s', '%s', %"PRId64", %Q)",
256 last_all_objectID++, refID, class, detailID, name);
258 else if( strstr(class, "audioItem") )
260 asprintf(&sql, "SELECT ALBUM, ARTIST, GENRE, ALBUM_ART from DETAILS where ID = %"PRId64, detailID);
261 ret = sql_get_table(db, sql, &result, &row, &cols);
262 free(sql);
263 if( ret != SQLITE_OK )
264 return;
265 if( !row )
267 sqlite3_free_table(result);
268 return;
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;
281 if( album )
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);
288 else
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) "
298 "VALUES"
299 " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
300 last_album.parentID, last_album.objectID, last_album.parentID, refID, class, detailID, name);
302 if( artist )
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;
315 else
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);
324 else
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) "
335 "VALUES"
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) "
340 "VALUES"
341 " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
342 last_artistAlbumAll.parentID, last_artistAlbumAll.objectID, last_artistAlbumAll.parentID, refID, class, detailID, name);
344 if( genre )
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;
357 else
359 last_genreArtistAll.objectID++;
361 if( valid_cache && strcmp(artist?artist:_("Unknown Artist"), last_genreArtist.name) == 0 )
363 last_genreArtist.objectID++;
365 else
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) "
376 "VALUES"
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) "
381 "VALUES"
382 " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
383 last_genreArtistAll.parentID, last_genreArtistAll.objectID, last_genreArtistAll.parentID, refID, class, detailID, name);
385 /* All Music */
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) "
392 "VALUES"
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;
400 /* All Videos */
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) "
407 "VALUES"
408 " ('"VIDEO_ALL_ID"$%"PRIX64"', '"VIDEO_ALL_ID"', '%s', '%s', %"PRId64", %Q)",
409 last_all_objectID++, refID, class, detailID, name);
410 return;
412 else
414 return;
416 sqlite3_free_table(result);
417 valid_cache = 1;
421 insert_directory(const char * name, const char * path, const char * base, const char * parentID, int objectID)
423 int found = 0;
424 sqlite_int64 detailID = 0;
425 char * refID = NULL;
426 char class[] = "container.storageFolder";
427 char * id_buf = NULL;
428 char * parent_buf = NULL;
429 char *dir_buf, *dir;
430 char *result, *p;
431 static char last_found[256] = "-1";
433 if( strcmp(base, BROWSEDIR_ID) != 0 )
434 asprintf(&refID, "%s%s$%X", BROWSEDIR_ID, parentID, objectID);
436 if( refID )
438 dir_buf = strdup(path);
439 dir = dirname(dir_buf);
440 asprintf(&id_buf, "%s%s$%X", base, parentID, objectID);
441 asprintf(&parent_buf, "%s%s", base, parentID);
442 while( !found )
444 if( strcmp(id_buf, last_found) == 0 )
445 break;
446 if( sql_get_int_field(db, "SELECT count(*) from OBJECTS where OBJECT_ID = '%s'", id_buf) > 0 )
448 strcpy(last_found, id_buf);
449 break;
451 /* Does not exist. Need to create, and may need to create parents also */
452 result = sql_get_text_field(db, "SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s'", refID);
453 if( result )
455 detailID = strtoll(result, NULL, 10);
456 sqlite3_free(result);
458 sql_exec(db, "INSERT into OBJECTS"
459 " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
460 "VALUES"
461 " ('%s', '%s', %Q, %"PRId64", '%s', '%q')",
462 id_buf, parent_buf, refID, detailID, class, strrchr(dir, '/')+1);
463 if( (p = strrchr(id_buf, '$')) )
464 *p = '\0';
465 if( (p = strrchr(parent_buf, '$')) )
466 *p = '\0';
467 if( (p = strrchr(refID, '$')) )
468 *p = '\0';
469 dir = dirname(dir);
471 free(refID);
472 free(parent_buf);
473 free(id_buf);
474 free(dir_buf);
475 return 1;
478 detailID = GetFolderMetadata(name, path, NULL, NULL, NULL);
479 sql_exec(db, "INSERT into OBJECTS"
480 " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
481 "VALUES"
482 " ('%s%s$%X', '%s%s', %Q, %"PRId64", '%s', '%q')",
483 base, parentID, objectID, base, parentID, refID, detailID, class, name);
484 if( refID )
485 free(refID);
487 return -1;
491 insert_file(char * name, const char * path, const char * parentID, int object)
493 char class[32];
494 char objectID[64];
495 sqlite3_int64 detailID = 0;
496 char base[8];
497 char * typedir_parentID;
498 int typedir_objectID;
499 char * baseid;
500 char * orig_name = NULL;
502 if( is_image(name) )
504 if( is_album_art(name) )
505 return -1;
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);
516 if( !detailID )
517 strcpy(name, orig_name);
519 else if( is_playlist(name) )
521 if( insert_playlist(path, name) == 0 )
522 return 1;
524 if( !detailID && is_audio(name) )
526 strcpy(base, MUSIC_DIR_ID);
527 strcpy(class, "item.audioItem.musicTrack");
528 detailID = GetAudioMetadata(path, name);
530 if( orig_name )
531 free(orig_name);
532 if( !detailID )
534 DPRINTF(E_WARN, L_SCANNER, "Unsuccessful getting details for %s!\n", path);
535 return -1;
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) "
542 "VALUES"
543 " ('%s', '%s%s', '%s', %"PRId64", '%q')",
544 objectID, BROWSEDIR_ID, parentID, class, detailID, name);
546 if( *parentID )
548 typedir_objectID = 0;
549 typedir_parentID = strdup(parentID);
550 baseid = strrchr(typedir_parentID, '$');
551 if( baseid )
553 typedir_objectID = strtol(baseid+1, NULL, 16);
554 *baseid = '\0';
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) "
561 "VALUES"
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);
566 return 0;
570 CreateDatabase(void)
572 int ret, i;
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"),
593 0 };
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"
603 ");");
604 if( ret != SQLITE_OK )
605 goto sql_failed;
606 ret = sql_exec(db, "CREATE TABLE DETAILS ( "
607 "ID INTEGER PRIMARY KEY AUTOINCREMENT, "
608 "PATH TEXT DEFAULT NULL, "
609 "SIZE INTEGER, "
610 "TITLE TEXT COLLATE NOCASE, "
611 "DURATION TEXT, "
612 "BITRATE INTEGER, "
613 "SAMPLERATE INTEGER, "
614 "ARTIST TEXT COLLATE NOCASE, "
615 "ALBUM TEXT COLLATE NOCASE, "
616 "GENRE TEXT COLLATE NOCASE, "
617 "COMMENT TEXT, "
618 "CHANNELS INTEGER, "
619 "TRACK INTEGER, "
620 "DATE DATE, "
621 "RESOLUTION TEXT, "
622 "THUMBNAIL BOOL DEFAULT 0, "
623 "CREATOR TEXT COLLATE NOCASE, "
624 "DLNA_PN TEXT, "
625 "MIME TEXT, "
626 "ALBUM_ART INTEGER DEFAULT 0, "
627 "DISC INTEGER, "
628 "TIMESTAMP INTEGER"
629 ")");
630 if( ret != SQLITE_OK )
631 goto sql_failed;
632 ret = sql_exec(db, "CREATE TABLE ALBUM_ART ( "
633 "ID INTEGER PRIMARY KEY AUTOINCREMENT, "
634 "PATH TEXT NOT NULL"
635 ")");
636 if( ret != SQLITE_OK )
637 goto sql_failed;
638 ret = sql_exec(db, "CREATE TABLE CAPTIONS ("
639 "ID INTEGER PRIMARY KEY, "
640 "PATH TEXT NOT NULL"
641 ")");
642 if( ret != SQLITE_OK )
643 goto sql_failed;
644 ret = sql_exec(db, "CREATE TABLE BOOKMARKS ("
645 "ID INTEGER PRIMARY KEY, "
646 "SEC INTEGER"
647 ")");
648 if( ret != SQLITE_OK )
649 goto sql_failed;
650 ret = sql_exec(db, "CREATE TABLE PLAYLISTS ("
651 "ID INTEGER PRIMARY KEY AUTOINCREMENT, "
652 "NAME TEXT NOT NULL, "
653 "PATH TEXT NOT NULL, "
654 "ITEMS INTEGER DEFAULT 0, "
655 "FOUND INTEGER DEFAULT 0"
656 ")");
657 if( ret != SQLITE_OK )
658 goto sql_failed;
659 ret = sql_exec(db, "CREATE TABLE SETTINGS ("
660 "UPDATE_ID INTEGER PRIMARY KEY DEFAULT 0, "
661 "FLAGS INTEGER DEFAULT 0"
662 ")");
663 if( ret != SQLITE_OK )
664 goto sql_failed;
665 ret = sql_exec(db, "INSERT into SETTINGS values (0, 0)");
666 if( ret != SQLITE_OK )
667 goto sql_failed;
668 for( i=0; containers[i]; i=i+3 )
670 ret = sql_exec(db, "INSERT into OBJECTS (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME)"
671 " values "
672 "('%s', '%s', %lld, 'container.storageFolder', '%q')",
673 containers[i], containers[i+1], GetFolderMetadata(containers[i+2], NULL, NULL, NULL, NULL), containers[i+2]);
674 if( ret != SQLITE_OK )
675 goto sql_failed;
677 sql_exec(db, "create INDEX IDX_OBJECTS_OBJECT_ID ON OBJECTS(OBJECT_ID);");
678 sql_exec(db, "create INDEX IDX_OBJECTS_PARENT_ID ON OBJECTS(PARENT_ID);");
679 sql_exec(db, "create INDEX IDX_OBJECTS_DETAIL_ID ON OBJECTS(DETAIL_ID);");
680 sql_exec(db, "create INDEX IDX_OBJECTS_CLASS ON OBJECTS(CLASS);");
681 sql_exec(db, "create INDEX IDX_DETAILS_PATH ON DETAILS(PATH);");
682 sql_exec(db, "create INDEX IDX_DETAILS_ID ON DETAILS(ID);");
683 sql_exec(db, "create INDEX IDX_ALBUM_ART ON ALBUM_ART(ID);");
684 sql_exec(db, "create INDEX IDX_SCANNER_OPT ON OBJECTS(PARENT_ID, NAME, OBJECT_ID);");
686 sql_failed:
687 if( ret != SQLITE_OK )
688 fprintf(stderr, "Error creating SQLite3 database!\n");
689 return (ret != SQLITE_OK);
693 filter_audio(const struct dirent *d)
695 return ( (*d->d_name != '.') &&
696 ((d->d_type == DT_DIR) ||
697 (d->d_type == DT_LNK) ||
698 (d->d_type == DT_UNKNOWN) ||
699 ((d->d_type == DT_REG) &&
700 (is_audio(d->d_name) ||
701 is_playlist(d->d_name)
703 ) ));
707 filter_video(const struct dirent *d)
709 return ( (*d->d_name != '.') &&
710 ((d->d_type == DT_DIR) ||
711 (d->d_type == DT_LNK) ||
712 (d->d_type == DT_UNKNOWN) ||
713 ((d->d_type == DT_REG) &&
714 is_video(d->d_name) )
715 ) );
719 filter_images(const struct dirent *d)
721 return ( (*d->d_name != '.') &&
722 ((d->d_type == DT_DIR) ||
723 (d->d_type == DT_LNK) ||
724 (d->d_type == DT_UNKNOWN) ||
725 ((d->d_type == DT_REG) &&
726 is_image(d->d_name) )
727 ) );
731 filter_media(const struct dirent *d)
733 return ( (*d->d_name != '.') &&
734 ((d->d_type == DT_DIR) ||
735 (d->d_type == DT_LNK) ||
736 (d->d_type == DT_UNKNOWN) ||
737 ((d->d_type == DT_REG) &&
738 (is_image(d->d_name) ||
739 is_audio(d->d_name) ||
740 is_video(d->d_name) ||
741 is_playlist(d->d_name)
743 ) ));
746 void
747 ScanDirectory(const char * dir, const char * parent, enum media_types dir_type)
749 struct dirent **namelist;
750 int i, n, startID=0;
751 char parent_id[PATH_MAX];
752 char full_path[PATH_MAX];
753 char * name = NULL;
754 static long long unsigned int fileno = 0;
755 enum file_types type;
757 setlocale(LC_COLLATE, "");
758 if( chdir(dir) != 0 )
759 return;
761 if ( wait_for_mount(dir) < 0 )
762 return;
764 DPRINTF(parent?E_INFO:E_WARN, L_SCANNER, _("Scanning %s\n"), dir);
765 switch( dir_type )
767 case ALL_MEDIA:
768 n = scandir(".", &namelist, filter_media, alphasort);
769 break;
770 case AUDIO_ONLY:
771 n = scandir(".", &namelist, filter_audio, alphasort);
772 break;
773 case VIDEO_ONLY:
774 n = scandir(".", &namelist, filter_video, alphasort);
775 break;
776 case IMAGES_ONLY:
777 n = scandir(".", &namelist, filter_images, alphasort);
778 break;
779 default:
780 n = -1;
781 break;
783 if (n < 0) {
784 fprintf(stderr, "Error scanning %s [scandir]\n", dir);
785 return;
788 if( !parent )
790 startID = get_next_available_id("OBJECTS", BROWSEDIR_ID);
793 for (i=0; i < n; i++)
795 #if !USE_FORK
796 if( quitting )
797 break;
798 #endif
799 type = TYPE_UNKNOWN;
800 sprintf(full_path, "%s/%s", dir, namelist[i]->d_name);
801 name = escape_tag(namelist[i]->d_name, 1);
802 if( namelist[i]->d_type == DT_DIR )
804 type = TYPE_DIR;
806 else if( namelist[i]->d_type == DT_REG )
808 type = TYPE_FILE;
810 else
812 type = resolve_unknown_type(full_path, dir_type);
814 if( (type == TYPE_DIR) && (access(full_path, R_OK|X_OK) == 0) )
816 insert_directory(name, full_path, BROWSEDIR_ID, (parent ? parent:""), i+startID);
817 sprintf(parent_id, "%s$%X", (parent ? parent:""), i+startID);
818 ScanDirectory(full_path, parent_id, dir_type);
820 else if( type == TYPE_FILE )
822 if( insert_file(name, full_path, (parent ? parent:""), i+startID) == 0 )
823 fileno++;
825 free(name);
826 free(namelist[i]);
828 free(namelist);
829 if( parent )
831 chdir(dirname((char*)dir));
833 else
835 DPRINTF(E_WARN, L_SCANNER, _("Scanning %s finished (%llu files)!\n"), dir, fileno);
839 void
840 start_scanner()
842 struct media_dir_s * media_path = media_dirs;
844 if (setpriority(PRIO_PROCESS, 0, 15) == -1)
845 DPRINTF(E_WARN, L_INOTIFY, "Failed to reduce scanner thread priority\n");
847 begin_scan();
848 freopen("/dev/null", "a", stderr);
849 while( media_path )
851 ScanDirectory(media_path->path, NULL, media_path->type);
852 media_path = media_path->next;
854 freopen("/proc/self/fd/2", "a", stderr);
855 end_scan();
857 /* Create this index after scanning, so it doesn't slow down the scanning process.
858 * This index is very useful for large libraries used with an XBox360 (or any
859 * client that uses UPnPSearch on large containers). */
860 sql_exec(db, "create INDEX IDX_SEARCH_OPT ON OBJECTS(OBJECT_ID, CLASS, DETAIL_ID);");
862 fill_playlists();
864 //JM: Set up a db version number, so we know if we need to rebuild due to a new structure.
865 sql_exec(db, "pragma user_version = %d;", DB_VERSION);