Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Batch File upload/ Folder upload. Bulk Enhance #3540

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f1e9a5c
Batch File upload/ Folder upload. Bulk Enhance
ChrisColeTech Aug 18, 2024
ee4f442
remove unused imports
ChrisColeTech Aug 18, 2024
9f535e8
Change to resolve GitHub Advanced Security check
ChrisColeTech Aug 18, 2024
7a0b8ee
Rework Stop/Skip while bulk enhancing
ChrisColeTech Aug 18, 2024
2ab91c9
Update bulk_enhance_helpers.py
ChrisColeTech Aug 19, 2024
ec177f2
Update bulk_enhance_helpers.py
ChrisColeTech Aug 19, 2024
ad18a93
Update async_worker.py
ChrisColeTech Aug 20, 2024
e268cd5
Merge branch 'lllyasviel:main' into dev
ChrisColeTech Aug 20, 2024
83d0935
more code cleanup
ChrisColeTech Aug 20, 2024
3db125a
To resolve github CodeQL warning
ChrisColeTech Aug 20, 2024
4b90d70
Update async_worker.py
ChrisColeTech Aug 21, 2024
67edbf2
Update bulk_enhance_helpers.py
ChrisColeTech Aug 21, 2024
e68d7b5
automatic tkinter installation
ChrisColeTech Aug 22, 2024
1afc7b3
Update tkinter_installer.py
ChrisColeTech Aug 22, 2024
672baf0
Update launch.py
ChrisColeTech Aug 23, 2024
8921ac8
Remove code comments, added backend logic for perf monitor. renamed t…
ChrisColeTech Aug 24, 2024
7722d2c
html front end component for resource monitor
ChrisColeTech Aug 24, 2024
4848427
wired up perf monitor
ChrisColeTech Aug 24, 2024
85429b0
Update launch.py
ChrisColeTech Aug 24, 2024
4c32ebe
final touches on the perf monitor
ChrisColeTech Aug 26, 2024
5774787
perf monitor 2.0 with dragging
ChrisColeTech Aug 29, 2024
f0fa022
fix invisible element
ChrisColeTech Aug 30, 2024
c5a290a
remove unused code
ChrisColeTech Aug 30, 2024
c8d4d2d
Merge branch 'dev' of https://github.com/ChrisColeTech/Fooocus into dev
ChrisColeTech Aug 30, 2024
ce74b6b
speed up animation
ChrisColeTech Aug 30, 2024
eb934c9
Using websockets for resource monitor instead of rest api
ChrisColeTech Sep 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@ user_path_config-deprecated.txt
/.coverage*
/auth.json
.DS_Store
/.venv
23 changes: 23 additions & 0 deletions api/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import os
import importlib
from flask import Blueprint
from flask_restx import Namespace


def register_blueprints(app, api):
"""Register all Blueprints to the Flask app automatically."""
controllers_dir = os.path.dirname(__file__)
for filename in os.listdir(controllers_dir):
if filename.endswith('_controller.py') and filename != '__init__.py':
module_name = filename[:-3] # Remove ".py"
module = importlib.import_module(
f'.{module_name}', package=__package__)
for attribute_name in dir(module):
attribute = getattr(module, attribute_name)
if isinstance(attribute, Namespace):
api.add_namespace(attribute)

if isinstance(attribute, Blueprint):
app.register_blueprint(
attribute)
print(f"Registered blueprint: {attribute_name}")
105 changes: 105 additions & 0 deletions api/controllers/gpu_usage_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from flask_restx import Api, Resource, fields, Namespace
from flask import jsonify, request, make_response, Blueprint
import psutil
import GPUtil
import time

# Create a Blueprint for the gpu_usage controller
gpu_usage_bp = Blueprint('gpu_usage', __name__)
gpu_usage_api = Api(gpu_usage_bp, version='1.0', title='gpu_usage API',
description='API for managing gpu_usage')

