Commit 03fbb356 authored by Matteo's avatar Matteo
Browse files

update

parent e0b63fef
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false
\ No newline at end of file
FILES_NAME=BERIO100
WORKING_PATH=../data
\ No newline at end of file
...@@ -10,4 +10,4 @@ RUN poetry install --no-cache --only main ...@@ -10,4 +10,4 @@ RUN poetry install --no-cache --only main
VOLUME [ "/data" ] VOLUME [ "/data" ]
CMD ["poetry", "run", "uvicorn", "src.server:app", "--host", "0.0.0.0", "--port", "80", "--log-config", "config/logger.yaml", "--root-path", "/packager"] CMD ["poetry", "run", "python", "src/packager/server.py"]
\ No newline at end of file \ No newline at end of file
FILES_NAME: BERIO100
WORKING_PATH: ../data
version: 1
formatters:
brief:
format: '%(message)s'
precise:
format: '[%(levelname)s %(name)s %(module)s:%(lineno)s - %(funcName)s() - %(asctime)s]\n\t %(message)s \n'
datefmt: '%d/%m/%Y %H:%M:%S'
handlers:
file:
class: logging.handlers.RotatingFileHandler
formatter: precise
filename: /var/log/packager/server.log
maxBytes: 10485760
backupCount: 3
console:
class: logging.StreamHandler
formatter: brief
loggers:
uvicorn:
level: INFO
handlers: [file, console]
propagate: no
\ No newline at end of file
...@@ -497,6 +497,43 @@ files = [ ...@@ -497,6 +497,43 @@ files = [
{file = "llvmlite-0.39.1.tar.gz", hash = "sha256:b43abd7c82e805261c425d50335be9a6c4f84264e34d6d6e475207300005d572"}, {file = "llvmlite-0.39.1.tar.gz", hash = "sha256:b43abd7c82e805261c425d50335be9a6c4f84264e34d6d6e475207300005d572"},
] ]
[[package]]
name = "markdown-it-py"
version = "2.2.0"
description = "Python port of markdown-it. Markdown parsing, done right!"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"},
{file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"},
]
[package.dependencies]
mdurl = ">=0.1,<1.0"
[package.extras]
benchmarking = ["psutil", "pytest", "pytest-benchmark"]
code-style = ["pre-commit (>=3.0,<4.0)"]
compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
linkify = ["linkify-it-py (>=1,<3)"]
plugins = ["mdit-py-plugins"]
profiling = ["gprof2dot"]
rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
[[package]]
name = "mdurl"
version = "0.1.2"
description = "Markdown URL utilities"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
]
[[package]] [[package]]
name = "moviepy" name = "moviepy"
version = "1.0.3" version = "1.0.3"
...@@ -527,14 +564,14 @@ test = ["coverage (<5.0)", "coveralls (>=1.1,<2.0)", "pytest (>=3.0.0,<4.0)", "p ...@@ -527,14 +564,14 @@ test = ["coverage (<5.0)", "coveralls (>=1.1,<2.0)", "pytest (>=3.0.0,<4.0)", "p
[[package]] [[package]]
name = "mpai-cae-arp" name = "mpai-cae-arp"
version = "0.3.2" version = "0.4.1"
description = "The MPAI CAE-ARP software API" description = "The MPAI CAE-ARP software API"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.10,<4.0" python-versions = ">=3.10,<4.0"
files = [ files = [
{file = "mpai_cae_arp-0.3.2-py3-none-any.whl", hash = "sha256:468db6aaae5a6d54d1460ede6e9a47686d12ab92680a51e14adf14dcba405aac"}, {file = "mpai_cae_arp-0.4.1-py3-none-any.whl", hash = "sha256:9601eb46c4561cae52347f8bdfbd8b575861fb6dad180b82eb0496b9dfa9f041"},
{file = "mpai_cae_arp-0.3.2.tar.gz", hash = "sha256:e447ee46f6c4ca18dd87e3b94dbf366d3f4852ad5530886fd577df9b8d32b2cd"}, {file = "mpai_cae_arp-0.4.1.tar.gz", hash = "sha256:11329c3abc906112a123130ebfb728151979af05d01ee9a1984af06b8c7be74c"},
] ]
[package.dependencies] [package.dependencies]
...@@ -916,6 +953,21 @@ typing-extensions = ">=4.2.0" ...@@ -916,6 +953,21 @@ typing-extensions = ">=4.2.0"
dotenv = ["python-dotenv (>=0.10.4)"] dotenv = ["python-dotenv (>=0.10.4)"]
email = ["email-validator (>=1.0.3)"] email = ["email-validator (>=1.0.3)"]
[[package]]
name = "pygments"
version = "2.15.1"
description = "Pygments is a syntax highlighting package written in Python."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"},
{file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"},
]
[package.extras]
plugins = ["importlib-metadata"]
[[package]] [[package]]
name = "pyyaml" name = "pyyaml"
version = "6.0" version = "6.0"
...@@ -988,6 +1040,25 @@ urllib3 = ">=1.21.1,<1.27" ...@@ -988,6 +1040,25 @@ urllib3 = ">=1.21.1,<1.27"
socks = ["PySocks (>=1.5.6,!=1.5.7)"] socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "rich"
version = "13.3.4"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
category = "main"
optional = false
python-versions = ">=3.7.0"
files = [
{file = "rich-13.3.4-py3-none-any.whl", hash = "sha256:22b74cae0278fd5086ff44144d3813be1cedc9115bdfabbfefd86400cb88b20a"},
{file = "rich-13.3.4.tar.gz", hash = "sha256:b5d573e13605423ec80bdd0cd5f8541f7844a0e71a13f74cf454ccb2f490708b"},
]
[package.dependencies]
markdown-it-py = ">=2.2.0,<3.0.0"
pygments = ">=2.13.0,<3.0.0"
[package.extras]
jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]] [[package]]
name = "scikit-learn" name = "scikit-learn"
version = "1.2.2" version = "1.2.2"
...@@ -1072,14 +1143,14 @@ test = ["asv", "gmpy2", "mpmath", "pytest", "pytest-cov", "pytest-xdist", "sciki ...@@ -1072,14 +1143,14 @@ test = ["asv", "gmpy2", "mpmath", "pytest", "pytest-cov", "pytest-xdist", "sciki
[[package]] [[package]]
name = "setuptools" name = "setuptools"
version = "67.7.1" version = "67.7.2"
description = "Easily download, build, install, upgrade, and uninstall Python packages" description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "setuptools-67.7.1-py3-none-any.whl", hash = "sha256:6f0839fbdb7e3cfef1fc38d7954f5c1c26bf4eebb155a55c9bf8faf997b9fb67"}, {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"},
{file = "setuptools-67.7.1.tar.gz", hash = "sha256:bb16732e8eb928922eabaa022f881ae2b7cdcfaf9993ef1f5e841a96d32b8e0c"}, {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"},
] ]
[package.extras] [package.extras]
...@@ -1220,4 +1291,4 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] ...@@ -1220,4 +1291,4 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.10" python-versions = "^3.10"
content-hash = "a551987715c8ce857de09712be1d99220a26f0cf9091ef50cdb97e8221a0e93d" content-hash = "452bb4a990359a117b785dc2aba650d39e7cad5cb79cb587e2309712a31342db"
[tool.poetry] [tool.poetry]
name = "packager" name = "packager"
version = "1.0.1" version = "1.1.0"
description = "MPAI CAE-ARP Packager." description = "MPAI CAE-ARP Packager."
authors = [ authors = [
"Matteo Spanio <dev2@audioinnova.com>", "Matteo Spanio <dev2@audioinnova.com>",
...@@ -14,7 +14,8 @@ packages = [{ include = "packager", from = "src" }] ...@@ -14,7 +14,8 @@ packages = [{ include = "packager", from = "src" }]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.10" python = "^3.10"
moviepy = "1.0.3" moviepy = "1.0.3"
mpai-cae-arp = "^0.3.2" mpai-cae-arp = "^0.4.1"
rich = "^13.3.4"
[tool.poetry.scripts] [tool.poetry.scripts]
packager = 'packager.cli:main' packager = 'packager.cli:main'
......
...@@ -27,20 +27,15 @@ from moviepy.editor import VideoFileClip, AudioFileClip ...@@ -27,20 +27,15 @@ from moviepy.editor import VideoFileClip, AudioFileClip
from mpai_cae_arp.files import File, FileType from mpai_cae_arp.files import File, FileType
from mpai_cae_arp.io import Color, Style, pprint from mpai_cae_arp.io import Color, Style, pprint
__author__ = "Nadir Dalla Pozza"
__copyright__ = "Copyright 2022, Audio Innova S.r.l." __copyright__ = "Copyright 2022, Audio Innova S.r.l."
__credits__ = ["Niccolò Pretto", "Nadir Dalla Pozza", "Sergio Canazza"] __credits__ = ["Niccolò Pretto", "Nadir Dalla Pozza", "Sergio Canazza"]
__license__ = "GPL v3.0"
__version__ = "1.0.1"
__maintainer__ = "Nadir Dalla Pozza"
__email__ = "nadir.dallapozza@unipd.it"
__status__ = "Production" __status__ = "Production"
def get_arguments() -> tuple[str, str]: def get_arguments() -> tuple[str, str]:
""" """
Method to obtain arguments from config/args.yaml file or command line. Method to obtain arguments from environment or command line.
Default config/args.yaml, ignored if a command line argument is passed. Default environment, ignored if a command line argument is passed.
:return: tuple consisting of two strings: :return: tuple consisting of two strings:
1) the working path; 1) the working path;
2) the name of the Preservation files, which is key element to retrieve necessary files. 2) the name of the Preservation files, which is key element to retrieve necessary files.
...@@ -70,21 +65,13 @@ def get_arguments() -> tuple[str, str]: ...@@ -70,21 +65,13 @@ def get_arguments() -> tuple[str, str]:
working_path = args.working_path working_path = args.working_path
files_name = args.files_name files_name = args.files_name
else: else:
# Read from configuration file arg_names = ['WORKING_PATH', 'FILES_NAME']
config = object config = {argument: os.getenv(argument) for argument in arg_names}
try: working_path, files_name = map(config.get, arg_names)
config = File('./config/args.yaml', FileType.YAML).get_content()
if 'WORKING_PATH' not in config: if any(value is None for value in config.values()):
pprint('WORKING_PATH key not found in config/args.yaml!', color=Color.RED) raise ValueError("Please, set the environment variables: WORKING_PATH, FILES_NAME")
quit(os.EX_CONFIG)
if 'FILES_NAME' not in config:
pprint('FILES_NAME key not found in config/args.yaml!', color=Color.RED)
quit(os.EX_CONFIG)
except FileNotFoundError:
pprint('config/args.yaml file not found!', color=Color.RED)
quit(os.EX_NOINPUT)
working_path = config['WORKING_PATH']
files_name = config['FILES_NAME']
return working_path, files_name return working_path, files_name
......
import subprocess import os
from fastapi import FastAPI, Response, status, Query import shutil
from typing import Annotated from concurrent import futures
import grpc
import json
from grpc import StatusCode
from rich.console import Console
from moviepy.editor import VideoFileClip, AudioFileClip
from mpai_cae_arp.files import File, FileType from mpai_cae_arp.files import File, FileType
from mpai_cae_arp.types.schema import Info from mpai_cae_arp.network import arp_pb2_grpc
from mpai_cae_arp.network.arp_pb2 import (
info = File("config/server.yaml", FileType.YAML).get_content() JobRequest,
app = FastAPI(**info) JobResponse,
Contact,
InfoResponse,
@app.get("/") License,
async def list_all_entrypoints() -> list[str]: )
""" from packager import lib
Get the list of available main routes.
""" PORT = os.getenv("PORT") or '50051'
return ["restore", "description", "docs"] info = File('config.yml', FileType.YAML).get_content()
@app.get("/description") def error_response(context, status, message):
async def get_server_info() -> Info: context.set_code(status)
""" context.set_details(message)
Retrieve all the informations about the software running on the server. return JobResponse(status="error", message=message)
"""
return info
class PackagerServicer(arp_pb2_grpc.AIMServicer):
@app.get("/package", status_code=200) def __init__(self, console: Console):
async def predict( self.console = console
response: Response,
files_name: Annotated[str, Query( def getInfo(self, request, context) -> InfoResponse:
description="Name of the audio/video file to be analyzed without the extension.", self.console.log('Received request for AIM info')
example="filename"
)], context.set_code(StatusCode.OK)
working_path: Annotated[str, Query( context.set_details('Success')
description="Path to the directory containing the preservation files.",
examples={ return InfoResponse(
"Absolute path": {"value": "/home/user/preservation"}, title=info['title'],
"Relative path": {"value": "preservation"} description=info['description'],
} version=info['version'],
)] = None contact=Contact(
): name=info['contact']['name'],
email=info['contact']['email'],
process = [ ),
"python", license=License(
"src/packager.py", name=info['license_info']['name'],
] url=info['license_info']['url'],
)
config_file = File("config/args.yaml", FileType.YAML) )
# Read configuration file
config = config_file.get_content() def work(self, request: JobRequest, context):
self.console.log('Received request for computation')
# Update configuration file with query parameters self.console.log(request)
config["FilesName"] = files_name
if working_path is not None: working_dir: str = request.working_dir
config["WorkingPath"] = working_path files_name: str = request.files_name
config_file.write_content(config)
temp_dir = os.path.join(working_dir, "temp", files_name)
cprocess = subprocess.run(process, capture_output=True) yield JobResponse(status="success", message="Job started")
if cprocess.returncode == 0: # By default, AccessCopyFiles directory is created in the output path...
return {"message": "Package created successfully"} acf_dir = 'AccessCopyFiles/' + files_name + '/'
acf_path = os.path.join(working_dir, acf_dir)
else: # ...however, if it already exists, the user can decide if it has to be overridden
response.status_code = status.HTTP_412_PRECONDITION_FAILED make_acf = lib.make_dir(acf_path)
return {"error": {
"returncode": cprocess.returncode, if make_acf:
"stdout": cprocess.stdout.decode("utf-8"), # Copy RestoredAudioFiles
"stderr": cprocess.stderr.decode("utf-8"), raf_path = os.path.join(temp_dir, 'RestoredAudioFiles')
}} if not os.path.exists(raf_path):
yield error_response(context, StatusCode.INVALID_ARGUMENT, "Restored Audio Files directory not found")
restored_audio_files = os.listdir(raf_path)
if len(restored_audio_files) == 1:
yield JobResponse(status="warning", message="Restored Audio Files directory is empty")
shutil.copytree(raf_path, os.path.join(acf_path, 'RestoredAudioFiles'))
console.log("Restored Audio Files copied")
yield JobResponse(status="success", message="Restored Audio Files copied")
# Copy Editing List
el_path = os.path.join(temp_dir, 'EditingList.json')
try:
shutil.copy2(el_path, acf_path)
except FileNotFoundError:
yield error_response(context, StatusCode.INVALID_ARGUMENT, "Editing List file not found")
console.log("Editing List copied")
yield JobResponse(status="success", message="Editing List copied")
# Create Irregularity Images archive
ii_path = os.path.join(temp_dir, 'IrregularityImages')
if not os.path.exists(ii_path):
yield error_response(context, StatusCode.INVALID_ARGUMENT, "Irregularity Images directory not found")
irregularity_images = os.listdir(ii_path)
if len(irregularity_images) == 1:
yield JobResponse(status="warning", message="Irregularity Images directory is empty")
shutil.make_archive(acf_path + 'IrregularityImages', 'zip', temp_dir, 'IrregularityImages')
console.log("Irregularity Images archive created")
yield JobResponse(status="success", message="Irregularity Images archive created")
# Copy Irregularity File
if_path = os.path.join(temp_dir, 'TapeIrregularityClassifier_IrregularityFileOutput2.json')
try:
shutil.copy2(if_path, os.path.join(acf_path, 'IrregularityFile.json'))
except FileNotFoundError:
yield error_response(context, StatusCode.INVALID_ARGUMENT, "Irregularity File file not found")
console.log("Irregularity File copied")
yield JobResponse(status="success", message="Irregularity File copied")
# Preservation Master Files
console.log('Creation of Preservation Master Files...')
yield JobResponse(status="success", message="Creation of Preservation Master Files...")
# By default, PreservationMasterFiles directory is created in the output path...
pmf_dir = 'PreservationMasterFiles/' + files_name + '/'
pmf_path = os.path.join(working_dir, pmf_dir)
# ...however, if it already exists, it is possible to skip its creation
make_pmf = lib.make_dir(pmf_path)
if make_pmf:
# Copy Preservation Audio File
audio_file = files_name + '.wav'
paf_path = os.path.join(working_dir, 'PreservationAudioFile/', audio_file)
try:
shutil.copy2(paf_path, pmf_path + 'PreservationAudioFile.wav')
except FileNotFoundError:
yield error_response(context, StatusCode.INVALID_ARGUMENT, "Preservation Audio File file not found")
console.log("Preservation Audio File copied")
yield JobResponse(status="success", message="Preservation Audio File copied")
# Create Preservation Audio-Visual File with substituted audio
video_file = files_name + '.mov'
pvf_path = os.path.join(working_dir, 'PreservationAudioVisualFile/', video_file)
try:
audio = AudioFileClip(paf_path)
video = VideoFileClip(pvf_path)
# Open Irregularity File to get offset
irregularity_file_json = open(
os.path.join(temp_dir, 'TapeIrregularityClassifier_IrregularityFileOutput2.json')
)
irregularity_file = json.load(irregularity_file_json)
offset = irregularity_file['Offset']/1000
if offset > 0:
audio = audio.subclip(t_start=offset)
else:
video = video.subclip(t_start=offset)
video = video.set_audio(audio)
video.write_videofile(pmf_path + 'PreservationAudioVisualFile.mov', bitrate='3000k', codec='mpeg4')
console.log("Preservation Audio-Visual File created")
yield JobResponse(status="success", message="Preservation Audio-Visual File created")
except OSError:
yield error_response(context, StatusCode.INVALID_ARGUMENT, "Preservation Audio-Visual File file not found")
# Create Irregularity Images archive (already checked)
shutil.make_archive(pmf_path + 'IrregularityImages', 'zip', temp_dir, 'IrregularityImages')
console.log("Irregularity Images archive created")
yield JobResponse(status="success", message="Irregularity Images archive created")
# Copy Irregularity File (already checked)
shutil.copy2(
os.path.join(temp_dir, 'TapeIrregularityClassifier_IrregularityFileOutput2.json'),
os.path.join(pmf_path, 'IrregularityFile.json')
)
console.log("Irregularity File copied")
yield JobResponse(status="success", message="Irregularity File copied")
# End Preservation Master Files
yield JobResponse(status="success", message="Success!")
else:
console.log("\nExit")
yield JobResponse(status="success", message="Exit")
def serve(console):
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
arp_pb2_grpc.add_AIMServicer_to_server(PackagerServicer(console), server)
server.add_insecure_port(f'[::]:{PORT}')
server.start()
server.wait_for_termination()
if __name__ == '__main__':
console = Console()
console.print(f'Server started at localhost:{PORT} :satellite:')
serve(console)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment