Observable .create(o => { const watcher = chokidar .watch(config.scanner.paths, Object.assign({ alwaysStat: true, awaitWriteFinish: { stabilityThreshold: 2000, pollInterval: 1000 } }, config.scanner)) .on('error', err => logger.error({ err })) .on('add', (path, stat) => o.next([ path, stat ])) .on('change', (path, stat) => o.next([ path, stat ])) .on('unlink', (path, stat) => o.next([ path ])) return () => watcher.close() }) // TODO (perf) groupBy + mergeMap with concurrency. .concatMap(async ([ mediaPath, mediaStat ]) => { const mediaId = getId(config.paths.media, mediaPath) try { if (!mediaStat) { await db.remove(await db.get(mediaId)) } else { await scanFile(mediaPath, mediaId, mediaStat) } } catch (err) { logger.error({ err }) } }) .subscribe()