Add ability to get file list and download files from project output

This commit is contained in:
Brett Williams
2022-10-26 18:27:13 -07:00
parent 2da06ab166
commit eb9e719f47
8 changed files with 71 additions and 28 deletions

View File

@@ -21,7 +21,7 @@ class RenderJob:
self.date_created = datetime.now()
self.scheduled_start = None
self.renderer = render.renderer
self.name = os.path.basename(render.input) + '_' + self.date_created.isoformat()
self.name = os.path.basename(render.input_path) + '_' + self.date_created.isoformat()
self.archived = False
def render_status(self):

View File

@@ -92,7 +92,9 @@ class RenderQueue:
for job in saved_state.get('jobs', []):
# Identify renderer type and recreate Renderer object
job_render_object = RenderWorkerFactory.create_worker(job['renderer'], input_path=job['render']['input'], output_path=job['render']['output'])
job_render_object = RenderWorkerFactory.create_worker(job['renderer'],
input_path=job['render']['input_path'],
output_path=job['render']['output_path'])
# Load Renderer values
for key, val in job['render'].items():

View File

@@ -6,3 +6,5 @@ Flask==2.2.2
rich==12.6.0
ffmpeg-python
Werkzeug~=2.2.2
tkinterdnd2~=0.3.0
future~=0.18.2

View File

@@ -3,15 +3,17 @@
import json
import logging
import os
import pathlib
import shutil
import socket
import threading
import time
from datetime import datetime
from zipfile import ZipFile
import requests
import yaml
from flask import Flask, request, render_template
from flask import Flask, request, render_template, send_file, after_this_request
from werkzeug.utils import secure_filename
from lib.render_job import RenderJob
@@ -34,17 +36,53 @@ def filtered_jobs_json(status_val):
if jobs:
return jobs
else:
return {'error', f'Cannot find jobs with status {status_val}'}, 400
return f'Cannot find jobs with status {status_val}', 400
@app.get('/job_status/<job_id>')
def get_job_status(job_id):
found_job = RenderQueue.job_with_id(job_id)
if found_job:
logger.info("Founbd jobs")
return found_job.json()
else:
return {'error': f'Cannot find job with ID {job_id}'}, 400
return f'Cannot find job with ID {job_id}', 400
@app.get('/file_list/<job_id>')
def get_file_list(job_id):
found_job = RenderQueue.job_with_id(job_id)
if found_job:
job_dir = os.path.dirname(found_job.render.output_path)
return os.listdir(job_dir)
else:
return f'Cannot find job with ID {job_id}', 400
@app.route('/download_all/<job_id>')
def download_all(job_id):
zip_filename = None
@after_this_request
def clear_zip(response):
if zip_filename and os.path.exists(zip_filename):
os.remove(zip_filename)
return response
found_job = RenderQueue.job_with_id(job_id)
if found_job:
output_dir = os.path.dirname(found_job.render.output_path)
if os.path.exists(output_dir):
zip_filename = pathlib.Path(found_job.render.input_path).stem + '.zip'
with ZipFile(zip_filename, 'w') as zipObj:
for f in os.listdir(output_dir):
zipObj.write(filename=os.path.join(output_dir, f),
arcname=os.path.basename(f))
return send_file(zip_filename, mimetype="zip", as_attachment=True, )
else:
return f'Cannot find project files for job {job_id}', 500
else:
return f'Cannot find job with ID {job_id}', 400
@app.post('/register_client')
@@ -103,7 +141,6 @@ def snapshot():
@app.post('/add_job')
def add_job():
def remove_job_dir():
if job_dir and os.path.exists(job_dir):
logger.debug(f"Removing job dir: {job_dir}")
@@ -146,14 +183,17 @@ def add_job():
local_path = os.path.join(job_dir, secure_filename(uploaded_file.filename))
uploaded_file.save(local_path)
input_path = local_path
output_path = os.path.join(job_dir, os.path.basename(output_path))
output_dir = os.path.join(job_dir, 'output')
os.makedirs(output_dir, exist_ok=True)
output_path = os.path.join(output_dir, os.path.basename(output_path))
# local renders
if client == RenderQueue.host_name:
logger.info(f"Creating job locally - {input_path}")
try:
render_job = RenderWorkerFactory.create_worker(renderer, input_path, output_path, args)
except ValueError as e:
render_job.log_path = os.path.join(os.path.dirname(input_path), os.path.basename(input_path) + '.log')
except Exception as e:
err_msg = f"Error creating job: {str(e)}"
logger.exception(err_msg)
remove_job_dir()
@@ -268,7 +308,6 @@ def post_job_to_server(input_path, job_json, client, server_port=8080):
def start_server(background_thread=False):
def eval_loop(delay_sec=1):
while True:
RenderQueue.evaluate_queue()

View File

