1
0
Fork 0
mirror of https://github.com/evilhero/mylar synced 2024-12-25 01:01:47 +00:00

IMP: WebViewer 1.0

This commit is contained in:
Barbeque Sauce 2019-12-31 10:24:20 -05:00 committed by evilhero
parent 85f295976d
commit a834df2179
8 changed files with 1924 additions and 0 deletions

330
data/css/webviewerstyle.css Normal file
View file

@ -0,0 +1,330 @@
html {
}
header,
main,
footer {
background: #757575;
}
body {
color: #FFFFFF;
background: #757575;
}
#breadcrumbs {
top: 0;
background: #BDBDBD;
}
#comic-info {
display: inline;
}
#dash_dashboard {
padding-left: 8px;
display: inline-block;
}
#dash_library {
padding-left: 8px;
display: inline-block;
}
#dash_settings {
padding-left: 8px;
display: inline-block;
}
#directory-card-content {
display:flex;
color: #FFFFFF;
background-color: #BDBDBD;
text-position: center;
}
#footer {
background: #757575;
}
#theme-settings .dropdown-content li>a, .dropdown-content li>span, .select-dropdown li.disabled, .select-dropdown li.disabled>span, .select-dropdown li.optgroup {
background: #757575;
color: #FFFFFF;
}
#log-modal {
background: #BDBDBD;
color: #FFFFFF;
width: 100%;
height: 100%;
}
#logo-wrapper {
background: #757575;
}
i {
color: #FFFFFF;
}
#nav-dropdown {
background-color: #BDBDBD;
}
#nav-dropdown a {
color: #FFFFFF;
}
#page-settings-text {
color: #FFFFFF;
}
#page-settings-right-text i {
color: #FFFFFF;
}
#page-settings-left-text i {
color: #FFFFFF;
}
#pagination-num a {
color: #FFFFFF;
}
#pagination-num.active {
background-color: #BDBDBD;
}
#read-link {
color: #FFFFFF;
}
#search-modal {
background: #BDBDBD;
color: #FFFFFF;
}
#search-modal input {
margin: 0 auto;
}
#settings-arrow {
color: #BDBDBD;
}
#settings-button {
background: #BDBDBD;
color: #FFFFFF;
}
#settings-help {
position: absolute;
z-index:5000;
color: #FFFFFF;
background: #BDBDBD;
}
#size-height-button {
background: #BDBDBD;
color: #FFFFFF;
}
#size-width-button {
background: #BDBDBD;
color: #FFFFFF;
}
#size-normal-button {
background: #BDBDBD;
color: #FFFFFF;
}
#summary-pane {
background: #BDBDBD;
color: #FFFFFF;
}
#tab-row {
background: #757575;
color: #FFFFFF;
}
.btn-flat {
background: #BDBDBD;
color: #FFFFFF;
}
.card {
position: relative;
background-color: #BDBDBD;
box-shadow: 1px 1px 10px 1px rgba(0, 0, 0, 0.7);
}
.card-image {
position: relative;
height: 350px;
overflow: hidden;
}
.card-image img {
position: absolute;
height: 100%;
width: 100%;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.card-content {
height: auto;
background: #BDBDBD;
color: #FFFFFF;
text-align: center;
}
.card-content a {
color: #FFFFFF;
}
.center-cols > .col {
float:none; /* disable the float */
display: inline-block; /* make blocks inline-block */
text-align: initial; /* restore text-align to default */
vertical-align: top;
}
.dimmed {
background-color: rgba(0, 0, 0, 0.7);
}
.dropdown-button {
background-color: #BDBDBD;
color: #FFFFFF;
}
.input-field {
color: #FFFFFF;
}
.input-field label {
color: #FFFFFF;
}
.input-field input[type=text]:focus + label {
color: #FFFFFF;
}
.input-field input[type=text]:focus {
border-bottom: 1px solid #FFFFFF;
box-shadow: 0 1px 0 0 #FFFFFF;
}
.input-field input[type=password]:focus + label {
color: #FFFFFF;
}
.input-field input[type=password]:focus {
border-bottom: 1px solid #FFFFFF;
box-shadow: 0 1px 0 0 #FFFFFF;
}
.nav-wrapper .input-field input[type="search"] {
height: auto;
color: #FFFFFF;
background: #BDBDBD;
}
.nav-wrapper .input-field input[type="search"]:focus {
background: #BDBDBD;
color: #FFFFFF;
box-shadow: 0 1px 0 0 #BDBDBD;
}
.nav-wrapper .input-field input[type="search"]:focus ~ .material-icons.icon-close {
right: 2rem;
}
.nav-wrapper .dropdown-button {
position:absolute;
left: 0;
top: 0;
}
.overlay-settings {
position: fixed;
width: 100%;
background-color: rgba(0, 0, 0, 0);
color: #000;
z-index:4000;
min-height: 1px;
}
.overlay-settings-text {
color: #FFFFFF;
height: 900px;
line-height: 900px;
text-align: center;
}
.page-left {
position: fixed;
left: 0px;
top: 0px;
height: 100%;
width: 33.33%;
}
.page-settings {
position: fixed;
left: 33.33%;
top: 0px;
height: 100%;
width: 33.33%;
}
.page-right {
position: fixed;
right: 0px;
top: 0px;
height: 100%;
width: 33.33%;
}
.reader-overlay {
position: absolute;
background-color:#757575;
width:100%;
height:100%;
top:0px;
left:0px;
z-index:1000;
max-width:100%;
}
.reader-page-wide {
width:100%;
}
.reader-page-high {
position: absolute;
margin: auto;
top: 0px;
bottom: 0px;
left: 0;
right: 0;
height: 100%;
max-width:100%;
}
.reader-page-norm {
position: absolute;
margin-left: auto;
margin-right: auto;
top: 0px;
bottom: 0px;
left: 0;
right: 0;
max-width:100%;
}
.settings-span-left {
display: inline-block;
left: 0px;
vertical-align: middle;
line-height: normal;
}
.settings-span-center {
display: inline-block;
text-align: center;
vertical-align: middle;
line-height: normal;
}
.settings-span-right {
display: inline-block;
right: 0px;
vertical-align: middle;
line-height: normal;
}
.tabs .tab a {
background: #757575;
color: #FFFFFF;
}
.tabs .tab a:hover {
color: #BDBDBD;
}
.tabs .indicator {
background-color: #FFFFFF;
}
@media only screen and (min-width : 601px) and (max-width : 1260px) {
.toast {
border-radius: 0;
}
}
@media only screen and (min-width : 1261px) {
.toast {
border-radius: 0;
}
}
@media only screen and (min-width : 601px) and (max-width : 1260px) {
#toast-container {
bottom: 0;
top: 90%;
right: 50%;
transform: translate(50%, 0);
white-space: nowrap;
}
}
@media only screen and (min-width : 1261px) {
#toast-container {
bottom: 0;
top: 90%;
right: 50%;
transform: translate(50%, 0);
white-space: nowrap;
}
}

