mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
image support
This commit is contained in:
parent
7d0eb6dd39
commit
a8e2f992c8
7 changed files with 83 additions and 18 deletions
|
@ -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,
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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")))
|
||||||
|
|
||||||
|
|
13
aqt/main.py
13
aqt/main.py
|
@ -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
58
aqt/mediasrv.py
Normal 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))
|
|
@ -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)
|
||||||
|
|
12
aqt/utils.py
12
aqt/utils.py
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue