-
-
Notifications
You must be signed in to change notification settings - Fork 170
Fix syncthing plugin #223
Fix syncthing plugin #223
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,14 +22,16 @@ | |
|
|
||
| class Plugin(PluginInstance, GlobalQueryHandler): | ||
|
|
||
| config_key = 'syncthing_api_key' | ||
| config_key = 'api_key' | ||
|
|
||
| def __init__(self): | ||
| PluginInstance.__init__(self) | ||
| GlobalQueryHandler.__init__(self) | ||
|
|
||
| self.iconUrls = ["xdg:syncthing", f"file:{Path(__file__).parent}/syncthing.svg"] | ||
| self._api_key = self.readConfig(self.config_key, str) | ||
| if self._api_key is None: | ||
| self._api_key = '' | ||
|
Comment on lines
+33
to
+34
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This fixes the widget not appearing with error Basically just initializes api_key to an empty string if an api key isn't found in the config. Otherwise it breaks because of it being
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be shortened to |
||
| if self._api_key: | ||
| self.st = Syncthing(self._api_key) | ||
|
|
||
|
|
@@ -39,6 +41,7 @@ def defaultTrigger(self): | |
| @property | ||
| def api_key(self) -> str: | ||
| return self._api_key | ||
| # return '1234' | ||
|
||
|
|
||
| @api_key.setter | ||
| def api_key(self, value: str): | ||
|
|
@@ -65,76 +68,90 @@ def configWidget(self): | |
| def handleGlobalQuery(self, query): | ||
|
|
||
| results = [] | ||
|
|
||
| if self.st: | ||
|
|
||
| config = self.st.system.config() | ||
|
|
||
| devices = dict() | ||
| for d in config['devices']: | ||
| if not d['name']: | ||
| d['name'] = d['deviceID'] | ||
| d['_shared_folders'] = {} | ||
| devices[d['deviceID']] = d | ||
|
|
||
| folders = dict() | ||
| for f in config['folders']: | ||
| if not f['label']: | ||
| f['label'] = f['id'] | ||
| for d in f['devices']: | ||
| devices[d['deviceID']]['_shared_folders'][f['id']] = f | ||
| folders[f['id']] = f | ||
|
|
||
| matcher = Matcher(query.string) | ||
|
|
||
| # create device items | ||
| for device_id, d in devices.items(): | ||
| device_name = d['name'] | ||
|
|
||
| if match := matcher.match(device_name): | ||
| device_folders = ", ".join([f['label'] for f in d['_shared_folders'].values()]) | ||
|
|
||
| actions = [] | ||
| if d['paused']: | ||
| actions.append( | ||
| Action("resume", "Resume synchronization", | ||
| lambda did=device_id: self.st.system.resume(did)) | ||
| ) | ||
| else: | ||
| actions.append( | ||
| Action("pause", "Pause synchronization", | ||
| lambda did=device_id: self.st.system.pause(did)) | ||
| try: | ||
| if self.st: | ||
| config = self.st.system.config() | ||
|
|
||
| devices = dict() | ||
| for d in config['devices']: | ||
| if not d['name']: | ||
| d['name'] = d['deviceID'] | ||
| d['_shared_folders'] = {} | ||
| devices[d['deviceID']] = d | ||
|
|
||
| folders = dict() | ||
| for f in config['folders']: | ||
| if not f['label']: | ||
| f['label'] = f['id'] | ||
| for d in f['devices']: | ||
| devices[d['deviceID']]['_shared_folders'][f['id']] = f | ||
| folders[f['id']] = f | ||
|
|
||
| matcher = Matcher(query.string) | ||
|
|
||
| # create device items | ||
| for device_id, d in devices.items(): | ||
| device_name = d['name'] | ||
|
|
||
| if match := matcher.match(device_name): | ||
| device_folders = ", ".join([f['label'] for f in d['_shared_folders'].values()]) | ||
|
|
||
| actions = [] | ||
| if d['paused']: | ||
| actions.append( | ||
| Action("resume", "Resume synchronization", | ||
| lambda did=device_id: self.st.system.resume(did)) | ||
| ) | ||
| else: | ||
| actions.append( | ||
| Action("pause", "Pause synchronization", | ||
| lambda did=device_id: self.st.system.pause(did)) | ||
| ) | ||
|
|
||
| item = StandardItem( | ||
| id=device_id, | ||
| text=f"{device_name}", | ||
| subtext=f"{'Paused ' if d['paused'] else ''}Syncthing device. " | ||
| f"Shared: {device_folders if device_folders else 'Nothing'}.", | ||
| iconUrls=self.iconUrls, | ||
| actions=actions | ||
| ) | ||
|
|
||
| results.append(RankItem(item, match)) | ||
|
|
||
| # create folder items | ||
| for folder_id, f in folders.items(): | ||
| folder_name = f['label'] | ||
| if match := matcher.match(folder_name): | ||
| folders_devices = ", ".join([devices[d['deviceID']]['name'] for d in f['devices']]) | ||
| item = StandardItem( | ||
| id=folder_id, | ||
| text=folder_name, | ||
| subtext=f"Syncthing folder {f['path']}. " | ||
| f"Shared with {folders_devices if folders_devices else 'nobody'}.", | ||
| iconUrls=self.iconUrls, | ||
| actions=[ | ||
| Action("scan", "Scan the folder", | ||
| lambda fid=folder_id: self.st.database.scan(fid)), | ||
| Action("open", "Open this folder in file browser", | ||
| lambda p=f['path']: openFile(p)) | ||
| ] | ||
| ) | ||
| results.append(RankItem(item, match)) | ||
|
Comment on lines
+70
to
+139
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This bit
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I maybe could do the try except only around the bit that checks if
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would suggest a few points to refactor this and make it more robust First: I would look into Second: You wouldn't need exception handling for empty API key. Instead, the first thing in Third: You should keep the try block for any call to syncthing, do not wrap it only for the initial call. It could go down between the loop iterations and you need to catch those errors. However, printing out "Invalid API key" may not be appropriate, as Syncthing could just be down/not available for any reason. To make it more readable, exporting this logic into another function and then calling that function in a try except block is also an option
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm crazy busy for the next few weeks, but I will mark this as draft for now and refactor when I am able. I'll let y'all know when it's ready for re-review |
||
| except: | ||
| if self._api_key == '': | ||
| item = StandardItem( | ||
| id=device_id, | ||
| text=f"{device_name}", | ||
| subtext=f"{'Paused ' if d['paused'] else ''}Syncthing device. " | ||
| f"Shared: {device_folders if device_folders else 'Nothing'}.", | ||
| iconUrls=self.iconUrls, | ||
| actions=actions | ||
| id="no_key", | ||
| text="Please enter your Syncthing API key in settings", | ||
| subtext="You can find your api key on your Syncthing web dashboard.", | ||
| iconUrls=self.iconUrls | ||
| ) | ||
|
|
||
| results.append(RankItem(item, match)) | ||
|
|
||
| # create folder items | ||
| for folder_id, f in folders.items(): | ||
| folder_name = f['label'] | ||
| if match := matcher.match(folder_name): | ||
| folders_devices = ", ".join([devices[d['deviceID']]['name'] for d in f['devices']]) | ||
| else: | ||
| item = StandardItem( | ||
| id=folder_id, | ||
| text=folder_name, | ||
| subtext=f"Syncthing folder {f['path']}. " | ||
| f"Shared with {folders_devices if folders_devices else 'nobody'}.", | ||
| iconUrls=self.iconUrls, | ||
| actions=[ | ||
| Action("scan", "Scan the folder", | ||
| lambda fid=folder_id: self.st.database.scan(fid)), | ||
| Action("open", "Open this folder in file browser", | ||
| lambda p=f['path']: openFile(p)) | ||
| ] | ||
| id="invalid_key", | ||
| text='Invalid API Key', | ||
| subtext=f"API Key {self._api_key} is invalid. Please try entering your API key again in settings.", | ||
| iconUrls=self.iconUrls | ||
| ) | ||
| results.append(RankItem(item, match)) | ||
|
|
||
| results.append(RankItem(item, 0)) | ||
|
Comment on lines
+141
to
+155
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So in theory, this except should only fire if the api key is invalid or missing. But I can see how we might want the logic to be a little less broad. Basically rn it just checks if the key is blank, at which point it reports missing, else it reports invalid. |
||
| return results | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to make the config key consistent with references throughout the rest of the script
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the code, it seems that
self.stis only defined when api key is set. So for anyone using this plugin and having that config present (maybe from a past version), this would be a breaking change. If I'm understanding this correctly, the plugin is not working properly for someone enabling it for the first time.I would recommend keeping the old config key name not to force people into having to input it again.