image support

This commit is contained in:
Damien Elmes 2016-07-07 23:39:48 +10:00
parent 7d0eb6dd39
commit a8e2f992c8
7 changed files with 83 additions and 18 deletions

View file

@ -15,7 +15,7 @@ import aqt.forms
from anki.utils import fmtTimeSpan, ids2str, stripHTMLMedia, isWin, intTime, isMac from anki.utils import fmtTimeSpan, ids2str, stripHTMLMedia, isWin, intTime, isMac
from aqt.utils import saveGeom, restoreGeom, saveSplitter, restoreSplitter, \ from aqt.utils import saveGeom, restoreGeom, saveSplitter, restoreSplitter, \
saveHeader, restoreHeader, saveState, restoreState, applyStyles, getTag, \ saveHeader, restoreHeader, saveState, restoreState, applyStyles, getTag, \
showInfo, askUser, tooltip, openHelp, showWarning, shortcut, getBase, mungeQA showInfo, askUser, tooltip, openHelp, showWarning, shortcut, mungeQA
from anki.hooks import runHook, addHook, remHook from anki.hooks import runHook, addHook, remHook
from aqt.webview import AnkiWebView from aqt.webview import AnkiWebView
from aqt.toolbar import Toolbar from aqt.toolbar import Toolbar
@ -1089,7 +1089,7 @@ where id in %s""" % ids2str(sf))
txt = c.a() txt = c.a()
txt = re.sub("\[\[type:[^]]+\]\]", "", txt) txt = re.sub("\[\[type:[^]]+\]\]", "", txt)
ti = lambda x: x ti = lambda x: x
base = getBase(self.mw.col) base = self.mw.baseHTML()
self._previewWeb.stdHtml( self._previewWeb.stdHtml(
ti(mungeQA(self.col, txt)), self.mw.reviewer._styles(), ti(mungeQA(self.col, txt)), self.mw.reviewer._styles(),
bodyClass="card card%d" % (c.ord+1), head=base, bodyClass="card card%d" % (c.ord+1), head=base,

View file

@ -8,7 +8,7 @@ from aqt.qt import *
from anki.consts import * from anki.consts import *
import aqt import aqt
from anki.sound import playFromText, clearAudioQueue from anki.sound import playFromText, clearAudioQueue
from aqt.utils import saveGeom, restoreGeom, getBase, mungeQA,\ from aqt.utils import saveGeom, restoreGeom, mungeQA,\
showInfo, askUser, getOnlyText, \ showInfo, askUser, getOnlyText, \
showWarning, openHelp, downArrow showWarning, openHelp, downArrow
from anki.utils import isMac, isWin, joinFields from anki.utils import isMac, isWin, joinFields
@ -225,7 +225,7 @@ Please create a new card type first."""))
def renderPreview(self): def renderPreview(self):
c = self.card c = self.card
ti = self.maybeTextInput ti = self.maybeTextInput
base = getBase(self.mw.col) base = self.mw.baseHTML()
self.tab['pform'].frontWeb.stdHtml( self.tab['pform'].frontWeb.stdHtml(
ti(mungeQA(self.mw.col, c.q(reload=True))), self.mw.reviewer._styles(), ti(mungeQA(self.mw.col, c.q(reload=True))), self.mw.reviewer._styles(),
bodyClass="card card%d" % (c.ord+1), head=base), bodyClass="card card%d" % (c.ord+1), head=base),

View file

@ -14,7 +14,7 @@ import anki.sound
from anki.hooks import runHook, runFilter from anki.hooks import runHook, runFilter
from aqt.sound import getAudio from aqt.sound import getAudio
from aqt.webview import AnkiWebView from aqt.webview import AnkiWebView
from aqt.utils import shortcut, showInfo, showWarning, getBase, getFile, \ from aqt.utils import shortcut, showInfo, showWarning, getFile, \
openHelp, tooltip, downArrow openHelp, tooltip, downArrow
import aqt import aqt
import anki.js import anki.js
@ -370,7 +370,7 @@ class Editor(object):
</div> </div>
""" % dict(flds=_("Fields"), cards=_("Cards")) """ % dict(flds=_("Fields"), cards=_("Cards"))
self.web.stdHtml(_html % ( self.web.stdHtml(_html % (
getBase(self.mw.col), anki.js.jquery, self.mw.baseHTML(), anki.js.jquery,
topbuts, topbuts,
_("Show Duplicates"))) _("Show Duplicates")))

View file

