2016-05-05 05:27:40 +02:00
|
|
|
import logging
|
2016-06-27 23:07:59 +02:00
|
|
|
import mimetypes
|
2016-05-05 05:27:40 +02:00
|
|
|
import os
|
2016-08-09 18:26:34 +02:00
|
|
|
import random
|
2016-05-05 05:27:40 +02:00
|
|
|
|
2016-10-23 07:17:24 +02:00
|
|
|
from twisted.internet import threads, defer, reactor
|
2016-05-05 05:27:40 +02:00
|
|
|
|
2016-09-27 20:18:16 +02:00
|
|
|
from lbrynet.lbryfilemanager.EncryptedFileCreator import create_lbry_file
|
2016-01-21 04:00:28 +01:00
|
|
|
from lbrynet.lbryfile.StreamDescriptor import publish_sd_blob
|
2016-09-27 20:18:35 +02:00
|
|
|
from lbrynet.metadata.Metadata import Metadata
|
2016-09-27 20:18:16 +02:00
|
|
|
from lbrynet.lbryfilemanager.EncryptedFileDownloader import ManagedEncryptedFileDownloader
|
2016-08-10 14:44:41 +02:00
|
|
|
from lbrynet import reflector
|
2016-12-21 20:55:43 +01:00
|
|
|
from lbrynet import conf
|
2016-06-07 10:19:51 +02:00
|
|
|
|
|
|
|
|
2016-01-21 04:00:28 +01:00
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
class Publisher(object):
|
|
|
|
def __init__(self, session, lbry_file_manager, wallet):
|
|
|
|
self.session = session
|
|
|
|
self.lbry_file_manager = lbry_file_manager
|
|
|
|
self.wallet = wallet
|
|
|
|
self.received_file_name = False
|
|
|
|
self.file_path = None
|
|
|
|
self.file_name = None
|
|
|
|
self.publish_name = None
|
|
|
|
self.bid_amount = None
|
|
|
|
self.verified = False
|
|
|
|
self.lbry_file = None
|
2016-06-27 23:07:59 +02:00
|
|
|
self.txid = None
|
2016-10-14 08:13:37 +02:00
|
|
|
self.nout = None
|
2017-01-06 18:17:49 +01:00
|
|
|
self.claim_id = None
|
|
|
|
self.fee = None
|
2016-07-27 03:24:58 +02:00
|
|
|
self.stream_hash = None
|
2016-11-04 19:15:27 +01:00
|
|
|
# TODO: this needs to be passed into the constructor
|
2016-12-21 20:55:43 +01:00
|
|
|
reflector_server = random.choice(conf.settings.reflector_servers)
|
2016-08-09 18:26:34 +02:00
|
|
|
self.reflector_server, self.reflector_port = reflector_server[0], reflector_server[1]
|
2016-07-25 07:40:26 +02:00
|
|
|
self.metadata = {}
|
2016-01-21 04:00:28 +01:00
|
|
|
|
2016-08-05 05:08:54 +02:00
|
|
|
def start(self, name, file_path, bid, metadata):
|
2016-08-11 02:04:03 +02:00
|
|
|
log.info('Starting publish for %s', name)
|
2017-01-13 18:19:26 +01:00
|
|
|
|
2016-01-21 04:00:28 +01:00
|
|
|
def _show_result():
|
2017-01-13 18:19:26 +01:00
|
|
|
log.info(
|
|
|
|
"Success! Published %s --> lbry://%s txid: %s nout: %d",
|
|
|
|
self.file_name, self.publish_name, self.txid, self.nout
|
|
|
|
)
|
|
|
|
return defer.succeed({
|
|
|
|
'nout': self.nout,
|
|
|
|
'txid': self.txid,
|
|
|
|
'claim_id': self.claim_id,
|
|
|
|
'fee': self.fee,
|
|
|
|
})
|
2016-01-21 04:00:28 +01:00
|
|
|
|
|
|
|
self.publish_name = name
|
|
|
|
self.file_path = file_path
|
|
|
|
self.bid_amount = bid
|
2016-06-27 23:07:59 +02:00
|
|
|
self.metadata = metadata
|
2016-01-21 04:00:28 +01:00
|
|
|
|
2016-11-04 19:15:27 +01:00
|
|
|
# TODO: we cannot have this sort of code scattered throughout
|
|
|
|
# our code base. Use polymorphism instead
|
2016-08-23 01:55:08 +02:00
|
|
|
if os.name == "nt":
|
|
|
|
file_mode = 'rb'
|
|
|
|
else:
|
|
|
|
file_mode = 'r'
|
|
|
|
|
2016-01-21 04:00:28 +01:00
|
|
|
d = self._check_file_path(self.file_path)
|
2016-11-04 19:15:27 +01:00
|
|
|
# TODO: ensure that we aren't leaving this resource open
|
2016-01-21 04:00:28 +01:00
|
|
|
d.addCallback(lambda _: create_lbry_file(self.session, self.lbry_file_manager,
|
2016-08-23 01:55:08 +02:00
|
|
|
self.file_name, open(self.file_path, file_mode)))
|
2016-01-21 04:00:28 +01:00
|
|
|
d.addCallback(self.add_to_lbry_files)
|
|
|
|
d.addCallback(lambda _: self._create_sd_blob())
|
|
|
|
d.addCallback(lambda _: self._claim_name())
|
2016-07-27 03:24:58 +02:00
|
|
|
d.addCallback(lambda _: self.set_status())
|
2016-08-09 18:07:26 +02:00
|
|
|
d.addCallback(lambda _: self.start_reflector())
|
2016-12-10 20:42:57 +01:00
|
|
|
d.addCallbacks(
|
|
|
|
lambda _: _show_result(),
|
|
|
|
errback=log.fail(self._throw_publish_error),
|
|
|
|
errbackArgs=(
|
|
|
|
"An error occurred publishing %s to %s", self.file_name, self.publish_name)
|
|
|
|
)
|
2016-01-21 04:00:28 +01:00
|
|
|
return d
|
|
|
|
|
2016-08-09 18:07:26 +02:00
|
|
|
def start_reflector(self):
|
2016-11-04 19:15:27 +01:00
|
|
|
# TODO: is self.reflector_server unused?
|
2016-12-21 20:55:43 +01:00
|
|
|
reflector_server = random.choice(conf.settings.reflector_servers)
|
2016-08-11 07:06:51 +02:00
|
|
|
reflector_address, reflector_port = reflector_server[0], reflector_server[1]
|
2016-08-21 06:58:25 +02:00
|
|
|
log.info("Reflecting new publication")
|
2016-08-10 14:44:41 +02:00
|
|
|
factory = reflector.ClientFactory(
|
|
|
|
self.session.blob_manager,
|
|
|
|
self.lbry_file_manager.stream_info_manager,
|
|
|
|
self.stream_hash
|
|
|
|
)
|
2016-08-11 07:06:51 +02:00
|
|
|
d = reactor.resolve(reflector_address)
|
|
|
|
d.addCallback(lambda ip: reactor.connectTCP(ip, reflector_port, factory))
|
|
|
|
d.addCallback(lambda _: factory.finished_deferred)
|
|
|
|
return d
|
2016-08-09 18:07:26 +02:00
|
|
|
|
2016-01-21 04:00:28 +01:00
|
|
|
def _check_file_path(self, file_path):
|
|
|
|
def check_file_threaded():
|
|
|
|
f = open(file_path)
|
|
|
|
f.close()
|
|
|
|
self.file_name = os.path.basename(self.file_path)
|
|
|
|
return True
|
|
|
|
return threads.deferToThread(check_file_threaded)
|
|
|
|
|
2016-07-27 03:24:58 +02:00
|
|
|
def set_lbry_file(self, lbry_file_downloader):
|
2016-01-21 04:00:28 +01:00
|
|
|
self.lbry_file = lbry_file_downloader
|
2016-07-27 03:24:58 +02:00
|
|
|
return defer.succeed(None)
|
2016-01-21 04:00:28 +01:00
|
|
|
|
|
|
|
def add_to_lbry_files(self, stream_hash):
|
2016-07-27 03:24:58 +02:00
|
|
|
self.stream_hash = stream_hash
|
2016-09-27 19:52:44 +02:00
|
|
|
prm = self.session.payment_rate_manager
|
2016-01-21 04:00:28 +01:00
|
|
|
d = self.lbry_file_manager.add_lbry_file(stream_hash, prm)
|
2016-07-27 03:24:58 +02:00
|
|
|
d.addCallback(self.set_lbry_file)
|
2016-01-21 04:00:28 +01:00
|
|
|
return d
|
|
|
|
|
|
|
|
def _create_sd_blob(self):
|
2016-08-11 02:04:03 +02:00
|
|
|
log.debug('Creating stream descriptor blob')
|
|
|
|
d = publish_sd_blob(self.lbry_file_manager.stream_info_manager,
|
|
|
|
self.session.blob_manager,
|
2016-01-21 04:00:28 +01:00
|
|
|
self.lbry_file.stream_hash)
|
|
|
|
|
|
|
|
def set_sd_hash(sd_hash):
|
2016-08-11 02:04:03 +02:00
|
|
|
log.debug('stream descriptor hash: %s', sd_hash)
|
2016-07-25 07:40:26 +02:00
|
|
|
if 'sources' not in self.metadata:
|
|
|
|
self.metadata['sources'] = {}
|
|
|
|
self.metadata['sources']['lbry_sd_hash'] = sd_hash
|
2016-01-21 04:00:28 +01:00
|
|
|
|
|
|
|
d.addCallback(set_sd_hash)
|
|
|
|
return d
|
|
|
|
|
2016-07-27 03:24:58 +02:00
|
|
|
def set_status(self):
|
2016-08-11 02:04:03 +02:00
|
|
|
log.debug('Setting status')
|
2016-11-30 21:20:45 +01:00
|
|
|
d = self.lbry_file_manager.change_lbry_file_status(
|
|
|
|
self.lbry_file, ManagedEncryptedFileDownloader.STATUS_FINISHED)
|
2016-07-27 03:24:58 +02:00
|
|
|
d.addCallback(lambda _: self.lbry_file.restore())
|
|
|
|
return d
|
|
|
|
|
2016-01-21 04:00:28 +01:00
|
|
|
def _claim_name(self):
|
2016-08-11 02:04:03 +02:00
|
|
|
log.debug('Claiming name')
|
|
|
|
self._update_metadata()
|
2016-08-05 05:08:54 +02:00
|
|
|
m = Metadata(self.metadata)
|
2016-07-27 02:52:05 +02:00
|
|
|
|
2017-01-06 18:17:49 +01:00
|
|
|
def set_claim_out(claim_out):
|
|
|
|
log.debug('Name claimed using txid: %s, nout: %d, claim_id: %s, fee :%f',
|
|
|
|
claim_out['txid'], claim_out['nout'],
|
|
|
|
claim_out['claim_id'], claim_out['fee'])
|
|
|
|
self.txid = claim_out['txid']
|
|
|
|
self.nout = claim_out['nout']
|
|
|
|
self.claim_id = claim_out['claim_id']
|
|
|
|
self.fee = claim_out['fee']
|
2016-01-21 04:00:28 +01:00
|
|
|
|
2016-08-05 05:08:54 +02:00
|
|
|
d = self.wallet.claim_name(self.publish_name, self.bid_amount, m)
|
2017-01-06 18:17:49 +01:00
|
|
|
d.addCallback(set_claim_out)
|
2016-01-21 04:00:28 +01:00
|
|
|
return d
|
|
|
|
|
2016-08-11 02:04:03 +02:00
|
|
|
def _update_metadata(self):
|
|
|
|
filename = os.path.join(self.lbry_file.download_directory, self.lbry_file.file_name)
|
2016-08-25 23:59:20 +02:00
|
|
|
self.metadata['content_type'] = get_content_type(filename)
|
|
|
|
self.metadata['ver'] = Metadata.current_version
|
2016-08-11 02:04:03 +02:00
|
|
|
|
2016-12-10 20:42:57 +01:00
|
|
|
def _throw_publish_error(self, err):
|
|
|
|
# TODO: I'm not a fan of the log and re-throw, especially when
|
|
|
|
# the new exception is more generic. Look over this to
|
|
|
|
# see if there is a reason not to remove the errback
|
|
|
|
# handler and allow the original exception to move up
|
|
|
|
# the stack.
|
|
|
|
raise Exception("Publish failed")
|
2016-08-11 02:04:03 +02:00
|
|
|
|
|
|
|
|
|
|
|
def get_content_type(filename):
|
2016-10-14 15:55:45 +02:00
|
|
|
return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
|