FIX:(#1198) Updated py-unrar2 to latest version + patched to fix date format errors which was causing problems with ComicTagger and converting cbr-to-cbz, FIX: small typos in Config and Manage pages.

This commit is contained in:
evilhero 2016-01-29 12:38:32 -05:00
parent 52d0b29702
commit 81f8a4b76d
8 changed files with 166 additions and 34 deletions

View File

@ -312,8 +312,8 @@
</div>
<div class="row checkbox left clearfix">
<input id="sab_to_mylar" type="checkbox" onclick="initConfigCheckbox($(this));" name="sab_to_mylar" value="1" ${config['sab_to_mylar']} /><label>Are Mylar / SABnzbd on seperate machines</label>
<small class="heading"><span style="float: left; margin-right: .3em; margin-top: 4px;" class="ui-icon ui-icon-info"></span>This is *ONLY* required if Mylar and SABnzbd are on seperate machines, otherwise don't touch it</small>
<input id="sab_to_mylar" type="checkbox" onclick="initConfigCheckbox($(this));" name="sab_to_mylar" value="1" ${config['sab_to_mylar']} /><label>Are Mylar / SABnzbd on separate machines</label>
<small class="heading"><span style="float: left; margin-right: .3em; margin-top: 4px;" class="ui-icon ui-icon-info"></span>This is *ONLY* required if Mylar and SABnzbd are on separate machines, otherwise don't touch it</small>
</div>
<div class="config">
<div class="row">

View File

@ -116,7 +116,7 @@
<legend>Force Options</legend>
<a href="#" onclick="doAjaxCall('forceSearch',$(this))" data-success="Checking for wanted issues successful" data-error="Error checking wanted issues"><span class="ui-icon ui-icon-search"></span>Force Check for Wanted Issues</a>
<a href="#" onclick="doAjaxCall('forceUpdate',$(this))" data-success="Update active comics successful" data-error="Error forcing update Comics"><span class="ui-icon ui-icon-heart"></span>Force Update Active Comics</a>
<a href="#" onclick="doAjaxCall('checkGithub',$(this))" data-success="Checking for update successful" data-error="Error checking for update"><span class="ui-icon ui-icon-refresh"></span>Check for mylar Updates</a>
<a href="#" onclick="doAjaxCall('checkGithub',$(this))" data-success="Checking for update successful" data-error="Error checking for update"><span class="ui-icon ui-icon-refresh"></span>Check for Mylar Updates</a>
</div>
</fieldset>
</td>

View File

@ -0,0 +1,11 @@
pyUnRAR2 is a Python (ctypes-based) wrapper around the free UnRAR.dll (Windows) or command-line unrar binary (Unix).
It is an modified version of Jimmy Retzlaff's pyUnRAR - more simple,
stable and foolproof, with Unix support added.
Notice that it has INCOMPATIBLE interface.
It enables reading and unpacking of archives created with the RAR/WinRAR archivers.
Downloads:
https://drive.google.com/folderview?id=0B6L2WW4Bg4jvMnFtbjRKOUtDQTQ&usp=sharing#list

View File

@ -33,7 +33,7 @@ similar to the C interface provided by UnRAR. There is also a
higher level interface which makes some common operations easier.
"""
__version__ = '0.99.3'
__version__ = '0.99.6'
try:
WindowsError
@ -74,8 +74,6 @@ class RarInfo(object):
self.size = data['size']
self.datetime = data['datetime']
self.comment = data['comment']
def __str__(self):
try :
@ -159,6 +157,11 @@ class RarFile(RarFileImplementation):
checker = condition2checker(condition)
return RarFileImplementation.extract(self, checker, path, withSubpath, overwrite)
def get_volume(self):
"""Determine which volume is it in a multi-volume archive. Returns None if it's not a
multi-volume archive, 0-based volume number otherwise."""
return RarFileImplementation.get_volume(self)
def condition2checker(condition):
"""Converts different condition types to callback"""
if type(condition) in [str, unicode]:

View File

@ -15,6 +15,7 @@ def cleanup(dir='test'):
# basic test
cleanup()
rarc = UnRAR2.RarFile('test.rar')
assert rarc.get_volume() == None
rarc.infolist()
assert rarc.comment == "This is a test."
for info in rarc.infoiter():
@ -28,6 +29,21 @@ del rarc
assert (str(saveinfo)=="""<RarInfo "test" in "[ARCHIVE_NO_LONGER_LOADED]">""")
cleanup()
# shell-unsafe-name test
cleanup()
rarc = UnRAR2.RarFile('[test].rar')
rarc.infolist()
for info in rarc.infoiter():
saveinfo = info
assert (str(info)=="""<RarInfo "[test].txt" in "[test].rar">""")
break
rarc.extract()
assert os.path.exists('[test].txt')
del rarc
assert (str(saveinfo)=="""<RarInfo "[test].txt" in "[ARCHIVE_NO_LONGER_LOADED]">""")
cleanup()
# extract all the files in test.rar
cleanup()
UnRAR2.RarFile('test.rar').extract()
@ -108,6 +124,15 @@ except IncorrectRARPassword:
assert not os.path.exists('test'+os.sep+'top_secret_xxx_file.txt')
assert errored
cleanup()
errored = False
try:
UnRAR2.RarFile('test_protected_files.rar').extract()
except IncorrectRARPassword:
errored = True
assert not os.path.exists('test'+os.sep+'top_secret_xxx_file.txt')
assert errored
cleanup()
# extract files from an archive with protected headers
cleanup()
@ -122,6 +147,27 @@ except IncorrectRARPassword:
assert not os.path.exists('test'+os.sep+'top_secret_xxx_file.txt')
assert errored
cleanup()
errored = False
try:
UnRAR2.RarFile('test_protected_headers.rar').extract()
except IncorrectRARPassword:
errored = True
assert not os.path.exists('test'+os.sep+'top_secret_xxx_file.txt')
assert errored
cleanup()
# check volume number
cleanup()
rarc1 = UnRAR2.RarFile('test_volumes.part1.rar')
assert rarc1.get_volume() == 0
rarc2 = UnRAR2.RarFile('test_volumes.part2.rar')
assert rarc2.get_volume() == 1
cleanup()
rarc1 = UnRAR2.RarFile('test_volumes_old.rar')
assert rarc1.get_volume() == 0
rarc2 = UnRAR2.RarFile('test_volumes_old.r00')
assert rarc2.get_volume() == 1
cleanup()
# make sure docstring examples are working
import doctest

View File

@ -48,7 +48,7 @@ def call_unrar(params):
pass
if rar_executable_cached is None:
raise UnpackerNotInstalled("No suitable RAR unpacker installed")
assert type(params) == list, "params must be list"
args = [rar_executable_cached] + params
try:
@ -62,25 +62,25 @@ class RarFileImplementation(object):
def init(self, password=None):
global rar_executable_version
self.password = password
stdoutdata, stderrdata = self.call('v', []).communicate()
for line in stderrdata.splitlines():
if line.strip().startswith("Cannot open"):
raise FileOpenError
if line.find("CRC failed")>=0:
raise IncorrectRARPassword
raise IncorrectRARPassword
accum = []
source = iter(stdoutdata.splitlines())
line = ''
while not (line.startswith('UNRAR')):
while (line.find('RAR ') == -1):
line = source.next()
signature = line
# The code below is mighty flaky
# and will probably crash on localized versions of RAR
# but I see no safe way to rewrite it using a CLI tool
if signature.startswith("UNRAR 4"):
if signature.find("RAR 4") > -1:
rar_executable_version = 4
while not (line.startswith('Comment:') or line.startswith('Pathname/Comment')):
if line.strip().endswith('is not RAR archive'):
@ -94,7 +94,7 @@ class RarFileImplementation(object):
self.comment = '\n'.join(accum[:-1])
else:
self.comment = None
elif signature.startswith("UNRAR 5"):
elif signature.find("RAR 5") > -1:
rar_executable_version = 5
line = source.next()
while not line.startswith('Archive:'):
@ -107,14 +107,14 @@ class RarFileImplementation(object):
else:
self.comment = None
else:
raise UnpackerNotInstalled("Unsupported RAR version, expected 4.x or 5.x, found: "
raise UnpackerNotInstalled("Unsupported RAR version, expected 4.x or 5.x, found: "
+ signature.split(" ")[1])
def escaped_password(self):
return '-' if self.password == None else self.password
def call(self, cmd, options=[], files=[]):
options2 = options + ['p'+self.escaped_password()]
soptions = ['-'+x for x in options2]
@ -136,7 +136,7 @@ class RarFileImplementation(object):
if line.strip().endswith('is not RAR archive'):
raise InvalidRARArchive
if line.startswith("CRC failed") or line.startswith("Checksum error"):
raise IncorrectRARPassword
raise IncorrectRARPassword
line = source.next()
line = source.next()
i = 0
@ -153,8 +153,13 @@ class RarFileImplementation(object):
data['size'] = int(fields[0])
attr = fields[5]
data['isdir'] = 'd' in attr.lower()
data['datetime'] = time.strptime(fields[3]+" "+fields[4], '%d-%m-%y %H:%M')
try:
data['datetime'] = time.strptime(fields[3]+" "+fields[4], '%d-%m-%y %H:%M')
except ValueError:
data['datetime'] = time.strptime(fields[3]+" "+fields[4], '%Y-%m-%d %H:%M')
data['comment'] = None
data['volume'] = None
yield data
accum = []
i += 1
@ -168,12 +173,17 @@ class RarFileImplementation(object):
data['size'] = int(fields[1])
attr = fields[0]
data['isdir'] = 'd' in attr.lower()
data['datetime'] = time.strptime(fields[2]+" "+fields[3], '%d-%m-%y %H:%M')
try:
data['datetime'] = time.strptime(fields[2]+" "+fields[3], '%d-%m-%y %H:%M')
except ValueError:
data['datetime'] = time.strptime(fields[2]+" "+fields[3], '%Y-%m-%d %H:%M')
data['comment'] = None
data['volume'] = None
yield data
i += 1
line = source.next()
def read_files(self, checker):
res = []
@ -184,7 +194,7 @@ class RarFileImplementation(object):
res.append((info, pipe.read()))
return res
def extract(self, checker, path, withSubpath, overwrite):
res = []
command = 'x'
@ -209,10 +219,45 @@ class RarFileImplementation(object):
proc = self.call(command, options, names)
stdoutdata, stderrdata = proc.communicate()
if stderrdata.find("CRC failed")>=0 or stderrdata.find("Checksum error")>=0:
raise IncorrectRARPassword
return res
raise IncorrectRARPassword
return res
def destruct(self):
pass
def get_volume(self):
command = "v" if rar_executable_version == 4 else "l"
stdoutdata, stderrdata = self.call(command, ['c-']).communicate()
for line in stderrdata.splitlines():
if line.strip().startswith("Cannot open"):
raise FileOpenError
source = iter(stdoutdata.splitlines())
line = ''
while not line.startswith('-----------'):
if line.strip().endswith('is not RAR archive'):
raise InvalidRARArchive
if line.startswith("CRC failed") or line.startswith("Checksum error"):
raise IncorrectRARPassword
line = source.next()
line = source.next()
if rar_executable_version == 4:
while not line.startswith('-----------'):
line = source.next()
line = source.next()
items = line.strip().split()
if len(items)>4 and items[4]=="volume":
return int(items[5]) - 1
else:
return None
elif rar_executable_version == 5:
while not line.startswith('-----------'):
line = source.next()
line = source.next()
items = line.strip().split()
if items[1]=="volume":
return int(items[2]) - 1
else:
return None

View File

@ -25,7 +25,7 @@
from __future__ import generators
import ctypes, ctypes.wintypes
import os, os.path, sys
import os, os.path, sys, re
import Queue
import time
@ -43,6 +43,7 @@ ERAR_EREAD = 18
ERAR_EWRITE = 19
ERAR_SMALL_BUF = 20
ERAR_UNKNOWN = 21
ERAR_MISSING_PASSWORD = 22
RAR_OM_LIST = 0
RAR_OM_EXTRACT = 1
@ -66,6 +67,9 @@ dll_name = "unrar.dll"
if architecture_bits == 64:
dll_name = "x64\\unrar64.dll"
volume_naming1 = re.compile("\.r([0-9]{2})$")
volume_naming2 = re.compile("\.([0-9]{3}).rar$")
volume_naming3 = re.compile("\.part([0-9]+).rar$")
try:
unrar = ctypes.WinDLL(os.path.join(os.path.split(__file__)[0], 'UnRARDLL', dll_name))
@ -188,7 +192,7 @@ class RarInfoIterator(object):
self.index = 0
self.headerData = RARHeaderDataEx()
self.res = RARReadHeaderEx(self.arc._handle, ctypes.byref(self.headerData))
if self.res==ERAR_BAD_DATA:
if self.res in [ERAR_BAD_DATA, ERAR_MISSING_PASSWORD]:
raise IncorrectRARPassword
self.arc.lockStatus = "locked"
self.arc.needskip = False
@ -208,7 +212,7 @@ class RarInfoIterator(object):
data = {}
data['index'] = self.index
data['filename'] = self.headerData.FileName
data['filename'] = self.headerData.FileNameW
data['datetime'] = DosDateTimeToTimeTuple(self.headerData.FileTime)
data['isdir'] = ((self.headerData.Flags & 0xE0) == 0xE0)
data['size'] = self.headerData.UnpSize + (self.headerData.UnpSizeHigh << 32)
@ -251,7 +255,8 @@ class RarFileImplementation(object):
RARSetPassword(self._handle, password)
self.lockStatus = "ready"
self.isVolume = archiveData.Flags & 1
def destruct(self):
@ -277,7 +282,7 @@ class RarFileImplementation(object):
c_callback = UNRARCALLBACK(reader._callback)
RARSetCallback(self._handle, c_callback, 1)
tmpres = RARProcessFile(self._handle, RAR_TEST, None, None)
if tmpres==ERAR_BAD_DATA:
if tmpres in [ERAR_BAD_DATA, ERAR_MISSING_PASSWORD]:
raise IncorrectRARPassword
self.needskip = False
res.append((info, reader.get_result()))
@ -299,11 +304,29 @@ class RarFileImplementation(object):
target = checkres
if overwrite or (not os.path.exists(target)):
tmpres = RARProcessFile(self._handle, RAR_EXTRACT, None, target)
if tmpres==ERAR_BAD_DATA:
if tmpres in [ERAR_BAD_DATA, ERAR_MISSING_PASSWORD]:
raise IncorrectRARPassword
self.needskip = False
res.append(info)
return res
def get_volume(self):
if not self.isVolume:
return None
headerData = RARHeaderDataEx()
res = RARReadHeaderEx(self._handle, ctypes.byref(headerData))
arcName = headerData.ArcNameW
match3 = volume_naming3.search(arcName)
if match3 != None:
return int(match3.group(1)) - 1
match2 = volume_naming3.search(arcName)
if match2 != None:
return int(match2.group(1))
match1 = volume_naming1.search(arcName)
if match1 != None:
return int(match1.group(1)) + 1
return 0

View File

@ -238,6 +238,10 @@ def run(dirName, nzbName=None, issueid=None, comversion=None, manual=None, filen
initial_ctrun = False
elif initial_ctrun and 'Archive is not a RAR' in out:
initial_ctrun = False
elif initial_ctrun:
logger.warn(module + '[COMIC-TAGGER][CBR-TO-CBZ] Failed to convert cbr to cbz - check permissions on folder : ' + mylar.CACHE_DIR + ' and/or the location where Mylar is trying to tag the files from.')
initial_ctrun = False
return 'fail'
elif 'Cannot find' in out:
logger.warn(module + '[COMIC-TAGGER] Unable to locate file: ' + filename)
file_error = 'file not found||' + filename