diff --git a/lib/render_queue.py b/lib/render_queue.py index 5be9dd6..3de9986 100755 --- a/lib/render_queue.py +++ b/lib/render_queue.py @@ -7,8 +7,7 @@ import requests from sqlalchemy import create_engine, Column, String, Integer from sqlalchemy.orm import sessionmaker -from .render_workers.base_worker import RenderStatus -from .scheduled_job import ScheduledJob, Base +from .render_workers.base_worker import RenderStatus, BaseRenderWorker, Base logger = logging.getLogger() @@ -45,7 +44,6 @@ class RenderQueue: Base.metadata.create_all(engine) Session = sessionmaker(bind=engine) session = Session() - ScheduledJob.register_user_events() job_queue = [] maximum_renderer_instances = {'blender': 1, 'aerender': 1, 'ffmpeg': 4} hostname = None @@ -62,7 +60,7 @@ class RenderQueue: def add_to_render_queue(cls, render_job, force_start=False, client=None): if not client or render_job.client == cls.hostname: - logger.debug('Adding priority {} job to render queue: {}'.format(render_job.priority, render_job.worker_object)) + logger.debug('Adding priority {} job to render queue: {}'.format(render_job.priority, render_job)) render_job.client = cls.hostname cls.job_queue.append(render_job) if force_start: @@ -89,7 +87,7 @@ class RenderQueue: @classmethod def jobs_with_status(cls, status, priority_sorted=False): - found_jobs = [x for x in cls.all_jobs() if x.render_status() == status] + found_jobs = [x for x in cls.all_jobs() if x.status == status] if priority_sorted: found_jobs = sorted(found_jobs, key=lambda a: a.priority, reverse=False) return found_jobs @@ -103,7 +101,7 @@ class RenderQueue: @classmethod def clear_history(cls): - to_remove = [x for x in cls.all_jobs() if x.render_status() in [RenderStatus.CANCELLED, + to_remove = [x for x in cls.all_jobs() if x.status in [RenderStatus.CANCELLED, RenderStatus.COMPLETED, RenderStatus.ERROR]] for job_to_remove in to_remove: cls.delete_job(job_to_remove) @@ -141,15 +139,14 @@ class RenderQueue: @classmethod def start_job(cls, job): - logger.info('Starting {}render: {} - Priority {}'.format('scheduled ' if job.scheduled_start else '', job.name, - job.priority)) + logger.info(f'Starting render: {job.name} - Priority {job.priority}') job.start() @classmethod def cancel_job(cls, job): logger.info(f'Cancelling job ID: {job.id}') job.stop() - return job.render_status() == RenderStatus.CANCELLED + return job.status == RenderStatus.CANCELLED @classmethod def delete_job(cls, job): diff --git a/lib/render_workers/aerender_worker.py b/lib/render_workers/aerender_worker.py index 5a47a8e..48ba2ae 100644 --- a/lib/render_workers/aerender_worker.py +++ b/lib/render_workers/aerender_worker.py @@ -22,9 +22,10 @@ class AERenderWorker(BaseRenderWorker): supported_extensions = ['.aep'] engine = AERender - def __init__(self, input_path, output_path, args=None): - super(AERenderWorker, self).__init__(input_path=input_path, output_path=output_path, ignore_extensions=False, - args=args) + def __init__(self, input_path, output_path, priority=2, args=None, owner=None, + client=None, name=None): + super(AERenderWorker, self).__init__(input_path=input_path, output_path=output_path, args=args, + client=client, priority=priority, owner=owner, name=name) self.comp = args.get('comp', None) self.render_settings = args.get('render_settings', None) diff --git a/lib/render_workers/base_worker.py b/lib/render_workers/base_worker.py index bd9f34f..74bde72 100644 --- a/lib/render_workers/base_worker.py +++ b/lib/render_workers/base_worker.py @@ -8,9 +8,13 @@ import json from datetime import datetime from enum import Enum +from sqlalchemy import Column, Integer, String, DateTime, JSON, event +from sqlalchemy.ext.declarative import declarative_base + import psutil logger = logging.getLogger() +Base = declarative_base() class RenderStatus(Enum): @@ -30,11 +34,25 @@ def string_to_status(string): return RenderStatus.ERROR -class BaseRenderWorker(object): +class BaseRenderWorker(Base): + __tablename__ = 'render_workers' + + id = Column(Integer, primary_key=True) + input_path = Column(String) + output_path = Column(String) + date_created = Column(DateTime) + renderer = Column(String) + renderer_version = Column(String) + priority = Column(Integer) + owner = Column(String) + client = Column(String) + name = Column(String) + file_hash = Column(String) engine = None - def __init__(self, input_path, output_path, args=None, ignore_extensions=True): + def __init__(self, input_path, output_path, priority=2, args=None, ignore_extensions=True, owner=None, client=None, + name=None): if not ignore_extensions: if not any(ext in input_path for ext in self.engine.supported_extensions): @@ -49,7 +67,12 @@ class BaseRenderWorker(object): self.output_path = output_path self.args = args or {} self.date_created = datetime.now() + self.renderer = self.engine.name() self.renderer_version = self.engine.version() + self.priority = priority + self.owner = owner + self.client = client + self.name = name # Frame Ranges self.total_frames = 0 diff --git a/lib/render_workers/blender_worker.py b/lib/render_workers/blender_worker.py index c67e95f..d079862 100644 --- a/lib/render_workers/blender_worker.py +++ b/lib/render_workers/blender_worker.py @@ -13,9 +13,10 @@ class BlenderRenderWorker(BaseRenderWorker): engine = Blender - def __init__(self, input_path, output_path, args=None): - super(BlenderRenderWorker, self).__init__(input_path=input_path, output_path=output_path, - ignore_extensions=False, args=args) + def __init__(self, input_path, output_path, priority=2, args=None, owner=None, + client=None, name=None): + super(BlenderRenderWorker, self).__init__(input_path=input_path, output_path=output_path, args=args, + client=client, priority=priority, owner=owner, name=name) # Args self.blender_engine = self.args.get('engine', 'BLENDER_EEVEE').upper() diff --git a/lib/render_workers/ffmpeg_worker.py b/lib/render_workers/ffmpeg_worker.py index 4156313..0f44401 100644 --- a/lib/render_workers/ffmpeg_worker.py +++ b/lib/render_workers/ffmpeg_worker.py @@ -8,9 +8,10 @@ class FFMPEGRenderWorker(BaseRenderWorker): engine = FFMPEG - def __init__(self, input_path, output_path, args=None): - super(FFMPEGRenderWorker, self).__init__(input_path=input_path, output_path=output_path, ignore_extensions=True, - args=args) + def __init__(self, input_path, output_path, priority=2, args=None, owner=None, + client=None, name=None): + super(FFMPEGRenderWorker, self).__init__(input_path=input_path, output_path=output_path, args=args, + client=client, priority=priority, owner=owner, name=name) stream_info = subprocess.check_output([self.engine.renderer_path(), "-i", # https://stackoverflow.com/a/61604105 input_path, "-map", "0:v:0", "-c", "copy", "-f", "null", "-y", diff --git a/lib/render_workers/worker_factory.py b/lib/render_workers/worker_factory.py index f2cc3eb..4d40220 100644 --- a/lib/render_workers/worker_factory.py +++ b/lib/render_workers/worker_factory.py @@ -10,9 +10,11 @@ class RenderWorkerFactory: return classes @staticmethod - def create_worker(renderer, input_path, output_path, args=None): + def create_worker(renderer, input_path, output_path, priority=2, args=None, owner=None, + client=None, name=None): worker_class = RenderWorkerFactory.class_for_name(renderer) - return worker_class(input_path=input_path, output_path=output_path, args=args) + return worker_class(input_path=input_path, output_path=output_path, args=args, priority=priority, owner=owner, + client=client, name=name) @staticmethod def supported_renderers(): diff --git a/lib/server/job_server.py b/lib/server/job_server.py index 82a8850..3408d58 100755 --- a/lib/server/job_server.py +++ b/lib/server/job_server.py @@ -34,7 +34,7 @@ def sorted_jobs(all_jobs, sort_by_date=True): sorted_job_list = [] if all_jobs: for status_category in categories: - found_jobs = [x for x in all_jobs if x.render_status() == status_category.value] + found_jobs = [x for x in all_jobs if x.status == status_category.value] if found_jobs: sorted_found_jobs = sorted(found_jobs, key=lambda d: d.date_created, reverse=True) sorted_job_list.extend(sorted_found_jobs) @@ -61,11 +61,11 @@ def job_detail(job_id): table_html = json2html.json2html.convert(json=found_job.json(), table_attributes='class="table is-narrow is-striped is-fullwidth"') media_url = None - if found_job.file_list() and found_job.render_status() == RenderStatus.COMPLETED: + if found_job.file_list() and found_job.status == RenderStatus.COMPLETED: media_basename = os.path.basename(found_job.file_list()[0]) media_url = f"/api/job/{job_id}/file/{media_basename}" return render_template('details.html', detail_table=table_html, media_url=media_url, - hostname=RenderQueue.hostname, job_status=found_job.render_status().value.title(), + hostname=RenderQueue.hostname, job_status=found_job.status.value.title(), job=found_job, renderer_info=renderer_info()) @@ -79,20 +79,20 @@ def job_thumbnail(job_id): thumb_image_path = os.path.join(server.config['THUMBS_FOLDER'], found_job.id + '.jpg') if not os.path.exists(thumb_video_path) and not os.path.exists(thumb_video_path + '_IN-PROGRESS') and \ - found_job.render_status() not in [RenderStatus.CANCELLED, RenderStatus.ERROR]: + found_job.status not in [RenderStatus.CANCELLED, RenderStatus.ERROR]: generate_thumbnail_for_job(found_job, thumb_video_path, thumb_image_path, max_width=240) if os.path.exists(thumb_video_path) and not os.path.exists(thumb_video_path + '_IN-PROGRESS'): return send_file(thumb_video_path, mimetype="video/mp4") elif os.path.exists(thumb_image_path): return send_file(thumb_image_path, mimetype='image/jpeg') - elif found_job.render_status() == RenderStatus.RUNNING: + elif found_job.status == RenderStatus.RUNNING: return send_file('static/images/gears.png', mimetype="image/png") - elif found_job.render_status() == RenderStatus.CANCELLED: + elif found_job.status == RenderStatus.CANCELLED: return send_file('static/images/cancelled.png', mimetype="image/png") - elif found_job.render_status() == RenderStatus.SCHEDULED: + elif found_job.status == RenderStatus.SCHEDULED: return send_file('static/images/scheduled.png', mimetype="image/png") - elif found_job.render_status() == RenderStatus.NOT_STARTED: + elif found_job.status == RenderStatus.NOT_STARTED: return send_file('static/images/not_started.png', mimetype="image/png") return send_file('static/images/error.png', mimetype="image/png") @@ -329,8 +329,13 @@ def add_job(job_params, remove_job_dir_on_failure=False): if client == RenderQueue.hostname: logger.info(f"Creating job locally - {name if name else input_path}") try: - render_job = ScheduledJob(renderer, input_path, output_path, args, priority, job_owner, client, - notify=False, custom_id=custom_id, name=name) + render_job = RenderWorkerFactory.create_worker(renderer=renderer, input_path=input_path, + output_path=output_path, args=args) + render_job.client = client + render_job.owner = job_owner + render_job.name = name + render_job.priority = priority + RenderQueue.add_to_render_queue(render_job, force_start=force_start) return render_job.json() except Exception as e: diff --git a/lib/server/templates/details.html b/lib/server/templates/details.html index 5e19b6a..020daa6 100644 --- a/lib/server/templates/details.html +++ b/lib/server/templates/details.html @@ -17,7 +17,7 @@
Time Elapsed: {{job.worker_data()['time_elapsed']}}