# Define a namespace for gpu_usage
gpu_usage_ns = Namespace('gpu_usage', description='gpu usage operations')

# Define the model for a gpu
gpu_model = gpu_usage_ns.model('gpu_usage', {
'id': fields.Integer(required=True, description='The unique identifier of the gpu'),
'description': fields.String(required=True, description='Description of the gpu'),
'status': fields.String(description='Status of the gpu')
})

# Cache for system usage data
cache = {
'timestamp': 0,
'data': {
'cpu': 0,
'ram': 0,
'gpu': 0,
'vram': 0,
'hdd': 0,
'temp': 0
}
}
CACHE_DURATION = 1 # Cache duration in seconds


@gpu_usage_ns.route('/')
class GPUInfo(Resource):
def get_cache(self, current_time):
# Get CPU utilization
cpu_percent = psutil.cpu_percent(interval=0)

# Get Memory utilization
mem = psutil.virtual_memory()
mem_percent = mem.percent

# Get GPU utilization (considering only the first GPU)
gpus = GPUtil.getGPUs()
gpu_percent = gpus[0].load * 100 if gpus else 0

# Get VRAM usage (considering only the first GPU)
vram_usage = 0
if gpus:
used = gpus[0].memoryUsed
total = gpus[0].memoryTotal
vram_usage = (used / total) * 100

# Get HDD usage (assuming usage of the primary disk)
hdd = psutil.disk_usage('/')
hdd_percent = hdd.percent

# Get temperature (if available)
temperature = gpus[0].temperature

# Update the cache
cache['data'] = {
'cpu': cpu_percent,
'ram': mem_percent,
'gpu': gpu_percent,
'vram': vram_usage, # Convert bytes to MB
'hdd': hdd_percent,
'temp': temperature # Add temperature
}
cache['timestamp'] = current_time
return cache

def get(self):
if request.method == "OPTIONS": # CORS preflight
return _build_cors_preflight_response()

current_time = time.time()

# Check if the cache is still valid
if current_time - cache['timestamp'] < CACHE_DURATION:
return _corsify_actual_response(jsonify(cache['data']))

try:
self.get_cache(current_time)

return _corsify_actual_response(jsonify(cache['data']))
except Exception as e:
return _corsify_actual_response(jsonify({'error': str(e)}))

Check warning

Code scanning / CodeQL

Information exposure through an exception

[Stack trace information](1) flows to this location and may be exposed to an external user.


def _build_cors_preflight_response():
response = make_response()
response.headers.add("Access-Control-Allow-Origin", "*")
response.headers.add("Access-Control-Allow-Headers", "*")
response.headers.add("Access-Control-Allow-Methods", "*")
return response


def _corsify_actual_response(response):
response.headers.add("Access-Control-Allow-Origin", "*")
return response
110 changes: 110 additions & 0 deletions api/controllers/jobs_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import pickle
import os
from flask import Blueprint, request, jsonify, make_response
from flask_restx import Api, Resource, fields, Namespace

# Create a Blueprint for the jobs controller
jobs_bp = Blueprint('jobs', __name__)
jobs_api = Api(jobs_bp, version='1.0', title='Jobs API',
description='API for managing jobs')

# Define a namespace for jobs
jobs_ns = Namespace('jobs', description='Job operations')

# Define the model for a job
job_model = jobs_ns.model('Job', {
'id': fields.Integer(required=True, description='The unique identifier of the job'),
'description': fields.String(required=True, description='Description of the job'),
'status': fields.String(description='Status of the job')
})


# File to persist data
DATA_FILE = 'jobs.pkl'


def load_jobs():
if os.path.exists(DATA_FILE):
with open(DATA_FILE, 'rb') as file:
return pickle.load(file)
else:
# Create an empty file if it doesn't exist
with open(DATA_FILE, 'wb') as file:
pickle.dump({}, file)
return {}


def save_jobs(jobs):
with open(DATA_FILE, 'wb') as file:
pickle.dump(jobs, file)


