mirror of
https://github.com/blw1138/Zordon.git
synced 2025-12-17 08:48:13 +00:00
Add ability to get file list and download files from project output
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -5,4 +5,6 @@ PyYAML~=6.0
|
||||
Flask==2.2.2
|
||||
rich==12.6.0
|
||||
ffmpeg-python
|
||||
Werkzeug~=2.2.2
|
||||
Werkzeug~=2.2.2
|
||||
tkinterdnd2~=0.3.0
|
||||
future~=0.18.2
|
||||
57
server.py
57
server.py
@@ -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()
|
||||
@@ -311,4 +350,4 @@ def start_server(background_thread=False):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
start_server()
|
||||
start_server()
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)])
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user