11import ftplib
22from io import BytesIO
33import ssl
4+ from PIL import Image
45
56import logging
67from typing import Any , BinaryIO
78
9+ from PIL .ImageFile import ImageFile
10+
811
912class ImplicitFTP_TLS (ftplib .FTP_TLS ):
1013 """FTP_TLS subclass that automatically wraps sockets in SSL to support implicit FTPS.""" # noqa
@@ -19,7 +22,7 @@ def sock(self):
1922 return self ._sock
2023
2124 @sock .setter
22- def sock (self , value ):
25+ def sock (self , value ): # type: ignore
2326 """When modifying the socket, ensure that it is ssl wrapped."""
2427 if value is not None and not isinstance (value , ssl .SSLSocket ):
2528 value = self .context .wrap_socket (value )
@@ -89,29 +92,97 @@ def upload_file(self, file: BinaryIO, file_path: str) -> str:
8992 callback = lambda x : logging .debug (f"Uploaded { x } bytes" )) # noqa # pylint: disable=logging-fstring-interpolation
9093
9194 @connect_and_run
92- def list_directory (self , path : str | None = None ):
93- lines = []
95+ def list_directory (self , path : str | None = None ) -> tuple [str , list [str ]]:
96+ """
97+ List paths in the given directory.
98+
99+ Args:
100+ path (str | None): Path to check. Default None.
101+
102+ Returns:
103+ tuple[str, list[str]]: ftp result and list of paths in directory.
104+ """
105+ lines : list [str ] = []
94106 res = self .ftps .retrlines (
95107 f'LIST { path if path is not None else "" } ' ,
96108 lines .append )
97109 return res , lines
98110
99- def list_images_dir (self ):
111+ def list_images_dir (self ) -> tuple [str , list [str ]]:
112+ """
113+ List paths in the image directory.
114+
115+ Returns:
116+ tuple[str, list[str]]: ftp result and list of files in image
117+ directory.
118+ """
100119 return self .list_directory ("image" )
101120
102- def list_cache_dir (self ):
121+ def list_cache_dir (self ) -> tuple [str , list [str ]]:
122+ """
123+ List paths in the cache directory.
124+
125+ Returns:
126+ tuple[str, list[str]]: ftp result and list of files in cache
127+ directory.
128+ """
103129 return self .list_directory ("cache" )
104130
105- def list_timelapse_dir (self ):
131+ def list_timelapse_dir (self ) -> tuple [str , list [str ]]:
132+ """
133+ List paths in the timelapse directory.
134+
135+ Returns:
136+ tuple[str, list[str]]: ftp result and list of files in timelapse
137+ directory.
138+ """
106139 return self .list_directory ("timelapse" )
107140
108- def list_logger_dir (self ):
141+ def list_logger_dir (self ) -> tuple [str , list [str ]]:
142+ """
143+ List paths in the logger directory.
144+
145+ Returns:
146+ tuple[str, list[str]]: ftp result and list of files in logger
147+ directory.
148+ """
109149 return self .list_directory ("logger" )
110150
151+ def last_image_print (self ) -> ImageFile | None :
152+ """
153+ Get the last image stored in the image directory - generally the
154+ preview of the last print.
155+
156+ Returns:
157+ ImageFile | None: last file/image in the image directory,
158+ otherwise None.
159+ """
160+ _ , img_dir = self .list_images_dir ()
161+ if img_dir :
162+ img_path = img_dir [- 1 ].split (" " )[- 1 ]
163+ b = self .download_file (f"image/{ img_path } " )
164+ return Image .open (b )
165+
166+ return None
167+
111168 @connect_and_run
112- def download_file (self , file_path : str ):
169+ def download_file (
170+ self ,
171+ file_path : str ,
172+ blocksize : int = 524288 ) -> BytesIO :
173+ """
174+ Get the last image stored in the image directory - generally the
175+ preview of the last print.
176+
177+ Args:
178+ file_path (str): path of file to download.
179+ blocksize (int): block size. Default: 524288.
180+
181+ Returns:
182+ BytesIO: downloaded file in BytesIO.
183+ """
113184 b = BytesIO ()
114- self .ftps .retrbinary (f'RETR { file_path } ' , b .write )
185+ self .ftps .retrbinary (f'RETR { file_path } ' , b .write , blocksize = blocksize )
115186 return b
116187
117188 @connect_and_run
0 commit comments