DigitalSignage/models/dss_binaries.py

336 lines
19 KiB
Python

import ast
import datetime
import json
import re
import uuid
import logging
import base64
import subprocess
import tempfile
import easywebdav
import os
import os.path
import requests
import hashlib
from PIL import Image,ImageDraw,ImageFont
from odoo import api, fields, models, _
from odoo import tools
from odoo.exceptions import ValidationError
from datetime import date
from datetime import datetime
from dateutil.relativedelta import relativedelta
from pyffmpeg import FFmpeg
from tuya_iot import TuyaOpenAPI, TUYA_LOGGER
from tuya_connector import TuyaOpenAPI, TUYA_LOGGER
from webdav4.client import Client
from webdav4.fsspec import WebdavFileSystem
import sys
TUYA_LOGGER.setLevel(logging.DEBUG)
_logger = logging.getLogger(__name__)
class dssbinaries(models.Model):
@api.model
def create(self,vals):
result = super().create(vals)
return result
_name = "dss.binaries"
_description = "DigitalSignage Dateien"
# _inherit = ['mail.thread','mail.activity.mixin']
_rec_name = "binary_id"
# _inherit = ['mail.thread', 'mail.activity.mixin']
uuid = fields.Char(default=lambda self: self._default_uuid(), required=True, readonly=True, copy=False, string='UUID')
date_create = fields.Date('Erstellungsdatum',default=lambda self: self._default_create_date())
date_lastedit = fields.Date('Änderungsdatum')
user_create = fields.Char('Erstellungsuser',default=lambda self: self._default_create_user())
user_lastedit = fields.Char('Änderungsuser')
binary_id = fields.Char(string="Binary ID", required=True, help = "Das ist eine eindeutige ID für diese Dateityp. Sie wird automatisch generiert und sollte nicht manuell geändert werden.", default=lambda self: self._default_binary_id())
binary_name = fields.Char(string="Binary Name", required=True, help = "Der Name des Dateityp, die Sie hochladen möchten. Dieser Name wird in der Benutzeroberfläche angezeigt.")
binary_mediatype = fields.Many2one('dss.mediatypes', string="Binary Mediatype", required=True, help = "Der Mediatyp, der für diese Datei verwendet werden soll. Dies kann z.B. 'Video', 'Bild' oder 'Audio' sein.")
binary_mediatype_can_gen_stamp = fields.Boolean(related='binary_mediatype.can_gen_stamp')
binary_mediatype_name = fields.Char(related='binary_mediatype.medianame', string="Mediatype Name", store=True, help = "Der Name des Mediatyps, der für diese Datei verwendet wird. Dies wird automatisch aus dem Mediatypen-Feld abgerufen")
binary_mediatype_def_filename = fields.Char(related='binary_mediatype.filepartname', string="Mediatype Default Dateiname", help = "Der Standard-Dateiname für diesen Mediatyp. Dies wird automatisch aus dem Mediatypen-Feld abgerufen")
binary_binary = fields.Binary(string="Binary Datei", help = "Die eigentliche Datei, die Sie hochladen möchten. Diese Datei wird in der Datenbank und Cloud gespeichert")
binary_binary_t = fields.Binary(string="Binary Temp Datei", help = "Die eigentliche Datei, die Sie hochladen möchten. Diese Datei wird in der Datenbank und Cloud gespeichert.")
binary_filename = fields.Char(string="Binary Dateiname", help = "Der Name der hochgeladenen Datei. Dies wird automatisch aus dem Dateinamen der hochgeladenen Datei abgerufen.",compute="_compute_binary_filename", store=True)
binary_type = fields.Char(string="Binary Typ", help = "Der Typ den die hochgeladenen Datei haben sollte.")
binary_cloud_link = fields.Char(string="Cloud Link", help = "Der Link zur Datei in der Cloud, wenn die Datei in der Cloud gespeichert ist.")
binary_size = fields.Integer(string="Binary Größe", help = "Die Größe der hochgeladenen Datei in Bytes.")
binary_description = fields.Text(string="Binary Beschreibung", help = "Eine Beschreibung der hochgeladenen Datei. Dies kann Informationen über den Inhalt der Datei, den Verwendungszweck oder andere relevante Details enthalten.")
binary_issaved = fields.Boolean(string="Binary Gespeichert", default=False, help = "Ein Flag, das angibt, ob die Datei in der Datenbank und Cloud gespeichert ist. Wenn dies aktiviert ist, wird die Datei in der Datenbank und Cloud gespeichert.")
binary_secured_ro = fields.Boolean(string="Gesperrt", default=False, help = "Ein Flag, das angibt, ob die Datei gesperrt ist. Wenn dies aktiviert ist, kann die Datei nicht mehr bearbeitet oder gelöscht werden. Sie wird dann auch nicht in Übertragungen oder Ansichten beachtet.")
binary_used_ro = fields.Boolean(string="Genutzt", default=False, help = "Ein Flag, das angibt, ob die Datei in einer Übertragung/Ausstrahlung verwendet wird. Wenn dies aktiviert ist, kann die Datei nicht mehr gelöscht oder überarbeitet werden.")
binary_contract = fields.Many2one('dss.contracts', string="Vertrag", help = "Der Vertrag, zu dem diese Datei gehört. Dies kann verwendet werden, um Dateien zu organisieren und zu verwalten, die mit bestimmten Verträgen verbunden sind.")
@api.depends('binary_binary')
@api.onchange('binary_binary')
def _compute_binary_filename(self):
"""Compute the binary filename based on the uploaded file."""
_logger.info("Binary getting Filename - B_"+str(self.id))
if len(self)>0:
for record in self:
if record.binary_binary:
# Extract the filename from the binary content
extension = 'jpg' # Default extension
if isinstance(self.binary_binary, str):
filedata = base64.b64decode(self.binary_binary.split("base64,")[-1])
else:
filedata = base64.b64decode(self.binary_binary)
filedataStr = str(filedata)
_logger.info("Binary getting Filename - B_"+str(self.id)+" - ["+str(filedataStr[0:23])+"]")
if filedataStr[0:12] == "b'\\x89PNG\\":
extension = 'png'
elif filedataStr[0:17] == "b'\\xff\\xd8\\xff":
extension = 'jpg'
elif filedataStr[0:8] == "b'GIF89a":
extension = 'gif'
elif filedataStr[0:22] == "b'\\x49\\x49\\x2a\\x00":
extension = 'tif'
elif filedataStr[0:27] == "b'\\x25\\x50\\x44\\x46\\x2d":
extension = 'pdf'
elif filedataStr[0:23] == "b'\\x00\\x00\\x00 ftypisom":
extension = 'mp4'
elif filedataStr[0:22] == "b'\\x00\\x00\\x00\\x18ftyp":
extension = 'mp4'
elif filedataStr[0:12] == "b'\\x42\\x4d":
extension = 'bmp'
record.binary_filename = f"{record.binary_mediatype.filepartname}{record.binary_contract.contract_auto_id}.{extension}"
@api.model
def _default_uuid(self):
return str(uuid.uuid4())
@api.model
def _default_create_date(self):
return datetime.now().strftime('%Y-%m-%d %H:%M:%S')
@api.model
def _default_create_user(self):
return self.env.user.name if self.env.user else 'System'
@api.model
def _default_binary_id(self):
# Generate a unique binary ID based on the current timestamp and a random UUID
return f"Datei_{str(uuid.uuid4())[:8]}"
@api.onchange('binary_binary')
def _onchange_binary_binary(self):
"""Update binary size and type when binary content is changed."""
_logger.info("New Binary B_"+str(self.id))
existing_binary = False
if not self._origin.binary_binary:
existing_binary = False
else:
existing_binary = True
if not self.binary_contract.cloudlink:
raise ValidationError(_("Der Vertrag hat keinen Cloudlink. Bitte überprüfen Sie den Vertrag."))
cloudpath = str(self.binary_contract.cloudlink)
_logger.info("New Binary checking Cloudlink B_"+str(self.id)+" - "+str(cloudpath))
cloudpath = cloudpath + '/' + str(self.binary_mediatype.cloudlink)
client = Client("https://cloud.logumedia.de/remote.php/dav/files/OdooDav/", auth=("odooClient@logumedia.de", "lm2020#OdooDav"))
_logger.info("New Binary checking Cloudlink for cont : "+str(cloudpath))
cloudexists = False
if client.exists(cloudpath):
# Convert self.binary_binary to bytes-like object
if isinstance(self.binary_binary, str):
filedata = base64.b64decode(self.binary_binary.split("base64,")[-1])
else:
filedata = base64.b64decode(self.binary_binary)
filedataStr = str(filedata)
_logger.info("New Binary Cloudlink exists - Saving File - B_"+str(self.id)+" - "+str(cloudpath)+" - " + str(filedata)+' - ' + filedataStr[0:8])
try:
decoded_data = filedata
except base64.binascii.Error as e:
if 'Incorrect padding' in str(e):
filedataStr += b'=' * (4 - len(filedataStr) % 4)
decoded_data = base64.b64decode(filedataStr)
if filedataStr[0:10] == "b'\x89PNG":
extension = 'png'
else:
extension = 'jpg'
_logger.info("New Binary Cloudlink exists - Saving File - B_"+str(self.id)+" - "+str(cloudpath)+" - " + str(base64.b64encode(decoded_data)))
##infile = open('/tmp/'+filename, "w+b")
#infile = tempfile.NamedTemporaryFile(delete=False)
##infile.write(filedata)
##client.upload_fileobj(infile, f"{cloudpath}/{filename}")
##infile.close()
##infile.delete() # Delete the temporary file after upload
cloudexists = True
else:
_logger.info("New Binary Cloudlink does not exists B_"+str(self.id)+" - "+str(cloudpath))
raise ValidationError(_("Der Cloudlink im Vertrag ist nicht vorhanden. Bitte überprüfen Sie den Link ggf. anlegen."))
if cloudexists:
if self.binary_binary:
# Calculate the size of the binary content
self.binary_size = len(filedata)
# Determine the file type based on the binary content
self.binary_cloud_link = f"{cloudpath}/{self.binary_filename}"
self.binary_issaved = True
else:
self.binary_size = 0
self.binary_type = ''
self.binary_cloud_link = ''
self.binary_issaved = False
def dload(self):
"""Download the binary file."""
if not self.binary_binary:
raise ValidationError(_("No binary file to download."))
# Create a temporary file to store the downloaded content
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=self.binary_filename)
temp_file.write(base64.b64decode(self.binary_binary))
temp_file.close()
# Return the file for download
return {
'type': 'ir.actions.act_url',
'url': f'/web/content/{self.id}/{self.binary_filename}?download=true&filename={self.binary_filename}',
'target': 'self',
}
def dedit(self):
"""Edit the binary file."""
return {
'type': 'ir.actions.act_window',
'res_model': 'dss.binaries',
'view_mode': 'form',
'res_id': self.id,
'target': 'new',
}
def resend(self):
"""Resend the binary file to the contract's partner email."""
if not self.binary_contract:
raise ValidationError(_("No contract associated with this binary file."))
if not self.binary_contract.project.standortpartner:
raise ValidationError(_("No partner associated with the contract."))
if not self.binary_contract.project.standortpartner.email:
raise ValidationError(_("The partner does not have an email address."))
# Send email with the binary file as attachment
def genstamp(self):
_logger.info('Create Stamps for Binary: B_'+str(self.id)+' '+str(id))
# Bildparameter definieren
Medium = self.binary_mediatype
if Medium and Medium.can_gen_stamp and self.binary_contract:
_logger.info('Create Stamps for Contract : B_'+str(self.id)+' - Contract: C_'+str(self.binary_contract.id)+'- Medium : M_'+str(Medium.id)+' - '+str(Medium.medianame))
# Bildgröße und Hintergrundfarbe festlegen
background_color = (255, 255, 255) # Weißer Hintergrund (RGB)
# Neues Bild erstellen
maxh = Medium.maxsize_h
maxw = Medium.maxsize_w
image = Image.new("RGB", (maxw, maxh), background_color)
d = ImageDraw.Draw(image)
Zeilen = Medium.maxsize_h // 6
zposy = 2
if self.binary_contract.client:
text = self.binary_contract.contact_company_name
_logger.info('Create Stamps for Contract : B_'+str(self.id)+' - Contract: C_'+str(self.binary_contract.id)+'- Medium : M_'+str(Medium.id)+' - Text1: '+str(text))
if len(text) > 35:
# Trenne den Text an einem Leerzeichen nahe bei Position 40
idx = text.rfind(' ', 0, 35)
if idx == -1:
idx = 35
text2 = text[:idx]
bcount = len(text2) if text2 else 1
fonts = maxw // (bcount ) *1.8
if fonts > maxh:
fonts = maxh - 10
if fonts <=0 :
fonts = 10
# font = ImageFont.truetype("addons/web/static/fonts/sign/Zeyada-Regular.ttf", fonts)
font = ImageFont.truetype("addons/web/static/fonts/google/Open_Sans/Open_Sans-SemiBold.ttf", fonts)
d.text((maxw // 2, 1*Zeilen), text2, fill="black",align="center", anchor="ms", font=font) # Beispiel: Ein Rechteck
text = text[idx:]
zposy = 2
if len(text) > 35:
# Trenne den Text an einem Leerzeichen nahe bei Position 40
idx = text.rfind(' ', 0, 35)
if idx == -1:
idx = 35
text2 = text[:idx]
# font = ImageFont.truetype("addons/web/static/fonts/sign/Zeyada-Regular.ttf", fonts)
font = ImageFont.truetype("addons/web/static/fonts/google/Open_Sans/Open_Sans-SemiBold.ttf", fonts)
d.text((maxw // 2, zposy*Zeilen), text2, fill="black",align="center", anchor="ms", font=font) # Beispiel: Ein Rechteck
text = text[idx:]
zposy = 3
else:
bcount = len(text) if text else 1
fonts = maxw // (bcount ) *1.8
if fonts > maxh:
fonts = maxh - 10
if fonts <=0 :
fonts = 10
# font = ImageFont.truetype("addons/web/static/fonts/sign/Zeyada-Regular.ttf", fonts)
font = ImageFont.truetype("addons/web/static/fonts/google/Open_Sans/Open_Sans-SemiBold.ttf", fonts)
d.text((maxw // 2, zposy*Zeilen), text, fill="black",align="center", anchor="ms", font=font) # Beispiel: Ein Rechteck
text = self.binary_contract.contact_street
bcount = len(text) if text else 1
Zeilen = maxh // 6
fonts = maxw // (bcount ) *1.2
if fonts > maxh:
fonts = maxh - 10
if fonts <=0 :
fonts = 10
# font = ImageFont.truetype("addons/web/static/fonts/sign/Zeyada-Regular.ttf", fonts)
font = ImageFont.truetype("addons/web/static/fonts/google/Open_Sans/Open_Sans-Regular.ttf", fonts)
d.text((maxw // 2, 4*Zeilen), text, fill="black",align="center", anchor="ms", font=font) # Beispiel: Ein Rechteck
text = self.binary_contract.contact_zip+' '+self.binary_contract.contact_city
bcount = len(text) if text else 1
Zeilen = maxh // 6
fonts = maxw // (bcount ) *1.2
if fonts > maxh:
fonts = maxh - 10
if fonts <=0 :
fonts = 10
# font = ImageFont.truetype("addons/web/static/fonts/sign/Zeyada-Regular.ttf", fonts)
font = ImageFont.truetype("addons/web/static/fonts/google/Open_Sans/Open_Sans-Regular.ttf", fonts)
d.text((maxw // 2, 5*Zeilen), text, fill="black",align="center", anchor="ms", font=font) # Beispiel: Ein Rechteck
# Bild speichern
filename = Medium.filepartname+self.binary_contract.contract_auto_id+".png"
_logger.info('Create Stamps for Contract : B_'+str(self.id)+' - Contract: C_'+str(self.binary_contract.id)+'- Feld : F_'+str(Medium.id)+' - Filename: '+str(filename))
image.save(filename)
_logger.info("Stamp file created "+str(filename))
_logger.info("Copy file to Binaries B_"+str(self))
try:
self.binary_binary = base64.b64encode(open(filename, "rb").read())
self.binary_filename = filename
self.binary_type = 'image/png'
self.binary_size = os.path.getsize(filename)
self.binary_issaved = False
self.binary_secured_ro = False
self.binary_used_ro = False
finally:
_logger.info("Binary inserted "+str(self))
cloudpath = self.binary_contract.cloudlink
if not cloudpath:
raise ValidationError(_("Der Vertrag hat keinen Cloudlink. Bitte überprüfen Sie den Vertrag."))
if not Medium.cloudlink:
raise ValidationError(_("Der Mediatyp hat keinen Cloudlink. Bitte überprüfen Sie den Mediatyp."))
cloudpath = cloudpath + '/' + str(Medium.cloudlink)
_logger.info("Copy file to Cloud Path : "+str(cloudpath))
try:
client = Client("https://cloud.logumedia.de/remote.php/dav/files/OdooDav/", auth=("odooClient@logumedia.de", "lm2020#OdooDav"))
try:
#client.mkdir(new_folder)
#_logger.info("Make Cloud Path : "+str(new_folder))
client.upload_file(filename, f"{cloudpath}/{filename}")
except Exception as e:
_logger.info("Make Cloud Path error : "+str(e))
finally:
self.binary_issaved = True
_logger.info("File copied to Cloud Path : "+str(cloudpath))
return True