Removed bundled brush pack
[blender-addons.git] / greasepencil_tools / import_brush_pack.py
blob90d186c3811474e2df1259c0bd83e6f8d1951135
1 import bpy
2 import re
3 import ssl
4 import urllib.request
5 import urllib.parse
6 import zipfile
7 from pathlib import Path
9 def unzip(zip_path, extract_dir_path):
10 '''Get a zip path and a directory path to extract to'''
11 with zipfile.ZipFile(zip_path, 'r') as zip_ref:
12 zip_ref.extractall(extract_dir_path)
14 def simple_dl_url(url, dest, fallback_url=None):
15 ## need to import urlib.request or linux module does not found 'request' using urllib directly
16 ## need to create an SSl context or linux fail returning unverified ssl
17 # ssl._create_default_https_context = ssl._create_unverified_context
19 try:
20 urllib.request.urlretrieve(url, dest)
21 except Exception as e:
22 print('Error trying to download\n', e)
23 if fallback_url:
24 print('\nDownload page for manual install:', fallback_url)
25 return e
27 def download_url(url, dest):
28 '''download passed url to dest file (include filename)'''
29 import shutil
30 import time
31 ssl._create_default_https_context = ssl._create_unverified_context
32 start_time = time.time()
34 try:
35 with urllib.request.urlopen(url) as response, open(dest, 'wb') as out_file:
36 shutil.copyfileobj(response, out_file)
37 except Exception as e:
38 print('Error trying to download\n', e)
39 return e
41 print(f"Download time {time.time() - start_time:.2f}s",)
44 def get_brushes(blend_fp):
45 cur_brushes = [b.name for b in bpy.data.brushes]
46 with bpy.data.libraries.load(str(blend_fp), link=False) as (data_from, data_to):
47 # load brushes starting with 'tex' prefix if there are not already there
48 data_to.brushes = [b for b in data_from.brushes if b.startswith('tex_') and not b in cur_brushes]
49 # Add holdout
50 if 'z_holdout' in data_from.brushes and not 'z_holdout' in cur_brushes:
51 data_to.brushes.append('z_holdout')
53 ## force fake user for the brushes
54 for b in data_to.brushes:
55 b.use_fake_user = True
57 return len(data_to.brushes)
59 class GP_OT_install_brush_pack(bpy.types.Operator):
60 bl_idname = "gp.import_brush_pack"
61 bl_label = "Download and import texture brush pack"
62 bl_description = "Download and import Grease Pencil brush pack from the web (~3.7 Mo)"
63 bl_options = {"REGISTER", "INTERNAL"}
65 # @classmethod
66 # def poll(cls, context):
67 # return True
69 def _append_brushes(self, blend_fp):
70 bct = get_brushes(blend_fp)
71 if bct:
72 self.report({'INFO'}, f'{bct} brushes installed')
73 else:
74 self.report({'WARNING'}, 'Brushes already loaded')
76 def _install_from_zip(self):
77 ## get blend file name
78 blendname = None
79 with zipfile.ZipFile(self.brushzip, 'r') as zfd:
80 for f in zfd.namelist():
81 if f.endswith('.blend'):
82 blendname = f
83 break
84 if not blendname:
85 self.report({'ERROR'}, f'blend file not found in zip {self.brushzip}')
86 return
88 unzip(self.brushzip, self.temp)
90 self._append_brushes(Path(self.temp) / blendname)
92 def execute(self, context):
94 import tempfile
95 import json
96 import hashlib
97 import os
99 ## get temp dir
100 temp = tempfile.gettempdir()
101 if not temp:
102 self.report({'ERROR'}, 'no os temporary directory found to download brush pack (using python tempfile.gettempdir())')
103 return {"CANCELLED"}
105 self.temp = Path(temp)
107 ## download link from gitlab
108 # brush pack project https://gitlab.com/pepe-school-land/gp-brush-pack
109 repo_url = r'https://gitlab.com/api/v4/projects/21994857'
110 tree_url = f'{repo_url}/repository/tree'
112 ## need to create an SSl context or linux fail and raise unverified ssl
113 ssl._create_default_https_context = ssl._create_unverified_context
115 try:
116 with urllib.request.urlopen(tree_url) as response:
117 html = response.read()
118 except:
119 ## try loading from tempdir
120 packs = [f for f in os.listdir(self.temp) if 'GP_brush_pack' in f and f.endswith('.blend')]
121 if packs:
122 packs.sort()
123 self._append_brushes(Path(self.temp) / packs[-1])
124 self.report({'WARNING'}, 'Brushes loaded from temp directory (No download)')
125 # print('Could not reach web url : Brushes were loaded from temp directory file (No download)')
126 return {"FINISHED"}
128 self.report({'ERROR'}, f'Check your internet connexion, Impossible to connect to url: {tree_url}')
129 return {"CANCELLED"}
131 if not html:
132 self.report({'ERROR'}, f'No response read from: {tree_url}')
133 return {"CANCELLED"}
135 tree_dic = json.loads(html)
136 zips = [fi for fi in tree_dic if fi['type'] == 'blob' and fi['name'].endswith('.zip')]
138 if not zips:
139 print(f'no zip file found in {tree_url}')
140 return {"CANCELLED"}
142 ## sort by name to get last
143 zips.sort(key=lambda x: x['name'])
144 last_zip = zips[-1]
145 zipname = last_zip['name']
146 id_num = last_zip['id']
149 ## url by filename
150 # filepath_encode = urllib.parse.quote(zipname, safe='')# need safe to convert possible '/'
151 # dl_url = f'{repo_url}/repository/files/{filepath_encode}/raw?ref=master'
153 ## url by blobs
154 dl_url = f"{repo_url}/repository/blobs/{id_num}/raw"
156 self.brushzip = self.temp / zipname
159 ### Load existing files instead of redownloading if exists and up to date (same hash)
160 if self.brushzip.exists():
161 ### Test the hash against online git hash (check for update)
162 BLOCK_SIZE = 524288# 512 Kb buf size
163 file_hash = hashlib.sha1()
164 file_hash.update(("blob %u\0" % os.path.getsize(self.brushzip)).encode('utf-8'))
165 with open(self.brushzip, 'rb') as f:
166 fb = f.read(BLOCK_SIZE)
167 while len(fb) > 0:
168 file_hash.update(fb)
169 fb = f.read(BLOCK_SIZE)
171 if file_hash.hexdigest() == id_num: # same git SHA1
172 ## is up to date, install
173 print(f'{self.brushzip} is up do date, appending brushes')
174 self._install_from_zip()
175 return {"FINISHED"}
177 ## Download, unzip, use blend
178 print(f'Downloading brushpack in {self.brushzip}')
179 ## https://cloud.blender.org/p/gallery/5f235cc297f8815e74ffb90b
181 fallback_url='https://gitlab.com/pepe-school-land/gp-brush-pack/-/blob/master/Official_GP_brush_pack_v01.zip'
182 err = simple_dl_url(dl_url, str(self.brushzip), fallback_url)
183 # err = download_url(dl_url, str(self.brushzip), fallback_url)
185 if err:
186 self.report({'ERROR'}, 'Could not download brush pack. Check your internet connection. (see console for detail)')
187 return {"CANCELLED"}
188 else:
189 print('Done')
190 self._install_from_zip()
191 return {"FINISHED"}
194 def register():
195 bpy.utils.register_class(GP_OT_install_brush_pack)
197 def unregister():
198 bpy.utils.unregister_class(GP_OT_install_brush_pack)