mirror of
https://github.com/Almamu/linux-wallpaperengine.git
synced 2025-09-14 13:56:48 +08:00
feat: add testing tools to run over all backgrounds and getting output data
This commit is contained in:
parent
2cc5e8f272
commit
29fb78b695
@ -23,7 +23,8 @@ struct option long_options [] = {
|
|||||||
{"set-property", required_argument, nullptr, 'o'}, {"noautomute", no_argument, nullptr, 'm'},
|
{"set-property", required_argument, nullptr, 'o'}, {"noautomute", no_argument, nullptr, 'm'},
|
||||||
{"no-audio-processing", no_argument, nullptr, 'g'}, {"no-fullscreen-pause", no_argument, nullptr, 'n'},
|
{"no-audio-processing", no_argument, nullptr, 'g'}, {"no-fullscreen-pause", no_argument, nullptr, 'n'},
|
||||||
{"disable-mouse", no_argument, nullptr, 'e'}, {"scaling", required_argument, nullptr, 't'},
|
{"disable-mouse", no_argument, nullptr, 'e'}, {"scaling", required_argument, nullptr, 't'},
|
||||||
{"clamping", required_argument, nullptr, 't'}, {nullptr, 0, nullptr, 0}};
|
{"clamping", required_argument, nullptr, 't'}, {"screenshot-delay", required_argument, nullptr, 'y'},
|
||||||
|
{nullptr, 0, nullptr, 0}};
|
||||||
|
|
||||||
/* std::hash::operator() isn't constexpr, so it can't be used to get hash values as compile-time constants
|
/* std::hash::operator() isn't constexpr, so it can't be used to get hash values as compile-time constants
|
||||||
* So here is customHash. It skips all spaces, so hashes for " find " and "fi nd" are the same
|
* So here is customHash. It skips all spaces, so hashes for " find " and "fi nd" are the same
|
||||||
@ -198,6 +199,7 @@ CApplicationContext::CApplicationContext (int argc, char* argv []) :
|
|||||||
this->settings.screenshot.take = true;
|
this->settings.screenshot.take = true;
|
||||||
this->settings.screenshot.path = stringPathFixes (optarg);
|
this->settings.screenshot.path = stringPathFixes (optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'y': this->settings.screenshot.delay = std::min (atoi (optarg), 5); break;
|
||||||
|
|
||||||
case 'm': this->settings.audio.automute = false; break;
|
case 'm': this->settings.audio.automute = false; break;
|
||||||
|
|
||||||
|
@ -97,6 +97,8 @@ class CApplicationContext {
|
|||||||
struct {
|
struct {
|
||||||
/** If an screenshot should be taken */
|
/** If an screenshot should be taken */
|
||||||
bool take;
|
bool take;
|
||||||
|
/** The frames to wait until the screenshot is taken */
|
||||||
|
uint32_t delay;
|
||||||
/** The path to where the screenshot must be saved */
|
/** The path to where the screenshot must be saved */
|
||||||
std::filesystem::path path;
|
std::filesystem::path path;
|
||||||
} screenshot;
|
} screenshot;
|
||||||
|
@ -435,7 +435,7 @@ void CWallpaperApplication::show () {
|
|||||||
this->m_context.state.general.keepRunning = false;
|
this->m_context.state.general.keepRunning = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->m_context.settings.screenshot.take || m_videoDriver->getFrameCounter () < 5)
|
if (!this->m_context.settings.screenshot.take || m_videoDriver->getFrameCounter () < this->m_context.settings.screenshot.delay)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
this->takeScreenshot (this->m_context.settings.screenshot.path);
|
this->takeScreenshot (this->m_context.settings.screenshot.path);
|
||||||
|
12
tools/run-over-all-backgrounds.sh
Executable file
12
tools/run-over-all-backgrounds.sh
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
if [ $# -eq 0 ]
|
||||||
|
then
|
||||||
|
echo "Please provide the current build's executable path. You might want to run this script off the same folder."
|
||||||
|
fi
|
||||||
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
|
||||||
|
# ensure the output and images directory exists
|
||||||
|
[ -d "output" ] || mkdir output
|
||||||
|
[ -d "images" ] || mkdir images
|
||||||
|
python ${SCRIPT_DIR}/scripts/run.py $1 ~/.steam/steam/steamapps/workshop/content/431960/
|
||||||
|
python ${SCRIPT_DIR}/scripts/process.py
|
80
tools/scripts/process.py
Normal file
80
tools/scripts/process.py
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import base64
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
import shutil
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
def extract_base64_image(html_file):
|
||||||
|
"""Extracts base64-encoded image data from an HTML file."""
|
||||||
|
with open(html_file, "r", encoding="utf-8") as f:
|
||||||
|
soup = BeautifulSoup(f, "html.parser")
|
||||||
|
img_tag = soup.find("img")
|
||||||
|
if img_tag and 'src' in img_tag.attrs:
|
||||||
|
match = re.search(r'data:image/png;base64,(.+)', img_tag['src'])
|
||||||
|
if match:
|
||||||
|
return match.group(1)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def categorize_image(image_data):
|
||||||
|
"""Categorizes the image as 'grey', 'no image', or 'content'."""
|
||||||
|
if not image_data:
|
||||||
|
return "no_image"
|
||||||
|
|
||||||
|
img_array = np.frombuffer(base64.b64decode(image_data), dtype=np.uint8)
|
||||||
|
img = cv2.imdecode(img_array, cv2.IMREAD_GRAYSCALE)
|
||||||
|
|
||||||
|
if img is None:
|
||||||
|
return "no_image"
|
||||||
|
|
||||||
|
mean_value = cv2.mean(img)[0] # Average pixel brightness
|
||||||
|
std_dev = np.std(img) # Standard deviation of pixel values
|
||||||
|
|
||||||
|
if mean_value > 200 and std_dev < 10:
|
||||||
|
return "grey"
|
||||||
|
return "content"
|
||||||
|
|
||||||
|
def process_html_files(input_folder, output_html="gallery.html"):
|
||||||
|
"""Processes all HTML files, categorizes them, and generates a gallery."""
|
||||||
|
categories = {"no_image": [], "grey": [], "content": []}
|
||||||
|
|
||||||
|
for file in os.listdir(input_folder):
|
||||||
|
if file.endswith(".html") and not file.startswith('report'):
|
||||||
|
file_path = os.path.join(input_folder, file)
|
||||||
|
image_data = extract_base64_image(file_path)
|
||||||
|
category = categorize_image(image_data)
|
||||||
|
categories[category].append((file, image_data))
|
||||||
|
|
||||||
|
with open(output_html, "w", encoding="utf-8") as f:
|
||||||
|
f.write("""
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Image Gallery</title>
|
||||||
|
<style>
|
||||||
|
.gallery { display: flex; flex-wrap: wrap; }
|
||||||
|
.thumb { margin: 10px; text-align: center; }
|
||||||
|
img { width: 100px; height: auto; border: 1px solid black; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Image Gallery</h1>
|
||||||
|
""")
|
||||||
|
|
||||||
|
for category, files in categories.items():
|
||||||
|
f.write(f"<h2>{category.replace('_', ' ').title()}</h2>")
|
||||||
|
f.write("<div class='gallery'>")
|
||||||
|
for file_name, img_data in files:
|
||||||
|
if img_data:
|
||||||
|
f.write(f"<div class='thumb'><a href='{file_name}'><img src='data:image/png;base64,{img_data}'></a></div>")
|
||||||
|
else:
|
||||||
|
f.write(f"<div class='thumb'><a href='{file_name}'>[No Image]</a></div>")
|
||||||
|
f.write("</div>")
|
||||||
|
|
||||||
|
f.write("</body></html>")
|
||||||
|
|
||||||
|
print(f"Gallery created at {output_html}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
input_folder = "./output" # Change to your directory
|
||||||
|
process_html_files(input_folder, "output/gallery.html")
|
90
tools/scripts/run.py
Normal file
90
tools/scripts/run.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import html
|
||||||
|
import base64
|
||||||
|
import argparse
|
||||||
|
import signal
|
||||||
|
|
||||||
|
def run_and_monitor(program, program_args=None, output_file="output.png", wait_time=1, timeout=30, html_report="report.html"):
|
||||||
|
try:
|
||||||
|
# Build the full command
|
||||||
|
full_command = [program] + ([program_args] if program_args else [])
|
||||||
|
|
||||||
|
print(f"Running command: {' '.join(full_command)}")
|
||||||
|
|
||||||
|
# Start the program with shell=True for better compatibility
|
||||||
|
process = subprocess.Popen(
|
||||||
|
f"exec {" ".join(full_command)}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, preexec_fn=os.setsid
|
||||||
|
)
|
||||||
|
stdout_data, stderr_data = "", ""
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
while time.time() - start_time < timeout:
|
||||||
|
print("Polling for file {output_file}".format(output_file=output_file))
|
||||||
|
# Check if the file is created
|
||||||
|
if os.path.exists(output_file):
|
||||||
|
break
|
||||||
|
time.sleep(wait_time)
|
||||||
|
else:
|
||||||
|
print("Timeout reached. File not found.")
|
||||||
|
|
||||||
|
# Capture output
|
||||||
|
if process.poll() is None:
|
||||||
|
os.killpg(os.getpgid(process.pid), signal.SIGKILL)
|
||||||
|
stdout_data, stderr_data = process.communicate()
|
||||||
|
|
||||||
|
# Read the created image file as base64
|
||||||
|
image_base64 = ""
|
||||||
|
if os.path.exists(output_file):
|
||||||
|
with open(output_file, "rb") as f:
|
||||||
|
image_base64 = base64.b64encode(f.read()).decode('utf-8')
|
||||||
|
|
||||||
|
# Save results to an HTML file
|
||||||
|
with open(os.path.join("output", html_report), "w", encoding="utf-8") as report:
|
||||||
|
report.write("""
|
||||||
|
<html>
|
||||||
|
<head><title>Program Output Review</title></head>
|
||||||
|
<body>
|
||||||
|
<h1>Generated Image</h1>
|
||||||
|
<img src='data:image/png;base64,{image_base64}' alt='Generated Image'/>
|
||||||
|
<h1>Standard Output</h1>
|
||||||
|
<pre>{stdout_data}</pre>
|
||||||
|
<h1>Error Output</h1>
|
||||||
|
<pre>{stderr_data}</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""".format(
|
||||||
|
image_base64=image_base64,
|
||||||
|
stdout_data=html.escape(stdout_data),
|
||||||
|
stderr_data=html.escape(stderr_data)
|
||||||
|
))
|
||||||
|
print(f"Report saved to {html_report}")
|
||||||
|
|
||||||
|
if stderr_data:
|
||||||
|
print("Error output detected:")
|
||||||
|
print(stderr_data)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description="Run a program, monitor a file, and capture output.")
|
||||||
|
parser.add_argument("program", help="The program to execute.")
|
||||||
|
parser.add_argument("directory", help="Directory containing folders to process.")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if not os.path.isdir(args.directory):
|
||||||
|
print("Error: Specified directory does not exist.")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
folders = [f for f in os.listdir(args.directory) if os.path.isdir(os.path.join(args.directory, f))]
|
||||||
|
|
||||||
|
for folder in folders:
|
||||||
|
run_and_monitor(
|
||||||
|
args.program,
|
||||||
|
program_args="--screenshot {path}/images/{folder}.png --screenshot-delay 420 {folder}".format(path=os.getcwd(), folder=folder),
|
||||||
|
output_file="{path}/images/{folder}.png".format(path=os.getcwd(), folder=folder),
|
||||||
|
html_report="{folder}.html".format(folder=folder)
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user