The move to using auto-formatter makes it easier to read, submit and speeds up development time. https://github.com/ambv/black/ Although I would prefer 79 chars, the default line length of 88 chars used by black suffices. The flake8 line length remains at 120 chars since black does not touch comments or docstrings and this will require another round of fixes. The only black setting that is not standard is the use of double-quotes for strings so disabled any formatting of these. Note however that flake8 will still flag usage of double-quotes. I may change my mind on double vs single quotes but for now leave them. A new pyproject.toml file has been created for black configuration.
247 lines
6.6 KiB
Python
247 lines
6.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# Original file from BitTorrent-5.3-GPL.tar.gz
|
|
# Copyright (C) Bram Cohen
|
|
#
|
|
# Modifications for use in Deluge:
|
|
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
|
|
#
|
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
|
# See LICENSE for more details.
|
|
#
|
|
|
|
from __future__ import division, unicode_literals
|
|
|
|
import logging
|
|
import os.path
|
|
import time
|
|
from hashlib import sha1 as sha
|
|
|
|
import deluge.component as component
|
|
from deluge.bencode import bencode
|
|
from deluge.common import utf8_encode_structure
|
|
from deluge.event import CreateTorrentProgressEvent
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
ignore = ['core', 'CVS', 'Thumbs.db', 'desktop.ini']
|
|
|
|
noncharacter_translate = {}
|
|
for i in range(0xD800, 0xE000):
|
|
noncharacter_translate[i] = ord('-')
|
|
for i in range(0xFDD0, 0xFDF0):
|
|
noncharacter_translate[i] = ord('-')
|
|
for i in (0xFFFE, 0xFFFF):
|
|
noncharacter_translate[i] = ord('-')
|
|
|
|
|
|
def gmtime():
|
|
return time.mktime(time.gmtime())
|
|
|
|
|
|
def dummy(*v):
|
|
pass
|
|
|
|
|
|
class RemoteFileProgress(object):
|
|
def __init__(self, session_id):
|
|
self.session_id = session_id
|
|
|
|
def __call__(self, piece_count, num_pieces):
|
|
component.get('RPCServer').emit_event_for_session_id(
|
|
self.session_id, CreateTorrentProgressEvent(piece_count, num_pieces)
|
|
)
|
|
|
|
|
|
def make_meta_file(
|
|
path,
|
|
url,
|
|
piece_length,
|
|
progress=None,
|
|
title=None,
|
|
comment=None,
|
|
safe=None,
|
|
content_type=None,
|
|
target=None,
|
|
webseeds=None,
|
|
name=None,
|
|
private=False,
|
|
created_by=None,
|
|
trackers=None,
|
|
):
|
|
data = {'creation date': int(gmtime())}
|
|
if url:
|
|
data['announce'] = url.strip()
|
|
a, b = os.path.split(path)
|
|
if not target:
|
|
if b == '':
|
|
f = a + '.torrent'
|
|
else:
|
|
f = os.path.join(a, b + '.torrent')
|
|
else:
|
|
f = target
|
|
|
|
if progress is None:
|
|
progress = dummy
|
|
try:
|
|
session_id = component.get('RPCServer').get_session_id()
|
|
except KeyError:
|
|
pass
|
|
else:
|
|
if session_id:
|
|
progress = RemoteFileProgress(session_id)
|
|
|
|
info = makeinfo(path, piece_length, progress, name, content_type, private)
|
|
|
|
# check_info(info)
|
|
data['info'] = info
|
|
if title:
|
|
data['title'] = title.encode('utf8')
|
|
if comment:
|
|
data['comment'] = comment.encode('utf8')
|
|
if safe:
|
|
data['safe'] = safe.encode('utf8')
|
|
|
|
httpseeds = []
|
|
url_list = []
|
|
|
|
if webseeds:
|
|
for webseed in webseeds:
|
|
if webseed.endswith('.php'):
|
|
httpseeds.append(webseed)
|
|
else:
|
|
url_list.append(webseed)
|
|
|
|
if url_list:
|
|
data['url-list'] = url_list
|
|
if httpseeds:
|
|
data['httpseeds'] = httpseeds
|
|
if created_by:
|
|
data['created by'] = created_by.encode('utf8')
|
|
|
|
if trackers and (len(trackers[0]) > 1 or len(trackers) > 1):
|
|
data['announce-list'] = trackers
|
|
|
|
data['encoding'] = 'UTF-8'
|
|
with open(f, 'wb') as file_:
|
|
file_.write(bencode(utf8_encode_structure(data)))
|
|
|
|
|
|
def calcsize(path):
|
|
total = 0
|
|
for s in subfiles(os.path.abspath(path)):
|
|
total += os.path.getsize(s[1])
|
|
return total
|
|
|
|
|
|
def makeinfo(path, piece_length, progress, name=None, content_type=None, private=False):
|
|
# HEREDAVE. If path is directory, how do we assign content type?
|
|
path = os.path.abspath(path)
|
|
piece_count = 0
|
|
if os.path.isdir(path):
|
|
subs = sorted(subfiles(path))
|
|
pieces = []
|
|
sh = sha()
|
|
done = 0
|
|
fs = []
|
|
totalsize = 0.0
|
|
totalhashed = 0
|
|
for p, f in subs:
|
|
totalsize += os.path.getsize(f)
|
|
if totalsize >= piece_length:
|
|
import math
|
|
|
|
num_pieces = math.ceil(totalsize / piece_length)
|
|
else:
|
|
num_pieces = 1
|
|
|
|
for p, f in subs:
|
|
pos = 0
|
|
size = os.path.getsize(f)
|
|
p2 = [n.encode('utf8') for n in p]
|
|
if content_type:
|
|
fs.append(
|
|
{'length': size, 'path': p2, 'content_type': content_type}
|
|
) # HEREDAVE. bad for batch!
|
|
else:
|
|
fs.append({'length': size, 'path': p2})
|
|
with open(f, 'rb') as file_:
|
|
while pos < size:
|
|
a = min(size - pos, piece_length - done)
|
|
sh.update(file_.read(a))
|
|
done += a
|
|
pos += a
|
|
totalhashed += a
|
|
|
|
if done == piece_length:
|
|
pieces.append(sh.digest())
|
|
piece_count += 1
|
|
done = 0
|
|
sh = sha()
|
|
progress(piece_count, num_pieces)
|
|
if done > 0:
|
|
pieces.append(sh.digest())
|
|
piece_count += 1
|
|
progress(piece_count, num_pieces)
|
|
|
|
if not name:
|
|
name = os.path.split(path)[1]
|
|
|
|
return {
|
|
'pieces': b''.join(pieces),
|
|
'piece length': piece_length,
|
|
'files': fs,
|
|
'name': name.encode('utf8'),
|
|
'private': private,
|
|
}
|
|
else:
|
|
size = os.path.getsize(path)
|
|
if size >= piece_length:
|
|
num_pieces = size // piece_length
|
|
else:
|
|
num_pieces = 1
|
|
|
|
pieces = []
|
|
p = 0
|
|
with open(path, 'rb') as _file:
|
|
while p < size:
|
|
x = _file.read(min(piece_length, size - p))
|
|
pieces.append(sha(x).digest())
|
|
piece_count += 1
|
|
p += piece_length
|
|
if p > size:
|
|
p = size
|
|
progress(piece_count, num_pieces)
|
|
name = os.path.split(path)[1].encode('utf8')
|
|
if content_type is not None:
|
|
return {
|
|
'pieces': b''.join(pieces),
|
|
'piece length': piece_length,
|
|
'length': size,
|
|
'name': name,
|
|
'content_type': content_type,
|
|
'private': private,
|
|
}
|
|
return {
|
|
'pieces': b''.join(pieces),
|
|
'piece length': piece_length,
|
|
'length': size,
|
|
'name': name,
|
|
'private': private,
|
|
}
|
|
|
|
|
|
def subfiles(d):
|
|
r = []
|
|
stack = [([], d)]
|
|
while stack:
|
|
p, n = stack.pop()
|
|
if os.path.isdir(n):
|
|
for s in os.listdir(n):
|
|
if s not in ignore and not s.startswith('.'):
|
|
stack.append((p + [s], os.path.join(n, s)))
|
|
else:
|
|
r.append((p, n))
|
|
return r
|