├── LilyImageFromUrl.py ├── README.md └── doc ├── button.png ├── copy-url.png └── result.png /LilyImageFromUrl.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021 -- Elie Michel 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the “Software”), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # The Software is provided “as is”, without warranty of any kind, express or 14 | # implied, including but not limited to the warranties of merchantability, 15 | # fitness for a particular purpose and noninfringement. In no event shall 16 | # the authors or copyright holders be liable for any claim, damages or other 17 | # liability, whether in an action of contract, tort or otherwise, arising from, 18 | # out of or in connection with the software or the use or other dealings in the 19 | # Software. 20 | 21 | bl_info = { 22 | "name": "Lily Image From URL", 23 | "author": "Élie Michel", 24 | "version": (1, 0, 0), 25 | "blender": (2, 90, 0), 26 | "location": "UV/Image Editor > From Url", 27 | "description": "Import an image from the URL currently in the clipboard", 28 | "warning": "", 29 | "doc_url": "https://github.com/eliemichel/LilyImageFromURL", 30 | "category": "Import", 31 | } 32 | 33 | import bpy 34 | from bpy.types import Operator, IMAGE_MT_image, IMAGE_MT_editor_menus 35 | from bpy.props import BoolProperty, StringProperty 36 | 37 | import requests 38 | from urllib.parse import urlparse 39 | from mimetypes import guess_extension 40 | from os.path import join 41 | from tempfile import gettempdir 42 | import shutil 43 | 44 | class CannotDownload(Exception): 45 | pass 46 | 47 | def make_filename(url, response): 48 | filename = urlparse(url).path.split('/')[-1] 49 | mime = response.headers['content-type'] 50 | ext = guess_extension(mime) 51 | if not filename.endswith(ext): 52 | filename += ext 53 | return filename 54 | 55 | def download_image_direct(url): 56 | headers = {"User-Agent":"Mozilla/5.0"} # fake user agent 57 | r = requests.get(url, headers=headers) 58 | if r.status_code != 200: 59 | raise CannotDownload(f"Http request returned status {r.status_code}") 60 | 61 | filename = make_filename(url, r) 62 | raw_data = r.content 63 | 64 | # Not working, we have to go through an actual file :/ 65 | # So we use download_image_via_file instead for now 66 | img = bpy.data.images.new(filename, width=1, height=1) 67 | img.source = 'FILE' 68 | img.filepath = filename 69 | img.pack(data=raw_data) 70 | img.reload() 71 | return img 72 | 73 | def download_image_via_file(url): 74 | headers = {"User-Agent":"Mozilla/5.0"} # fake user agent 75 | r = requests.get(url, stream=True, headers=headers) 76 | if r.status_code != 200: 77 | raise CannotDownload(f"Http request returned status {r.status_code}") 78 | 79 | tmp_dir = gettempdir() 80 | filename = make_filename(url, r) 81 | filepath = join(tmp_dir, filename) 82 | 83 | with open(filepath, 'wb') as f: 84 | r.raw.decode_content = True 85 | shutil.copyfileobj(r.raw, f) 86 | 87 | img = bpy.data.images.load(filepath) 88 | img.name = filename 89 | return img 90 | 91 | download_image = download_image_via_file 92 | 93 | class ImageFromUrl(Operator): 94 | bl_idname = "lily.image_from_url" 95 | bl_label = "New Image from URL" 96 | 97 | url: StringProperty( 98 | name = "URL", 99 | description = "URL of the image to import, ignored if use_clipboard is turned on", 100 | default = "", 101 | ) 102 | 103 | use_clipboard: BoolProperty( 104 | name = "Import from Clipboard", 105 | description = "Get the URL of the image to import from the clipboard rather than using the url property", 106 | default = True 107 | ) 108 | 109 | def execute(self, context): 110 | if self.use_clipboard: 111 | url = context.window_manager.clipboard 112 | else: 113 | url = self.url 114 | 115 | try: 116 | img = download_image(url) 117 | except CannotDownload as e: 118 | self.report({'ERROR'}, f"Cannot download image from url '{url}': {e.message}") 119 | return {'CANCELLED'} 120 | 121 | area = context.area 122 | if area.type == 'IMAGE_EDITOR' : 123 | context.area.spaces.active.image = img 124 | 125 | return {'FINISHED'} 126 | 127 | def draw_menu(self, context): 128 | layout = self.layout 129 | layout.operator(ImageFromUrl.bl_idname) 130 | 131 | def draw_menu_short(self, context): 132 | layout = self.layout 133 | layout.operator(ImageFromUrl.bl_idname, text="From URL") 134 | 135 | classes = ( 136 | ImageFromUrl, 137 | ) 138 | register_cls, unregister_cls = bpy.utils.register_classes_factory(classes) 139 | 140 | def register(): 141 | register_cls() 142 | IMAGE_MT_image.append(draw_menu) 143 | IMAGE_MT_editor_menus.append(draw_menu_short) 144 | 145 | def unregister(): 146 | IMAGE_MT_image.remove(draw_menu) 147 | IMAGE_MT_editor_menus.append(draw_menu_short) 148 | unregister_cls() 149 | 150 | if __name__ == "__main__": 151 | register() 152 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Lily Image From URL 2 | =================== 3 | 4 | A very very simple Blender add-on to very very quickly import images from URLs. 5 | 6 | Download 7 | -------- 8 | 9 | Just download [LilyImageFromUrl.py](https://github.com/eliemichel/LilyImageFromURL/releases/latest/download/LilyImageFromUrl.py) from the last release. 10 | 11 | Usage 12 | ----- 13 | 14 | A. On any image on the Internet, right click, `Copy Image Address`: 15 | 16 | ![Copy Image URL](doc/copy-url.png) 17 | 18 | B. In Blender, once the add-on is installed, click on the `From URL` button in the UV/Image Editor. 19 | 20 | ![From URL button](doc/button.png) 21 | 22 | C. And voilà! 23 | 24 | ![Imported image](doc/result.png) 25 | 26 | License 27 | ------- 28 | 29 | This add-on is distributed under the terms of the GPLv3 license (see file header). 30 | 31 | Troubleshooting 32 | --------------- 33 | 34 | Any question is welcome either in the [bug tracker](https://github.com/eliemichel/LilyImageFromURL/issues) or on twitter [@exppad](https://twitter.com/exppad). 35 | 36 | -------------------------------------------------------------------------------- /doc/button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliemichel/LilyImageFromURL/9cbca57807b59ae1f4e07aa9bfbf7fa34c4ab557/doc/button.png -------------------------------------------------------------------------------- /doc/copy-url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliemichel/LilyImageFromURL/9cbca57807b59ae1f4e07aa9bfbf7fa34c4ab557/doc/copy-url.png -------------------------------------------------------------------------------- /doc/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliemichel/LilyImageFromURL/9cbca57807b59ae1f4e07aa9bfbf7fa34c4ab557/doc/result.png --------------------------------------------------------------------------------