"Fossies" - the Fresh Open Source Software Archive

Member "minidlna-1.3.0/scanner.c" (24 Nov 2020, 30446 Bytes) of package /linux/privat/minidlna-1.3.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "scanner.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.2.1_vs_1.3.0.

    1 /* MiniDLNA media server
    2  * Copyright (C) 2008-2017  Justin Maggard
    3  *
    4  * This file is part of MiniDLNA.
    5  *
    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.
    9  *
   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.
   14  *
   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/>.
   17  */
   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/param.h>
   27 #include <sys/stat.h>
   28 #include <sys/time.h>
   29 #include <sys/resource.h>
   30 
   31 #include "config.h"
   32 
   33 #ifdef ENABLE_NLS
   34 #include <libintl.h>
   35 #endif
   36 #include <sqlite3.h>
   37 #include "libav.h"
   38 
   39 #include "scanner_sqlite.h"
   40 #include "upnpglobalvars.h"
   41 #include "metadata.h"
   42 #include "playlist.h"
   43 #include "utils.h"
   44 #include "sql.h"
   45 #include "scanner.h"
   46 #include "albumart.h"
   47 #include "containers.h"
   48 #include "log.h"
   49 #include "monitor.h"
   50 
   51 #if SCANDIR_CONST
   52 typedef const struct dirent scan_filter;
   53 #else
   54 typedef struct dirent scan_filter;
   55 #endif
   56 #ifndef AV_LOG_PANIC
   57 #define AV_LOG_PANIC AV_LOG_FATAL
   58 #endif
   59 
   60 int valid_cache = 0;
   61 
   62 struct virtual_item
   63 {
   64     int64_t objectID;
   65     char parentID[80];
   66     char name[256];
   67 };
   68 
   69 int64_t
   70 get_next_available_id(const char *table, const char *parentID)
   71 {
   72         char *ret, *base;
   73         int64_t objectID = 0;
   74 
   75         ret = sql_get_text_field(db, "SELECT OBJECT_ID from %s where ID = "
   76                                      "(SELECT max(ID) from %s where PARENT_ID = '%s')",
   77                                      table, table, parentID);
   78         if( ret )
   79         {
   80             base = strrchr(ret, '$');
   81             if( base )
   82                 objectID = strtoll(base+1, NULL, 16) + 1;
   83             sqlite3_free(ret);
   84         }
   85 
   86         return objectID;
   87 }
   88 
   89 int
   90 insert_container(const char *item, const char *rootParent, const char *refID, const char *class,
   91                  const char *artist, const char *genre, const char *album_art, int64_t *objectID, int64_t *parentID)
   92 {
   93     char *result;
   94     char *base;
   95     int ret = 0;
   96 
   97     result = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS o "
   98                     "left join DETAILS d on (o.DETAIL_ID = d.ID)"
   99                     " where o.PARENT_ID = '%s'"
  100                     " and o.NAME like '%q'"
  101                     " and d.ARTIST %s %Q"
  102                     " and o.CLASS = 'container.%s' limit 1",
  103                     rootParent, item, artist?"like":"is", artist, class);
  104     if( result )
  105     {
  106         base = strrchr(result, '$');
  107         if( base )
  108             *parentID = strtoll(base+1, NULL, 16);
  109         else
  110             *parentID = 0;
  111         *objectID = get_next_available_id("OBJECTS", result);
  112     }
  113     else
  114     {
  115         int64_t detailID = 0;
  116         *objectID = 0;
  117         *parentID = get_next_available_id("OBJECTS", rootParent);
  118         if( refID )
  119         {
  120             result = sql_get_text_field(db, "SELECT DETAIL_ID from OBJECTS where OBJECT_ID = %Q", refID);
  121             if( result )
  122                 detailID = strtoll(result, NULL, 10);
  123         }
  124         if( !detailID )
  125         {
  126             detailID = GetFolderMetadata(item, NULL, artist, genre, (album_art ? strtoll(album_art, NULL, 10) : 0));
  127         }
  128         ret = sql_exec(db, "INSERT into OBJECTS"
  129                            " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
  130                            "VALUES"
  131                            " ('%s$%llX', '%s', %Q, %lld, 'container.%s', '%q')",
  132                            rootParent, (long long)*parentID, rootParent,
  133                            refID, (long long)detailID, class, item);
  134     }
  135     sqlite3_free(result);
  136 
  137     return ret;
  138 }
  139 
  140 static void
  141 insert_containers(const char *name, const char *path, const char *refID, const char *class, int64_t detailID)
  142 {
  143     char sql[128];
  144     char **result;
  145     int ret;
  146     int cols, row;
  147     int64_t objectID, parentID;
  148 
  149     if( strstr(class, "imageItem") )
  150     {
  151         char *date_taken = NULL, *camera = NULL;
  152         static struct virtual_item last_date;
  153         static struct virtual_item last_cam;
  154         static struct virtual_item last_camdate;
  155         static long long last_all_objectID = 0;
  156 
  157         snprintf(sql, sizeof(sql), "SELECT DATE, CREATOR from DETAILS where ID = %lld", (long long)detailID);
  158         ret = sql_get_table(db, sql, &result, &row, &cols);
  159         if( ret == SQLITE_OK )
  160         {
  161             date_taken = result[2];
  162             camera = result[3];
  163         }
  164         if( date_taken )
  165             date_taken[10] = '\0';
  166         else
  167             date_taken = _("Unknown Date");
  168         if( !camera )
  169             camera = _("Unknown Camera");
  170 
  171         if( valid_cache && strcmp(last_date.name, date_taken) == 0 )
  172         {
  173             last_date.objectID++;
  174             //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last date item: %s/%s/%X\n", last_date.name, last_date.parentID, last_date.objectID);
  175         }
  176         else
  177         {
  178             insert_container(date_taken, IMAGE_DATE_ID, NULL, "album.photoAlbum", NULL, NULL, NULL, &objectID, &parentID);
  179             sprintf(last_date.parentID, IMAGE_DATE_ID"$%llX", (unsigned long long)parentID);
  180             last_date.objectID = objectID;
  181             strncpyt(last_date.name, date_taken, sizeof(last_date.name));
  182             //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached date item: %s/%s/%X\n", last_date.name, last_date.parentID, last_date.objectID);
  183         }
  184         sql_exec(db, "INSERT into OBJECTS"
  185                      " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
  186                      "VALUES"
  187                      " ('%s$%llX', '%s', '%s', '%s', %lld, %Q)",
  188                      last_date.parentID, (long long)last_date.objectID, last_date.parentID, refID, class, (long long)detailID, name);
  189 
  190         if( !valid_cache || strcmp(camera, last_cam.name) != 0 )
  191         {
  192             insert_container(camera, IMAGE_CAMERA_ID, NULL, "storageFolder", NULL, NULL, NULL, &objectID, &parentID);
  193             sprintf(last_cam.parentID, IMAGE_CAMERA_ID"$%llX", (long long)parentID);
  194             strncpyt(last_cam.name, camera, sizeof(last_cam.name));
  195             /* Invalidate last_camdate cache */
  196             last_camdate.name[0] = '\0';
  197         }
  198         if( valid_cache && strcmp(last_camdate.name, date_taken) == 0 )
  199         {
  200             last_camdate.objectID++;
  201             //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);
  202         }
  203         else
  204         {
  205             insert_container(date_taken, last_cam.parentID, NULL, "album.photoAlbum", NULL, NULL, NULL, &objectID, &parentID);
  206             sprintf(last_camdate.parentID, "%.63s$%llX", last_cam.parentID, (long long)parentID);
  207             last_camdate.objectID = objectID;
  208             strncpyt(last_camdate.name, date_taken, sizeof(last_camdate.name));
  209             //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);
  210         }
  211         sql_exec(db, "INSERT into OBJECTS"
  212                      " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
  213                      "VALUES"
  214                      " ('%s$%llX', '%s', '%s', '%s', %lld, %Q)",
  215                      last_camdate.parentID, last_camdate.objectID, last_camdate.parentID, refID, class, (long long)detailID, name);
  216         /* All Images */
  217         if( !last_all_objectID )
  218         {
  219             last_all_objectID = get_next_available_id("OBJECTS", IMAGE_ALL_ID);
  220         }
  221         sql_exec(db, "INSERT into OBJECTS"
  222                      " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
  223                      "VALUES"
  224                      " ('"IMAGE_ALL_ID"$%llX', '"IMAGE_ALL_ID"', '%s', '%s', %lld, %Q)",
  225                      last_all_objectID++, refID, class, (long long)detailID, name);
  226     }
  227     else if( strstr(class, "audioItem") )
  228     {
  229         snprintf(sql, sizeof(sql), "SELECT ALBUM, ARTIST, GENRE, ALBUM_ART from DETAILS where ID = %lld", (long long)detailID);
  230         ret = sql_get_table(db, sql, &result, &row, &cols);
  231         if( ret != SQLITE_OK )
  232             return;
  233         if( !row )
  234         {
  235             sqlite3_free_table(result);
  236             return;
  237         }
  238         char *album = result[4], *artist = result[5], *genre = result[6];
  239         char *album_art = result[7];
  240         static struct virtual_item last_album;
  241         static struct virtual_item last_artist;
  242         static struct virtual_item last_artistAlbum;
  243         static struct virtual_item last_artistAlbumAll;
  244         static struct virtual_item last_genre;
  245         static struct virtual_item last_genreArtist;
  246         static struct virtual_item last_genreArtistAll;
  247         static long long last_all_objectID = 0;
  248 
  249         if( album )
  250         {
  251             if( valid_cache && strcmp(album, last_album.name) == 0 )
  252             {
  253                 last_album.objectID++;
  254                 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last album item: %s/%s/%X\n", last_album.name, last_album.parentID, last_album.objectID);
  255             }
  256             else
  257             {
  258                 strncpyt(last_album.name, album, sizeof(last_album.name));
  259                 insert_container(album, MUSIC_ALBUM_ID, NULL, "album.musicAlbum", artist, genre, album_art, &objectID, &parentID);
  260                 sprintf(last_album.parentID, MUSIC_ALBUM_ID"$%llX", (long long)parentID);
  261                 last_album.objectID = objectID;
  262                 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached album item: %s/%s/%X\n", last_album.name, last_album.parentID, last_album.objectID);
  263             }
  264             sql_exec(db, "INSERT into OBJECTS"
  265                          " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
  266                          "VALUES"
  267                          " ('%s$%llX', '%s', '%s', '%s', %lld, %Q)",
  268                          last_album.parentID, last_album.objectID, last_album.parentID, refID, class, (long long)detailID, name);
  269         }
  270         if( artist )
  271         {
  272             if( !valid_cache || strcmp(artist, last_artist.name) != 0 )
  273             {
  274                 insert_container(artist, MUSIC_ARTIST_ID, NULL, "person.musicArtist", NULL, genre, NULL, &objectID, &parentID);
  275                 sprintf(last_artist.parentID, MUSIC_ARTIST_ID"$%llX", (long long)parentID);
  276                 strncpyt(last_artist.name, artist, sizeof(last_artist.name));
  277                 last_artistAlbum.name[0] = '\0';
  278                 /* Add this file to the "- All Albums -" container as well */
  279                 insert_container(_("- All Albums -"), last_artist.parentID, NULL, "album", artist, genre, NULL, &objectID, &parentID);
  280                 sprintf(last_artistAlbumAll.parentID, "%.63s$%llX", last_artist.parentID, (long long)parentID);
  281                 last_artistAlbumAll.objectID = objectID;
  282             }
  283             else
  284             {
  285                 last_artistAlbumAll.objectID++;
  286             }
  287             if( valid_cache && strcmp(album?album:_("Unknown Album"), last_artistAlbum.name) == 0 )
  288             {
  289                 last_artistAlbum.objectID++;
  290                 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last artist/album item: %s/%s/%X\n", last_artist.name, last_artist.parentID, last_artist.objectID);
  291             }
  292             else
  293             {
  294                 insert_container(album?album:_("Unknown Album"), last_artist.parentID, album?last_album.parentID:NULL,
  295                                  "album.musicAlbum", artist, genre, album_art, &objectID, &parentID);
  296                 sprintf(last_artistAlbum.parentID, "%.63s$%llX", last_artist.parentID, (long long)parentID);
  297                 last_artistAlbum.objectID = objectID;
  298                 strncpyt(last_artistAlbum.name, album ? album : _("Unknown Album"), sizeof(last_artistAlbum.name));
  299                 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached artist/album item: %s/%s/%X\n", last_artist.name, last_artist.parentID, last_artist.objectID);
  300             }
  301             sql_exec(db, "INSERT into OBJECTS"
  302                          " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
  303                          "VALUES"
  304                          " ('%s$%llX', '%s', '%s', '%s', %lld, %Q)",
  305                          last_artistAlbum.parentID, last_artistAlbum.objectID, last_artistAlbum.parentID, refID, class, (long long)detailID, name);
  306             sql_exec(db, "INSERT into OBJECTS"
  307                          " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
  308                          "VALUES"
  309                          " ('%s$%llX', '%s', '%s', '%s', %lld, %Q)",
  310                          last_artistAlbumAll.parentID, last_artistAlbumAll.objectID, last_artistAlbumAll.parentID, refID, class, (long long)detailID, name);
  311         }
  312         if( genre )
  313         {
  314             if( !valid_cache || strcmp(genre, last_genre.name) != 0 )
  315             {
  316                 insert_container(genre, MUSIC_GENRE_ID, NULL, "genre.musicGenre", NULL, NULL, NULL, &objectID, &parentID);
  317                 sprintf(last_genre.parentID, MUSIC_GENRE_ID"$%llX", (long long)parentID);
  318                 strncpyt(last_genre.name, genre, sizeof(last_genre.name));
  319                 /* Add this file to the "- All Artists -" container as well */
  320                 insert_container(_("- All Artists -"), last_genre.parentID, NULL, "person", NULL, genre, NULL, &objectID, &parentID);
  321                 sprintf(last_genreArtistAll.parentID, "%.63s$%llX", last_genre.parentID, (long long)parentID);
  322                 last_genreArtistAll.objectID = objectID;
  323             }
  324             else
  325             {
  326                 last_genreArtistAll.objectID++;
  327             }
  328             if( valid_cache && strcmp(artist?artist:_("Unknown Artist"), last_genreArtist.name) == 0 )
  329             {
  330                 last_genreArtist.objectID++;
  331             }
  332             else
  333             {
  334                 insert_container(artist?artist:_("Unknown Artist"), last_genre.parentID, artist?last_artist.parentID:NULL,
  335                                  "person.musicArtist", NULL, genre, NULL, &objectID, &parentID);
  336                 sprintf(last_genreArtist.parentID, "%.63s$%llX", last_genre.parentID, (long long)parentID);
  337                 last_genreArtist.objectID = objectID;
  338                 strncpyt(last_genreArtist.name, artist ? artist : _("Unknown Artist"), sizeof(last_genreArtist.name));
  339                 //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached genre/artist item: %s/%s/%X\n", last_genreArtist.name, last_genreArtist.parentID, last_genreArtist.objectID);
  340             }
  341             sql_exec(db, "INSERT into OBJECTS"
  342                          " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
  343                          "VALUES"
  344                          " ('%s$%llX', '%s', '%s', '%s', %lld, %Q)",
  345                          last_genreArtist.parentID, last_genreArtist.objectID, last_genreArtist.parentID, refID, class, (long long)detailID, name);
  346             sql_exec(db, "INSERT into OBJECTS"
  347                          " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
  348                          "VALUES"
  349                          " ('%s$%llX', '%s', '%s', '%s', %lld, %Q)",
  350                          last_genreArtistAll.parentID, last_genreArtistAll.objectID, last_genreArtistAll.parentID, refID, class, (long long)detailID, name);
  351         }
  352         /* All Music */
  353         if( !last_all_objectID )
  354         {
  355             last_all_objectID = get_next_available_id("OBJECTS", MUSIC_ALL_ID);
  356         }
  357         sql_exec(db, "INSERT into OBJECTS"
  358                      " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
  359                      "VALUES"
  360                      " ('"MUSIC_ALL_ID"$%llX', '"MUSIC_ALL_ID"', '%s', '%s', %lld, %Q)",
  361                      last_all_objectID++, refID, class, (long long)detailID, name);
  362     }
  363     else if( strstr(class, "videoItem") )
  364     {
  365         static long long last_all_objectID = 0;
  366 
  367         /* All Videos */
  368         if( !last_all_objectID )
  369         {
  370             last_all_objectID = get_next_available_id("OBJECTS", VIDEO_ALL_ID);
  371         }
  372         sql_exec(db, "INSERT into OBJECTS"
  373                      " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
  374                      "VALUES"
  375                      " ('"VIDEO_ALL_ID"$%llX', '"VIDEO_ALL_ID"', '%s', '%s', %lld, %Q)",
  376                      last_all_objectID++, refID, class, (long long)detailID, name);
  377         return;
  378     }
  379     else
  380     {
  381         return;
  382     }
  383     sqlite3_free_table(result);
  384     valid_cache = 1;
  385 }
  386 
  387 int64_t
  388 insert_directory(const char *name, const char *path, const char *base, const char *parentID, int objectID)
  389 {
  390     int64_t detailID = 0;
  391     char class[] = "container.storageFolder";
  392     char *result, *p;
  393     static char last_found[256] = "-1";
  394 
  395     if( strcmp(base, BROWSEDIR_ID) != 0 )
  396     {
  397         int found = 0;
  398         char id_buf[64], parent_buf[64], refID[64];
  399         char *dir_buf, *dir;
  400 
  401         dir_buf = strdup(path);
  402         dir = dirname(dir_buf);
  403         snprintf(refID, sizeof(refID), "%s%s$%X", BROWSEDIR_ID, parentID, objectID);
  404         snprintf(id_buf, sizeof(id_buf), "%s%s$%X", base, parentID, objectID);
  405         snprintf(parent_buf, sizeof(parent_buf), "%s%s", base, parentID);
  406         while( !found )
  407         {
  408             if( valid_cache && strcmp(id_buf, last_found) == 0 )
  409                 break;
  410             if( sql_get_int_field(db, "SELECT count(*) from OBJECTS where OBJECT_ID = '%s'", id_buf) > 0 )
  411             {
  412                 strcpy(last_found, id_buf);
  413                 break;
  414             }
  415             /* Does not exist.  Need to create, and may need to create parents also */
  416             result = sql_get_text_field(db, "SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s'", refID);
  417             if( result )
  418             {
  419                 detailID = strtoll(result, NULL, 10);
  420                 sqlite3_free(result);
  421             }
  422             sql_exec(db, "INSERT into OBJECTS"
  423                          " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
  424                          "VALUES"
  425                          " ('%s', '%s', %Q, %lld, '%s', '%q')",
  426                          id_buf, parent_buf, refID, detailID, class, strrchr(dir, '/')+1);
  427             if( (p = strrchr(id_buf, '$')) )
  428                 *p = '\0';
  429             if( (p = strrchr(parent_buf, '$')) )
  430                 *p = '\0';
  431             if( (p = strrchr(refID, '$')) )
  432                 *p = '\0';
  433             dir = dirname(dir);
  434         }
  435         free(dir_buf);
  436         return 0;
  437     }
  438 
  439     detailID = GetFolderMetadata(name, path, NULL, NULL, find_album_art(path, NULL, 0));
  440     sql_exec(db, "INSERT into OBJECTS"
  441                  " (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME) "
  442                  "VALUES"
  443                  " ('%s%s$%X', '%s%s', %lld, '%s', '%q')",
  444                  base, parentID, objectID, base, parentID, detailID, class, name);
  445 
  446     return detailID;
  447 }
  448 
  449 int
  450 insert_file(const char *name, const char *path, const char *parentID, int object, media_types types)
  451 {
  452     const char *class;
  453     char objectID[64];
  454     int64_t detailID = 0;
  455     char base[8];
  456     char *typedir_parentID;
  457     char *baseid;
  458     char *objname;
  459     media_types mtype = get_media_type(name);
  460 
  461     if( mtype == TYPE_IMAGE && (types & TYPE_IMAGE) )
  462     {
  463         if( is_album_art(name) )
  464             return -1;
  465         strcpy(base, IMAGE_DIR_ID);
  466         class = "item.imageItem.photo";
  467         detailID = GetImageMetadata(path, name);
  468     }
  469     else if( mtype == TYPE_VIDEO && (types & TYPE_VIDEO) )
  470     {
  471         strcpy(base, VIDEO_DIR_ID);
  472         class = "item.videoItem";
  473         detailID = GetVideoMetadata(path, name);
  474     }
  475     else if( mtype == TYPE_PLAYLIST && (types & TYPE_PLAYLIST) )
  476     {
  477         if( insert_playlist(path, name) == 0 )
  478             return 1;
  479     }
  480     /* Some file extensions can be used for both audio and video.
  481     ** Fall back to audio on these files if video parsing fails. */
  482     if (!detailID && (types & TYPE_AUDIO) && is_audio(name) )
  483     {
  484         strcpy(base, MUSIC_DIR_ID);
  485         class = "item.audioItem.musicTrack";
  486         detailID = GetAudioMetadata(path, name);
  487     }
  488     if( !detailID )
  489     {
  490         DPRINTF(E_WARN, L_SCANNER, "Unsuccessful getting details for %s\n", path);
  491         return -1;
  492     }
  493 
  494     snprintf(objectID, sizeof(objectID), "%s%s$%X", BROWSEDIR_ID, parentID, object);
  495     objname = strdup(name);
  496     strip_ext(objname);
  497 
  498     sql_exec(db, "INSERT into OBJECTS"
  499                  " (OBJECT_ID, PARENT_ID, CLASS, DETAIL_ID, NAME) "
  500                  "VALUES"
  501                  " ('%s', '%s%s', '%s', %lld, '%q')",
  502                  objectID, BROWSEDIR_ID, parentID, class, detailID, objname);
  503 
  504     if( *parentID )
  505     {
  506         int typedir_objectID = 0;
  507         typedir_parentID = strdup(parentID);
  508         baseid = strrchr(typedir_parentID, '$');
  509         if( baseid )
  510         {
  511             typedir_objectID = strtol(baseid+1, NULL, 16);
  512             *baseid = '\0';
  513         }
  514         insert_directory(objname, path, base, typedir_parentID, typedir_objectID);
  515         free(typedir_parentID);
  516     }
  517     sql_exec(db, "INSERT into OBJECTS"
  518                  " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
  519                  "VALUES"
  520                  " ('%s%s$%X', '%s%s', '%s', '%s', %lld, '%q')",
  521                  base, parentID, object, base, parentID, objectID, class, detailID, objname);
  522 
  523     insert_containers(objname, path, objectID, class, detailID);
  524     free(objname);
  525 
  526     return 0;
  527 }
  528 
  529 int
  530 CreateDatabase(void)
  531 {
  532     int ret, i;
  533     const char *containers[] = { "0","-1",   "root",
  534                             MUSIC_ID, "0", _("Music"),
  535                         MUSIC_ALL_ID, MUSIC_ID, _("All Music"),
  536                       MUSIC_GENRE_ID, MUSIC_ID, _("Genre"),
  537                      MUSIC_ARTIST_ID, MUSIC_ID, _("Artist"),
  538                       MUSIC_ALBUM_ID, MUSIC_ID, _("Album"),
  539                         MUSIC_DIR_ID, MUSIC_ID, _("Folders"),
  540                       MUSIC_PLIST_ID, MUSIC_ID, _("Playlists"),
  541 
  542                             VIDEO_ID, "0", _("Video"),
  543                         VIDEO_ALL_ID, VIDEO_ID, _("All Video"),
  544                         VIDEO_DIR_ID, VIDEO_ID, _("Folders"),
  545 
  546                             IMAGE_ID, "0", _("Pictures"),
  547                         IMAGE_ALL_ID, IMAGE_ID, _("All Pictures"),
  548                        IMAGE_DATE_ID, IMAGE_ID, _("Date Taken"),
  549                      IMAGE_CAMERA_ID, IMAGE_ID, _("Camera"),
  550                         IMAGE_DIR_ID, IMAGE_ID, _("Folders"),
  551 
  552                         BROWSEDIR_ID, "0", _("Browse Folders"),
  553             0 };
  554 
  555     ret = sql_exec(db, create_objectTable_sqlite);
  556     if( ret != SQLITE_OK )
  557         goto sql_failed;
  558     ret = sql_exec(db, create_detailTable_sqlite);
  559     if( ret != SQLITE_OK )
  560         goto sql_failed;
  561     ret = sql_exec(db, create_albumArtTable_sqlite);
  562     if( ret != SQLITE_OK )
  563         goto sql_failed;
  564     ret = sql_exec(db, create_captionTable_sqlite);
  565     if( ret != SQLITE_OK )
  566         goto sql_failed;
  567     ret = sql_exec(db, create_bookmarkTable_sqlite);
  568     if( ret != SQLITE_OK )
  569         goto sql_failed;
  570     ret = sql_exec(db, create_playlistTable_sqlite);
  571     if( ret != SQLITE_OK )
  572         goto sql_failed;
  573     ret = sql_exec(db, create_settingsTable_sqlite);
  574     if( ret != SQLITE_OK )
  575         goto sql_failed;
  576     ret = sql_exec(db, "INSERT into SETTINGS values ('UPDATE_ID', '0')");
  577     if( ret != SQLITE_OK )
  578         goto sql_failed;
  579     for( i=0; containers[i]; i=i+3 )
  580     {
  581         ret = sql_exec(db, "INSERT into OBJECTS (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME)"
  582                            " values "
  583                            "('%s', '%s', %lld, 'container.storageFolder', '%q')",
  584                            containers[i], containers[i+1], GetFolderMetadata(containers[i+2], NULL, NULL, NULL, 0), containers[i+2]);
  585         if( ret != SQLITE_OK )
  586             goto sql_failed;
  587     }
  588     for( i=0; magic_containers[i].objectid_match; i++ )
  589     {
  590         struct magic_container_s *magic = &magic_containers[i];
  591         if (!magic->name)
  592             continue;
  593         if( sql_get_int_field(db, "SELECT 1 from OBJECTS where OBJECT_ID = '%s'", magic->objectid_match) == 0 )
  594         {
  595             char *parent = strdup(magic->objectid_match);
  596             if (strrchr(parent, '$'))
  597                 *strrchr(parent, '$') = '\0';
  598             ret = sql_exec(db, "INSERT into OBJECTS (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME)"
  599                                " values "
  600                        "('%s', '%s', %lld, 'container.storageFolder', '%q')",
  601                        magic->objectid_match, parent,
  602                        GetFolderMetadata(_(magic->name), NULL, NULL, NULL, 0), _(magic->name));
  603             free(parent);
  604             if( ret != SQLITE_OK )
  605                 goto sql_failed;
  606         }
  607     }
  608     sql_exec(db, "create INDEX IDX_OBJECTS_OBJECT_ID ON OBJECTS(OBJECT_ID);");
  609     sql_exec(db, "create INDEX IDX_OBJECTS_PARENT_ID ON OBJECTS(PARENT_ID);");
  610     sql_exec(db, "create INDEX IDX_OBJECTS_DETAIL_ID ON OBJECTS(DETAIL_ID);");
  611     sql_exec(db, "create INDEX IDX_OBJECTS_CLASS ON OBJECTS(CLASS);");
  612     sql_exec(db, "create INDEX IDX_DETAILS_PATH ON DETAILS(PATH);");
  613     sql_exec(db, "create INDEX IDX_DETAILS_ID ON DETAILS(ID);");
  614     sql_exec(db, "create INDEX IDX_ALBUM_ART ON ALBUM_ART(ID);");
  615     sql_exec(db, "create INDEX IDX_SCANNER_OPT ON OBJECTS(PARENT_ID, NAME, OBJECT_ID);");
  616 
  617 sql_failed:
  618     if( ret != SQLITE_OK )
  619         DPRINTF(E_ERROR, L_DB_SQL, "Error creating SQLite3 database!\n");
  620     return (ret != SQLITE_OK);
  621 }
  622 
  623 static inline int
  624 filter_hidden(scan_filter *d)
  625 {
  626     return (d->d_name[0] != '.');
  627 }
  628 
  629 static int
  630 filter_type(scan_filter *d)
  631 {
  632 #if HAVE_STRUCT_DIRENT_D_TYPE
  633     return ( (d->d_type == DT_DIR) ||
  634          (d->d_type == DT_LNK) ||
  635          (d->d_type == DT_UNKNOWN)
  636         );
  637 #else
  638     return 1;
  639 #endif
  640 }
  641 
  642 static int
  643 filter_a(scan_filter *d)
  644 {
  645     return ( filter_hidden(d) &&
  646          (filter_type(d) ||
  647           (is_reg(d) &&
  648            (is_audio(d->d_name) ||
  649             is_playlist(d->d_name))))
  650         );
  651 }
  652 
  653 static int
  654 filter_av(scan_filter *d)
  655 {
  656     return ( filter_hidden(d) &&
  657          (filter_type(d) ||
  658           (is_reg(d) &&
  659            (is_audio(d->d_name) ||
  660             is_video(d->d_name) ||
  661             is_playlist(d->d_name))))
  662         );
  663 }
  664 
  665 static int
  666 filter_ap(scan_filter *d)
  667 {
  668     return ( filter_hidden(d) &&
  669          (filter_type(d) ||
  670           (is_reg(d) &&
  671            (is_audio(d->d_name) ||
  672             is_image(d->d_name) ||
  673             is_playlist(d->d_name))))
  674         );
  675 }
  676 
  677 static int
  678 filter_v(scan_filter *d)
  679 {
  680     return ( filter_hidden(d) &&
  681          (filter_type(d) ||
  682           (is_reg(d) &&
  683            is_video(d->d_name)))
  684         );
  685 }
  686 
  687 static int
  688 filter_vp(scan_filter *d)
  689 {
  690     return ( filter_hidden(d) &&
  691          (filter_type(d) ||
  692           (is_reg(d) &&
  693            (is_video(d->d_name) ||
  694             is_image(d->d_name))))
  695         );
  696 }
  697 
  698 static int
  699 filter_p(scan_filter *d)
  700 {
  701     return ( filter_hidden(d) &&
  702          (filter_type(d) ||
  703           (is_reg(d) &&
  704            is_image(d->d_name)))
  705         );
  706 }
  707 
  708 static int
  709 filter_avp(scan_filter *d)
  710 {
  711     return ( filter_hidden(d) &&
  712          (filter_type(d) ||
  713           (is_reg(d) &&
  714            (is_audio(d->d_name) ||
  715             is_image(d->d_name) ||
  716             is_video(d->d_name) ||
  717             is_playlist(d->d_name))))
  718         );
  719 }
  720 
  721 static void
  722 ScanDirectory(const char *dir, const char *parent, media_types dir_types)
  723 {
  724     struct dirent **namelist;
  725     int i, n, startID = 0;
  726     char *full_path;
  727     char *name = NULL;
  728     static long long unsigned int fileno = 0;
  729     enum file_types type;
  730 
  731     DPRINTF(parent?E_INFO:E_WARN, L_SCANNER, _("Scanning %s\n"), dir);
  732     switch( dir_types )
  733     {
  734         case ALL_MEDIA:
  735             n = scandir(dir, &namelist, filter_avp, alphasort);
  736             break;
  737         case TYPE_AUDIO:
  738             n = scandir(dir, &namelist, filter_a, alphasort);
  739             break;
  740         case TYPE_AUDIO|TYPE_VIDEO:
  741             n = scandir(dir, &namelist, filter_av, alphasort);
  742             break;
  743         case TYPE_AUDIO|TYPE_IMAGE:
  744             n = scandir(dir, &namelist, filter_ap, alphasort);
  745             break;
  746         case TYPE_VIDEO:
  747             n = scandir(dir, &namelist, filter_v, alphasort);
  748             break;
  749         case TYPE_VIDEO|TYPE_IMAGE:
  750             n = scandir(dir, &namelist, filter_vp, alphasort);
  751             break;
  752         case TYPE_IMAGE:
  753             n = scandir(dir, &namelist, filter_p, alphasort);
  754             break;
  755         default:
  756             n = -1;
  757             errno = EINVAL;
  758             break;
  759     }
  760     if( n < 0 )
  761     {
  762         DPRINTF(E_WARN, L_SCANNER, "Error scanning %s [%s]\n",
  763             dir, strerror(errno));
  764         return;
  765     }
  766 
  767     full_path = malloc(PATH_MAX);
  768     if (!full_path)
  769     {
  770         DPRINTF(E_ERROR, L_SCANNER, "Memory allocation failed scanning %s\n", dir);
  771         return;
  772     }
  773 
  774     if( !parent )
  775     {
  776         startID = get_next_available_id("OBJECTS", BROWSEDIR_ID);
  777     }
  778 
  779     for (i=0; i < n; i++)
  780     {
  781 #if !USE_FORK
  782         if( quitting )
  783             break;
  784 #endif
  785         type = TYPE_UNKNOWN;
  786         snprintf(full_path, PATH_MAX, "%s/%s", dir, namelist[i]->d_name);
  787         name = escape_tag(namelist[i]->d_name, 1);
  788         if( is_dir(namelist[i]) == 1 )
  789         {
  790             type = TYPE_DIR;
  791         }
  792         else if( is_reg(namelist[i]) == 1 )
  793         {
  794             type = TYPE_FILE;
  795         }
  796         else
  797         {
  798             type = resolve_unknown_type(full_path, dir_types);
  799         }
  800         if( (type == TYPE_DIR) && (access(full_path, R_OK|X_OK) == 0) )
  801         {
  802             char *parent_id;
  803             insert_directory(name, full_path, BROWSEDIR_ID, THISORNUL(parent), i+startID);
  804             xasprintf(&parent_id, "%s$%X", THISORNUL(parent), i+startID);
  805             ScanDirectory(full_path, parent_id, dir_types);
  806             free(parent_id);
  807         }
  808         else if( type == TYPE_FILE && (access(full_path, R_OK) == 0) )
  809         {
  810             if( insert_file(name, full_path, THISORNUL(parent), i+startID, dir_types) == 0 )
  811                 fileno++;
  812         }
  813         free(name);
  814         free(namelist[i]);
  815     }
  816     free(namelist);
  817     free(full_path);
  818     if( !parent )
  819     {
  820         DPRINTF(E_WARN, L_SCANNER, _("Scanning %s finished (%llu files)!\n"), dir, fileno);
  821     }
  822 }
  823 
  824 /* rescan functions added by shrimpkin@sourceforge.net */
  825 static int
  826 cb_orphans(void *args, int argc, char **argv, char **azColName)
  827 {
  828     const char *path = argv[0];
  829     const char *mime = argv[1];
  830 
  831     /* If we can't access the path, remove it */
  832     if (access(path, R_OK) != 0)
  833     {
  834         DPRINTF(E_DEBUG, L_SCANNER, "Removing %s [%s]\n", path, mime ? "file" : "dir");
  835         if (mime)
  836             monitor_remove_file(path);
  837         else
  838             monitor_remove_directory(0, path);
  839     }
  840 
  841     return 0;
  842 }
  843 
  844 void
  845 start_rescan(void)
  846 {
  847     struct media_dir_s *media_path;
  848     char *esc_name = NULL;
  849     char *zErrMsg;
  850     const char *sql_files = "SELECT path, mime FROM details WHERE path NOT NULL AND mime IS NOT NULL;";
  851     const char *sql_dir = "SELECT path, mime FROM details WHERE path NOT NULL AND mime IS NULL;";
  852     int changes = sqlite3_total_changes(db);
  853     const char *summary;
  854     int ret;
  855 
  856     DPRINTF(E_INFO, L_SCANNER, "Starting rescan\n");
  857 
  858     /* Find and remove any dead directory links */
  859     ret = sqlite3_exec(db, sql_dir, cb_orphans, NULL, &zErrMsg);
  860     if (ret != SQLITE_OK)
  861     {
  862         DPRINTF(E_MAXDEBUG, L_SCANNER, "SQL error: %s\nBAD SQL: %s\n", zErrMsg, sql_dir);
  863         sqlite3_free(zErrMsg);
  864     }
  865 
  866     /* Find and remove any dead file links */
  867     ret = sqlite3_exec(db, sql_files, cb_orphans, NULL, &zErrMsg);
  868     if (ret != SQLITE_OK)
  869     {
  870         DPRINTF(E_MAXDEBUG, L_SCANNER, "SQL error: %s\nBAD SQL: %s\n", zErrMsg, sql_files);
  871         sqlite3_free(zErrMsg);
  872     }
  873 
  874     /* Rescan media_paths for new and/or modified files */
  875     for (media_path = media_dirs; media_path != NULL; media_path = media_path->next)
  876     {
  877         char path[MAXPATHLEN], buf[MAXPATHLEN];
  878         strncpyt(path, media_path->path, sizeof(path));
  879         strncpyt(buf, media_path->path, sizeof(buf));
  880         esc_name = escape_tag(basename(buf), 1);
  881         monitor_insert_directory(0, esc_name, path);
  882         free(esc_name);
  883     }
  884     fill_playlists();
  885 
  886     if (sqlite3_total_changes(db) != changes)
  887         summary = "changes found";
  888     else
  889         summary = "no changes";
  890     DPRINTF(E_INFO, L_SCANNER, "Rescan completed. (%s)\n", summary);
  891 }
  892 /* end rescan functions */
  893 
  894 void
  895 start_scanner(void)
  896 {
  897     struct media_dir_s *media_path;
  898     char path[MAXPATHLEN];
  899 
  900     if (setpriority(PRIO_PROCESS, 0, 15) == -1)
  901         DPRINTF(E_WARN, L_INOTIFY,  "Failed to reduce scanner thread priority\n");
  902 
  903     setlocale(LC_COLLATE, "");
  904     lav_register_all();
  905     av_log_set_level(AV_LOG_PANIC);
  906 
  907     if( GETFLAG(RESCAN_MASK) )
  908         return start_rescan();
  909 
  910     for( media_path = media_dirs; media_path != NULL; media_path = media_path->next )
  911     {
  912         int64_t id;
  913         char *bname, *parent = NULL;
  914         char buf[8];
  915         strncpyt(path, media_path->path, sizeof(path));
  916         bname = basename(path);
  917         /* If there are multiple media locations, add a level to the ContentDirectory */
  918         if( !GETFLAG(MERGE_MEDIA_DIRS_MASK) && media_dirs->next )
  919         {
  920             int startID = get_next_available_id("OBJECTS", BROWSEDIR_ID);
  921             id = insert_directory(bname, path, BROWSEDIR_ID, "", startID);
  922             snprintf(buf, sizeof(buf), "$%X", startID);
  923             parent = buf;
  924         }
  925         else
  926             id = GetFolderMetadata(bname, media_path->path, NULL, NULL, 0);
  927         /* Use TIMESTAMP to store the media type */
  928         sql_exec(db, "UPDATE DETAILS set TIMESTAMP = %d where ID = %lld", media_path->types, (long long)id);
  929         ScanDirectory(media_path->path, parent, media_path->types);
  930         sql_exec(db, "INSERT into SETTINGS values (%Q, %Q)", "media_dir", media_path->path);
  931     }
  932     /* Create this index after scanning, so it doesn't slow down the scanning process.
  933      * This index is very useful for large libraries used with an XBox360 (or any
  934      * client that uses UPnPSearch on large containers). */
  935     sql_exec(db, "create INDEX IDX_SEARCH_OPT ON OBJECTS(OBJECT_ID, CLASS, DETAIL_ID);");
  936 
  937     fill_playlists();
  938 
  939     DPRINTF(E_DEBUG, L_SCANNER, "Initial file scan completed\n");
  940     //JM: Set up a db version number, so we know if we need to rebuild due to a new structure.
  941     sql_exec(db, "pragma user_version = %d;", DB_VERSION);
  942 }