View file

@ -431,6 +431,7 @@
%> %>
%if linky: %if linky:
<a href="downloadthis?pathfile=${linky |u}"><img src="interfaces/default/images/download_icon.png" height="25" width="25" title="Download the Issue" class="highqual" /></a> <a href="downloadthis?pathfile=${linky |u}"><img src="interfaces/default/images/download_icon.png" height="25" width="25" title="Download the Issue" class="highqual" /></a>
<a href="read_comic?ish_id=${issue['IssueID']}&page_num=0&size=high"><img src="interfaces/default/images/readabook.png" height="25" width="25" title="Read the Issue in your Browser" class="highqual" /></a>
%if linky.endswith('.cbz'): %if linky.endswith('.cbz'):
<a href="#issue-box" onclick="return runMetaIssue('${linky |u}', '${comic['ComicName']| u}', '${issue['Issue_Number']}', '${issue['IssueDate']}', '${issue['IssueName'] |u}');" class="issue-window"><img src="interfaces/default/images/issueinfo.png" height="25" width="25" title="View Issue Details" class="highqual" /></a> <a href="#issue-box" onclick="return runMetaIssue('${linky |u}', '${comic['ComicName']| u}', '${issue['Issue_Number']}', '${issue['IssueDate']}', '${issue['IssueName'] |u}');" class="issue-window"><img src="interfaces/default/images/issueinfo.png" height="25" width="25" title="View Issue Details" class="highqual" /></a>
<div id="issue-box" class="issue-popup"> <div id="issue-box" class="issue-popup">

