1 /* MiniDLNA media server
2 * Copyright (C) 2008-2010 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/>.
30 #include <sys/types.h>
35 #include "upnpglobalvars.h"
45 time_t next_pl_fill
= 0;
48 monitor_remove_file(const char * path
)
51 char art_cache
[PATH_MAX
];
58 if( is_caption(path
) )
60 return sql_exec(db
, "DELETE from CAPTIONS where PATH = '%q'", path
);
62 /* Invalidate the scanner cache so we don't insert files into non-existent containers */
64 playlist
= is_playlist(path
);
65 id
= sql_get_text_field(db
, "SELECT ID from %s where PATH = '%q'", playlist
?"PLAYLISTS":"DETAILS", path
);
68 detailID
= strtoll(id
, NULL
, 10);
72 sql_exec(db
, "DELETE from PLAYLISTS where ID = %lld", detailID
);
73 sql_exec(db
, "DELETE from DETAILS where ID ="
74 " (SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s$%llX')",
75 MUSIC_PLIST_ID
, detailID
);
76 sql_exec(db
, "DELETE from OBJECTS where OBJECT_ID = '%s$%llX' or PARENT_ID = '%s$%llX'",
77 MUSIC_PLIST_ID
, detailID
, MUSIC_PLIST_ID
, detailID
);
81 /* Delete the parent containers if we are about to empty them. */
82 snprintf(sql
, sizeof(sql
), "SELECT PARENT_ID from OBJECTS where DETAIL_ID = %lld"
83 " and PARENT_ID not like '64$%%'",
84 (long long int)detailID
);
85 if( (sql_get_table(db
, sql
, &result
, &rows
, NULL
) == SQLITE_OK
) )
88 for( i
= 1; i
<= rows
; i
++ )
90 /* If it's a playlist item, adjust the item count of the playlist */
91 if( strncmp(result
[i
], MUSIC_PLIST_ID
, strlen(MUSIC_PLIST_ID
)) == 0 )
93 sql_exec(db
, "UPDATE PLAYLISTS set FOUND = (FOUND-1) where ID = %d",
94 atoi(strrchr(result
[i
], '$') + 1));
97 children
= sql_get_int_field(db
, "SELECT count(*) from OBJECTS where PARENT_ID = '%s'", result
[i
]);
102 sql_exec(db
, "DELETE from OBJECTS where OBJECT_ID = '%s'", result
[i
]);
104 ptr
= strrchr(result
[i
], '$');
107 if( sql_get_int_field(db
, "SELECT count(*) from OBJECTS where PARENT_ID = '%s'", result
[i
]) == 0 )
109 sql_exec(db
, "DELETE from OBJECTS where OBJECT_ID = '%s'", result
[i
]);
113 sqlite3_free_table(result
);
115 /* Now delete the actual objects */
116 sql_exec(db
, "DELETE from DETAILS where ID = %lld", detailID
);
117 sql_exec(db
, "DELETE from OBJECTS where DETAIL_ID = %lld", detailID
);
119 snprintf(art_cache
, sizeof(art_cache
), "%s/art_cache%s", db_path
, path
);
126 check_nfo(const char *path
)
130 strncpyt(file
, path
, sizeof(file
));
133 return sql_get_text_field(db
, "SELECT PATH from DETAILS where (PATH > '%q.' and PATH <= '%q.z')"
134 " and MIME glob 'video/*' limit 1", file
, file
);
138 monitor_insert_file(const char *name
, const char *path
)
145 char *parent_buf
= NULL
;
147 char video
[PATH_MAX
];
148 const char *tbl
= "DETAILS";
151 media_types dir_types
;
152 media_types mtype
= get_media_type(path
);
155 /* Is it cover art for another file? */
156 if (mtype
== TYPE_IMAGE
)
157 update_if_album_art(path
);
158 else if (mtype
== TYPE_CAPTION
)
159 check_for_captions(path
, 0);
160 else if (mtype
== TYPE_PLAYLIST
)
162 else if (mtype
== TYPE_NFO
)
164 char *vpath
= check_nfo(path
);
167 strncpyt(video
, vpath
, sizeof(video
));
169 DPRINTF(E_DEBUG
, L_INOTIFY
, "Found modified nfo %s\n", video
);
170 monitor_remove_file(video
);
171 name
= strrchr(video
, '/');
179 /* Check if we're supposed to be scanning for this file type in this directory */
180 dir_types
= valid_media_types(path
);
181 if (!(mtype
& dir_types
))
184 /* If it's already in the database and hasn't been modified, skip it. */
185 if( stat(path
, &st
) != 0 )
188 ts
= sql_get_int_field(db
, "SELECT TIMESTAMP from %s where PATH = '%q'", tbl
, path
);
191 DPRINTF(E_DEBUG
, L_INOTIFY
, "Adding: %s\n", path
);
193 else if( ts
!= st
.st_mtime
)
195 DPRINTF(E_DEBUG
, L_INOTIFY
, "%s is %s than the last db entry.\n",
196 path
, (ts
> st
.st_mtime
) ? "older" : "newer");
197 monitor_remove_file(path
);
201 if( ts
== st
.st_mtime
&& !GETFLAG(RESCAN_MASK
) )
202 DPRINTF(E_DEBUG
, L_INOTIFY
, "%s already exists\n", path
);
206 /* Find the parentID. If it's not found, create all necessary parents. */
207 len
= strlen(path
)+1;
208 if( !(path_buf
= malloc(len
)) ||
209 !(last_dir
= malloc(len
)) ||
210 !(base_name
= malloc(len
)) )
212 base_copy
= base_name
;
216 strcpy(path_buf
, path
);
217 parent_buf
= dirname(path_buf
);
221 //DEBUG DPRINTF(E_DEBUG, L_INOTIFY, "Checking %s\n", parent_buf);
222 id
= sql_get_text_field(db
, "SELECT OBJECT_ID from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
223 " where d.PATH = '%q' and REF_ID is NULL", parent_buf
);
228 DPRINTF(E_DEBUG
, L_INOTIFY
, "Found first known parentID: %s [%s]\n", id
, parent_buf
);
229 /* Insert newly-found directory */
230 strcpy(base_name
, last_dir
);
231 base_copy
= basename(base_name
);
232 insert_directory(base_copy
, last_dir
, BROWSEDIR_ID
, id
+2, get_next_available_id("OBJECTS", id
));
237 strcpy(last_dir
, parent_buf
);
238 parent_buf
= dirname(parent_buf
);
240 while( strcmp(parent_buf
, "/") != 0 );
242 if( strcmp(parent_buf
, "/") == 0 )
244 id
= sqlite3_mprintf("%s", BROWSEDIR_ID
);
248 strcpy(path_buf
, path
);
256 //DEBUG DPRINTF(E_DEBUG, L_INOTIFY, "Inserting %s\n", name);
257 int ret
= insert_file(name
, path
, id
+2, get_next_available_id("OBJECTS", id
), dir_types
);
258 if (ret
== 1 && (mtype
& TYPE_PLAYLIST
))
260 next_pl_fill
= time(NULL
) + 120; // Schedule a playlist scan for 2 minutes from now.
261 //DEBUG DPRINTF(E_MAXDEBUG, L_INOTIFY, "Playlist scan scheduled for %s", ctime(&next_pl_fill));
269 check_notsparse(const char *path
)
270 #if HAVE_DECL_SEEK_HOLE
275 if ((fd
= open(path
, O_RDONLY
)) == -1)
277 if (lseek(fd
, 0, SEEK_HOLE
) == lseek(fd
, 0, SEEK_END
))
288 return (stat(path
, &st
) == 0 && (st
.st_blocks
<< 9 >= st
.st_size
));
293 monitor_insert_directory(int fd
, char *name
, const char * path
)
297 char *id
, *parent_buf
, *esc_name
;
298 char path_buf
[PATH_MAX
];
299 enum file_types type
= TYPE_UNKNOWN
;
300 media_types dir_types
;
302 if( access(path
, R_OK
|X_OK
) != 0 )
304 DPRINTF(E_WARN
, L_INOTIFY
, "Could not access %s [%s]\n", path
, strerror(errno
));
307 if( sql_get_int_field(db
, "SELECT ID from DETAILS where PATH = '%q'", path
) > 0 )
310 if (!GETFLAG(RESCAN_MASK
))
311 DPRINTF(E_DEBUG
, L_INOTIFY
, "%s already exists\n", path
);
315 parent_buf
= strdup(path
);
316 id
= sql_get_text_field(db
, "SELECT OBJECT_ID from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
317 " WHERE d.PATH = '%q' and REF_ID is NULL", dirname(parent_buf
));
319 id
= sqlite3_mprintf("%s", BROWSEDIR_ID
);
320 insert_directory(name
, path
, BROWSEDIR_ID
, id
+2, get_next_available_id("OBJECTS", id
));
327 monitor_add_watch(fd
, path
);
330 dir_types
= valid_media_types(path
);
335 DPRINTF(E_ERROR
, L_INOTIFY
, "opendir failed! [%s]\n", strerror(errno
));
338 while( !quitting
&& (e
= readdir(ds
)) )
340 if( e
->d_name
[0] == '.' )
342 esc_name
= escape_tag(e
->d_name
, 1);
343 snprintf(path_buf
, sizeof(path_buf
), "%s/%s", path
, e
->d_name
);
350 type
= resolve_unknown_type(path_buf
, dir_types
);
354 if( type
== TYPE_DIR
)
356 monitor_insert_directory(fd
, esc_name
, path_buf
);
358 else if( type
== TYPE_FILE
&& check_notsparse(path_buf
)) {
359 monitor_insert_file(esc_name
, path_buf
);
369 monitor_remove_tree(const char * path
)
373 int64_t detailID
= 0;
374 int rows
, i
, ret
= 1;
376 sql
= sqlite3_mprintf("SELECT ID from DETAILS where (PATH > '%q/' and PATH <= '%q/%c')"
377 " or PATH = '%q'", path
, path
, 0xFF, path
);
378 if( (sql_get_table(db
, sql
, &result
, &rows
, NULL
) == SQLITE_OK
) )
382 for( i
=1; i
<= rows
; i
++ )
384 detailID
= strtoll(result
[i
], NULL
, 10);
385 sql_exec(db
, "DELETE from DETAILS where ID = %lld", detailID
);
386 sql_exec(db
, "DELETE from OBJECTS where DETAIL_ID = %lld", detailID
);
390 sqlite3_free_table(result
);
393 /* Clean up any album art entries in the deleted directory */
394 sql_exec(db
, "DELETE from ALBUM_ART where (PATH > '%q/' and PATH <= '%q/%c')", path
, path
, 0xFF);
400 monitor_remove_directory(int fd
, const char * path
)
402 /* Invalidate the scanner cache so we don't insert files into non-existent containers */
407 monitor_remove_watch(fd
, path
);
410 return monitor_remove_tree(path
);