Files
blender-portable-repo/extensions/user_default/blenderkit/tasks_queue.py
T
2026-03-17 14:30:01 -06:00

162 lines
4.9 KiB
Python

# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
import logging
import queue
from typing import Tuple
import bpy
from bpy.app.handlers import persistent
from . import utils
bk_logger = logging.getLogger(__name__)
@persistent
def scene_load(context):
if bpy.app.background is True:
return
if not (bpy.app.timers.is_registered(queue_worker)):
bpy.app.timers.register(queue_worker)
def get_queue():
# we pick just a random one of blender types, to try to get a persistent queue
t = bpy.types.Scene
if not hasattr(t, "task_queue"):
t.task_queue = queue.Queue()
return t.task_queue
class task_object:
def __init__(
self,
command="",
arguments=(),
wait=0,
only_last=False,
fake_context=False,
fake_context_area="VIEW_3D",
):
self.command = command
self.arguments = arguments
self.wait = wait
self.only_last = only_last
self.fake_context = fake_context
self.fake_context_area = fake_context_area
def add_task(
task: Tuple,
wait=0,
only_last=False,
fake_context=False,
fake_context_area="VIEW_3D",
):
q = get_queue()
taskob = task_object(
task[0],
task[1],
wait=wait,
only_last=only_last,
fake_context=fake_context,
fake_context_area=fake_context_area,
)
q.put(taskob)
# @bpy.app.handlers.persistent
def queue_worker():
# utils.p('start queue worker timer')
# bk_logger.debug('timer queue worker')
time_step = 0.3
q = get_queue()
# save some performance by returning early
if q.empty():
return time_step
back_to_queue = [] # delayed events
stashed = {}
# first round we get all tasks that are supposed to be stashed and run only once (only_last option)
# stashing finds tasks with the property only_last and same command and executes only the last one.
while not q.empty():
task = q.get()
if task.only_last:
# this now makes the keys not only by task, but also two arguments.
# by now stashing is only used for ratings, where the first argument is url, second rating type.
# This enables fast rating of multiple assets while allowing larger delay for uploading of ratings.
# this avoids a duplicate request error on the server
stashed[f"{task.command}-{task.arguments[0]}-{task.arguments[1]}"] = task
else:
back_to_queue.append(task)
# return tasks to que except for stashed
for task in back_to_queue:
q.put(task)
# return stashed tasks to queue
for k in stashed.keys():
q.put(stashed[k])
# second round, execute or put back waiting tasks.
back_to_queue = []
while not q.empty():
# print('window manager', bpy.context.window_manager)
task = q.get()
if task.wait > 0:
task.wait -= time_step
back_to_queue.append(task)
else:
bk_logger.debug(
"task queue task:" + str(task.command) + str(task.arguments)
)
try:
if task.fake_context:
fc = utils.get_fake_context(
bpy.context, area_type=task.fake_context_area
)
if bpy.app.version < (4, 0, 0):
task.command(fc, *task.arguments)
else:
with bpy.context.temp_override(**fc):
task.command(*task.arguments)
else:
task.command(*task.arguments)
except Exception as e:
bk_logger.error(
"task queue failed task:"
+ str(task.command)
+ str(task.arguments)
+ str(e)
)
# bk_logger.exception('Got exception on main handler')
# raise
# print('queue while 2')
for task in back_to_queue:
q.put(task)
# utils.p('end queue worker timer')
return time_step
def register():
bpy.app.handlers.load_post.append(scene_load)
def unregister():
bpy.app.handlers.load_post.remove(scene_load)