Major file reorganization
@@ -17,7 +17,7 @@ from rich.table import Table
|
|||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
from rich.tree import Tree
|
from rich.tree import Tree
|
||||||
|
|
||||||
from utilities.render_worker import RenderStatus, string_to_status
|
from lib.render_workers.render_worker import RenderStatus, string_to_status
|
||||||
from start_server import start_server
|
from start_server import start_server
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import os
|
|||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from utilities.render_worker import RenderStatus, RenderWorkerFactory
|
from .render_workers.render_worker import RenderStatus, RenderWorkerFactory
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ from datetime import datetime
|
|||||||
import psutil
|
import psutil
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from lib.render_job import RenderJob
|
from .render_job import RenderJob
|
||||||
from utilities.render_worker import RenderStatus
|
from .render_workers.render_worker import RenderStatus
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import json
|
|||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from utilities.render_worker import *
|
from .render_worker import *
|
||||||
|
|
||||||
|
|
||||||
def aerender_path():
|
def aerender_path():
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from utilities.render_worker import *
|
from .render_worker import *
|
||||||
|
|
||||||
|
|
||||||
class BlenderRenderWorker(BaseRenderWorker):
|
class BlenderRenderWorker(BaseRenderWorker):
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
from utilities.render_worker import *
|
from .render_worker import *
|
||||||
|
|
||||||
|
|
||||||
class FFMPEGRenderWorker(BaseRenderWorker):
|
class FFMPEGRenderWorker(BaseRenderWorker):
|
||||||
@@ -218,9 +218,9 @@ class RenderWorkerFactory:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def supported_classes():
|
def supported_classes():
|
||||||
# to add support for any additional RenderWorker classes, import their classes and add to list here
|
# to add support for any additional RenderWorker classes, import their classes and add to list here
|
||||||
from utilities.blender_worker import BlenderRenderWorker
|
from .blender_worker import BlenderRenderWorker
|
||||||
from utilities.aerender_worker import AERenderWorker
|
from .aerender_worker import AERenderWorker
|
||||||
from utilities.ffmpeg_worker import FFMPEGRenderWorker
|
from .ffmpeg_worker import FFMPEGRenderWorker
|
||||||
classes = [BlenderRenderWorker, AERenderWorker, FFMPEGRenderWorker]
|
classes = [BlenderRenderWorker, AERenderWorker, FFMPEGRenderWorker]
|
||||||
return classes
|
return classes
|
||||||
|
|
||||||
0
lib/server/__init__.py
Normal file
@@ -4,10 +4,13 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import shutil
|
import shutil
|
||||||
import json2html
|
import socket
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
|
||||||
|
import json2html
|
||||||
import requests
|
import requests
|
||||||
import yaml
|
import yaml
|
||||||
from flask import Flask, request, render_template, send_file, after_this_request, Response, redirect, url_for, abort
|
from flask import Flask, request, render_template, send_file, after_this_request, Response, redirect, url_for, abort
|
||||||
@@ -15,11 +18,11 @@ from werkzeug.utils import secure_filename
|
|||||||
|
|
||||||
from lib.render_job import RenderJob
|
from lib.render_job import RenderJob
|
||||||
from lib.render_queue import RenderQueue, JobNotFoundError
|
from lib.render_queue import RenderQueue, JobNotFoundError
|
||||||
from lib.server_helper import post_job_to_server, generate_thumbnail_for_job
|
from lib.render_workers.render_worker import RenderWorkerFactory, string_to_status, RenderStatus
|
||||||
from utilities.render_worker import RenderWorkerFactory, string_to_status, RenderStatus
|
from lib.utilities.server_helper import post_job_to_server, generate_thumbnail_for_job
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
server = Flask(__name__, template_folder='../templates')
|
server = Flask(__name__, template_folder='templates', static_folder='static')
|
||||||
|
|
||||||
categories = [RenderStatus.RUNNING, RenderStatus.ERROR, RenderStatus.NOT_STARTED, RenderStatus.SCHEDULED,
|
categories = [RenderStatus.RUNNING, RenderStatus.ERROR, RenderStatus.NOT_STARTED, RenderStatus.SCHEDULED,
|
||||||
RenderStatus.COMPLETED, RenderStatus.CANCELLED]
|
RenderStatus.COMPLETED, RenderStatus.CANCELLED]
|
||||||
@@ -43,11 +46,12 @@ def sorted_jobs(all_jobs, sort_by_date=True):
|
|||||||
@server.route('/index')
|
@server.route('/index')
|
||||||
def index():
|
def index():
|
||||||
|
|
||||||
with open('utilities/presets.yaml') as f:
|
with open('config/presets.yaml') as f:
|
||||||
presets = yaml.load(f, Loader=yaml.FullLoader)
|
presets = yaml.load(f, Loader=yaml.FullLoader)
|
||||||
|
|
||||||
return render_template('index.html', all_jobs=sorted_jobs(RenderQueue.job_queue), hostname=RenderQueue.host_name,
|
return render_template('index.html', all_jobs=sorted_jobs(RenderQueue.job_queue),
|
||||||
renderer_info=renderer_info(), render_clients=RenderQueue.render_clients, preset_list=presets)
|
hostname=RenderQueue.host_name, renderer_info=renderer_info(),
|
||||||
|
render_clients=RenderQueue.render_clients, preset_list=presets)
|
||||||
|
|
||||||
|
|
||||||
@server.route('/ui/job/<job_id>/full_details')
|
@server.route('/ui/job/<job_id>/full_details')
|
||||||
@@ -133,11 +137,7 @@ def get_job_logs(job_id):
|
|||||||
|
|
||||||
@server.get('/api/job/<job_id>/file_list')
|
@server.get('/api/job/<job_id>/file_list')
|
||||||
def get_file_list(job_id):
|
def get_file_list(job_id):
|
||||||
found_job = RenderQueue.job_with_id(job_id)
|
return RenderQueue.job_with_id(job_id)
|
||||||
if found_job:
|
|
||||||
return '\n'.join(found_job.file_list())
|
|
||||||
else:
|
|
||||||
return f'Cannot find job with ID {job_id}', 400
|
|
||||||
|
|
||||||
|
|
||||||
@server.route('/api/job/<job_id>/download_all')
|
@server.route('/api/job/<job_id>/download_all')
|
||||||
@@ -439,3 +439,47 @@ def renderer_info():
|
|||||||
def upload_file_page():
|
def upload_file_page():
|
||||||
return render_template('upload.html', render_clients=RenderQueue.render_clients,
|
return render_template('upload.html', render_clients=RenderQueue.render_clients,
|
||||||
supported_renderers=RenderWorkerFactory.supported_renderers())
|
supported_renderers=RenderWorkerFactory.supported_renderers())
|
||||||
|
|
||||||
|
|
||||||
|
def start_server(background_thread=False):
|
||||||
|
def eval_loop(delay_sec=1):
|
||||||
|
while True:
|
||||||
|
RenderQueue.evaluate_queue()
|
||||||
|
time.sleep(delay_sec)
|
||||||
|
|
||||||
|
with open('config/config.yaml') as f:
|
||||||
|
config = yaml.load(f, Loader=yaml.FullLoader)
|
||||||
|
|
||||||
|
logging.basicConfig(format='%(asctime)s: %(levelname)s: %(module)s: %(message)s', datefmt='%d-%b-%y %H:%M:%S',
|
||||||
|
level=config.get('server_log_level', 'INFO').upper())
|
||||||
|
|
||||||
|
server.config['UPLOAD_FOLDER'] = os.path.expanduser(config['upload_folder'])
|
||||||
|
server.config['THUMBS_FOLDER'] = os.path.join(os.path.expanduser(config['upload_folder']), 'thumbs')
|
||||||
|
server.config['MAX_CONTENT_PATH'] = config['max_content_path']
|
||||||
|
|
||||||
|
# Get hostname and render clients
|
||||||
|
RenderQueue.host_name = socket.gethostname()
|
||||||
|
server.config['HOSTNAME'] = RenderQueue.host_name
|
||||||
|
if not RenderQueue.render_clients:
|
||||||
|
RenderQueue.render_clients = [RenderQueue.host_name]
|
||||||
|
|
||||||
|
# disable most Flask logging
|
||||||
|
flask_log = logging.getLogger('werkzeug')
|
||||||
|
flask_log.setLevel(config.get('flask_log_level', 'ERROR').upper())
|
||||||
|
|
||||||
|
# Set up the RenderQueue object
|
||||||
|
RenderQueue.load_state()
|
||||||
|
|
||||||
|
thread = threading.Thread(target=eval_loop, kwargs={'delay_sec': config.get('queue_eval_seconds', 1)}, daemon=True)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
logging.info(f"Starting Zordon Render Server - Hostname: '{RenderQueue.host_name}'")
|
||||||
|
|
||||||
|
if background_thread:
|
||||||
|
server_thread = threading.Thread(
|
||||||
|
target=lambda: server.run(host='0.0.0.0', port=RenderQueue.port, debug=False, use_reloader=False))
|
||||||
|
server_thread.start()
|
||||||
|
server_thread.join()
|
||||||
|
else:
|
||||||
|
server.run(host='0.0.0.0', port=RenderQueue.port, debug=config.get('flask_debug_enable', False),
|
||||||
|
use_reloader=False, threaded=True)
|
||||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 995 B After Width: | Height: | Size: 995 B |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
@@ -50,19 +50,10 @@ columns: [
|
|||||||
</div>
|
</div>
|
||||||
`),
|
`),
|
||||||
sort: false
|
sort: false
|
||||||
}
|
},
|
||||||
|
{ id: 'owner', name: 'Owner' }
|
||||||
],
|
],
|
||||||
style: {
|
autoWidth: true,
|
||||||
table: {
|
|
||||||
'white-space': 'nowrap'
|
|
||||||
},
|
|
||||||
th: {
|
|
||||||
'vertical-align': 'middle',
|
|
||||||
},
|
|
||||||
td: {
|
|
||||||
'vertical-align': 'middle',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
server: {
|
server: {
|
||||||
url: '/api/jobs',
|
url: '/api/jobs',
|
||||||
then: results => results,
|
then: results => results,
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (startingStatus == 'running'){
|
if (startingStatus == 'running'){
|
||||||
var renderingTimer = setInterval(update_job, 2000);
|
var renderingTimer = setInterval(update_job, 1000);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
0
lib/utilities/__init__.py
Normal file
@@ -1,4 +1,5 @@
|
|||||||
import ffmpeg
|
import subprocess
|
||||||
|
import ffmpeg # todo: remove all references to ffmpeg library and instead use direct subprocesses
|
||||||
|
|
||||||
|
|
||||||
def file_info(path):
|
def file_info(path):
|
||||||
@@ -9,6 +10,11 @@ def file_info(path):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def image_sequence_to_video(source_glob_pattern, output_path, framerate="24", encoder="libx264"):
|
||||||
|
subprocess.run(['ffmpeg', "-y", "-framerate", framerate, "-pattern_type", "glob", "-i", f"{source_glob_pattern}",
|
||||||
|
"-c:v", encoder, output_path])
|
||||||
|
|
||||||
|
|
||||||
def save_first_frame(source_path, dest_path, max_width=1280, run_async=False):
|
def save_first_frame(source_path, dest_path, max_width=1280, run_async=False):
|
||||||
stream = ffmpeg.input(source_path)
|
stream = ffmpeg.input(source_path)
|
||||||
stream = ffmpeg.output(stream, dest_path, **{'vf': f'format=yuv420p,scale={max_width}:trunc(ow/a/2)*2',
|
stream = ffmpeg.output(stream, dest_path, **{'vf': f'format=yuv420p,scale={max_width}:trunc(ow/a/2)*2',
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
import subprocess
|
|
||||||
import requests
|
|
||||||
import os
|
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
from utilities.render_worker import RenderStatus
|
|
||||||
from utilities.ffmpeg_presets import generate_thumbnail, save_first_frame
|
import requests
|
||||||
|
|
||||||
|
from .ffmpeg_helper import generate_thumbnail, save_first_frame
|
||||||
|
from lib.render_workers.render_worker import RenderStatus
|
||||||
|
|
||||||
|
|
||||||
def post_job_to_server(input_path, job_list, client, server_port=8080):
|
def post_job_to_server(input_path, job_list, client, server_port=8080):
|
||||||
@@ -6,17 +6,16 @@ import pathlib
|
|||||||
import socket
|
import socket
|
||||||
from tkinter import *
|
from tkinter import *
|
||||||
from tkinter import filedialog, messagebox
|
from tkinter import filedialog, messagebox
|
||||||
from tkinter.ttk import Frame, Label, Entry, Combobox
|
|
||||||
from tkinter.simpledialog import askstring
|
from tkinter.simpledialog import askstring
|
||||||
|
from tkinter.ttk import Frame, Label, Entry, Combobox
|
||||||
|
|
||||||
import psutil
|
import psutil
|
||||||
import requests
|
import requests
|
||||||
|
from lib.utilities.server_helper import post_job_to_server
|
||||||
from lib.job_server import post_job_to_server
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
prefs_name = '.scheduler_prefs'
|
prefs_name = 'config/.scheduler_prefs'
|
||||||
label_width = 7
|
label_width = 7
|
||||||
header_padding = 6
|
header_padding = 6
|
||||||
server_setup_timeout = 5
|
server_setup_timeout = 5
|
||||||
@@ -244,7 +243,7 @@ class ScheduleJob(Frame):
|
|||||||
|
|
||||||
# get file stats
|
# get file stats
|
||||||
if self.chosen_file:
|
if self.chosen_file:
|
||||||
from utilities.blender_worker import get_scene_info
|
from lib.render_workers.blender_worker import get_scene_info
|
||||||
scene_data = get_scene_info(self.chosen_file)
|
scene_data = get_scene_info(self.chosen_file)
|
||||||
|
|
||||||
# blender settings
|
# blender settings
|
||||||
@@ -317,7 +316,7 @@ class ScheduleJob(Frame):
|
|||||||
temp_files = []
|
temp_files = []
|
||||||
if renderer == 'blender':
|
if renderer == 'blender':
|
||||||
if self.blender_pack_textures.get():
|
if self.blender_pack_textures.get():
|
||||||
from utilities.blender_worker import pack_blender_files
|
from lib.render_workers.blender_worker import pack_blender_files
|
||||||
new_path = pack_blender_files(path=input_path)
|
new_path = pack_blender_files(path=input_path)
|
||||||
if new_path:
|
if new_path:
|
||||||
logger.info(f'Packed Blender file successfully: {new_path}')
|
logger.info(f'Packed Blender file successfully: {new_path}')
|
||||||
|
|||||||
@@ -1,57 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import logging
|
from lib.server.job_server import start_server
|
||||||
import os
|
|
||||||
import socket
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
import yaml
|
|
||||||
from lib.job_server import server
|
|
||||||
from lib.render_queue import RenderQueue
|
|
||||||
|
|
||||||
|
|
||||||
def start_server(background_thread=False):
|
|
||||||
def eval_loop(delay_sec=1):
|
|
||||||
while True:
|
|
||||||
RenderQueue.evaluate_queue()
|
|
||||||
time.sleep(delay_sec)
|
|
||||||
|
|
||||||
with open('config.yaml') as f:
|
|
||||||
config = yaml.load(f, Loader=yaml.FullLoader)
|
|
||||||
|
|
||||||
logging.basicConfig(format='%(asctime)s: %(levelname)s: %(module)s: %(message)s', datefmt='%d-%b-%y %H:%M:%S',
|
|
||||||
level=config.get('server_log_level', 'INFO').upper())
|
|
||||||
|
|
||||||
server.config['UPLOAD_FOLDER'] = os.path.expanduser(config['upload_folder'])
|
|
||||||
server.config['THUMBS_FOLDER'] = os.path.join(os.path.expanduser(config['upload_folder']), 'thumbs')
|
|
||||||
server.config['MAX_CONTENT_PATH'] = config['max_content_path']
|
|
||||||
|
|
||||||
# Get hostname and render clients
|
|
||||||
RenderQueue.host_name = socket.gethostname()
|
|
||||||
server.config['HOSTNAME'] = RenderQueue.host_name
|
|
||||||
if not RenderQueue.render_clients:
|
|
||||||
RenderQueue.render_clients = [RenderQueue.host_name]
|
|
||||||
|
|
||||||
# disable most Flask logging
|
|
||||||
flask_log = logging.getLogger('werkzeug')
|
|
||||||
flask_log.setLevel(config.get('flask_log_level', 'ERROR').upper())
|
|
||||||
|
|
||||||
# Set up the RenderQueue object
|
|
||||||
RenderQueue.load_state()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=eval_loop, kwargs={'delay_sec': config.get('queue_eval_seconds', 1)}, daemon=True)
|
|
||||||
thread.start()
|
|
||||||
|
|
||||||
logging.info(f"Starting Zordon Render Server - Hostname: '{RenderQueue.host_name}'")
|
|
||||||
|
|
||||||
if background_thread:
|
|
||||||
server_thread = threading.Thread(
|
|
||||||
target=lambda: server.run(host='0.0.0.0', port=RenderQueue.port, debug=False, use_reloader=False))
|
|
||||||
server_thread.start()
|
|
||||||
server_thread.join()
|
|
||||||
else:
|
|
||||||
server.run(host='0.0.0.0', port=RenderQueue.port, debug=config.get('flask_debug_enable', False),
|
|
||||||
use_reloader=True, threaded=True)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
start_server()
|
start_server()
|
||||||
|
|||||||