Почему загрузка файлов utorrents Magnet to Torrent выполняется быстрее, чем мой скрипт python?

Я пытаюсь преобразовать URL-адреса торрент-магнита в файлы .torrent, используя скрипт Python. Скрипт python подключается к dht и ждет метаданных, а затем создает из него торрент-файл.

e.g.

#!/usr/bin/env python
'''
Created on Apr 19, 2012
@author: dan, Faless

    GNU GENERAL PUBLIC LICENSE - Version 3

    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/>.

    http://www.gnu.org/licenses/gpl-3.0.txt

'''

import shutil
import tempfile
import os.path as pt
import sys
import libtorrent as lt
from time import sleep


def magnet2torrent(magnet, output_name=None):
    if output_name and \
            not pt.isdir(output_name) and \
            not pt.isdir(pt.dirname(pt.abspath(output_name))):
        print("Invalid output folder: " + pt.dirname(pt.abspath(output_name)))
        print("")
        sys.exit(0)

    tempdir = tempfile.mkdtemp()
    ses = lt.session()
    params = {
        'save_path': tempdir,
        'duplicate_is_error': True,
        'storage_mode': lt.storage_mode_t(2),
        'paused': False,
        'auto_managed': True,
        'duplicate_is_error': True
    }
    handle = lt.add_magnet_uri(ses, magnet, params)

    print("Downloading Metadata (this may take a while)")
    while (not handle.has_metadata()):
        try:
            sleep(1)
        except KeyboardInterrupt:
            print("Aborting...")
            ses.pause()
            print("Cleanup dir " + tempdir)
            shutil.rmtree(tempdir)
            sys.exit(0)
    ses.pause()
    print("Done")

    torinfo = handle.get_torrent_info()
    torfile = lt.create_torrent(torinfo)

    output = pt.abspath(torinfo.name() + ".torrent")

    if output_name:
        if pt.isdir(output_name):
            output = pt.abspath(pt.join(
                output_name, torinfo.name() + ".torrent"))
        elif pt.isdir(pt.dirname(pt.abspath(output_name))):
            output = pt.abspath(output_name)

    print("Saving torrent file here : " + output + " ...")
    torcontent = lt.bencode(torfile.generate())
    f = open(output, "wb")
    f.write(lt.bencode(torfile.generate()))
    f.close()
    print("Saved! Cleaning up dir: " + tempdir)
    ses.remove_torrent(handle)
    shutil.rmtree(tempdir)

    return output


def showHelp():
    print("")
    print("USAGE: " + pt.basename(sys.argv[0]) + " MAGNET [OUTPUT]")
    print("  MAGNET\t- the magnet url")
    print("  OUTPUT\t- the output torrent file name")
    print("")


def main():
    if len(sys.argv) < 2:
        showHelp()
        sys.exit(0)

    magnet = sys.argv[1]
    output_name = None

    if len(sys.argv) >= 3:
        output_name = sys.argv[2]

    magnet2torrent(magnet, output_name)


if __name__ == "__main__":
    main()

Приведенный выше скрипт занимает около 1+ минут, чтобы получить метаданные и создать .torrent файл, в то время как utorrent клиент занимает всего несколько секунд, почему это так?

Как сделать скрипт быстрее?

я хотел бы получить метаданные примерно для 1k+ торрентов.

например Магнитная ссылка

magnet:?xt=urn:btih:BFEFB51F4670D682E98382ADF81014638A25105A&dn=openSUSE+13.2+DVD+x86_64.iso&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80&tr=udp%3A%2F%2Ftracker.ccc.de%3A80

Обновить :

я указал известные URL-адреса маршрутизатора dht, подобные этому, в моем сценарии.

session = lt.session()
session.listen_on(6881, 6891)

session.add_dht_router("router.utorrent.com", 6881)
session.add_dht_router("router.bittorrent.com", 6881)
session.add_dht_router("dht.transmissionbt.com", 6881)
session.add_dht_router("router.bitcomet.com", 6881)
session.add_dht_router("dht.aelitis.com", 6881)
session.start_dht()

но это все еще медленно, и иногда я получаю такие ошибки, как

DHT error [hostname lookup] (1) Host not found (authoritative)
could not map port using UPnP: no router found

обновление:

Я написал этот скрипт scmall, который извлекает хэш шестнадцатеричной информации из БД и пытается получить метаданные из dht, а затем вставляет торрент-файл в БД.

я заставил его работать на неопределенный срок, так как я не знал, как сохранить состояние, поэтому, поддерживая его работу, вы получите больше пиров, а выборка метаданных будет быстрее.

#!/usr/bin/env python
# this file will run as client or daemon and fetch torrent meta data i.e. torrent files from magnet uri

import libtorrent as lt # libtorrent library
import tempfile # for settings parameters while fetching metadata as temp dir
import sys #getting arguiments from shell or exit script
from time import sleep #sleep
import shutil # removing directory tree from temp directory 
import os.path # for getting pwd and other things
from pprint import pprint # for debugging, showing object data
import MySQLdb # DB connectivity 
import os
from datetime import date, timedelta

#create lock file to make sure only single instance is running
lock_file_name = "/daemon.lock"

if(os.path.isfile(lock_file_name)):
    sys.exit('another instance running')
#else:
    #f = open(lock_file_name, "w")
    #f.close()

session = lt.session()
session.listen_on(6881, 6891)

session.add_dht_router("router.utorrent.com", 6881)
session.add_dht_router("router.bittorrent.com", 6881)
session.add_dht_router("dht.transmissionbt.com", 6881)
session.add_dht_router("router.bitcomet.com", 6881)
session.add_dht_router("dht.aelitis.com", 6881)
session.start_dht()

alive = True
while alive:

    db_conn = MySQLdb.connect(  host = 'localhost',     user = '',  passwd = '',    db = 'basesite',    unix_socket='') # Open database connection
    #print('reconnecting')
    #get all records where enabled = 0 and uploaded within yesterday 
    subset_count = 5 ;

    yesterday = date.today() - timedelta(1)
    yesterday = yesterday.strftime('%Y-%m-%d %H:%M:%S')
    #print(yesterday)

    total_count_query = ("SELECT COUNT(*) as total_count FROM content WHERE upload_date > '"+ yesterday +"' AND enabled = '0' ")
    #print(total_count_query)
    try:
        total_count_cursor = db_conn.cursor()# prepare a cursor object using cursor() method
        total_count_cursor.execute(total_count_query) # Execute the SQL command
        total_count_results = total_count_cursor.fetchone() # Fetch all the rows in a list of lists.
        total_count = total_count_results[0]
        print(total_count)
    except:
            print "Error: unable to select data"

    total_pages = total_count/subset_count
    #print(total_pages)

    current_page = 1
    while(current_page <= total_pages):
        from_count = (current_page * subset_count) - subset_count

        #print(current_page)
        #print(from_count)

        hashes = []

        get_mysql_data_query = ("SELECT hash FROM content WHERE upload_date > '" + yesterday +"' AND enabled = '0' ORDER BY record_num ASC LIMIT "+ str(from_count) +" , " + str(subset_count) +" ")
        #print(get_mysql_data_query)
        try:
            get_mysql_data_cursor = db_conn.cursor()# prepare a cursor object using cursor() method
            get_mysql_data_cursor.execute(get_mysql_data_query) # Execute the SQL command
            get_mysql_data_results = get_mysql_data_cursor.fetchall() # Fetch all the rows in a list of lists.
            for row in get_mysql_data_results:
                hashes.append(row[0].upper())
        except:
            print "Error: unable to select data"

        print(hashes)

        handles = []

        for hash in hashes:
            tempdir = tempfile.mkdtemp()
            add_magnet_uri_params = {
                'save_path': tempdir,
                'duplicate_is_error': True,
                'storage_mode': lt.storage_mode_t(2),
                'paused': False,
                'auto_managed': True,
                'duplicate_is_error': True
            }
            magnet_uri = "magnet:?xt=urn:btih:" + hash.upper() + "&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80&tr=udp%3A%2F%2Ftracker.ccc.de%3A80"
            #print(magnet_uri)
            handle = lt.add_magnet_uri(session, magnet_uri, add_magnet_uri_params)
            handles.append(handle) #push handle in handles list

        #print("handles length is :")
        #print(len(handles))

        while(len(handles) != 0):
            for h in handles:
                #print("inside handles for each loop")
                if h.has_metadata():
                    torinfo = h.get_torrent_info()
                    final_info_hash = str(torinfo.info_hash())
                    final_info_hash = final_info_hash.upper()
                    torfile = lt.create_torrent(torinfo)
                    torcontent = lt.bencode(torfile.generate())
                    tfile_size = len(torcontent)
                    try:
                        insert_cursor = db_conn.cursor()# prepare a cursor object using cursor() method
                        insert_cursor.execute("""INSERT INTO dht_tfiles (hash, tdata) VALUES (%s, %s)""",  [final_info_hash , torcontent] )
                        db_conn.commit()
                        #print "data inserted in DB"
                    except MySQLdb.Error, e:
                        try:
                            print "MySQL Error [%d]: %s" % (e.args[0], e.args[1])
                        except IndexError:
                            print "MySQL Error: %s" % str(e)    

                    shutil.rmtree(h.save_path())    #   remove temp data directory
                    session.remove_torrent(h) # remove torrnt handle from session   
                    handles.remove(h) #remove handle from list

                else:
                    if(h.status().active_time > 600):   # check if handle is more than 10 minutes old i.e. 600 seconds
                        #print('remove_torrent')
                        shutil.rmtree(h.save_path())    #   remove temp data directory
                        session.remove_torrent(h) # remove torrnt handle from session   
                        handles.remove(h) #remove handle from list
                sleep(1)        
                #print('sleep1')

        print('sleep10')
        sleep(10)
        current_page = current_page + 1
    #print('sleep20')
    sleep(20)