@ -18,6 +18,7 @@ import aqt.progress
import aqt.webview import aqt.webview
import aqt.toolbar import aqt.toolbar
import aqt.stats import aqt.stats
import aqt.mediasrv
from aqt.utils import saveGeom, restoreGeom, showInfo, showWarning, \ from aqt.utils import saveGeom, restoreGeom, showInfo, showWarning, \
restoreState, getOnlyText, askUser, applyStyles, showText, tooltip, \ restoreState, getOnlyText, askUser, applyStyles, showText, tooltip, \
openHelp, openLink, checkInvalidFilename openHelp, openLink, checkInvalidFilename
@ -79,6 +80,7 @@ class AnkiQt(QMainWindow):
self.setupHooks() self.setupHooks()
self.setupRefreshTimer() self.setupRefreshTimer()
self.updateTitleBar() self.updateTitleBar()
self.setupMediaServer()
# screens # screens
self.setupDeckBrowser() self.setupDeckBrowser()
self.setupOverview() self.setupOverview()
@ -1157,3 +1159,14 @@ Please ensure a profile is open and Anki is not busy, then try again."""),
def _onCollect(self): def _onCollect(self):
gc.collect() gc.collect()
# Media server
##########################################################################
# prevent malicious decks from accessing the local filesystem
def setupMediaServer(self):
self.mediaServer = aqt.mediasrv.MediaServer()
self.mediaServer.start()
def baseHTML(self):
return '<base href="http://localhost:%d/">' % self.mediaServer.port

58
aqt/mediasrv.py Normal file
View file

@ -0,0 +1,58 @@
# Copyright: Damien Elmes <anki@ichi2.net>
# -*- coding: utf-8 -*-
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from aqt.qt import *
from http import HTTPStatus
import http.server
import errno
class MediaServer(QThread):
def run(self):
self.port = 8080
self.server = None
while not self.server:
try:
self.server = http.server.HTTPServer(
("localhost", self.port), RequestHandler)
except OSError as e:
if e.errno == errno.EADDRINUSE:
self.port += 1
continue
raise
break
self.server.serve_forever()
class RequestHandler(http.server.SimpleHTTPRequestHandler):
def send_head(self):
path = self.translate_path(self.path)
if os.path.isdir(path):
self.send_error(HTTPStatus.NOT_FOUND, "File not found")
return None
ctype = self.guess_type(path)
try:
f = open(path, 'rb')
except OSError:
self.send_error(HTTPStatus.NOT_FOUND, "File not found")
return None
try:
self.send_response(HTTPStatus.OK)
self.send_header("Content-type", ctype)
fs = os.fstat(f.fileno())
self.send_header("Content-Length", str(fs[6]))
self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
self.end_headers()
return f
except:
f.close()
raise
def log_message(self, format, *args):
if not os.getenv("ANKIDEV"):
return
print("%s - - [%s] %s" %
(self.address_string(),
self.log_date_time_string(),
format%args))

View file

@ -10,10 +10,10 @@ import html.parser
from anki.lang import _, ngettext from anki.lang import _, ngettext
from aqt.qt import * from aqt.qt import *
from anki.utils import stripHTML, isMac, json from anki.utils import stripHTML, json
from anki.hooks import addHook, runHook from anki.hooks import addHook, runHook
from anki.sound import playFromText, clearAudioQueue, play from anki.sound import playFromText, clearAudioQueue, play
from aqt.utils import mungeQA, getBase, openLink, tooltip, askUserDialog, \ from aqt.utils import mungeQA, tooltip, askUserDialog, \
downArrow downArrow
from aqt.sound import getAudio from aqt.sound import getAudio
import aqt import aqt
@ -166,7 +166,7 @@ function _typeAnsPress() {
def _initWeb(self): def _initWeb(self):
self._reps = 0 self._reps = 0
self._bottomReady = False self._bottomReady = False
base = getBase(self.mw.col) base = self.mw.baseHTML()
# main window # main window
self.web.onLoadFinished = self._showQuestion self.web.onLoadFinished = self._showQuestion
self.web.stdHtml(self._revHtml, self._styles(), head=base) self.web.stdHtml(self._revHtml, self._styles(), head=base)

View file

@ -338,16 +338,10 @@ def applyStyles(widget):
if os.path.exists(p): if os.path.exists(p):
widget.setStyleSheet(open(p).read()) widget.setStyleSheet(open(p).read())
# this will go away in the future - please use mw.baseHTML() instead
def getBase(col): def getBase(col):
base = None from aqt import mw
mdir = col.media.dir() return mw.baseHTML()
if isWin and not mdir.startswith("\\\\"):
prefix = "file:///"
else:
prefix = "file://"
mdir = mdir.replace("\\", "/")
base = prefix + urllib.parse.quote(mdir) + "/"
return '<base href="%s">' % base
def openFolder(path): def openFolder(path):
if isWin: if isWin: