MiniDLNA 1.0.22: cvs 2011-08-25
[tomato.git] / release / src / router / minidlna / scanner.c
blob7360ba7cc2c51661e5b0deb91ab41d97f9d155c7
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>
30 #include "config.h"
32 #ifdef ENABLE_NLS
33 #include <libintl.h>
34 #endif
35 #include <sqlite3.h>
37 #include "upnpglobalvars.h"
38 #include "metadata.h"
39 #include "playlist.h"
40 #include "utils.h"
41 #include "sql.h"
42 #include "scanner.h"
43 #include "albumart.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 sqlite_int64
56 get_next_available_id(const char * table, const char * parentID)
58 char *ret, *base;
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);
64 if( ret )
66 base = strrchr(ret, '$');
67 if( base )
68 objectID = strtoll(base+1, NULL, 16) + 1;
69 sqlite3_free(ret);
72 return objectID;
75 int
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)
79 char *result;
80 char *base;
81 int ret = 0;
82 sqlite_int64 detailID = 0;
84 result = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS"
85 " where PARENT_ID = '%s'"
86 " and NAME = '%q'"
87 " and CLASS = 'container.%s' limit 1",
88 rootParent, item, class);
89 if( result )
91 base = strrchr(result, '$');
92 if( base )
93 *parentID = strtoll(base+1, NULL, 16);
94 else
95 *parentID = 0;
96 *objectID = get_next_available_id("OBJECTS", result);
98 else
100 *objectID = 0;
101 *parentID = get_next_available_id("OBJECTS", rootParent);
102 if( refID )
104 result = sql_get_text_field(db, "SELECT DETAIL_ID from OBJECTS where OBJECT_ID = %Q", refID);
105 if( result )
106 detailID = strtoll(result, NULL, 10);
108 if( !detailID )
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) "
114 "VALUES"
115 " ('%s$%"PRIX64"', '%s', %Q, %"PRId64", 'container.%s', '%q')",
116 rootParent, *parentID, rootParent, refID, detailID, class, item);
118 sqlite3_free(result);
120 return ret;
123 static void
124 insert_containers(const char * name, const char *path, const char * refID, const char * class, sqlite3_int64 detailID)
126 char *sql;
127 char **result;
128 int ret;
129 int cols, row;
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);
143 free(sql);
144 if( ret == SQLITE_OK )
146 date = result[2];
147 cam = result[3];
150 if( date )
152 strncpy(date_taken, date, 10);
153 date_taken[10] = '\0';
155 else
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);
164 else
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) "
174 "VALUES"
175 " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
176 last_date.parentID, last_date.objectID, last_date.parentID, refID, class, detailID, name);
178 if( cam )
180 strncpy(camera, cam, 63);
181 camera[63] = '\0';
183 else
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);
199 else
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) "
209 "VALUES"
210 " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
211 last_camdate.parentID, last_camdate.objectID, last_camdate.parentID, refID, class, detailID, name);
212 /* All Images */
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) "
219 "VALUES"
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);
227 free(sql);
228 if( ret != SQLITE_OK )
229 return;
230 if( !row )
232 sqlite3_free_table(result);
233 return;
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;
246 if( album )
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);
253 else
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) "
263 "VALUES"
264 " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
265 last_album.parentID, last_album.objectID, last_album.parentID, refID, class, detailID, name);
267 if( artist )
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;
280 else
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);
289 else
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) "
300 "VALUES"
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) "
305 "VALUES"
306 " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
307 last_artistAlbumAll.parentID, last_artistAlbumAll.objectID, last_artistAlbumAll.parentID, refID, class, detailID, name);
309 if( genre )
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;
322 else
324 last_genreArtistAll.objectID++;
326 if( valid_cache && strcmp(artist?artist:_("Unknown Artist"), last_genreArtist.name) == 0 )
328 last_genreArtist.objectID++;
330 else
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) "
341 "VALUES"
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) "
346 "VALUES"
347 " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
348 last_genreArtistAll.parentID, last_genreArtistAll.objectID, last_genreArtistAll.parentID, refID, class, detailID, name);
350 /* All Music */
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) "
357 "VALUES"
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;
365 /* All Videos */
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) "
372 "VALUES"
373 " ('"VIDEO_ALL_ID"$%"PRIX64"', '"VIDEO_ALL_ID"', '%s', '%s', %"PRId64", %Q)",
374 last_all_objectID++, refID, class, detailID, name);
375 return;
377 else
379 return;
381 sqlite3_free_table(result);
382 valid_cache = 1;
386 insert_directory(const char * name, const char * path, const char * base, const char * parentID, int objectID)
388 int found = 0;
389 sqlite_int64 detailID = 0;
390 char * refID = NULL;
391 char class[] = "container.storageFolder";
392 char * id_buf = NULL;
393 char * parent_buf = NULL;
394 char *dir_buf, *dir;
395 char *result, *p;
396 static char last_found[256] = "-1";
398 if( strcmp(base, BROWSEDIR_ID) != 0 )
399 asprintf(&refID, "%s%s$%X", BROWSEDIR_ID, parentID, objectID);
401 if( refID )
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);
407 while( !found )
409 if( strcmp(id_buf, last_found) == 0 )
410 break;
411 if( sql_get_int_field(db, "SELECT count(*) from OBJECTS where OBJECT_ID = '%s'", id_buf) > 0 )
413 strcpy(last_found, id_buf);
414 break;
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);
418 if( result )
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) "
425 "VALUES"
426 " ('%s', '%s', %Q, %"PRId64", '%s', '%q')",
427 id_buf, parent_buf, refID, detailID, class, strrchr(dir, '/')+1);
428 if( (p = strrchr(id_buf, '$')) )
429 *p = '\0';
430 if( (p = strrchr(parent_buf, '$')) )
431 *p = '\0';
432 if( (p = strrchr(refID, '$')) )
433 *p = '\0';
434 dir = dirname(dir);
436 free(refID);
437 free(parent_buf);
438 free(id_buf);
439 free(dir_buf);
440 return 1;
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) "
446 "VALUES"
447 " ('%s%s$%X', '%s%s', %Q, %"PRId64", '%s', '%q')",
448 base, parentID, objectID, base, parentID, refID, detailID, class, name);
449 if( refID )
450 free(refID);
452 return -1;
456 insert_file(char * name, const char * path, const char * parentID, int object)
458 char class[32];
459 char objectID[64];
460 sqlite3_int64 detailID = 0;
461 char base[8];
462 char * typedir_parentID;
463 int typedir_objectID;
464 char * baseid;
465 char * orig_name = NULL;
467 if( is_image(name) )
469 if( is_album_art(name) )
470 return -1;
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);
481 if( !detailID )
482 strcpy(name, orig_name);
484 else if( is_playlist(name) )
486 if( insert_playlist(path, name) == 0 )
487 return 1;
489 if( !detailID && is_audio(name) )
491 strcpy(base, MUSIC_DIR_ID);
492 strcpy(class, "item.audioItem.musicTrack");
493 detailID = GetAudioMetadata(path, name);
495 if( orig_name )
496 free(orig_name);
497 if( !detailID )
499 DPRINTF(E_WARN, L_SCANNER, "Unsuccessful getting details for %s!\n", path);
500 return -1;
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) "
507 "VALUES"
508 " ('%s', '%s%s', '%s', %"PRId64", '%q')",
509 objectID, BROWSEDIR_ID, parentID, class, detailID, name);
511 if( *parentID )
513 typedir_objectID = 0;
514 typedir_parentID = strdup(parentID);
515 baseid = strrchr(typedir_parentID, '$');
516 if( baseid )
518 typedir_objectID = strtol(baseid+1, NULL, 16);
519 *baseid = '\0';
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) "
526 "VALUES"
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);
531 return 0;
535 CreateDatabase(void)
537 int ret, i;
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"),
558 0 };
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"
568 ");");
569 if( ret != SQLITE_OK )
570 goto sql_failed;
571 ret = sql_exec(db, "CREATE TABLE DETAILS ( "
572 "ID INTEGER PRIMARY KEY AUTOINCREMENT, "
573 "PATH TEXT DEFAULT NULL, "
574 "SIZE INTEGER, "
575 "TITLE TEXT COLLATE NOCASE, "
576 "DURATION TEXT, "
577 "BITRATE INTEGER, "
578 "SAMPLERATE INTEGER, "
579 "ARTIST TEXT COLLATE NOCASE, "
580 "ALBUM TEXT COLLATE NOCASE, "
581 "GENRE TEXT COLLATE NOCASE, "
582 "COMMENT TEXT, "
583 "CHANNELS INTEGER, "
584 "TRACK INTEGER, "
585 "DATE DATE, "
586 "RESOLUTION TEXT, "
587 "THUMBNAIL BOOL DEFAULT 0, "
588 "CREATOR TEXT COLLATE NOCASE, "
589 "DLNA_PN TEXT, "
590 "MIME TEXT, "
591 "ALBUM_ART INTEGER DEFAULT 0, "
592 "DISC INTEGER, "
593 "TIMESTAMP INTEGER"
594 ")");
595 if( ret != SQLITE_OK )
596 goto sql_failed;
597 ret = sql_exec(db, "CREATE TABLE ALBUM_ART ( "
598 "ID INTEGER PRIMARY KEY AUTOINCREMENT, "
599 "PATH TEXT NOT NULL"
600 ")");
601 if( ret != SQLITE_OK )
602 goto sql_failed;
603 ret = sql_exec(db, "CREATE TABLE CAPTIONS ("
604 "ID INTEGER PRIMARY KEY, "
605 "PATH TEXT NOT NULL"
606 ")");
607 if( ret != SQLITE_OK )
608 goto sql_failed;
609 ret = sql_exec(db, "CREATE TABLE BOOKMARKS ("
610 "ID INTEGER PRIMARY KEY, "
611 "SEC INTEGER"
612 ")");
613 if( ret != SQLITE_OK )
614 goto sql_failed;
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"
621 ")");
622 if( ret != SQLITE_OK )
623 goto sql_failed;
624 ret = sql_exec(db, "CREATE TABLE SETTINGS ("
625 "UPDATE_ID INTEGER PRIMARY KEY DEFAULT 0, "
626 "FLAGS INTEGER DEFAULT 0"
627 ")");
628 if( ret != SQLITE_OK )
629 goto sql_failed;
630 ret = sql_exec(db, "INSERT into SETTINGS values (0, 0)");
631 if( ret != SQLITE_OK )
632 goto sql_failed;
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)"
636 " values "
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 )
640 goto sql_failed;
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);");
651 sql_failed:
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)
668 ) ));
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) )
680 ) );
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) )
692 ) );
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)
708 ) ));
711 void
712 ScanDirectory(const char * dir, const char * parent, enum media_types dir_type)
714 struct dirent **namelist;
715 int i, n, startID=0;
716 char parent_id[PATH_MAX];
717 char full_path[PATH_MAX];
718 char * name = NULL;
719 static long long unsigned int fileno = 0;
720 enum file_types type;
722 setlocale(LC_COLLATE, "");
723 if( chdir(dir) != 0 )
724 return;
726 DPRINTF(parent?E_INFO:E_WARN, L_SCANNER, _("Scanning %s\n"), dir);
727 switch( dir_type )
729 case ALL_MEDIA:
730 n = scandir(".", &namelist, filter_media, alphasort);
731 break;
732 case AUDIO_ONLY:
733 n = scandir(".", &namelist, filter_audio, alphasort);
734 break;
735 case VIDEO_ONLY:
736 n = scandir(".", &namelist, filter_video, alphasort);
737 break;
738 case IMAGES_ONLY:
739 n = scandir(".", &namelist, filter_images, alphasort);
740 break;
741 default:
742 n = -1;
743 break;
745 if (n < 0) {
746 fprintf(stderr, "Error scanning %s [scandir]\n", dir);
747 return;
750 if( !parent )
752 startID = get_next_available_id("OBJECTS", BROWSEDIR_ID);
755 for (i=0; i < n; i++)
757 #if !USE_FORK
758 if( quitting )
759 break;
760 #endif
761 type = TYPE_UNKNOWN;
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 )
766 type = TYPE_DIR;
768 else if( namelist[i]->d_type == DT_REG )
770 type = TYPE_FILE;
772 else
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 )
785 fileno++;
787 free(name);
788 free(namelist[i]);
790 free(namelist);
791 if( parent )
793 chdir(dirname((char*)dir));
795 else
797 DPRINTF(E_WARN, L_SCANNER, _("Scanning %s finished (%llu files)!\n"), dir, fileno);
801 void
802 start_scanner()
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");
809 #ifdef READYNAS
810 FILE * flag = fopen("/ramfs/.upnp-av_scan", "w");
811 if( flag )
812 fclose(flag);
813 #endif
814 freopen("/dev/null", "a", stderr);
815 while( media_path )
817 ScanDirectory(media_path->path, NULL, media_path->type);
818 media_path = media_path->next;
820 freopen("/proc/self/fd/2", "a", stderr);
821 #ifdef READYNAS
822 if( access("/ramfs/.rescan_done", F_OK) == 0 )
823 system("/bin/sh /ramfs/.rescan_done");
824 unlink("/ramfs/.upnp-av_scan");
825 #endif
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);");
831 fill_playlists();
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);