SickGear/lib/rtorrent/lib/torrentparser.py
2023-02-09 13:41:15 +00:00

119 lines
4.4 KiB
Python

# Copyright (c) 2013 Chris Lucas, <chris@chrisjlucas.com>
# 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.
from ..compat import parse_qs, urlopen, urlparse
import bencode
import hashlib
import os.path
import re
class TorrentParser(object):
def __init__(self, torrent):
"""Decode and parse given torrent
@param torrent: handles: urls, file paths, string of torrent data
@type torrent: str
@raise AssertionError: Can be raised for a couple reasons:
- If _get_raw_torrent() couldn't figure out
what X{torrent} is
- if X{torrent} isn't a valid bencoded torrent file
"""
self.torrent = torrent
self._raw_torrent = None # : testing yo
self._torrent_decoded = None # : what up
self.file_type = None
self._get_raw_torrent()
assert self._raw_torrent is not None, 'Couldn\'t get raw_torrent.'
if self._torrent_decoded is None:
self._decode_torrent()
assert isinstance(self._torrent_decoded, dict), 'Invalid torrent file.'
self._parse_torrent()
@property
def raw_torrent(self):
return self._raw_torrent
def _is_raw(self):
raw = False
if isinstance(self.torrent, (str, bytes)):
if isinstance(self._decode_torrent(self.torrent), dict):
raw = True
else:
# reset self._torrent_decoded (currently equals False)
self._torrent_decoded = None
return raw
def _get_raw_torrent(self):
"""Get raw torrent data by determining what self.torrent is"""
# already raw?
if self._is_raw():
self.file_type = 'raw'
self._raw_torrent = self.torrent
return
# local file?
if os.path.isfile(self.torrent):
self.file_type = 'file'
self._raw_torrent = open(self.torrent, 'rb').read()
# url?
elif re.search(r'^(http|ftp)s?://', self.torrent, re.I):
self.file_type = 'url'
self._raw_torrent = urlopen(self.torrent).read()
# magnet link?
elif re.match(r'magnet:', self.torrent, re.I):
self.file_type = 'url'
query = {}
try:
parsed = urlparse(self.torrent)
query = parse_qs(parsed.query)
except (BaseException, Exception):
pass
self.title = query.get('dn', [''])[0]
self.info_hash = query.get('xt', [''])[0].split(':')[-1]
self.trackers = query.get('tr', [])
self._torrent_decoded = query
self._raw_torrent = self.torrent
def _decode_torrent(self, raw_torrent=None):
if raw_torrent is None:
raw_torrent = self._raw_torrent
self._torrent_decoded = bencode.decode(raw_torrent)
return self._torrent_decoded
def _calc_info_hash(self):
self.info_hash = None
if 'info' in self._torrent_decoded:
info_encoded = bencode.encode(self._torrent_decoded['info'])
if info_encoded:
self.info_hash = hashlib.sha1(info_encoded).hexdigest().upper()
return self.info_hash
def _parse_torrent(self):
for k in self._torrent_decoded:
key = k.replace(' ', '_').lower()
setattr(self, key, self._torrent_decoded[k])
self._calc_info_hash()