wepub

Artifact Content
Login

Artifact 69ca8b8dea3597f13e2d8f1a6e275c3a90646b9fdc17f9a0cba3d36d005a027a:


#
# get.py
#	Mixin to implement HTML GET operations
#
# Structure of paths in the server:
#  /
#	Main UI.  Top part is playlist, bottom is file/dir browser
#  /media/<prefix>/...
#	For each prefix of files, its contents is served by
#	way of its path under here.
import os, time, stat, urllib, json
import epub
import chore

# How many of their most recent books to offer
NRECENT = 4

# The GET part of our handling
class GET_mixin(object):

    # Configure our WPlayer GET treatment
    def __init__(self):

	# GET handlers
	self.dispatchers.append( ("GET", self.open_path) )
	self.dispatchers.append( ("GET", self.get_state) )

    # Return HTML enumeration of recent documents
    def list_recent(self):
        global NRECENT

        res = ""

        # Sweep var/state/<user> and get most recent files
        newest = []
        for path,dirs,files in os.walk("var/state/%s" % (self.user,)):
            # There shouldn't be subdirs, but just in case...
            del dirs[:]

            # Process saved files w. position state
            for f in files:
                # Path to saved state
                fn = path + '/' + f

                # Get contents and modified time
                fd = open(fn, "r")
                buf = fd.read()
                d = json.loads(buf)
                url = d["url"]
                st = os.fstat(fd.fileno())
                fd.close()
                ftm = st.st_mtime

                # Assemble this entry
                newest.append( (url, ftm) )

        # Just list of URL's, newest first
        newest.sort(key=lambda tup: tup[1])
        newest = newest[-NRECENT:]
        newest.reverse()

        # Add <a> href's to these files
        res += '<table>\n'
        for f,ftm in newest:
            # Make URL a local ref, so it can straddle HTTP/HTTPS
            #  and internal versus extenal port numbering.
            url = '/' + ('/'.join(f.split('/')[3:]))

            # Extract title of book
            parts = f.split('/')
            title = urllib.unquote_plus(parts[-2])
            secnum = int(parts[-1])

            # When they read it
            tm = time.localtime(ftm)
            tms = time.strftime("%m/%d %H:%M", tm)

            # Add table entry
            res += ' <tr>\n'
            res += '  <td>%s</td>\n' % (tms,)
            res += '  <td><a href="%s">%s (%d)</a></td>\n' % \
                (url, title, secnum)
            res += ' </tr>\n'
        res += '</table>\n'
        return res

    # "/"; main UI
    def send_top(self):
	app = self.server
	webroot = app.approot
        buf = self.build_header("EPUB categories")

        # Recent readings
        buf += self.list_recent()

        # Provide a list of top-level document categories
        buf += "<ul>\n"
	for f,cfg in webroot.config["files"]:
            buf += ' <li><a href="/%s/">%s</a></li>\n' % (f,f)
        buf += "</ul>\n"

        buf = self.build_tailer(buf)

        return self.send_result(buf, "text/html")

    # Access something along a path
    # It can be an actual epub file, or a path to a directory
    #  where we'll list what's available
    def open_path(self):
	app = self.server
	webroot = app.approot
        for f,cfg in webroot.config["files"]:
            section = f
            f = '/%s/' % (f,)
            if self.path.startswith(f):
                break
        else:
            # Not a known path
            return False,None

        # We have the "cfg" for this prefix, point at the
        #  root of those files
        path = cfg["path"]
        rest = self.path.replace(f, '', 1)

        # Deflect nonsense
        parts = [urllib.unquote(p) for p in os.path.split(rest) if p]
        if any((f == "..") for f in parts):
            return False,None

        # Is this an active book?
        # /prefix/...path.../<book>/<chap#>
        if (len(parts) > 1) and parts[-2].endswith(".epub"):
            try:
                chapnum = int(parts[-1])
            except:
                return False,None
            if chapnum <= 0:
                return False,None
	    chapnum -= 1

            # Access the book file
            try:
                docpath = os.path.join(path, *(parts[:-1]))
                doc = epub.open_epub(docpath, "r")
                book = epub.Book(doc)
                chap = book.chapters[chapnum]
            except:
                return False,None

            # Here's your chapter
            nm = book.titles[0] if book.titles else parts[-2]
            head = "%s chapter %d" % (nm, chapnum)
            buf = self.build_header(head)
            buf += '<script src="/js/reader.js"></script>\n'
            if chapnum > 0:
                buf += '<a href="%d">Previous Chapter</a>\n' % \
                 (chapnum,)
	    buf += '<link rel="stylesheet" type="text/css"'
	    buf += ' href="/css/epub.css" />\n'
            buf += '<div id="textview"\n'
            buf += '  style="color: white; background: black;'
            buf += '  width: 100%; font-size: 14px;">\n'
            buf += chore.utils.uncharenc(chap.read())
            buf += '</div>\n'
            if chapnum < len(book.chapters)-1:
                buf += '<a href="%d">Next Chapter</a>\n' % \
                 (chapnum+1,)
            doc.close()
            buf += '<script>reading("%s", "%s", "%s");</script>\n' % \
                (self.user, docpath, "textview")
            buf = self.build_tailer(buf)

            return True,self.send_result(buf, "text/html")

        # Index of book?
        if parts and parts[-1].endswith(".epub"):
            try:
                doc = epub.open_epub(
                 os.path.join(path, *parts), "r")
                book = epub.Book(doc)
            except:
                return False,None
            buf = self.build_header("<h2>Chapters</h2><p>\n")
            buf += "<ul>\n"
            for chnum in xrange(len(book.chapters)):
                chnum += 1
                tpath = [section] + parts + [ "%d" % (chnum,) ]
                tpath = os.path.join(*tpath)
                buf += ' <li><a href="/%s">%d</a></li>\n' % \
                    (tpath, chnum)
            buf += "</ul>\n"

            # Your index
            buf = self.build_tailer(buf)
            return True,self.send_result(buf, "text/html")

        # List a folder/directory
        dirpath = os.path.join(path, *parts)
        try:
            st = os.stat(dirpath)
        except:
            return False,None
        if not stat.S_ISDIR(st.st_mode):
            return False,None

        # Listing; files, then sub-folders
        books = []
        dirs = []
        for path,dirs,files in os.walk(dirpath):
            for f in files:
                if f.endswith(".epub"):
                    books.append(f)
            break
        if (not books) and (not dirs):
            return False,None
        label = parts[-1] if parts else section
        buf = self.build_header("<h2>%s</h2><p>\n" % (label,))

        # List book files (if any)
        if books:
            buf += "<p><h3>Books</h3>\n<ul>\n"
            for f in sorted(books):
                buf += ' <li><a href="%s">%s</a></li>\n' % \
                    (os.path.join(self.path, f), f[:-5])
            buf += "</ul>\n"

        # List sub-folders (if any)
        if dirs:
            buf += "<p><h3>Sub-Folders</h3>\n<ul>\n"
            for d in sorted(dirs):
                buf += ' <li><a href="%s">%s</a></li>\n' % \
                    (os.path.join(self.path, d), d)
            buf += "</ul>\n"

        buf = self.build_tailer(buf)
        return True,self.send_result(buf, "text/html")

    # /state.json?doc=X
    # User's saved state for reading the document with path X
    def get_state(self):
        if not self.path_match("state.json"):
            return False,None
        doc = self.vals.get("doc")
        if doc is None:
            return False,None

        # Get a filesystem safe name, see if we can get the info
        try:
            doc = urllib.quote_plus(urllib.unquote_plus(doc))
            f = open("var/state/%s/%s.json" % (self.user, doc), "r")
            buf = f.read()
            f.close()
        except:
            return False,None

        return True,self.send_result(buf, "application/json")