Skip to content

Commit

Permalink
medialib: debounce file monitoring on mac, refresh after 5 sec of ina…
Browse files Browse the repository at this point in the history
…ctivity
  • Loading branch information
Oleksiy-Yakovenko committed Oct 19, 2023
1 parent a471938 commit b576213
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 37 deletions.
12 changes: 6 additions & 6 deletions osx/deadbeef.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1404,7 +1404,6 @@
2D7492871CCFFE7700D3A59E /* TestData in Resources */ = {isa = PBXBuildFile; fileRef = 2D7492861CCFFE7700D3A59E /* TestData */; };
2D76DD4B24F6D693000EDA3E /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D76DD3B24F6D693000EDA3E /* IOKit.framework */; };
2D78C535275583E900F96F9D /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D78C525275583E900F96F9D /* CoreServices.framework */; platformFilter = maccatalyst; };
2D78C54927568AC500F96F9D /* medialibfilesystem_mac.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D78C53F27568A1300F96F9D /* medialibfilesystem_mac.c */; };
2D78C54A27568AC500F96F9D /* medialibcommon.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D78C54327568A4D00F96F9D /* medialibcommon.c */; };
2D78C54B27568AC500F96F9D /* medialib.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D887BCD24B1C82A0078392F /* medialib.h */; };
2D78C54C27568AC500F96F9D /* medialibsource.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D78C5372756891400F96F9D /* medialibsource.c */; };
Expand Down Expand Up @@ -2014,6 +2013,7 @@
2DB1641B24FAB13400034E11 /* MediaLibraryManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DB1641924FAB13400034E11 /* MediaLibraryManager.m */; };
2DB2F73F240466AF00F7C000 /* PluginsPreferencesViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DB2F73D240466AF00F7C000 /* PluginsPreferencesViewController.h */; };
2DB2F740240466AF00F7C000 /* PluginsPreferencesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DB2F73E240466AF00F7C000 /* PluginsPreferencesViewController.m */; };
2DB4E0692AE18ECA00028624 /* medialibfilesystem_mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DB4E0682AE18ECA00028624 /* medialibfilesystem_mac.m */; };
2DB520E62AA8FADB00B6250A /* scriptable.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D135EE8226E38AF00BAAE84 /* scriptable.c */; };
2DB520E72AA8FF0800B6250A /* scriptable_tfquery.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D20924D2A9A077800CDBB2B /* scriptable_tfquery.c */; };
2DB521152AAC51F000B6250A /* scriptable.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D135EE8226E38AF00BAAE84 /* scriptable.c */; };
Expand Down Expand Up @@ -5964,7 +5964,6 @@
2D78C53A2756892300F96F9D /* medialibdb.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = medialibdb.h; sourceTree = "<group>"; };
2D78C53B2756892300F96F9D /* medialibdb.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = medialibdb.c; sourceTree = "<group>"; };
2D78C53E27568A1300F96F9D /* medialibfilesystem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = medialibfilesystem.h; sourceTree = "<group>"; };
2D78C53F27568A1300F96F9D /* medialibfilesystem_mac.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = medialibfilesystem_mac.c; sourceTree = "<group>"; };
2D78C54227568A4D00F96F9D /* medialibcommon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = medialibcommon.h; sourceTree = "<group>"; };
2D78C54327568A4D00F96F9D /* medialibcommon.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = medialibcommon.c; sourceTree = "<group>"; };
2D78C55727568B4A00F96F9D /* medialibscanner.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = medialibscanner.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -6605,6 +6604,7 @@
2DB1641924FAB13400034E11 /* MediaLibraryManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MediaLibraryManager.m; sourceTree = "<group>"; };
2DB2F73D240466AF00F7C000 /* PluginsPreferencesViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PluginsPreferencesViewController.h; sourceTree = "<group>"; };
2DB2F73E240466AF00F7C000 /* PluginsPreferencesViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PluginsPreferencesViewController.m; sourceTree = "<group>"; };
2DB4E0682AE18ECA00028624 /* medialibfilesystem_mac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = medialibfilesystem_mac.m; sourceTree = "<group>"; };
2DB522F72AACF09600B6250A /* gtkScriptableSelectViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = gtkScriptableSelectViewController.h; sourceTree = "<group>"; };
2DB522F82AACF09600B6250A /* gtkScriptableSelectViewController.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = gtkScriptableSelectViewController.c; sourceTree = "<group>"; };
2DB52F7A24CF7CF20046D516 /* TrackContextMenu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TrackContextMenu.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -10351,7 +10351,7 @@
2D78C53B2756892300F96F9D /* medialibdb.c */,
2D78C53A2756892300F96F9D /* medialibdb.h */,
2D78C565275698D400F96F9D /* medialibfilesystem_inotify.c */,
2D78C53F27568A1300F96F9D /* medialibfilesystem_mac.c */,
2DB4E0682AE18ECA00028624 /* medialibfilesystem_mac.m */,
2D78C5672756990B00F96F9D /* medialibfilesystem_stub.c */,
2D78C53E27568A1300F96F9D /* medialibfilesystem.h */,
2D78C55827568B4A00F96F9D /* medialibscanner.c */,
Expand Down Expand Up @@ -12139,10 +12139,10 @@
4D1B3E7118379829003E6066 = {
isa = PBXGroup;
children = (
2DA9777324C5EC26002405DA /* xcodeconfig */,
2D92D19C29B9044300218F1D /* include */,
2D92D1ED29B92DF900218F1D /* shared */,
2D92D18C29B9039A00218F1D /* src */,
2DA9777324C5EC26002405DA /* xcodeconfig */,
2D92D1ED29B92DF900218F1D /* shared */,
2D9E5BFE24AE6AAD0099B108 /* external */,
2DCD78AD19A6658C00EA50FA /* icons */,
4D1B514C1837EF7C003E6066 /* plugins */,
Expand Down Expand Up @@ -17048,9 +17048,9 @@
2D78C5642756919500F96F9D /* medialib.c in Sources */,
2D78C54C27568AC500F96F9D /* medialibsource.c in Sources */,
2D78C54A27568AC500F96F9D /* medialibcommon.c in Sources */,
2D78C54927568AC500F96F9D /* medialibfilesystem_mac.c in Sources */,
2D78C56127568FFB00F96F9D /* medialibscanner.c in Sources */,
2DB520E62AA8FADB00B6250A /* scriptable.c in Sources */,
2DB4E0692AE18ECA00028624 /* medialibfilesystem_mac.m in Sources */,
2D78C5622756915900F96F9D /* medialibtree.c in Sources */,
2DB520E72AA8FF0800B6250A /* scriptable_tfquery.c in Sources */,
);
Expand Down
6 changes: 3 additions & 3 deletions plugins/medialib/medialib.c
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ ml_insert_folder_at_index (ddb_mediasource_source_t *_source, const char *folder
}
json_decref(value);
_save_folders_config(source);
ml_watch_fs_start(source);
ml_source_update_fs_watch(source);
});
if (notify) {
ml_notify_listeners (source, DDB_MEDIALIB_MEDIASOURCE_EVENT_FOLDERS_DID_CHANGE);
Expand All @@ -377,7 +377,7 @@ ml_remove_folder_at_index (ddb_mediasource_source_t *_source, int index) {
notify = 1;
}
_save_folders_config(source);
ml_watch_fs_start(source);
ml_source_update_fs_watch(source);
});
if (notify) {
ml_notify_listeners (source, DDB_MEDIALIB_MEDIASOURCE_EVENT_FOLDERS_DID_CHANGE);
Expand All @@ -395,7 +395,7 @@ ml_append_folder (ddb_mediasource_source_t *_source, const char *folder) {
}
json_decref(value);
_save_folders_config(source);
ml_watch_fs_start(source);
ml_source_update_fs_watch(source);
});
if (notify) {
ml_notify_listeners (source, DDB_MEDIALIB_MEDIASOURCE_EVENT_FOLDERS_DID_CHANGE);
Expand Down
11 changes: 8 additions & 3 deletions plugins/medialib/medialibfilesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,15 @@

#include "medialibsource.h"

void
ml_watch_fs_start (medialib_source_t *source);
struct ml_watch_s;
typedef struct ml_watch_s ml_watch_t;

struct json_t;

ml_watch_t *
ml_watch_fs_start (struct json_t *musicpathsJson, void (*eventCallback)(void *), void *userdata);

void
ml_watch_fs_stop (medialib_source_t *source);
ml_watch_fs_stop (ml_watch_t *watch);

#endif /* medialibfilesystem_h */
10 changes: 7 additions & 3 deletions plugins/medialib/medialibfilesystem_inotify.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@
#include <stdio.h>
#include "medialibsource.h"

void
ml_watch_fs_start (medialib_source_t *source) {
struct ml_watch_s {
};

ml_watch_t *
ml_watch_fs_start (struct json_t *musicpathsJson, void (*eventCallback)(void *), void *userdata) {
return NULL;
}

void
ml_watch_fs_stop (medialib_source_t *source) {
ml_watch_fs_stop (ml_watch_t *wrapper) {
}

Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,46 @@

#include <CoreServices/CoreServices.h>
#include <jansson.h>
#include <dispatch/dispatch.h>
#import <Foundation/Foundation.h>
#include "medialibcommon.h"
#include "medialibfilesystem.h"
#include "medialibsource.h"

struct ml_watch_s {
void *watch; // strong DdbMLWatch *
};

@interface DdbMLWatch: NSObject {
FSEventStreamRef _eventStream;
void (*_callback)(void *);
void *_userData;
NSTimer *_debounceTimer;
}

@end

@implementation DdbMLWatch

static void
_FSEventStreamCallback(ConstFSEventStreamRef streamRef, void * __nullable clientCallBackInfo, size_t numEvents, void *eventPaths, const FSEventStreamEventFlags * _Nonnull eventFlags, const FSEventStreamEventId * _Nonnull eventIds) {
medialib_source_t *source = clientCallBackInfo;
ml_notify_listeners (source, DDB_MEDIASOURCE_EVENT_OUT_OF_SYNC);
DdbMLWatch *self = (__bridge DdbMLWatch *)clientCallBackInfo;
[self streamCallback];
}

void
ml_watch_fs_start (medialib_source_t *source) {
ml_watch_fs_stop(source);
- (instancetype)initWithMusicPath:(struct json_t * _Nonnull)musicpathsJson callback:(void (* _Nonnull)(void *))callback userData:(void *)userData {
self = [super init];

_callback = callback;
_userData = userData;

FSEventStreamContext context = {0};
context.info = source;
context.info = (__bridge void *)self;

size_t count = json_array_size(source->musicpaths_json);
size_t count = json_array_size(musicpathsJson);
CFMutableArrayRef arrayRef = CFArrayCreateMutable(NULL, count, NULL);
for (int i = 0; i < count; i++) {
json_t *data = json_array_get (source->musicpaths_json, i);
json_t *data = json_array_get (musicpathsJson, i);
if (json_is_string (data)) {
const char *bytes = json_string_value (data);
CFStringRef stringRef = CFStringCreateWithBytes(NULL, (const UInt8 *)bytes, strlen(bytes), kCFStringEncodingUTF8, FALSE);
Expand All @@ -57,19 +76,47 @@ ml_watch_fs_start (medialib_source_t *source) {
FSEventStreamScheduleWithRunLoop(eventStream, CFRunLoopGetMain(), kCFRunLoopCommonModes);
FSEventStreamStart(eventStream);

source->fs_watcher = eventStream;
return self;
}

- (void)stop {
[_debounceTimer invalidate];
FSEventStreamStop(_eventStream);
FSEventStreamUnscheduleFromRunLoop(_eventStream, CFRunLoopGetMain(), kCFRunLoopCommonModes);
FSEventStreamRelease(_eventStream);
}

- (void)streamCallback {
[_debounceTimer invalidate];
__weak DdbMLWatch *weakSelf = self;
_debounceTimer = [NSTimer timerWithTimeInterval:5 repeats:NO block:^(NSTimer * _Nonnull timer) {
DdbMLWatch *self = weakSelf;
if (self != nil) {
self->_callback(self->_userData);
self->_debounceTimer = nil;
}
}];
[NSRunLoop.mainRunLoop addTimer:_debounceTimer forMode:NSRunLoopCommonModes];
}

@end

ml_watch_t *
ml_watch_fs_start (struct json_t *musicpathsJson, void (*eventCallback)(void *), void *userdata) {
ml_watch_t *wrapper = calloc(sizeof (ml_watch_t), 1);
wrapper->watch = (__bridge_retained void *)[[DdbMLWatch alloc] initWithMusicPath:musicpathsJson callback:eventCallback userData:userdata];
return wrapper;
}

void
ml_watch_fs_stop (medialib_source_t *source) {
if (source->fs_watcher == NULL) {
ml_watch_fs_stop (ml_watch_t *wrapper) {
if (wrapper == NULL) {
return;
}
DdbMLWatch *watch = (__bridge_transfer DdbMLWatch *)wrapper->watch;
free (wrapper);
wrapper = NULL;

FSEventStreamRef eventStream = source->fs_watcher;
FSEventStreamStop(eventStream);
FSEventStreamUnscheduleFromRunLoop(eventStream, CFRunLoopGetMain(), kCFRunLoopCommonModes);
FSEventStreamRelease(eventStream);
source->fs_watcher = NULL;
[watch stop];
}

10 changes: 7 additions & 3 deletions plugins/medialib/medialibfilesystem_stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@
#include <stdio.h>
#include "medialibsource.h"

void
ml_watch_fs_start (medialib_source_t *source) {
struct ml_watch_s {
};

ml_watch_t *
ml_watch_fs_start (struct json_t *musicpathsJson, void (*eventCallback)(void *), void *userdata) {
return NULL;
}

void
ml_watch_fs_stop (medialib_source_t *source) {
ml_watch_fs_stop (ml_watch_t *wrapper) {
}

17 changes: 15 additions & 2 deletions plugins/medialib/medialibsource.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,19 @@ _ml_source_get_music_paths (medialib_source_t *source, size_t *medialib_paths_co

return medialib_paths;
}

static void
_fs_watch_callback (void *userdata) {
medialib_source_t *source = userdata;
ml_notify_listeners (source, DDB_MEDIASOURCE_EVENT_OUT_OF_SYNC);
}

void
ml_source_update_fs_watch(medialib_source_t *source) {
ml_watch_fs_stop(source->fs_watcher);
source->fs_watcher = ml_watch_fs_start(source->musicpaths_json, _fs_watch_callback, source);
}

ddb_mediasource_source_t *
ml_create_source (const char *source_path) {
medialib_source_t *source = calloc (1, sizeof (medialib_source_t));
Expand All @@ -162,7 +175,7 @@ ml_create_source (const char *source_path) {
snprintf (plpath, sizeof (plpath), "%s/medialib.dbpl", deadbeef->get_system_dir (DDB_SYS_DIR_CONFIG));
_ml_load_playlist(source, plpath);
dispatch_sync(source->sync_queue, ^{
ml_watch_fs_start(source);
ml_source_update_fs_watch(source);
});
});

Expand All @@ -174,7 +187,7 @@ ml_free_source (ddb_mediasource_source_t *_source) {
medialib_source_t *source = (medialib_source_t *)_source;

dispatch_sync(source->sync_queue, ^{
ml_watch_fs_stop(source);
ml_watch_fs_stop(source->fs_watcher);
source->scanner_terminate = 1;
});

Expand Down
6 changes: 5 additions & 1 deletion plugins/medialib/medialibsource.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include <dispatch/dispatch.h>
#include "medialibdb.h"
#include "medialibfilesystem.h"

#define MAX_LISTENERS 10

Expand All @@ -34,7 +35,7 @@ typedef struct medialib_source_s {
dispatch_queue_t scanner_queue;
dispatch_queue_t sync_queue;

void *fs_watcher;
ml_watch_t *fs_watcher;

// The following properties should only be accessed / changed on the sync_queue
int64_t scanner_current_index;
Expand Down Expand Up @@ -76,4 +77,7 @@ _ml_get_music_paths (medialib_source_t *source);
void
ml_source_init (DB_functions_t *_deadbeef);

void
ml_source_update_fs_watch(medialib_source_t *source);

#endif /* medialibsource_h */

0 comments on commit b576213

Please sign in to comment.