Cleanup: simplify file name incrementing logic
[blender-addons.git] / blenderkit / categories.py
blob2da830cb8a33e3edb6c281591e4ad0f082727c58
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
20 from blenderkit import paths, utils, tasks_queue, rerequests
22 import requests
23 import json
24 import os
25 import bpy
26 import time
28 import shutil
29 import threading
30 import logging
32 bk_logger = logging.getLogger('blenderkit')
35 def count_to_parent(parent):
36 for c in parent['children']:
37 count_to_parent(c)
38 parent['assetCount'] += c['assetCount']
41 def fix_category_counts(categories):
42 for c in categories:
43 count_to_parent(c)
46 def filter_category(category):
47 ''' filter categories with no assets, so they aren't shown in search panel'''
48 if category['assetCount'] < 1:
49 return True
50 else:
51 to_remove = []
52 for c in category['children']:
53 if filter_category(c):
54 to_remove.append(c)
55 for c in to_remove:
56 category['children'].remove(c)
59 def filter_categories(categories):
60 for category in categories:
61 filter_category(category)
64 def get_category_path(categories, category):
65 '''finds the category in all possible subcategories and returns the path to it'''
66 category_path = []
67 check_categories = categories[:]
68 parents = {}
69 while len(check_categories) > 0:
70 ccheck = check_categories.pop()
71 # print(ccheck['name'])
72 if not ccheck.get('children'):
73 continue
75 for ch in ccheck['children']:
76 # print(ch['name'])
77 parents[ch['slug']] = ccheck['slug']
79 if ch['slug'] == category:
80 category_path = [ch['slug']]
81 slug = ch['slug']
82 while parents.get(slug):
83 slug = parents.get(slug)
85 category_path.insert(0, slug)
86 return category_path
87 check_categories.append(ch)
90 def get_category(categories, cat_path=()):
91 for category in cat_path:
92 for c in categories:
93 if c['slug'] == category:
94 categories = c['children']
95 if category == cat_path[-1]:
96 return (c)
97 break;
100 # def get_upload_asset_type(self):
101 # typemapper = {
102 # bpy.types.Object.blenderkit: 'model',
103 # bpy.types.Scene.blenderkit: 'scene',
104 # bpy.types.Image.blenderkit: 'hdr',
105 # bpy.types.Material.blenderkit: 'material',
106 # bpy.types.Brush.blenderkit: 'brush'
108 # asset_type = typemapper[type(self)]
109 # return asset_type
111 def update_category_enums(self, context):
112 '''Fixes if lower level is empty - sets it to None, because enum value can be higher.'''
113 enums = get_subcategory_enums(self, context)
114 if enums[0][0] == 'NONE' and self.subcategory != 'NONE':
115 self.subcategory = 'NONE'
118 def update_subcategory_enums(self, context):
119 '''Fixes if lower level is empty - sets it to None, because enum value can be higher.'''
120 enums = get_subcategory1_enums(self, context)
121 if enums[0][0] == 'NONE' and self.subcategory1 != 'NONE':
122 self.subcategory1 = 'NONE'
125 def get_category_enums(self, context):
126 wm = bpy.context.window_manager
127 props = bpy.context.scene.blenderkitUI
128 asset_type = props.asset_type.lower()
129 # asset_type = self.asset_type#get_upload_asset_type(self)
130 asset_categories = get_category(wm['bkit_categories'], cat_path=(asset_type,))
131 items = []
132 for c in asset_categories['children']:
133 items.append((c['slug'], c['name'], c['description']))
134 if len(items) == 0:
135 items.append(('NONE', '', 'no categories on this level defined'))
136 return items
139 def get_subcategory_enums(self, context):
140 wm = bpy.context.window_manager
141 props = bpy.context.scene.blenderkitUI
142 asset_type = props.asset_type.lower()
143 items = []
144 if self.category != '':
145 asset_categories = get_category(wm['bkit_categories'], cat_path=(asset_type, self.category,))
146 for c in asset_categories['children']:
147 items.append((c['slug'], c['name'], c['description']))
148 if len(items) == 0:
149 items.append(('NONE', '', 'no categories on this level defined'))
150 # print('subcategory', items)
151 return items
154 def get_subcategory1_enums(self, context):
155 wm = bpy.context.window_manager
156 props = bpy.context.scene.blenderkitUI
157 asset_type = props.asset_type.lower()
158 items = []
159 if self.category != '' and self.subcategory != '':
160 asset_categories = get_category(wm['bkit_categories'], cat_path=(asset_type, self.category, self.subcategory,))
161 if asset_categories:
162 for c in asset_categories['children']:
163 items.append((c['slug'], c['name'], c['description']))
164 if len(items) == 0:
165 items.append(('NONE', '', 'no categories on this level defined'))
166 return items
169 def copy_categories():
170 # this creates the categories system on only
171 tempdir = paths.get_temp_dir()
172 categories_filepath = os.path.join(tempdir, 'categories.json')
173 if not os.path.exists(categories_filepath):
174 source_path = paths.get_addon_file(subpath='data' + os.sep + 'categories.json')
175 # print('attempt to copy categories from: %s to %s' % (categories_filepath, source_path))
176 try:
177 shutil.copy(source_path, categories_filepath)
178 except:
179 print("couldn't copy categories file")
182 def load_categories():
183 copy_categories()
184 tempdir = paths.get_temp_dir()
185 categories_filepath = os.path.join(tempdir, 'categories.json')
187 wm = bpy.context.window_manager
188 try:
189 with open(categories_filepath, 'r', encoding='utf-8') as catfile:
190 wm['bkit_categories'] = json.load(catfile)
192 wm['active_category'] = {
193 'MODEL': ['model'],
194 'SCENE': ['scene'],
195 'HDR': ['hdr'],
196 'MATERIAL': ['material'],
197 'BRUSH': ['brush'],
199 except:
200 print('categories failed to read')
204 catfetch_counter = 0
207 def fetch_categories(API_key, force=False):
208 url = paths.get_api_url() + 'categories/'
210 headers = utils.get_headers(API_key)
212 tempdir = paths.get_temp_dir()
213 categories_filepath = os.path.join(tempdir, 'categories.json')
214 if os.path.exists(categories_filepath):
215 catfile_age = time.time() - os.path.getmtime(categories_filepath)
216 else:
217 catfile_age = 10000000
219 # global catfetch_counter
220 # catfetch_counter += 1
221 # bk_logger.debug('fetching categories: ', catfetch_counter)
222 # bk_logger.debug('age of cat file', catfile_age)
223 try:
224 # read categories only once per day maximum, or when forced to do so.
225 if catfile_age > 86400 or force:
226 bk_logger.debug('requesting categories from server')
227 r = rerequests.get(url, headers=headers)
228 rdata = r.json()
229 categories = rdata['results']
230 fix_category_counts(categories)
231 # filter_categories(categories) #TODO this should filter categories for search, but not for upload. by now off.
232 with open(categories_filepath, 'w', encoding='utf-8') as s:
233 json.dump(categories, s, ensure_ascii=False, indent=4)
234 tasks_queue.add_task((load_categories, ()))
235 except Exception as e:
236 bk_logger.debug('category fetching failed')
237 bk_logger.exception(e)
238 if not os.path.exists(categories_filepath):
239 source_path = paths.get_addon_file(subpath='data' + os.sep + 'categories.json')
240 shutil.copy(source_path, categories_filepath)
243 def fetch_categories_thread(API_key, force=False):
244 cat_thread = threading.Thread(target=fetch_categories, args=([API_key, force]), daemon=True)
245 cat_thread.start()