66import os
77import tempfile
88import json
9+ import unicodedata
910
1011
1112class SynologyFileStation :
@@ -16,7 +17,7 @@ def __init__(self, base_url: str, session_id: str):
1617 self .session_id = session_id
1718 self .api_url = f"{ self .base_url } /webapi/entry.cgi"
1819
19- def _make_request (self , api : str , version : str , method : str , ** params ) -> Dict [str , Any ]:
20+ def _make_request (self , api : str , version : str , method : str , use_post : bool = False , ** params ) -> Dict [str , Any ]:
2021 """Make a request to Synology API."""
2122 request_params = {
2223 'api' : api ,
@@ -26,13 +27,36 @@ def _make_request(self, api: str, version: str, method: str, **params) -> Dict[s
2627 ** params
2728 }
2829
29- response = requests .get (self .api_url , params = request_params )
30+ if use_post :
31+ # For POST requests, ensure UTF-8 encoding for Unicode characters
32+ response = requests .post (
33+ self .api_url ,
34+ data = request_params ,
35+ headers = {'Content-Type' : 'application/x-www-form-urlencoded; charset=utf-8' }
36+ )
37+ else :
38+ response = requests .get (self .api_url , params = request_params )
3039 response .raise_for_status ()
3140
3241 data = response .json ()
3342 if not data .get ('success' ):
3443 error_code = data .get ('error' , {}).get ('code' , 'unknown' )
35- raise Exception (f"Synology API error: { error_code } " )
44+ error_info = data .get ('error' , {})
45+
46+ # Include detailed error information if available
47+ error_message = f"Synology API error: { error_code } "
48+
49+ # Check for detailed errors array as mentioned in documentation
50+ if 'errors' in error_info and error_info ['errors' ]:
51+ detailed_errors = []
52+ for err in error_info ['errors' ]:
53+ err_detail = f"Code { err .get ('code' , 'unknown' )} "
54+ if 'path' in err :
55+ err_detail += f" for path: { err ['path' ]} "
56+ detailed_errors .append (err_detail )
57+ error_message += f" - Details: { '; ' .join (detailed_errors )} "
58+
59+ raise Exception (error_message )
3660
3761 return data .get ('data' , {})
3862
@@ -62,6 +86,9 @@ def _format_path(self, path: str) -> str:
6286 path = '/' + path
6387 if path != '/' and path .endswith ('/' ):
6488 path = path .rstrip ('/' )
89+
90+ # Normalize Unicode characters to NFC form (most common for filesystems)
91+ path = unicodedata .normalize ('NFC' , path )
6592 return path
6693
6794 def list_shares (self ) -> List [Dict [str , Any ]]:
@@ -248,11 +275,20 @@ def rename_file(self, path: str, new_name: str) -> Dict[str, Any]:
248275 if not new_name :
249276 raise Exception ("Invalid new name" )
250277
251- # Use the rename API
278+ # According to official Synology API docs, path and name must be JSON arrays even for single values
279+ # The parameters should be formatted as: path=["/path"] and name=["name"]
280+ # Let requests library handle URL encoding automatically
281+
282+ # Create JSON arrays without manual URL encoding - let requests handle it
283+ path_array = json .dumps ([formatted_path ])
284+ name_array = json .dumps ([new_name ])
285+
286+ # Use GET request as specified in official documentation
252287 data = self ._make_request (
253288 'SYNO.FileStation.Rename' , '2' , 'rename' ,
254- path = formatted_path ,
255- name = new_name
289+ use_post = False , # Official docs specify GET
290+ path = path_array ,
291+ name = name_array
256292 )
257293
258294 # Get the parent directory path
0 commit comments