Updates to Tomato RAF including NGINX && PHP
[tomato.git] / release / src / router / minidlna / scanner.c
blob4d2b07011e0810eb7099209026c1b211b466723f
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 "albumart.h"
45 #include "log.h"
47 int valid_cache = 0;
49 struct virtual_item
51 sqlite3_int64 objectID;
52 char parentID[64];
53 char name[256];
56 // Tomato
57 static int
58 is_external_path(const char * path)
60 struct statfs sf;
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);
67 else
69 return -1;
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.
79 int
80 wait_for_mount(const char * path)
82 int r, n = 50;
83 while ( ((r = is_external_path(path)) == 0) && (n-- > 0) )
85 usleep(100 * 1000);
87 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 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);
126 if( result )
128 base = strrchr(result, '$');
129 if( base )
130 *parentID = strtoll(base+1, NULL, 16);
131 else
132 *parentID = 0;
133 *objectID = get_next_available_id("OBJECTS", result);
135 else
137 *objectID = 0;
138 *parentID = get_next_available_id("OBJECTS", rootParent);
139 if( refID )
141 result = sql_get_text_field(db, "SELECT DETAIL_ID from OBJECTS where OBJECT_ID = %Q", refID);
142 if( result )
143 detailID = strtoll(result, NULL, 10);
145 if( !detailID )
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) "
151 "VALUES"
152 " ('%s$%"PRIX64"', '%s', %Q, %"PRId64", 'container.%s', '%q')",
153 rootParent, *parentID, rootParent, refID, detailID, class, item);
155 sqlite3_free(result);
157 return ret;
160 static void
161 insert_containers(const char * name, const char *path, const char * refID, const char * class, sqlite3_int64 detailID)
163 char sql[128];
164 char **result;
165 int ret;
166 int cols, row;
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 )
182 date = result[2];
183 cam = result[3];
186 if( date )
188 strncpy(date_taken, date, 10);
189 date_taken[10] = '\0';
191 else
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);
200 else
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) "
210 "VALUES"
211 " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
212 last_date.parentID, last_date.objectID, last_date.parentID, refID, class, detailID, name);
214 if( cam )
216 strncpy(camera, cam, 63);
217 camera[63] = '\0';
219 else
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);
235 else
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) "
245 "VALUES"
246 " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
247 last_camdate.parentID, last_camdate.objectID, last_camdate.parentID, refID, class, detailID, name);
248 /* All Images */
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) "
255 "VALUES"
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 )
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 *result, *p;
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 )
433 return 1;
436 if( refID )
438 char id_buf[64], parent_buf[64];
439 char *dir_buf, *dir;
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);
444 while( !found )
446 if( valid_cache && strcmp(id_buf, last_found) == 0 )
447 break;
448 if( sql_get_int_field(db, "SELECT count(*) from OBJECTS where OBJECT_ID = '%s'", id_buf) > 0 )
450 strcpy(last_found, id_buf);
451 break;
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);
455 if( result )
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) "
462 "VALUES"
463 " ('%s', '%s', %Q, %"PRId64", '%s', '%q')",
464 id_buf, parent_buf, refID, detailID, class, strrchr(dir, '/')+1);
465 if( (p = strrchr(id_buf, '$')) )
466 *p = '\0';
467 if( (p = strrchr(parent_buf, '$')) )
468 *p = '\0';
469 if( (p = strrchr(refID, '$')) )
470 *p = '\0';
471 dir = dirname(dir);
473 free(refID);
474 free(dir_buf);
475 return 0;
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) "
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 0;
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 "TIMESTAMP INTEGER, "
611 "TITLE TEXT COLLATE NOCASE, "
612 "DURATION TEXT, "
613 "BITRATE INTEGER, "
614 "SAMPLERATE INTEGER, "
615 "CREATOR TEXT COLLATE NOCASE, "
616 "ARTIST TEXT COLLATE NOCASE, "
617 "ALBUM TEXT COLLATE NOCASE, "
618 "GENRE TEXT COLLATE NOCASE, "
619 "COMMENT TEXT, "
620 "CHANNELS INTEGER, "
621 "DISC INTEGER, "
622 "TRACK INTEGER, "
623 "DATE DATE, "
624 "RESOLUTION TEXT, "
625 "THUMBNAIL BOOL DEFAULT 0, "
626 "ALBUM_ART INTEGER DEFAULT 0, "
627 "ROTATION INTEGER, "
628 "DLNA_PN TEXT, "
629 "MIME TEXT"
630 ")");
631 if( ret != SQLITE_OK )
632 goto sql_failed;
633 ret = sql_exec(db, "CREATE TABLE ALBUM_ART ( "
634 "ID INTEGER PRIMARY KEY AUTOINCREMENT, "
635 "PATH TEXT NOT NULL"
636 ")");
637 if( ret != SQLITE_OK )
638 goto sql_failed;
639 ret = sql_exec(db, "CREATE TABLE CAPTIONS ("
640 "ID INTEGER PRIMARY KEY, "
641 "PATH TEXT NOT NULL"
642 ")");
643 if( ret != SQLITE_OK )
644 goto sql_failed;
645 ret = sql_exec(db, "CREATE TABLE BOOKMARKS ("
646 "ID INTEGER PRIMARY KEY, "
647 "SEC INTEGER"
648 ")");
649 if( ret != SQLITE_OK )
650 goto sql_failed;
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"
657 ")");
658 if( ret != SQLITE_OK )
659 goto sql_failed;
660 ret = sql_exec(db, "CREATE TABLE SETTINGS ("
661 "UPDATE_ID INTEGER PRIMARY KEY DEFAULT 0, "
662 "FLAGS INTEGER DEFAULT 0"
663 ")");
664 if( ret != SQLITE_OK )
665 goto sql_failed;
666 ret = sql_exec(db, "INSERT into SETTINGS values (0, 0)");
667 if( ret != SQLITE_OK )
668 goto sql_failed;
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)"
672 " values "
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 )
676 goto sql_failed;
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);");
687 sql_failed:
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)
704 ) ));
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) )
716 ) );
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) )
728 ) );
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)
744 ) ));
747 void
748 ScanDirectory(const char * dir, const char * parent, enum media_types dir_type)
750 struct dirent **namelist;
751 int i, n, startID=0;
752 char parent_id[PATH_MAX];
753 char full_path[PATH_MAX];
754 char * name = NULL;
755 static long long unsigned int fileno = 0;
756 enum file_types type;
758 setlocale(LC_COLLATE, "");
759 if( chdir(dir) != 0 )
760 return;
762 if ( wait_for_mount(dir) < 0 )
763 return;
765 DPRINTF(parent?E_INFO:E_WARN, L_SCANNER, _("Scanning %s\n"), dir);
766 switch( dir_type )
768 case ALL_MEDIA:
769 n = scandir(".", &namelist, filter_media, alphasort);
770 break;
771 case AUDIO_ONLY:
772 n = scandir(".", &namelist, filter_audio, alphasort);
773 break;
774 case VIDEO_ONLY:
775 n = scandir(".", &namelist, filter_video, alphasort);
776 break;
777 case IMAGES_ONLY:
778 n = scandir(".", &namelist, filter_images, alphasort);
779 break;
780 default:
781 n = -1;
782 break;
784 if (n < 0) {
785 fprintf(stderr, "Error scanning %s [scandir]\n", dir);
786 return;
789 if( !parent )
791 startID = get_next_available_id("OBJECTS", BROWSEDIR_ID);
794 for (i=0; i < n; i++)
796 #if !USE_FORK
797 if( quitting )
798 break;
799 #endif
800 type = TYPE_UNKNOWN;
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 )
805 type = TYPE_DIR;
807 else if( namelist[i]->d_type == DT_REG )
809 type = TYPE_FILE;
811 else
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 )
824 fileno++;
826 free(name);
827 free(namelist[i]);
829 free(namelist);
830 if( parent )
832 chdir(dirname((char*)dir));
834 else
836 DPRINTF(E_WARN, L_SCANNER, _("Scanning %s finished (%llu files)!\n"), dir, fileno);
840 void
841 start_scanner()
843 struct media_dir_s * media_path = media_dirs;
844 char name[PATH_MAX];
846 if (setpriority(PRIO_PROCESS, 0, 15) == -1)
847 DPRINTF(E_WARN, L_INOTIFY, "Failed to reduce scanner thread priority\n");
849 begin_scan();
850 freopen("/dev/null", "a", stderr);
851 while( media_path )
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);
859 end_scan();
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");
870 else
872 fill_playlists();
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);