Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip: add initial support for streaming torrent files #3657

Draft
wants to merge 33 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6378462
locate stream for streaming API by identifier
shyba Sep 1, 2022
8212e73
stream torrent from file
shyba Sep 1, 2022
7828041
stream type independent stream_url
shyba Sep 1, 2022
8ee5cee
update flags, set sequential as a flag
shyba Sep 5, 2022
6efd4dd
fix save path, fix prio, update deprecated calls
shyba Sep 5, 2022
b3bff39
stream from torrent pieces, holding the response until the piece is c…
shyba Sep 5, 2022
b2f8207
piece prioritization and deadlines
shyba Sep 7, 2022
df680e7
fix tests and off by one error
shyba Sep 9, 2022
7410991
save resume data on stop, remove/replace deprecated calls
shyba Sep 9, 2022
dd103d0
save file-torrent association for file list
shyba Sep 16, 2022
7746ded
add test case for restart, fix torrent file update
shyba Sep 16, 2022
37adc59
add tests for streaming, fix bugs
shyba Sep 23, 2022
7c7e185
refactor add_torrent, lints
shyba Sep 23, 2022
f650e8f
test and bugfixes for streaming multifile in a subfolder case
shyba Sep 29, 2022
e862c99
generate 3 files, check that streamed is the largest, add method to l…
shyba Sep 29, 2022
c8f2502
start the stream after adding
shyba Sep 30, 2022
af0ad41
generalize DownloadSDTimeout to DownloadMetadata timeout + fix usages
shyba Oct 12, 2022
b39971b
fix tests for changed error msg
shyba Oct 12, 2022
77d2c81
fix missing added_on for torrent files
shyba Oct 27, 2022
2bf0ca6
fix mime_type for torrent on json encoder
shyba Oct 27, 2022
732b7e7
fix suggested_file_name for torrent on json encoder
shyba Oct 27, 2022
31c6e0e
fix stream_name for torrent on json encoder
shyba Oct 27, 2022
651348f
fix status for completed torrents
shyba Oct 28, 2022
1041a19
deserialize torrent fields properly
shyba Oct 28, 2022
39da718
remove dead code
shyba Oct 28, 2022
8ce5306
fix filtering for fields missing on torrents
shyba Oct 28, 2022
2bea8f5
fix duplicated file entry on startup
shyba Oct 28, 2022
9dc617f
use a non-default port for streaming test so it can run with a live i…
shyba Oct 28, 2022
5cf63fa
restore torrent rowid on restart
shyba Nov 1, 2022
9d86982
test picking file from claim file name
shyba Nov 5, 2022
64aad14
pick file from file name, fallback to largest
shyba Nov 5, 2022
636b7ed
tests: enable logging lbry.torrent when verbosity changes
shyba Nov 5, 2022
dbe3ace
pylint
shyba Dec 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
generalize DownloadSDTimeout to DownloadMetadata timeout + fix usages
  • Loading branch information
shyba committed Dec 15, 2022
commit af0ad417dfcba2fca98bdf9e37451c0ed1c4361d
4 changes: 2 additions & 2 deletions lbry/error/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ Code | Name | Message
511 | CorruptBlob | Blobs is corrupted.
520 | BlobFailedEncryption | Failed to encrypt blob.
531 | DownloadCancelled | Download was canceled.
532 | DownloadSDTimeout | Failed to download sd blob {download} within timeout.
533 | DownloadDataTimeout | Failed to download data blobs for sd hash {download} within timeout.
532 | DownloadMetadataTimeout | Failed to download metadata for {download} within timeout.
533 | DownloadDataTimeout | Failed to download data blobs for {download} within timeout.
534 | InvalidStreamDescriptor | {message}
535 | InvalidData | {message}
536 | InvalidBlobHash | {message}
Expand Down
6 changes: 3 additions & 3 deletions lbry/error/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,18 +411,18 @@ def __init__(self):
super().__init__("Download was canceled.")


class DownloadSDTimeoutError(BlobError):
class DownloadMetadataTimeoutError(BlobError):

def __init__(self, download):
self.download = download
super().__init__(f"Failed to download sd blob {download} within timeout.")
super().__init__(f"Failed to download metadata for {download} within timeout.")


class DownloadDataTimeoutError(BlobError):

