Compare commits
3
Commits
9283151f3b
...
3da7e7c152
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3da7e7c152 | ||
|
|
8ce03eddec | ||
|
|
8104060d0a |
Vendored
+3
@@ -55,6 +55,9 @@
|
||||
"env": {
|
||||
"PYTHONPATH": "${workspaceFolder}",
|
||||
"INTERVAL": "15",
|
||||
"DCP_INTERVAL" : "300",
|
||||
"DCP_SERVER_FILE": "./dcpservers.json",
|
||||
"DCP_NEW_TOPIC": "projmon/newdcp",
|
||||
"LOG_FILE": "/tmp/upsmon.log",
|
||||
"LOG_FILE_LVL": "WARNING",
|
||||
"LOG_CLI_LVL": "DEBUG",
|
||||
|
||||
+234
-2
@@ -1,21 +1,233 @@
|
||||
{
|
||||
"servers": [
|
||||
{
|
||||
"name": "MART",
|
||||
"host": "192.168.31.15",
|
||||
"port": 21,
|
||||
"username": null,
|
||||
"password": null,
|
||||
"cwd": "TRAILER",
|
||||
"timeout": 15,
|
||||
"dirs": [
|
||||
{
|
||||
"name": "A-NEW-DAWN---TRL--23-06-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898002",
|
||||
"unique": "1788972262.89801"
|
||||
},
|
||||
{
|
||||
"name": "BEAST---TRL-90----31-07-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898043",
|
||||
"unique": "1788972262.898044"
|
||||
},
|
||||
{
|
||||
"name": "CINEMA-IN-FESTA-15--18-GIUGNO-2026--CIN-REV26_TLR-15---SPOT-15---ANIMATO--09-06-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898054",
|
||||
"unique": "1788972262.898055"
|
||||
},
|
||||
{
|
||||
"name": "COYOTE-VS-ACME---TRL-60---NEW--06-08-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898064",
|
||||
"unique": "1788972262.898065"
|
||||
},
|
||||
{
|
||||
"name": "COYOTE-VS-ACME---TRL-90----21-07-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898074",
|
||||
"unique": "1788972262.898074"
|
||||
},
|
||||
{
|
||||
"name": "COYOTE-VS-ACME--COYOTEVSACME30----TRL-30----22-06-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898083",
|
||||
"unique": "1788972262.898084"
|
||||
},
|
||||
{
|
||||
"name": "ERA-MEGLIO-DOMANI---TRL--30-06-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898093",
|
||||
"unique": "1788972262.898093"
|
||||
},
|
||||
{
|
||||
"name": "FERRARI-250-GTO---TRL--06-08-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898102",
|
||||
"unique": "1788972262.898102"
|
||||
},
|
||||
{
|
||||
"name": "FRAMMENTI---TRL-90----13-05-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898111",
|
||||
"unique": "1788972262.898112"
|
||||
},
|
||||
{
|
||||
"name": "GODZILLA-MINUS-ZERO---TRL-83---04-09-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898121",
|
||||
"unique": "1788972262.898121"
|
||||
},
|
||||
{
|
||||
"name": "HOKUM--HOKUMFADO----TSR-40----11-05-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898130",
|
||||
"unique": "1788972262.898131"
|
||||
},
|
||||
{
|
||||
"name": "HOKUM--HOKUM_BELLS----TSR-30----11-05-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898139",
|
||||
"unique": "1788972262.89814"
|
||||
},
|
||||
{
|
||||
"name": "HOPPER-E-IL-SEGRETO-DELLA-GRANDE-MARMOTTA---TRL-90----13-02-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898149",
|
||||
"unique": "1788972262.898149"
|
||||
},
|
||||
{
|
||||
"name": "HUNGER-GAMES-6--HGALBAMIETITU----TLR-150--30-04-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898158",
|
||||
"unique": "1788972262.898158"
|
||||
},
|
||||
{
|
||||
"name": "HUNGER-GAMES-6--HGALBAMIETITU----TLR-90----05-05-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898167",
|
||||
"unique": "1788972262.898168"
|
||||
},
|
||||
{
|
||||
"name": "HUNGER-GAMES-6--HGALBAMIETITU----TLR-D-60----11-05-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898177",
|
||||
"unique": "1788972262.898177"
|
||||
},
|
||||
{
|
||||
"name": "I-FIGLI-DELLA-SCIMMIA---TRL-1-120----06-08-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898186",
|
||||
"unique": "1788972262.898186"
|
||||
},
|
||||
{
|
||||
"name": "I-FIGLI-DELLA-SCIMMIA---TRL-1-90----06-08-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898195",
|
||||
"unique": "1788972262.898196"
|
||||
},
|
||||
{
|
||||
"name": "ICE-CREAM-MAN--ICM90----TRL-90----29-07-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898205",
|
||||
"unique": "1788972262.898205"
|
||||
},
|
||||
{
|
||||
"name": "IO-NON-TI-LASCIO-SOLO---TRL--28-04-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898214",
|
||||
"unique": "1788972262.898214"
|
||||
},
|
||||
{
|
||||
"name": "L-ALBERO-DI-MOLTO-LONTANO---TRL90----14-07-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898223",
|
||||
"unique": "1788972262.898224"
|
||||
},
|
||||
{
|
||||
"name": "LE-AQUILE-DELLA-REPUBBLICA---TLR-B_CON_BOLLINI--08-01-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898232",
|
||||
"unique": "1788972262.898233"
|
||||
},
|
||||
{
|
||||
"name": "MAIGRET-L-AMORE-E-LA-MORTE---TRL--27-07-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898241",
|
||||
"unique": "1788972262.898242"
|
||||
},
|
||||
{
|
||||
"name": "ONSLAUGHT-ASSALTO-FINALE---TRL-1--10-08-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898262",
|
||||
"unique": "1788972262.898262"
|
||||
},
|
||||
{
|
||||
"name": "QUASI-VERA---TRL-1-60----30-07-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898271",
|
||||
"unique": "1788972262.898272"
|
||||
},
|
||||
{
|
||||
"name": "QUASI-VERA---TRL-1-90----28-07-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898281",
|
||||
"unique": "1788972262.898281"
|
||||
},
|
||||
{
|
||||
"name": "RIDER---TRL-1--13-08-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898290",
|
||||
"unique": "1788972262.898291"
|
||||
},
|
||||
{
|
||||
"name": "RITORNO-A-BUENOS-AIRES---TRL-145----03-08-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898300",
|
||||
"unique": "1788972262.8983"
|
||||
},
|
||||
{
|
||||
"name": "RITORNO-A-BUENOS-AIRES---TRL-60----03-08-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898309",
|
||||
"unique": "1788972262.898309"
|
||||
},
|
||||
{
|
||||
"name": "SANTIAGO-UN-CAMMINO-PER-RICOMINCIARE---TRL-120----26-06-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898318",
|
||||
"unique": "1788972262.898319"
|
||||
},
|
||||
{
|
||||
"name": "SE-DOMANI-NON-TORNO---TRL-90SEC--10-07-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898328",
|
||||
"unique": "1788972262.898328"
|
||||
},
|
||||
{
|
||||
"name": "SERPENTI---TRL-60--07-08-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898337",
|
||||
"unique": "1788972262.898337"
|
||||
},
|
||||
{
|
||||
"name": "SHAUN-VITA-DA-PECORA--SHAUNTHESHEE60----TRL-60----15-06-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898346",
|
||||
"unique": "1788972262.898347"
|
||||
},
|
||||
{
|
||||
"name": "SHAUN-VITA-DA-PECORA--STS30SALA_TLR----TRL-30----23-06-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898356",
|
||||
"unique": "1788972262.898356"
|
||||
},
|
||||
{
|
||||
"name": "THE-MORTUARY-ASSISTANT---TRL--05-08-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898365",
|
||||
"unique": "1788972262.898365"
|
||||
},
|
||||
{
|
||||
"name": "TRAIN-TO-BUSAN-RE-RELEASE--TRL-40S--15-07-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898374",
|
||||
"unique": "1788972262.898374"
|
||||
},
|
||||
{
|
||||
"name": "UNA-STORIA---TRL1---26-08-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898383",
|
||||
"unique": "1788972262.898383"
|
||||
},
|
||||
{
|
||||
"name": "WILDWOOD---TRL-150-SEC-B--19-08-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898392",
|
||||
"unique": "1788972262.898393"
|
||||
},
|
||||
{
|
||||
"name": "WILDWOOD---TRL-90-SEC-B--19-08-2026-",
|
||||
"modified_time": "2026-09-09T18:44:22.898401",
|
||||
"unique": "1788972262.898402"
|
||||
}
|
||||
],
|
||||
"notify_filters": [
|
||||
"TLR",
|
||||
"TRL",
|
||||
"FTR"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "DELUXE",
|
||||
"host": "192.168.31.16",
|
||||
"port": 21,
|
||||
"username": "dcpftp",
|
||||
"password": "dcpftp",
|
||||
"cwd": null,
|
||||
"timeout": 15,
|
||||
"dirs": [
|
||||
{
|
||||
"name": ".",
|
||||
"modified_time": "2026-09-09T10:42:53",
|
||||
"modified_time": "2026-09-09T15:41:58",
|
||||
"unique": "FD02UF00001"
|
||||
},
|
||||
{
|
||||
"name": "..",
|
||||
"modified_time": "2026-09-09T10:42:53",
|
||||
"modified_time": "2026-09-09T15:41:58",
|
||||
"unique": "FD02UF00001"
|
||||
},
|
||||
{
|
||||
@@ -118,6 +330,16 @@
|
||||
"modified_time": "2026-08-19T13:42:38",
|
||||
"unique": "FD02UF1001E"
|
||||
},
|
||||
{
|
||||
"name": "CIF26_Sett-2026_F_IT-XX_IT_51_2K_Demba_20260909_LSF_IOP_OV",
|
||||
"modified_time": "2026-09-09T13:02:20",
|
||||
"unique": "FD02UF10063"
|
||||
},
|
||||
{
|
||||
"name": "CIF26_Sett-2026_S_IT-XX_IT_51_2K_Demba_20260909_LSF_IOP_OV",
|
||||
"modified_time": "2026-09-09T13:03:07",
|
||||
"unique": "FD02UF10064"
|
||||
},
|
||||
{
|
||||
"name": "Cars20year_TLR-30A_S_IT-XX_IT_20_2K_DI_20260624_RL1_IOP_OV",
|
||||
"modified_time": "2026-08-14T07:05:17",
|
||||
@@ -613,6 +835,16 @@
|
||||
"modified_time": "2026-08-19T13:52:28",
|
||||
"unique": "FD02UF10035"
|
||||
},
|
||||
{
|
||||
"name": "WildHorseNine_TLR-B-2D-6Plus_F_IT-XX_IT_51_2K_DI_20260903_DDS_IOP",
|
||||
"modified_time": "2026-09-09T15:41:58",
|
||||
"unique": "FD02UF1007B"
|
||||
},
|
||||
{
|
||||
"name": "WildHorseNine_TLR-B-2D-6Plus_S_IT-XX_IT_51_2K_DI_20260903_DDS_IOP",
|
||||
"modified_time": "2026-09-09T15:41:58",
|
||||
"unique": "FD02UF1007A"
|
||||
},
|
||||
{
|
||||
"name": "assets-folder",
|
||||
"modified_time": "2025-08-30T16:32:48",
|
||||
|
||||
@@ -161,6 +161,9 @@ services:
|
||||
max-file: "5"
|
||||
environment:
|
||||
- INTERVAL=15
|
||||
- DCP_INTERVAL=300
|
||||
- DCP_SERVER_FILE=/home/dcpservers.json,
|
||||
- DCP_NEW_TOPIC=projmon/newdcp
|
||||
- PROJECTOR_IP=192.168.31.10
|
||||
- PROJECTOR_USER=alberto
|
||||
- PROJECTOR_PASSWORD=alberto
|
||||
|
||||
+51
-36
@@ -1,12 +1,10 @@
|
||||
from ftplib import FTP
|
||||
from ftplib import FTP, error_perm
|
||||
import os
|
||||
import sys
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
from time import sleep
|
||||
from paho.mqtt import client as mqtt_client
|
||||
from paho.mqtt.enums import CallbackAPIVersion
|
||||
from logging import Logger
|
||||
|
||||
class FTPDirInfo(BaseModel):
|
||||
@@ -20,6 +18,7 @@ class FTPServer(BaseModel):
|
||||
port: int = 21
|
||||
username: Optional[str] = None
|
||||
password: Optional[str] = None
|
||||
cwd: Optional[str] = None
|
||||
timeout: int = 15
|
||||
dirs: List[FTPDirInfo] = Field(default_factory=list)
|
||||
notify_filters: List[str] = Field(default_factory=list)
|
||||
@@ -30,6 +29,9 @@ class FTPServer(BaseModel):
|
||||
ftp.connect(self.host, self.port, timeout=self.timeout)
|
||||
if self.username and self.password:
|
||||
ftp.login(self.username, self.password)
|
||||
else:
|
||||
# Anonymous
|
||||
ftp.login()
|
||||
return ftp
|
||||
|
||||
class FTPServers(BaseModel):
|
||||
@@ -53,11 +55,13 @@ def save_ftp_servers_to_json(ftp_servers: FTPServers, file_path: str) -> None:
|
||||
def initialize_ftp_servers(file_path: str) -> FTPServers:
|
||||
"""Initializes FTP server configurations."""
|
||||
dcp_servers = FTPServers()
|
||||
# dcp_servers.servers.append(FTPServer(
|
||||
# name="MART",
|
||||
# host="192.168.31.15",
|
||||
# port=21)
|
||||
# )
|
||||
dcp_servers.servers.append(FTPServer(
|
||||
name="MART",
|
||||
host="192.168.31.15",
|
||||
port=21,
|
||||
notify_filters=["TLR", "TRL", "FTR"],
|
||||
cwd='TRAILER'
|
||||
))
|
||||
|
||||
dcp_servers.servers.append(FTPServer(
|
||||
name="DELUXE",
|
||||
@@ -65,8 +69,8 @@ def initialize_ftp_servers(file_path: str) -> FTPServers:
|
||||
port=21,
|
||||
username="dcpftp",
|
||||
password="dcpftp",
|
||||
notify_filters=["TLR", "TRL", "FTR"])
|
||||
)
|
||||
notify_filters=["TLR", "TRL", "FTR"]
|
||||
))
|
||||
|
||||
save_ftp_servers_to_json(dcp_servers, file_path)
|
||||
return dcp_servers
|
||||
@@ -74,14 +78,24 @@ def initialize_ftp_servers(file_path: str) -> FTPServers:
|
||||
def list_new_directories(ftp_server: FTPServer) -> List[FTPDirInfo]:
|
||||
"""Lists new directories on the FTP server that are not already known."""
|
||||
with ftp_server.connect() as ftp_connection:
|
||||
if ftp_server.cwd:
|
||||
ftp_connection.cwd(ftp_server.cwd)
|
||||
new_dirs: List[FTPDirInfo] = []
|
||||
for d in ftp_connection.mlsd(): # List directory contents
|
||||
dir_name: str = d[0]
|
||||
dir_info: dict = d[1]
|
||||
modified_time_str: str = dir_info.get('modify', '')
|
||||
modified_time: datetime = datetime.strptime(modified_time_str, '%Y%m%d%H%M%S') if modified_time_str else datetime.now()
|
||||
unique: str = dir_info.get('unique', str(datetime.now().timestamp()))
|
||||
new_dirs.append(FTPDirInfo(name=dir_name, modified_time=modified_time, unique=unique))
|
||||
try:
|
||||
# main method, retrieve directories with details
|
||||
for d in ftp_connection.mlsd(): # List directory contents
|
||||
dir_name: str = d[0]
|
||||
dir_info: dict = d[1]
|
||||
modified_time_str: str = dir_info.get('modify', '')
|
||||
modified_time: datetime = datetime.strptime(modified_time_str, '%Y%m%d%H%M%S') if modified_time_str else datetime.now()
|
||||
unique: str = dir_info.get('unique', str(datetime.now().timestamp()))
|
||||
new_dirs.append(FTPDirInfo(name=dir_name, modified_time=modified_time, unique=unique))
|
||||
except error_perm:
|
||||
# revert to simple directory listing mode
|
||||
for d in ftp_connection.nlst():
|
||||
dir_name = d
|
||||
new_dirs.append(FTPDirInfo(name=dir_name))
|
||||
|
||||
new_dirs.sort(key=lambda x: x.name, reverse=False) # Sort directories by name
|
||||
|
||||
diff_dirs: List[FTPDirInfo] = list(filter(lambda x: x.name not in [d.name for d in ftp_server.dirs], new_dirs))
|
||||
@@ -98,24 +112,25 @@ def check_new_titles(mqtt: mqtt_client.Client, topic: str, file_path: str, logge
|
||||
first_run = True
|
||||
|
||||
for server in dcp_servers.servers:
|
||||
with server.connect() as ftp_connection:
|
||||
logger.debug("Connected to FTP server %s: %s", server.name, ftp_connection.getwelcome())
|
||||
|
||||
diff_dirs: List[FTPDirInfo] = list_new_directories(server)
|
||||
# do not publish complete list with garbage
|
||||
if first_run:
|
||||
return
|
||||
for d in diff_dirs:
|
||||
logger.info(f"New DCP found on {server.name}: {d.name} (Modified: {d.modified_time}, Unique: {d.unique})")
|
||||
for filter_str in server.notify_filters:
|
||||
if filter_str in d.name:
|
||||
topic = topic
|
||||
notification = NotificationMessage(
|
||||
server=server.name,
|
||||
name=d.name,
|
||||
modified_time=d.modified_time
|
||||
)
|
||||
mqtt.publish(topic, notification.model_dump_json())
|
||||
sleep(1) # Small delay to ensure message is sent
|
||||
# find differences between register and FTP server
|
||||
diff_dirs: List[FTPDirInfo] = list_new_directories(server)
|
||||
# do not publish complete list with garbage
|
||||
if first_run:
|
||||
logger.info("DCP first check on %s, %d DCPs found", server.name, len(diff_dirs))
|
||||
continue
|
||||
for dir in diff_dirs:
|
||||
logger.info(f"New DCP found on {server.name}: {dir.name} (Modified: {dir.modified_time}, Unique: {dir.unique})")
|
||||
for filter_str in server.notify_filters:
|
||||
if filter_str in dir.name:
|
||||
notification = NotificationMessage(
|
||||
server=server.name,
|
||||
name=dir.name,
|
||||
modified_time=dir.modified_time
|
||||
)
|
||||
mqtt.publish(topic, notification.model_dump_json())
|
||||
sleep(1) # Small delay to ensure message is sent
|
||||
|
||||
# Save server information with updated DCP list
|
||||
save_ftp_servers_to_json(dcp_servers, file_path)
|
||||
return
|
||||
|
||||
+6
-2
@@ -39,6 +39,9 @@ def on_shutwown(client: mqtt_client.Client, userdata: Any, msg: mqtt_client.MQTT
|
||||
##################
|
||||
def main() -> int:
|
||||
INTERVAL = int(env['INTERVAL'])
|
||||
DCP_INTERVAL = int(env['DCP_INTERVAL'])
|
||||
DCP_SERVER_FILE = str(env['DCP_SERVER_FILE'])
|
||||
DCP_NEW_TOPIC = str(env['DCP_NEW_TOPIC'])
|
||||
try:
|
||||
mqtt = mqtt_client.Client(CallbackAPIVersion.VERSION2)
|
||||
mqtt.username_pw_set(username=env['MQTT_USER'], password=env['MQTT_PASSWORD'])
|
||||
@@ -312,9 +315,10 @@ def main() -> int:
|
||||
LOGGER.error("Unexpected exception: [%s]", e)
|
||||
return 1
|
||||
|
||||
if now - last_title_check > 300:
|
||||
# Check for new DCP packages in FTP servers
|
||||
if now - last_title_check > DCP_INTERVAL:
|
||||
try:
|
||||
check_new_titles(mqtt, "projmon/ftp", "dcpservers.json", LOGGER)
|
||||
check_new_titles(mqtt, DCP_NEW_TOPIC, DCP_SERVER_FILE, LOGGER)
|
||||
last_title_check = now
|
||||
except Exception as e:
|
||||
LOGGER.error("Failed to check DCP on FTP servers")
|
||||
|
||||
Reference in New Issue
Block a user