diff --git a/.vscode/launch.json b/.vscode/launch.json index d9853e9..e5e3bea 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -63,8 +63,8 @@ "request": "launch", "program": "${file}", "args": [ - "-d", - "-1", + "-w", + "-3", "-c", "-p" ], diff --git a/README.md b/README.md index e3bacc1..3a0da5c 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ crontab -e > > - [Python 3](https://www.python.org/downloads/) (tested version 3.9.5 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/)) > - [paramiko](https://www.paramiko.org/) Python 3 module (tested version 3.1.0) +> - [smbprotocol](https://github.com/jborean93/smbprotocol) Python 3 module (tested version 1.10.1) > - [ffmpeg](https://ffmpeg.org) (tested version 4.3.4 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/)) > - specified record pictures filesystem organization > diff --git a/cctv-scheduler.conf b/cctv-scheduler.conf index 9ddc2b4..750db03 100644 --- a/cctv-scheduler.conf +++ b/cctv-scheduler.conf @@ -15,31 +15,37 @@ [enable-broadcast] # List the broadcast block names. Only blocks with the TRUE value will be used. +# camera.test.local = true [enable-sequences] # List the sequence camera block names. Only blocks with the TRUE value will be used. +# camera.test.local = true [enable-sensors] # List the sensor block names. Only blocks with the TRUE value will be used. +# sensor.test.local = true [enable-convert] # List the convert block names. Only blocks with the TRUE value will be used. +# camera.test.local = true [enable-publish] # List the publish block names. Only blocks with the TRUE value will be used. +# camera.test.local = true [broadcast-config:camera.test.local] # Broadcast parameter description block always starts with "broadcast-config:". +# src = rtsp://user:pass@192.168.254.253:554/Streaming/Channels/101,http://radio.fm:8000/stream.mp3 dst = rtp://239.0.0.1:5554 # Optionality you can change video stream framerate. @@ -66,9 +72,11 @@ dst = rtp://239.0.0.1:5554 [sensor-config:sensor.test.local] # Remote host's sensor parameter description block always starts with "sensor-config:". +# hostname = 192.168.254.252 username = user userpass = pass +# # To recognize options for polling a sensor, you must specify the type of sensor. # Supported types: # ds18b20 @@ -78,19 +86,39 @@ nodename = 28-1a2b3c4d5e6f [camera-config:camera.test.local] # Camera parameter description block always starts with "camera-config:". +# hostname = 192.168.254.253 username = user userpass = pass +# # If a record directory on a remote host is used, a username and password must be specified. # Supported protocols: -# FTP. -records_root_path = ftp://192.168.254.254/Records/camera.test.local -records_root_user = user -records_root_pass = pass +# FTP, SFTP, SMB. +records_root_path = ftp://user:pass@192.168.254.254:21/Records/camera.test.local +# +# Separated parameters string has lower priority and parameters are overwritten by +# records_root_path = 'hosttype://username:password@hostname:hostport/some/path' +# if you use both. +# +#records_root_path = /Records/camera.test.local +# +#records_root_host = 192.168.254.254 +# +# Optionality you can set custom connection port: +#records_root_port = 21 +# +# You must set connection type (ftp is faster than sftp, sftp is faster than smb): +# ftp, sftp, smb. +#records_root_type = ftp +# +#records_root_user = user +# +#records_root_pass = pass [camera-sequences:camera.test.local] # Camera sequence description block always starts with "camera-sequences:". +# # Place only the sequence of PTZ-actions in this block! # Variable name can be anything. Only 'downloadjpeg' is using this for filename prefix. # Available actions: @@ -128,14 +156,48 @@ step999 = rebootcamera, -, -, -, -, -, -, image_find_names = step071, image-01, image-02 # If image root or destination video directories on a remote host is used, username and password must be specified. # Supported protocols: -# FTP. -image_root_path = ftp://192.168.254.254/Records/camera.test.local -image_root_user = user -image_root_pass = pass +# FTP, SFTP, SMB. +image_root_path = ftp://user:pass@192.168.254.254/Records/camera.test.local +# +# Separated parameters string has lower priority and parameters are overwritten by +# image_root_path = 'hosttype://username:password@hostname:hostport/some/path' +# if you use both. +# +#image_root_path = /Records/camera.test.local +# +#image_root_host = 192.168.254.254 +# +# Optionality you can set custom connection port: +#image_root_port = 21 +# +# You must set connection type (ftp is faster than sftp, sftp is faster than smb): +# ftp, sftp, smb. +#image_root_type = ftp +# +#image_root_user = user +# +#image_root_pass = pass -video_dest_path = ftp://192.168.254.254/Downloads -video_dest_user = user -video_dest_pass = pass +video_dest_path = ftp://user:pass@192.168.254.254/Downloads +# +# Separated parameters string has lower priority and parameters are overwritten by +# video_dest_path = 'hosttype://username:password@hostname:hostport/some/path' +# if you use both. +# +#video_dest_path = /Downloads +# +# Optionality you can set custom connection port: +#video_dest_host = 192.168.254.254 +# +#video_dest_port = 21 +# +# You must set connection type (ftp is faster than sftp, sftp is faster than smb): +# ftp, sftp, smb. +#video_dest_type = ftp +# +#video_dest_user = user +# +#video_dest_pass = pass video_scale_x = 1920 video_scale_y = 1080 @@ -144,20 +206,42 @@ video_framerate = 25 [publish-config:camera.test.local] # Publisher parameter description block always starts with "publish-config:". +# video_find_names = step071, image-01, image-02 # If a video directory on a remote host is used, a username and password must be specified. # Supported protocols: -# FTP. -video_root_path = ftp://192.168.254.254/Downloads -video_root_user = user -video_root_pass = pass - +# FTP, SFTP, SMB. +video_root_path = ftp://user:pass@192.168.254.254/Downloads +# +# Separated parameters string has lower priority and parameters are overwritten by +# video_root_path = 'hosttype://username:password@hostname:hostport/some/path' +# if you use both. +# +#video_dest_path = /Downloads +# +#video_dest_host = 192.168.254.254 +# +# Optionality you can set custom connection port: +#video_dest_port = 21 +# +# You must set connection type (ftp is faster than sftp, sftp is faster than smb): +# ftp, sftp, smb. +#video_dest_type = ftp +# +#video_dest_user = user +# +#video_dest_pass = pass +# +# Optionality you can enable or disable publishing by Wordpress: +# true - Wordpress enabled, false - Wordpress disbaled. wp_enabled = true wp_site_name = www.site.name wp_user_name = user wp_user_pass = pass wp_update_page_id = 4848 - +# +# Optionality you can enable or disable publishing by Telegram: +# true - Wordpress enabled, false - Telegram disbaled. tg_enabled = true tg_api_key = TELEGRAM_API_KEY tg_chat_id = @blackhole \ No newline at end of file diff --git a/cctv-scheduler.py b/cctv-scheduler.py index 5bcb0b9..c1d452c 100644 --- a/cctv-scheduler.py +++ b/cctv-scheduler.py @@ -16,13 +16,14 @@ import urllib.request from argparse import ArgumentParser from ftplib import FTP from multiprocessing import Process, Queue -from os import environ, makedirs, path, remove, replace, rmdir, sep, stat, walk +from os import environ, makedirs, path, remove, rmdir, sep, stat, walk from string import ascii_letters, digits from subprocess import Popen, PIPE, STDOUT from sys import modules, platform from time import sleep import requests -from paramiko import SSHClient, AutoAddPolicy +from paramiko import AutoAddPolicy, SSHClient, SFTPClient +import smbclient class Parse: @@ -734,6 +735,313 @@ class Connect: local_logger.debug(msg='error: ' + '\n' + str(error)) return 'ERROR' + @staticmethod + # pylint: disable=W0718 + def ftp_file_search( + root_path: str, + search_name: (str, type(None)) = None, + ftp: (FTP, type(None)) = None, + hostname: (str, type(None)) = None, + username: (str, type(None)) = None, + password: (str, type(None)) = None, + port: int = 21, + logger_alias: str = inspect.stack()[0].function + ) -> list: + """Search files over FTP. + + Args: + root_path (str): where to search. + search_name (str, None, optional): full or partial filename for the filter. + Defaults to None. + ftp (FTP, None, optional): FTP object generated by recursive search. + Defaults to None. + hostname (str, None, optional): ftp hostname. + Defaults to None. + username (str, None, optional): ftp username. + Defaults to None. + password (str, None, optional): ftp password. + Defaults to None. + port (int, optional): remote host connection port. + Defaults to 21. + logger_alias (str, optional): sublogger name. + Defaults to function or method name. + + Returns: + list: list of found files. + """ + local_logger = logging.getLogger(logger_alias) + if Do.args_valid(locals(), Connect.ftp_file_search.__annotations__): + local_logger.debug(msg='' + + '\n' + 'root_path: ' + root_path + + '\n' + 'search_name: ' + str(search_name) + + '\n' + 'ftp: ' + str(ftp) + + '\n' + 'hostname: ' + str(hostname) + + '\n' + 'username: ' + str(username) + + '\n' + 'password: ' + str(password) + ) + parent = False + if not ftp: + try: + ftp = FTP(host=hostname) + ftp.connect(host=hostname, port=port) + ftp.login(user=username, passwd=password) + parent = True + except Exception as error: + local_logger.debug(msg='error: ' + '\n' + str(error)) + + result = [] + ftp.cwd(root_path) + for file in ftp.mlsd(): + if file[1]['type'] == 'dir': + result = result + Connect.ftp_file_search( + root_path=root_path + "/" + file[0], + search_name=search_name, + ftp=ftp + ) + elif file[1]['type'] == 'file': + if search_name: + if search_name in file[0]: + result.append(root_path + "/" + file[0]) + else: + result.append(root_path + "/" + file[0]) + + if parent: + ftp.close() + result.sort() + return result + + @staticmethod + # pylint: disable=W0718 + def ftp_get_file( + src_file: str, + dst_file: str, + hostname: str, + username: str, + password: str, + port: int = 21, + logger_alias: str = inspect.stack()[0].function + ) -> dict: + """Download file from FTP. + + Args: + src_file (str): /remote/path/to/file. + dst_file (str): /local/path/to/file. + hostname (str): ftp hostname. + username (str): ftp username. + password (str): ftp password. + port (int, optional): remote host connection port. Defaults to 21. + logger_alias (str, optional): sublogger name. Defaults to function or method name. + + Returns: + dict: {'success':bool,'result':/local/path/to/file or 'ERROR'}. + """ + local_logger = logging.getLogger(logger_alias) + if Do.args_valid(locals(), Connect.ftp_get_file.__annotations__): + ftp = FTP(host=hostname) + local_logger.debug(msg='' + + '\n' + 'src_file: ' + src_file + + '\n' + 'dst_file: ' + dst_file + + '\n' + 'hostname: ' + hostname + + '\n' + 'username: ' + username + + '\n' + 'password: ' + password + ) + try: + ftp.connect(host=hostname, port=port) + ftp.login(user=username, passwd=password) + with open(dst_file, "wb+") as file: + ftp.retrbinary(f"RETR {src_file}", file.write) + ftp.quit() + local_logger.info(msg=dst_file + ' saved') + return {"success": True, "result": dst_file} + except Exception as error: + local_logger.debug(msg='error: ' + '\n' + str(error)) + return {"success": False, "result": "ERROR"} + + @staticmethod + # pylint: disable=W0718 + def ftp_put_file( + src_file: str, + dst_file: str, + hostname: str, + username: str, + password: str, + port: int = 21, + logger_alias: str = inspect.stack()[0].function + ) -> dict: + """Upload file to FTP. + + Args: + src_file (str): /local/path/to/file. + dst_file (str): /remote/path/to/file. + hostname (str): ftp hostname. + username (str): ftp username. + password (str): ftp password. + port (int, optional): remote host connection port. Defaults to 21. + logger_alias (str, optional): sublogger name. Defaults to function or method name. + + Returns: + dict: {'success':bool,'result':/remote/path/to/file or 'ERROR'}. + """ + local_logger = logging.getLogger(logger_alias) + if Do.args_valid(locals(), Connect.ftp_put_file.__annotations__): + dst_path = dst_file.split('/')[:-1] + ftp = FTP(host=hostname) + local_logger.debug(msg='' + + '\n' + 'src_file: ' + src_file + + '\n' + 'dst_file: ' + dst_file + + '\n' + 'hostname: ' + hostname + + '\n' + 'username: ' + username + + '\n' + 'password: ' + password + ) + try: + ftp.connect(host=hostname, port=port) + ftp.login(user=username, passwd=password) + for path_item in dst_path: + if path_item.strip() == '': + continue + path_item = path_item.replace('/', '') + try: + ftp.cwd(path_item) + except Exception: + ftp.mkd(path_item) + ftp.cwd(path_item) + with open(src_file, "rb") as file: + ftp.storbinary(f"STOR {dst_file}", file) + ftp.quit() + local_logger.info(msg='ftp://' + hostname + dst_file + ' saved') + return {"success": True, "result": 'ftp://' + hostname + dst_file} + except Exception as error: + local_logger.debug(msg='error: ' + '\n' + str(error)) + return {"success": False, "result": "ERROR"} + + @staticmethod + # pylint: disable=W0718 + def ssh_file_search( + root_path: str, + search_name: (str, type(None)) = None, + sftp: (SFTPClient, type(None)) = None, + hostname: (str, type(None)) = None, + username: (str, type(None)) = None, + password: (str, type(None)) = None, + port: int = 22, + logger_alias: str = inspect.stack()[0].function + ) -> list: + """Search files over SFTP. + + Args: + root_path (str): where to search. + search_name (str, None, optional): full or partial filename for the filter. + Defaults to None. + sftp (SFTPClient, None, optional): SFTP object generated by recursive search. + Defaults to None. + hostname (str, None, optional): sftp hostname. + Defaults to None. + username (str, None, optional): sftp username. + Defaults to None. + password (str, None, optional): sftp password. + Defaults to None. + port (int, optional): remote host connection port. + Defaults to 22. + logger_alias (str, optional): sublogger name. + Defaults to function or method name. + + Returns: + list: list of found files. + """ + local_logger = logging.getLogger(logger_alias) + if Do.args_valid(locals(), Connect.ssh_file_search.__annotations__): + local_logger.debug(msg='' + + '\n' + 'root_path: ' + root_path + + '\n' + 'search_name: ' + str(search_name) + + '\n' + 'sftp: ' + str(sftp) + + '\n' + 'hostname: ' + str(hostname) + + '\n' + 'username: ' + str(username) + + '\n' + 'password: ' + str(password) + ) + parent = False + if not sftp: + try: + client = SSHClient() + client.set_missing_host_key_policy(AutoAddPolicy()) + client.connect( + hostname=hostname, + username=username, + password=password, + port=port + ) + sftp = client.open_sftp() + parent = True + except Exception as error: + local_logger.debug(msg='error: ' + '\n' + str(error)) + + result = [] + for file in sftp.listdir(root_path): + try: + result = result + Connect.ssh_file_search( + root_path=root_path + '/' + file, + search_name=search_name, + sftp=sftp + ) + except IOError: + if search_name: + if search_name in file: + result.append(root_path + '/' + file) + else: + result.append(root_path + '/' + file) + + if parent: + client.close() + result.sort() + return result + + @staticmethod + # pylint: disable=W0718 + def ssh_get_file( + src_file: str, + dst_file: str, + hostname: str, + username: str, + password: str, + port: int = 22, + logger_alias: str = inspect.stack()[0].function + ) -> dict: + """Download file from SFTP. + + Args: + src_file (str): /remote/path/to/file. + dst_file (str): /local/path/to/file. + hostname (str): sftp hostname. + username (str): sftp username. + password (str): sftp password. + port (int, optional): remote host connection port. Defaults to 22. + logger_alias (str, optional): sublogger name. Defaults to function or method name. + + Returns: + dict: {'success':bool,'result':/local/path/to/file or 'ERROR'}. + """ + local_logger = logging.getLogger(logger_alias) + if Do.args_valid(locals(), Connect.ssh_get_file.__annotations__): + client = SSHClient() + client.set_missing_host_key_policy(AutoAddPolicy()) + local_logger.debug(msg='' + + '\n' + 'src_file: ' + src_file + + '\n' + 'dst_file: ' + dst_file + + '\n' + 'host: ' + hostname + ':' + str(port) + + '\n' + 'user: ' + str(username) + + '\n' + 'pass: ' + str(password) + ) + try: + client.connect(hostname=hostname, username=username, password=password, port=port) + with client.open_sftp() as sftp: + sftp.get(remotepath=src_file, localpath=dst_file) + client.close() + local_logger.info(msg=dst_file + ' saved') + return {"success": True, "result": dst_file} + except Exception as error: + remove(dst_file) + local_logger.debug(msg='error: ' + '\n' + str(error)) + return {"success": False, "result": "ERROR"} + @staticmethod # pylint: disable=W0718 def ssh_put_file( @@ -744,7 +1052,7 @@ class Connect: password: str, port: int = 22, logger_alias: str = inspect.stack()[0].function - ) -> str: + ) -> dict: """Handling SFTP upload file. Args: @@ -757,194 +1065,619 @@ class Connect: logger_alias (str, optional): sublogger name. Defaults to function or method name. Returns: - str: '/remote/path/to/file' or 'ERROR'. + dict: {'success':bool,'result':/remote/path/to/file or 'ERROR'}. """ local_logger = logging.getLogger(logger_alias) - client = SSHClient() - client.set_missing_host_key_policy(AutoAddPolicy()) - local_logger.debug(msg='' - + '\n' + 'src_file: ' + src_file - + '\n' + 'dst_file: ' + dst_file - + '\n' + 'host: ' + hostname + ':' + str(port) - + '\n' + 'user: ' + username - + '\n' + 'pass: ' + password - ) - try: - client.connect(hostname=hostname, username=username, password=password, port=port) - client.exec_command('mkdir -p ' + path.dirname(dst_file)) + if Do.args_valid(locals(), Connect.ssh_put_file.__annotations__): + client = SSHClient() + client.set_missing_host_key_policy(AutoAddPolicy()) + local_logger.debug(msg='' + + '\n' + 'src_file: ' + src_file + + '\n' + 'dst_file: ' + dst_file + + '\n' + 'host: ' + hostname + ':' + str(port) + + '\n' + 'user: ' + username + + '\n' + 'pass: ' + password + ) try: - sftp = client.open_sftp() - sftp.put(localpath=src_file, remotepath=dst_file) - sftp.stat(dst_file) - sftp.close() - local_logger.info(msg='sftp://' + hostname + dst_file + ' saved') - return dst_file + client.connect(hostname=hostname, username=username, password=password, port=port) + client.exec_command('mkdir -p ' + path.dirname(dst_file)) + try: + sftp = client.open_sftp() + sftp.put(localpath=src_file, remotepath=dst_file) + sftp.stat(dst_file) + sftp.close() + local_logger.info(msg='sftp://' + hostname + dst_file + ' saved') + return {"success": True, "result": 'sftp://' + hostname + dst_file} + except Exception as error: + local_logger.debug(msg='error: ' + '\n' + str(error)) + return {"success": False, "result": "ERROR"} except Exception as error: local_logger.debug(msg='error: ' + '\n' + str(error)) - return 'ERROR' - except Exception as error: - local_logger.debug(msg='error: ' + '\n' + str(error)) - return 'ERROR' + return {"success": False, "result": "ERROR"} @staticmethod - # pylint: disable=W0718 - def ftp_file_search( + # pylint: disable=W0718 + def smb_file_search( root_path: str, search_name: (str, type(None)) = None, - ftp: FTP = None, - hostname: str = None, - username: str = None, - password: str = None, + smb: (smbclient._pool.ClientConfig, type(None)) = None, + hostname: (str, type(None)) = None, + username: (str, type(None)) = None, + password: (str, type(None)) = None, + port: int = 445, logger_alias: str = inspect.stack()[0].function ) -> list: - """Search files over FTP. + """Search files over SMB. Args: root_path (str): where to search. search_name (str, None, optional): full or partial filename for the filter. Defaults to None. - ftp (FTP, optional): FTP object generated by recursive search. Defaults to None. - hostname (str, optional): ftp hostname. Defaults to None. - username (str, optional): ftp username. Defaults to None. - password (str, optional): ftp password. Defaults to None. + smb (smbclient.ClientConfig, None, optional): object generated by recursive search. + Defaults to None. + hostname (str, None, optional): smb hostname. + Defaults to None. + username (str, None, optional): smb username. + Defaults to None. + password (str, None, optional): smb password. + Defaults to None. + port (int, optional): remote host connection port. + Defaults to 445. + logger_alias (str, optional): sublogger name. + Defaults to function or method name. Returns: list: list of found files. """ local_logger = logging.getLogger(logger_alias) - local_logger.debug(msg='' - + '\n' + 'root_path: ' + root_path - + '\n' + 'search_name: ' + str(search_name) - + '\n' + 'ftp: ' + str(ftp) - + '\n' + 'hostname: ' + str(hostname) - + '\n' + 'username: ' + str(username) - + '\n' + 'password: ' + str(password) - ) - parent = False - if not ftp: - try: - ftp = FTP(host=hostname) - ftp.login(user=username, passwd=password) - parent = True - except Exception as error: - local_logger.debug(msg='error: ' + '\n' + str(error)) + if Do.args_valid(locals(), Connect.smb_file_search.__annotations__): + local_logger.debug(msg='' + + '\n' + 'root_path: ' + root_path + + '\n' + 'search_name: ' + str(search_name) + + '\n' + 'sftp: ' + str(smb) + + '\n' + 'hostname: ' + str(hostname) + + '\n' + 'username: ' + str(username) + + '\n' + 'password: ' + str(password) + ) + parent = False + if not smb: + try: + smb = smbclient.ClientConfig(username=username, password=password) + parent = True + except Exception as error: + local_logger.debug(msg='error: ' + '\n' + str(error)) - result = [] - ftp.cwd(root_path) - for file in ftp.mlsd(): - if file[1]['type'] == 'dir': - result = result + Connect.ftp_file_search( - root_path=root_path + "/" + file[0], - search_name=search_name, - ftp=ftp - ) - elif file[1]['type'] == 'file': - if search_name: - if search_name in file[0]: - result.append(root_path + "/" + file[0]) - else: - result.append(root_path + "/" + file[0]) + result = [] + for file in smbclient.listdir(path='//' + hostname + root_path, port=port): + try: + result = result + Connect.smb_file_search( + root_path=root_path + '/' + file, + search_name=search_name, + hostname=hostname, + smb=smb + ) + except IOError: + if search_name: + if search_name in file: + result.append(root_path + '/' + file) + else: + result.append(root_path + '/' + file) - if parent: - ftp.close() - result.sort() - return result + if parent: + smbclient.reset_connection_cache() + result.sort() + return result @staticmethod - # pylint: disable=W0718 - def ftp_get_file( + # pylint: disable=W0718 + def smb_get_file( src_file: str, dst_file: str, hostname: str, username: str, password: str, + port: int = 445, logger_alias: str = inspect.stack()[0].function - ) -> bool: - """Download file from FTP. + ) -> dict: + """Download file from SMB. Args: src_file (str): /remote/path/to/file. dst_file (str): /local/path/to/file. - hostname (str): ftp hostname. - username (str): ftp username. - password (str): ftp password. + hostname (str): smb hostname. + username (str): smb username. + password (str): smb password. + port (int, optional): remote host connection port. Defaults to 445. logger_alias (str, optional): sublogger name. Defaults to function or method name. Returns: - bool: True if successed. + dict: {'success':bool,'result':/local/path/to/file or 'ERROR'}. """ local_logger = logging.getLogger(logger_alias) - ftp = FTP(host=hostname) - local_logger.debug(msg='' - + '\n' + 'src_file: ' + src_file - + '\n' + 'dst_file: ' + dst_file - + '\n' + 'hostname: ' + hostname - + '\n' + 'username: ' + username - + '\n' + 'password: ' + password - ) - try: - ftp.login(user=username, passwd=password) - with open(dst_file, "wb+") as file: - ftp.retrbinary(f"RETR {src_file}", file.write) - ftp.quit() - local_logger.info(msg=dst_file + ' saved') - return True - except Exception as error: - local_logger.debug(msg='error: ' + '\n' + str(error)) - return False + if Do.args_valid(locals(), Connect.smb_get_file.__annotations__): + smbclient.ClientConfig(username=username, password=password) + local_logger.debug(msg='' + + '\n' + 'src_file: ' + src_file + + '\n' + 'dst_file: ' + dst_file + + '\n' + 'host: ' + hostname + ':' + str(port) + + '\n' + 'user: ' + str(username) + + '\n' + 'pass: ' + str(password) + ) + src_file = '//' + hostname + src_file + try: + with smbclient.open_file(src_file, mode="rb", port=port) as remote_file: + with open(dst_file, "wb+") as local_file: + local_file.write(remote_file.read()) + smbclient.reset_connection_cache() + local_logger.info(msg=dst_file + ' saved') + return {"success": True, "result": dst_file} + except Exception as error: + local_logger.debug(msg='error: ' + '\n' + str(error)) + return {"success": False, "result": "ERROR"} @staticmethod - # pylint: disable=W0718 - def ftp_put_file( + # pylint: disable=W0718 + def smb_put_file( src_file: str, dst_file: str, hostname: str, username: str, password: str, + port: int = 445, logger_alias: str = inspect.stack()[0].function - ) -> bool: - """Upload file to FTP. + ) -> dict: + """Upload file to SMB. Args: src_file (str): /local/path/to/file. dst_file (str): /remote/path/to/file. - hostname (str): ftp hostname. - username (str): ftp username. - password (str): ftp password. + hostname (str): smb hostname. + username (str): smb username. + password (str): smb password. + port (int, optional): remote host connection port. Defaults to 445. logger_alias (str, optional): sublogger name. Defaults to function or method name. Returns: - bool: True if successed. + dict: {'success':bool,'result':/remote/path/to/file or 'ERROR'}. """ local_logger = logging.getLogger(logger_alias) - dst_path = dst_file.split('/')[:-1] - ftp = FTP(host=hostname) - local_logger.debug(msg='' - + '\n' + 'src_file: ' + src_file - + '\n' + 'dst_file: ' + dst_file - + '\n' + 'hostname: ' + hostname - + '\n' + 'username: ' + username - + '\n' + 'password: ' + password - ) - try: - ftp.login(user=username, passwd=password) - for path_item in dst_path: - if path_item.strip() == '': - continue - path_item = path_item.replace('/', '') - try: - ftp.cwd(path_item) - except Exception: - ftp.mkd(path_item) - ftp.cwd(path_item) - with open(src_file, "rb") as file: - ftp.storbinary(f"STOR {dst_file}", file) - ftp.quit() - local_logger.info(msg='ftp://' + hostname + dst_file + ' saved') - return True - except Exception as error: - local_logger.debug(msg='error: ' + '\n' + str(error)) - return False + if Do.args_valid(locals(), Connect.smb_get_file.__annotations__): + smbclient.ClientConfig(username=username, password=password) + local_logger.debug(msg='' + + '\n' + 'src_file: ' + src_file + + '\n' + 'dst_file: ' + dst_file + + '\n' + 'host: ' + hostname + ':' + str(port) + + '\n' + 'user: ' + str(username) + + '\n' + 'pass: ' + str(password) + ) + dst_file = '//' + hostname + dst_file + try: + if not smbclient.path.exists(path.dirname(dst_file)): + smbclient.mkdir(path=path.dirname(dst_file)) + with smbclient.open_file(dst_file, mode="wb+", port=port) as remote_file: + with open(src_file, "rb") as local_file: + remote_file.write(local_file.read()) + smbclient.reset_connection_cache() + local_logger.info(msg='smb:' + dst_file + ' saved') + return {"success": True, "result": 'smb:' + dst_file} + except Exception as error: + local_logger.debug(msg='error: ' + '\n' + str(error)) + return {"success": False, "result": "ERROR"} + + + + @staticmethod + def local_file_search( + root_path: str, + search_name: (str, type(None)) = None, + logger_alias: str = inspect.stack()[0].function + ) -> list: + """Search local files. + + Args: + root_path (str): where to search. + search_name (str, None, optional): full or partial filename for the filter. + Defaults to None. + logger_alias (str, optional): sublogger name. + Defaults to function or method name. + + Returns: + list: list of found files. + """ + local_logger = logging.getLogger(logger_alias) + if Do.args_valid(locals(), Connect.local_file_search.__annotations__): + local_logger.debug(msg='' + + '\n' + 'root_path: ' + root_path + + '\n' + 'search_name: ' + str(search_name) + ) + return [] + + @staticmethod + def local_get_file( + src_file: str, + dst_file: str, + logger_alias: str = inspect.stack()[0].function + ) -> dict: + """Like download local file. Interface plug. + + Args: + src_file (str): /local/path/to/src/file. + dst_file (str): /local/path/to/dst/file. + logger_alias (str, optional): sublogger name. Defaults to function or method name. + + Returns: + dict: {'success':bool,'result':/local/path/to/file or 'ERROR'}. + """ + local_logger = logging.getLogger(logger_alias) + if Do.args_valid(locals(), Connect.local_get_file.__annotations__): + local_logger.debug(msg='' + + '\n' + 'src_file: ' + src_file + + '\n' + 'dst_file: ' + dst_file + ) + return {"success": False, "result": "ERROR"} + + @staticmethod + def local_put_file( + src_file: str, + dst_file: str, + logger_alias: str = inspect.stack()[0].function + ) -> dict: + """Like upload local file. Interface plug. + + Args: + src_file (str): /local/path/to/src/file. + dst_file (str): /local/path/to/dst/file. + logger_alias (str, optional): sublogger name. Defaults to function or method name. + + Returns: + dict: {'success':bool,'result':/local/path/to/file or 'ERROR'}. + """ + local_logger = logging.getLogger(logger_alias) + if Do.args_valid(locals(), Connect.local_put_file.__annotations__): + local_logger.debug(msg='' + + '\n' + 'src_file: ' + src_file + + '\n' + 'dst_file: ' + dst_file + ) + return {"success": False, "result": "ERROR"} + + + + @classmethod + def file_search( + cls, + search_path: str, + search_name: (str, type(None)) = None, + hostname: (str, type(None)) = None, + hostport: (int, type(None)) = None, + hosttype: (str, type(None)) = None, + username: (str, type(None)) = None, + password: (str, type(None)) = None, + logger_alias: str = inspect.stack()[0].function + ) -> list: + """Interface for file search over FTP, SFTP, SMB. + + Args: + search_path (str): where to search. + search_path='/some/path', hostname='hostname', username='username', etc. + has lower priority and parameters are overwritten by + search_path = 'hosttype://username:password@hostname:hostport/some/path' + search_name (str, type, optional): full or partial filename for the filter. + Defaults to None. + hostname (str, type, optional): remote hostname. + Defaults to None. + hostport (int, type, optional): remote host connection port. + Defaults to None. + hosttype (str, type, optional): 'ftp', 'sftp', 'smb'. + Defaults to None. + username (str, type, optional): remote username. + Defaults to None. + password (str, type, optional): remote password. + Defaults to None. + logger_alias (str, optional): sublogger name. + Defaults to function or method name. + + Returns: + list: list of found files. + """ + local_logger = logging.getLogger(logger_alias) + if Do.args_valid(locals(), cls.file_search.__annotations__): + local_logger.debug(msg='' + + '\n' + 'search_path: ' + search_path + + '\n' + 'search_name: ' + str(search_name) + + '\n' + 'hostname: ' + str(hostname) + + '\n' + 'hostport: ' + str(hostport) + + '\n' + 'hosttype: ' + str(hosttype) + + '\n' + 'username: ' + str(username) + + '\n' + 'password: ' + str(password) + ) + remote_hostpath = search_path + remote_hostname = hostname + remote_hostport = hostport + remote_hosttype = hosttype + remote_username = username + remote_password = password + if '://' in search_path: + remote_hostname = search_path.split('/')[2] + remote_hosttype = search_path.split('://')[0] + remote_hostpath = search_path.replace(remote_hosttype + '://' + remote_hostname, '') + if '@' in remote_hostname: + remote_username = remote_hostname.split('@')[0].split(':')[0] + remote_password = remote_hostname.split('@')[0].split(':')[1] + remote_hostname = remote_hostname.split('@')[1] + if ':' in remote_hostname: + remote_hostport = int(remote_hostname.split(':')[1]) + remote_hostname = remote_hostname.split(':')[0] + if not hostport: + if remote_hosttype == 'ftp': + remote_hostport = 21 + if remote_hosttype == 'sftp': + remote_hostport = 22 + if remote_hosttype == 'smb': + remote_hostport = 445 + + if remote_hosttype == 'ftp': + files_found = cls.ftp_file_search( + root_path=remote_hostpath, + search_name=search_name, + hostname=remote_hostname, + username=remote_username, + password=remote_password, + port=remote_hostport, + logger_alias=logger_alias + ) + elif remote_hosttype == 'sftp': + files_found = cls.ssh_file_search( + root_path=remote_hostpath, + search_name=search_name, + hostname=remote_hostname, + username=remote_username, + password=remote_password, + port=remote_hostport, + logger_alias=logger_alias + ) + elif remote_hosttype == 'smb': + files_found = cls.smb_file_search( + root_path=remote_hostpath, + search_name=search_name, + hostname=remote_hostname, + username=remote_username, + password=remote_password, + port=remote_hostport, + logger_alias=logger_alias + ) + else: + files_found = cls.local_file_search( + root_path=search_path, + search_name=search_name, + logger_alias=logger_alias + ) + + local_logger.debug(msg='files_found:' + '\n' + str(files_found)) + return files_found + + @classmethod + def file_download( + cls, + src_file: str, + dst_file: str, + hostname: (str, type(None)) = None, + hostport: (int, type(None)) = None, + hosttype: (str, type(None)) = None, + username: (str, type(None)) = None, + password: (str, type(None)) = None, + logger_alias: str = inspect.stack()[0].function + ) -> dict: + """Interface for file download over FTP, SFTP, SMB. + + Args: + src_file (str): + src_file='/remote/path/to/file.', hostname='hostname', username='username', etc. + has lower priority and parameters are overwritten by + src_file = 'hosttype://username:password@hostname:hostport/remote/path/to/file.' + dst_file (str):/local/path/to/file. + hostname (str, type, optional): remote hostname. + Defaults to None. + hostport (int, type, optional): remote host connection port. + Defaults to None. + hosttype (str, type, optional): 'ftp', 'sftp', 'smb'. + Defaults to None. + username (str, type, optional): remote username. + Defaults to None. + password (str, type, optional): remote password. + Defaults to None. + logger_alias (str, optional): sublogger name. + Defaults to function or method name. + + Returns: + dict: {'success':bool,'result':/local/path/to/file or 'ERROR'}. + """ + local_logger = logging.getLogger(logger_alias) + if Do.args_valid(locals(), cls.file_download.__annotations__): + local_logger.debug(msg='' + + '\n' + 'src_file: ' + src_file + + '\n' + 'dst_file: ' + dst_file + + '\n' + 'hostname: ' + str(hostname) + + '\n' + 'hostport: ' + str(hostport) + + '\n' + 'hosttype: ' + str(hosttype) + + '\n' + 'username: ' + str(username) + + '\n' + 'password: ' + str(password) + ) + remote_hostpath = src_file + remote_hostname = hostname + remote_hostport = hostport + remote_hosttype = hosttype + remote_username = username + remote_password = password + if '://' in src_file: + remote_hostname = src_file.split('/')[2] + remote_hosttype = src_file.split('://')[0] + remote_hostpath = src_file.replace(remote_hosttype + '://' + remote_hostname, '') + if '@' in remote_hostname: + remote_username = remote_hostname.split('@')[0].split(':')[0] + remote_password = remote_hostname.split('@')[0].split(':')[1] + remote_hostname = remote_hostname.split('@')[1] + if ':' in remote_hostname: + remote_hostport = int(remote_hostname.split(':')[1]) + remote_hostname = remote_hostname.split(':')[0] + if not hostport: + if remote_hosttype == 'ftp': + remote_hostport = 21 + if remote_hosttype == 'sftp': + remote_hostport = 22 + if remote_hosttype == 'smb': + remote_hostport = 445 + + if remote_hosttype == 'ftp': + result = cls.ftp_get_file( + src_file=remote_hostpath, + dst_file=dst_file, + hostname=remote_hostname, + username=remote_username, + password=remote_password, + port=remote_hostport, + logger_alias=logger_alias + ) + elif remote_hosttype == 'sftp': + result = cls.ssh_get_file( + src_file=remote_hostpath, + dst_file=dst_file, + hostname=remote_hostname, + username=remote_username, + password=remote_password, + port=remote_hostport, + logger_alias=logger_alias + ) + elif remote_hosttype == 'smb': + result = cls.smb_get_file( + src_file=remote_hostpath, + dst_file=dst_file, + hostname=remote_hostname, + username=remote_username, + password=remote_password, + port=remote_hostport, + logger_alias=logger_alias + ) + else: + result = cls.local_get_file( + src_file=remote_hostpath, + dst_file=dst_file, + logger_alias=logger_alias + ) + + local_logger.debug(msg='result: ' + '\n' + str(result)) + return result + + @classmethod + def file_upload( + cls, + src_file: str, + dst_file: str, + hostname: (str, type(None)) = None, + hostport: (int, type(None)) = None, + hosttype: (str, type(None)) = None, + username: (str, type(None)) = None, + password: (str, type(None)) = None, + logger_alias: str = inspect.stack()[0].function + ) -> dict: + """Interface for file download over FTP, SFTP, SMB. + + Args: + src_file (str):/local/path/to/file. + dst_file (str): + dst_file='/remote/path/to/file.', hostname='hostname', username='username', etc. + has lower priority and parameters are overwritten by + dst_file = 'hosttype://username:password@hostname:hostport/remote/path/to/file.' + hostname (str, type, optional): remote hostname. + Defaults to None. + hostport (int, type, optional): remote host connection port. + Defaults to None. + hosttype (str, type, optional): 'ftp', 'sftp', 'smb'. + Defaults to None. + username (str, type, optional): remote username. + Defaults to None. + password (str, type, optional): remote password. + Defaults to None. + logger_alias (str, optional): sublogger name. + Defaults to function or method name. + + Returns: + dict: {'success':bool,'result':/remote/path/to/file or 'ERROR'}. + """ + local_logger = logging.getLogger(logger_alias) + if Do.args_valid(locals(), cls.file_upload.__annotations__): + local_logger.debug(msg='' + + '\n' + 'src_file: ' + src_file + + '\n' + 'dst_file: ' + dst_file + + '\n' + 'hostname: ' + str(hostname) + + '\n' + 'hostport: ' + str(hostport) + + '\n' + 'hosttype: ' + str(hosttype) + + '\n' + 'username: ' + str(username) + + '\n' + 'password: ' + str(password) + ) + remote_hostpath = dst_file + remote_hostname = hostname + remote_hostport = hostport + remote_hosttype = hosttype + remote_username = username + remote_password = password + if '://' in dst_file: + remote_hostname = dst_file.split('/')[2] + remote_hosttype = dst_file.split('://')[0] + remote_hostpath = dst_file.replace(remote_hosttype + '://' + remote_hostname, '') + if '@' in remote_hostname: + remote_username = remote_hostname.split('@')[0].split(':')[0] + remote_password = remote_hostname.split('@')[0].split(':')[1] + remote_hostname = remote_hostname.split('@')[1] + if ':' in remote_hostname: + remote_hostport = int(remote_hostname.split(':')[1]) + remote_hostname = remote_hostname.split(':')[0] + if not hostport: + if remote_hosttype == 'ftp': + remote_hostport = 21 + if remote_hosttype == 'sftp': + remote_hostport = 22 + if remote_hosttype == 'smb': + remote_hostport = 445 + + if remote_hosttype == 'ftp': + result = cls.ftp_put_file( + src_file=src_file, + dst_file=remote_hostpath, + hostname=remote_hostname, + username=remote_username, + password=remote_password, + port=remote_hostport, + logger_alias=logger_alias + ) + elif remote_hosttype == 'sftp': + result = cls.ssh_put_file( + src_file=src_file, + dst_file=remote_hostpath, + hostname=remote_hostname, + username=remote_username, + password=remote_password, + port=remote_hostport, + logger_alias=logger_alias + ) + elif remote_hosttype == 'smb': + result = cls.smb_put_file( + src_file=src_file, + dst_file=remote_hostpath, + hostname=remote_hostname, + username=remote_username, + password=remote_password, + port=remote_hostport, + logger_alias=logger_alias + ) + else: + result = cls.local_put_file( + src_file=src_file, + dst_file=remote_hostpath, + logger_alias=logger_alias + ) + + local_logger.debug(msg='result: ' + '\n' + str(result)) + return result class HikISAPI(Connect): @@ -1909,7 +2642,7 @@ class Wordpress(Connect): return {"success": False, "result": "ERROR"} -class Telegram(): +class Telegram: """Set of methods (functions) for Telegram Bot API. Reference: https://core.telegram.org/bots/api#available-methods """ @@ -2280,6 +3013,9 @@ class Sequence: sequence: dict, temp_path: str, records_root_path: str = None, + records_root_host: str = None, + records_root_port: int = None, + records_root_type: str = None, records_root_user: str = None, records_root_pass: str = None, logger_alias: str = inspect.stack()[0].function @@ -2293,6 +3029,12 @@ class Sequence: temp_path (str): path to directory for temp files. records_root_path (str, optional): path (local|smb|ftp,sftp) to records directory. Defaults to None. + records_root_host (str, optional): hostname if path on remote host. + Defaults to None. + records_root_port (int, optional): connection port if path on remote host. + Defaults to None. + records_root_type (str, optional): 'ftp', 'sftp', 'smb' if path on remote host. + Defaults to None. records_root_user (str, optional): username if path on remote host. Defaults to None. records_root_pass (str, optional): password if path on remote host. @@ -2373,95 +3115,30 @@ class Sequence: y=int(y), dst_file=temp_path + sep + records_file_name ): - hostname = 'localhost' - hostport, hosttype = None, None - username = records_root_user - userpass = records_root_pass - hostpath = records_root_path - if '://' in records_root_path: - hostname = records_root_path.split('/')[2] - hosttype = records_root_path.split('://')[0] - if hosttype == 'ftp': - hostport = 21 - if hosttype == 'sftp': - hostport = 22 - if hosttype == 'smb': - hostport = 445 - hostpath = records_root_path.replace(hosttype + '://' + hostname, '') - if '@' in hostname: - username = hostname.split('@')[0].split(':')[0] - userpass = hostname.split('@')[0].split(':')[1] - hostname = hostname.split('@')[1] - if ':' in hostname: - hostport = int(hostname.split(':')[1]) - hostname = hostname.split(':')[0] - if hosttype == 'ftp': - src_file = temp_path + sep + records_file_name - dst_file = ( - hostpath - + '/' + dy + '/' + dm + '/' + dv + '/' + dd + '/' - + records_file_name - ) - if Connect.ftp_put_file( - src_file=src_file, - dst_file=dst_file, - hostname=hostname, - username=username, - password=userpass, - logger_alias=logger_alias - ): - try: - remove(src_file) - except OSError as error: - local_logger.debug(msg='error: ' + '\n' + str(error)) - response = True - else: - response = False - elif hosttype == 'sftp': - src_file = temp_path + sep + records_file_name - dst_file = ( - hostpath - + '/' + dy + '/' + dm + '/' + dv + '/' + dd + '/' - + records_file_name - ) - response = Connect.ssh_put_file( - src_file=src_file, - dst_file=dst_file, - hostname=hostname, - port=hostport, - username=username, - password=userpass, - logger_alias=logger_alias - ) - if response != 'ERROR': - try: - remove(src_file) - except OSError as error: - local_logger.debug(msg='error: ' + '\n' + str(error)) - response = True - else: - response = False - else: - src_file = temp_path + sep + records_file_name - dst_file = ( - hostpath - + sep + dy + sep + dm + sep + dv + sep + dd + sep - + records_file_name - ) - local_logger.debug(msg='' - + '\n' + 'src_file: ' + src_file - + '\n' + 'dst_file: ' + dst_file - ) + src_file = temp_path + sep + records_file_name + dst_file = ('' + + records_root_path + + '/' + dy + '/' + dm + '/' + dv + '/' + dd + '/' + + records_file_name + ) + response = Connect.file_upload( + src_file=src_file, + dst_file=dst_file, + hostname=records_root_host, + hostport=records_root_port, + hosttype=records_root_type, + username=records_root_user, + password=records_root_pass, + logger_alias=logger_alias + ) + if response['success']: try: - makedirs( - hostpath + sep + dy + sep + dm + sep + dv + sep + dd, - exist_ok=True - ) - replace(src=src_file, dst=dst_file) - response = True + remove(src_file) except OSError as error: local_logger.debug(msg='error: ' + '\n' + str(error)) - response = False + response = True + else: + response = False else: response = False if w != '-' or float(w) != 0: @@ -2469,7 +3146,7 @@ class Sequence: if response: local_logger.info(msg='result:' + key + ' = OK') else: - local_logger.warning(msg='result:' + key + ' = ERROR') + local_logger.warning(msg='result: ' + key + ' = ERROR') try: rmdir(temp_path) except OSError as error: @@ -2491,8 +3168,14 @@ class Convert: video_framerate: int, video_duration: int, temp_path: str, + image_root_host: (str, type(None)) = None, + image_root_port: (int, type(None)) = None, + image_root_type: (str, type(None)) = None, image_root_user: (str, type(None)) = None, image_root_pass: (str, type(None)) = None, + video_dest_host: (str, type(None)) = None, + video_dest_port: (int, type(None)) = None, + video_dest_type: (str, type(None)) = None, video_dest_user: (str, type(None)) = None, video_dest_pass: (str, type(None)) = None, logger_alias: str = inspect.stack()[0].function @@ -2509,13 +3192,25 @@ class Convert: video_framerate (int): destination video frame per second. video_duration (int): destination video duration. temp_path (str): path to directory for temp files. - image_root_user (str, type, optional): username to source images ftp,sftp,smb path. + image_root_host (str, None, optional): source images hostname. Defaults to None. - image_root_pass (str, type, optional): password to source images ftp,sftp,smb path. + image_root_port (int, None, optional): source images connection port. Defaults to None. - video_dest_user (str, type, optional): username to destination video ftp,sftp,smb path. + image_root_type (str, None, optional): ftp,sftp,smb. Defaults to None. - video_dest_pass (str, type, optional): password to destination video ftp,sftp,smb path. + image_root_user (str, None, optional): username to source images ftp,sftp,smb path. + Defaults to None. + image_root_pass (str, None, optional): password to source images ftp,sftp,smb path. + Defaults to None. + video_dest_host (str, None, optional): destination video hostname. + Defaults to None. + video_dest_port (int, None, optional): destination video connection port. + Defaults to None. + video_dest_type (str, None, optional): ftp,sftp,smb. + Defaults to None. + video_dest_user (str, None, optional): username to destination video ftp,sftp,smb path. + Defaults to None. + video_dest_pass (str, None, optional): password to destination video ftp,sftp,smb path. Defaults to None. logger_alias (str, optional): sublogger name. Defaults to function or method name. @@ -2528,46 +3223,28 @@ class Convert: for name in image_find_names: image_found = [] for image_root in image_root_path: - if '://' in image_root: - image_hostname = image_root.split('/')[2] - image_hosttype = image_root.split('://')[0] - if image_hosttype == 'ftp': - image_hostport = 21 - if image_hosttype == 'sftp': - image_hostport = 22 - if image_hosttype == 'smb': - image_hostport = 445 - image_hostpath = image_root.replace(image_hosttype + '://' + image_hostname, '') - if '@' in image_hostname: - image_root_user = image_hostname.split('@')[0].split(':')[0] - image_root_pass = image_hostname.split('@')[0].split(':')[1] - image_hostname = image_hostname.split('@')[1] - if ':' in image_hostname: - image_hostport = int(image_hostname.split(':')[1]) - image_hostname = image_hostname.split(':')[0] - if image_hosttype == 'ftp': - image_found = image_found + Connect.ftp_file_search( - root_path=image_hostpath, - search_name=name, - hostname=image_hostname, + image_found = image_found + Connect.file_search( + search_path=image_root, + search_name=name, + hostname=image_root_host, + hostport=image_root_port, + hosttype=image_root_type, + username=image_root_user, + password=image_root_pass, + logger_alias=logger_alias + ) + makedirs(temp_path, exist_ok=True) + for image in image_found: + if Connect.file_download( + src_file=image, + dst_file=temp_path + sep + path.basename(image), + hostname=image_root_host, + hostport=image_root_port, + hosttype=image_root_type, username=image_root_user, - password=image_root_pass - ) - makedirs(temp_path, exist_ok=True) - for image in image_found: - if Connect.ftp_get_file( - src_file=image, - dst_file=temp_path + sep + path.basename(image), - hostname=image_hostname, - username=image_root_user, - password=image_root_pass - ): - temp_files.append(temp_path + sep + path.basename(image)) - elif image_hosttype == 'sftp': - pass - else: - pass - + password=image_root_pass, + )['success']: + temp_files.append(temp_path + sep + path.basename(image)) image_temp_list = temp_path + sep + 'convert.list' image_amount = 0 with open(image_temp_list, mode='w+', encoding='UTF-8') as converter_list: @@ -2591,40 +3268,16 @@ class Convert: if FFmpeg.run(raw=video_conv_conf, logger_alias=logger_alias) == 0: temp_files.append(video_temp_path) - - if '://' in video_dest_path: - video_hostname = video_dest_path.split('/')[2] - video_hosttype = video_dest_path.split('://')[0] - if video_hosttype == 'ftp': - video_hostport = 21 - if video_hosttype == 'sftp': - video_hostport = 22 - if video_hosttype == 'smb': - video_hostport = 445 - video_hostpath = video_dest_path.replace( - video_hosttype + '://' + video_hostname, - '' - ) - if '@' in image_hostname: - video_dest_user = video_hostname.split('@')[0].split(':')[0] - video_dest_pass = video_hostname.split('@')[0].split(':')[1] - video_hostname = video_hostname.split('@')[1] - if ':' in video_hostname: - video_hostport = int(video_hostname.split(':')[1]) - video_hostname = video_hostname.split(':')[0] - if video_hosttype == 'ftp': - if Connect.ftp_put_file( - src_file=video_temp_path, - dst_file=video_hostpath + '/' + video_converted, - hostname=video_hostname, - username=video_dest_user, - password=video_dest_pass, - logger_alias=logger_alias - ): - pass - elif image_hosttype == 'sftp': - pass - else: + if Connect.file_upload( + src_file=video_temp_path, + dst_file=video_dest_path + '/' + video_converted, + hostname=video_dest_host, + hostport=video_dest_port, + hosttype=video_dest_type, + username=video_dest_user, + password=video_dest_pass, + logger_alias=logger_alias + )['success']: pass for temp_file in temp_files: @@ -2649,6 +3302,9 @@ class Publish: temp_path: str, publish_period: str, publish_amount: int, + video_root_host: (str, type(None)) = None, + video_root_port: (int, type(None)) = None, + video_root_type: (str, type(None)) = None, video_root_user: (str, type(None)) = None, video_root_pass: (str, type(None)) = None, wp_site_name: (str, type(None)) = None, @@ -2667,6 +3323,12 @@ class Publish: temp_path (str): path to directory for temp files. publish_period (str): +/- periods. publish_amount (int): 'y'|'year','m'|'month','w'|'week','d'|'day'. + video_root_host (str, None, optional): source video hostname. + Defaults to None. + video_root_port (int, None, optional): source video connection port. + Defaults to None. + video_root_type (str, None, optional): ftp,sftp,smb. + Defaults to None. video_root_user (str, None, optional): username to source video ftp,sftp,smb path. Defaults to None. video_root_pass (str, None, optional): password to source video ftp,sftp,smb path. @@ -2719,44 +3381,33 @@ class Publish: for name in video_find_names: video_found = [] for video_root in video_root_path: - if '://' in video_root: - video_hostname = video_root.split('/')[2] - video_hosttype = video_root.split('://')[0] - video_hostpath = video_root.replace(video_hosttype + '://' + video_hostname, '') - if '@' in video_hostname: - video_root_user = video_hostname.split('@')[0].split(':')[0] - video_root_pass = video_hostname.split('@')[0].split(':')[1] - video_hostname = video_hostname.split('@')[1] - if ':' in video_hostname: - video_hostport = int(video_hostname.split(':')[1]) - video_hostname = video_hostname.split(':')[0] - if video_hosttype == 'ftp': - video_found = video_found + Connect.ftp_file_search( - root_path=video_hostpath, + video_found = video_found + Connect.file_search( + search_path=video_root, search_name=name + '_' + video_find_sufx + '.mp4', - hostname=video_hostname, + hostname=video_root_host, + hostport=video_root_port, + hosttype=video_root_type, username=video_root_user, password=video_root_pass, logger_alias=logger_alias ) - makedirs(temp_path, exist_ok=True) - for video in video_found: - if Connect.ftp_get_file( - src_file=video, - dst_file=temp_path + sep + path.basename(video), - hostname=video_hostname, - username=video_root_user, - password=video_root_pass, - logger_alias=logger_alias - ): - temp_files.append(temp_path + sep + path.basename(video)) - video_files[publish_period][name] = ( - temp_path + sep + path.basename(video) - ) - elif video_hosttype == 'sftp': - pass - else: - pass + makedirs(temp_path, exist_ok=True) + + for video in video_found: + if Connect.file_download( + src_file=video, + dst_file=temp_path + sep + path.basename(video), + hostname=video_root_host, + hostport=video_root_port, + hosttype=video_root_type, + username=video_root_user, + password=video_root_pass, + logger_alias=logger_alias + )['success']: + temp_files.append(temp_path + sep + path.basename(video)) + video_files[publish_period][name] = ( + temp_path + sep + path.basename(video) + ) if tg_api_key: tg = Telegram(tg_api_key) @@ -2797,6 +3448,19 @@ class Publish: class Do(): """Set of various methods (functions) for routine. """ + + @staticmethod + def random_string(length: int) -> str: + """Generate string from lowercase letters, uppercase letters, digits. + + Args: + length (int): string lenght. + + Returns: + str: random string. + """ + return ''.join(choice(ascii_letters + digits) for i in range(length)) + @staticmethod def args_valid(arguments: dict, annotations: dict) -> bool: """Arguments type validating by annotations. @@ -2828,14 +3492,17 @@ class Do(): def date_calc( target: datetime.date = datetime.datetime.now(), amount: int = 0, - period: str = None + period: (str, type(None)) = None ) -> dict: """Calculating start/end dates for period: day, week, month, year. Args: - target (datetime.date, optional): date in the calculation period. Defaults to now. - amount (int, optional): +/- periods. Defaults to 0. - period (str, optional): 'y'|'year','m'|'month','w'|'week','d'|'day'. Defaults to None. + target (datetime.date, optional): date in the calculation period. + Defaults to now. + amount (int, optional): +/- periods. + Defaults to 0. + period (str, type, optional): 'y'|'year','m'|'month','w'|'week','d'|'day'. + Defaults to None. Raises: ValueError: 'period' value is wrong. @@ -2906,18 +3573,6 @@ class Do(): } return date - @staticmethod - def random_string(length: int) -> str: - """Generate string from lowercase letters, uppercase letters, digits. - - Args: - length (int): string lenght. - - Returns: - str: random string. - """ - return ''.join(choice(ascii_letters + digits) for i in range(length)) - @staticmethod # pylint: disable=W0612 def file_search(root_path: str, search_name: (str, type(None)) = None) -> list: @@ -3362,7 +4017,7 @@ if __name__ == "__main__": description='Hikvision PTZ IP-Camera management.', epilog='Dependencies: ' '- Python 3 (tested version 3.9.5), ' - '- Python 3 modules: paramiko ' + '- Python 3 modules: paramiko, smbprotocol ' ) args.add_argument('--config', type=str, default=path.splitext(__file__)[0] + '.conf', required=False, @@ -3428,6 +4083,7 @@ if __name__ == "__main__": level=log_level ) logging.getLogger("paramiko").setLevel(logging.WARNING) + logging.getLogger("smbprotocol").setLevel(logging.WARNING) if args['broadcast']: logging.info(msg='Streaming starts...') @@ -3518,10 +4174,19 @@ if __name__ == "__main__": userpass=device_config['userpass'] ) records_root_path = path.dirname(path.realpath(__file__)) + records_root_host = None + records_root_port = None + records_root_type = None records_root_user = None records_root_pass = None if 'records_root_path' in device_config: records_root_path = device_config['records_root_path'] + if 'records_root_host' in device_config: + records_root_host = device_config['records_root_host'] + if 'records_root_port' in device_config: + records_root_port = int(device_config['records_root_port']) + if 'records_root_type' in device_config: + records_root_type = device_config['records_root_type'] if 'records_root_user' in device_config: records_root_user = device_config['records_root_user'] if 'records_root_pass' in device_config: @@ -3532,6 +4197,9 @@ if __name__ == "__main__": sequence=device_sequence, temp_path=temp_path + sep + Do.random_string(8), records_root_path=records_root_path, + records_root_host=records_root_host, + records_root_port=records_root_port, + records_root_type=records_root_type, records_root_user=records_root_user, records_root_pass=records_root_pass, logger_alias='sequence ' + key @@ -3566,11 +4234,20 @@ if __name__ == "__main__": block='convert-config:' + key ).data image_root_path = path.dirname(path.realpath(__file__)) + image_root_host = None + image_root_port = None + image_root_type = None image_root_user = None image_root_pass = None image_find_names = None if 'image_root_path' in convert_config: image_root_path = convert_config['image_root_path'] + if 'image_root_host' in convert_config: + image_root_host = convert_config['image_root_host'] + if 'image_root_port' in convert_config: + image_root_port = int(convert_config['image_root_port']) + if 'image_root_type' in convert_config: + image_root_type = convert_config['image_root_type'] if 'image_root_user' in convert_config: image_root_user = convert_config['image_root_user'] if 'image_root_pass' in convert_config: @@ -3643,10 +4320,19 @@ if __name__ == "__main__": video_duration = 360 video_dest_path = path.dirname(path.realpath(__file__)) + video_dest_host = None + video_dest_port = None + video_dest_type = None video_dest_user = None video_dest_pass = None if 'video_dest_path' in convert_config: video_dest_path = convert_config['video_dest_path'] + if 'video_dest_host' in convert_config: + video_dest_host = convert_config['video_dest_host'] + if 'video_dest_port' in convert_config: + video_dest_port = int(convert_config['video_dest_port']) + if 'video_dest_type' in convert_config: + video_dest_type = convert_config['video_dest_type'] if 'video_dest_user' in convert_config: video_dest_user = convert_config['video_dest_user'] if 'video_dest_pass' in convert_config: @@ -3654,11 +4340,17 @@ if __name__ == "__main__": Convert.run( image_root_path=image_root_path, + image_root_host=image_root_host, + image_root_port=image_root_port, + image_root_type=image_root_type, image_root_user=image_root_user, image_root_pass=image_root_pass, image_find_names=image_find_names, video_dest_path=video_dest_path, video_dest_sufx=video_dest_sufx, + video_dest_host=video_dest_host, + video_dest_port=video_dest_port, + video_dest_type=video_dest_type, video_dest_user=video_dest_user, video_dest_pass=video_dest_pass, video_scale_x=int(convert_config['video_scale_x']), @@ -3680,11 +4372,20 @@ if __name__ == "__main__": block='publish-config:' + key ).data video_root_path = path.dirname(path.realpath(__file__)) + video_root_host = None + video_root_port = None + video_root_type = None video_root_user = None video_root_pass = None video_find_names = None if 'video_root_path' in publish_config: video_root_path = publish_config['video_root_path'] + if 'video_root_host' in publish_config: + video_root_host = publish_config['video_root_host'] + if 'video_root_port' in publish_config: + video_root_port = int(publish_config['video_root_port']) + if 'video_root_type' in publish_config: + video_root_type = publish_config['video_root_type'] if 'video_root_user' in publish_config: video_root_user = publish_config['video_root_user'] if 'video_root_pass' in publish_config: @@ -3698,7 +4399,9 @@ if __name__ == "__main__": wp_user_name = None wp_user_pass = None wp_update_page_id = None - if publish_config['wp_enabled'] == 'true': + if ('wp_enabled' in publish_config and + publish_config['wp_enabled'] == 'true' + ): if 'wp_site_name' in publish_config: wp_site_name = publish_config['wp_site_name'] if 'wp_user_name' in publish_config: @@ -3710,7 +4413,9 @@ if __name__ == "__main__": tg_api_key = None tg_chat_id = None - if publish_config['tg_enabled'] == 'true': + if ('tg_enabled' in publish_config and + publish_config['tg_enabled'] == 'true' + ): if 'tg_api_key' in publish_config: tg_api_key = publish_config['tg_api_key'] if 'tg_chat_id' in publish_config: @@ -3718,6 +4423,9 @@ if __name__ == "__main__": Publish.run( video_root_path=video_root_path, + video_root_host=video_root_host, + video_root_port=video_root_port, + video_root_type=video_root_type, video_root_user=video_root_user, video_root_pass=video_root_pass, video_find_names=video_find_names,