View file

@ -0,0 +1,16 @@
<%page args="jscolor=False"/>
<head>
<title>Mylar WebViewer</title>
<meta name="description" content="Mylar Web Viewer">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/css/materialize.min.css">
<link rel="stylesheet" type="text/css" href="css/webviewerstyle.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/js/materialize.min.js"></script>
% if jscolor is True:
<script src="js/jscolor.min.js"></script>
% endif
</head>

View file

@ -0,0 +1,114 @@
<%!
import mylar
%>
<%
now_page = pages[current_page]
%>
<!doctype html>
<html class="whole-page">
<%include file="header.html" />
<body class="inner-page">
<div class="reader-whole">
% if (current_page + 1) == 1:
<a class="btn btn-floating btn-large pulse tooltipped" id="settings-help" data-position="bottom" data-delay="50" data-tooltip="Click the center to bring up settings. Click the left and right sides of the page to change pages."><i class="material-icons">help_outline</i></a>
% endif
<div class="col s12 overlay-settings">
<div class="overlay-settings-text">
<div class="page-left">
<span class="settings-span-left">
<h4 style="display: none;" id="page-settings-left-text">
<i class="large material-icons" id="settings-arrow">arrow_back</i>
</h4>
</span>
</div>
<div class="page-settings">
<span class="settings-span-center">
<p style="display: none;" id="page-settings-text">
<a class="waves-effect waves-light btn-large" id="settings-button" href="index">Home</a>
</br>
</br>
<b>On Page ${current_page + 1} of ${nop} Pages</b>
</br>
</br>
<a class="waves-effect waves-light btn-large" id="settings-button" action="action" value="Back" onclick="window.history.go(-1); return false">Close Book</a>
</br>
</br>
<b>Fit Comic to Height/Width/No Fit</b>
</br>
</br>
<a class="waves-effect waves-light btn-large" id="size-width-button" href="#!">Width</a>
<a class="waves-effect waves-light btn-large" id="size-height-button" href="#!">Height</a>
<a class="waves-effect waves-light btn-large" id="size-normal-button" href="#!">No Fit</a>
</p>
</span>
</div>
<div class="page-right">
<span class="settings-span-right">
<h4 style="display: none;" id="page-settings-right-text"><i class="large material-icons" id="settings-arrow">arrow_forward</i></h4>
</span>
</div>
</div>
</div>
<div class="row reader-overlay">
% if size == "wide":
<img class="reader-page-wide" src="${now_page}"/>
% elif size == "high":
<img class="reader-page-high" src="${now_page}"/>
% elif size == "norm":
<img class="reader-page-norm" src="${now_page}"/>
% else:
<img class="reader-page-wide" src="${now_page}"/>
% endif:
</div>
</div>
</body>
<script type="text/javascript">
$(document).ready(function() {
$('.modal-trigger').leanModal();
$(".page-settings").click(function() {
$(".page-settings").toggleClass( "dimmed" );
$(".page-left").toggleClass( "dimmed" );
$(".page-right").toggleClass( "dimmed" );
$("#page-settings-text").toggle();
$("#page-settings-left-text").toggle();
$("#page-settings-right-text").toggle();
});
$(".page-right").click(function() {
$(".whole-page").load("${mylar.CONFIG.HTTP_ROOT}read_comic?ish_id=${ish_id}&page_num=${np}&size=${size}");
});
$(".page-left").click(function() {
$(".whole-page").load("${mylar.CONFIG.HTTP_ROOT}read_comic?ish_id=${ish_id}&page_num=${pp}&size=${size}");
});
$(document).one("keyup", function(e) {
if (e.which == 39) {
$(".whole-page").load("${mylar.CONFIG.HTTP_ROOT}read_comic?ish_id=${ish_id}&page_num=${np}&size=${size}");
}
});
$(document).one("keyup", function(e) {
if (e.which == 37) {
$(".whole-page").load("${mylar.CONFIG.HTTP_ROOT}read_comic?ish_id=${ish_id}&page_num=${pp}&size=${size}");
}
});
$("#size-width-button").click(function() {
$(".reader-overlay").html("<img class='reader-page-wide' src='${now_page}'/>");
// $.ajax({
// url: "up_size_pref?pref=wide"
// });
});
$("#size-height-button").click(function() {
$(".reader-overlay").html("<img class='reader-page-high' src='${now_page}'/>");
// $.ajax({
// url: "up_size_pref?pref=high"
// });
});
$("#size-normal-button").click(function() {
$(".reader-overlay").html("<img class='reader-page-norm' src='${now_page}'/>");
// $.ajax({
// url: "up_size_pref?pref=norm"
// });
});
});
</script>
</html>

