n4s4 / synology-api Goto Github PK
View Code? Open in Web Editor NEWA Python wrapper around Synology API
License: MIT License
A Python wrapper around Synology API
License: MIT License
I am trying to log in to NAS file station it is giving me following error
ConnectionError: HTTPSConnectionPool(host='https', port=443): Max retries exceeded with url: //IP:portwebapi/auth.cgi?api=SYNO.API.Auth&version=2&method=login&account=username&passwd=password&session=FileStation&format=cookie (Caused by NewConnectionError('<urllib3.connection.VerifiedHTTPSConnection object at 0x7f94b4092f10>: Failed to establish a new connection: [Errno -2] Name or service not known
If I usefl = filestation.FileStation('ip', 'port', 'user', 'pass', True)
with flag True i get error сertificate not verify, and then i do in auth.py
I add verify=Fasle in source code, please do it as parametr
session_request = requests.get(self._base_url + login_api, param, verify=False)
response = requests.get(self._base_url + query_path, list_query, verify=False).json()
response = requests.get(url, req_param, verify=False)
response = requests.post(url, req_param, verify=False)
synology-api/auth.py line42 in login
self._sid = session_request.json()['data']['sid']
KeyError: 'data'
Hello ! Thanks for the amazing project :)
I have got some issues using multiple additional parameters while using get_file_info method
Neither :
fl.get_file_info(path,additional = {'size','time','real_path'})
or
fl.get_file_info(path,additional = ['size','time','real_path'])
works.
I only have one parameter working or less, like when I use it this way :
fl.get_file_info(path',additional = 'size')
I only have the results like this, so with only one parameter (size, time, real_path or owner) working
{'data': {'files': [{'additional': {'size': 3342336}, 'isdir': False, 'name': 'file_name', 'path': '/path'}]}, 'success': True}
Thanks for your convenient library, but I'm having some problem when using get_md5_status
function.
My step:
start_md5_calc
and get return taskidget_md5_status
and taskid{'error': {'code': 599}, 'success': False}
.We've had the project working greatly and now enabled 2FA on our Synology. I updated the library (We were using one which is older than 2 years) and now I get the error:
TypeError: init() got an unexpected keyword argument 'dsm_version'
What am I doing wrong?
I'm trying to get a proper monitoring solution in place for a new client's hyper backup and have just been testing out some of the backup functions.
The list functions work great, as does the backup_task_result
, but I noticed none of the get functions like backup_task_status
, backup_repository_get
or backup_task_get
have any parameters, and simple return an error, presumably because they're expecting the ID of a task or repository.
I gave it a quick test by altering the file myself and including a taskid
parameter in the backup_task_get
function and it seemed to work at that point.
Is this something that can be fixed in this manner, or do they work perfectly and I'm just not using them correctly?
Do you have permission to set the API?
I have been trying to get backup task status via API calls for years with no success. I found a reference to user 'leirn' and their project to monitor Syno things with nagios. In there was the key, undocumented extended attributes to pass to the 'status' method of SYNO.Backup.Task. I have no idea how 'leirn' found this, credit to him or her. I am sorry I do not have time to properly contribute the code changes, so I offer the extended attributes to someone who does. The normal 'status' method will only return the job's config status, is it a job that is configured correctly. If what you really want is the result from the last time the job ran, get the task_id with a call to backup_task_list, then use this new function:
def backup_task_result(self, taskid):
api_name = 'SYNO.Backup.Task'
info = self.gen_list[api_name]
api_path = info['path']
req_param = {'version': info['minVersion'], 'method': 'status', 'blOnline': 'false', 'additional': '["last_bkp_time","next_bkp_time","last_bkp_result","is_modified","last_bkp_progress"]', 'task_id': taskid}
I tested it against DSM6, I have not upgraded to DSM7 yet so no idea how it works there.
when downloading a file, the code iterates over the request with iter_content(chunk_size=8192).
Downloading large files appears to be really slow. Would changing the chunk_size value help? Perhaps setting chunk_size to "None" ?
Can you please comment on this ?
Hey!
Loading any of the functions of the class Backup raises a KeyError.
I think there is an problem with the info = self.core_list[api_name]
.
If i change this to info = self.gen_list[api_name]
i'll get an Error 103 from the syno-nas.
If i then pin the version to '1' in the req_param, i'll get an Error 4400 from the syno-nas, but only at:
What i'm missing is an implementation of:
additional | "[\"last_bkp_time\",\"last_bkp_result\",\"get_source\",\"is_modified\",\"progress_title_type\"]"
api | "SYNO.Backup.Task"
method | "list"
version | "1"
which could be easily be done with:
def backup_task_list(self):
api_name = 'SYNO.Backup.Task'
info = self.gen_list[api_name]
api_path = info['path']
req_param = {'version': '1', 'method': 'list', 'additional': '["last_bkp_time","last_bkp_result","get_source","is_modified","progress_title_type"]'}
return self.request_data(api_name, api_path, req_param)
How are we supposed to fix this?
Should i raise a PR with the fix for the one function?
Thanks!
I recently upgraded from version 0.2.1 to version 0.4.1 of this package (so quite a big leap!). After that, logging in via the api would result in a response of {'error': {'code': 104}, 'success': False}
.
My machine is called the Synology RS1219+.
It works perfectly with version 0.2.1. Any idea what might have changed to break it?
chc@h03:~/git/sftpbox$ pip3 install git+https://github.com/N4S4/synology-api
Defaulting to user installation because normal site-packages is not writeable
Collecting git+https://github.com/N4S4/synology-api
Cloning https://github.com/N4S4/synology-api to /tmp/pip-req-build-rfkevfpr
Running command git clone --filter=blob:none --quiet https://github.com/N4S4/synology-api /tmp/pip-req-build-rfkevfpr
Resolved https://github.com/N4S4/synology-api to commit fabed66871c660f01440bc64efb97d86a437a798
Preparing metadata (setup.py) ... done
Requirement already satisfied: requests in /usr/lib/python3/dist-packages (from synology-api==0.4.4) (2.22.0)
chc@h03:~/git/sftpbox$ cat 1.py
#!/bin/env python3.8
# Version: v1.0.1
# Filename: 1.py
# Author: {{ author }}
# Description: ---
# HostName: {{ hostname }}
# Create: 2022-10-12 17:36:26
from synology_api import filestation, downloadstation
# Initiate the classes DownloadStation & FileStation with (ip_address, port, username, password)
# it will log in automatically
fl = filestation.FileStation('192.168.11.161', '5000', 'myaccount', 'mypassword', secure=False, cert_verify=False, dsm_version=6, debug=True, otp_code=None)
fl.get_list_share()
chc@h03:~/git/sftpbox$ python 1.py
You are now logged in!
chc@h03:~/git/sftpbox$
any suggestions ?
It's a good practice, I suppose, that to not use print() function for logging purpose, but define a logger on the logging library.
The advantages are :
I use create_folder api
path = "/mynas/example" desPath = str(302342) result = fl.create_folder(path, desPath)
but this code result is
{'error': {'code': 400}, 'success': False}
I can't create folder just number name
In filestation.py : the argument date_expired is mentioned in the function signature but it is not used (and the same for date_available) :
def create_sharing_link(self, path=None, password=None, date_expired=None, date_available=None):
...
def edit_shared_link(self, link_id=None, password=None, date_expired=None, date_available=None):
...
Would it be possible to allow for using this parameter ?
See this issue first: #7
Each api call in each class currently repeats a lot of the same code over and over. Using a decorator function could easily reduce the number of instances of the repeating code to one. This will be especially easy to do if #9 is implemented.
The decorator function would be defined as a method in the "Authentication" class:
def api_call(self, function):
...
and then can be used in each other class as so:
class DownloadStation(Authentication):
@self.api_call
def get_info(self):
...
Writing new methods and changing existing ones becomes much easier.
Pls support [offset] and [limit] parameter.It`s useful.
ths.
Python version: 3.6.8
Pip version: 19.3.1
Install command: python setup.py install
Traceback:
Traceback (most recent call last):
File "synology-api\setup.py", line 9, in <module>
long_description=open('README.md').read(),
UnicodeDecodeError: 'cp949' codec can't decode byte 0xe2 in position 6173: illegal multibyte sequence
Quick fix:
Delete all content of README.md and re-run the command.
"SyntaxWarning: "is" with a literal. Did you mean "=="?
if method is 'get':"
When comparing to string literals, it's more preferred to use a value comparison than an object identity comparison. In fact, I'm surprised it actually works...
Am I missing something?
I am wondering, is it possible to make a JavaScript wrapper with the same functionality like this wrapper ?
Have a problem with building the project using Python 3.5.3
File "d:\Projects\home-library\api\.venv35\lib\site-packages\synology_api\__init__.py", line 1, in from . import auth, filestation, downloadstation, audiostation, sys_info, virtualization File "d:\Projects\home-library\api\.venv35\lib\site-packages\synology_api\filestation.py", line 438 req_param['taskid'] = f'"{self._md5_calc_taskid}"'
Hi!
Thank you first for writing this package!
I was using the official document myself and tried to use those API with python in code instead of pasting their API in browser directly.
I then encountered an error whenever I want to do response.json()
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
I tried your package and it is the same error. In auth.py
login
function, line
self._sid = session_request.json()['data']['sid']
shows an syntax error of json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
.
I printed the response.text out and it is a long <!doctype html> <html> <head> <script async src="https://www.googletagmanager.com/gta........
HTML string.
I wonder whether you have encountered this issue when developing this package. In your case, your response.json() does not show any error?
hi:
我这里遇到一个问题,使用filestation.git_file()下载的zip或txt不完整,文件大小大概在1kB,这个有办法解决吗?我尝试去判断文件大小,如果小于10KB则重复下载,这样更多的时候是卡在循环里
May I know how to retrieve the folder tree using this API?
for example to retrieve the tree in google drive, https://stackoverflow.com/a/64984764/10189759
如果频繁请求会得到这个报错
On the auth.py, you creates the base_url using the params ip_address/port.
It should be great if we can directly give a base_url for some reasons :
I will try to submit a PR that can manage both ways, to not break something.
BUG Report:
upload file
fl = filestation.FileStation(.................
fl.upload_file('/file/dest','c:\temp\abc.zip')
if abc.zip large than 4G
Traceback (most recent call last):
File "main.py", line 43, in
File "synology_api\filestation.py", line 500, in upload_file
File "requests\sessions.py", line 590, in post
File "requests\sessions.py", line 542, in request
File "requests\sessions.py", line 655, in send
File "requests\adapters.py", line 449, in send
File "urllib3\connectionpool.py", line 706, in urlopen
File "urllib3\connectionpool.py", line 394, in _make_request
File "urllib3\connection.py", line 234, in request
File "http\client.py", line 1239, in request
File "http\client.py", line 1285, in _send_request
File "http\client.py", line 1234, in endheaders
File "http\client.py", line 1065, in _send_output
File "http\client.py", line 986, in send
File "ssl.py", line 975, in sendall
File "ssl.py", line 944, in send
File "ssl.py", line 642, in write
OverflowError: string longer than 2147483647 bytes
[6980] Failed to execute script 'main' due to unhandled exception!
OverflowError: string longer than 2147483647 bytes
See this issue first: #7
Many functions return strings instead of raising errors, returning success or failure values, etc. This should be changed. For example, Authentication.login should be changed from this:
` def login(self, application):
login_api = 'auth.cgi?api=SYNO.API.Auth'
param = {'version': '2', 'method': 'login', 'account': self._username,
'passwd': self._password, 'session': application, 'format': 'cookie'}
if not self._session_expire:
if self._sid is not None:
self._session_expire = False
return 'User already logged'
else:
session_request = requests.get(self._base_url + login_api, param)
self._sid = session_request.json()['data']['sid']
self._session_expire = False
return 'User logging... New session started!'`
to this:
class AlreadyLoggedInError(ConnectionRefusedError):
pass
_login_api = 'auth.cgi?api=SYNO.API.AUTH' #we use this multiple places so I've made it a hidden attribute
def login(self, application):
param = {'version': '2', 'method': 'login', 'account': self._username,
'passwd': self._password, 'session': application, 'format': 'cookie'}
if not self.session_expire:
if self.sid is not None:
self.session_expire = False
raise self.AlreadyLoggedInError("Already logged in.")
self.session = self._response(self._log_api, param)
self.sid = self.session.json()['data']['sid']
self.session_expire = False
return True #true for success, could also not return or return None.
This uses less memory and is quicker than allocating a string to return. We can let the user determine how they want to deal with the error or with success.
This will be a breaking change, so any existing scripts using the library would need to be updated. But doing things this way will be easier to maintain on top of the performance benefit.
Not sure if I read that correctly.
The parameter "path" is used for
url = ('%s%s' % (self.base_url, api_path)) + '?api=%s&version=%s&method=download&path=%s&mode=%s&_sid=%s' % ( api_name, info['maxVersion'], parse.quote_plus(path), mode, self._sid)
with session.get(url, stream=True) as r: r.raise_for_status() with open(os.path.basename(path), 'wb') as f:
These 2 would not be the same in most cases.
The function should take 2 separate parameters.
Any plans to support VideoStation API?
We are using the FileStation API and this package is really helpful but it is a bit annoying to use for the asyn requests in a fully automatic mode. Usually our workflow is the following:
The parsing could be easily prevented if the output of the "start" method was the task_id directly. If it seems ok with you, I am willing to do a PR where depending on an option in FileStation (or in the methods) the output is directly the task_id. This option would be set by default to False to keep backward compatibility.
This would lead to the following code (not sure about the parameter name):
fs = FileStation(..., dict_output=False) # default
res = fs.start_search(...)
# res is a str with the current message "You can now ..."
fs = FileStation(..., dict_output=True)
res = fs.start_search(...)
# res is of the form {"message": "'You can now ...", "task_id": 35000}
If that sounds ok with you, I can submit a PR soon.
Hello,
I have been using the "python-synology 1.0.0" library and I have managed to connect to my NAS by putting ("sensor12345.synology.me", "5000", "username", "password") but with this library I can't upload files.
I have seen this other “synology-api” library that I think can help me upload files to my NAS more easily.
The thing is that I try with:
from synology_api import filestation
fl = filestation.FileStation ('sensor12345.synology.me', '5000', 'username', 'password')
fl.get_info ()
and it gives me the following error:
Traceback (most recent call last):
File "C: /Users/moti/PycharmProjects/GUItutorial/venv/synology_wrapper_test.py", line 7, in
fl = filestation.FileStation ('sensor12345.synology.me', '5000', 'username', 'password')
File "C: \ Users \ moti \ PycharmProjects \ GUItutorial \ venv \ lib \ site-packages \ synology_api \ filestation.py", line 34, in init
self.session.login ('FileStation')
File "C: \ Users \ moti \ PycharmProjects \ GUItutorial \ venv \ lib \ site-packages \ synology_api \ auth.py", line 30, in login
self._sid = session_request.json () ['data'] ['sid']
KeyError: 'data'
I hope you can help me.
Thanks in advance
See this issue first: #7
Currently there's a lot of code repeated between each class. On top of that, each class receives an "Authentication" object as an argument. If we were use the "Authentication" object as a base class, each other class could inherit authentication attributes from the authentication object. This would make things easier to change as it would only need to change in one place instead of if every api.
As an example, downloadstation's class would change from this:
class DownloadStation:
def __init__(self, auth_args, ...):
self.session = syn.Authentication(auth_args)
to this:
class DownloadStation(syn.Authentication):
def __init__(self, auth_args, ...):
super(DownloadStation, self).__init__(auth_args)
We can then put any attributes repeated between classes into the "Authentication" class so that we aren't repeating so much code. Since the class will no longer be exclusively dealing with authentication if we make this change, I think it would be a good idea to change the Authentication class's name to just "Synology" as authentication would imply just authentication. Renaming is a separate issue however so I will open a separate issue report for it.
Before I begin with the issue, I want to say that I run the IT department for a small business that also owns a Synology. We need to automate some things on it and using you pre-written api will be quicker for us than me writing my own implementation from the ground up. That being said there are a few things that need to be changed to make the code ready for business use, and since you said you would accept help simplifying things I would like to take it upon myself to provide that help.
This issue and the next few that I plan to create are issues that I myself am working on in my fork, and I will create a pull request to implement the fixes when completed. I'm saying all this because I don't want you to see the number of issues I'm about to create and become stressed out that people are waiting on you to fix things.
Being a hobbyist, I'm not sure where your level of python skill is so if you have any questions about what I'm doing please feel free to ask. Let me know how experienced you are so that I can explain what I'm doing at the appropriate level. I don't want to talk down to you if you are more experienced than me, but I don't want to go over your head if you have less experience.
I will be writing up a few more issue reports to document what the issues I see are,and so that we can discuss how to best fix them. I don't want to come in and take your baby from you, so any feedback to what I do with the project is appreciated.
I will link this issue in the other reports. Once you've read this, feel free to close the issue.
Any plans to support Universal Search API?
The case is specific to a shared folder.
In order to check file permissions, I do :
from synology_api import filestation, downloadstation
fl = filestation.FileStation(<URL>, <port>, <userName>, <password>,secure=True)
fl.check_permissions(path="/SharedFolder/myFile.png", filename="myFile.png")
According to the permissions of the user, I get:
NO ACCESS : {'error': {'code': 407}, 'success': False}
FULL ACCESS : {'data': {'blSkip': False}, 'success': True}
READ ONLY: {'data': {'blSkip': False}, 'success': True}
Is there a way to discriminate between FULL ACCESS and READ ONLY access ?
Note that because of #54 I am still on v.0.1.3.1. I think v.0.4.0 broke that fix (but I have not investigated further yet)
Hello! I use your libs and its good) But I should ask you function save session
I every time create FileStation like this code
fl = filestation.FileStation(cp.synology_ip, cp.synology_port, cp.synology_user,
cp.synology_pass, cp.synology_https)
But for this need password and user. Storage pass and user require encrypt. What if I will store only session. But I cant create FileStation object only have sesiion.
Could you change your library so that you can initialize the object knowing the session.
thanks..
I’m trying to put together something similar for a nodejs project and have found it difficult to find documentation regarding all the iscsi methods.
Have you looked into implementing here and/or know where the documentation could be found?
I don’t actually own a device I’ve just had some requests to add synology support to my project.
I am just starting with the API and tried to connect using the core module.
For me, base_api_core.Core()
failed on line 9:
self.session.login('Core')
Relevant part of the traceback:
<...>synology_api\base_api_core.py in __init__(self, ip_address, port, username, password, secure, cert_verify, dsm_version, debug, otp_code)
7 self.session = syn.Authentication(ip_address, port, username, password, secure, cert_verify, dsm_version, debug, otp_code)
8
----> 9 self.session.login('Core')
10 self.session.get_api_list('Core')
11 self.session.get_api_list()
<...>synology_api\auth.py in login(self, application)
40 else:
41 session_request = requests.get(self._base_url + login_api, param, verify=self._verify)
---> 42 self._sid = session_request.json()['data']['sid']
43 self._session_expire = False
44 if self._debug is True:
KeyError: 'data'
The error is misleading, since auth.login()
has no error checking on the requests.get()
result, but what happens is that session_request .json()
has this content:
{'error': {'code': 402}, 'success': False}
And finally, 402
seems to be permission denied.
My first suggestion would be to improve the auth.login()
in a similar fashion to auth.logou()
and check for
if response.json()['success'] is True:
But there is also a PR which tries to improve the situation: #100
I like the addition of the error codes, but since the original author was not responding I wonder if it makes sense to make a similar PR which resolves your requests?
I think is time to create a WIKI for a better understanding
Maybe there is a way to hide "You are now logged in!", but I can't find it?!
Nas: DS21Plus
Code used:
from synology_api import filestation ip = 'xx.xx.xx.xx port = '5001' username = 'xxxxxxxxxxxxxxxxxxx' password = 'xxxxxxxxxxxxxxxxxxx' fl = filestation.FileStation(ip, port, username, password, secure=False, dsm_version=7, debug=True, otp_code=None)
Exception:
Traceback (most recent call last): File "x\test.py", line 8, in fl = downloadstation.DownloadStation(ip, port, username, password, secure=False, dsm_version=7, debug=True, otp_code=None) File "x\Python310\lib\site-packages\synology_api\downloadstation.py", line 11, in __init__ self.session.login('DownloadStation') File "x\Python310\lib\site-packages\synology_api\auth.py", line 42, in login self._sid = session_request.json()['data']['sid'] File "x\Python310\lib\site-packages\requests\models.py", line 910, in json return complexjson.loads(self.text, **kwargs) File "x\Python310\lib\json\__init__.py", line 346, in loads return _default_decoder.decode(s) File "x\Python310\lib\json\decoder.py", line 337, in decode obj, end = self.raw_decode(s, idx=_w(s, 0).end()) File "x\Python310\lib\json\decoder.py", line 355, in raw_decode raise JSONDecodeError("Expecting value", s, err.value) from None json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
Thanks for your help.
Does the Auth bypass 2FA? And if so, I guess I'm a little concerned about that, but glad that it does when I'm trying to access it...
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.