Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions script.pystone.benchmark/addon.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="script.pystone.benchmark" name="CPU Benchmark" version="1.1.2" provider-name="Lunatixz">
<addon id="script.pystone.benchmark" name="CPU Benchmark" version="1.1.8" provider-name="Lunatixz">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
<import addon="script.module.kodi-six" version="0.1.3.1"/>
<import addon="script.module.requests" version="0.0.1"/>
</requires>
<extension point="xbmc.python.pluginsource" library="default.py">
<provides>executable</provides>
Expand All @@ -13,7 +14,7 @@
<license>MIT</license>
<platform>all</platform>
<source>https://github.com/Lunatixz/Kodi_Addons/tree/master/script.pystone.benchmark</source>
<news>v.1.1.2-Matrix</news>
<news>v.1.1.6-Piers</news>
<forum>http://forum.kodi.tv/showthread.php?tid=314081</forum>
<assets>
<icon>resources/images/icon.png</icon>
Expand Down
14 changes: 12 additions & 2 deletions script.pystone.benchmark/default.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2020 Lunatixz
# Copyright (C) 2025 Lunatixz
#
#
# This file is part of CPU Benchmark.
Expand All @@ -19,5 +19,15 @@
# -*- coding: utf-8 -*-

#entrypoint
import os, json
from resources.lib import cpubenchmark
if __name__ == '__main__': cpubenchmark.CPU().run()

if __name__ == '__main__':
DEBUG_ENABLED = json.loads(xbmc.executeJSONRPC(json.dumps({"jsonrpc":"2.0","id":"script.pystone.benchmark","method":"Settings.GetSettingValue","params":{"setting":"debug.showloginfo"}}))).get('result',{}).get('value',False)
if not DEBUG_ENABLED: xbmc.executeJSONRPC(json.dumps({"jsonrpc":"2.0","id":"script.pystone.benchmark","method":"Settings.SetSettingValue","params":{"setting":"debug.showloginfo","value":True}}))
try: cpubenchmark.TEXTVIEW("DialogTextViewer.xml", os.getcwd(), "Default")
except: pass
if not DEBUG_ENABLED: xbmc.executeJSONRPC(json.dumps({"jsonrpc":"2.0","id":"script.pystone.benchmark","method":"Settings.SetSettingValue","params":{"setting":"debug.showloginfo","value":False}}))



Binary file modified script.pystone.benchmark/resources/images/screenshot01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@ msgid "Something went wrong, Try Again..."
msgstr ""

msgctxt "#30002"
msgid "Top "
msgid "Score"
msgstr ""

msgctxt "#30003"
msgid "Bottom "
msgid "View your results here"
msgstr ""

msgctxt "#30004"
msgid "[COLOR=%s]%s[/COLOR]"
msgstr ""

msgctxt "#30005"
msgid "[I] Back [/I] or [I]Okay [/I] to exit"
msgid "[I]Back [/I] or [I]Select [/I] to exit!"
msgstr ""

msgctxt "#30006"
Expand Down
248 changes: 190 additions & 58 deletions script.pystone.benchmark/resources/lib/cpubenchmark.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2020 Lunatixz
# Copyright (C) 2025 Lunatixz
#
#
# This file is part of CPU Benchmark.
Expand All @@ -15,19 +15,25 @@
#
# You should have received a copy of the GNU General Public License
# along with CPU Benchmark. If not, see <http://www.gnu.org/licenses/>.
# https://pybenchmarks.org/u64q/performance.php?test=pystone

import os, re, sys, platform, time
import re, os, sys, time, platform, subprocess, textwrap, requests

try:
import multiprocessing
cpu_count = multiprocessing.cpu_count()
ENABLE_POOL = True
except:
ENABLE_POOL = False
cpu_count = os.cpu_count()

from resources.lib import pystone
from resources.lib import platform_detect
from kodi_six import xbmc, xbmcaddon, xbmcplugin, xbmcgui, xbmcvfs, py2_encode, py2_decode

try:
from multiprocessing import cpu_count
from multiprocessing.pool import ThreadPool
ENABLE_POOL = True
except: ENABLE_POOL = False

LIMIT = 45
LINE = 64
LOOP = 50000

# Plugin Info
ADDON_ID = 'script.pystone.benchmark'
REAL_SETTINGS = xbmcaddon.Addon(id=ADDON_ID)
Expand All @@ -39,57 +45,183 @@
FANART = REAL_SETTINGS.getAddonInfo('fanart')
LANGUAGE = REAL_SETTINGS.getLocalizedString

# System Info
cpu_name = platform.processor()
os_name = platform.system() # Get the OS name
os_version = platform.release()# Get the OS version
platform_info = platform.platform()# Get a general platform identifier
python_implementation = platform.python_implementation()# Get the Python implementation
python_version = platform.python_version()# Get the Python version
machine_arch = platform.machine()# Get the machine architecture
architecture = platform.architecture()
is_arm = platform.machine().startswith('arm')
kodi_info = xbmc.getInfoLabel('System.BuildVersion')