def __init__(self, download):
self.download = download
super().__init__(f"Failed to download data blobs for sd hash {download} within timeout.")
super().__init__(f"Failed to download data blobs for {download} within timeout.")


class InvalidStreamDescriptorError(BlobError):
Expand Down
4 changes: 2 additions & 2 deletions lbry/extras/daemon/daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from lbry.blob_exchange.downloader import download_blob
from lbry.dht.peer import make_kademlia_peer
from lbry.error import (
DownloadSDTimeoutError, ComponentsNotStartedError, ComponentStartConditionNotMetError,
DownloadMetadataTimeoutError, ComponentsNotStartedError, ComponentStartConditionNotMetError,
CommandDoesNotExistError, BaseError, WalletNotFoundError, WalletAlreadyLoadedError, WalletAlreadyExistsError,
ConflictingInputValueError, AlreadyPurchasedError, PrivateKeyNotFoundError, InputStringIsBlankError,
InputValueError
Expand Down Expand Up @@ -1140,7 +1140,7 @@ async def jsonrpc_get(
save_file=save_file, wallet=wallet
)
if not stream:
raise DownloadSDTimeoutError(uri)
raise DownloadMetadataTimeoutError(uri)
except Exception as e:
# TODO: use error from lbry.error
log.warning("Error downloading %s: %s", uri, str(e))
Expand Down
6 changes: 3 additions & 3 deletions lbry/file/file_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import typing
from typing import Optional
from aiohttp.web import Request
from lbry.error import ResolveError, DownloadSDTimeoutError, InsufficientFundsError
from lbry.error import ResolveError, DownloadMetadataTimeoutError, InsufficientFundsError
from lbry.error import ResolveTimeoutError, DownloadDataTimeoutError, KeyFeeAboveMaxAllowedError
from lbry.error import InvalidStreamURLError
from lbry.stream.managed_stream import ManagedStream
Expand Down Expand Up @@ -247,10 +247,10 @@ async def download_from_uri(self, uri, exchange_rate_manager: 'ExchangeRateManag
await asyncio.wait_for(stream.save_file(), timeout - (self.loop.time() - before_download))
return stream
except asyncio.TimeoutError:
error = DownloadDataTimeoutError(stream.sd_hash)
error = DownloadDataTimeoutError(stream.identifier)
raise error
except Exception as err: # forgive data timeout, don't delete stream
expected = (DownloadSDTimeoutError, DownloadDataTimeoutError, InsufficientFundsError,
expected = (DownloadMetadataTimeoutError, DownloadDataTimeoutError, InsufficientFundsError,
KeyFeeAboveMaxAllowedError, ResolveError, InvalidStreamURLError)
if isinstance(err, expected):
log.warning("Failed to download %s: %s", uri, str(err))
Expand Down
4 changes: 2 additions & 2 deletions lbry/stream/downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import binascii

from lbry.dht.node import get_kademlia_peers_from_hosts
from lbry.error import DownloadSDTimeoutError
from lbry.error import DownloadMetadataTimeoutError
from lbry.utils import lru_cache_concurrent
from lbry.stream.descriptor import StreamDescriptor
from lbry.blob_exchange.downloader import BlobDownloader
Expand Down Expand Up @@ -77,7 +77,7 @@ async def load_descriptor(self, connection_id: int = 0):
log.info("downloaded sd blob %s", self.sd_hash)
self.time_to_descriptor = self.loop.time() - now
except asyncio.TimeoutError:
raise DownloadSDTimeoutError(self.sd_hash)
raise DownloadMetadataTimeoutError(self.sd_hash)

# parse the descriptor
self.descriptor = await StreamDescriptor.from_stream_descriptor_blob(
Expand Down
4 changes: 2 additions & 2 deletions lbry/stream/managed_stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import logging
from typing import Optional
from aiohttp.web import Request, StreamResponse, HTTPRequestRangeNotSatisfiable
from lbry.error import DownloadSDTimeoutError
from lbry.error import DownloadMetadataTimeoutError
from lbry.schema.mime_types import guess_media_type
from lbry.stream.downloader import StreamDownloader
from lbry.stream.descriptor import StreamDescriptor, sanitize_file_name
Expand Down Expand Up @@ -160,7 +160,7 @@ async def start(self, timeout: Optional[float] = None,
await asyncio.wait_for(self.downloader.start(), timeout)
except asyncio.TimeoutError:
self._running.clear()
raise DownloadSDTimeoutError(self.sd_hash)
raise DownloadMetadataTimeoutError(self.identifier)

if self.delayed_stop_task and not self.delayed_stop_task.done():
self.delayed_stop_task.cancel()
Expand Down
8 changes: 7 additions & 1 deletion lbry/torrent/torrent_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from typing import Optional
from aiohttp.web import Request, StreamResponse, HTTPRequestRangeNotSatisfiable

from lbry.error import DownloadMetadataTimeoutError
from lbry.file.source_manager import SourceManager
from lbry.file.source import ManagedDownloadSource
from lbry.schema.mime_types import guess_media_type
Expand Down Expand Up @@ -57,7 +58,12 @@ def mime_type(self) -> Optional[str]:
return guess_media_type(os.path.basename(self.full_path))[0]

async def start(self, timeout: Optional[float] = None, save_now: Optional[bool] = False):
await self.torrent_session.add_torrent(self.identifier, self.download_directory)
try:
metadata_download = self.torrent_session.add_torrent(self.identifier, self.download_directory)
await asyncio.wait_for(metadata_download, timeout, loop=self.loop)
except asyncio.TimeoutError:
self.torrent_session.remove_torrent(btih=self.identifier)
raise DownloadMetadataTimeoutError(self.identifier)
self.download_directory = self.torrent_session.save_path(self.identifier)
self._file_name = Path(self.torrent_session.full_path(self.identifier)).name
await self.storage.add_torrent(self.identifier, self.torrent_length, self.torrent_name)
Expand Down
10 changes: 5 additions & 5 deletions tests/unit/stream/test_stream_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from lbry.testcase import get_fake_exchange_rate_manager
from lbry.utils import generate_id
from lbry.error import InsufficientFundsError
from lbry.error import KeyFeeAboveMaxAllowedError, ResolveError, DownloadSDTimeoutError, DownloadDataTimeoutError
from lbry.error import KeyFeeAboveMaxAllowedError, ResolveError, DownloadMetadataTimeoutError, DownloadDataTimeoutError
from lbry.wallet import WalletManager, Wallet, Ledger, Transaction, Input, Output, Database
from lbry.wallet.constants import CENT, NULL_HASH32
from lbry.wallet.network import ClientSession
Expand Down Expand Up @@ -232,7 +232,7 @@ def check_post(event):
event['properties']['error_message'], f'Failed to download sd blob {self.sd_hash} within timeout.'
)

await self._test_time_to_first_bytes(check_post, DownloadSDTimeoutError, after_setup=after_setup)
await self._test_time_to_first_bytes(check_post, DownloadMetadataTimeoutError, after_setup=after_setup)

async def test_override_fixed_peer_delay_dht_disabled(self):
self.client_config.fixed_peers = [(self.server_from_client.address, self.server_from_client.tcp_port)]
Expand Down Expand Up @@ -266,7 +266,7 @@ async def test_no_peers_timeout(self):

def check_post(event):
self.assertEqual(event['event'], 'Time To First Bytes')
self.assertEqual(event['properties']['error'], 'DownloadSDTimeoutError')
self.assertEqual(event['properties']['error'], 'DownloadMetadataTimeoutError')
self.assertEqual(event['properties']['tried_peers_count'], 0)
self.assertEqual(event['properties']['active_peer_count'], 0)
self.assertFalse(event['properties']['use_fixed_peers'])
Expand All @@ -277,7 +277,7 @@ def check_post(event):
)

start = self.loop.time()
await self._test_time_to_first_bytes(check_post, DownloadSDTimeoutError)
await self._test_time_to_first_bytes(check_post, DownloadMetadataTimeoutError)
duration = self.loop.time() - start
self.assertLessEqual(duration, 5)
self.assertGreaterEqual(duration, 3.0)
Expand Down Expand Up @@ -387,7 +387,7 @@ async def test_download_sd_timeout(self):
self.server.stop_server()
await self.setup_stream_manager()
await self._test_download_error_analytics_on_start(
DownloadSDTimeoutError, f'Failed to download sd blob {self.sd_hash} within timeout.', timeout=1
DownloadMetadataTimeoutError, f'Failed to download sd blob {self.sd_hash} within timeout.', timeout=1
)

async def test_download_data_timeout(self):
Expand Down