This commit is contained in:
Yusarina
2024-10-22 00:08:07 +01:00
parent 2e06cc9945
commit 1b908a4200
4 changed files with 100 additions and 14 deletions
+1 -1
View File
@@ -3,7 +3,7 @@
schema_version = "1.0.0" schema_version = "1.0.0"
id = "avatar_toolkit" id = "avatar_toolkit"
version = "4.3.1" version = "0.1.0"
name = "Avatar Toolkit" name = "Avatar Toolkit"
tagline = "A modern tool for importing and optimizing models for VRChat, Resonite, and other similar games." tagline = "A modern tool for importing and optimizing models for VRChat, Resonite, and other similar games."
maintainer = "Team NekoNeo" maintainer = "Team NekoNeo"
+8
View File
@@ -1,5 +1,6 @@
import bpy import bpy
import os import os
import toml
import json import json
from bpy.types import AddonPreferences from bpy.types import AddonPreferences
from typing import Any, Dict from typing import Any, Dict
@@ -8,6 +9,13 @@ from typing import Any, Dict
PREFERENCES_DIR = os.path.dirname(os.path.abspath(__file__)) PREFERENCES_DIR = os.path.dirname(os.path.abspath(__file__))
PREFERENCES_FILE = os.path.join(PREFERENCES_DIR, "preferences.json") PREFERENCES_FILE = os.path.join(PREFERENCES_DIR, "preferences.json")
def get_current_version():
main_dir = os.path.dirname(os.path.dirname(__file__))
manifest_path = os.path.join(main_dir, "blender_manifest.toml")
with open(manifest_path, 'r') as f:
manifest_data = toml.load(f)
return manifest_data.get('version', 'Unknown')
def save_preference(key: str, value: Any) -> None: def save_preference(key: str, value: Any) -> None:
"""Save a single preference to the JSON file.""" """Save a single preference to the JSON file."""
prefs = load_preferences() prefs = load_preferences()
+89 -11
View File
@@ -9,11 +9,11 @@ import zipfile
from threading import Thread from threading import Thread
from bpy.app.handlers import persistent from bpy.app.handlers import persistent
from ..functions.translations import t from ..functions.translations import t
from .addon_preferences import get_preference from .addon_preferences import get_preference, get_current_version, save_preference
from .register import register_wrap from .register import register_wrap
from ..ui.panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME from ..ui.panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
GITHUB_REPO = "teamneoneko/Avatar-Toolkit-rinadev" GITHUB_REPO = "teamneoneko/Avatar-Toolkit"
is_checking_for_update = False is_checking_for_update = False
update_needed = False update_needed = False
@@ -46,6 +46,26 @@ class AvatarToolkit_OT_UpdateToLatest(bpy.types.Operator):
update_now(latest=True) update_now(latest=True)
return {'FINISHED'} return {'FINISHED'}
@register_wrap
class AvatarToolkit_OT_UpdateNotificationPopup(bpy.types.Operator):
bl_idname = "avatar_toolkit.update_notification_popup"
bl_label = t('UpdateNotificationPopup.label')
bl_description = t('UpdateNotificationPopup.desc')
bl_options = {'INTERNAL'}
def execute(self, context):
update_now(latest=True)
self.report({'INFO'}, "Update started. Please wait for the process to complete.")
return {'FINISHED'}
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self, width=300)
def draw(self, context):
layout = self.layout
col = layout.column(align=True)
col.label(text=t('UpdateNotificationPopup.newUpdate', default="New update available: {version}").format(version=latest_version_str))
@register_wrap @register_wrap
class AvatarToolkit_PT_UpdaterPanel(bpy.types.Panel): class AvatarToolkit_PT_UpdaterPanel(bpy.types.Panel):
bl_label = t("Updater.label") bl_label = t("Updater.label")
@@ -54,12 +74,39 @@ class AvatarToolkit_PT_UpdaterPanel(bpy.types.Panel):
bl_region_type = 'UI' bl_region_type = 'UI'
bl_category = CATEGORY_NAME bl_category = CATEGORY_NAME
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
bl_order = 5 bl_order = 9
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
draw_updater_panel(context, layout) draw_updater_panel(context, layout)
@register_wrap
class AvatarToolkit_OT_RestartBlenderPopup(bpy.types.Operator):
bl_idname = "avatar_toolkit.restart_blender_popup"
bl_label = t('RestartBlenderPopup.label', default="Restart Blender")
bl_description = t('RestartBlenderPopup.desc', default="Restart Blender to complete the update")
bl_options = {'INTERNAL'}
def execute(self, context):
return {'FINISHED'}
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self, width=300)
def draw(self, context):
layout = self.layout
col = layout.column(align=True)
col.label(text=t('RestartBlenderPopup.message', default="Update successful! Please restart Blender."))
@persistent
def check_for_update_on_start(dummy):
if get_preference("check_for_updates_on_startup", True):
current_time = time.time()
last_check = get_preference("last_update_check", 0)
if current_time - last_check > 86400: # 24 hours
check_for_update_background()
save_preference("last_update_check", current_time)
def check_for_update_background(): def check_for_update_background():
global is_checking_for_update global is_checking_for_update
if is_checking_for_update: if is_checking_for_update:
@@ -72,17 +119,18 @@ def check_for_update():
global update_needed, latest_version, latest_version_str, version_list global update_needed, latest_version, latest_version_str, version_list
if not get_github_releases(): if not get_github_releases():
finish_update_checking(error=t('check_for_update.cantCheck')) bpy.app.timers.register(lambda: finish_update_checking(error=t('check_for_update.cantCheck')))
return return
update_needed = check_for_update_available() update_needed = check_for_update_available()
if update_needed: if update_needed:
print('Update found!') print('Update found!')
bpy.app.timers.register(lambda: bpy.ops.avatar_toolkit.update_notification_popup('INVOKE_DEFAULT') or None)
else: else:
print('No update found.') print('No update found.')
finish_update_checking() bpy.app.timers.register(finish_update_checking)
def get_github_releases(): def get_github_releases():
global version_list global version_list
@@ -114,16 +162,31 @@ def check_for_update_available():
latest_version = max(version_list.keys(), key=lambda v: [int(x) for x in v.split('.')]) latest_version = max(version_list.keys(), key=lambda v: [int(x) for x in v.split('.')])
latest_version_str = latest_version latest_version_str = latest_version
current_version = get_preference("version") current_version = get_current_version()
current_version_parts = [int(x) for x in current_version.split('.')] print(f"Current version: {current_version}") # Debugging statement
latest_version_parts = [int(x) for x in latest_version.split('.')]
if current_version is None:
print("Current version is None. Cannot check for updates.")
return False
try:
# Validate that the version string contains only numeric parts
current_version_parts = [int(x) for x in current_version.split('.')]
latest_version_parts = [int(x) for x in latest_version.split('.')]
except ValueError as e:
print(f"Error parsing version numbers: {e}")
return False
return latest_version_parts > current_version_parts return latest_version_parts > current_version_parts
def finish_update_checking(error=''): def finish_update_checking(error=''):
global is_checking_for_update global is_checking_for_update
is_checking_for_update = False is_checking_for_update = False
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1) if update_needed:
bpy.ops.avatar_toolkit.update_notification_popup('INVOKE_DEFAULT')
ui_refresh()
return None # Important for bpy.app.timers
def update_now(latest=False): def update_now(latest=False):
if latest: if latest:
@@ -132,6 +195,7 @@ def update_now(latest=False):
update_link = version_list[bpy.context.scene.avatar_toolkit_updater_version_list][0] update_link = version_list[bpy.context.scene.avatar_toolkit_updater_version_list][0]
download_file(update_link) download_file(update_link)
ui_refresh()
def download_file(update_url): def download_file(update_url):
update_zip_file = os.path.join(downloads_dir, "avatar-toolkit-update.zip") update_zip_file = os.path.join(downloads_dir, "avatar-toolkit-update.zip")
@@ -198,7 +262,9 @@ def finish_update(error=''):
print(f"Update failed: {error}") print(f"Update failed: {error}")
else: else:
print("Update successful!") print("Update successful!")
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1) save_preference("version", latest_version_str)
bpy.ops.avatar_toolkit.restart_blender_popup('INVOKE_DEFAULT')
ui_refresh()
def get_version_list(self, context): def get_version_list(self, context):
return [(v, v, '') for v in version_list.keys()] if version_list else [] return [(v, v, '') for v in version_list.keys()] if version_list else []
@@ -219,5 +285,17 @@ def draw_updater_panel(context, layout):
row.operator(AvatarToolkit_OT_UpdateToLatest.bl_idname, text=t('Updater.UpdateToSelectedButton.label')) row.operator(AvatarToolkit_OT_UpdateToLatest.bl_idname, text=t('Updater.UpdateToSelectedButton.label'))
col.separator() col.separator()
col.label(text=t('Updater.currentVersion').format(name=get_preference("version"))) col.label(text=t('Updater.currentVersion').format(name=get_current_version()))
def ui_refresh():
for windowManager in bpy.data.window_managers:
for window in windowManager.windows:
for area in window.screen.areas:
area.tag_redraw()
def register():
bpy.app.handlers.load_post.append(check_for_update_on_start)
def unregister():
if check_for_update_on_start in bpy.app.handlers.load_post:
bpy.app.handlers.load_post.remove(check_for_update_on_start)
+2 -2
View File
@@ -66,14 +66,14 @@ def load_translations() -> bool:
return dictionary != old_dictionary return dictionary != old_dictionary
def t(phrase: str, default: str = None) -> str: def t(phrase: str, default: str = None, **kwargs) -> str:
output: str = dictionary.get(phrase) output: str = dictionary.get(phrase)
if output is None: if output is None:
if verbose: if verbose:
print(f'Warning: Unknown phrase: {phrase}') print(f'Warning: Unknown phrase: {phrase}')
return default if default is not None else phrase return default if default is not None else phrase
# print(f"Translating '{phrase}' to '{output}'") # Debug print # print(f"Translating '{phrase}' to '{output}'") # Debug print
return output return output.format(**kwargs) if kwargs else output
def get_language_display_name(lang: str) -> str: def get_language_display_name(lang: str) -> str:
if lang == "auto": if lang == "auto":