2023-07-15 06:20:43 +00:00
|
|
|
import json
|
2023-08-10 10:17:45 +00:00
|
|
|
import os
|
2023-07-15 06:20:43 +00:00
|
|
|
import os.path
|
2023-07-16 06:25:32 +00:00
|
|
|
import threading
|
2023-07-17 06:29:36 +00:00
|
|
|
import time
|
2023-07-15 06:20:43 +00:00
|
|
|
|
|
|
|
from modules.paths import data_path, script_path
|
|
|
|
|
2023-08-10 10:17:45 +00:00
|
|
|
cache_filename = os.environ.get('SD_WEBUI_CACHE_FILE', os.path.join(data_path, "cache.json"))
|
2023-07-15 06:20:43 +00:00
|
|
|
cache_data = None
|
2023-07-16 06:25:32 +00:00
|
|
|
cache_lock = threading.Lock()
|
2023-07-15 06:20:43 +00:00
|
|
|
|
2023-07-17 06:29:36 +00:00
|
|
|
dump_cache_after = None
|
|
|
|
dump_cache_thread = None
|
|
|
|
|
2023-07-15 06:20:43 +00:00
|
|
|
|
|
|
|
def dump_cache():
|
|
|
|
"""
|
2023-07-17 06:29:36 +00:00
|
|
|
Marks cache for writing to disk. 5 seconds after no one else flags the cache for writing, it is written.
|
2023-07-15 06:20:43 +00:00
|
|
|
"""
|
|
|
|
|
2023-07-17 06:29:36 +00:00
|
|
|
global dump_cache_after
|
|
|
|
global dump_cache_thread
|
|
|
|
|
|
|
|
def thread_func():
|
|
|
|
global dump_cache_after
|
|
|
|
global dump_cache_thread
|
|
|
|
|
|
|
|
while dump_cache_after is not None and time.time() < dump_cache_after:
|
|
|
|
time.sleep(1)
|
|
|
|
|
|
|
|
with cache_lock:
|
2023-08-22 14:45:38 +00:00
|
|
|
cache_filename_tmp = cache_filename + "-"
|
|
|
|
with open(cache_filename_tmp, "w", encoding="utf8") as file:
|
2023-11-26 12:55:50 +00:00
|
|
|
json.dump(cache_data, file, indent=4, ensure_ascii=False)
|
2023-07-17 06:29:36 +00:00
|
|
|
|
2023-08-22 14:45:38 +00:00
|
|
|
os.replace(cache_filename_tmp, cache_filename)
|
|
|
|
|
2023-07-17 06:29:36 +00:00
|
|
|
dump_cache_after = None
|
|
|
|
dump_cache_thread = None
|
|
|
|
|
2023-07-16 06:25:32 +00:00
|
|
|
with cache_lock:
|
2023-07-17 06:29:36 +00:00
|
|
|
dump_cache_after = time.time() + 5
|
|
|
|
if dump_cache_thread is None:
|
|
|
|
dump_cache_thread = threading.Thread(name='cache-writer', target=thread_func)
|
|
|
|
dump_cache_thread.start()
|
2023-07-15 06:20:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
def cache(subsection):
|
|
|
|
"""
|
|
|
|
Retrieves or initializes a cache for a specific subsection.
|
|
|
|
|
|
|
|
Parameters:
|
|
|
|
subsection (str): The subsection identifier for the cache.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
dict: The cache data for the specified subsection.
|
|
|
|
"""
|
|
|
|
|
|
|
|
global cache_data
|
|
|
|
|
|
|
|
if cache_data is None:
|
2023-07-16 06:25:32 +00:00
|
|
|
with cache_lock:
|
|
|
|
if cache_data is None:
|
2024-01-03 22:16:58 +00:00
|
|
|
try:
|
|
|
|
with open(cache_filename, "r", encoding="utf8") as file:
|
|
|
|
cache_data = json.load(file)
|
|
|
|
except FileNotFoundError:
|
|
|
|
cache_data = {}
|
|
|
|
except Exception:
|
|
|
|
os.replace(cache_filename, os.path.join(script_path, "tmp", "cache.json"))
|
|
|
|
print('[ERROR] issue occurred while trying to read cache.json, move current cache to tmp/cache.json and create new cache')
|
2023-07-15 06:20:43 +00:00
|
|
|
cache_data = {}
|
|
|
|
|
|
|
|
s = cache_data.get(subsection, {})
|
|
|
|
cache_data[subsection] = s
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
|
|
|
|
|
|
|
def cached_data_for_file(subsection, title, filename, func):
|
|
|
|
"""
|
|
|
|
Retrieves or generates data for a specific file, using a caching mechanism.
|
|
|
|
|
|
|
|
Parameters:
|
|
|
|
subsection (str): The subsection of the cache to use.
|
|
|
|
title (str): The title of the data entry in the subsection of the cache.
|
|
|
|
filename (str): The path to the file to be checked for modifications.
|
|
|
|
func (callable): A function that generates the data if it is not available in the cache.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
dict or None: The cached or generated data, or None if data generation fails.
|
|
|
|
|
|
|
|
The `cached_data_for_file` function implements a caching mechanism for data stored in files.
|
|
|
|
It checks if the data associated with the given `title` is present in the cache and compares the
|
|
|
|
modification time of the file with the cached modification time. If the file has been modified,
|
|
|
|
the cache is considered invalid and the data is regenerated using the provided `func`.
|
|
|
|
Otherwise, the cached data is returned.
|
|
|
|
|
|
|
|
If the data generation fails, None is returned to indicate the failure. Otherwise, the generated
|
|
|
|
or cached data is returned as a dictionary.
|
|
|
|
"""
|
|
|
|
|
|
|
|
existing_cache = cache(subsection)
|
|
|
|
ondisk_mtime = os.path.getmtime(filename)
|
|
|
|
|
|
|
|
entry = existing_cache.get(title)
|
|
|
|
if entry:
|
2023-07-16 06:49:22 +00:00
|
|
|
cached_mtime = entry.get("mtime", 0)
|
2023-07-15 06:20:43 +00:00
|
|
|
if ondisk_mtime > cached_mtime:
|
|
|
|
entry = None
|
|
|
|
|
2023-07-16 10:46:33 +00:00
|
|
|
if not entry or 'value' not in entry:
|
2023-07-16 06:49:22 +00:00
|
|
|
value = func()
|
|
|
|
if value is None:
|
2023-07-15 06:20:43 +00:00
|
|
|
return None
|
|
|
|
|
2023-07-16 06:49:22 +00:00
|
|
|
entry = {'mtime': ondisk_mtime, 'value': value}
|
2023-07-15 06:20:43 +00:00
|
|
|
existing_cache[title] = entry
|
|
|
|
|
|
|
|
dump_cache()
|
|
|
|
|
2023-07-16 06:49:22 +00:00
|
|
|
return entry['value']
|