loading images from internet working
This commit is contained in:
3
.sublime/MarkdownLivePreview.sublime-settings
Normal file
3
.sublime/MarkdownLivePreview.sublime-settings
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"load_from_internet_when_starts": ["http://", "https://"]
|
||||||
|
}
|
||||||
@ -1,10 +1,13 @@
|
|||||||
todo:
|
todo:
|
||||||
|
|
||||||
☐ add **custom css** feature
|
☐ sync scroll @needsUpdate(because of images)
|
||||||
☐ sync scroll @needsUpdate
|
✔ load images from internet (`https:`) @done (17-01-02 15:51)
|
||||||
☐ load images from internet (`https:`)
|
|
||||||
☐ regive focus to the right markdown view
|
☐ regive focus to the right markdown view
|
||||||
☐ set the title of the preview
|
☐ set the title of the preview
|
||||||
☐ disable previewing when the preview is closed
|
|
||||||
☐ preview.set_scratch(True)
|
☐ preview.set_scratch(True)
|
||||||
☐ add 404 image
|
☐ add 404 image
|
||||||
|
☐ fix #4
|
||||||
|
☐ bug when empty `src`
|
||||||
|
☐ auto refresh preview if loading images
|
||||||
|
☐ call settings listener on_new too
|
||||||
|
☐ preview.wordWrap => True
|
||||||
|
|||||||
50
functions.py
Normal file
50
functions.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
import base64
|
||||||
|
import os.path
|
||||||
|
import sublime
|
||||||
|
|
||||||
|
file404 = os.path.join(os.path.dirname(__file__), '404.png')
|
||||||
|
|
||||||
|
def to_base64(path=None, content=None):
|
||||||
|
if path is None and content is None:
|
||||||
|
return to_base64(file404)
|
||||||
|
elif content is None and path is not None:
|
||||||
|
try:
|
||||||
|
with open(path, 'rb') as fp:
|
||||||
|
content = fp.read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
return to_base64(file404)
|
||||||
|
|
||||||
|
return 'data:image/png;base64,' + ''.join([chr(el) for el in list(base64.standard_b64encode(content))])
|
||||||
|
|
||||||
|
def md(*t, **kwargs):
|
||||||
|
sublime.message_dialog(kwargs.get('sep', '\n').join([str(el) for el in t]))
|
||||||
|
|
||||||
|
def sm(*t, **kwargs):
|
||||||
|
sublime.status_message(kwargs.get('sep', ' ').join([str(el) for el in t]))
|
||||||
|
|
||||||
|
def em(*t, **kwargs):
|
||||||
|
sublime.error_message(kwargs.get('sep', ' ').join([str(el) for el in t]))
|
||||||
|
|
||||||
|
def mini(val, min):
|
||||||
|
if val < min:
|
||||||
|
return min
|
||||||
|
return val
|
||||||
|
|
||||||
|
def get_content_till(string, char_to_look_for, start=0):
|
||||||
|
i = start
|
||||||
|
while i < len(string):
|
||||||
|
i += 1
|
||||||
|
if string[i] == char_to_look_for:
|
||||||
|
return string[start:i], i
|
||||||
|
|
||||||
|
def get_view_content(view):
|
||||||
|
return view.substr(sublime.Region(0, view.size()))
|
||||||
|
|
||||||
|
def get_view_from_id(window, id):
|
||||||
|
for view in window.views():
|
||||||
|
if view.id() == id:
|
||||||
|
return view
|
||||||
|
|
||||||
|
def get_settings():
|
||||||
|
return sublime.load_settings('MarkdownLivePreview.sublime-settings')
|
||||||
@ -5,24 +5,15 @@ from threading import Thread
|
|||||||
import urllib.request
|
import urllib.request
|
||||||
import base64
|
import base64
|
||||||
import sublime
|
import sublime
|
||||||
|
from .functions import *
|
||||||
|
|
||||||
CACHE_FILE = os.path.join(os.path.dirname(__file__), 'cache.txt')
|
CACHE_FILE = os.path.join(os.path.dirname(__file__), 'cache.txt')
|
||||||
TIMEOUT = 20
|
TIMEOUT = 20 # seconds
|
||||||
|
|
||||||
SEPARATOR = '---%cache%--'
|
SEPARATOR = '---%cache%--'
|
||||||
|
|
||||||
class InternalError(Exception): pass
|
class InternalError(Exception): pass
|
||||||
|
|
||||||
def to_base64(path=None, content=None):
|
|
||||||
if content is None and path is not None:
|
|
||||||
try:
|
|
||||||
with open(path, 'rb') as fp:
|
|
||||||
content = fp.read()
|
|
||||||
except FileNotFoundError:
|
|
||||||
return to_base64(os.path.join(os.path.dirname(__file__), '404.png'))
|
|
||||||
|
|
||||||
return 'data:image/png;base64,' + ''.join([chr(el) for el in list(base64.standard_b64encode(content))])
|
|
||||||
|
|
||||||
def load_and_save_image(url, user_callback):
|
def load_and_save_image(url, user_callback):
|
||||||
def callback(content):
|
def callback(content):
|
||||||
content = to_base64(content=content)
|
content = to_base64(content=content)
|
||||||
@ -31,7 +22,25 @@ def load_and_save_image(url, user_callback):
|
|||||||
user_callback(content)
|
user_callback(content)
|
||||||
thread = ImageLoader(url, callback)
|
thread = ImageLoader(url, callback)
|
||||||
thread.start()
|
thread.start()
|
||||||
sublime.set_timeout_async(lambda: thread.join(), TIMEOUT)
|
sublime.set_timeout_async(lambda: thread.join(), TIMEOUT * 1000)
|
||||||
|
|
||||||
|
def get_base64_saver(loading, url):
|
||||||
|
def callback(content):
|
||||||
|
loading[url] = to_base64(content=content)
|
||||||
|
return callback
|
||||||
|
|
||||||
|
def get_cache_for(imageurl):
|
||||||
|
if not os.path.exists(CACHE_FILE):
|
||||||
|
return
|
||||||
|
with open(CACHE_FILE) as fp:
|
||||||
|
for line in fp.read().splitlines():
|
||||||
|
url, base64 = line.split(SEPARATOR, 1)
|
||||||
|
if url == imageurl:
|
||||||
|
return base64
|
||||||
|
|
||||||
|
def cache(imageurl, base64):
|
||||||
|
with open(CACHE_FILE, 'a') as fp:
|
||||||
|
fp.write(imageurl + SEPARATOR + base64 + '\n')
|
||||||
|
|
||||||
class ImageLoader(Thread):
|
class ImageLoader(Thread):
|
||||||
|
|
||||||
@ -42,42 +51,45 @@ class ImageLoader(Thread):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
page = urllib.request.urlopen(self.url, None, TIMEOUT)
|
page = urllib.request.urlopen(self.url, None, TIMEOUT)
|
||||||
|
# self.callback(self.url, page.read())
|
||||||
self.callback(page.read())
|
self.callback(page.read())
|
||||||
|
|
||||||
|
|
||||||
class ImageManager(object):
|
class ImageManager(object):
|
||||||
|
|
||||||
currently_loading = []
|
"""
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
>>> image = ImageManager.get('http://domain.com/image.png')
|
||||||
|
>>> image = ImageManager.get('http://domain.com/image.png')
|
||||||
|
# still loading (this is a comment, no an outputed text), it doesn't
|
||||||
|
# run an other request
|
||||||
|
>>> image = ImageManager.get('http://domain.com/image.png')
|
||||||
|
'data:image/png;base64,....'
|
||||||
|
|
||||||
|
"""
|
||||||
|
loading = {}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get(imageurl, user_callback):
|
def get(imageurl, user_callback=None):
|
||||||
if imageurl in ImageManager.currently_loading:
|
# if imageurl in ImageManager.loading.keys():
|
||||||
return None
|
# return None
|
||||||
def callback(content):
|
|
||||||
try:
|
|
||||||
ImageManager.currently_loading.remove(imageurl)
|
|
||||||
except ValueError:
|
|
||||||
sublime.error_message('Internal error: Trying to remove an URL'
|
|
||||||
'from loading_url, but not found. Please'
|
|
||||||
'report to the issue tracker.')
|
|
||||||
sublime.run_command('open_url', {
|
|
||||||
'url': 'https://github.com/math2001/MarkdownLivePreview/'
|
|
||||||
'issues/new'
|
|
||||||
})
|
|
||||||
|
|
||||||
user_callback(content)
|
cached = get_cache_for(imageurl)
|
||||||
|
if cached:
|
||||||
ImageManager.currently_loading.append(imageurl)
|
return cached
|
||||||
try:
|
elif imageurl in ImageManager.loading.keys():
|
||||||
with open(CACHE_FILE, 'r') as fp:
|
# return None (the file is still loading, already made a request)
|
||||||
lines = fp.readlines()
|
# return string the base64 of the url (which is going to be cached)
|
||||||
except FileNotFoundError:
|
temp_cached = ImageManager.loading[imageurl]
|
||||||
pass
|
if temp_cached:
|
||||||
|
cache(imageurl, temp_cached)
|
||||||
|
del ImageManager.loading[imageurl]
|
||||||
|
return temp_cached
|
||||||
else:
|
else:
|
||||||
for line in lines:
|
# load from internet
|
||||||
url, base64 = line.split(SEPARATOR, 1)
|
ImageManager.loading[imageurl] = None
|
||||||
if url == imageurl:
|
callback = get_base64_saver(ImageManager.loading, imageurl)
|
||||||
return callback(base64)
|
loader = ImageLoader(imageurl, callback)
|
||||||
else:
|
loader.start()
|
||||||
print(url + '\n' + imageurl)
|
sublime.set_timeout_async(lambda: loader.join(), TIMEOUT * 1000)
|
||||||
load_and_save_image(imageurl, callback)
|
|
||||||
|
|||||||
BIN
loading.png
Normal file
BIN
loading.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 953 B |
109
md_in_popup.py
109
md_in_popup.py
@ -1,69 +1,45 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
import sublime
|
import sublime
|
||||||
import sublime_plugin
|
import sublime_plugin
|
||||||
from . import markdown2
|
|
||||||
import os.path
|
import os.path
|
||||||
import re
|
import re
|
||||||
import base64
|
|
||||||
|
|
||||||
from .image_manager import ImageManager
|
|
||||||
|
|
||||||
from .escape_amp import *
|
|
||||||
from html.parser import HTMLParser
|
from html.parser import HTMLParser
|
||||||
|
|
||||||
|
from .lib import markdown2
|
||||||
|
from .image_manager import ImageManager
|
||||||
|
from .escape_amp import *
|
||||||
|
from .functions import *
|
||||||
|
|
||||||
# Main sublime tools function
|
# Main sublime tools function
|
||||||
|
|
||||||
def md(*t, **kwargs):
|
def plugin_loaded():
|
||||||
sublime.message_dialog(kwargs.get('sep', '\n').join([str(el) for el in t]))
|
global STYLE_FILE
|
||||||
|
STYLE_FILE = os.path.join(sublime.packages_path(), 'User', 'MarkdownLivePreview.css')
|
||||||
def sm(*t, **kwargs):
|
|
||||||
sublime.status_message(kwargs.get('sep', ' ').join([str(el) for el in t]))
|
|
||||||
|
|
||||||
def em(*t, **kwargs):
|
|
||||||
sublime.error_message(kwargs.get('sep', ' ').join([str(el) for el in t]))
|
|
||||||
|
|
||||||
def mini(val, min):
|
|
||||||
if val < min:
|
|
||||||
return min
|
|
||||||
return val
|
|
||||||
|
|
||||||
|
|
||||||
def get_content_till(string, char_to_look_for, start=0):
|
|
||||||
i = start
|
|
||||||
while i < len(string):
|
|
||||||
i += 1
|
|
||||||
if string[i] == char_to_look_for:
|
|
||||||
return string[start:i], i
|
|
||||||
|
|
||||||
def to_base64(path):
|
|
||||||
if not os.path.exists(path):
|
|
||||||
return to_base64(os.path.join(os.path.dirname(__file__), '404.png'))
|
|
||||||
|
|
||||||
with open(path, 'rb') as fp:
|
|
||||||
content = fp.read()
|
|
||||||
return 'data:image/png;base64,' + ''.join([chr(el) for el in list(base64.standard_b64encode(content))])
|
|
||||||
|
|
||||||
def replace_img_src_base64(html):
|
def replace_img_src_base64(html):
|
||||||
"""Really messy, but it works (should be updated)"""
|
"""Really messy, but it works (should be updated)"""
|
||||||
index = -1
|
index = -1
|
||||||
tag_start = '<img src="'
|
tag_start = '<img src="'
|
||||||
counter355 = 0
|
shtml, html = html, list(html)
|
||||||
shtml = html
|
|
||||||
html = list(html)
|
|
||||||
while True:
|
while True:
|
||||||
counter355 += 1
|
|
||||||
if counter355 > 100:
|
|
||||||
print("end up counter355")
|
|
||||||
return False # counter355
|
|
||||||
index = shtml.find(tag_start, index + 1)
|
index = shtml.find(tag_start, index + 1)
|
||||||
if index == -1:
|
if index == -1:
|
||||||
return ''.join(html)
|
break
|
||||||
path, end = get_content_till(html, '"', start=index + len(tag_start))
|
path, end = get_content_till(html, '"', start=index + len(tag_start))
|
||||||
html[index+len(tag_start):end] = to_base64(''.join(path))
|
if ''.join(path).startswith('data:image/'):
|
||||||
|
continue
|
||||||
|
if ''.join(path).startswith(tuple(get_settings().get('load_from_internet'
|
||||||
|
'_when_starts'))):
|
||||||
|
image = ImageManager.get(''.join(path))
|
||||||
|
image = image or to_base64('loading.png')
|
||||||
|
|
||||||
|
else:
|
||||||
|
# local image
|
||||||
|
image = to_base64(''.join(path))
|
||||||
|
html[index+len(tag_start):end] = image
|
||||||
|
shtml = ''.join(html)
|
||||||
return ''.join(html)
|
return ''.join(html)
|
||||||
|
|
||||||
|
|
||||||
STYLE_FILE = os.path.join(sublime.packages_path(), 'User', 'MarkdownLivePreview.css')
|
|
||||||
def get_style():
|
def get_style():
|
||||||
content = None
|
content = None
|
||||||
if os.path.exists(STYLE_FILE):
|
if os.path.exists(STYLE_FILE):
|
||||||
@ -142,7 +118,8 @@ def create_preview(window, md_view):
|
|||||||
focus_group, focus_view = window.get_view_index(md_view)
|
focus_group, focus_view = window.get_view_index(md_view)
|
||||||
preview = window.new_file()
|
preview = window.new_file()
|
||||||
window.run_command('new_pane') # move the preview to a new group
|
window.run_command('new_pane') # move the preview to a new group
|
||||||
preview.set_name(os.path.basename(md_view.file_name()) + ' - Preview')
|
preview.set_name(os.path.basename(md_view.file_name() or 'Untilted')
|
||||||
|
+ ' - Preview')
|
||||||
|
|
||||||
preview_settings = preview.settings()
|
preview_settings = preview.settings()
|
||||||
preview_settings.set('gutter', False)
|
preview_settings.set('gutter', False)
|
||||||
@ -190,15 +167,20 @@ def show_html(md_view, preview):
|
|||||||
vector[1] += preview.line_height()
|
vector[1] += preview.line_height()
|
||||||
preview.set_viewport_position(vector, animate=False)
|
preview.set_viewport_position(vector, animate=False)
|
||||||
|
|
||||||
def get_view_content(view):
|
class MLPDevListener(sublime_plugin.EventListener):
|
||||||
return view.substr(sublime.Region(0, view.size()))
|
|
||||||
|
|
||||||
def get_view_from_id(window, id):
|
def on_post_save(self, view):
|
||||||
for view in window.views():
|
if not (os.path.dirname(__file__) in view.file_name() and
|
||||||
if view.id() == id:
|
view.file_name().endswith('.py')):
|
||||||
return view
|
return
|
||||||
|
sublime.run_command('reload_plugin', {
|
||||||
|
'main': os.path.join(sublime.packages_path(), 'MarkdownLivePreview',
|
||||||
|
'md_in_popup.py'),
|
||||||
|
'scripts': ['image_manager', 'functions'],
|
||||||
|
'quiet': True
|
||||||
|
})
|
||||||
|
|
||||||
class MarkdownInPopupCommand(sublime_plugin.EventListener):
|
class MarkdownLivePReviewListener(sublime_plugin.EventListener):
|
||||||
|
|
||||||
def on_load(self, view):
|
def on_load(self, view):
|
||||||
settings = view.settings()
|
settings = view.settings()
|
||||||
@ -248,22 +230,3 @@ class MarkdownInPopupCommand(sublime_plugin.EventListener):
|
|||||||
md_view_settings.erase('markdown_preview_enabled')
|
md_view_settings.erase('markdown_preview_enabled')
|
||||||
md_view_settings.erase('markdown_preview_id')
|
md_view_settings.erase('markdown_preview_id')
|
||||||
sublime.set_timeout_async(callback, 250)
|
sublime.set_timeout_async(callback, 250)
|
||||||
|
|
||||||
class LoadImageCommand(sublime_plugin.ApplicationCommand):
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
ImageManager.get('https://images.duckduckgo.com/iu/?u=http%3A%2F%2Fi82.photobucket.com%2Falbums%2Fj261%2FOrestesElVerdadero%2Favatar-fenix-100.jpg&f=1',
|
|
||||||
lambda content: print('got content', len(content)))
|
|
||||||
|
|
||||||
class MLPDevListener(sublime_plugin.EventListener):
|
|
||||||
|
|
||||||
def on_post_save(self, view):
|
|
||||||
if not (os.path.dirname(__file__) in view.file_name() and
|
|
||||||
view.file_name().endswith('.py')):
|
|
||||||
return
|
|
||||||
sublime.run_command('reload_plugin', {
|
|
||||||
'main': os.path.join(sublime.packages_path(), 'MarkdownLivePreview',
|
|
||||||
'md_in_popup.py'),
|
|
||||||
'scripts': ['image_manager'],
|
|
||||||
'quiet': True
|
|
||||||
})
|
|
||||||
|
|||||||
Reference in New Issue
Block a user