[UI] Refactor TorrentInfo and add functionality
Make a clearer distinction about torrent metainfo and metadata and allow passing these to TorrentInfo.
This commit is contained in:
parent
d45dbfe064
commit
63b25311f5
|
@ -183,60 +183,67 @@ DISK_CACHE_KEYS = [
|
||||||
|
|
||||||
|
|
||||||
class TorrentInfo(object):
|
class TorrentInfo(object):
|
||||||
"""
|
"""Collects information about a torrent file.
|
||||||
Collects information about a torrent file.
|
|
||||||
|
|
||||||
:param filename: The path to the torrent
|
Args:
|
||||||
:type filename: string
|
filename (str): The path to the .torrent file.
|
||||||
|
filetree (int, optional): The version of filetree to create (defaults to 1).
|
||||||
|
metainfo (bytes, optional): A bencoded filedump from a .torrent file.
|
||||||
|
metadata (bytes, optional): A bencoded metadata info_dict.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, filename, filetree=1):
|
def __init__(self, filename='', filetree=1, metainfo=None, metadata=None):
|
||||||
# Get the torrent data from the torrent file
|
# Get the torrent metainfo from the torrent file
|
||||||
try:
|
if metadata:
|
||||||
|
self._metainfo_dict = {'info': bencode.bdecode(metadata)}
|
||||||
|
else:
|
||||||
|
self._metainfo = metainfo
|
||||||
|
if filename and not self._metainfo:
|
||||||
log.debug('Attempting to open %s.', filename)
|
log.debug('Attempting to open %s.', filename)
|
||||||
|
try:
|
||||||
with open(filename, 'rb') as _file:
|
with open(filename, 'rb') as _file:
|
||||||
self.__m_filedata = _file.read()
|
self._metainfo = _file.read()
|
||||||
except IOError as ex:
|
except IOError as ex:
|
||||||
log.warning('Unable to open %s: %s', filename, ex)
|
log.warning('Unable to open %s: %s', filename, ex)
|
||||||
raise ex
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.__m_metadata = bencode.bdecode(self.__m_filedata)
|
self._metainfo_dict = bencode.bdecode(self._metainfo)
|
||||||
except bencode.BTFailure as ex:
|
except bencode.BTFailure as ex:
|
||||||
log.warning('Failed to decode %s: %s', filename, ex)
|
log.warning('Failed to decode %s: %s', filename, ex)
|
||||||
raise ex
|
return
|
||||||
|
|
||||||
self.__m_info_hash = sha(bencode.bencode(self.__m_metadata['info'])).hexdigest()
|
info_dict = self._metainfo_dict['info']
|
||||||
|
self._info_hash = sha(bencode.bencode(info_dict)).hexdigest()
|
||||||
|
|
||||||
# Get encoding from torrent file if available
|
# Get encoding from torrent file if available
|
||||||
self.encoding = None
|
encoding = self._metainfo_dict.get('encoding', None)
|
||||||
if 'encoding' in self.__m_metadata:
|
codepage = self._metainfo_dict.get('codepage', None)
|
||||||
self.encoding = self.__m_metadata['encoding']
|
if not encoding:
|
||||||
elif 'codepage' in self.__m_metadata:
|
encoding = codepage if codepage else 'UTF-8'
|
||||||
self.encoding = str(self.__m_metadata['codepage'])
|
|
||||||
if not self.encoding:
|
|
||||||
self.encoding = 'UTF-8'
|
|
||||||
|
|
||||||
# Check if 'name.utf-8' is in the torrent and if not try to decode the string
|
# Decode 'name' with encoding unless 'name.utf-8' found.
|
||||||
# using the encoding found.
|
if 'name.utf-8' in info_dict:
|
||||||
if 'name.utf-8' in self.__m_metadata['info']:
|
self._name = decode_bytes(info_dict['name.utf-8'])
|
||||||
self.__m_name = decode_bytes(self.__m_metadata['info']['name.utf-8'])
|
|
||||||
else:
|
else:
|
||||||
self.__m_name = decode_bytes(self.__m_metadata['info']['name'], self.encoding)
|
self._name = decode_bytes(info_dict['name'], encoding)
|
||||||
|
|
||||||
# Get list of files from torrent info
|
# Get list of files from torrent info
|
||||||
|
if 'files' in info_dict:
|
||||||
paths = {}
|
paths = {}
|
||||||
dirs = {}
|
dirs = {}
|
||||||
if 'files' in self.__m_metadata['info']:
|
prefix = self._name if len(info_dict['files']) > 1 else ''
|
||||||
prefix = ''
|
|
||||||
if len(self.__m_metadata['info']['files']) > 1:
|
|
||||||
prefix = self.__m_name
|
|
||||||
|
|
||||||
for index, f in enumerate(self.__m_metadata['info']['files']):
|
for index, f in enumerate(info_dict['files']):
|
||||||
if 'path.utf-8' in f:
|
if 'path.utf-8' in f:
|
||||||
path = decode_bytes(os.path.join(prefix, *f['path.utf-8']))
|
path = decode_bytes(os.path.join(*f['path.utf-8']))
|
||||||
del f['path.utf-8']
|
del f['path.utf-8']
|
||||||
else:
|
else:
|
||||||
path = os.path.join(prefix, decode_bytes(os.path.join(*f['path']), self.encoding))
|
path = decode_bytes(os.path.join(*f['path']), encoding)
|
||||||
|
|
||||||
|
if prefix:
|
||||||
|
path = os.path.join(prefix, path)
|
||||||
|
|
||||||
f['path'] = path
|
f['path'] = path
|
||||||
f['index'] = index
|
f['index'] = index
|
||||||
if 'sha1' in f and len(f['sha1']) == 20:
|
if 'sha1' in f and len(f['sha1']) == 20:
|
||||||
|
@ -270,84 +277,86 @@ class TorrentInfo(object):
|
||||||
|
|
||||||
file_tree = FileTree(paths)
|
file_tree = FileTree(paths)
|
||||||
file_tree.walk(walk)
|
file_tree.walk(walk)
|
||||||
self.__m_files_tree = file_tree.get_tree()
|
self._files_tree = file_tree.get_tree()
|
||||||
else:
|
else:
|
||||||
if filetree == 2:
|
if filetree == 2:
|
||||||
self.__m_files_tree = {
|
self._files_tree = {
|
||||||
'contents': {
|
'contents': {
|
||||||
self.__m_name: {
|
self._name: {
|
||||||
'type': 'file',
|
'type': 'file',
|
||||||
'index': 0,
|
'index': 0,
|
||||||
'length': self.__m_metadata['info']['length'],
|
'length': info_dict['length'],
|
||||||
'download': True,
|
'download': True,
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
self.__m_files_tree = {
|
self._files_tree = {
|
||||||
self.__m_name: (0, self.__m_metadata['info']['length'], True),
|
self._name: (0, info_dict['length'], True),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.__m_files = []
|
self._files = []
|
||||||
if 'files' in self.__m_metadata['info']:
|
if 'files' in info_dict:
|
||||||
prefix = ''
|
prefix = ''
|
||||||
if len(self.__m_metadata['info']['files']) > 1:
|
if len(info_dict['files']) > 1:
|
||||||
prefix = self.__m_name
|
prefix = self._name
|
||||||
|
|
||||||
for f in self.__m_metadata['info']['files']:
|
for f in info_dict['files']:
|
||||||
self.__m_files.append({
|
self._files.append({
|
||||||
'path': f['path'],
|
'path': f['path'],
|
||||||
'size': f['length'],
|
'size': f['length'],
|
||||||
'download': True,
|
'download': True,
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
self.__m_files.append({
|
self._files.append({
|
||||||
'path': self.__m_name,
|
'path': self._name,
|
||||||
'size': self.__m_metadata['info']['length'],
|
'size': info_dict['length'],
|
||||||
'download': True,
|
'download': True,
|
||||||
})
|
})
|
||||||
|
|
||||||
def as_dict(self, *keys):
|
def as_dict(self, *keys):
|
||||||
"""
|
"""The torrent info as a dictionary, filtered by keys.
|
||||||
Return the torrent info as a dictionary, only including the passed in
|
|
||||||
keys.
|
|
||||||
|
|
||||||
:param keys: a number of key strings
|
Args:
|
||||||
:type keys: string
|
keys (str): A space-separated string of keys.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: The torrent info dict with specified keys.
|
||||||
"""
|
"""
|
||||||
return {key: getattr(self, key) for key in keys}
|
return {key: getattr(self, key) for key in keys}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""
|
"""The name of the torrent.
|
||||||
The name of the torrent.
|
|
||||||
|
Returns:
|
||||||
|
str: The torrent name.
|
||||||
|
|
||||||
:rtype: string
|
|
||||||
"""
|
"""
|
||||||
return self.__m_name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def info_hash(self):
|
def info_hash(self):
|
||||||
"""
|
"""The calculated torrent info_hash.
|
||||||
The torrents info_hash
|
|
||||||
|
|
||||||
:rtype: string
|
Returns:
|
||||||
|
str: The torrent info_hash.
|
||||||
"""
|
"""
|
||||||
return self.__m_info_hash
|
return self._info_hash
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def files(self):
|
def files(self):
|
||||||
"""
|
"""The files that the torrent contains.
|
||||||
A list of the files that the torrent contains.
|
|
||||||
|
Returns:
|
||||||
|
list: The list of torrent files.
|
||||||
|
|
||||||
:rtype: list
|
|
||||||
"""
|
"""
|
||||||
return self.__m_files
|
return self._files
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def files_tree(self):
|
def files_tree(self):
|
||||||
"""
|
"""A tree of the files the torrent contains.
|
||||||
A dictionary based tree of the files.
|
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
@ -357,28 +366,31 @@ class TorrentInfo(object):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:rtype: dictionary
|
Returns:
|
||||||
|
dict: The tree of files.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self.__m_files_tree
|
return self._files_tree
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def metadata(self):
|
def metadata(self):
|
||||||
"""
|
"""The torrents metainfo dictionary.
|
||||||
The torrents metadata.
|
|
||||||
|
Returns:
|
||||||
|
dict: The bdecoded metainfo dictionary.
|
||||||
|
|
||||||
:rtype: dictionary
|
|
||||||
"""
|
"""
|
||||||
return self.__m_metadata
|
return self._metainfo_dict
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def filedata(self):
|
def filedata(self):
|
||||||
"""
|
"""The contents of the .torrent file.
|
||||||
The torrents file data. This will be the bencoded dictionary read
|
|
||||||
from the torrent file.
|
Returns:
|
||||||
|
str: The metainfo bencoded dictionary from a torrent file.
|
||||||
|
|
||||||
:rtype: string
|
|
||||||
"""
|
"""
|
||||||
return self.__m_filedata
|
return self._metainfo
|
||||||
|
|
||||||
|
|
||||||
class FileTree2(object):
|
class FileTree2(object):
|
||||||
|
|
Loading…
Reference in New Issue