try:
system_info = platform.uname()
sys_processor = system_info.processor
sys_machine = system_info.machine
sys_system = system_info.system
except:
system_info = None
sys_processor = None
sys_machine = None
sys_system = None

def log(msg, level=xbmc.LOGDEBUG):
xbmc.log('%s-%s-%s'%(ADDON_ID,ADDON_VERSION,msg),level)

def textviewer(text, header=ADDON_NAME):
xbmcgui.Dialog().textviewer(header, text,usemono=True)

def ok(text, header=ADDON_NAME):
xbmcgui.Dialog().ok(header, text)

def repeat_to_length(string_to_expand, length):
length = int(round(length))
return (string_to_expand * int((length/len(string_to_expand))+1))[:length]

class CPU(object):
def __init__(self):
if ENABLE_POOL:#todo multiprocessing
self.pool = ThreadPool(cpu_count())


def getpystone(self):
maxpystone = 0
for pyseed in [1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000]:
duration, pystonefloat = pystone.pystones(pyseed)
maxpystone = max(maxpystone, int(pystonefloat))
if duration > 0.1:
break
return duration, maxpystone
def _repeat(length=LIMIT, fill='█'):
length = int(round(length))
return (fill * int((length/len(fill))+1))[:length]

def replace_with_k(number_string):
number = int(number_string)
if number >= 1000: return f"{number // 1000}k"
else: return number_string

