Wrap up version 1.3.3.
[minidlna.git] / monitor.c
blobb5757d9d9b1453b28400945b67432dc72310d7c7
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/>.
18 #include "config.h"
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdbool.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <dirent.h>
27 #include <libgen.h>
28 #include <signal.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/time.h>
33 #include "libav.h"
35 #include "upnpglobalvars.h"
36 #include "monitor.h"
37 #include "utils.h"
38 #include "sql.h"
39 #include "scanner.h"
40 #include "metadata.h"
41 #include "albumart.h"
42 #include "playlist.h"
43 #include "log.h"
45 time_t next_pl_fill = 0;
47 int
48 monitor_remove_file(const char * path)
50 char sql[128];
51 char art_cache[PATH_MAX];
52 char *id;
53 char *ptr;
54 char **result;
55 int64_t detailID;
56 int rows, playlist;
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 */
63 valid_cache = 0;
64 playlist = is_playlist(path);
65 id = sql_get_text_field(db, "SELECT ID from %s where PATH = '%q'", playlist?"PLAYLISTS":"DETAILS", path);
66 if( !id )
67 return 1;
68 detailID = strtoll(id, NULL, 10);
69 sqlite3_free(id);
70 if( playlist )
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);
79 else
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) )
87 int i, children;
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]);
98 if( children < 0 )
99 continue;
100 if( children < 2 )
102 sql_exec(db, "DELETE from OBJECTS where OBJECT_ID = '%s'", result[i]);
104 ptr = strrchr(result[i], '$');
105 if( ptr )
106 *ptr = '\0';
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);
120 remove(art_cache);
122 return 0;
125 static char *
126 check_nfo(const char *path)
128 char file[PATH_MAX];
130 strncpyt(file, path, sizeof(file));
131 strip_ext(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)
140 int len;
141 char *last_dir;
142 char *path_buf;
143 char *base_name;
144 char *base_copy;
145 char *parent_buf = NULL;
146 char *id = NULL;
147 char video[PATH_MAX];
148 const char *tbl = "DETAILS";
149 int depth = 1;
150 int ts;
151 media_types dir_types;
152 media_types mtype = get_media_type(path);
153 struct stat st;
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)
161 tbl = "PLAYLISTS";
162 else if (mtype == TYPE_NFO)
164 char *vpath = check_nfo(path);
165 if (!vpath)
166 return -1;
167 strncpyt(video, vpath, sizeof(video));
168 sqlite3_free(vpath);
169 DPRINTF(E_DEBUG, L_INOTIFY, "Found modified nfo %s\n", video);
170 monitor_remove_file(video);
171 name = strrchr(video, '/');
172 if (!name)
173 return -1;
174 name++;
175 path = video;
176 mtype = TYPE_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))
182 return -1;
184 /* If it's already in the database and hasn't been modified, skip it. */
185 if( stat(path, &st) != 0 )
186 return -1;
188 ts = sql_get_int_field(db, "SELECT TIMESTAMP from %s where PATH = '%q'", tbl, path);
189 if( !ts )
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);
199 else
201 if( ts == st.st_mtime && !GETFLAG(RESCAN_MASK) )
202 DPRINTF(E_DEBUG, L_INOTIFY, "%s already exists\n", path);
203 return 0;
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)) )
211 return -1;
212 base_copy = base_name;
213 while( depth )
215 depth = 0;
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);
224 if( id )
226 if( !depth )
227 break;
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));
233 sqlite3_free(id);
234 break;
236 depth++;
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);
245 depth = 0;
246 break;
248 strcpy(path_buf, path);
250 free(last_dir);
251 free(path_buf);
252 free(base_name);
254 if( !depth )
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));
263 sqlite3_free(id);
265 return depth;
268 static bool
269 check_notsparse(const char *path)
270 #if HAVE_DECL_SEEK_HOLE
272 int fd;
273 bool rv;
275 if ((fd = open(path, O_RDONLY)) == -1)
276 return (false);
277 if (lseek(fd, 0, SEEK_HOLE) == lseek(fd, 0, SEEK_END))
278 rv = true;
279 else
280 rv = false;
281 close(fd);
282 return (rv);
284 #else
286 struct stat st;
288 return (stat(path, &st) == 0 && (st.st_blocks << 9 >= st.st_size));
290 #endif
293 monitor_insert_directory(int fd, char *name, const char * path)
295 DIR * ds;
296 struct dirent * e;
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));
305 return -1;
307 if( sql_get_int_field(db, "SELECT ID from DETAILS where PATH = '%q'", path) > 0 )
309 fd = 0;
310 if (!GETFLAG(RESCAN_MASK))
311 DPRINTF(E_DEBUG, L_INOTIFY, "%s already exists\n", path);
313 else
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));
318 if( !id )
319 id = sqlite3_mprintf("%s", BROWSEDIR_ID);
320 insert_directory(name, path, BROWSEDIR_ID, id+2, get_next_available_id("OBJECTS", id));
321 sqlite3_free(id);
322 free(parent_buf);
325 #ifdef HAVE_WATCH
326 if( fd > 0 )
327 monitor_add_watch(fd, path);
328 #endif
330 dir_types = valid_media_types(path);
332 ds = opendir(path);
333 if( !ds )
335 DPRINTF(E_ERROR, L_INOTIFY, "opendir failed! [%s]\n", strerror(errno));
336 return -1;
338 while( !quitting && (e = readdir(ds)) )
340 if( e->d_name[0] == '.' )
341 continue;
342 esc_name = escape_tag(e->d_name, 1);
343 snprintf(path_buf, sizeof(path_buf), "%s/%s", path, e->d_name);
344 switch( e->d_type )
346 case DT_DIR:
347 case DT_REG:
348 case DT_LNK:
349 case DT_UNKNOWN:
350 type = resolve_unknown_type(path_buf, dir_types);
351 default:
352 break;
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);
361 free(esc_name);
363 closedir(ds);
365 return 0;
369 monitor_remove_tree(const char * path)
371 char * sql;
372 char **result;
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) )
380 if( rows )
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);
388 ret = 0;
390 sqlite3_free_table(result);
392 sqlite3_free(sql);
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);
396 return ret;
400 monitor_remove_directory(int fd, const char * path)
402 /* Invalidate the scanner cache so we don't insert files into non-existent containers */
403 valid_cache = 0;
404 #ifdef HAVE_WATCH
405 if( fd > 0 )
407 monitor_remove_watch(fd, path);
409 #endif
410 return monitor_remove_tree(path);