10
data/js/jscolor.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1279
lib/pathlib.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -6426,3 +6426,14 @@ class WebInterface(object):
download_specific_release.exposed = True download_specific_release.exposed = True
def read_comic(self, ish_id, page_num, size):
from mylar.webviewer import WebViewer
wv = WebViewer()
page_num = int(page_num)
#cherrypy.session['ishid'] = ish_id
data = wv.read_comic(ish_id, page_num, size)
#data = wv.read_comic(ish_id)
return data
read_comic.exposed = True

163
mylar/webviewer.py Normal file
View file

@ -0,0 +1,163 @@
import os
import re
import cherrypy
import stat
import zipfile
from lib.rarfile import rarfile
import mylar
from PIL import Image
from mylar import logger, db, importer, mb, search, filechecker, helpers, updater, parseit, weeklypull, librarysync, moveit, Failed, readinglist, config
from mylar.webserve import serve_template
class WebViewer(object):
def __init__(self):
self.ish_id = None
self.page_num = None
self.kwargs = None
self.data = None
if not os.path.exists(os.path.join(mylar.DATA_DIR, 'sessions')):
os.makedirs(os.path.abspath(os.path.join(mylar.DATA_DIR, 'sessions')))
updatecherrypyconf = {
'tools.gzip.on': True,
'tools.gzip.mime_types': ['text/*', 'application/*', 'image/*'],
'tools.sessions.timeout': 1440,
'tools.sessions.storage_class': cherrypy.lib.sessions.FileSession,
'tools.sessions.storage_path': os.path.join(mylar.DATA_DIR, "sessions"),
'request.show_tracebacks': False,
}
if mylar.CONFIG.HTTP_PASSWORD is None:
updatecherrypyconf.update({
'tools.sessions.on': True,
})
cherrypy.config.update(updatecherrypyconf)
cherrypy.engine.signals.subscribe()
cherrypy.engine.timeout_monitor.unsubscribe()
def read_comic(self, ish_id = None, page_num = None, size = None):
logger.debug("WebReader Requested, looking for ish_id %s and page_num %s" % (ish_id, page_num))
if size == None:
user_size_pref = 'wide'
else:
user_size_pref = size
try:
ish_id
except:
logger.warn("WebReader: ish_id not set!")
myDB = db.DBConnection()
comic = myDB.selectone('select comics.ComicLocation, issues.Location from comics, issues where comics.comicid = issues.comicid and issues.issueid = ?' , [ish_id]).fetchone()
if comic is None:
logger.warn("WebReader: ish_id %s requested but not in the database!" % ish_id)
raise cherrypy.HTTPRedirect("home")
# cherrypy.config.update()
comic_path = os.path.join(comic['ComicLocation'], comic['Location'])
logger.debug("WebReader found ish_id %s at %s" % (ish_id, comic_path))
# cherrypy.session['ish_id'].load()
# if 'sizepref' not in cherrypy.session:
# cherrypy.session['sizepref'] = user_size_pref
# user_size_pref = cherrypy.session['sizepref']
# logger.debug("WebReader setting user_size_pref to %s" % user_size_pref)
scanner = ComicScanner()
image_list = scanner.reading_images(ish_id)
logger.debug("Image list contains %s pages" % (len(image_list)))
if len(image_list) == 0:
logger.debug("Unpacking ish_id %s from comic_path %s" % (ish_id, comic_path))
scanner.user_unpack_comic(ish_id, comic_path)
else:
logger.debug("ish_id %s already unpacked." % ish_id)
num_pages = len(image_list)
logger.debug("Found %s pages for ish_id %s from comic_path %s" % (num_pages, ish_id, comic_path))
if num_pages == 0:
image_list = ['images/skipped_icon.png']
cookie_comic = re.sub(r'\W+', '', comic_path)
cookie_comic = "wv_" + cookie_comic.decode('unicode_escape')
logger.debug("about to drop a cookie for " + cookie_comic + " which represents " + comic_path)
cookie_check = cherrypy.request.cookie
if cookie_comic not in cookie_check:
logger.debug("Cookie Creation")
cookie_path = '/'
cookie_maxage = '2419200'
cookie_set = cherrypy.response.cookie
cookie_set['cookie_comic'] = 0
cookie_set['cookie_comic']['path'] = cookie_path.decode('unicode_escape')
cookie_set['cookie_comic']['max-age'] = cookie_maxage.decode('unicode_escape')
next_page = page_num + 1
prev_page = page_num - 1
else:
logger.debug("Cookie Read")
page_num = int(cherrypy.request.cookie['cookie_comic'].value)
logger.debug("Cookie Set To %d" % page_num)
next_page = page_num + 1
prev_page = page_num - 1
logger.info("Reader Served")
logger.debug("Serving comic " + comic['Location'] + " page number " + str(page_num))
return serve_template(templatename="read.html", pages=image_list, current_page=page_num, np=next_page, pp=prev_page, nop=num_pages, size=user_size_pref, cc=cookie_comic, comicpath=comic_path, ish_id=ish_id)
def up_size_pref(self, pref):
cherrypy.session.load()
cherrypy.session['sizepref'] = pref
cherrypy.session.save()
return
class ComicScanner(object):
# This method will handle scanning the directories and returning a list of them all.
def dir_scan(self):
logger.debug("Dir Scan Requested")
full_paths = []
full_paths.append(mylar.CONFIG.DESTINATION_DIR)
for root, dirs, files in os.walk(mylar.CONFIG.DESTINATION_DIR):
full_paths.extend(os.path.join(root, d) for d in dirs)
logger.info("Dir Scan Completed")
logger.info("%i Dirs Found" % (len(full_paths)))
return full_paths
def user_unpack_comic(self, ish_id, comic_path):
logger.info("%s unpack requested" % comic_path)
for root, dirs, files in os.walk(os.path.join(mylar.CONFIG.CACHE_DIR, "webviewer", ish_id), topdown=False):
for f in files:
os.chmod(os.path.join(root, f), stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) # 0777
os.remove(os.path.join(root, f))
for root, dirs, files in os.walk(os.path.join(mylar.CONFIG.CACHE_DIR, "webviewer", ish_id), topdown=False):
for d in dirs:
os.chmod(os.path.join(root, d), stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) # 0777
os.rmdir(os.path.join(root, d))
if comic_path.endswith(".cbr"):
opened_rar = rarfile.RarFile(comic_path)
opened_rar.extractall(os.path.join(mylar.CONFIG.CACHE_DIR, "webviewer", ish_id))
elif comic_path.endswith(".cbz"):
opened_zip = zipfile.ZipFile(comic_path)
opened_zip.extractall(os.path.join(mylar.CONFIG.CACHE_DIR, "webviewer", ish_id))
return
# This method will return a list of .jpg files in their numberical order to be fed into the reading view.
def reading_images(self, ish_id):
logger.debug("Image List Requested")
image_list = []
image_src = os.path.join(mylar.CONFIG.CACHE_DIR, "webviewer", ish_id)
image_loc = os.path.join(mylar.CONFIG.HTTP_ROOT, 'cache', "webviewer", ish_id)
for root, dirs, files in os.walk(image_src):
for f in files:
if f.endswith((".png", ".gif", ".bmp", ".dib", ".jpg", ".jpeg", ".jpe", ".jif", ".jfif", ".jfi", ".tiff", ".tif")):
image_list.append( os.path.join(image_loc, f) )
image_list.sort()
logger.debug("Image List Created")
return image_list