     case failed(Error?)
 }
 
+extension URL {
+    
+    var isDirectory: Bool {
+        guard isFileURL else { return false }
+        var directory: ObjCBool = false
+        return FileManager.default.fileExists(atPath: path, isDirectory: &directory) ? directory.boolValue : false
+    }
+    
+    var subDirectories: [URL] {
+        guard isDirectory else { return [] }
+        return (try? FileManager.default.contentsOfDirectory(at: self, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]).filter{ $0.isDirectory }) ?? []
+    }
+    
+}
+
 final class DownloadManager: NSObject {
     
     fileprivate let configuration = URLSessionConfiguration.background(withIdentifier: "WWDC Video Downloader")

    
    // MARK: File observation
    
    fileprivate var folderMonitor: DTFolderMonitor!
    fileprivate var existingVideoFiles = [String]()
    
    func monitorDownloadsFolder() {
        if folderMonitor != nil {
            folderMonitor.stopMonitoring()
            folderMonitor = nil
        }
        
        let videosPath = Preferences.shared.localVideoStorageURL.path
        enumerateVideoFiles(videosPath)
        
        folderMonitor = DTFolderMonitor(for: URL(fileURLWithPath: videosPath)) {
            self.enumerateVideoFiles(videosPath)
            
            NotificationCenter.default.post(name: .DownloadManagerFilesChangedNotification, object: nil)
        }
        
        folderMonitor.startMonitoring()
    }
    
    /// Updates the downloaded status for the sessions on the database based on the existence of the downloaded video file

                checkDownloadedState()
            }
            
            let started = nc.addObserver(forName: .DownloadManagerDownloadStarted, object: nil, queue: q) { note in
                guard asset.remoteURL == note.object as? String else { return }
                
                observer.onNext(.downloading(-1))
            }
            
            let cancelled = nc.addObserver(forName: .DownloadManagerDownloadCancelled, object: nil, queue: q) { note in
                guard asset.remoteURL == note.object as? String else { return }
                
                observer.onNext(.cancelled)
            }
            
            let paused = nc.addObserver(forName: .DownloadManagerDownloadPaused, object: nil, queue: q) { note in
                guard asset.remoteURL == note.object as? String else { return }
                
                observer.onNext(.paused)
            }
            
            let resumed = nc.addObserver(forName: .DownloadManagerDownloadResumed, object: nil, queue: q) { note in
                guard asset.remoteURL == note.object as? String else { return }
                
                observer.onNext(.downloading(-1))
            }
            
            let failed = nc.addObserver(forName: .DownloadManagerDownloadFailed, object: nil, queue: q) { note in
                guard asset.remoteURL == note.object as? String else { return }
                
                let error = note.userInfo?["error"] as? Error
                observer.onNext(.failed(error))
            }
            
            let finished = nc.addObserver(forName: .DownloadManagerDownloadFinished, object: nil, queue: q) { note in
                guard asset.remoteURL == note.object as? String else { return }
                
                observer.onNext(.finished)
            }
            
            let progress = nc.addObserver(forName: .DownloadManagerDownloadProgressChanged, object: nil, queue: q) { note in
                guard asset.remoteURL == note.object as? String else { return }
                
                if let info = note.userInfo?["info"] as? DownloadInfo {
                    observer.onNext(.downloading(info.progress))
                } else {
