Connecting to the p1s ftp with python

Trying to connect to my p1s with python. I can connect just fine with FileZilla (telling it to use FTP over TLS and accepting the cert). When I try with either python or powershell, I get a timeout when I try to connect. No funky errors … just times out. I want to run a script on a schedule to connect to my printers and do some cleanup (download timelapse video then delete, clean off old models, etc). I can’t figure out why it won’t connect, My script uses the same port, forces the FTP over TLS, even auto accepts any cert but it just hangs and times out when I attempt to connect, I even verify the port is open before I attempt to connect. Here is my code:

import ftplib
import ssl
import socket

# Define FTP server details
ftp_server = '192.168.86.180'
ftp_user = 'bblp'
ftp_password = '35868996'

# Create an SSL context to accept any certificates
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE

try:
    # Check network connectivity
    print(f"Checking connectivity to {ftp_server}...")
    socket.create_connection((ftp_server, 990), timeout=10)
    print("Network connectivity check passed.")

    # Connect to the FTP server using implicit FTP over TLS with a timeout
    ftp = ftplib.FTP_TLS(context=context)
    print(f"Connecting to {ftp_server} on port 990...")
    ftp.connect(ftp_server, 990, timeout=20)  # Increase timeout to 20 seconds
    ftp.login(ftp_user, ftp_password)

    # Force the control connection to be encrypted
    ftp.prot_p()

    print("Connection established and secured with FTP over TLS")

    # Perform FTP operations here
    # Example: List directory contents
    ftp.retrlines('LIST')

    # Close the connection
    ftp.quit()

except ftplib.all_errors as e:
    print(f"FTP error: {e}")
except socket.timeout:
    print("Connection timed out")
except Exception as e:
    print(f"An error occurred: {e}")
1 Like

FYI, I figured it out. I can now connect to my p1s via python and do any ftp command I want (including downloading files). Here is my sample script to connect and download the latest timelapse video. This makes sure it’s the latest video by checking to see if it has a corresponding thumbnail. If it doesn’t that means the timelapse is not finished (print is still active). Once it gets the newest file it. downloads it locally. This is just an example.

import ftplib
import ssl
import os
from datetime import datetime

class ImplicitFTP_TLS(ftplib.FTP_TLS):
    """FTP_TLS subclass that automatically wraps sockets in SSL to support implicit FTPS."""
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._sock = None

    @property
    def sock(self):
        """Return the socket."""
        return self._sock

    @sock.setter
    def sock(self, value):
        """When modifying the socket, ensure that it is ssl wrapped."""
        if value is not None and not isinstance(value, ssl.SSLSocket):
            value = self.context.wrap_socket(value)
        self._sock = value

from ftplib import all_errors

def parse_ftp_listing(line):
    """Parse a line from an FTP LIST command."""
    parts = line.split(maxsplit=8)
    if len(parts) < 9:
        return None
    return {
        'permissions': parts[0],
        'links': int(parts[1]),
        'owner': parts[2],
        'group': parts[3],
        'size': int(parts[4]),
        'month': parts[5],
        'day': int(parts[6]),
        'time_or_year': parts[7],
        'name': parts[8]
    }

def get_base_name(filename):
    return filename.rsplit('.', 1)[0]

def parse_date(item):
    """Parse the date and time from the FTP listing item."""
    try:
        date_str = f"{item['month']} {item['day']} {item['time_or_year']}"
        return datetime.strptime(date_str, "%b %d %H:%M")
    except ValueError:
        return None

ftp = ImplicitFTP_TLS()
ftp.set_pasv(True)
ftp.connect(host='192.168.86.180', port=990, timeout=5, source_address=None)
ftp.login('bblp', '35868996')
ftp.prot_p()
try:
    tldirlist = []
    tltndirlist = []
    
    ftp.cwd('/timelapse')
    ftp.retrlines('LIST', tldirlist.append)
    tldirlist = [parse_ftp_listing(line) for line in tldirlist if parse_ftp_listing(line)]
    
    ftp.cwd('/timelapse/thumbnail')
    ftp.retrlines('LIST', tltndirlist.append)
    tltndirlist = [parse_ftp_listing(line) for line in tltndirlist if parse_ftp_listing(line)]
    
    # Compare the lists and create a new list with matching files (ignoring extensions)
    tldirlist_dict = {get_base_name(item['name']): item for item in tldirlist}
    tltndirlist_set = {get_base_name(item['name']) for item in tltndirlist}

    matching_files = [tldirlist_dict[base_name] for base_name in tldirlist_dict if base_name in tltndirlist_set]
    
    # Find the newest file based on the time and date fields
    newest_file = None
    newest_time = None
    for item in matching_files:
        file_time = parse_date(item)
        if file_time and (newest_time is None or file_time > newest_time):
            newest_time = file_time
            newest_file = item
    
    if newest_file:
        print(f'Newest file: {newest_file["name"]}')
        # Download the newest file
        local_filename = os.path.join(os.getcwd(), newest_file["name"])
        with open(local_filename, 'wb') as f:
            ftp.retrbinary(f'RETR /timelapse/{newest_file["name"]}', f.write)
        print(f'File downloaded: {local_filename}')
    else:
        print('No matching files found.')
except all_errors as ex:
    print(ex)
finally:
    ftp.quit()
    print('FTP closed.')

I’m going to expand on this script to do clean-up. If anyone does some cool stuff, I’d appreciate it if they posted it so I can steal some of your ideas for my code.

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.