syncthing sync completed or not #1
69
syncthing_sync.py
Normal file
69
syncthing_sync.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
import os
|
||||
import xml.etree.ElementTree as ET
|
||||
import requests
|
||||
|
||||
# Global variables for API access - These variables do not change and are set once when the app starts.
|
||||
API_URL = os.getenv('SYNCTHING_API_URL', 'http://127.0.0.1:8384')
|
||||
API_KEY = None
|
||||
|
||||
def get_api_key(config_file):
|
||||
""" Retrieve the API key from the local Syncthing configuration XML file. """
|
||||
|
||||
try:
|
||||
tree = ET.parse(config_file)
|
||||
root = tree.getroot()
|
||||
return root.find('.//apikey').text
|
||||
except FileNotFoundError:
|
||||
raise Exception("Configuration file not found.")
|
||||
except AttributeError:
|
||||
raise Exception("API key not found in the configuration file.")
|
||||
|
||||
|
||||
def syncthing_request(url):
|
||||
""" Generic function for Syncthing API GET requests. """
|
||||
PeterSurda
commented
I think we only need I think we only need `GET`.
|
||||
headers = {'X-Api-Key': API_KEY}
|
||||
response = requests.get(url, headers=headers)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
|
||||
def check_folder_idle(folder_id):
|
||||
""" Check if the specified folder is idle. """
|
||||
status_url = f"{API_URL}/rest/db/status?folder={folder_id}"
|
||||
status = syncthing_request(status_url)
|
||||
return status['state'] == 'idle'
|
||||
|
||||
def long_poll_events(last_event_id, folder_id):
|
||||
""" Long poll the events endpoint filtering by the last known event ID and specific events. """
|
||||
events_url = f"{API_URL}/rest/events?since={last_event_id}&events=StateChanged&timeout=60"
|
||||
events = syncthing_request(events_url)
|
||||
PeterSurda
commented
One more thing, we can filter the events and then the code looks nicer:
and then you don't need to check inside One more thing, we can filter the events and then the code looks nicer:
`events_url = f"{api_url}/rest/events?since={last_event_id}&events=StateChanged&timeout=60"`
and then you don't need to check inside `main` for `event['type']` anymore.
|
||||
return [event for event in events if event['data']['folder'] == folder_id]
|
||||
|
||||
def main():
|
||||
global API_KEY
|
||||
folder_id = os.getenv('SYNCTHING_FOLDER_ID')
|
||||
config_file = os.getenv('SYNCTHING_CONFIG_FILE')
|
||||
|
||||
if not folder_id:
|
||||
raise ValueError("Folder ID must be provided as an environment variable.")
|
||||
|
||||
API_KEY = get_api_key(config_file)
|
||||
|
||||
# Start by getting the latest event ID
|
||||
current_events = syncthing_request(f"{API_URL}/rest/events?limit=1")
|
||||
last_event_id = current_events[0]['id'] if current_events else 0
|
||||
|
||||
# Check initial state
|
||||
PeterSurda
commented
It unnecessarily queries and sleeps. I'd prefer something like this:
I.e. it's not necessary to query the folder state in a loop, as we have the value in the event already. Long poll has a timeout so there's no need for a separate sleep. We could also make the It unnecessarily queries and sleeps. I'd prefer something like this:
```
is_idle = check_folder_idle()
while not is_idle:
events = long_poll_events()
for event in events:
if event['type'] == 'StateChanged',
event['data']['folder'] == folder_id:
is_idle = event['data']['to'] == 'idle'
if is_idle:
break
```
I.e. it's not necessary to query the folder state in a loop, as we have the value in the event already. Long poll has a timeout so there's no need for a separate sleep.
We could also make the `api_url` and `api_key` variables global, or a class variable, or a module variable, or a class property, because it doesn't change, it's only set once, when the app starts.
|
||||
is_idle = check_folder_idle(folder_id)
|
||||
|
||||
while not is_idle:
|
||||
events = long_poll_events(last_event_id, folder_id)
|
||||
for event in events:
|
||||
last_event_id = event['id']
|
||||
is_idle = event['data']['to'] == 'idle'
|
||||
if is_idle:
|
||||
print("Folder transitioned to idle.")
|
||||
break
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Reference in New Issue
Block a user
Check here:
The config file is XML, not JSON.