# Load initial data
jobs_store = load_jobs()


@jobs_ns.route('/')
class JobList(Resource):
def get(self):
"""List all jobs"""
jobs_store = load_jobs()
return _corsify_actual_response(jsonify(list(jobs_store.values())))

@jobs_ns.expect(job_model)
def post(self):
"""Create a new job"""
if request.method == "OPTIONS": # CORS preflight
return _build_cors_preflight_response()

job = request.json
job_id = job['id']
if job_id in jobs_store:
return {'message': 'Job already exists'}, 400
jobs_store[job_id] = job
save_jobs(jobs_store) # Save to file
return _corsify_actual_response(jsonify(job))


@jobs_ns.route('/<int:job_id>')
class JobItem(Resource):
def get(self, job_id):
"""Get a job by ID"""
job = jobs_store.get(job_id)
if job is None:
return {'message': 'Job not found'}, 404
return _corsify_actual_response(jsonify(job))

@jobs_ns.expect(job_model)
def put(self, job_id):
"""Update a job by ID"""
if request.method == "OPTIONS": # CORS preflight
return _build_cors_preflight_response()
job = request.json
if job_id not in jobs_store:
return {'message': 'Job not found'}, 404
jobs_store[job_id] = job
save_jobs(jobs_store) # Save to file
return _corsify_actual_response(jsonify(job))

def delete(self, job_id):
"""Delete a job by ID"""
if request.method == "OPTIONS": # CORS preflight
return _build_cors_preflight_response()
if job_id not in jobs_store:
return {'message': 'Job not found'}, 404
del jobs_store[job_id]
save_jobs(jobs_store) # Save to file
return _corsify_actual_response(jsonify({'message': 'Job deleted'}))


def _build_cors_preflight_response():
response = make_response()
response.headers.add("Access-Control-Allow-Origin", "*")
response.headers.add("Access-Control-Allow-Headers", "*")
response.headers.add("Access-Control-Allow-Methods", "*")
return response


def _corsify_actual_response(response):
response.headers.add("Access-Control-Allow-Origin", "*")
return response
71 changes: 71 additions & 0 deletions api/controllers/settings_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import json
import os
from flask import Blueprint, jsonify, request
from flask_restx import Api, Resource, fields, Namespace

# Create a Blueprint for the settings controller
settings_bp = Blueprint('settings', __name__)
settings_api = Api(settings_bp, version='1.0', title='Settings API',
description='API for managing settings')

# Define a namespace for settings
settings_ns = Namespace('settings', description='Settings operations')

# Define the model for settings
settings_model = settings_ns.model('Setting', {
'key': fields.String(required=True, description='The key of the setting'),
'value': fields.String(required=True, description='The value of the setting')
})

# File to persist settings data
SETTINGS_FILE = 'settings.json'


def load_settings():
if os.path.exists(SETTINGS_FILE):
with open(SETTINGS_FILE, 'r') as file:
return json.load(file)
return {}


def save_settings(settings):
with open(SETTINGS_FILE, 'w') as file:
json.dump(settings, file, indent=4)


# Load initial data
settings_store = load_settings()


@settings_ns.route('/')
class SettingsList(Resource):
def get(self):
"""List all settings"""
return jsonify({'settings': list(settings_store.values())})

@settings_ns.expect(settings_model)
def post(self):
"""Create or update a setting"""
setting = request.json
key = setting['key']
settings_store[key] = setting
save_settings(settings_store) # Save to file
return jsonify(setting)


@settings_ns.route('/<string:key>')
class SettingItem(Resource):
def get(self, key):
"""Get a setting by key"""
setting = settings_store.get(key)
if setting is None:
return {'message': 'Setting not found'}, 404
return jsonify(setting)

def delete(self, key):
"""Delete a setting by key"""
if key not in settings_store:
return {'message': 'Setting not found'}, 404
del settings_store[key]
save_settings(settings_store) # Save to file
return {'message': 'Setting deleted'}
Loading