forked from LBRYCommunity/lbry-sdk
192ac6959a
More specific exceptions are raised when problems are encountered looking up metadata and validating stream descriptor files, and on the GUI those more specific exceptions are used to prevent errors from being presented to the user. If the user has selected the option to re-upload data for some stream, blobs downloaded for that purpose will not be deleted when they are finished being output. Instead, by default, for the GUI they will be deleted when the stream is removed from the GUI. That can be changed so they are not deleted at all, using the lbry.conf file.
280 lines
No EOL
11 KiB
Python
280 lines
No EOL
11 KiB
Python
import subprocess
|
|
import binascii
|
|
|
|
from zope.interface import implements
|
|
|
|
from lbrynet.lbryfile.StreamDescriptor import save_sd_info
|
|
from lbrynet.cryptstream.client.CryptStreamDownloader import CryptStreamDownloader
|
|
from lbrynet.core.client.StreamProgressManager import FullStreamProgressManager
|
|
from lbrynet.interfaces import IStreamDownloaderFactory
|
|
from lbrynet.lbryfile.client.LBRYFileMetadataHandler import LBRYFileMetadataHandler
|
|
import os
|
|
from twisted.internet import defer, threads, reactor
|
|
from twisted.python.procutils import which
|
|
|
|
|
|
class LBRYFileDownloader(CryptStreamDownloader):
|
|
"""Classes which inherit from this class download LBRY files"""
|
|
|
|
def __init__(self, stream_hash, peer_finder, rate_limiter, blob_manager,
|
|
stream_info_manager, payment_rate_manager, wallet, upload_allowed):
|
|
CryptStreamDownloader.__init__(self, peer_finder, rate_limiter, blob_manager,
|
|
payment_rate_manager, wallet, upload_allowed)
|
|
self.stream_hash = stream_hash
|
|
self.stream_info_manager = stream_info_manager
|
|
self.suggested_file_name = None
|
|
self._calculated_total_bytes = None
|
|
|
|
def set_stream_info(self):
|
|
if self.key is None:
|
|
d = self.stream_info_manager.get_stream_info(self.stream_hash)
|
|
|
|
def set_stream_info(stream_info):
|
|
key, stream_name, suggested_file_name = stream_info
|
|
self.key = binascii.unhexlify(key)
|
|
self.stream_name = binascii.unhexlify(stream_name)
|
|
self.suggested_file_name = binascii.unhexlify(suggested_file_name)
|
|
|
|
d.addCallback(set_stream_info)
|
|
return d
|
|
else:
|
|
return defer.succeed(True)
|
|
|
|
def stop(self):
|
|
d = self._close_output()
|
|
d.addCallback(lambda _: CryptStreamDownloader.stop(self))
|
|
return d
|
|
|
|
def _get_progress_manager(self, download_manager):
|
|
return FullStreamProgressManager(self._finished_downloading, self.blob_manager, download_manager)
|
|
|
|
def _start(self):
|
|
d = self._setup_output()
|
|
d.addCallback(lambda _: CryptStreamDownloader._start(self))
|
|
return d
|
|
|
|
def _setup_output(self):
|
|
pass
|
|
|
|
def _close_output(self):
|
|
pass
|
|
|
|
def get_total_bytes(self):
|
|
if self._calculated_total_bytes is None or self._calculated_total_bytes == 0:
|
|
if self.download_manager is None:
|
|
return 0
|
|
else:
|
|
self._calculated_total_bytes = self.download_manager.calculate_total_bytes()
|
|
return self._calculated_total_bytes
|
|
|
|
def get_bytes_left_to_output(self):
|
|
if self.download_manager is not None:
|
|
return self.download_manager.calculate_bytes_left_to_output()
|
|
else:
|
|
return 0
|
|
|
|
def get_bytes_left_to_download(self):
|
|
if self.download_manager is not None:
|
|
return self.download_manager.calculate_bytes_left_to_download()
|
|
else:
|
|
return 0
|
|
|
|
def _get_metadata_handler(self, download_manager):
|
|
return LBRYFileMetadataHandler(self.stream_hash, self.stream_info_manager, download_manager)
|
|
|
|
|
|
class LBRYFileDownloaderFactory(object):
|
|
implements(IStreamDownloaderFactory)
|
|
|
|
def __init__(self, peer_finder, rate_limiter, blob_manager, stream_info_manager,
|
|
wallet):
|
|
self.peer_finder = peer_finder
|
|
self.rate_limiter = rate_limiter
|
|
self.blob_manager = blob_manager
|
|
self.stream_info_manager = stream_info_manager
|
|
self.wallet = wallet
|
|
|
|
def can_download(self, sd_validator):
|
|
return True
|
|
|
|
def make_downloader(self, sd_validator, options, payment_rate_manager, **kwargs):
|
|
payment_rate_manager.min_blob_data_payment_rate = options[0]
|
|
upload_allowed = options[1]
|
|
|
|
def create_downloader(stream_hash):
|
|
downloader = self._make_downloader(stream_hash, payment_rate_manager, sd_validator.raw_info,
|
|
upload_allowed)
|
|
d = downloader.set_stream_info()
|
|
d.addCallback(lambda _: downloader)
|
|
return d
|
|
|
|
d = save_sd_info(self.stream_info_manager, sd_validator.raw_info)
|
|
|
|
d.addCallback(create_downloader)
|
|
return d
|
|
|
|
def _make_downloader(self, stream_hash, payment_rate_manager, stream_info, upload_allowed):
|
|
pass
|
|
|
|
|
|
class LBRYFileSaver(LBRYFileDownloader):
|
|
def __init__(self, stream_hash, peer_finder, rate_limiter, blob_manager, stream_info_manager,
|
|
payment_rate_manager, wallet, download_directory, upload_allowed, file_name=None):
|
|
LBRYFileDownloader.__init__(self, stream_hash, peer_finder, rate_limiter, blob_manager,
|
|
stream_info_manager, payment_rate_manager, wallet, upload_allowed)
|
|
self.download_directory = download_directory
|
|
self.file_name = file_name
|
|
self.file_handle = None
|
|
|
|
def set_stream_info(self):
|
|
d = LBRYFileDownloader.set_stream_info(self)
|
|
|
|
def set_file_name():
|
|
if self.file_name is None:
|
|
if self.suggested_file_name:
|
|
self.file_name = os.path.basename(self.suggested_file_name)
|
|
else:
|
|
self.file_name = os.path.basename(self.stream_name)
|
|
|
|
d.addCallback(lambda _: set_file_name())
|
|
return d
|
|
|
|
def stop(self):
|
|
d = LBRYFileDownloader.stop(self)
|
|
d.addCallback(lambda _: self._delete_from_info_manager())
|
|
return d
|
|
|
|
def _get_progress_manager(self, download_manager):
|
|
return FullStreamProgressManager(self._finished_downloading, self.blob_manager, download_manager,
|
|
delete_blob_after_finished=not self.upload_allowed)
|
|
|
|
def _setup_output(self):
|
|
def open_file():
|
|
if self.file_handle is None:
|
|
file_name = self.file_name
|
|
if not file_name:
|
|
file_name = "_"
|
|
if os.path.exists(os.path.join(self.download_directory, file_name)):
|
|
ext_num = 1
|
|
while os.path.exists(os.path.join(self.download_directory,
|
|
file_name + "_" + str(ext_num))):
|
|
ext_num += 1
|
|
file_name = file_name + "_" + str(ext_num)
|
|
self.file_handle = open(os.path.join(self.download_directory, file_name), 'wb')
|
|
return threads.deferToThread(open_file)
|
|
|
|
def _close_output(self):
|
|
self.file_handle, file_handle = None, self.file_handle
|
|
|
|
def close_file():
|
|
if file_handle is not None:
|
|
name = file_handle.name
|
|
file_handle.close()
|
|
if self.completed is False:
|
|
os.remove(name)
|
|
|
|
return threads.deferToThread(close_file)
|
|
|
|
def _get_write_func(self):
|
|
def write_func(data):
|
|
if self.stopped is False and self.file_handle is not None:
|
|
self.file_handle.write(data)
|
|
return write_func
|
|
|
|
def _delete_from_info_manager(self):
|
|
return self.stream_info_manager.delete_stream(self.stream_hash)
|
|
|
|
|
|
class LBRYFileSaverFactory(LBRYFileDownloaderFactory):
|
|
def __init__(self, peer_finder, rate_limiter, blob_manager, stream_info_manager,
|
|
wallet, download_directory):
|
|
LBRYFileDownloaderFactory.__init__(self, peer_finder, rate_limiter, blob_manager,
|
|
stream_info_manager, wallet)
|
|
self.download_directory = download_directory
|
|
|
|
def _make_downloader(self, stream_hash, payment_rate_manager, stream_info, upload_allowed):
|
|
return LBRYFileSaver(stream_hash, self.peer_finder, self.rate_limiter, self.blob_manager,
|
|
self.stream_info_manager, payment_rate_manager, self.wallet,
|
|
self.download_directory, upload_allowed)
|
|
|
|
def get_description(self):
|
|
return "Save"
|
|
|
|
|
|
class LBRYFileOpener(LBRYFileDownloader):
|
|
def __init__(self, stream_hash, peer_finder, rate_limiter, blob_manager, stream_info_manager,
|
|
payment_rate_manager, wallet, upload_allowed):
|
|
LBRYFileDownloader.__init__(self, stream_hash, peer_finder, rate_limiter, blob_manager,
|
|
stream_info_manager, payment_rate_manager, wallet, upload_allowed)
|
|
self.process = None
|
|
self.process_log = None
|
|
|
|
def stop(self):
|
|
d = LBRYFileDownloader.stop(self)
|
|
d.addCallback(lambda _: self._delete_from_info_manager())
|
|
return d
|
|
|
|
def _get_progress_manager(self, download_manager):
|
|
return FullStreamProgressManager(self._finished_downloading, self.blob_manager, download_manager,
|
|
delete_blob_after_finished=not self.upload_allowed)
|
|
|
|
def _setup_output(self):
|
|
def start_process():
|
|
if os.name == "nt":
|
|
paths = [r'C:\Program Files\VideoLAN\VLC\vlc.exe',
|
|
r'C:\Program Files (x86)\VideoLAN\VLC\vlc.exe']
|
|
for p in paths:
|
|
if os.path.exists(p):
|
|
vlc_path = p
|
|
break
|
|
else:
|
|
raise ValueError("You must install VLC media player to stream files")
|
|
else:
|
|
vlc_path = 'vlc'
|
|
self.process_log = open("vlc.out", 'a')
|
|
try:
|
|
self.process = subprocess.Popen([vlc_path, '-'], stdin=subprocess.PIPE,
|
|
stdout=self.process_log, stderr=self.process_log)
|
|
except OSError:
|
|
raise ValueError("VLC media player could not be opened")
|
|
|
|
d = threads.deferToThread(start_process)
|
|
return d
|
|
|
|
def _close_output(self):
|
|
if self.process is not None:
|
|
self.process.stdin.close()
|
|
self.process = None
|
|
return defer.succeed(True)
|
|
|
|
def _get_write_func(self):
|
|
def write_func(data):
|
|
if self.stopped is False and self.process is not None:
|
|
try:
|
|
self.process.stdin.write(data)
|
|
except IOError:
|
|
reactor.callLater(0, self.stop)
|
|
return write_func
|
|
|
|
def _delete_from_info_manager(self):
|
|
return self.stream_info_manager.delete_stream(self.stream_hash)
|
|
|
|
|
|
class LBRYFileOpenerFactory(LBRYFileDownloaderFactory):
|
|
def can_download(self, sd_validator):
|
|
if which('vlc'):
|
|
return True
|
|
elif os.name == "nt":
|
|
paths = [r'C:\Program Files\VideoLAN\VLC\vlc.exe',
|
|
r'C:\Program Files (x86)\VideoLAN\VLC\vlc.exe']
|
|
for p in paths:
|
|
if os.path.exists(p):
|
|
return True
|
|
return False
|
|
|
|
def _make_downloader(self, stream_hash, payment_rate_manager, stream_info, upload_allowed):
|
|
return LBRYFileOpener(stream_hash, self.peer_finder, self.rate_limiter, self.blob_manager,
|
|
self.stream_info_manager, payment_rate_manager, self.wallet, upload_allowed)
|
|
|
|
def get_description(self):
|
|
return "Stream" |