83 changed files with 2576 additions and 1598 deletions
@ -1,40 +0,0 @@ |
|||||
from .main import FTDWorld |
|
||||
|
|
||||
def start(): |
|
||||
return FTDWorld() |
|
||||
|
|
||||
config = [{ |
|
||||
'name': 'ftdworld', |
|
||||
'groups': [ |
|
||||
{ |
|
||||
'tab': 'searcher', |
|
||||
'list': 'nzb_providers', |
|
||||
'name': 'FTDWorld', |
|
||||
'description': 'Free provider, less accurate. See <a href="http://ftdworld.net">FTDWorld</a>', |
|
||||
'wizard': True, |
|
||||
'options': [ |
|
||||
{ |
|
||||
'name': 'enabled', |
|
||||
'type': 'enabler', |
|
||||
}, |
|
||||
{ |
|
||||
'name': 'username', |
|
||||
'default': '', |
|
||||
}, |
|
||||
{ |
|
||||
'name': 'password', |
|
||||
'default': '', |
|
||||
'type': 'password', |
|
||||
}, |
|
||||
{ |
|
||||
'name': 'extra_score', |
|
||||
'advanced': True, |
|
||||
'label': 'Extra Score', |
|
||||
'type': 'int', |
|
||||
'default': 0, |
|
||||
'description': 'Starting score for each release found via this provider.', |
|
||||
} |
|
||||
], |
|
||||
}, |
|
||||
], |
|
||||
}] |
|
@ -1,83 +0,0 @@ |
|||||
from couchpotato.core.helpers.encoding import toUnicode, tryUrlencode |
|
||||
from couchpotato.core.helpers.variable import tryInt |
|
||||
from couchpotato.core.logger import CPLog |
|
||||
from couchpotato.core.providers.nzb.base import NZBProvider |
|
||||
from couchpotato.environment import Env |
|
||||
import json |
|
||||
import traceback |
|
||||
|
|
||||
log = CPLog(__name__) |
|
||||
|
|
||||
|
|
||||
class FTDWorld(NZBProvider): |
|
||||
|
|
||||
urls = { |
|
||||
'search': 'http://ftdworld.net/api/index.php?%s', |
|
||||
'detail': 'http://ftdworld.net/spotinfo.php?id=%s', |
|
||||
'download': 'http://ftdworld.net/cgi-bin/nzbdown.pl?fileID=%s', |
|
||||
'login': 'http://ftdworld.net/api/login.php', |
|
||||
'login_check': 'http://ftdworld.net/api/login.php', |
|
||||
} |
|
||||
|
|
||||
http_time_between_calls = 3 #seconds |
|
||||
|
|
||||
cat_ids = [ |
|
||||
([4, 11], ['dvdr']), |
|
||||
([1], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr', 'brrip']), |
|
||||
([7, 10, 13, 14], ['bd50', '720p', '1080p']), |
|
||||
] |
|
||||
cat_backup_id = 1 |
|
||||
|
|
||||
def _searchOnTitle(self, title, movie, quality, results): |
|
||||
|
|
||||
q = '"%s" %s' % (title, movie['library']['year']) |
|
||||
|
|
||||
params = tryUrlencode({ |
|
||||
'ctitle': q, |
|
||||
'customQuery': 'usr', |
|
||||
'cage': Env.setting('retention', 'nzb'), |
|
||||
'csizemin': quality.get('size_min'), |
|
||||
'csizemax': quality.get('size_max'), |
|
||||
'ccategory': 14, |
|
||||
'ctype': ','.join([str(x) for x in self.getCatId(quality['identifier'])]), |
|
||||
}) |
|
||||
|
|
||||
data = self.getJsonData(self.urls['search'] % params, opener = self.login_opener) |
|
||||
|
|
||||
if data: |
|
||||
try: |
|
||||
|
|
||||
if data.get('numRes') == 0: |
|
||||
return |
|
||||
|
|
||||
for item in data.get('data'): |
|
||||
|
|
||||
nzb_id = tryInt(item.get('id')) |
|
||||
results.append({ |
|
||||
'id': nzb_id, |
|
||||
'name': toUnicode(item.get('Title')), |
|
||||
'age': self.calculateAge(tryInt(item.get('Created'))), |
|
||||
'size': item.get('Size', 0), |
|
||||
'url': self.urls['download'] % nzb_id, |
|
||||
'detail_url': self.urls['detail'] % nzb_id, |
|
||||
'score': (tryInt(item.get('webPlus', 0)) - tryInt(item.get('webMin', 0))) * 3, |
|
||||
}) |
|
||||
|
|
||||
except: |
|
||||
log.error('Failed to parse HTML response from FTDWorld: %s', traceback.format_exc()) |
|
||||
|
|
||||
def getLoginParams(self): |
|
||||
return tryUrlencode({ |
|
||||
'userlogin': self.conf('username'), |
|
||||
'passlogin': self.conf('password'), |
|
||||
'submit': 'Log In', |
|
||||
}) |
|
||||
|
|
||||
def loginSuccess(self, output): |
|
||||
try: |
|
||||
return json.loads(output).get('goodToGo', False) |
|
||||
except: |
|
||||
return False |
|
||||
|
|
||||
loginCheckSuccess = loginSuccess |
|
||||
|
|
@ -0,0 +1,161 @@ |
|||||
|
# Copyright 2013 Dean Gardiner <gardiner91@gmail.com> |
||||
|
# |
||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
# you may not use this file except in compliance with the License. |
||||
|
# You may obtain a copy of the License at |
||||
|
# |
||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
# |
||||
|
# Unless required by applicable law or agreed to in writing, software |
||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
# See the License for the specific language governing permissions and |
||||
|
# limitations under the License. |
||||
|
|
||||
|
from logr import Logr |
||||
|
from caper.matcher import FragmentMatcher |
||||
|
from caper.objects import CaperFragment, CaperClosure |
||||
|
from caper.parsers.anime import AnimeParser |
||||
|
from caper.parsers.scene import SceneParser |
||||
|
|
||||
|
|
||||
|
__version_info__ = ('0', '2', '0') |
||||
|
__version_branch__ = 'master' |
||||
|
|
||||
|
__version__ = "%s%s" % ( |
||||
|
'.'.join(__version_info__), |
||||
|
'-' + __version_branch__ if __version_branch__ else '' |
||||
|
) |
||||
|
|
||||
|
|
||||
|
CL_START_CHARS = ['(', '['] |
||||
|
CL_END_CHARS = [')', ']'] |
||||
|
|
||||
|
STRIP_START_CHARS = ''.join(CL_START_CHARS) |
||||
|
STRIP_END_CHARS = ''.join(CL_END_CHARS) |
||||
|
STRIP_CHARS = ''.join(['_', ' ', '.']) |
||||
|
|
||||
|
FRAGMENT_SEPARATORS = ['.', '-', '_', ' '] |
||||
|
|
||||
|
|
||||
|
CL_START = 0 |
||||
|
CL_END = 1 |
||||
|
|
||||
|
|
||||
|
class Caper(object): |
||||
|
def __init__(self): |
||||
|
self.parsers = { |
||||
|
'scene': SceneParser(), |
||||
|
'anime': AnimeParser() |
||||
|
} |
||||
|
|
||||
|
def _closure_split(self, name): |
||||
|
""" |
||||
|
:type name: str |
||||
|
|
||||
|
:rtype: list of CaperClosure |
||||
|
""" |
||||
|
|
||||
|
closures = [] |
||||
|
|
||||
|
def end_closure(closures, buf): |
||||
|
buf = buf.strip(STRIP_CHARS) |
||||
|
if len(buf) < 1: |
||||
|
return |
||||
|
|
||||
|
cur = CaperClosure(buf) |
||||
|
cur.left = closures[len(closures) - 1] if len(closures) > 0 else None |
||||
|
|
||||
|
if cur.left: |
||||
|
cur.left.right = cur |
||||
|
|
||||
|
closures.append(cur) |
||||
|
|
||||
|
state = CL_START |
||||
|
buf = "" |
||||
|
for x, ch in enumerate(name): |
||||
|
if state == CL_START and ch in CL_START_CHARS: |
||||
|
end_closure(closures, buf) |
||||
|
|
||||
|
state = CL_END |
||||
|
buf = "" |
||||
|
|
||||
|
buf += ch |
||||
|
|
||||
|
if state == CL_END and ch in CL_END_CHARS: |
||||
|
end_closure(closures, buf) |
||||
|
|
||||
|
state = CL_START |
||||
|
buf = "" |
||||
|
|
||||
|
end_closure(closures, buf) |
||||
|
|
||||
|
return closures |
||||
|
|
||||
|
def _clean_closure(self, closure): |
||||
|
""" |
||||
|
:type closure: str |
||||
|
|
||||
|
:rtype: str |
||||
|
""" |
||||
|
|
||||
|
return closure.lstrip(STRIP_START_CHARS).rstrip(STRIP_END_CHARS) |
||||
|
|
||||
|
def _fragment_split(self, closures): |
||||
|
""" |
||||
|
:type closures: list of CaperClosure |
||||
|
|
||||
|
:rtype: list of CaperClosure |
||||
|
""" |
||||
|
|
||||
|
cur_position = 0 |
||||
|
cur = CaperFragment() |
||||
|
|
||||
|
def end_fragment(fragments, cur, cur_position): |
||||
|
cur.position = cur_position |
||||
|
|
||||
|
cur.left = fragments[len(fragments) - 1] if len(fragments) > 0 else None |
||||
|
if cur.left: |
||||
|
cur.left_sep = cur.left.right_sep |
||||
|
cur.left.right = cur |
||||
|
|
||||
|
cur.right_sep = ch |
||||
|
|
||||
|
fragments.append(cur) |
||||
|
|
||||
|
for closure in closures: |
||||
|
closure.fragments = [] |
||||
|
|
||||
|
for x, ch in enumerate(self._clean_closure(closure.value)): |
||||
|
if ch in FRAGMENT_SEPARATORS: |
||||
|
end_fragment(closure.fragments, cur, cur_position) |
||||
|
|
||||
|
# Reset |
||||
|
cur = CaperFragment() |
||||
|
cur_position += 1 |
||||
|
else: |
||||
|
cur.value += ch |
||||
|
|
||||
|
# Finish parsing the last fragment |
||||
|
if cur.value != "": |
||||
|
end_fragment(closure.fragments, cur, cur_position) |
||||
|
|
||||
|
# Reset |
||||
|
cur_position = 0 |
||||
|
cur = CaperFragment() |
||||
|
|
||||
|
return closures |
||||
|
|
||||
|
def parse(self, name, parser='scene'): |
||||
|
closures = self._closure_split(name) |
||||
|
closures = self._fragment_split(closures) |
||||
|
|
||||
|
# Print closures |
||||
|
for closure in closures: |
||||
|
Logr.debug("closure [%s]", closure.value) |
||||
|
|
||||
|
if parser not in self.parsers: |
||||
|
raise ValueError("Unknown parser") |
||||
|
|
||||
|
# TODO autodetect the parser type |
||||
|
return self.parsers[parser].run(closures) |
@ -0,0 +1,74 @@ |
|||||
|
# Copyright 2013 Dean Gardiner <gardiner91@gmail.com> |
||||
|
# |
||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
# you may not use this file except in compliance with the License. |
||||
|
# You may obtain a copy of the License at |
||||
|
# |
||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
# |
||||
|
# Unless required by applicable law or agreed to in writing, software |
||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
# See the License for the specific language governing permissions and |
||||
|
# limitations under the License. |
||||
|
|
||||
|
|
||||
|
class CaptureConstraint(object): |
||||
|
def __init__(self, capture_group, comparisons=None, **kwargs): |
||||
|
"""Capture constraint object |
||||
|
|
||||
|
:type capture_group: CaptureGroup |
||||
|
""" |
||||
|
|
||||
|
self.capture_group = capture_group |
||||
|
|
||||
|
self.comparisons = comparisons if comparisons else [] |
||||
|
|
||||
|
for key, value in kwargs.items(): |
||||
|
key = key.split('__') |
||||
|
if len(key) != 2: |
||||
|
continue |
||||
|
name, method = key |
||||
|
|
||||
|
method = '_compare_' + method |
||||
|
if not hasattr(self, method): |
||||
|
continue |
||||
|
|
||||
|
self.comparisons.append((name, getattr(self, method), value)) |
||||
|
|
||||
|
def _compare_eq(self, fragment, name, expected): |
||||
|
if not hasattr(fragment, name): |
||||
|
return None |
||||
|
|
||||
|
return 1.0, getattr(fragment, name) == expected |
||||
|
|
||||
|
def _compare_re(self, fragment, name, arg): |
||||
|
if name == 'fragment': |
||||
|
group, minimum_weight = arg if type(arg) is tuple and len(arg) > 1 else (arg, 0) |
||||
|
|
||||
|
weight, match, num_fragments = self.capture_group.parser.matcher.fragment_match(fragment, group) |
||||
|
return weight, weight > minimum_weight |
||||
|
elif type(arg).__name__ == 'SRE_Pattern': |
||||
|
return 1.0, arg.match(getattr(fragment, name)) is not None |
||||
|
elif hasattr(fragment, name): |
||||
|
match = self.capture_group.parser.matcher.value_match(getattr(fragment, name), arg, single=True) |
||||
|
return 1.0, match is not None |
||||
|
|
||||
|
if not hasattr(fragment, name): |
||||
|
raise ValueError("Unable to find fragment with name '%s'" % name) |
||||
|
else: |
||||
|
raise ValueError("Unexpected argument type") |
||||
|
|
||||
|
def execute(self, fragment): |
||||
|
results = [] |
||||
|
total_weight = 0 |
||||
|
|
||||
|
for name, method, argument in self.comparisons: |
||||
|
weight, success = method(fragment, name, argument) |
||||
|
total_weight += weight |
||||
|
results.append(success) |
||||
|
|
||||
|
return total_weight / float(len(results)), all(results) if len(results) > 0 else False |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "CaptureConstraint(comparisons=%s)" % repr(self.comparisons) |
@ -0,0 +1,147 @@ |
|||||
|
# Copyright 2013 Dean Gardiner <gardiner91@gmail.com> |
||||
|
# |
||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
# you may not use this file except in compliance with the License. |
||||
|
# You may obtain a copy of the License at |
||||
|
# |
||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
# |
||||
|
# Unless required by applicable law or agreed to in writing, software |
||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
# See the License for the specific language governing permissions and |
||||
|
# limitations under the License. |
||||
|
|
||||
|
|
||||
|
from logr import Logr |
||||
|
from caper.helpers import clean_dict |
||||
|
from caper.result import CaperFragmentNode |
||||
|
from caper.step import CaptureStep |
||||
|
from caper.constraint import CaptureConstraint |
||||
|
|
||||
|
|
||||
|
class CaptureGroup(object): |
||||
|
def __init__(self, parser, result): |
||||
|
"""Capture group object |
||||
|
|
||||
|
:type parser: caper.parsers.base.Parser |
||||
|
:type result: caper.result.CaperResult |
||||
|
""" |
||||
|
|
||||
|
self.parser = parser |
||||
|
self.result = result |
||||
|
|
||||
|
#: @type: list of CaptureStep |
||||
|
self.steps = [] |
||||
|
#: @type: list of CaptureConstraint |
||||
|
self.constraints = [] |
||||
|
|
||||
|
def capture_fragment(self, tag, regex=None, func=None, single=True): |
||||
|
Logr.debug('capture_fragment("%s", "%s", %s, %s)', tag, regex, func, single) |
||||
|
|
||||
|
self.steps.append(CaptureStep( |
||||
|
self, tag, |
||||
|
'fragment', |
||||
|
regex=regex, |
||||
|
func=func, |
||||
|
single=single |
||||
|
)) |
||||
|
|
||||
|
return self |
||||
|
|
||||
|
def capture_closure(self, tag, regex=None, func=None, single=True): |
||||
|
Logr.debug('capture_closure("%s", "%s", %s, %s)', tag, regex, func, single) |
||||
|
|
||||
|
self.steps.append(CaptureStep( |
||||
|
self, tag, |
||||
|
'closure', |
||||
|
regex=regex, |
||||
|
func=func, |
||||
|
single=single |
||||
|
)) |
||||
|
|
||||
|
return self |
||||
|
|
||||
|
def until(self, **kwargs): |
||||
|
self.constraints.append(CaptureConstraint(self, **kwargs)) |
||||
|
|
||||
|
return self |
||||
|
|
||||
|
def parse_subject(self, parent_head, subject): |
||||
|
parent_node = parent_head[0] if type(parent_head) is list else parent_head |
||||
|
|
||||
|
# TODO - if subject is a closure? |
||||
|
|
||||
|
nodes = [] |
||||
|
|
||||
|
# Check constraints |
||||
|
for constraint in self.constraints: |
||||
|
weight, success = constraint.execute(subject) |
||||
|
if success: |
||||
|
Logr.debug('capturing broke on "%s" at %s', subject.value, constraint) |
||||
|
parent_node.finished_groups.append(self) |
||||
|
nodes.append(parent_head) |
||||
|
|
||||
|
if weight == 1.0: |
||||
|
return nodes |
||||
|
else: |
||||
|
Logr.debug('Branching result') |
||||
|
|
||||
|
# Try match subject against the steps available |
||||
|
tag, success, weight, match, num_fragments = (None, None, None, None, None) |
||||
|
for step in self.steps: |
||||
|
tag = step.tag |
||||
|
success, weight, match, num_fragments = step.execute(subject) |
||||
|
if success: |
||||
|
match = clean_dict(match) if type(match) is dict else match |
||||
|
Logr.debug('Found match with weight %s, match: %s, num_fragments: %s' % (weight, match, num_fragments)) |
||||
|
break |
||||
|
|
||||
|
Logr.debug('created fragment node with subject.value: "%s"' % subject.value) |
||||
|
|
||||
|
result = [CaperFragmentNode(parent_node.closure, subject.take_right(num_fragments), parent_head, tag, weight, match)] |
||||
|
|
||||
|
if match and weight < 1.0: |
||||
|
if num_fragments == 1: |
||||
|
result.append(CaperFragmentNode(parent_node.closure, [subject], parent_head, None, None, None)) |
||||
|
else: |
||||
|
nodes.append(CaperFragmentNode(parent_node.closure, [subject], parent_head, None, None, None)) |
||||
|
|
||||
|
nodes.append(result[0] if len(result) == 1 else result) |
||||
|
|
||||
|
return nodes |
||||
|
|
||||
|
def execute(self): |
||||
|
heads_finished = None |
||||
|
|
||||
|
while heads_finished is None or not (len(heads_finished) == len(self.result.heads) and all(heads_finished)): |
||||
|
heads_finished = [] |
||||
|
|
||||
|
heads = self.result.heads |
||||
|
self.result.heads = [] |
||||
|
|
||||
|
for head in heads: |
||||
|
node = head[0] if type(head) is list else head |
||||
|
|
||||
|
Logr.debug("head node: %s" % node) |
||||
|
|
||||
|
if self in node.finished_groups: |
||||
|
Logr.debug("head finished for group") |
||||
|
self.result.heads.append(head) |
||||
|
heads_finished.append(True) |
||||
|
continue |
||||
|
|
||||
|
next_subject = node.next() |
||||
|
|
||||
|
if next_subject: |
||||
|
for node_result in self.parse_subject(head, next_subject): |
||||
|
self.result.heads.append(node_result) |
||||
|
|
||||
|
heads_finished.append(self in node.finished_groups or next_subject is None) |
||||
|
|
||||
|
if len(self.result.heads) == 0: |
||||
|
self.result.heads = heads |
||||
|
|
||||
|
Logr.debug("heads_finished: %s, self.result.heads: %s", heads_finished, self.result.heads) |
||||
|
|
||||
|
Logr.debug("group finished") |
@ -0,0 +1,64 @@ |
|||||
|
# Copyright 2013 Dean Gardiner <gardiner91@gmail.com> |
||||
|
# |
||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
# you may not use this file except in compliance with the License. |
||||
|
# You may obtain a copy of the License at |
||||
|
# |
||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
# |
||||
|
# Unless required by applicable law or agreed to in writing, software |
||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
# See the License for the specific language governing permissions and |
||||
|
# limitations under the License. |
||||
|
|
||||
|
import sys |
||||
|
|
||||
|
|
||||
|
PY2 = sys.version_info[0] == 2 |
||||
|
PY3 = sys.version_info[0] == 3 |
||||
|
|
||||
|
|
||||
|
def is_list_type(obj, element_type): |
||||
|
if not type(obj) is list: |
||||
|
return False |
||||
|
|
||||
|
if len(obj) < 1: |
||||
|
raise ValueError("Unable to determine list element type from empty list") |
||||
|
|
||||
|
return type(obj[0]) is element_type |
||||
|
|
||||
|
|
||||
|
def clean_dict(target, remove=None): |
||||
|
"""Recursively remove items matching a value 'remove' from the dictionary |
||||
|
|
||||
|
:type target: dict |
||||
|
""" |
||||
|
if type(target) is not dict: |
||||
|
raise ValueError("Target is required to be a dict") |
||||
|
|
||||
|
remove_keys = [] |
||||
|
for key in target.keys(): |
||||
|
if type(target[key]) is not dict: |
||||
|
if target[key] == remove: |
||||
|
remove_keys.append(key) |
||||
|
else: |
||||
|
clean_dict(target[key], remove) |
||||
|
|
||||
|
for key in remove_keys: |
||||
|
target.pop(key) |
||||
|
|
||||
|
return target |
||||
|
|
||||
|
|
||||
|
def xrange_six(start, stop=None, step=None): |
||||
|
if stop is not None and step is not None: |
||||
|
if PY3: |
||||
|
return range(start, stop, step) |
||||
|
else: |
||||
|
return xrange(start, stop, step) |
||||
|
else: |
||||
|
if PY3: |
||||
|
return range(start) |
||||
|
else: |
||||
|
return xrange(start) |
@ -0,0 +1,193 @@ |
|||||
|
# Copyright 2013 Dean Gardiner <gardiner91@gmail.com> |
||||
|
# |
||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
# you may not use this file except in compliance with the License. |
||||
|
# You may obtain a copy of the License at |
||||
|
# |
||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
# |
||||
|
# Unless required by applicable law or agreed to in writing, software |
||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
# See the License for the specific language governing permissions and |
||||
|
# limitations under the License. |
||||
|
|
||||
|
import pprint |
||||
|
import re |
||||
|
from logr import Logr |
||||
|
from caper.helpers import is_list_type, clean_dict |
||||
|
|
||||
|
|
||||
|
class FragmentMatcher(object): |
||||
|
def __init__(self, pattern_groups): |
||||
|
self.regex = {} |
||||
|
|
||||
|
for group_name, patterns in pattern_groups: |
||||
|
if group_name not in self.regex: |
||||
|
self.regex[group_name] = [] |
||||
|
|
||||
|
# Transform into weight groups |
||||
|
if type(patterns[0]) is str or type(patterns[0][0]) not in [int, float]: |
||||
|
patterns = [(1.0, patterns)] |
||||
|
|
||||
|
for weight, patterns in patterns: |
||||
|
weight_patterns = [] |
||||
|
|
||||
|
for pattern in patterns: |
||||
|
# Transform into multi-fragment patterns |
||||
|
if type(pattern) is str: |
||||
|
pattern = (pattern,) |
||||
|
|
||||
|
if type(pattern) is tuple and len(pattern) == 2: |
||||
|
if type(pattern[0]) is str and is_list_type(pattern[1], str): |
||||
|
pattern = (pattern,) |
||||
|
|
||||
|
result = [] |
||||
|
for value in pattern: |
||||
|
if type(value) is tuple: |
||||
|
if len(value) == 2: |
||||
|
# Construct OR-list pattern |
||||
|
value = value[0] % '|'.join(value[1]) |
||||
|
elif len(value) == 1: |
||||
|
value = value[0] |
||||
|
|
||||
|
result.append(re.compile(value, re.IGNORECASE)) |
||||
|
|
||||
|
weight_patterns.append(tuple(result)) |
||||
|
|
||||
|
self.regex[group_name].append((weight, weight_patterns)) |
||||
|
|
||||
|
pprint.pprint(self.regex) |
||||
|
|
||||
|
def find_group(self, name): |
||||
|
for group_name, weight_groups in self.regex.items(): |
||||
|
if group_name and group_name == name: |
||||
|
return group_name, weight_groups |
||||
|
|
||||
|
return None |
||||
|
|
||||
|
def parser_match(self, parser, group_name, single=True): |
||||
|
""" |
||||
|
|
||||
|
:type parser: caper.parsers.base.Parser |
||||
|
""" |
||||
|
result = None |
||||
|
|
||||
|
for group, weight_groups in self.regex.items(): |
||||
|
if group_name and group != group_name: |
||||
|
continue |
||||
|
|
||||
|
# TODO handle multiple weights |
||||
|
weight, patterns = weight_groups[0] |
||||
|
|
||||
|
for pattern in patterns: |
||||
|
fragments = [] |
||||
|
pattern_matched = True |
||||
|
pattern_result = {} |
||||
|
|
||||
|
for fragment_pattern in pattern: |
||||
|
if not parser.fragment_available(): |
||||
|
pattern_matched = False |
||||
|
break |
||||
|
|
||||
|
fragment = parser.next_fragment() |
||||
|
fragments.append(fragment) |
||||
|
|
||||
|
Logr.debug('[r"%s"].match("%s")', fragment_pattern.pattern, fragment.value) |
||||
|
match = fragment_pattern.match(fragment.value) |
||||
|
if match: |
||||
|
Logr.debug('Pattern "%s" matched', fragment_pattern.pattern) |
||||
|
else: |
||||
|
pattern_matched = False |
||||
|
break |
||||
|
|
||||
|
pattern_result.update(clean_dict(match.groupdict())) |
||||
|
|
||||
|
if pattern_matched: |
||||
|
if result is None: |
||||
|
result = {} |
||||
|
|
||||
|
if group not in result: |
||||
|
result[group] = {} |
||||
|
|
||||
|
Logr.debug('Matched on <%s>', ' '.join([f.value for f in fragments])) |
||||
|
|
||||
|
result[group].update(pattern_result) |
||||
|
parser.commit() |
||||
|
|
||||
|
if single: |
||||
|
return result |
||||
|
else: |
||||
|
parser.rewind() |
||||
|
|
||||
|
return result |
||||
|
|
||||
|
def value_match(self, value, group_name=None, single=True): |
||||
|
result = None |
||||
|
|
||||
|
for group, weight_groups in self.regex.items(): |
||||
|
if group_name and group != group_name: |
||||
|
continue |
||||
|
|
||||
|
# TODO handle multiple weights |
||||
|
weight, patterns = weight_groups[0] |
||||
|
|
||||
|
for pattern in patterns: |
||||
|
match = pattern[0].match(value) |
||||
|
if not match: |
||||
|
continue |
||||
|
|
||||
|
if result is None: |
||||
|
result = {} |
||||
|
if group not in result: |
||||
|
result[group] = {} |
||||
|
|
||||
|
result[group].update(match.groupdict()) |
||||
|
|
||||
|
if single: |
||||
|
return result |
||||
|
|
||||
|
return result |
||||
|
|
||||
|
def fragment_match(self, fragment, group_name=None): |
||||
|
"""Follow a fragment chain to try find a match |
||||
|
|
||||
|
:type fragment: caper.objects.CaperFragment |
||||
|
:type group_name: str or None |
||||
|
|
||||
|
:return: The weight of the match found between 0.0 and 1.0, |
||||
|
where 1.0 means perfect match and 0.0 means no match |
||||
|
:rtype: (float, dict, int) |
||||
|
""" |
||||
|
|
||||
|
group_name, weight_groups = self.find_group(group_name) |
||||
|
|
||||
|
for weight, patterns in weight_groups: |
||||
|
for pattern in patterns: |
||||
|
cur_fragment = fragment |
||||
|
success = True |
||||
|
result = {} |
||||
|
|
||||
|
# Ignore empty patterns |
||||
|
if len(pattern) < 1: |
||||
|
break |
||||
|
|
||||
|
for fragment_pattern in pattern: |
||||
|
if not cur_fragment: |
||||
|
success = False |
||||
|
break |
||||
|
|
||||
|
match = fragment_pattern.match(cur_fragment.value) |
||||
|
if match: |
||||
|
result.update(match.groupdict()) |
||||
|
else: |
||||
|
success = False |
||||
|
break |
||||
|
|
||||
|
cur_fragment = cur_fragment.right if cur_fragment else None |
||||
|
|
||||
|
if success: |
||||
|
Logr.debug("Found match with weight %s" % weight) |
||||
|
return float(weight), result, len(pattern) |
||||
|
|
||||
|
return 0.0, None, 1 |
@ -0,0 +1,75 @@ |
|||||
|
# Copyright 2013 Dean Gardiner <gardiner91@gmail.com> |
||||
|
# |
||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
# you may not use this file except in compliance with the License. |
||||
|
# You may obtain a copy of the License at |
||||
|
# |
||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
# |
||||
|
# Unless required by applicable law or agreed to in writing, software |
||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
# See the License for the specific language governing permissions and |
||||
|
# limitations under the License. |
||||
|
|
||||
|
from caper.helpers import xrange_six |
||||
|
|
||||
|
|
||||
|
class CaperClosure(object): |
||||
|
def __init__(self, value): |
||||
|
#: :type: str |
||||
|
self.value = value |
||||
|
|
||||
|
#: :type: CaperClosure |
||||
|
self.left = None |
||||
|
#: :type: CaperClosure |
||||
|
self.right = None |
||||
|
|
||||
|
#: :type: list of CaperFragment |
||||
|
self.fragments = [] |
||||
|
|
||||
|
|
||||
|
class CaperFragment(object): |
||||
|
def __init__(self): |
||||
|
#: :type: str |
||||
|
self.value = "" |
||||
|
|
||||
|
#: :type: CaperFragment |
||||
|
self.left = None |
||||
|
#: :type: str |
||||
|
self.left_sep = None |
||||
|
|
||||
|
#: :type: CaperFragment |
||||
|
self.right = None |
||||
|
#: :type: str |
||||
|
self.right_sep = None |
||||
|
|
||||
|
#: :type: int |
||||
|
self.position = None |
||||
|
|
||||
|
def take(self, direction, count, include_self=True): |
||||
|
if direction not in ['left', 'right']: |
||||
|
raise ValueError('Un-Expected value for "direction", expected "left" or "right".') |
||||
|
|
||||
|
result = [] |
||||
|
|
||||
|
if include_self: |
||||
|
result.append(self) |
||||
|
count -= 1 |
||||
|
|
||||
|
cur = self |
||||
|
for x in xrange_six(count): |
||||
|
if cur and getattr(cur, direction): |
||||
|
cur = getattr(cur, direction) |
||||
|
result.append(cur) |
||||
|
else: |
||||
|
result.append(None) |
||||
|
cur = None |
||||
|
|
||||
|
return result |
||||
|
|
||||
|
def take_left(self, count, include_self=True): |
||||
|
return self.take('left', count, include_self) |
||||
|
|
||||
|
def take_right(self, count, include_self=True): |
||||
|
return self.take('right', count, include_self) |
@ -0,0 +1,88 @@ |
|||||
|
# Copyright 2013 Dean Gardiner <gardiner91@gmail.com> |
||||
|
# |
||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
# you may not use this file except in compliance with the License. |
||||
|
# You may obtain a copy of the License at |
||||
|
# |
||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
# |
||||
|
# Unless required by applicable law or agreed to in writing, software |
||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
# See the License for the specific language governing permissions and |
||||
|
# limitations under the License. |
||||
|
|
||||
|
import re |
||||
|
from caper.parsers.base import Parser |
||||
|
|
||||
|
|
||||
|
REGEX_GROUP = re.compile(r'(\(|\[)(?P<group>.*?)(\)|\])', re.IGNORECASE) |
||||
|
|
||||
|
|
||||
|
PATTERN_GROUPS = [ |
||||
|
('identifier', [ |
||||
|
r'S(?P<season>\d+)E(?P<episode>\d+)', |
||||
|
r'(S(?P<season>\d+))|(E(?P<episode>\d+))', |
||||
|
|
||||
|
r'Ep(?P<episode>\d+)', |
||||
|
r'$(?P<absolute>\d+)^', |
||||
|
|
||||
|
(r'Episode', r'(?P<episode>\d+)'), |
||||
|
]), |
||||
|
('video', [ |
||||
|
(r'(?P<h264_profile>%s)', [ |
||||
|
'Hi10P' |
||||
|
]), |
||||
|
(r'.(?P<resolution>%s)', [ |
||||
|
'720p', |
||||
|
'1080p', |
||||
|
|
||||
|
'960x720', |
||||
|
'1920x1080' |
||||
|
]), |
||||
|
(r'(?P<source>%s)', [ |
||||
|
'BD' |
||||
|
]), |
||||
|
]), |
||||
|
('audio', [ |
||||
|
(r'(?P<codec>%s)', [ |
||||
|
'FLAC' |
||||
|
]), |
||||
|
]) |
||||
|
] |
||||
|
|
||||
|
|
||||
|
class AnimeParser(Parser): |
||||
|
def __init__(self): |
||||
|
super(AnimeParser, self).__init__(PATTERN_GROUPS) |
||||
|
|
||||
|
def capture_group(self, fragment): |
||||
|
match = REGEX_GROUP.match(fragment.value) |
||||
|
|
||||
|
if not match: |
||||
|
return None |
||||
|
|
||||
|
return match.group('group') |
||||
|
|
||||
|
def run(self, closures): |
||||
|
""" |
||||
|
:type closures: list of CaperClosure |
||||
|
""" |
||||
|
|
||||
|
self.setup(closures) |
||||
|
|
||||
|
self.capture_closure('group', func=self.capture_group)\ |
||||
|
.execute(once=True) |
||||
|
|
||||
|
self.capture_fragment('show_name', single=False)\ |
||||
|
.until(value__re='identifier')\ |
||||
|
.until(value__re='video')\ |
||||
|
.execute() |
||||
|
|
||||
|
self.capture_fragment('identifier', regex='identifier') \ |
||||
|
.capture_fragment('video', regex='video', single=False) \ |
||||
|
.capture_fragment('audio', regex='audio', single=False) \ |
||||
|
.execute() |
||||
|
|
||||
|
self.result.build() |
||||
|
return self.result |
@ -0,0 +1,136 @@ |
|||||
|
# Copyright 2013 Dean Gardiner <gardiner91@gmail.com> |
||||
|
# |
||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
# you may not use this file except in compliance with the License. |
||||
|
# You may obtain a copy of the License at |
||||
|
# |
||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
# |
||||
|
# Unless required by applicable law or agreed to in writing, software |
||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
# See the License for the specific language governing permissions and |
||||
|
# limitations under the License. |
||||
|
|
||||
|
from logr import Logr |
||||
|
from caper import FragmentMatcher |
||||
|
from caper.group import CaptureGroup |
||||
|
from caper.result import CaperResult, CaperClosureNode |
||||
|
|
||||
|
|
||||
|
class Parser(object): |
||||
|
def __init__(self, pattern_groups): |
||||
|
self.matcher = FragmentMatcher(pattern_groups) |
||||
|
|
||||
|
self.closures = None |
||||
|
#: :type: caper.result.CaperResult |
||||
|
self.result = None |
||||
|
|
||||
|
self._match_cache = None |
||||
|
self._fragment_pos = None |
||||
|
self._closure_pos = None |
||||
|
self._history = None |
||||
|
|
||||
|
self.reset() |
||||
|
|
||||
|
def reset(self): |
||||
|
self.closures = None |
||||
|
self.result = CaperResult() |
||||
|
|
||||
|
self._match_cache = {} |
||||
|
self._fragment_pos = -1 |
||||
|
self._closure_pos = -1 |
||||
|
self._history = [] |
||||
|
|
||||
|
def setup(self, closures): |
||||
|
""" |
||||
|
:type closures: list of CaperClosure |
||||
|
""" |
||||
|
|
||||
|
self.reset() |
||||
|
self.closures = closures |
||||
|
|
||||
|
self.result.heads = [CaperClosureNode(closures[0])] |
||||
|
|
||||
|
def run(self, closures): |
||||
|
""" |
||||
|
:type closures: list of CaperClosure |
||||
|
""" |
||||
|
|
||||
|
raise NotImplementedError() |
||||
|
|
||||
|
# |
||||
|
# Closure Methods |
||||
|
# |
||||
|
|
||||
|
def next_closure(self): |
||||
|
self._closure_pos += 1 |
||||
|
closure = self.closures[self._closure_pos] |
||||
|
|
||||
|
self._history.append(('fragment', -1 - self._fragment_pos)) |
||||
|
self._fragment_pos = -1 |
||||
|
|
||||
|
if self._closure_pos != 0: |
||||
|
self._history.append(('closure', 1)) |
||||
|
|
||||
|
Logr.debug('(next_closure) closure.value: "%s"', closure.value) |
||||
|
return closure |
||||
|
|
||||
|
def closure_available(self): |
||||
|
return self._closure_pos + 1 < len(self.closures) |
||||
|
|
||||
|
# |
||||
|
# Fragment Methods |
||||
|
# |
||||
|
|
||||
|
def next_fragment(self): |
||||
|
closure = self.closures[self._closure_pos] |
||||
|
|
||||
|
self._fragment_pos += 1 |
||||
|
fragment = closure.fragments[self._fragment_pos] |
||||
|
|
||||
|
self._history.append(('fragment', 1)) |
||||
|
|
||||
|
Logr.debug('(next_fragment) closure.value "%s" - fragment.value: "%s"', closure.value, fragment.value) |
||||
|
return fragment |
||||
|
|
||||
|
def fragment_available(self): |
||||
|
if not self.closure_available(): |
||||
|
return False |
||||
|
return self._fragment_pos + 1 < len(self.closures[self._closure_pos].fragments) |
||||
|
|
||||
|
def rewind(self): |
||||
|
for source, delta in reversed(self._history): |
||||
|
Logr.debug('(rewind) Rewinding step: %s', (source, delta)) |
||||
|
if source == 'fragment': |
||||
|
self._fragment_pos -= delta |
||||
|
elif source == 'closure': |
||||
|
self._closure_pos -= delta |
||||
|
else: |
||||
|
raise NotImplementedError() |
||||
|
|
||||
|
self.commit() |
||||
|
|
||||
|
def commit(self): |
||||
|
Logr.debug('(commit)') |
||||
|
self._history = [] |
||||
|
|
||||
|
# |
||||
|
# Capture Methods |
||||
|
# |
||||
|
|
||||
|
def capture_fragment(self, tag, regex=None, func=None, single=True): |
||||
|
return CaptureGroup(self, self.result).capture_fragment( |
||||
|
tag, |
||||
|
regex=regex, |
||||
|
func=func, |
||||
|
single=single |
||||
|
) |
||||
|
|
||||
|
def capture_closure(self, tag, regex=None, func=None, single=True): |
||||
|
return CaptureGroup(self, self.result).capture_closure( |
||||
|
tag, |
||||
|
regex=regex, |
||||
|
func=func, |
||||
|
single=single |
||||
|
) |
@ -0,0 +1,148 @@ |
|||||
|
# Copyright 2013 Dean Gardiner <gardiner91@gmail.com> |
||||
|
# |
||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
# you may not use this file except in compliance with the License. |
||||
|
# You may obtain a copy of the License at |
||||
|
# |
||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
# |
||||
|
# Unless required by applicable law or agreed to in writing, software |
||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
# See the License for the specific language governing permissions and |
||||
|
# limitations under the License. |
||||
|
|
||||
|
from logr import Logr |
||||
|
from caper.parsers.base import Parser |
||||
|
from caper.result import CaperFragmentNode |
||||
|
|
||||
|
|
||||
|
PATTERN_GROUPS = [ |
||||
|
('identifier', [ |
||||
|
(1.0, [ |
||||
|
# S01E01-E02 |
||||
|
('^S(?P<season>\d+)E(?P<episode_from>\d+)$', '^E(?P<episode_to>\d+)$'), |
||||
|
# S02E13 |
||||
|
r'^S(?P<season>\d+)E(?P<episode>\d+)$', |
||||
|
# S01 E13 |
||||
|
(r'^(S(?P<season>\d+))$', r'^(E(?P<episode>\d+))$'), |
||||
|
# S02 |
||||
|
# E13 |
||||
|
r'^((S(?P<season>\d+))|(E(?P<episode>\d+)))$', |
||||
|
# 3x19 |
||||
|
r'^(?P<season>\d+)x(?P<episode>\d+)$', |
||||
|
|
||||
|
# 2013.09.15 |
||||
|
(r'^(?P<year>\d{4})$', r'^(?P<month>\d{2})$', r'^(?P<day>\d{2})$'), |
||||
|
# 09.15.2013 |
||||
|
(r'^(?P<month>\d{2})$', r'^(?P<day>\d{2})$', r'^(?P<year>\d{4})$'), |
||||
|
# TODO - US/UK Date Format Conflict? will only support US format for now.. |
||||
|
# 15.09.2013 |
||||
|
#(r'^(?P<day>\d{2})$', r'^(?P<month>\d{2})$', r'^(?P<year>\d{4})$'), |
||||
|
# 130915 |
||||
|
r'^(?P<year_short>\d{2})(?P<month>\d{2})(?P<day>\d{2})$', |
||||
|
|
||||
|
# Season 3 Episode 14 |
||||
|
(r'^Se(ason)?$', r'^(?P<season>\d+)$', r'^Ep(isode)?$', r'^(?P<episode>\d+)$'), |
||||
|
# Season 3 |
||||
|
(r'^Se(ason)?$', r'^(?P<season>\d+)$'), |
||||
|
# Episode 14 |
||||
|
(r'^Ep(isode)?$', r'^(?P<episode>\d+)$'), |
||||
|
|
||||
|
# Part.3 |
||||
|
# Part.1.and.Part.3 |
||||
|
('^Part$', '(?P<part>\d+)'), |
||||
|
]), |
||||
|
(0.8, [ |
||||
|
# 100 - 1899, 2100 - 9999 (skips 1900 to 2099 - so we don't get years my mistake) |
||||
|
# TODO - Update this pattern on 31 Dec 2099 |
||||
|
r'^(?P<season>([1-9])|(1[0-8])|(2[1-9])|([3-9][0-9]))(?P<episode>\d{2})$' |
||||
|
]), |
||||
|
(0.5, [ |
||||
|
# 100 - 9999 |
||||
|
r'^(?P<season>([1-9])|([1-9][0-9]))(?P<episode>\d{2})$' |
||||
|
]) |
||||
|
]), |
||||
|
('video', [ |
||||
|
r'(?P<aspect>FS|WS)', |
||||
|
|
||||
|
(r'(?P<resolution>%s)', [ |
||||
|
'480p', |
||||
|
'720p', |
||||
|
'1080p' |
||||
|
]), |
||||
|
|
||||
|
(r'(?P<source>%s)', [ |
||||
|
'HDTV', |
||||
|
'PDTV', |
||||
|
'DSR', |
||||
|
'DVDRiP' |
||||
|
]), |
||||
|
|
||||
|
(r'(?P<codec>%s)', [ |
||||
|
'x264', |
||||
|
'XViD' |
||||
|
]), |
||||
|
|
||||
|
(r'(?P<language>%s)', [ |
||||
|
'GERMAN', |
||||
|
'DUTCH', |
||||
|
'FRENCH', |
||||
|
'SWEDiSH', |
||||
|
'DANiSH', |
||||
|
'iTALiAN' |
||||
|
]), |
||||
|
]) |
||||
|
] |
||||
|
|
||||
|
|
||||
|
class SceneParser(Parser): |
||||
|
def __init__(self): |
||||
|
super(SceneParser, self).__init__(PATTERN_GROUPS) |
||||
|
|
||||
|
def capture_group(self, fragment): |
||||
|
if fragment.left_sep == '-' and not fragment.right: |
||||
|
return fragment.value |
||||
|
|
||||
|
return None |
||||
|
|
||||
|
def run(self, closures): |
||||
|
""" |
||||
|
:type closures: list of CaperClosure |
||||
|
""" |
||||
|
|
||||
|
self.setup(closures) |
||||
|
|
||||
|
self.capture_fragment('show_name', single=False)\ |
||||
|
.until(fragment__re='identifier')\ |
||||
|
.until(fragment__re='video')\ |
||||
|
.execute() |
||||
|
|
||||
|
self.capture_fragment('identifier', regex='identifier', single=False)\ |
||||
|
.capture_fragment('video', regex='video', single=False)\ |
||||
|
.until(left_sep__eq='-', right__eq=None)\ |
||||
|
.execute() |
||||
|
|
||||
|
self.capture_fragment('group', func=self.capture_group)\ |
||||
|
.execute() |
||||
|
|
||||
|
self.print_tree(self.result.heads) |
||||
|
|
||||
|
self.result.build() |
||||
|
return self.result |
||||
|
|
||||
|
def print_tree(self, heads): |
||||
|
for head in heads: |
||||
|
head = head if type(head) is list else [head] |
||||
|
|
||||
|
if type(head[0]) is CaperFragmentNode: |
||||
|
for fragment in head[0].fragments: |
||||
|
Logr.debug(fragment.value) |
||||
|
else: |
||||
|
Logr.debug(head[0].closure.value) |
||||
|
|
||||
|
for node in head: |
||||
|
Logr.debug('\t' + str(node).ljust(55) + '\t' + str(node.weight) + '\t' + str(node.match)) |
||||
|
|
||||
|
if len(head) > 0 and head[0].parent: |
||||
|
self.print_tree([head[0].parent]) |
@ -0,0 +1,172 @@ |
|||||
|
# Copyright 2013 Dean Gardiner <gardiner91@gmail.com> |
||||
|
# |
||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
# you may not use this file except in compliance with the License. |
||||
|
# You may obtain a copy of the License at |
||||
|
# |
||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
# |
||||
|
# Unless required by applicable law or agreed to in writing, software |
||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
# See the License for the specific language governing permissions and |
||||
|
# limitations under the License. |
||||
|
|
||||
|
import copy |
||||
|
from logr import Logr |
||||
|
|
||||
|
|
||||
|
GROUP_MATCHES = ['identifier'] |
||||
|
|
||||
|
|
||||
|
class CaperNode(object): |
||||
|
def __init__(self, closure, parent=None, tag=None, weight=None, match=None): |
||||
|
""" |
||||
|
:type parent: CaperNode |
||||
|
:type weight: float |
||||
|
""" |
||||
|
|
||||
|
#: :type: caper.objects.CaperClosure |
||||
|
self.closure = closure |
||||
|
#: :type: CaperNode |
||||
|
self.parent = parent |
||||
|
#: :type: str |
||||
|
self.tag = tag |
||||
|
#: :type: float |
||||
|
self.weight = weight |
||||
|
#: :type: dict |
||||
|
self.match = match |
||||
|
#: :type: list of CaptureGroup |
||||
|
self.finished_groups = [] |
||||
|
|
||||
|
def next(self): |
||||
|
raise NotImplementedError() |
||||
|
|
||||
|
|
||||
|
class CaperClosureNode(CaperNode): |
||||
|
def __init__(self, closure, parent=None, tag=None, weight=None, match=None): |
||||
|
""" |
||||
|
:type closure: caper.objects.CaperClosure or list of caper.objects.CaperClosure |
||||
|
""" |
||||
|
super(CaperClosureNode, self).__init__(closure, parent, tag, weight, match) |
||||
|
|
||||
|
def next(self): |
||||
|
if self.closure and len(self.closure.fragments) > 0: |
||||
|
return self.closure.fragments[0] |
||||
|
return None |
||||
|
|
||||
|
|
||||
|
class CaperFragmentNode(CaperNode): |
||||
|
def __init__(self, closure, fragments, parent=None, tag=None, weight=None, match=None): |
||||
|
""" |
||||
|
:type closure: caper.objects.CaperClosure |
||||
|
:type fragments: list of caper.objects.CaperFragment |
||||
|
""" |
||||
|
super(CaperFragmentNode, self).__init__(closure, parent, tag, weight, match) |
||||
|
|
||||
|
#: :type: caper.objects.CaperFragment or list of caper.objects.CaperFragment |
||||
|
self.fragments = fragments |
||||
|
|
||||
|
def next(self): |
||||
|
if len(self.fragments) > 0 and self.fragments[-1] and self.fragments[-1].right: |
||||
|
return self.fragments[-1].right |
||||
|
|
||||
|
if self.closure.right: |
||||
|
return self.closure.right |
||||
|
|
||||
|
return None |
||||
|
|
||||
|
|
||||
|
class CaperResult(object): |
||||
|
def __init__(self): |
||||
|
#: :type: list of CaperNode |
||||
|
self.heads = [] |
||||
|
|
||||
|
self.chains = [] |
||||
|
|
||||
|
def build(self): |
||||
|
max_matched = 0 |
||||
|
|
||||
|
for head in self.heads: |
||||
|
for chain in self.combine_chain(head): |
||||
|
if chain.num_matched > max_matched: |
||||
|
max_matched = chain.num_matched |
||||
|
|
||||
|
self.chains.append(chain) |
||||
|
|
||||
|
for chain in self.chains: |
||||
|
chain.weights.append(chain.num_matched / float(max_matched)) |
||||
|
chain.finish() |
||||
|
|
||||
|
self.chains.sort(key=lambda chain: chain.weight, reverse=True) |
||||
|
|
||||
|
for chain in self.chains: |
||||
|
Logr.debug("chain weight: %.02f", chain.weight) |
||||
|
Logr.debug("\tInfo: %s", chain.info) |
||||
|
|
||||
|
Logr.debug("\tWeights: %s", chain.weights) |
||||
|
Logr.debug("\tNumber of Fragments Matched: %s", chain.num_matched) |
||||
|
|
||||
|
def combine_chain(self, subject, chain=None): |
||||
|
nodes = subject if type(subject) is list else [subject] |
||||
|
|
||||
|
if chain is None: |
||||
|
chain = CaperResultChain() |
||||
|
|
||||
|
result = [] |
||||
|
|
||||
|
for x, node in enumerate(nodes): |
||||
|
node_chain = chain if x == len(nodes) - 1 else chain.copy() |
||||
|
|
||||
|
if not node.parent: |
||||
|
result.append(node_chain) |
||||
|
continue |
||||
|
|
||||
|
# Skip over closure nodes |
||||
|
if type(node) is CaperClosureNode: |
||||
|
result.extend(self.combine_chain(node.parent, node_chain)) |
||||
|
|
||||
|
# Parse fragment matches |
||||
|
if type(node) is CaperFragmentNode: |
||||
|
node_chain.update(node) |
||||
|
|
||||
|
result.extend(self.combine_chain(node.parent, node_chain)) |
||||
|
|
||||
|
return result |
||||
|
|
||||
|
|
||||
|
class CaperResultChain(object): |
||||
|
def __init__(self): |
||||
|
#: :type: float |
||||
|
self.weight = None |
||||
|
self.info = {} |
||||
|
self.num_matched = 0 |
||||
|
|
||||
|
self.weights = [] |
||||
|
|
||||
|
def update(self, subject): |
||||
|
if subject.weight is None: |
||||
|
return |
||||
|
|
||||
|
self.num_matched += len(subject.fragments) if subject.fragments is not None else 0 |
||||
|
self.weights.append(subject.weight) |
||||
|
|
||||
|
if subject.match: |
||||
|
if subject.tag not in self.info: |
||||
|
self.info[subject.tag] = [] |
||||
|
|
||||
|
self.info[subject.tag].insert(0, subject.match) |
||||
|
|
||||
|
def finish(self): |
||||
|
self.weight = sum(self.weights) / len(self.weights) |
||||
|
|
||||
|
def copy(self): |
||||
|
chain = CaperResultChain() |
||||
|
|
||||
|
chain.weight = self.weight |
||||
|
chain.info = copy.deepcopy(self.info) |
||||
|
|
||||
|
chain.num_matched = self.num_matched |
||||
|
chain.weights = copy.copy(self.weights) |
||||
|
|
||||
|
return chain |
@ -0,0 +1,72 @@ |
|||||
|
# Copyright 2013 Dean Gardiner <gardiner91@gmail.com> |
||||
|
# |
||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
# you may not use this file except in compliance with the License. |
||||
|
# You may obtain a copy of the License at |
||||
|
# |
||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
# |
||||
|
# Unless required by applicable law or agreed to in writing, software |
||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
# See the License for the specific language governing permissions and |
||||
|
# limitations under the License. |
||||
|
|
||||
|
from logr import Logr |
||||
|
|
||||
|
|
||||
|
class CaptureStep(object): |
||||
|
REPR_KEYS = ['regex', 'func', 'single'] |
||||
|
|
||||
|
def __init__(self, capture_group, tag, source, regex=None, func=None, single=None): |
||||
|
#: @type: CaptureGroup |
||||
|
self.capture_group = capture_group |
||||
|
|
||||
|
#: @type: str |
||||
|
self.tag = tag |
||||
|
#: @type: str |
||||
|
self.source = source |
||||
|
#: @type: str |
||||
|
self.regex = regex |
||||
|
#: @type: function |
||||
|
self.func = func |
||||
|
#: @type: bool |
||||
|
self.single = single |
||||
|
|
||||
|
def _get_next_subject(self, parser): |
||||
|
if self.source == 'fragment': |
||||
|
if not parser.fragment_available(): |
||||
|
return None |
||||
|
return parser.next_fragment() |
||||
|
elif self.source == 'closure': |
||||
|
if not parser.closure_available(): |
||||
|
return None |
||||
|
return parser.next_closure() |
||||
|
|
||||
|
raise NotImplementedError() |
||||
|
|
||||
|
def execute(self, fragment): |
||||
|
if self.regex: |
||||
|
weight, match, num_fragments = self.capture_group.parser.matcher.fragment_match(fragment, self.regex) |
||||
|
Logr.debug('(execute) [regex] tag: "%s"', self.tag) |
||||
|
if match: |
||||
|
return True, weight, match, num_fragments |
||||
|
elif self.func: |
||||
|
match = self.func(fragment) |
||||
|
Logr.debug('(execute) [func] %s += "%s"', self.tag, match) |
||||
|
if match: |
||||
|
return True, 1.0, match, 1 |
||||
|
else: |
||||
|
Logr.debug('(execute) [raw] %s += "%s"', self.tag, fragment.value) |
||||
|
return True, 1.0, fragment.value, 1 |
||||
|
|
||||
|
return False, None, None, 1 |
||||
|
|
||||
|
def __repr__(self): |
||||
|
attribute_values = [key + '=' + repr(getattr(self, key)) |
||||
|
for key in self.REPR_KEYS |
||||
|
if hasattr(self, key) and getattr(self, key)] |
||||
|
|
||||
|
attribute_string = ', ' + ', '.join(attribute_values) if len(attribute_values) > 0 else '' |
||||
|
|
||||
|
return "CaptureStep('%s'%s)" % (self.tag, attribute_string) |
@ -0,0 +1,71 @@ |
|||||
|
#!/usr/bin/env python |
||||
|
# -*- coding: utf-8 -*- |
||||
|
# |
||||
|
# GuessIt - A library for guessing information from filenames |
||||
|
# Copyright (c) 2013 Nicolas Wack <wackou@gmail.com> |
||||
|
# |
||||
|
# GuessIt is free software; you can redistribute it and/or modify it under |
||||
|
# the terms of the Lesser GNU General Public License as published by |
||||
|
# the Free Software Foundation; either version 3 of the License, or |
||||
|
# (at your option) any later version. |
||||
|
# |
||||
|
# GuessIt is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# Lesser GNU General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the Lesser GNU General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
|
||||
|
from __future__ import unicode_literals |
||||
|
from guessit.transfo import SingleNodeGuesser |
||||
|
from guessit.patterns import find_properties |
||||
|
import re |
||||
|
import logging |
||||
|
|
||||
|
log = logging.getLogger(__name__) |
||||
|
|
||||
|
|
||||
|
def guess_properties(string): |
||||
|
try: |
||||
|
prop, value, pos, end = find_properties(string)[0] |
||||
|
return { prop: value }, (pos, end) |
||||
|
except IndexError: |
||||
|
return None, None |
||||
|
|
||||
|
_idnum = re.compile(r'(?P<idNumber>[a-zA-Z0-9-]{10,})') # 1.0, (0, 0)) |
||||
|
|
||||
|
def guess_idnumber(string): |
||||
|
match = _idnum.search(string) |
||||
|
if match is not None: |
||||
|
result = match.groupdict() |
||||
|
switch_count = 0 |
||||
|
DIGIT = 0 |
||||
|
LETTER = 1 |
||||
|
OTHER = 2 |
||||
|
last = LETTER |
||||
|
for c in result['idNumber']: |
||||
|
if c in '0123456789': |
||||
|
ci = DIGIT |
||||
|
elif c in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ': |
||||
|
ci = LETTER |
||||
|
else: |
||||
|
ci = OTHER |
||||
|
|
||||
|
if ci != last: |
||||
|
switch_count += 1 |
||||
|
|
||||
|
last = ci |
||||
|
|
||||
|
switch_ratio = float(switch_count) / len(result['idNumber']) |
||||
|
|
||||
|
# only return the result as probable if we alternate often between |
||||
|
# char type (more likely for hash values than for common words) |
||||
|
if switch_ratio > 0.4: |
||||
|
return result, match.span() |
||||
|
|
||||
|
return None, None |
||||
|
|
||||
|
def process(mtree): |
||||
|
SingleNodeGuesser(guess_idnumber, 0.4, log).process(mtree) |
@ -0,0 +1,201 @@ |
|||||
|
# logr - Simple python logging wrapper |
||||
|
# Packed by Dean Gardiner <gardiner91@gmail.com> |
||||
|
# |
||||
|
# File part of: |
||||
|
# rdio-sock - Rdio WebSocket Library |
||||
|
# Copyright (C) 2013 fzza- <fzzzzzzzza@gmail.com> |
||||
|
|
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU General Public License as published by |
||||
|
# the Free Software Foundation, either version 3 of the License, or |
||||
|
# (at your option) any later version. |
||||
|
|
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU General Public License for more details. |
||||
|
|
||||
|
# You should have received a copy of the GNU General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
|
||||
|
import inspect |
||||
|
import logging |
||||
|
import os |
||||
|
import sys |
||||
|
|
||||
|
IGNORE = () |
||||
|
PY3 = sys.version_info[0] == 3 |
||||
|
|
||||
|
|
||||
|
class Logr(object): |
||||
|
loggers = {} |
||||
|
handler = None |
||||
|
|
||||
|
@staticmethod |
||||
|
def configure(level=logging.WARNING, handler=None, formatter=None): |
||||
|
"""Configure Logr |
||||
|
|
||||
|
@param handler: Logger message handler |
||||
|
@type handler: logging.Handler or None |
||||
|
|
||||
|
@param formatter: Logger message Formatter |
||||
|
@type formatter: logging.Formatter or None |
||||
|
""" |
||||
|
if formatter is None: |
||||
|
formatter = LogrFormatter() |
||||
|
|
||||
|
if handler is None: |
||||
|
handler = logging.StreamHandler() |
||||
|
|
||||
|
handler.setFormatter(formatter) |
||||
|
handler.setLevel(level) |
||||
|
Logr.handler = handler |
||||
|
|
||||
|
@staticmethod |
||||
|
def configure_check(): |
||||
|
if Logr.handler is None: |
||||
|
Logr.configure() |
||||
|
|
||||
|
@staticmethod |
||||
|
def _get_name_from_path(filename): |
||||
|
try: |
||||
|
return os.path.splitext(os.path.basename(filename))[0] |
||||
|
except TypeError: |
||||
|
return "<unknown>" |
||||
|
|
||||
|
@staticmethod |
||||
|
def get_logger_name(): |
||||
|
stack = inspect.stack() |
||||
|
|
||||
|
for x in xrange_six(len(stack)): |
||||
|
frame = stack[x][0] |
||||
|
name = None |
||||
|
|
||||
|
# Try find name of function defined inside a class |
||||
|
if len(frame.f_code.co_varnames) > 0: |
||||
|
self_argument = frame.f_code.co_varnames[0] |
||||
|
|
||||
|
if self_argument == 'self' and self_argument in frame.f_locals: |
||||
|
instance = frame.f_locals[self_argument] |
||||
|
|
||||
|
class_ = instance.__class__ |
||||
|
class_name = class_.__name__ |
||||
|
module_name = class_.__module__ |
||||
|
|
||||
|
if module_name != '__main__': |
||||
|
name = module_name + '.' + class_name |
||||
|
else: |
||||
|
name = class_name |
||||
|
|
||||
|
# Try find name of function defined outside of a class |
||||
|
if name is None: |
||||
|
if frame.f_code.co_name in frame.f_globals: |
||||
|
name = frame.f_globals.get('__name__') |
||||
|
if name == '__main__': |
||||
|
name = Logr._get_name_from_path(frame.f_globals.get('__file__')) |
||||
|
name = name |
||||
|
elif frame.f_code.co_name == '<module>': |
||||
|
name = Logr._get_name_from_path(frame.f_globals.get('__file__')) |
||||
|
|
||||
|
if name is not None and name not in IGNORE: |
||||
|
return name |
||||
|
|
||||
|
return "" |
||||
|
|
||||
|
@staticmethod |
||||
|
def get_logger(): |
||||
|
"""Get or create logger (if it does not exist) |
||||
|
|
||||
|
@rtype: RootLogger |
||||
|
""" |
||||
|
name = Logr.get_logger_name() |
||||
|
if name not in Logr.loggers: |
||||
|
Logr.configure_check() |
||||
|
Logr.loggers[name] = logging.Logger(name) |
||||
|
Logr.loggers[name].addHandler(Logr.handler) |
||||
|
return Logr.loggers[name] |
||||
|
|
||||
|
@staticmethod |
||||
|
def debug(msg, *args, **kwargs): |
||||
|
Logr.get_logger().debug(msg, *args, **kwargs) |
||||
|
|
||||
|
@staticmethod |
||||
|
def info(msg, *args, **kwargs): |
||||
|
Logr.get_logger().info(msg, *args, **kwargs) |
||||
|
|
||||
|
@staticmethod |
||||
|
def warning(msg, *args, **kwargs): |
||||
|
Logr.get_logger().warning(msg, *args, **kwargs) |
||||
|
|
||||
|
warn = warning |
||||
|
|
||||
|
@staticmethod |
||||
|
def error(msg, *args, **kwargs): |
||||
|
Logr.get_logger().error(msg, *args, **kwargs) |
||||
|
|
||||
|
@staticmethod |
||||
|
def exception(msg, *args, **kwargs): |
||||
|
Logr.get_logger().exception(msg, *args, **kwargs) |
||||
|
|
||||
|
@staticmethod |
||||
|
def critical(msg, *args, **kwargs): |
||||
|
Logr.get_logger().critical(msg, *args, **kwargs) |
||||
|
|
||||
|
fatal = critical |
||||
|
|
||||
|
@staticmethod |
||||
|
def log(level, msg, *args, **kwargs): |
||||
|
Logr.get_logger().log(level, msg, *args, **kwargs) |
||||
|
|
||||
|
|
||||
|
class LogrFormatter(logging.Formatter): |
||||
|
LENGTH_NAME = 32 |
||||
|
LENGTH_LEVEL_NAME = 5 |
||||
|
|
||||
|
def __init__(self, fmt=None, datefmt=None): |
||||
|
if sys.version_info[:2] > (2,6): |
||||
|
super(LogrFormatter, self).__init__(fmt, datefmt) |
||||
|
else: |
||||
|
logging.Formatter.__init__(self, fmt, datefmt) |
||||
|
|
||||
|
def usesTime(self): |
||||
|
return True |
||||
|
|
||||
|
def format(self, record): |
||||
|
record.message = record.getMessage() |
||||
|
if self.usesTime(): |
||||
|
record.asctime = self.formatTime(record, self.datefmt) |
||||
|
|
||||
|
s = "%(asctime)s %(name)s %(levelname)s %(message)s" % { |
||||
|
'asctime': record.asctime, |
||||
|
'name': record.name[-self.LENGTH_NAME:].rjust(self.LENGTH_NAME, ' '), |
||||
|
'levelname': record.levelname[:self.LENGTH_LEVEL_NAME].ljust(self.LENGTH_LEVEL_NAME, ' '), |
||||
|
'message': record.message |
||||
|
} |
||||
|
|
||||
|
if record.exc_info: |
||||
|
if not record.exc_text: |
||||
|
record.exc_text = self.formatException(record.exc_info) |
||||
|
if record.exc_text: |
||||
|
if s[-1:] != "\n": |
||||
|
s += "\n" |
||||
|
try: |
||||
|
s += record.exc_text |
||||
|
except UnicodeError: |
||||
|
s = s + record.exc_text.decode(sys.getfilesystemencoding(), |
||||
|
'replace') |
||||
|
return s |
||||
|
|
||||
|
|
||||
|
def xrange_six(start, stop=None, step=None): |
||||
|
if stop is not None and step is not None: |
||||
|
if PY3: |
||||
|
return range(start, stop, step) |
||||
|
else: |
||||
|
return xrange(start, stop, step) |
||||
|
else: |
||||
|
if PY3: |
||||
|
return range(start) |
||||
|
else: |
||||
|
return xrange(start) |
@ -1,27 +0,0 @@ |
|||||
Metadata-Version: 1.0 |
|
||||
Name: pyUnRAR2 |
|
||||
Version: 0.99.2 |
|
||||
Summary: Improved Python wrapper around the free UnRAR.dll |
|
||||
Home-page: http://code.google.com/py-unrar2 |
|
||||
Author: Konstantin Yegupov |
|
||||
Author-email: yk4ever@gmail.com |
|
||||
License: MIT |
|
||||
Description: pyUnRAR2 is a ctypes based wrapper around the free UnRAR.dll. |
|
||||
|
|
||||
It is an modified version of Jimmy Retzlaff's pyUnRAR - more simple, |
|
||||
stable and foolproof. |
|
||||
Notice that it has INCOMPATIBLE interface. |
|
||||
|
|
||||
It enables reading and unpacking of archives created with the |
|
||||
RAR/WinRAR archivers. There is a low-level interface which is very |
|
||||
similar to the C interface provided by UnRAR. There is also a |
|
||||
higher level interface which makes some common operations easier. |
|
||||
Platform: Windows |
|
||||
Classifier: Development Status :: 4 - Beta |
|
||||
Classifier: Environment :: Win32 (MS Windows) |
|
||||
Classifier: License :: OSI Approved :: MIT License |
|
||||
Classifier: Natural Language :: English |
|
||||
Classifier: Operating System :: Microsoft :: Windows |
|
||||
Classifier: Programming Language :: Python |
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules |
|
||||
Classifier: Topic :: System :: Archiving :: Compression |
|
@ -1,191 +0,0 @@ |
|||||
|
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> |
|
||||
<html><head><title>Python: package UnRAR2</title> |
|
||||
</head><body bgcolor="#f0f0f8"> |
|
||||
|
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading"> |
|
||||
<tr bgcolor="#7799ee"> |
|
||||
<td valign=bottom> <br> |
|
||||
<font color="#ffffff" face="helvetica, arial"> <br><big><big><strong>UnRAR2</strong></big></big> (version 0.99.1)</font></td |
|
||||
><td align=right valign=bottom |
|
||||
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:///C|/python26/lib/site-packages/unrar2/__init__.py">c:\python26\lib\site-packages\unrar2\__init__.py</a></font></td></tr></table> |
|
||||
<p><tt>pyUnRAR2 is a ctypes based wrapper around the free UnRAR.dll. <br> |
|
||||
<br> |
|
||||
It is an modified version of Jimmy Retzlaff's pyUnRAR - more simple,<br> |
|
||||
stable and foolproof.<br> |
|
||||
Notice that it has INCOMPATIBLE interface.<br> |
|
||||
<br> |
|
||||
It enables reading and unpacking of archives created with the<br> |
|
||||
RAR/WinRAR archivers. There is a low-level interface which is very<br> |
|
||||
similar to the C interface provided by UnRAR. There is also a<br> |
|
||||
higher level interface which makes some common operations easier.</tt></p> |
|
||||
<p> |
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> |
|
||||
<tr bgcolor="#aa55cc"> |
|
||||
<td colspan=3 valign=bottom> <br> |
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr> |
|
||||
|
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </td> |
|
||||
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="UnRAR2.rar_exceptions.html">rar_exceptions</a><br> |
|
||||
<a href="UnRAR2.setup.html">setup</a><br> |
|
||||
</td><td width="25%" valign=top><a href="UnRAR2.test_UnRAR2.html">test_UnRAR2</a><br> |
|
||||
<a href="UnRAR2.unix.html">unix</a><br> |
|
||||
</td><td width="25%" valign=top><a href="UnRAR2.windows.html">windows</a><br> |
|
||||
</td><td width="25%" valign=top></td></tr></table></td></tr></table><p> |
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> |
|
||||
<tr bgcolor="#ee77aa"> |
|
||||
<td colspan=3 valign=bottom> <br> |
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr> |
|
||||
|
|
||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </td> |
|
||||
<td width="100%"><dl> |
|
||||
<dt><font face="helvetica, arial"><a href="UnRAR2.windows.html#RarFileImplementation">UnRAR2.windows.RarFileImplementation</a>(<a href="__builtin__.html#object">__builtin__.object</a>) |
|
||||
</font></dt><dd> |
|
||||
<dl> |
|
||||
<dt><font face="helvetica, arial"><a href="UnRAR2.html#RarFile">RarFile</a> |
|
||||
</font></dt></dl> |
|
||||
</dd> |
|
||||
<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a> |
|
||||
</font></dt><dd> |
|
||||
<dl> |
|
||||
<dt><font face="helvetica, arial"><a href="UnRAR2.html#RarInfo">RarInfo</a> |
|
||||
</font></dt></dl> |
|
||||
</dd> |
|
||||
</dl> |
|
||||
<p> |
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> |
|
||||
<tr bgcolor="#ffc8d8"> |
|
||||
<td colspan=3 valign=bottom> <br> |
|
||||
<font color="#000000" face="helvetica, arial"><a name="RarFile">class <strong>RarFile</strong></a>(<a href="UnRAR2.windows.html#RarFileImplementation">UnRAR2.windows.RarFileImplementation</a>)</font></td></tr> |
|
||||
|
|
||||
<tr><td bgcolor="#ffc8d8"><tt> </tt></td><td> </td> |
|
||||
<td width="100%"><dl><dt>Method resolution order:</dt> |
|
||||
<dd><a href="UnRAR2.html#RarFile">RarFile</a></dd> |
|
||||
<dd><a href="UnRAR2.windows.html#RarFileImplementation">UnRAR2.windows.RarFileImplementation</a></dd> |
|
||||
<dd><a href="__builtin__.html#object">__builtin__.object</a></dd> |
|
||||
</dl> |
|
||||
<hr> |
|
||||
Methods defined here:<br> |
|
||||
<dl><dt><a name="RarFile-__del__"><strong>__del__</strong></a>(self)</dt></dl> |
|
||||
|
|
||||
<dl><dt><a name="RarFile-__init__"><strong>__init__</strong></a>(self, archiveName, password<font color="#909090">=None</font>)</dt><dd><tt>Instantiate the archive.<br> |
|
||||
<br> |
|
||||
archiveName is the name of the RAR file.<br> |
|
||||
password is used to decrypt the files in the archive.<br> |
|
||||
<br> |
|
||||
Properties:<br> |
|
||||
comment - comment associated with the archive<br> |
|
||||
<br> |
|
||||
>>> print <a href="#RarFile">RarFile</a>('test.rar').comment<br> |
|
||||
This is a test.</tt></dd></dl> |
|
||||
|
|
||||
<dl><dt><a name="RarFile-extract"><strong>extract</strong></a>(self, condition<font color="#909090">='*'</font>, path<font color="#909090">='.'</font>, withSubpath<font color="#909090">=True</font>, overwrite<font color="#909090">=True</font>)</dt><dd><tt>Extract specific files from archive to disk.<br> |
|
||||
<br> |
|
||||
If "condition" is a list of numbers, then extract files which have those positions in infolist.<br> |
|
||||
If "condition" is a string, then it is treated as a wildcard for names of files to extract.<br> |
|
||||
If "condition" is a function, it is treated as a callback function, which accepts a <a href="#RarInfo">RarInfo</a> <a href="__builtin__.html#object">object</a><br> |
|
||||
and returns either boolean True (extract) or boolean False (skip).<br> |
|
||||
DEPRECATED: If "condition" callback returns string (only supported for Windows) - <br> |
|
||||
that string will be used as a new name to save the file under.<br> |
|
||||
If "condition" is omitted, all files are extracted.<br> |
|
||||
<br> |
|
||||
"path" is a directory to extract to<br> |
|
||||
"withSubpath" flag denotes whether files are extracted with their full path in the archive.<br> |
|
||||
"overwrite" flag denotes whether extracted files will overwrite old ones. Defaults to true.<br> |
|
||||
<br> |
|
||||
Returns list of RarInfos for extracted files.</tt></dd></dl> |
|
||||
|
|
||||
<dl><dt><a name="RarFile-infoiter"><strong>infoiter</strong></a>(self)</dt><dd><tt>Iterate over all the files in the archive, generating RarInfos.<br> |
|
||||
<br> |
|
||||
>>> import os<br> |
|
||||
>>> for fileInArchive in <a href="#RarFile">RarFile</a>('test.rar').<a href="#RarFile-infoiter">infoiter</a>():<br> |
|
||||
... print os.path.split(fileInArchive.filename)[-1],<br> |
|
||||
... print fileInArchive.isdir,<br> |
|
||||
... print fileInArchive.size,<br> |
|
||||
... print fileInArchive.comment,<br> |
|
||||
... print tuple(fileInArchive.datetime)[0:5],<br> |
|
||||
... print time.strftime('%a, %d %b %Y %H:%M', fileInArchive.datetime)<br> |
|
||||
test True 0 None (2003, 6, 30, 1, 59) Mon, 30 Jun 2003 01:59<br> |
|
||||
test.txt False 20 None (2003, 6, 30, 2, 1) Mon, 30 Jun 2003 02:01<br> |
|
||||
this.py False 1030 None (2002, 2, 8, 16, 47) Fri, 08 Feb 2002 16:47</tt></dd></dl> |
|
||||
|
|
||||
<dl><dt><a name="RarFile-infolist"><strong>infolist</strong></a>(self)</dt><dd><tt>Return a list of RarInfos, descripting the contents of the archive.</tt></dd></dl> |
|
||||
|
|
||||
<dl><dt><a name="RarFile-read_files"><strong>read_files</strong></a>(self, condition<font color="#909090">='*'</font>)</dt><dd><tt>Read specific files from archive into memory.<br> |
|
||||
If "condition" is a list of numbers, then return files which have those positions in infolist.<br> |
|
||||
If "condition" is a string, then it is treated as a wildcard for names of files to extract.<br> |
|
||||
If "condition" is a function, it is treated as a callback function, which accepts a <a href="#RarInfo">RarInfo</a> <a href="__builtin__.html#object">object</a> <br> |
|
||||
and returns boolean True (extract) or False (skip).<br> |
|
||||
If "condition" is omitted, all files are returned.<br> |
|
||||
<br> |
|
||||
Returns list of tuples (<a href="#RarInfo">RarInfo</a> info, str contents)</tt></dd></dl> |
|
||||
|
|
||||
<hr> |
|
||||
Methods inherited from <a href="UnRAR2.windows.html#RarFileImplementation">UnRAR2.windows.RarFileImplementation</a>:<br> |
|
||||
<dl><dt><a name="RarFile-destruct"><strong>destruct</strong></a>(self)</dt></dl> |
|
||||
|
|
||||
<dl><dt><a name="RarFile-init"><strong>init</strong></a>(self, password<font color="#909090">=None</font>)</dt></dl> |
|
||||
|
|
||||
<dl><dt><a name="RarFile-make_sure_ready"><strong>make_sure_ready</strong></a>(self)</dt></dl> |
|
||||
|
|
||||
<hr> |
|
||||
Data descriptors inherited from <a href="UnRAR2.windows.html#RarFileImplementation">UnRAR2.windows.RarFileImplementation</a>:<br> |
|
||||
<dl><dt><strong>__dict__</strong></dt> |
|
||||
<dd><tt>dictionary for instance variables (if defined)</tt></dd> |
|
||||
</dl> |
|
||||
<dl><dt><strong>__weakref__</strong></dt> |
|
||||
<dd><tt>list of weak references to the object (if defined)</tt></dd> |
|
||||
</dl> |
|
||||
</td></tr></table> <p> |
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> |
|
||||
<tr bgcolor="#ffc8d8"> |
|
||||
<td colspan=3 valign=bottom> <br> |
|
||||
<font color="#000000" face="helvetica, arial"><a name="RarInfo">class <strong>RarInfo</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr> |
|
||||
|
|
||||
<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td> |
|
||||
<td colspan=2><tt>Represents a file header in an archive. Don't instantiate directly.<br> |
|
||||
Use only to obtain information about file.<br> |
|
||||
YOU CANNOT EXTRACT FILE CONTENTS USING THIS OBJECT.<br> |
|
||||
USE METHODS OF <a href="#RarFile">RarFile</a> CLASS INSTEAD.<br> |
|
||||
<br> |
|
||||
Properties:<br> |
|
||||
index - index of file within the archive<br> |
|
||||
filename - name of the file in the archive including path (if any)<br> |
|
||||
datetime - file date/time as a struct_time suitable for time.strftime<br> |
|
||||
isdir - True if the file is a directory<br> |
|
||||
size - size in bytes of the uncompressed file<br> |
|
||||
comment - comment associated with the file<br> |
|
||||
<br> |
|
||||
Note - this is not currently intended to be a Python file-like <a href="__builtin__.html#object">object</a>.<br> </tt></td></tr> |
|
||||
<tr><td> </td> |
|
||||
<td width="100%">Methods defined here:<br> |
|
||||
<dl><dt><a name="RarInfo-__init__"><strong>__init__</strong></a>(self, rarfile, data)</dt></dl> |
|
||||
|
|
||||
<dl><dt><a name="RarInfo-__str__"><strong>__str__</strong></a>(self)</dt></dl> |
|
||||
|
|
||||
<hr> |
|
||||
Data descriptors defined here:<br> |
|
||||
<dl><dt><strong>__dict__</strong></dt> |
|
||||
<dd><tt>dictionary for instance variables (if defined)</tt></dd> |
|
||||
</dl> |
|
||||
<dl><dt><strong>__weakref__</strong></dt> |
|
||||
<dd><tt>list of weak references to the object (if defined)</tt></dd> |
|
||||
</dl> |
|
||||
</td></tr></table></td></tr></table><p> |
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> |
|
||||
<tr bgcolor="#eeaa77"> |
|
||||
<td colspan=3 valign=bottom> <br> |
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr> |
|
||||
|
|
||||
<tr><td bgcolor="#eeaa77"><tt> </tt></td><td> </td> |
|
||||
<td width="100%"><dl><dt><a name="-condition2checker"><strong>condition2checker</strong></a>(condition)</dt><dd><tt>Converts different condition types to callback</tt></dd></dl> |
|
||||
</td></tr></table><p> |
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section"> |
|
||||
<tr bgcolor="#55aa55"> |
|
||||
<td colspan=3 valign=bottom> <br> |
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr> |
|
||||
|
|
||||
<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </td> |
|
||||
<td width="100%"><strong>__version__</strong> = '0.99.1'<br> |
|
||||
<strong>in_windows</strong> = True</td></tr></table> |
|
||||
</body></html> |
|
@ -1,18 +0,0 @@ |
|||||
The unrar.dll library is freeware. This means: |
|
||||
|
|
||||
1. All copyrights to RAR and the unrar.dll are exclusively |
|
||||
owned by the author - Alexander Roshal. |
|
||||
|
|
||||
2. The unrar.dll library may be used in any software to handle RAR |
|
||||
archives without limitations free of charge. |
|
||||
|
|
||||
3. THE RAR ARCHIVER AND THE UNRAR.DLL LIBRARY ARE DISTRIBUTED "AS IS". |
|
||||
NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT |
|
||||
YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS, |
|
||||
DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING |
|
||||
OR MISUSING THIS SOFTWARE. |
|
||||
|
|
||||
Thank you for your interest in RAR and unrar.dll. |
|
||||
|
|
||||
|
|
||||
Alexander L. Roshal |
|
@ -1,140 +0,0 @@ |
|||||
#ifndef _UNRAR_DLL_ |
|
||||
#define _UNRAR_DLL_ |
|
||||
|
|
||||
#define ERAR_END_ARCHIVE 10 |
|
||||
#define ERAR_NO_MEMORY 11 |
|
||||
#define ERAR_BAD_DATA 12 |
|
||||
#define ERAR_BAD_ARCHIVE 13 |
|
||||
#define ERAR_UNKNOWN_FORMAT 14 |
|
||||
#define ERAR_EOPEN 15 |
|
||||
#define ERAR_ECREATE 16 |
|
||||
#define ERAR_ECLOSE 17 |
|
||||
#define ERAR_EREAD 18 |
|
||||
#define ERAR_EWRITE 19 |
|
||||
#define ERAR_SMALL_BUF 20 |
|
||||
#define ERAR_UNKNOWN 21 |
|
||||
#define ERAR_MISSING_PASSWORD 22 |
|
||||
|
|
||||
#define RAR_OM_LIST 0 |
|
||||
#define RAR_OM_EXTRACT 1 |
|
||||
#define RAR_OM_LIST_INCSPLIT 2 |
|
||||
|
|
||||
#define RAR_SKIP 0 |
|
||||
#define RAR_TEST 1 |
|
||||
#define RAR_EXTRACT 2 |
|
||||
|
|
||||
#define RAR_VOL_ASK 0 |
|
||||
#define RAR_VOL_NOTIFY 1 |
|
||||
|
|
||||
#define RAR_DLL_VERSION 4 |
|
||||
|
|
||||
#ifdef _UNIX |
|
||||
#define CALLBACK |
|
||||
#define PASCAL |
|
||||
#define LONG long |
|
||||
#define HANDLE void * |
|
||||
#define LPARAM long |
|
||||
#define UINT unsigned int |
|
||||
#endif |
|
||||
|
|
||||
struct RARHeaderData |
|
||||
{ |
|
||||
char ArcName[260]; |
|
||||
char FileName[260]; |
|
||||
unsigned int Flags; |
|
||||
unsigned int PackSize; |
|
||||
unsigned int UnpSize; |
|
||||
unsigned int HostOS; |
|
||||
unsigned int FileCRC; |
|
||||
unsigned int FileTime; |
|
||||
unsigned int UnpVer; |
|
||||
unsigned int Method; |
|
||||
unsigned int FileAttr; |
|
||||
char *CmtBuf; |
|
||||
unsigned int CmtBufSize; |
|
||||
unsigned int CmtSize; |
|
||||
unsigned int CmtState; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
struct RARHeaderDataEx |
|
||||
{ |
|
||||
char ArcName[1024]; |
|
||||
wchar_t ArcNameW[1024]; |
|
||||
char FileName[1024]; |
|
||||
wchar_t FileNameW[1024]; |
|
||||
unsigned int Flags; |
|
||||
unsigned int PackSize; |
|
||||
unsigned int PackSizeHigh; |
|
||||
unsigned int UnpSize; |
|
||||
unsigned int UnpSizeHigh; |
|
||||
unsigned int HostOS; |
|
||||
unsigned int FileCRC; |
|
||||
unsigned int FileTime; |
|
||||
unsigned int UnpVer; |
|
||||
unsigned int Method; |
|
||||
unsigned int FileAttr; |
|
||||
char *CmtBuf; |
|
||||
unsigned int CmtBufSize; |
|
||||
unsigned int CmtSize; |
|
||||
unsigned int CmtState; |
|
||||
unsigned int Reserved[1024]; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
struct RAROpenArchiveData |
|
||||
{ |
|
||||
char *ArcName; |
|
||||
unsigned int OpenMode; |
|
||||
unsigned int OpenResult; |
|
||||
char *CmtBuf; |
|
||||
unsigned int CmtBufSize; |
|
||||
unsigned int CmtSize; |
|
||||
unsigned int CmtState; |
|
||||
}; |
|
||||
|
|
||||
struct RAROpenArchiveDataEx |
|
||||
{ |
|
||||
char *ArcName; |
|
||||
wchar_t *ArcNameW; |
|
||||
unsigned int OpenMode; |
|
||||
unsigned int OpenResult; |
|
||||
char *CmtBuf; |
|
||||
unsigned int CmtBufSize; |
|
||||
unsigned int CmtSize; |
|
||||
unsigned int CmtState; |
|
||||
unsigned int Flags; |
|
||||
unsigned int Reserved[32]; |
|
||||
}; |
|
||||
|
|
||||
enum UNRARCALLBACK_MESSAGES { |
|
||||
UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD |
|
||||
}; |
|
||||
|
|
||||
typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2); |
|
||||
|
|
||||
typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode); |
|
||||
typedef int (PASCAL *PROCESSDATAPROC)(unsigned char *Addr,int Size); |
|
||||
|
|
||||
#ifdef __cplusplus |
|
||||
extern "C" { |
|
||||
#endif |
|
||||
|
|
||||
HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *ArchiveData); |
|
||||
HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *ArchiveData); |
|
||||
int PASCAL RARCloseArchive(HANDLE hArcData); |
|
||||
int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *HeaderData); |
|
||||
int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *HeaderData); |
|
||||
int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName); |
|
||||
int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar_t *DestPath,wchar_t *DestName); |
|
||||
void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData); |
|
||||
void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc); |
|
||||
void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc); |
|
||||
void PASCAL RARSetPassword(HANDLE hArcData,char *Password); |
|
||||
int PASCAL RARGetDllVersion(); |
|
||||
|
|
||||
#ifdef __cplusplus |
|
||||
} |
|
||||
#endif |
|
||||
|
|
||||
#endif |
|
Binary file not shown.
@ -1,606 +0,0 @@ |
|||||
|
|
||||
UnRAR.dll Manual |
|
||||
~~~~~~~~~~~~~~~~ |
|
||||
|
|
||||
UnRAR.dll is a 32-bit Windows dynamic-link library which provides |
|
||||
file extraction from RAR archives. |
|
||||
|
|
||||
|
|
||||
Exported functions |
|
||||
|
|
||||
==================================================================== |
|
||||
HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *ArchiveData) |
|
||||
==================================================================== |
|
||||
|
|
||||
Description |
|
||||
~~~~~~~~~~~ |
|
||||
Open RAR archive and allocate memory structures |
|
||||
|
|
||||
Parameters |
|
||||
~~~~~~~~~~ |
|
||||
ArchiveData Points to RAROpenArchiveData structure |
|
||||
|
|
||||
struct RAROpenArchiveData |
|
||||
{ |
|
||||
char *ArcName; |
|
||||
UINT OpenMode; |
|
||||
UINT OpenResult; |
|
||||
char *CmtBuf; |
|
||||
UINT CmtBufSize; |
|
||||
UINT CmtSize; |
|
||||
UINT CmtState; |
|
||||
}; |
|
||||
|
|
||||
Structure fields: |
|
||||
|
|
||||
ArcName |
|
||||
Input parameter which should point to zero terminated string |
|
||||
containing the archive name. |
|
||||
|
|
||||
OpenMode |
|
||||
Input parameter. |
|
||||
|
|
||||
Possible values |
|
||||
|
|
||||
RAR_OM_LIST |
|
||||
Open archive for reading file headers only. |
|
||||
|
|
||||
RAR_OM_EXTRACT |
|
||||
Open archive for testing and extracting files. |
|
||||
|
|
||||
RAR_OM_LIST_INCSPLIT |
|
||||
Open archive for reading file headers only. If you open an archive |
|
||||
in such mode, RARReadHeader[Ex] will return all file headers, |
|
||||
including those with "file continued from previous volume" flag. |
|
||||
In case of RAR_OM_LIST such headers are automatically skipped. |
|
||||
So if you process RAR volumes in RAR_OM_LIST_INCSPLIT mode, you will |
|
||||
get several file header records for same file if file is split between |
|
||||
volumes. For such files only the last file header record will contain |
|
||||
the correct file CRC and if you wish to get the correct packed size, |
|
||||
you need to sum up packed sizes of all parts. |
|
||||
|
|
||||
OpenResult |
|
||||
Output parameter. |
|
||||
|
|
||||
Possible values |
|
||||
|
|
||||
0 Success |
|
||||
ERAR_NO_MEMORY Not enough memory to initialize data structures |
|
||||
ERAR_BAD_DATA Archive header broken |
|
||||
ERAR_BAD_ARCHIVE File is not valid RAR archive |
|
||||
ERAR_UNKNOWN_FORMAT Unknown encryption used for archive headers |
|
||||
ERAR_EOPEN File open error |
|
||||
|
|
||||
CmtBuf |
|
||||
Input parameter which should point to the buffer for archive |
|
||||
comments. Maximum comment size is limited to 64Kb. Comment text is |
|
||||
zero terminated. If the comment text is larger than the buffer |
|
||||
size, the comment text will be truncated. If CmtBuf is set to |
|
||||
NULL, comments will not be read. |
|
||||
|
|
||||
CmtBufSize |
|
||||
Input parameter which should contain size of buffer for archive |
|
||||
comments. |
|
||||
|
|
||||
CmtSize |
|
||||
Output parameter containing size of comments actually read into the |
|
||||
buffer, cannot exceed CmtBufSize. |
|
||||
|
|
||||
CmtState |
|
||||
Output parameter. |
|
||||
|
|
||||
Possible values |
|
||||
|
|
||||
0 comments not present |
|
||||
1 Comments read completely |
|
||||
ERAR_NO_MEMORY Not enough memory to extract comments |
|
||||
ERAR_BAD_DATA Broken comment |
|
||||
ERAR_UNKNOWN_FORMAT Unknown comment format |
|
||||
ERAR_SMALL_BUF Buffer too small, comments not completely read |
|
||||
|
|
||||
Return values |
|
||||
~~~~~~~~~~~~~ |
|
||||
Archive handle or NULL in case of error |
|
||||
|
|
||||
|
|
||||
======================================================================== |
|
||||
HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *ArchiveData) |
|
||||
======================================================================== |
|
||||
|
|
||||
Description |
|
||||
~~~~~~~~~~~ |
|
||||
Similar to RAROpenArchive, but uses RAROpenArchiveDataEx structure |
|
||||
allowing to specify Unicode archive name and returning information |
|
||||
about archive flags. |
|
||||
|
|
||||
Parameters |
|
||||
~~~~~~~~~~ |
|
||||
ArchiveData Points to RAROpenArchiveDataEx structure |
|
||||
|
|
||||
struct RAROpenArchiveDataEx |
|
||||
{ |
|
||||
char *ArcName; |
|
||||
wchar_t *ArcNameW; |
|
||||
unsigned int OpenMode; |
|
||||
unsigned int OpenResult; |
|
||||
char *CmtBuf; |
|
||||
unsigned int CmtBufSize; |
|
||||
unsigned int CmtSize; |
|
||||
unsigned int CmtState; |
|
||||
unsigned int Flags; |
|
||||
unsigned int Reserved[32]; |
|
||||
}; |
|
||||
|
|
||||
Structure fields: |
|
||||
|
|
||||
ArcNameW |
|
||||
Input parameter which should point to zero terminated Unicode string |
|
||||
containing the archive name or NULL if Unicode name is not specified. |
|
||||
|
|
||||
Flags |
|
||||
Output parameter. Combination of bit flags. |
|
||||
|
|
||||
Possible values |
|
||||
|
|
||||
0x0001 - Volume attribute (archive volume) |
|
||||
0x0002 - Archive comment present |
|
||||
0x0004 - Archive lock attribute |
|
||||
0x0008 - Solid attribute (solid archive) |
|
||||
0x0010 - New volume naming scheme ('volname.partN.rar') |
|
||||
0x0020 - Authenticity information present |
|
||||
0x0040 - Recovery record present |
|
||||
0x0080 - Block headers are encrypted |
|
||||
0x0100 - First volume (set only by RAR 3.0 and later) |
|
||||
|
|
||||
Reserved[32] |
|
||||
Reserved for future use. Must be zero. |
|
||||
|
|
||||
Information on other structure fields and function return values |
|
||||
is available above, in RAROpenArchive function description. |
|
||||
|
|
||||
|
|
||||
==================================================================== |
|
||||
int PASCAL RARCloseArchive(HANDLE hArcData) |
|
||||
==================================================================== |
|
||||
|
|
||||
Description |
|
||||
~~~~~~~~~~~ |
|
||||
Close RAR archive and release allocated memory. It must be called when |
|
||||
archive processing is finished, even if the archive processing was stopped |
|
||||
due to an error. |
|
||||
|
|
||||
Parameters |
|
||||
~~~~~~~~~~ |
|
||||
hArcData |
|
||||
This parameter should contain the archive handle obtained from the |
|
||||
RAROpenArchive function call. |
|
||||
|
|
||||
Return values |
|
||||
~~~~~~~~~~~~~ |
|
||||
0 Success |
|
||||
ERAR_ECLOSE Archive close error |
|
||||
|
|
||||
|
|
||||
==================================================================== |
|
||||
int PASCAL RARReadHeader(HANDLE hArcData, |
|
||||
struct RARHeaderData *HeaderData) |
|
||||
==================================================================== |
|
||||
|
|
||||
Description |
|
||||
~~~~~~~~~~~ |
|
||||
Read header of file in archive. |
|
||||
|
|
||||
Parameters |
|
||||
~~~~~~~~~~ |
|
||||
hArcData |
|
||||
This parameter should contain the archive handle obtained from the |
|
||||
RAROpenArchive function call. |
|
||||
|
|
||||
HeaderData |
|
||||
It should point to RARHeaderData structure: |
|
||||
|
|
||||
struct RARHeaderData |
|
||||
{ |
|
||||
char ArcName[260]; |
|
||||
char FileName[260]; |
|
||||
UINT Flags; |
|
||||
UINT PackSize; |
|
||||
UINT UnpSize; |
|
||||
UINT HostOS; |
|
||||
UINT FileCRC; |
|
||||
UINT FileTime; |
|
||||
UINT UnpVer; |
|
||||
UINT Method; |
|
||||
UINT FileAttr; |
|
||||
char *CmtBuf; |
|
||||
UINT CmtBufSize; |
|
||||
UINT CmtSize; |
|
||||
UINT CmtState; |
|
||||
}; |
|
||||
|
|
||||
Structure fields: |
|
||||
|
|
||||
ArcName |
|
||||
Output parameter which contains a zero terminated string of the |
|
||||
current archive name. May be used to determine the current volume |
|
||||
name. |
|
||||
|
|
||||
FileName |
|
||||
Output parameter which contains a zero terminated string of the |
|
||||
file name in OEM (DOS) encoding. |
|
||||
|
|
||||
Flags |
|
||||
Output parameter which contains file flags: |
|
||||
|
|
||||
0x01 - file continued from previous volume |
|
||||
0x02 - file continued on next volume |
|
||||
0x04 - file encrypted with password |
|
||||
0x08 - file comment present |
|
||||
0x10 - compression of previous files is used (solid flag) |
|
||||
|
|
||||
bits 7 6 5 |
|
||||
|
|
||||
0 0 0 - dictionary size 64 Kb |
|
||||
0 0 1 - dictionary size 128 Kb |
|
||||
0 1 0 - dictionary size 256 Kb |
|
||||
0 1 1 - dictionary size 512 Kb |
|
||||
1 0 0 - dictionary size 1024 Kb |
|
||||
1 0 1 - dictionary size 2048 KB |
|
||||
1 1 0 - dictionary size 4096 KB |
|
||||
1 1 1 - file is directory |
|
||||
|
|
||||
Other bits are reserved. |
|
||||
|
|
||||
PackSize |
|
||||
Output parameter means packed file size or size of the |
|
||||
file part if file was split between volumes. |
|
||||
|
|
||||
UnpSize |
|
||||
Output parameter - unpacked file size. |
|
||||
|
|
||||
HostOS |
|
||||
Output parameter - operating system used for archiving: |
|
||||
|
|
||||
0 - MS DOS; |
|
||||
1 - OS/2. |
|
||||
2 - Win32 |
|
||||
3 - Unix |
|
||||
|
|
||||
FileCRC |
|
||||
Output parameter which contains unpacked file CRC. In case of file parts |
|
||||
split between volumes only the last part contains the correct CRC |
|
||||
and it is accessible only in RAR_OM_LIST_INCSPLIT listing mode. |
|
||||
|
|
||||
FileTime |
|
||||
Output parameter - contains date and time in standard MS DOS format. |
|
||||
|
|
||||
UnpVer |
|
||||
Output parameter - RAR version needed to extract file. |
|
||||
It is encoded as 10 * Major version + minor version. |
|
||||
|
|
||||
Method |
|
||||
Output parameter - packing method. |
|
||||
|
|
||||
FileAttr |
|
||||
Output parameter - file attributes. |
|
||||
|
|
||||
CmtBuf |
|
||||
File comments support is not implemented in the new DLL version yet. |
|
||||
Now CmtState is always 0. |
|
||||
|
|
||||
/* |
|
||||
* Input parameter which should point to the buffer for file |
|
||||
* comments. Maximum comment size is limited to 64Kb. Comment text is |
|
||||
* a zero terminated string in OEM encoding. If the comment text is |
|
||||
* larger than the buffer size, the comment text will be truncated. |
|
||||
* If CmtBuf is set to NULL, comments will not be read. |
|
||||
*/ |
|
||||
|
|
||||
CmtBufSize |
|
||||
Input parameter which should contain size of buffer for archive |
|
||||
comments. |
|
||||
|
|
||||
CmtSize |
|
||||
Output parameter containing size of comments actually read into the |
|
||||
buffer, should not exceed CmtBufSize. |
|
||||
|
|
||||
CmtState |
|
||||
Output parameter. |
|
||||
|
|
||||
Possible values |
|
||||
|
|
||||
0 Absent comments |
|
||||
1 Comments read completely |
|
||||
ERAR_NO_MEMORY Not enough memory to extract comments |
|
||||
ERAR_BAD_DATA Broken comment |
|
||||
ERAR_UNKNOWN_FORMAT Unknown comment format |
|
||||
ERAR_SMALL_BUF Buffer too small, comments not completely read |
|
||||
|
|
||||
Return values |
|
||||
~~~~~~~~~~~~~ |
|
||||
|
|
||||
0 Success |
|
||||
ERAR_END_ARCHIVE End of archive |
|
||||
ERAR_BAD_DATA File header broken |
|
||||
|
|
||||
|
|
||||
==================================================================== |
|
||||
int PASCAL RARReadHeaderEx(HANDLE hArcData, |
|
||||
struct RARHeaderDataEx *HeaderData) |
|
||||
==================================================================== |
|
||||
|
|
||||
Description |
|
||||
~~~~~~~~~~~ |
|
||||
Similar to RARReadHeader, but uses RARHeaderDataEx structure, |
|
||||
containing information about Unicode file names and 64 bit file sizes. |
|
||||
|
|
||||
struct RARHeaderDataEx |
|
||||
{ |
|
||||
char ArcName[1024]; |
|
||||
wchar_t ArcNameW[1024]; |
|
||||
char FileName[1024]; |
|
||||
wchar_t FileNameW[1024]; |
|
||||
unsigned int Flags; |
|
||||
unsigned int PackSize; |
|
||||
unsigned int PackSizeHigh; |
|
||||
unsigned int UnpSize; |
|
||||
unsigned int UnpSizeHigh; |
|
||||
unsigned int HostOS; |
|
||||
unsigned int FileCRC; |
|
||||
unsigned int FileTime; |
|
||||
unsigned int UnpVer; |
|
||||
unsigned int Method; |
|
||||
unsigned int FileAttr; |
|
||||
char *CmtBuf; |
|
||||
unsigned int CmtBufSize; |
|
||||
unsigned int CmtSize; |
|
||||
unsigned int CmtState; |
|
||||
unsigned int Reserved[1024]; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
==================================================================== |
|
||||
int PASCAL RARProcessFile(HANDLE hArcData, |
|
||||
int Operation, |
|
||||
char *DestPath, |
|
||||
char *DestName) |
|
||||
==================================================================== |
|
||||
|
|
||||
Description |
|
||||
~~~~~~~~~~~ |
|
||||
Performs action and moves the current position in the archive to |
|
||||
the next file. Extract or test the current file from the archive |
|
||||
opened in RAR_OM_EXTRACT mode. If the mode RAR_OM_LIST is set, |
|
||||
then a call to this function will simply skip the archive position |
|
||||
to the next file. |
|
||||
|
|
||||
Parameters |
|
||||
~~~~~~~~~~ |
|
||||
hArcData |
|
||||
This parameter should contain the archive handle obtained from the |
|
||||
RAROpenArchive function call. |
|
||||
|
|
||||
Operation |
|
||||
File operation. |
|
||||
|
|
||||
Possible values |
|
||||
|
|
||||
RAR_SKIP Move to the next file in the archive. If the |
|
||||
archive is solid and RAR_OM_EXTRACT mode was set |
|
||||
when the archive was opened, the current file will |
|
||||
be processed - the operation will be performed |
|
||||
slower than a simple seek. |
|
||||
|
|
||||
RAR_TEST Test the current file and move to the next file in |
|
||||
the archive. If the archive was opened with |
|
||||
RAR_OM_LIST mode, the operation is equal to |
|
||||
RAR_SKIP. |
|
||||
|
|
||||
RAR_EXTRACT Extract the current file and move to the next file. |
|
||||
If the archive was opened with RAR_OM_LIST mode, |
|
||||
the operation is equal to RAR_SKIP. |
|
||||
|
|
||||
|
|
||||
DestPath |
|
||||
This parameter should point to a zero terminated string containing the |
|
||||
destination directory to which to extract files to. If DestPath is equal |
|
||||
to NULL, it means extract to the current directory. This parameter has |
|
||||
meaning only if DestName is NULL. |
|
||||
|
|
||||
DestName |
|
||||
This parameter should point to a string containing the full path and name |
|
||||
to assign to extracted file or it can be NULL to use the default name. |
|
||||
If DestName is defined (not NULL), it overrides both the original file |
|
||||
name saved in the archive and path specigied in DestPath setting. |
|
||||
|
|
||||
Both DestPath and DestName must be in OEM encoding. If necessary, |
|
||||
use CharToOem to convert text to OEM before passing to this function. |
|
||||
|
|
||||
Return values |
|
||||
~~~~~~~~~~~~~ |
|
||||
0 Success |
|
||||
ERAR_BAD_DATA File CRC error |
|
||||
ERAR_BAD_ARCHIVE Volume is not valid RAR archive |
|
||||
ERAR_UNKNOWN_FORMAT Unknown archive format |
|
||||
ERAR_EOPEN Volume open error |
|
||||
ERAR_ECREATE File create error |
|
||||
ERAR_ECLOSE File close error |
|
||||
ERAR_EREAD Read error |
|
||||
ERAR_EWRITE Write error |
|
||||
|
|
||||
|
|
||||
Note: if you wish to cancel extraction, return -1 when processing |
|
||||
UCM_PROCESSDATA callback message. |
|
||||
|
|
||||
|
|
||||
==================================================================== |
|
||||
int PASCAL RARProcessFileW(HANDLE hArcData, |
|
||||
int Operation, |
|
||||
wchar_t *DestPath, |
|
||||
wchar_t *DestName) |
|
||||
==================================================================== |
|
||||
|
|
||||
Description |
|
||||
~~~~~~~~~~~ |
|
||||
Unicode version of RARProcessFile. It uses Unicode DestPath |
|
||||
and DestName parameters, other parameters and return values |
|
||||
are the same as in RARProcessFile. |
|
||||
|
|
||||
|
|
||||
==================================================================== |
|
||||
void PASCAL RARSetCallback(HANDLE hArcData, |
|
||||
int PASCAL (*CallbackProc)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2), |
|
||||
LPARAM UserData); |
|
||||
==================================================================== |
|
||||
|
|
||||
Description |
|
||||
~~~~~~~~~~~ |
|
||||
Set a user-defined callback function to process Unrar events. |
|
||||
|
|
||||
Parameters |
|
||||
~~~~~~~~~~ |
|
||||
hArcData |
|
||||
This parameter should contain the archive handle obtained from the |
|
||||
RAROpenArchive function call. |
|
||||
|
|
||||
CallbackProc |
|
||||
It should point to a user-defined callback function. |
|
||||
|
|
||||
The function will be passed four parameters: |
|
||||
|
|
||||
|
|
||||
msg Type of event. Described below. |
|
||||
|
|
||||
UserData User defined value passed to RARSetCallback. |
|
||||
|
|
||||
P1 and P2 Event dependent parameters. Described below. |
|
||||
|
|
||||
|
|
||||
Possible events |
|
||||
|
|
||||
UCM_CHANGEVOLUME Process volume change. |
|
||||
|
|
||||
P1 Points to the zero terminated name |
|
||||
of the next volume. |
|
||||
|
|
||||
P2 The function call mode: |
|
||||
|
|
||||
RAR_VOL_ASK Required volume is absent. The function should |
|
||||
prompt user and return a positive value |
|
||||
to retry or return -1 value to terminate |
|
||||
operation. The function may also specify a new |
|
||||
volume name, placing it to the address specified |
|
||||
by P1 parameter. |
|
||||
|
|
||||
RAR_VOL_NOTIFY Required volume is successfully opened. |
|
||||
This is a notification call and volume name |
|
||||
modification is not allowed. The function should |
|
||||
return a positive value to continue or -1 |
|
||||
to terminate operation. |
|
||||
|
|
||||
UCM_PROCESSDATA Process unpacked data. It may be used to read |
|
||||
a file while it is being extracted or tested |
|
||||
without actual extracting file to disk. |
|
||||
Return a positive value to continue process |
|
||||
or -1 to cancel the archive operation |
|
||||
|
|
||||
P1 Address pointing to the unpacked data. |
|
||||
Function may refer to the data but must not |
|
||||
change it. |
|
||||
|
|
||||
P2 Size of the unpacked data. It is guaranteed |
|
||||
only that the size will not exceed the maximum |
|
||||
dictionary size (4 Mb in RAR 3.0). |
|
||||
|
|
||||
UCM_NEEDPASSWORD DLL needs a password to process archive. |
|
||||
This message must be processed if you wish |
|
||||
to be able to handle archives with encrypted |
|
||||
file names. It can be also used as replacement |
|
||||
of RARSetPassword function even for usual |
|
||||
encrypted files with non-encrypted names. |
|
||||
|
|
||||
P1 Address pointing to the buffer for a password. |
|
||||
You need to copy a password here. |
|
||||
|
|
||||
P2 Size of the password buffer. |
|
||||
|
|
||||
|
|
||||
UserData |
|
||||
User data passed to callback function. |
|
||||
|
|
||||
Other functions of UnRAR.dll should not be called from the callback |
|
||||
function. |
|
||||
|
|
||||
Return values |
|
||||
~~~~~~~~~~~~~ |
|
||||
None |
|
||||
|
|
||||
|
|
||||
|
|
||||
==================================================================== |
|
||||
void PASCAL RARSetChangeVolProc(HANDLE hArcData, |
|
||||
int PASCAL (*ChangeVolProc)(char *ArcName,int Mode)); |
|
||||
==================================================================== |
|
||||
|
|
||||
Obsoleted, use RARSetCallback instead. |
|
||||
|
|
||||
|
|
||||
|
|
||||
==================================================================== |
|
||||
void PASCAL RARSetProcessDataProc(HANDLE hArcData, |
|
||||
int PASCAL (*ProcessDataProc)(unsigned char *Addr,int Size)) |
|
||||
==================================================================== |
|
||||
|
|
||||
Obsoleted, use RARSetCallback instead. |
|
||||
|
|
||||
|
|
||||
==================================================================== |
|
||||
void PASCAL RARSetPassword(HANDLE hArcData, |
|
||||
char *Password); |
|
||||
==================================================================== |
|
||||
|
|
||||
Description |
|
||||
~~~~~~~~~~~ |
|
||||
Set a password to decrypt files. |
|
||||
|
|
||||
Parameters |
|
||||
~~~~~~~~~~ |
|
||||
hArcData |
|
||||
This parameter should contain the archive handle obtained from the |
|
||||
RAROpenArchive function call. |
|
||||
|
|
||||
Password |
|
||||
It should point to a string containing a zero terminated password. |
|
||||
|
|
||||
Return values |
|
||||
~~~~~~~~~~~~~ |
|
||||
None |
|
||||
|
|
||||
|
|
||||
==================================================================== |
|
||||
void PASCAL RARGetDllVersion(); |
|
||||
==================================================================== |
|
||||
|
|
||||
Description |
|
||||
~~~~~~~~~~~ |
|
||||
Returns API version. |
|
||||
|
|
||||
Parameters |
|
||||
~~~~~~~~~~ |
|
||||
None. |
|
||||
|
|
||||
Return values |
|
||||
~~~~~~~~~~~~~ |
|
||||
Returns an integer value denoting UnRAR.dll API version, which is also |
|
||||
defined in unrar.h as RAR_DLL_VERSION. API version number is incremented |
|
||||
only in case of noticeable changes in UnRAR.dll API. Do not confuse it |
|
||||
with version of UnRAR.dll stored in DLL resources, which is incremented |
|
||||
with every DLL rebuild. |
|
||||
|
|
||||
If RARGetDllVersion() returns a value lower than UnRAR.dll which your |
|
||||
application was designed for, it may indicate that DLL version is too old |
|
||||
and it will fail to provide all necessary functions to your application. |
|
||||
|
|
||||
This function is absent in old versions of UnRAR.dll, so it is safer |
|
||||
to use LoadLibrary and GetProcAddress to access this function. |
|
||||
|
|
@ -1,80 +0,0 @@ |
|||||
List of unrar.dll API changes. We do not include performance and reliability |
|
||||
improvements into this list, but this library and RAR/UnRAR tools share |
|
||||
the same source code. So the latest version of unrar.dll usually contains |
|
||||
same decompression algorithm changes as the latest UnRAR version. |
|
||||
============================================================================ |
|
||||
|
|
||||
-- 18 January 2008 |
|
||||
|
|
||||
all LONG parameters of CallbackProc function were changed |
|
||||
to LPARAM type for 64 bit mode compatibility. |
|
||||
|
|
||||
|
|
||||
-- 12 December 2007 |
|
||||
|
|
||||
Added new RAR_OM_LIST_INCSPLIT open mode for function RAROpenArchive. |
|
||||
|
|
||||
|
|
||||
-- 14 August 2007 |
|
||||
|
|
||||
Added NoCrypt\unrar_nocrypt.dll without decryption code for those |
|
||||
applications where presence of encryption or decryption code is not |
|
||||
allowed because of legal restrictions. |
|
||||
|
|
||||
|
|
||||
-- 14 December 2006 |
|
||||
|
|
||||
Added ERAR_MISSING_PASSWORD error type. This error is returned |
|
||||
if empty password is specified for encrypted file. |
|
||||
|
|
||||
|
|
||||
-- 12 June 2003 |
|
||||
|
|
||||
Added RARProcessFileW function, Unicode version of RARProcessFile |
|
||||
|
|
||||
|
|
||||
-- 9 August 2002 |
|
||||
|
|
||||
Added RAROpenArchiveEx function allowing to specify Unicode archive |
|
||||
name and get archive flags. |
|
||||
|
|
||||
|
|
||||
-- 24 January 2002 |
|
||||
|
|
||||
Added RARReadHeaderEx function allowing to read Unicode file names |
|
||||
and 64 bit file sizes. |
|
||||
|
|
||||
|
|
||||
-- 23 January 2002 |
|
||||
|
|
||||
Added ERAR_UNKNOWN error type (it is used for all errors which |
|
||||
do not have special ERAR code yet) and UCM_NEEDPASSWORD callback |
|
||||
message. |
|
||||
|
|
||||
Unrar.dll automatically opens all next volumes not only when extracting, |
|
||||
but also in RAR_OM_LIST mode. |
|
||||
|
|
||||
|
|
||||
-- 27 November 2001 |
|
||||
|
|
||||
RARSetChangeVolProc and RARSetProcessDataProc are replaced by |
|
||||
the single callback function installed with RARSetCallback. |
|
||||
Unlike old style callbacks, the new function accepts the user defined |
|
||||
parameter. Unrar.dll still supports RARSetChangeVolProc and |
|
||||
RARSetProcessDataProc for compatibility purposes, but if you write |
|
||||
a new application, better use RARSetCallback. |
|
||||
|
|
||||
File comments support is not implemented in the new DLL version yet. |
|
||||
Now CmtState is always 0. |
|
||||
|
|
||||
|
|
||||
-- 13 August 2001 |
|
||||
|
|
||||
Added RARGetDllVersion function, so you may distinguish old unrar.dll, |
|
||||
which used C style callback functions and the new one with PASCAL callbacks. |
|
||||
|
|
||||
|
|
||||
-- 10 May 2001 |
|
||||
|
|
||||
Callback functions in RARSetChangeVolProc and RARSetProcessDataProc |
|
||||
use PASCAL style call convention now. |
|
@ -1 +0,0 @@ |
|||||
This is x64 version of unrar.dll. |
|
Binary file not shown.
@ -1,21 +0,0 @@ |
|||||
Copyright (c) 2003-2005 Jimmy Retzlaff, 2008 Konstantin Yegupov |
|
||||
|
|
||||
Permission is hereby granted, free of charge, to any person obtaining |
|
||||
a copy of this software and associated documentation files (the |
|
||||
"Software"), to deal in the Software without restriction, including |
|
||||
without limitation the rights to use, copy, modify, merge, publish, |
|
||||
distribute, sublicense, and/or sell copies of the Software, and to |
|
||||
permit persons to whom the Software is furnished to do so, subject to |
|
||||
the following conditions: |
|
||||
|
|
||||
The above copyright notice and this permission notice shall be |
|
||||
included in all copies or substantial portions of the Software. |
|
||||
|
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
||||
SOFTWARE. |
|
Binary file not shown.
Loading…
Reference in new issue