def progress_bar(iteration, total, length=LIMIT, fill='█'):
percent = ("{0:.1f}").format(100 * (iteration / float(total)))
filled_length = int(length * iteration // total)
bar = fill * filled_length + '-' * (length - filled_length)
if filled_length > 10: bar_with_percent = f"{bar[:length//2 - len(percent)//2]}{percent}%{bar[length//2 - len(percent)//2 + len(percent):]}"
else: bar_with_percent = bar
return f'|{bar_with_percent}|'

def score_bar(stones, pyseed, pydur, avg, length=LIMIT):
def _insert(value, score):
fill = _repeat(length-4)
if value >= 100: value = 90
value = (100-value)
sindex = int(length * ((length / 100) * value / length))
colors = ['green','yellow','orange','red','dimgrey','dimgrey']
chunks = textwrap.wrap(fill[:sindex - len(score)//2] + score + fill[sindex + len(score)//2:], length//4)
bars = ''.join([LANGUAGE(30004)%(colors.pop(0),chunk) for chunk in chunks if len(colors) > 0 ])
return f'| {bars} | {replace_with_k(pyseed)} in %ss'%("{0:.2f}".format(pydur))
return _insert(avg, f'| {stones} |')

def get_info():
def __rpi():
try: # Attempt to retrieve CPU frequency (Pi only, from /proc/device-tree/model)
with open("/proc/device-tree/model", "r") as f:
return f' {f.read().strip()}' # Remove leading/trailing whitespace
except: return ''

def __cpu():
try:
if is_arm or system_info.system == "Linux":# Attempt to retrieve CPU frequency (Linux only, from /proc/cpuinfo)
try:
with open("/proc/cpuinfo", "r") as f:
cpu_info = re.search(r'model name\s*:\s*(.+)', f.read()).group(1).strip()
if is_arm: return f'{cpu_info}{__rpi()}'
else: return cpu_info
except: pass
elif system_info.system == "Darwin":
return subprocess.check_output(["sysctl", "-n", "machdep.cpu.brand_string"]).strip()
elif system_info.system == "Windows":
import winreg
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"Hardware\Description\System\CentralProcessor\0")
cpu_info = winreg.QueryValueEx(key, "ProcessorNameString")[0]
winreg.CloseKey(key)
return cpu_info
except Exception as e: log("__cpu, failed! %s"%(e), xbmc.LOGERROR)

return '[CR]'.join([
(f"Kodi Build: [B]{kodi_info}[/B]"),
(f"Operating System: [B]{(sys_system or os_name)} v.{os_version} ({platform_info})[/B]"),
(_repeat(LINE,'_')),
(f"Processor: [B]{__cpu() or sys_processor or cpu_name}[/B]"),
(f"Machine Architecture: [B]{(sys_machine or machine_arch)} {' '.join(architecture)}[/B]"),
(f"Logical CPU Cores (including Hyperthreading if applicable): [B]{cpu_count}[/B]"),
(_repeat(LINE,'_')),
(f"Python: [B]{python_implementation} v.{python_version}[/B][CR]Benchmark: [B]pystone v.{pystone.__version__}[/B] n={LOOP}"),
(_repeat(LINE,'_'))
])

class TEXTVIEW(xbmcgui.WindowXMLDialog):
textbox = None

def rank(self, stones):
#https://pybenchmarks.org/u64q/performance.php?test=pystone
avg = ((stones - 1400) * 100) // 140000
msg = LANGUAGE(30002) if avg > 50 else LANGUAGE(30003)

if avg >= 75: color = 'green'
elif avg >= 50: color = 'yellow'
elif avg <= 25: color = 'red'
else: color = 'orange'

score = LANGUAGE(30006)%(platform.python_version(),pystone.__version__,LANGUAGE(30004)%(color,stones))
space1 = repeat_to_length(' ', 100 - avg)
space2 = repeat_to_length(' ', avg-1)
space3 = repeat_to_length(' ', (100 - avg) - len(msg))
line = '%s%s%s%s'%(LANGUAGE(30004)%('green',repeat_to_length('-',25)),LANGUAGE(30004)%('yellow',repeat_to_length('-',25)),LANGUAGE(30004)%('orange',repeat_to_length('-',25)),LANGUAGE(30004)%('red',repeat_to_length('-',25)))
arrow = '%s^%s[CR]%s%s%s%s%s'%(space1,space2,space3,msg,LANGUAGE(30004)%(color,avg),LANGUAGE(30004)%(color,'%'),space2)
back = LANGUAGE(30004)%('dimgrey',LANGUAGE(30005))
return '%s[CR]%s[CR]%s[CR]%s'%(score, line, arrow, back)


def run(self):
duration, stones = self.getpystone()
info = LANGUAGE(30007)%(platform_detect.platform_detect(),platform_detect.processor_detect(),platform_detect.getcpu(),cpu_count())
envo = LANGUAGE(30008)%(xbmc.getInfoLabel('System.BuildVersion'),platform.system())
textviewer('%s[CR]%s[CR]%s'%(envo,info,self.rank(stones)))
def __init__(self, *args, **kwargs):
self.head = f'{ADDON_NAME} v.{ADDON_VERSION}'
self.text = get_info()
self.doModal()

def _updateText(self, txt):
try:
self.textbox.setText(txt)
xbmc.executebuiltin('SetFocus(3000)')
xbmc.executebuiltin('AlarmClock(down,Action(down),.5,true,false)')
xbmc.executebuiltin('AlarmClock(down,Action(down),.5,true,false)')
except: pass

def onInit(self):
self.getControl(1).setLabel(self.head)
self.textbox = self.getControl(5)
self._updateText(self.text)
self._run([LOOP for i in range(cpu_count)]) #todo multiprocessing?

def onClick(self, control_id):
pass

def onFocus(self, control_id):
pass

def onAction(self, action):
if action in [xbmcgui.ACTION_PREVIOUS_MENU, xbmcgui.ACTION_NAV_BACK]:
self.close()

def _run(self, seeds=[50000]):
ranks = []
for i, pyseed in enumerate(seeds):
prog = progress_bar(i, len(seeds))
self._updateText(f"{self.text}[CR]{prog}")
pydur, pystonefloat = pystone.pystones(pyseed)
ranks.append({'core':i,'seed':pyseed,'duration':pydur,'score':pystonefloat})
if len(seeds) > 1:
self.text = f'{self.text}[CR]Pass {i+1}{self._rank(int(pystonefloat),pyseed,pydur)}'
self._updateText(self.text)
self._updateText(self._total(ranks))

def _rank(self, stones, pyseed, pydur, maxseed=200000):
return score_bar(stones,pyseed, pydur,((stones) * 100) // maxseed)

def _total(self, ranks):
seeds = []
scores = []
durations = []
for i, rank in enumerate(ranks):
if "seed" in rank: seeds.append(rank["seed"])
if "score" in rank: scores.append(rank["score"])
if "duration" in rank: durations.append(rank["duration"])
rank = self._rank(int(sum(scores) / len(scores)), int(sum(seeds) / len(seeds)), (sum(durations) / len(durations)))
text = f"{self.text}[CR]{LANGUAGE(30002)} {rank}[CR]{_repeat(LINE,'_')}"
exit = LANGUAGE(30004)%('dimgrey',LANGUAGE(30005))
post, link = self._post(text)
if post: text = f'{text}[CR]{LANGUAGE(30003)}: [B]{link}[/B]'
return f"{text}[CR]{exit}"

def _post(self, data):
def __clean(text):
text = text.replace('[CR]','\n')
text = re.sub(r'\[COLOR=(.+?)\]', '', text)
text = re.sub(r'\[/COLOR\]', '', text)
text = text.replace("[B]",'').replace("[/B]",'')
text = text.replace("[I]",'').replace("[/I]",'')
return text
try:
session = requests.Session()
response = session.post('https://paste.kodi.tv/' + 'documents', data=__clean(data).encode('utf-8'), headers={'User-Agent':'%s: %s'%(ADDON_ID, ADDON_VERSION)})
if 'key' in response.json():
url = 'https://paste.kodi.tv/' + response.json()['key']
log('_post, successful url = %s'%(url))
return True, url
elif 'message' in response.json():
log('_post, upload failed, paste may be too large')
return False, response.json()['message']
else:
log('_post failed! %s'%response.text)
return False, "Error making post"
except:
log('_post, unable to retrieve the paste url')
return False, "Failed to retrieve the paste url"

Loading
Loading