os.remove(lock_file_name);

теперь мне нужно реализовать новые вещи, как предложил Арвид.


ОБНОВЛЕНИЕ

мне удалось реализовать то, что предложил Арвид. и еще несколько расширений, которые я нашел на форумах поддержки потопа http://forum.deluge-torrent.org/viewtopic.php?f=7&t=42299&start=10

#!/usr/bin/env python

import libtorrent as lt # libtorrent library
import tempfile # for settings parameters while fetching metadata as temp dir
import sys #getting arguiments from shell or exit script
from time import sleep #sleep
import shutil # removing directory tree from temp directory 
import os.path # for getting pwd and other things
from pprint import pprint # for debugging, showing object data
import MySQLdb # DB connectivity 
import os
from datetime import date, timedelta

def var_dump(obj):
  for attr in dir(obj):
    print "obj.%s = %s" % (attr, getattr(obj, attr))

session = lt.session()
session.add_extension('ut_pex')
session.add_extension('ut_metadata')
session.add_extension('smart_ban')
session.add_extension('metadata_transfer')  

#session = lt.session(lt.fingerprint("DE", 0, 1, 0, 0), flags=1)

session_save_filename = "/tmp/new.client.save_state"

if(os.path.isfile(session_save_filename)):

    fileread = open(session_save_filename, 'rb')
    session.load_state(lt.bdecode(fileread.read()))
    fileread.close()
    print('session loaded from file')
else:
    print('new session started')

session.add_dht_router("router.utorrent.com", 6881)
session.add_dht_router("router.bittorrent.com", 6881)
session.add_dht_router("dht.transmissionbt.com", 6881)
session.add_dht_router("router.bitcomet.com", 6881)
session.add_dht_router("dht.aelitis.com", 6881)
session.start_dht()

alerts = [] 

alive = True
while alive:
    a = session.pop_alert()
    alerts.append(a)
    print('----------')
    for a in alerts:
        var_dump(a)
        alerts.remove(a)


    print('sleep10')
    sleep(10)
    filewrite = open(session_save_filename, "wb")
    filewrite.write(lt.bencode(session.save_state()))
    filewrite.close()

держал его в рабочем состоянии в течение минуты и получил предупреждение

obj.msg = no router found 

обновление:

после некоторых испытаний выглядит так

session.add_dht_router("router.bitcomet.com", 6881)

вызывая

('%s: %s', 'alert', 'DHT error [hostname lookup] (1) Host not found (authoritative)')

обновление: я добавил

session.start_dht()
session.start_lsd()
session.start_upnp()
session.start_natpmp()

и получил предупреждение

('%s: %s', 'portmap_error_alert', 'could not map port using UPnP: no router found')

