Skip to content

Commit bc72a66

Browse files
committed
Change code structure
2 parents baea54b + 56f10da commit bc72a66

File tree

1 file changed

+320
-0
lines changed

1 file changed

+320
-0
lines changed

__init__.py

+320
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<<<<<<< HEAD
12
from .Updater_OP import *
23
from .Updater import engine
34
import bpy
@@ -17,6 +18,318 @@
1718
# version of the addon and provides options to check for updates and update the addon.
1819

1920

21+
=======
22+
import datetime
23+
import os
24+
import zipfile
25+
import io
26+
import json
27+
import webbrowser
28+
import requests
29+
import shutil
30+
import bpy
31+
bl_info = {
32+
"name": "KUpdater",
33+
"description": "KUpdater allows users to directly update their addons in blender.",
34+
"author": "Kent Edoloverio",
35+
"blender": (3, 5, 1),
36+
"version": (1, 3, 1),
37+
"category": "3D View",
38+
"location": "3D View > KUpdater",
39+
"warning": "",
40+
"wiki_url": "https://github.com/kents00/KNTY-Updater",
41+
"tracker_url": "https://github.com/kents00/KNTY-Updater/issues",
42+
}
43+
44+
45+
class GithubEngine:
46+
def __init__(self):
47+
self._api_url = 'https://api.github.com'
48+
self._token = None
49+
self._user = None
50+
self._repo = None
51+
self._current_version = None
52+
self._latest_version = None
53+
self._response = None
54+
self._update_date = None
55+
56+
def clear_state(self):
57+
self._token = None
58+
self._user = None
59+
self._repo = None
60+
self._current_version = None
61+
self._latest_version = None
62+
self._response = None
63+
self._update_date = None
64+
65+
@property
66+
def token(self):
67+
return self._token
68+
69+
@token.setter
70+
def token(self, value):
71+
if value is None:
72+
self._token = None
73+
else:
74+
self._token = str(value)
75+
76+
@property
77+
def user(self):
78+
return self._user
79+
80+
@user.setter
81+
def user(self, value):
82+
self._user = str(value)
83+
84+
@property
85+
def repo(self):
86+
return self._repo
87+
88+
@repo.setter
89+
def repo(self, value):
90+
self._repo = str(value)
91+
92+
@property
93+
def api_url(self):
94+
return self._api_url
95+
96+
@property
97+
def update_date(self):
98+
return self._update_date
99+
100+
@api_url.setter
101+
def api_url(self, value):
102+
if not self.check_is_url(value):
103+
raise ValueError("Not a valid URL: " + value)
104+
self._api_url = value
105+
106+
@staticmethod
107+
def check_is_url(url):
108+
if not ("http://" in url or "https://" in url):
109+
return False
110+
if "." not in url:
111+
return False
112+
return True
113+
114+
def delete_file_in_folder(self):
115+
"""
116+
The function `delete_file_in_folder` deletes all files inside a specified folder.
117+
"""
118+
folder_path = os.path.join(
119+
os.path.dirname(__file__), "..", f"{self.repo}")
120+
121+
directories = [item for item in os.listdir(
122+
folder_path) if os.path.isdir(os.path.join(folder_path, item))]
123+
124+
try:
125+
for directory_name in directories:
126+
if directory_name.startswith(f"{self.user}"):
127+
target_folder = os.path.join(folder_path, directory_name)
128+
for root, dirs, files in os.walk(target_folder):
129+
for file in files:
130+
file_path = os.path.join(root, file)
131+
if file == "version_info.json":
132+
os.remove(file_path)
133+
134+
print(f"Files inside {folder_path} deleted successfully.")
135+
except FileNotFoundError as e:
136+
print(f"Error deleting files in {folder_path}: {e}")
137+
138+
def delete_folder(self):
139+
"""
140+
The `delete_folder` function deletes a specific folder and its contents within a given
141+
repository.
142+
"""
143+
folder_path = os.path.join(
144+
os.path.dirname(__file__), "..", f"{self.repo}")
145+
146+
directories = [item for item in os.listdir(
147+
folder_path) if os.path.isdir(os.path.join(folder_path, item))]
148+
try:
149+
for directory_name in directories:
150+
if directory_name.startswith(f"{self.user}"):
151+
target_folder = os.path.join(folder_path, directory_name)
152+
shutil.rmtree(target_folder)
153+
print(f"Folder {folder_path} deleted successfully.")
154+
except FileNotFoundError as e:
155+
print(f"Error deleting folder {folder_path}: {e}")
156+
157+
def extract_folder(self):
158+
"""
159+
The function `extract_folder` extracts the contents of a specific folder from a given repository
160+
and copies them to the base path.
161+
"""
162+
folder_path = os.path.join(
163+
os.path.dirname(__file__), "..", f"{self.repo}")
164+
directories = [item for item in os.listdir(
165+
folder_path) if os.path.isdir(os.path.join(folder_path, item))]
166+
# Find the specific folder that starts with username
167+
target_folder = None
168+
for directory_name in directories:
169+
if directory_name.startswith(f"{self.user}"):
170+
target_folder = os.path.join(folder_path, directory_name)
171+
break
172+
173+
if target_folder is not None:
174+
destination_folder = folder_path
175+
print(f"Found target folder: {target_folder}")
176+
for item in os.listdir(target_folder):
177+
source_item_path = os.path.join(target_folder, item)
178+
destination_item_path = os.path.join(destination_folder, item)
179+
180+
if os.path.isfile(source_item_path):
181+
shutil.copy2(source_item_path, destination_item_path)
182+
elif os.path.isdir(source_item_path):
183+
shutil.copytree(source_item_path, destination_item_path)
184+
print("Contents extracted to base path.")
185+
else:
186+
print("Target folder not found.")
187+
188+
@bpy.app.handlers.persistent
189+
def check_for_updates(self):
190+
"""
191+
The above function checks for updates of a Blender add-on by making a request to a specified API
192+
endpoint and compares the latest version with the current version of the add-on.
193+
:return: The code is returning the latest version of the addon, the current version of the
194+
addon, and the update date.
195+
"""
196+
update_url = f"{self.api_url}/repos/{self.user}/{self.repo}/releases/latest"
197+
addon_path = os.path.dirname(__file__)
198+
199+
try:
200+
response = requests.get(update_url)
201+
response.raise_for_status()
202+
except requests.exceptions.RequestException as e:
203+
print("Error checking for updates:", e)
204+
if response.status_code != 200:
205+
print("Response content:", response.content)
206+
return None
207+
208+
data = json.loads(response.text)
209+
date = datetime.datetime.now()
210+
latest_version = data["tag_name"]
211+
current_version = f"{bl_info['version'][0]}.{bl_info['version'][1]}.{bl_info['version'][2]}"
212+
213+
addon_version = {
214+
"current_version": current_version,
215+
"latest_version": latest_version,
216+
"update_date": str(date),
217+
}
218+
json_file_path = os.path.join(addon_path, "version_info.json")
219+
try:
220+
with open(json_file_path, 'w') as json_file:
221+
json.dump(addon_version, json_file, indent=1)
222+
except zipfile.BadZipFile as e:
223+
print("Error extracting zip file:", e)
224+
return None
225+
if self._latest_version != self._current_version:
226+
return self._latest_version
227+
return self._current_version, self._update_date
228+
229+
@bpy.app.handlers.persistent
230+
def update(self):
231+
"""
232+
The `update` function downloads a zip file from a specified URL, extracts its contents, and
233+
performs some additional operations on the extracted files.
234+
:return: None if there is an error extracting the zip file.
235+
"""
236+
zipball_url = f"{self.api_url}/repos/{self.user}/{self.repo}/zipball/{self.check_for_updates()}"
237+
238+
response = requests.get(zipball_url)
239+
self._response = response
240+
241+
addon_path = os.path.dirname(__file__)
242+
zip_data = io.BytesIO(response.content)
243+
244+
try:
245+
with zipfile.ZipFile(zip_data, 'r') as zip_ref:
246+
zip_ref.extractall(addon_path)
247+
self.extract_folder()
248+
self.delete_file_in_folder()
249+
self.delete_folder()
250+
except zipfile.BadZipFile as e:
251+
print("Error extracting zip file:", e)
252+
return None
253+
254+
255+
engine = GithubEngine()
256+
257+
258+
class AddCubeOperator(bpy.types.Operator):
259+
bl_label = "ADD CUBE"
260+
bl_idname = "add_simple.cube"
261+
bl_options = {'REGISTER', 'UNDO'}
262+
263+
def execute(self, context):
264+
bpy.ops.mesh.primitive_cube_add()
265+
return {'FINISHED'}
266+
267+
268+
# The `Release_Notes` class is an operator in Blender that opens the release notes of an addon in a
269+
# web browser.
270+
class Release_Notes(bpy.types.Operator):
271+
bl_label = "View the Release Notes"
272+
bl_idname = "addonupdater.release_notes"
273+
274+
def execute(self, context):
275+
webbrowser.open(
276+
f"https://github.com/{engine.user}/{engine.repo}", new=1)
277+
return {'FINISHED'}
278+
279+
280+
# The above class is an operator in Blender that checks for updates to an add-on and updates it if a
281+
# new version is available.
282+
class Update(bpy.types.Operator):
283+
bl_label = "Update"
284+
bl_idname = "addonupdater.checkupdate"
285+
286+
@classmethod
287+
def poll(cls, context):
288+
return engine._current_version != engine._latest_version
289+
290+
def execute(self, context):
291+
engine.update()
292+
if engine._response.status_code != 200:
293+
self.report({'ERROR'}, "Error getting update")
294+
return {'CANCELLED'}
295+
296+
if engine._current_version == engine._latest_version:
297+
self.report(
298+
{'ERROR'}, "You are already using the latest version of the add-on.")
299+
return {'CANCELLED'}
300+
301+
self.report(
302+
{'INFO'}, "Add-on has been updated. Please restart Blender to apply changes.")
303+
return {'FINISHED'}
304+
305+
306+
# The Check_for_update class is a Blender operator that checks for updates to an add-on and reports if
307+
# a new version is available.
308+
class Check_for_update(bpy.types.Operator):
309+
bl_label = "Check Update"
310+
bl_idname = "addonupdater.update"
311+
312+
def invoke(self, context, event):
313+
self.execute(self)
314+
return {'FINISHED'}
315+
316+
def execute(self, context):
317+
engine.check_for_updates()
318+
if not engine.user or not engine.repo:
319+
self.report(
320+
{'ERROR'}, "GitHub user and repository details are not set.")
321+
return {'CANCELLED'}
322+
if engine._current_version != engine._latest_version:
323+
self.report({'INFO'}, "A new version is available!")
324+
elif engine._current_version == engine._latest_version:
325+
self.report(
326+
{'INFO'}, "You are already using the latest version of the add-on.")
327+
return {'FINISHED'}
328+
329+
330+
# The above class is an addon preferences class in Python that displays information about the latest
331+
# version of the addon and provides options to check for updates and update the addon.
332+
>>>>>>> 56f10dafd2c56f3de74956cb97c99b407b3305be
20333
class AddonPreferences(bpy.types.AddonPreferences):
21334
bl_idname = __name__
22335

@@ -63,6 +376,7 @@ def draw(self, context):
63376
row.label(text="Error loading version information.")
64377

65378

379+
<<<<<<< HEAD
66380
class AddCubeOperator(bpy.types.Operator):
67381
bl_label = "ADD CUBE"
68382
bl_idname = "add_simple.cube"
@@ -73,6 +387,8 @@ def execute(self, context):
73387
return {'FINISHED'}
74388

75389

390+
=======
391+
>>>>>>> 56f10dafd2c56f3de74956cb97c99b407b3305be
76392
class AddonUpdaterPanel(bpy.types.Panel):
77393
bl_label = "Addon Updater"
78394
bl_space_type = "VIEW_3D"
@@ -104,6 +420,10 @@ def register():
104420

105421
engine.user = "kents00" # Replace this with your username
106422
engine.repo = "KUpdater" # Replace this with your repository name
423+
<<<<<<< HEAD
424+
=======
425+
engine.token = None # Set your GitHub token here if necessary
426+
>>>>>>> 56f10dafd2c56f3de74956cb97c99b407b3305be
107427

108428
for cls in classes:
109429
bpy.utils.register_class(cls)

0 commit comments

Comments
 (0)