Merge branch 'development'

This commit is contained in:
evilhero 2018-08-17 15:54:27 -04:00
commit 04fc85c4b0
12 changed files with 351 additions and 55 deletions

View File

@ -0,0 +1,83 @@
div.alphabet {
clear:both;
position:relative;
margin:0.5em 0;
}
@media screen and (max-width:963px){
div.alphabet {
text-align:center;
}
}
div.alphabet ul {
display:inline-block;
margin:0;
padding:0;
list-style:none;
}
div.alphabet li {
display:inline-block;
}
div.alphabet a {
display:inline-block;
cursor:pointer;
text-align:center;
text-decoration:none;
box-sizing:content-box;
padding:0.2em 0.1em;
min-width:1.3em;
color:#333 !important;
border:1px solid transparent;
border-radius:2px;
}
div.alphabet a:hover {
color:#FFF !important;
border:1px solid #111;
background-color:#585858;
background:linear-gradient(to bottom, #585858 0%, #111 100%);
}
div.alphabet a:active {
outline:none;
background-color:#2b2b2b;
background:linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%);
box-shadow:inset 0 0 3px #111;
}
div.alphabet a.empty {
color:#888 !important;
}
div.alphabet a.active,
div.alphabet a.active.empty {
color:#333 !important;
border:1px solid #979797;
background-color:#FFF;
background:linear-gradient(to bottom, #fff 0%, #dcdcdc 100%)
}
div.alphabet .alphabet-info-display {
margin-right:0.5em;
}
div.alphabet div.alphabet-info {
position:absolute;
border:1px solid #111;
background-color:#585858;
background:linear-gradient(to bottom, #585858 0%, #111 100%);
border-radius:2px;
color:#FFF;
margin-top:0.2em;
padding:0.2em 0.4em;
text-align:center;
opacity:0;
z-index:9999;
}
tr.alphabet-group, tr.alphabet-group:hover {
background-color:rgba(0,0,0,0.15) !important;
}

View File

@ -228,6 +228,7 @@
</div>
<div id="opdsoptions">
<div class="row_checkbox">
<small>Access the OPDS server at http://mylarhost/opds/ - keep in mind your scheme (http or https), your hostname, port, and any http_root you may have set. </small></br>
<input id="opds_authentication" type="checkbox" name="opds_authentication" value="1" ${config['opds_authentication']} /><label>OPDS Requires Credentials</label>
<%
opds_notes = "Require authentication for OPDS. If checked\nyou will need to provide a username/password.\nThe service user name will work (if set). Additionally,\nyou can provide a user with only OPDS access below.\nNOTE: If this is not checked, OPDS will be available\nwithout a password."

View File

@ -101,18 +101,20 @@
<%def name="headIncludes()">
<link rel="stylesheet" href="interfaces/default/css/data_table.css">
<link type="text/css" href="css/dataTables.alphabetSearch.css" rel="stylesheet">
</%def>
<%def name="javascriptIncludes()">
<script src="js/libs/jquery.dataTables.min.js"></script>
<script src="js/libs/full_numbers_no_ellipses.js"></script>
<script src="js/dataTables.alphabetSearch.min.js"></script>
<script>
function initThisPage() {
$.fn.DataTable.ext.pager.numbers_length = 3;
$('#series_table').dataTable( {
var table = $('#series_table').dataTable( {
"destroy": true,
"sDom": '<"clear"f><"clear"lp><"clear">rt<"clear"ip>',
"sDom": '<"clear"Af><"clear"lp><"clear">rt<"clear"ip>',
"columnDefs": [
{ "orderable": false, "targets": [5, 7, 10] },
{ "visible": false, "targets": [5, 7, 10] },
@ -132,7 +134,10 @@
"search" : ""},
"stateSave": true,
"pageLength": 25,
"pagingType": "simple_numbers"
"pagingType": "simple_numbers",
alphabetSearch: {
column:1
}
});
resetFilters("comic");
}

View File

@ -28,17 +28,25 @@
</br></br>
<table width="100%" align="center">
<tr>
<td style="vertical-align: middle; text-align: right"><a href="pullist?week=${weekinfo['prev_weeknumber']}&year=${weekinfo['prev_year']}" title="Previous Week (${weekinfo['prev_weeknumber']})"><img src="interfaces/default/images/prev.gif" width="16" height="18" Alt="Previous"/></td>
<td style="vertical-align: middle; text-align: right">
<a href="pullist?week=${weekinfo['prev_weeknumber']}&year=${weekinfo['prev_year']}" title="Previous Week (${weekinfo['prev_weeknumber']})" onclick="$('#pull_table').page('first').draw('page');">
<img src="interfaces/default/images/prev.gif" width="16" height="18" Alt="Previous"/>
</a>
</td>
<td style="vertical-align: middle; text-align: center">
%if wantedcount == 0:
<h1><center>Weekly Pull list for week ${weekinfo['weeknumber']} :</br>${weekinfo['startweek']} - ${weekinfo['endweek']}</center></h1>
%else:
<h1><center>Weekly Pull list for week ${weekinfo['weeknumber']} :</br>${weekinfo['startweek']} - ${weekinfo['endweek']} (${wantedcount})</center></h1>
%endif
</td><td style="vertical-align: middle; text-align: left">
<a href="pullist?week=${weekinfo['next_weeknumber']}&year=${weekinfo['next_year']}" title="Next Week (${weekinfo['next_weeknumber']})"><img src="interfaces/default/images/next.gif" width="16" height="18" Alt="Next"/></a></td>
<tr>
</table>
</td>
<td style="vertical-align: middle; text-align: left">
<a href="pullist?week=${weekinfo['next_weeknumber']}&year=${weekinfo['next_year']}" title="Next Week (${weekinfo['next_weeknumber']})" onclick="$('#pull_table').page('first').draw('page');">
<img src="interfaces/default/images/next.gif" width="16" height="18" Alt="Next"/>
</a>
</td>
</tr>
</table>
</div>
<div>

View File

@ -0,0 +1,94 @@
/*! AlphabetSearch for DataTables v1.2.4
* 2014 SpryMedia Ltd - datatables.net/license
* Gyrocode - MIT License
*/
(function() { $.fn.dataTable.Api.register("alphabetSearch()", function(searchTerm) { this.iterator("table", function(context) { context.alphabetSearch.letter = searchTerm; }); return this; });
$.fn.dataTable.Api.register("alphabetSearch.recalc()", function() { this.iterator("table", function(context) { draw(new $.fn.dataTable.Api(context), $("div.alphabet", this.table()
.container()), context); }); return this; });
$.fn.dataTable.ext.search.push(function(context, searchData) { if (!context.hasOwnProperty("alphabetSearch")) { return true; } if (!context.alphabetSearch.letterSearch) { return true; } var letter = searchData[context.alphabetSearch.column].toString()
.replace(/<.*?>/g, "")
.charAt(0)
.toUpperCase(); if (context.alphabetSearch.letterSearch !== "#") { if (letter === context.alphabetSearch.letterSearch) { return true; } } else { if (/[^a-zA-Z]/.test(letter)) { return true; } } return false; });
$.fn.dataTable.ext.order["alphabetSearch"] = function(context, col) { var order_col = this.api()
.order()[0][0]; var order_method = this.api()
.order()[0][1]; if (order_col !== context.alphabetSearch.column) { context.alphabetSearch.pass = 0; } var data = this.api()
.column(col, { order: "index" })
.data()
.map(function(value, index) { var letter = value.replace(/<.*?>/g, "")
.charAt(0)
.toUpperCase(); return (order_col === context.alphabetSearch.column) ? ((!context.alphabetSearch.pass) ? "" : ((order_method === "asc") ? letter : String.fromCharCode(65535 - letter.charCodeAt(0)))) : letter; }); if (order_col === context.alphabetSearch.column) { if (!context.alphabetSearchPass) { context.alphabetSearch.pass = 0; } context.alphabetSearch.pass = (context.alphabetSearch.pass + 1) % 2; } return data; };
function bin(data) { var letter, bins = {}; for (var i = 0, ien = data.length; i < ien; i++) { letter = data[i].toString()
.replace(/<.*?>/g, "")
.charAt(0)
.toUpperCase(); if (/[^a-zA-Z]/.test(letter)) { letter = "#"; } if (bins[letter]) { bins[letter]++; } else { bins[letter] = 1; } } return bins; }
function draw(table, alphabet, context) { alphabet.empty(); if (context.oLanguage.alphabetSearch.infoDisplay !== "") { $('<span class="alphabet-info-display"></span>')
.html(context.oLanguage.alphabetSearch.infoDisplay)
.appendTo(alphabet); } var columnData = table.column(context.alphabetSearch.column, { search: "applied" })
.data(); var bins = bin(columnData); var alphabetList = $("<ul/>");
$("<a/>")
.attr("href", "javascript:;")
.data("letter", "")
.data("match-count", columnData.length)
.addClass(((!context.alphabetSearch.letter) ? "active" : ""))
.html("<span>" + context.oLanguage.alphabetSearch.infoAll + "</span>")
.wrap("<li/>")
.parent()
.appendTo(alphabetList); for (var i = 0; i < context.oLanguage.alphabetSearch.alphabet.length; i++) { var letter = context.oLanguage.alphabetSearch.alphabet[i];
$("<a/>")
.attr("href", "javascript:;")
.data("letter", letter)
.data("match-count", bins[letter] || 0)
.addClass((!bins[letter] ? "empty" : "") + ((context.alphabetSearch.letter === letter) ? " active" : ""))
.html("<span>" + letter + "</span>")
.wrap("<li/>")
.parent()
.appendTo(alphabetList); } alphabetList.appendTo(alphabet);
$('<div class="alphabet-info"></div>')
.appendTo(alphabet); if (context.alphabetSearch.letter) { context.alphabetSearch.letterSearch = context.alphabetSearch.letter;
table.draw();
context.alphabetSearch.letterSearch = ""; } table.one("search", function(e, context) { var api = new $.fn.dataTable.Api(context);
api.alphabetSearch.recalc(); }); } $.fn.dataTable.AlphabetSearch = function(context) { var table = new $.fn.dataTable.Api(context); var alphabet = $('<div class="alphabet"/>');
context.oLanguage.alphabetSearch = $.extend({ "alphabet": "#ABCDEFGHIJKLMNOPQRSTUVWXYZ", "infoDisplay": "Display:", "infoAll": "All" }, ((context.oLanguage.alphabetSearch) ? context.oLanguage.alphabetSearch : {}));
context.oLanguage.alphabetSearch.alphabet.toUpperCase();
context.alphabetSearch = $.extend({ column: 0 }, $.isPlainObject(context.oInit.alphabetSearch) ? context.oInit.alphabetSearch : {}, { letter: "", letterSearch: "", pass: 0 }); if (context.alphabetSearch.column >= 0 && context.alphabetSearch.column < context.aoColumns.length) { context.aoColumns[context.alphabetSearch.column].sSortDataType = "alphabetSearch"; } if (context.hasOwnProperty("aaSortingFixed") && typeof context.aaSortingFixed === "object") { if ($.isArray(context.aaSortingFixed)) { if (context.aaSortingFixed.length && !$.isArray(context.aaSortingFixed[0])) { context.aaSortingFixed = [
[context.alphabetSearch.column, "asc"], context.aaSortingFixed
]; } else { context.aaSortingFixed.unshift([context.alphabetSearch.column, "asc"]); } } else { if (!context.aaSortingFixed.hasOwnProperty("pre")) { context.aaSortingFixed.pre = []; } if (context.aaSortingFixed.pre.length && !$.isArray(context.aaSortingFixed.pre[0])) { context.aaSortingFixed.pre = [
[context.alphabetSearch.column, "asc"], context.aaSortingFixed.pre
]; } else { context.aaSortingFixed.pre.unshift([context.alphabetSearch.column, "asc"]); } } } else { context.aaSortingFixed = [context.alphabetSearch.column, "asc"]; } draw(table, alphabet, context);
alphabet.on("click", "a", function(e) { e.preventDefault();
alphabet.find(".active")
.removeClass("active");
$(this)
.addClass("active");
table.alphabetSearch($(this)
.data("letter"))
.draw(); });
alphabet.on("mouseenter", "a", function() { var $el = $(this); var el_pos = $el.position(); var $alphabet_info = $(".alphabet-info", alphabet);
$alphabet_info.html($el.data("match-count"));
$alphabet_info.css({ opacity: 1, left: el_pos.left + Math.round(($el.outerWidth() - $alphabet_info.outerWidth()) / 2), top: $(this)
.position()
.top + $el.outerHeight() }); })
.on("mouseleave", "a", function() { alphabet.find("div.alphabet-info")
.css("opacity", 0); });
table.on("draw", function(e, context) { var api = new $.fn.dataTable.Api(context); var col_total = api.columns()
.nodes()
.length; var rows = api.rows({ page: "current" })
.nodes(); var group_last = null;
api.column(context.alphabetSearch.column, { page: "current" })
.data()
.each(function(name, index) { var group = name.replace(/<.*?>/g, "")
.charAt(0)
.toUpperCase(); if (group_last !== group) { $(rows)
.eq(index)
.before('<tr class="alphabet-group" style="display:none;"><td colspan="' + col_total + '">' + group + "</td></tr>");
group_last = group; } }); if (!rows.length && context.alphabetSearch) { var letter = context.alphabetSearch.letter;
$(api.table()
.body())
.prepend('<tr class="alphabet-group" style="display:none;"><td colspan="' + col_total + '">' + letter + "</td></tr>"); } });
this.node = function() { return alphabet; }; };
$.fn.DataTable.AlphabetSearch = $.fn.dataTable.AlphabetSearch;
$.fn.dataTable.ext.feature.push({ fnInit: function(settings) { var search = new $.fn.dataTable.AlphabetSearch(settings); return search.node(); }, cFeature: "A" }); }());

View File

@ -1707,10 +1707,9 @@ class PostProcessor(object):
if ml is not None and mylar.CONFIG.SNATCHEDTORRENT_NOTIFY:
snatchnzb = myDB.selectone("SELECT * from snatched WHERE IssueID=? AND ComicID=? AND (provider=? OR provider=? OR provider=? OR provider=?) AND Status='Snatched'", [issueid, comicid, 'TPSE', 'DEM', 'WWT', '32P']).fetchone()
if snatchnzb is None:
logger.fdebug(module + ' Was not snatched as a torrent. Disabling torrent manual post-processing completion notification.')
logger.fdebug(module + ' Was not snatched as a torrent. Using manual post-processing.')
else:
logger.fdebug(module + ' Was downloaded from ' + snatchnzb['Provider'] + '. Enabling torrent manual post-processing completion notification.')
snatchedtorrent = True
if issuenzb is None:
issuenzb = myDB.selectone("SELECT * from annuals WHERE issueid=? and comicid=?", [issueid, comicid]).fetchone()
annchk = "yes"
@ -2361,20 +2360,16 @@ class PostProcessor(object):
if ml is not None:
#we only need to return self.log if it's a manual run and it's not a snatched torrent
if snatchedtorrent:
#manual run + snatched torrent
pass
else:
#manual run + not snatched torrent (or normal manual-run)
logger.info(module + ' Post-Processing completed for: ' + series + ' ' + dispiss)
self._log(u"Post Processing SUCCESSFUL! ")
self.valreturn.append({"self.log": self.log,
"mode": 'stop',
"issueid": issueid,
"comicid": comicid})
if self.apicall is True:
self.sendnotify(series, issueyear, dispiss, annchk, module)
return self.queue.put(self.valreturn)
#manual run + not snatched torrent (or normal manual-run)
logger.info(module + ' Post-Processing completed for: ' + series + ' ' + dispiss)
self._log(u"Post Processing SUCCESSFUL! ")
self.valreturn.append({"self.log": self.log,
"mode": 'stop',
"issueid": issueid,
"comicid": comicid})
if self.apicall is True:
self.sendnotify(series, issueyear, dispiss, annchk, module)
return self.queue.put(self.valreturn)
self.sendnotify(series, issueyear, dispiss, annchk, module)

View File

@ -86,6 +86,7 @@ _CONFIG_DEFINITIONS = OrderedDict({
'INDIE_PUB': (int, 'Weekly', 75),
'BIGGIE_PUB': (int, 'Weekly', 55),
'PACK_0DAY_WATCHLIST_ONLY': (bool, 'Weekly', True),
'RESET_PULLIST_PAGINATION': (bool, 'Weekly', True),
'HTTP_PORT' : (int, 'Interface', 8090),
'HTTP_HOST' : (str, 'Interface', '0.0.0.0'),

View File

@ -292,7 +292,7 @@ def addComictoDB(comicid, mismatch=None, pullupd=None, imported=None, ogcname=No
if mylar.CONFIG.ENFORCE_PERMS:
filechecker.setperms(comiclocal)
except IOError as e:
logger.error('Unable to save cover (' + str(coverfile) + ') into series directory (' + str(comiclocal) + ') at this time.')
logger.error('Unable to save cover (' + str(comiclocal) + ') into series directory (' + str(comlocation) + ') at this time.')
else:
ComicImage = None
@ -1418,8 +1418,6 @@ def annual_check(ComicName, SeriesYear, comicid, issuetype, issuechk, annualslis
annual_types_ignore = {'paperback', 'collecting', 'reprints', 'collected edition', 'print edition', 'tpb', 'available in print', 'collects'}
if len(sresults) == 1:
logger.fdebug('[IMPORTER-ANNUAL] - 1 result')
if len(sresults) > 0:
logger.fdebug('[IMPORTER-ANNUAL] - there are ' + str(len(sresults)) + ' results.')
num_res = 0

View File

@ -208,8 +208,10 @@ class PUSHOVER:
def __init__(self, test_apikey=None, test_userkey=None, test_device=None):
if all([test_apikey is None, test_userkey is None, test_device is None]):
self.PUSHOVER_URL = 'https://api.pushover.net/1/messages.json'
self.test = False
else:
self.PUSHOVER_URL = 'https://api.pushover.net/1/users/validate.json'
self.test = True
self.enabled = mylar.CONFIG.PUSHOVER_ENABLED
if test_apikey is None:
if mylar.CONFIG.PUSHOVER_APIKEY is None or mylar.CONFIG.PUSHOVER_APIKEY == 'None':
@ -236,8 +238,7 @@ class PUSHOVER:
self._session.headers = {'Content-type': "application/x-www-form-urlencoded"}
def notify(self, event, message=None, snatched_nzb=None, prov=None, sent_to=None, module=None):
if not mylar.CONFIG.PUSHOVER_ENABLED:
return
if module is None:
module = ''
module += '[NOTIFIER]'
@ -261,23 +262,36 @@ class PUSHOVER:
if r.status_code == 200:
try:
response = r.json()
if 'devices' in response:
logger.info('%s PushOver notifications sent. Available devices: %s' % (module, response))
if 'devices' in response and self.test is True:
logger.fdebug('%s Available devices: %s' % (module, response))
if any([self.device is None, self.device == 'None']):
self.device = 'all available devices'
r = self._session.post('https://api.pushover.net/1/messages.json', data=data, verify=True)
if r.status_code == 200:
logger.info('%s PushOver notifications sent to %s.' % (module, self.device))
elif r.status_code >=400 and r.status_code < 500:
logger.error('%s PushOver request failed to %s: %s' % (module, self.device, r.content))
return False
else:
logger.error('%s PushOver notification failed serverside.' % module)
return False
else:
logger.info('%s PushOver notifications sent.' % module)
except Exception as e:
logger.warn('%s[ERROR] - %s' % (module, e))
return True
return False
else:
return True
elif r.status_code >= 400 and r.status_code < 500:
logger.error(module + ' PushOver request failed: %s' % r.content)
logger.error('%s PushOver request failed: %s' % (module, r.content))
return False
else:
logger.error(module + ' PushOver notification failed serverside.')
logger.error('%s PushOver notification failed serverside.' % module)
return False
def test_notify(self):
return self.notify(message='Release the Ninjas!',event='Test Message')
return self.notify(event='Test Message', message='Release the Ninjas!')
class BOXCAR:
@ -468,12 +482,19 @@ class TELEGRAM:
class SLACK:
def __init__(self, test_webhook_url=None):
self.webhook_url = mylar.CONFIG.SLACK_WEBHOOK_URL if test_webhook_url is None else test_webhook_url
def notify(self, text, attachment_text, module=None):
def notify(self, text, attachment_text, snatched_nzb=None, prov=None, sent_to=None, module=None):
if module is None:
module = ''
module += '[NOTIFIER]'
if all([sent_to is not None, prov is not None]):
attachment_text += ' from %s and sent to %s' % (prov, sent_to)
elif sent_to is None:
attachment_text += ' from %s' % prov
else:
pass
payload = {
# "text": text,
# "attachments": [
@ -499,6 +520,6 @@ class SLACK:
logger.info(module + u"Slack notifications sent.")
return sent_successfuly
def test_notify(self):
return self.notify('Test Message', 'Release the Ninjas!')

View File

@ -17,11 +17,12 @@
# along with Mylar. If not, see <http://www.gnu.org/licenses/>.
import mylar
from mylar import db, mb, importer, search, PostProcessor, versioncheck, logger, readinglist
from mylar import db, mb, importer, search, PostProcessor, versioncheck, logger, readinglist, helpers
import simplejson as simplejson
import cherrypy
from xml.sax.saxutils import escape
import os
import glob
import urllib2
from urllib import urlencode, quote_plus
import cache
@ -30,8 +31,9 @@ from operator import itemgetter
from cherrypy.lib.static import serve_file, serve_download
import datetime
from mylar.webserve import serve_template
import re
cmd_list = ['root', 'Publishers', 'AllTitles', 'StoryArcs', 'ReadList', 'Comic', 'Publisher', 'Issue', 'StoryArc', 'Recent']
cmd_list = ['root', 'Publishers', 'AllTitles', 'StoryArcs', 'ReadList', 'OneOffs', 'Comic', 'Publisher', 'Issue', 'StoryArc', 'Recent', 'deliverFile']
class OPDS(object):
@ -119,7 +121,8 @@ class OPDS(object):
myDB = db.DBConnection()
feed = {}
feed['title'] = 'Mylar OPDS'
feed['id'] = 'OPDSRoot'
currenturi = cherrypy.url()
feed['id'] = re.sub('/', ':', currenturi)
feed['updated'] = mylar.helpers.now()
links = []
entries=[]
@ -196,7 +199,20 @@ class OPDS(object):
'rel': 'subsection',
}
)
gbd = mylar.CONFIG.GRABBAG_DIR + '/*'
oneofflist = glob.glob(gbd)
if len(oneofflist) > 0:
entries.append(
{
'title': 'One-Offs (%s)' % len(oneofflist),
'id': 'OneOffs',
'updated': mylar.helpers.now(),
'content': 'OneOffs',
'href': '%s?cmd=OneOffs' % self.opdsroot,
'kind': 'navigation',
'rel': 'subsection',
}
)
feed['links'] = links
feed['entries'] = entries
self.data = feed
@ -267,10 +283,10 @@ class OPDS(object):
if comic['haveissues'] > 0:
entries.append(
{
'title': escape('%s (%s) (%s)' % (comic['ComicName'], comic['ComicYear'], comic['haveissues'])),
'id': escape('comic:%s (%s)' % (comic['ComicName'], comic['ComicYear'])),
'title': escape('%s (%s) (comicID: %s)' % (comic['ComicName'], comic['ComicYear'], comic['ComicID'])),
'id': escape('comic:%s (%s) [%s]' % (comic['ComicName'], comic['ComicYear'], comic['ComicID'])),
'updated': comic['DateAdded'],
'content': escape('%s (%s) (%s)' % (comic['ComicName'], comic['ComicYear'], comic['haveissues'])),
'content': escape('%s (%s)' % (comic['ComicName'], comic['ComicYear'])),
'href': '%s?cmd=Comic&amp;comicid=%s' % (self.opdsroot, quote_plus(comic['ComicID'])),
'kind': 'acquisition',
'rel': 'subsection',
@ -304,10 +320,10 @@ class OPDS(object):
if comic['ComicPublisher'] == kwargs['pubid'] and comic['haveissues'] > 0:
entries.append(
{
'title': escape('%s (%s) (%s)' % (comic['ComicName'], comic['ComicYear'], comic['haveissues'])),
'title': escape('%s (%s)' % (comic['ComicName'], comic['ComicYear'])),
'id': escape('comic:%s (%s)' % (comic['ComicName'], comic['ComicYear'])),
'updated': comic['DateAdded'],
'content': escape('%s (%s) (%s)' % (comic['ComicName'], comic['ComicYear'], comic['haveissues'])),
'content': escape('%s (%s)' % (comic['ComicName'], comic['ComicYear'])),
'href': '%s?cmd=Comic&amp;comicid=%s' % (self.opdsroot, quote_plus(comic['ComicID'])),
'kind': 'acquisition',
'rel': 'subsection',
@ -383,7 +399,7 @@ class OPDS(object):
entries.append(
{
'title': title,
'id': escape('comic:%s - %s' % (issue['ComicName'], issue['Issue_Number'])),
'id': escape('comic:%s (%s) [%s] - %s' % (issue['ComicName'], comic['ComicYear'], comic['ComicID'], issue['Issue_Number'])),
'updated': updated,
'content': escape('%s' % (metainfo[0]['summary'])),
'href': '%s?cmd=Issue&amp;issueid=%s&amp;file=%s' % (self.opdsroot, quote_plus(issue['IssueID']),quote_plus(issue['Location'].encode('utf-8'))),
@ -460,7 +476,7 @@ class OPDS(object):
entries.append(
{
'title': title,
'id': escape('comic:%s - %s' % (issuebook['ComicName'], issuebook['Issue_Number'])),
'id': escape('comic:%s (%s) - %s' % (issuebook['ComicName'], comic['ComicYear'], issuebook['Issue_Number'])),
'updated': updated,
'content': escape('%s' % (metainfo[0]['summary'])),
'href': '%s?cmd=Issue&amp;issueid=%s&amp;file=%s' % (self.opdsroot, quote_plus(issuebook['IssueID']),quote_plus(location)),
@ -489,7 +505,16 @@ class OPDS(object):
self.data = feed
return
def _deliverFile(self, **kwargs):
logger.fdebug("_deliverFile: kwargs: %s" % kwargs)
if 'file' not in kwargs:
self.data = self._error_with_message('No file provided')
elif 'filename' not in kwargs:
self.data = self._error_with_message('No filename provided')
else:
self.filename = os.path.split(str(kwargs['file']))[1]
self.file = str(kwargs['file'])
return
def _Issue(self, **kwargs):
if 'issueid' not in kwargs:
@ -573,6 +598,71 @@ class OPDS(object):
self.data = feed
return
def _OneOffs(self, **kwargs):
index = 0
if 'index' in kwargs:
index = int(kwargs['index'])
links = []
entries = []
flist = []
book = ''
gbd = str(mylar.CONFIG.GRABBAG_DIR + '/*').encode('utf-8')
flist = glob.glob(gbd)
readlist = []
for book in flist:
issue = {}
fileexists = True
book = book.encode('utf-8')
issue['Title'] = book
issue['IssueID'] = book
issue['fileloc'] = book
issue['filename'] = book
issue['image'] = None
issue['thumbnail'] = None
issue['updated'] = helpers.now()
if not os.path.isfile(issue['fileloc']):
fileexists = False
if fileexists:
readlist.append(issue)
if len(readlist) > 0:
if index <= len(readlist):
subset = readlist[index:(index + self.PAGE_SIZE)]
for issue in subset:
metainfo = None
metainfo = [{'writer': None,'summary': ''}]
entries.append(
{
'title': escape(issue['Title']),
'id': escape('comic:%s' % issue['IssueID']),
'updated': issue['updated'],
'content': escape('%s' % (metainfo[0]['summary'])),
'href': '%s?cmd=deliverFile&amp;file=%s&amp;filename=%s' % (self.opdsroot, quote_plus(issue['fileloc']), quote_plus(issue['filename'])),
'kind': 'acquisition',
'rel': 'file',
'author': metainfo[0]['writer'],
'image': issue['image'],
'thumbnail': issue['thumbnail'],
}
)
feed = {}
feed['title'] = 'Mylar OPDS - One-Offs'
feed['id'] = escape('OneOffs')
feed['updated'] = mylar.helpers.now()
links.append(getLink(href=self.opdsroot,type='application/atom+xml; profile=opds-catalog; kind=navigation', rel='start', title='Home'))
links.append(getLink(href='%s?cmd=OneOffs' % self.opdsroot,type='application/atom+xml; profile=opds-catalog; kind=navigation',rel='self'))
if len(readlist) > (index + self.PAGE_SIZE):
links.append(
getLink(href='%s?cmd=OneOffs&amp;index=%s' % (self.opdsroot, index+self.PAGE_SIZE), type='application/atom+xml; profile=opds-catalog; kind=navigation', rel='next'))
if index >= self.PAGE_SIZE:
links.append(
getLink(href='%s?cmd=Read&amp;index=%s' % (self.opdsroot, index-self.PAGE_SIZE), type='application/atom+xml; profile=opds-catalog; kind=navigation', rel='previous'))
feed['links'] = links
feed['entries'] = entries
self.data = feed
return
def _ReadList(self, **kwargs):
index = 0
if 'index' in kwargs:

View File

@ -635,7 +635,7 @@ def NZB_SEARCH(ComicName, IssueNumber, ComicYear, SeriesYear, Publisher, IssueDa
if host_torznab[len(host_torznab)-1:len(host_torznab)] == '/':
torznab_fix = host_torznab[:-1]
else:
torznab_fix = host.torznab
torznab_fix = host_torznab
findurl = str(torznab_fix) + "?t=search&q=" + str(comsearch)
if category_torznab is not None:
findurl += "&cat=" + str(category_torznab)
@ -2981,7 +2981,7 @@ def notify_snatch(nzbname, sent_to, modcomicname, comyear, IssueNumber, nzbprov)
if mylar.CONFIG.SLACK_ENABLED and mylar.CONFIG.SLACK_ONSNATCH:
logger.info(u"Sending Slack notification")
slack = notifiers.SLACK()
slack.notify("Snatched", snline)
slack.notify("Snatched", snline, snatched_nzb=nzbname, sent_to=sent_to, prov=nzbprov)
return

View File

@ -5393,7 +5393,7 @@ class WebInterface(object):
if result == True:
return "Successfully sent PushOver test - check to make sure it worked"
else:
logger.warn('Test variables used [APIKEY: %s][USERKEY: %s]' % (apikey, userkey))
logger.warn('Last six characters of the test variables used [APIKEY: %s][USERKEY: %s]' % (apikey[-6:], userkey[-6:]))
return "Error sending test message to Pushover"
testpushover.exposed = True