person Community    schedule 24.09.2015    source источник
comment
Вероятно, к вашему торрент-клиенту уже подключено большое количество пиров, и это позволяет быстро выполнить DHT-запрос. Вместо этого ваш скрипт запускается с нуля, поэтому он должен загружаться с одного известного узла.   -  person Matteo Italia    schedule 24.09.2015
comment
@MatteoItalia, как я могу запустить его как демон и поддерживать его работу, предоставляя ему URL-адреса магнита? есть ли способ, потому что прямо сейчас я выполняю этот скрипт для каждого URL-адреса магнита. так что это должно начинаться с нуля каждый раз, когда я думаю.   -  person    schedule 24.09.2015
comment
@AMB: Как быстро он работает, если вы передаете несколько URI магнитов и записываете несколько торрент-файлов в одном сеансе?   -  person Blender    schedule 26.09.2015
comment
@Blender иногда требуется столько же или больше времени, но utorrent работает довольно быстро.   -  person    schedule 26.09.2015
comment
Использовали ли вы какой-либо профилировщик, чтобы увидеть, где ваши части кода тратят время?   -  person Ozan    schedule 28.09.2015


Ответы (1)


как отмечает MatteoItalia, загрузка DHT не происходит мгновенно и иногда может занять некоторое время. Не существует четко определенного момента времени, когда процесс начальной загрузки завершен, это континуум все большего подключения к сети.

Чем больше подключений и чем больше хороших и стабильных узлов вы знаете, тем быстрее будет выполняться поиск. Одним из способов исключить большую часть процесса начальной загрузки (чтобы получить больше информации о сравнении яблок с яблоками) было бы начать синхронизацию после получения файла dht_bootstrap_alert (а также отложите добавление магнитной ссылки до этого момента).

Добавление узлов начальной загрузки dht в первую очередь сделает его возможным для начальной загрузки, но это не обязательно будет особенно быстрым. Обычно вы хотите, чтобы примерно 270 узлов (включая замещающие узлы) считались загруженными.

Чтобы ускорить процесс начальной загрузки, вы можете сохранить< /em> и загрузить состояние сеанса, которое включает таблицу маршрутизации dht. Это перезагрузит все узлы из предыдущего сеанса в таблицу маршрутизации и (при условии, что вы не изменили IP-адрес и все работает правильно) начальная загрузка должна быть быстрее.

Убедитесь, что вы не запускаете DHT в конструкторе сеанса (в качестве аргумента flags просто передайте add_default_plugins), загрузить состояние, добавьте узлы маршрутизатора, а затем запустите dht.

К сожалению, есть много движущихся частей, участвующих в том, чтобы заставить это работать внутри, порядок важен, и могут быть тонкие проблемы.

Кроме того, обратите внимание, что поддержание непрерывной работы DHT будет быстрее, поскольку перезагрузка состояния по-прежнему будет проходить через начальную загрузку, у него просто будет больше узлов, чтобы пропинговать и попытаться «подключиться».

отключение флага start_default_features также означает, что UPnP и NAT-PMP не будут запущены, если вы их используете, вам придется запустите их также вручную.

person Arvid    schedule 27.09.2015
comment
Спасибо за ваш ответ, это очистило большинство моих запросов, есть ли какой-нибудь пример кода Python, который использует состояние загрузки и сохранения, add_default_plugins, я написал небольшой скрипт для получения метаданных, которые я обновляю вопрос. - person ; 28.09.2015
comment
пожалуйста, проверьте добавленный код в вопросе. Я очень ценю ваш вклад, спасибо - person ; 28.09.2015
comment
обновление: я могу сохранять и загружать состояние, но не могу добавить add_default_plugins, так как не так много доступных руководств по Python. - person ; 28.09.2015
comment
ах, точно. есть проблема совместимости с python и плагинами. если вы используете достаточно новую версию libtorrent, вы можете вызвать ses.add_extension('ut_metadata') и ses.add_extension('ut_pex') - person Arvid; 28.09.2015
comment
Спасибо, мне наконец удалось запустить его, но когда я запускаю его без каких-либо торрентов или URL-адресов магнита, через минуту он все еще говорит, что маршрутизатор не найден. - person ; 29.09.2015
comment
вы имеете в виду ошибку UPnP? у вас есть роутер UPnP? вы ожидаете, что libtorrent найдет его? UPnP включен на нем? - person Arvid; 29.09.2015
comment
да ошибка UPnP, idk, понятия не имею, это выделенный сервер. Спасибо. - person ; 29.09.2015