@@ -75,15 +75,15 @@ class AERenderWorker(BaseRenderWorker):
# }
job = {'template':
{
'src': 'file://' + self.input, 'composition': self.comp.replace('"', ''),
'src': 'file://' + self.input_path, 'composition': self.comp.replace('"', ''),
'settingsTemplate': self.render_settings.replace('"', ''),
'outputModule': self.omsettings.replace('"', ''), 'outputExt': 'mov'}
}
x = ['./nexrender-cli-macos', "'{}'".format(json.dumps(job))]
else:
logging.info('nexrender not found')
x = [aerender_path(), '-project', self.input, '-comp', self.comp, '-RStemplate', self.render_settings,
'-OMtemplate', self.omsettings, '-output', self.output]
x = [aerender_path(), '-project', self.input_path, '-comp', self.comp, '-RStemplate', self.render_settings,
'-OMtemplate', self.omsettings, '-output', self.output_path]
return x
def _parse_stdout(self, line):

View File

@@ -44,12 +44,12 @@ class BlenderRenderWorker(BaseRenderWorker):
def _generate_subprocess(self):
cmd = [self.renderer_path(), '-b', self.input]
cmd = [self.renderer_path(), '-b', self.input_path]
if self.camera:
cmd.extend(['--python-expr', f"import bpy;bpy.context.scene.camera = bpy.data.objects['{self.camera}'];"])
cmd.extend(['-E', self.engine, '-o', self.output, '-F', self.export_format])
cmd.extend(['-E', self.engine, '-o', self.output_path, '-F', self.export_format])
# all frames or single
cmd.extend(['-a'] if self.render_all_frames else ['-f', str(self.frame)])

View File

@@ -41,10 +41,10 @@ class FFMPEGRenderWorker(BaseRenderWorker):
def _generate_subprocess(self):
cmd = [self.renderer_path(), '-y', '-stats', '-i', self.input]
cmd = [self.renderer_path(), '-y', '-stats', '-i', self.input_path]
if self.args:
cmd.extend(self.args)
cmd.append(self.output)
cmd.append(self.output_path)
return cmd

View File

@@ -45,8 +45,8 @@ class BaseRenderWorker(object):
raise ValueError(err_meg)
# Essential Info
self.input = input_path
self.output = output_path
self.input_path = input_path
self.output_path = output_path
self.args = args or {}
self.date_created = datetime.now()
self.renderer_version = self.version()
@@ -96,9 +96,9 @@ class BaseRenderWorker(object):
def start(self):
if not os.path.exists(self.input):
if not os.path.exists(self.input_path):
self.status = RenderStatus.ERROR
msg = 'Cannot find input path: {}'.format(self.input)
msg = 'Cannot find input path: {}'.format(self.input_path)
logger.error(msg)
self.errors.append(msg)
return
@@ -111,7 +111,7 @@ class BaseRenderWorker(object):
return
self.status = RenderStatus.RUNNING
logger.info('Starting {0} {1} Render for {2}'.format(self.renderer, self.version(), self.input))
logger.info('Starting {0} {1} Render for {2}'.format(self.renderer, self.version(), self.input_path))
self.thread.start()
def run(self):
@@ -119,10 +119,10 @@ class BaseRenderWorker(object):
# Setup logging
try:
if not self.log_path:
log_dir = os.path.join(os.path.dirname(self.input), 'logs')
log_dir = os.path.join(os.path.dirname(self.input_path), 'logs')
if not os.path.exists(log_dir):
os.makedirs(log_dir)
self.log_path = os.path.join(log_dir, os.path.basename(self.input)) + '.log'
self.log_path = os.path.join(log_dir, os.path.basename(self.input_path)) + '.log'
logger.info('Logs saved in {}'.format(self.log_path))
except Exception as e:
logger.error("Error setting up logging: {}".format(e))
@@ -141,7 +141,7 @@ class BaseRenderWorker(object):
with open(self.log_path, "a") as f:
f.write("{3} - Starting {0} {1} Render for {2}\n".format(self.renderer, self.version(), self.input,
f.write("{3} - Starting {0} {1} Render for {2}\n".format(self.renderer, self.version(), self.input_path,
self.start_time.isoformat()))
f.write(f"Running command: {' '.join(subprocess_cmds)}\n")
for c in io.TextIOWrapper(self.process.stdout, encoding="utf-8"): # or another encoding
@@ -173,7 +173,7 @@ class BaseRenderWorker(object):
f.write(message)
if self.failed_attempts >= self.maximum_attempts and self.status is not RenderStatus.CANCELLED:
logger.error('{} Render of {} failed after {} attempts'.format(self.renderer, self.input, self.failed_attempts))
logger.error('{} Render of {} failed after {} attempts'.format(self.renderer, self.input_path, self.failed_attempts))
self.status = RenderStatus.ERROR
if not self.errors:
self.errors = [self.last_output]