claim_type/stream_type/media_type claim search integration test

This commit is contained in:
Lex Berezhny 2019-06-04 00:10:59 -04:00
parent 9dbf47916b
commit 8d2c9e5785
3 changed files with 150 additions and 58 deletions

View file

@ -1705,6 +1705,7 @@ class Daemon(metaclass=JSONRPCServerType):
[--support_amount=<support_amount>] [--trending_group=<trending_group>] [--support_amount=<support_amount>] [--trending_group=<trending_group>]
[--trending_mixed=<trending_mixed>] [--trending_local=<trending_local>] [--trending_mixed=<trending_mixed>] [--trending_local=<trending_local>]
[--trending_global=<trending_global] [--trending_global=<trending_global]
[--claim_type=<claim_type>] [--stream_types=<stream_types>...] [--media_types=<media_types>...]
[--any_tags=<any_tags>...] [--all_tags=<all_tags>...] [--not_tags=<not_tags>...] [--any_tags=<any_tags>...] [--all_tags=<all_tags>...] [--not_tags=<not_tags>...]
[--any_languages=<any_languages>...] [--all_languages=<all_languages>...] [--any_languages=<any_languages>...] [--all_languages=<all_languages>...]
[--not_languages=<not_languages>...] [--not_languages=<not_languages>...]
@ -1772,6 +1773,9 @@ class Daemon(metaclass=JSONRPCServerType):
--trending_global=<trending_global>: (int) trending value calculated relative to all --trending_global=<trending_global>: (int) trending value calculated relative to all
trending content globally (supports trending content globally (supports
equality constraints) equality constraints)
--claim_type=<claim_type> : (str) filter by 'channel', 'stream' or 'unknown'
--stream_types=<stream_types> : (list) filter by 'video', 'image', 'document', etc
--media_types=<media_types> : (list) filter by 'video/mp4', 'image/png', etc
--any_tags=<any_tags> : (list) find claims containing any of the tags --any_tags=<any_tags> : (list) find claims containing any of the tags
--all_tags=<all_tags> : (list) find claims containing every tag --all_tags=<all_tags> : (list) find claims containing every tag
--not_tags=<not_tags> : (list) find claims not containing any of these tags --not_tags=<not_tags> : (list) find claims not containing any of these tags

View file

@ -10,6 +10,7 @@ from torba.server.util import class_logger
from torba.client.basedatabase import query, constraints_to_sql from torba.client.basedatabase import query, constraints_to_sql
from lbrynet.schema.url import URL, normalize_name from lbrynet.schema.url import URL, normalize_name
from lbrynet.schema.mime_types import guess_stream_type
from lbrynet.wallet.ledger import MainNetLedger, RegTestLedger from lbrynet.wallet.ledger import MainNetLedger, RegTestLedger
from lbrynet.wallet.transaction import Transaction, Output from lbrynet.wallet.transaction import Transaction, Output
from lbrynet.wallet.server.canonical import register_canonical_functions from lbrynet.wallet.server.canonical import register_canonical_functions
@ -19,6 +20,18 @@ from lbrynet.wallet.server.trending import (
ATTRIBUTE_ARRAY_MAX_LENGTH = 100 ATTRIBUTE_ARRAY_MAX_LENGTH = 100
CLAIM_TYPES = {
'stream': 1,
'channel': 2,
}
STREAM_TYPES = {
'video': 1,
'audio': 2,
'image': 3,
'document': 4,
'binary': 5,
'model': 6
}
def _apply_constraints_for_array_attributes(constraints, attr): def _apply_constraints_for_array_attributes(constraints, attr):
@ -87,8 +100,13 @@ class SQLDB:
short_url text not null, -- normalized#shortest-unique-claim_id short_url text not null, -- normalized#shortest-unique-claim_id
canonical_url text, -- channel's-short_url/normalized#shortest-unique-claim_id-within-channel canonical_url text, -- channel's-short_url/normalized#shortest-unique-claim_id-within-channel
claim_type integer,
-- streams
stream_type text,
media_type text,
-- claims which are channels -- claims which are channels
is_channel bool not null,
public_key_bytes bytes, public_key_bytes bytes,
public_key_hash bytes, public_key_hash bytes,
claims_in_channel integer, claims_in_channel integer,
@ -119,6 +137,10 @@ class SQLDB:
create index if not exists claim_activation_height_idx on claim (activation_height); create index if not exists claim_activation_height_idx on claim (activation_height);
create index if not exists claim_public_key_hash_idx on claim (public_key_hash); create index if not exists claim_public_key_hash_idx on claim (public_key_hash);
create index if not exists claim_claim_type_idx on claim (claim_type);
create index if not exists claim_stream_type_idx on claim (stream_type);
create index if not exists claim_media_type_idx on claim (media_type);
create index if not exists claim_effective_amount_idx on claim (effective_amount); create index if not exists claim_effective_amount_idx on claim (effective_amount);
create index if not exists claim_trending_group_idx on claim (trending_group); create index if not exists claim_trending_group_idx on claim (trending_group);
create index if not exists claim_trending_mixed_idx on claim (trending_mixed); create index if not exists claim_trending_mixed_idx on claim (trending_mixed);
@ -242,9 +264,11 @@ class SQLDB:
'txo_hash': sqlite3.Binary(txo.ref.hash), 'txo_hash': sqlite3.Binary(txo.ref.hash),
'tx_position': tx.position, 'tx_position': tx.position,
'amount': txo.amount, 'amount': txo.amount,
'is_channel': False,
'timestamp': header['timestamp'], 'timestamp': header['timestamp'],
'height': tx.height, 'height': tx.height,
'claim_type': None,
'stream_type': None,
'media_type': None,
'release_time': None, 'release_time': None,
} }
claims.append(claim_record) claims.append(claim_record)
@ -256,10 +280,13 @@ class SQLDB:
continue continue
if claim.is_stream: if claim.is_stream:
claim_record['claim_type'] = CLAIM_TYPES['stream']
claim_record['media_type'] = claim.stream.source.media_type
claim_record['stream_type'] = STREAM_TYPES[guess_stream_type(claim_record['media_type'])]
if claim.stream.release_time: if claim.stream.release_time:
claim_record['release_time'] = claim.stream.release_time claim_record['release_time'] = claim.stream.release_time
elif claim.is_channel: elif claim.is_channel:
claim_record['is_channel'] = True claim_record['claim_type'] = CLAIM_TYPES['channel']
for tag in claim.message.tags: for tag in claim.message.tags:
tags.append((tag, claim_hash, tx.height)) tags.append((tag, claim_hash, tx.height))
@ -280,11 +307,11 @@ class SQLDB:
self.db.executemany(""" self.db.executemany("""
INSERT INTO claim ( INSERT INTO claim (
claim_hash, claim_id, claim_name, normalized, txo_hash, tx_position, amount, claim_hash, claim_id, claim_name, normalized, txo_hash, tx_position, amount,
is_channel, timestamp, creation_timestamp, height, creation_height, claim_type, media_type, stream_type, timestamp, creation_timestamp, height,
release_time, activation_height, expiration_height, short_url) creation_height, release_time, activation_height, expiration_height, short_url)
VALUES ( VALUES (
:claim_hash, :claim_id, :claim_name, :normalized, :txo_hash, :tx_position, :amount, :claim_hash, :claim_id, :claim_name, :normalized, :txo_hash, :tx_position, :amount,
:is_channel, :timestamp, :timestamp, :height, :height, :claim_type, :media_type, :stream_type, :timestamp, :timestamp, :height, :height,
CASE WHEN :release_time IS NOT NULL THEN :release_time ELSE :timestamp END, CASE WHEN :release_time IS NOT NULL THEN :release_time ELSE :timestamp END,
CASE WHEN :normalized NOT IN (SELECT normalized FROM claimtrie) THEN :height END, CASE WHEN :normalized NOT IN (SELECT normalized FROM claimtrie) THEN :height END,
CASE WHEN :height >= 262974 THEN :height+2102400 ELSE :height+262974 END, CASE WHEN :height >= 262974 THEN :height+2102400 ELSE :height+262974 END,
@ -299,7 +326,9 @@ class SQLDB:
if claims: if claims:
self.db.executemany(""" self.db.executemany("""
UPDATE claim SET UPDATE claim SET
txo_hash=:txo_hash, tx_position=:tx_position, amount=:amount, height=:height, timestamp=:timestamp, txo_hash=:txo_hash, tx_position=:tx_position, amount=:amount, height=:height,
claim_type=:claim_type, media_type=:media_type, stream_type=:stream_type,
timestamp=:timestamp,
release_time=CASE WHEN :release_time IS NOT NULL THEN :release_time ELSE release_time END release_time=CASE WHEN :release_time IS NOT NULL THEN :release_time ELSE release_time END
WHERE claim_hash=:claim_hash; WHERE claim_hash=:claim_hash;
""", claims) """, claims)
@ -720,6 +749,15 @@ class SQLDB:
tx_hash + struct.pack('<I', nout) tx_hash + struct.pack('<I', nout)
) )
if 'claim_type' in constraints:
constraints['claim.claim_type'] = CLAIM_TYPES[constraints.pop('claim_type')]
if 'stream_types' in constraints:
constraints['claim.stream_type__in'] = [
STREAM_TYPES[stream_type] for stream_type in constraints.pop('stream_types')
]
if 'media_types' in constraints:
constraints['claim.media_type__in'] = constraints.pop('media_types')
_apply_constraints_for_array_attributes(constraints, 'tag') _apply_constraints_for_array_attributes(constraints, 'tag')
_apply_constraints_for_array_attributes(constraints, 'language') _apply_constraints_for_array_attributes(constraints, 'language')
_apply_constraints_for_array_attributes(constraints, 'location') _apply_constraints_for_array_attributes(constraints, 'location')
@ -750,7 +788,7 @@ class SQLDB:
""" """
claimtrie.claim_hash as is_controlling, claimtrie.claim_hash as is_controlling,
claim.claim_hash, claim.txo_hash, claim.claim_hash, claim.txo_hash,
claim.is_channel, claim.claims_in_channel, claim.claims_in_channel,
claim.height, claim.creation_height, claim.height, claim.creation_height,
claim.activation_height, claim.expiration_height, claim.activation_height, claim.expiration_height,
claim.effective_amount, claim.support_amount, claim.effective_amount, claim.support_amount,
@ -773,6 +811,7 @@ class SQLDB:
SEARCH_PARAMS = { SEARCH_PARAMS = {
'name', 'claim_id', 'txid', 'nout', 'channel', 'channel_ids', 'public_key_id', 'name', 'claim_id', 'txid', 'nout', 'channel', 'channel_ids', 'public_key_id',
'claim_type', 'stream_types', 'media_types',
'any_tags', 'all_tags', 'not_tags', 'any_tags', 'all_tags', 'not_tags',
'any_locations', 'all_locations', 'not_locations', 'any_locations', 'all_locations', 'not_locations',
'any_languages', 'all_languages', 'not_languages', 'any_languages', 'all_languages', 'not_languages',

View file

@ -8,12 +8,61 @@ from urllib.request import urlopen
from torba.client.errors import InsufficientFundsError from torba.client.errors import InsufficientFundsError
from lbrynet.testcase import CommandTestCase from lbrynet.testcase import CommandTestCase
from lbrynet.wallet.transaction import Transaction
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class ClaimSearchCommand(CommandTestCase): class ClaimTestCase(CommandTestCase):
files_directory = os.path.join(os.path.dirname(__file__), 'files')
video_file_url = 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4'
video_file_name = os.path.join(files_directory, 'ForBiggerEscapes.mp4')
def setUp(self):
if not os.path.exists(self.video_file_name):
if not os.path.exists(self.files_directory):
os.mkdir(self.files_directory)
log.info(f'downloading test video from {self.video_file_name}')
with urlopen(self.video_file_url) as response, \
open(self.video_file_name, 'wb') as video_file:
video_file.write(response.read())
async def image_stream_create(self, name='blank-image', bid='1.0', confirm=True):
with tempfile.NamedTemporaryFile(suffix='.png') as file:
file.write(unhexlify(
b'89504e470d0a1a0a0000000d49484452000000050000000708020000004fc'
b'510b9000000097048597300000b1300000b1301009a9c1800000015494441'
b'5408d763fcffff3f031260624005d4e603004c45030b5286e9ea000000004'
b'9454e44ae426082'
))
file.flush()
tx = await self.out(
self.daemon.jsonrpc_stream_create(
name, bid, file_path=file.name
)
)
if confirm:
await self.on_transaction_dict(tx)
await self.generate(1)
await self.on_transaction_dict(tx)
return tx
async def video_stream_create(self, name='chrome', bid='1.0', confirm=True):
tx = await self.out(
self.daemon.jsonrpc_stream_create(
name, bid, file_path=self.video_file_name
)
)
if confirm:
await self.on_transaction_dict(tx)
await self.generate(1)
await self.on_transaction_dict(tx)
return tx
class ClaimSearchCommand(ClaimTestCase):
async def create_channel(self): async def create_channel(self):
self.channel = await self.channel_create('@abc', '1.0') self.channel = await self.channel_create('@abc', '1.0')
@ -170,6 +219,37 @@ class ClaimSearchCommand(CommandTestCase):
await self.assertFindsClaims(claims, order_by=["^name"]) await self.assertFindsClaims(claims, order_by=["^name"])
async def test_claim_type_and_media_type_search(self):
# create an invalid/unknown claim
address = await self.account.receiving.get_or_create_usable_address()
tx = await Transaction.claim_create(
'unknown', b'{"sources":{"lbry_sd_hash":""}}', 1, address, [self.account], self.account)
await tx.sign([self.account])
await self.broadcast(tx)
await self.confirm_tx(tx.id)
octet = await self.stream_create()
video = await self.video_stream_create()
image = await self.image_stream_create()
channel = await self.channel_create()
unknown = self.sout(tx)
# claim_type
await self.assertFindsClaims([image, video, octet, unknown], claim_type='stream')
await self.assertFindsClaims([channel], claim_type='channel')
# stream_type
await self.assertFindsClaims([octet, unknown], stream_types=['binary'])
await self.assertFindsClaims([video], stream_types=['video'])
await self.assertFindsClaims([image], stream_types=['image'])
await self.assertFindsClaims([image, video], stream_types=['video', 'image'])
# stream_type
await self.assertFindsClaims([octet, unknown], media_types=['application/octet-stream'])
await self.assertFindsClaims([video], media_types=['video/mp4'])
await self.assertFindsClaims([image], media_types=['image/png'])
await self.assertFindsClaims([image, video], media_types=['video/mp4', 'image/png'])
class ChannelCommands(CommandTestCase): class ChannelCommands(CommandTestCase):
@ -370,20 +450,7 @@ class ChannelCommands(CommandTestCase):
self.assertEqual(channel_private_key.to_string(), channels[0].private_key.to_string()) self.assertEqual(channel_private_key.to_string(), channels[0].private_key.to_string())
class StreamCommands(CommandTestCase): class StreamCommands(ClaimTestCase):
files_directory = os.path.join(os.path.dirname(__file__), 'files')
video_file_url = 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4'
video_file_name = os.path.join(files_directory, 'ForBiggerEscapes.mp4')
def setUp(self):
if not os.path.exists(self.video_file_name):
if not os.path.exists(self.files_directory):
os.mkdir(self.files_directory)
log.info(f'downloading test video from {self.video_file_name}')
with urlopen(self.video_file_url) as response,\
open(self.video_file_name, 'wb') as video_file:
video_file.write(response.read())
async def test_create_stream_names(self): async def test_create_stream_names(self):
# claim new name # claim new name
@ -662,44 +729,26 @@ class StreamCommands(CommandTestCase):
self.assertEqual(len(await self.daemon.jsonrpc_claim_list(account_id=account2_id)), 1) self.assertEqual(len(await self.daemon.jsonrpc_claim_list(account_id=account2_id)), 1)
async def test_automatic_type_and_metadata_detection_for_image(self): async def test_automatic_type_and_metadata_detection_for_image(self):
with tempfile.NamedTemporaryFile(suffix='.png') as file: txo = (await self.image_stream_create())['outputs'][0]
file.write(unhexlify( self.assertEqual(
b'89504e470d0a1a0a0000000d49484452000000050000000708020000004fc' txo['value'], {
b'510b9000000097048597300000b1300000b1301009a9c1800000015494441' 'source': {
b'5408d763fcffff3f031260624005d4e603004c45030b5286e9ea000000004' 'size': '99',
b'9454e44ae426082' 'name': txo['value']['source']['name'],
)) 'media_type': 'image/png',
file.flush() 'hash': '6c7df435d412c603390f593ef658c199817c7830ba3f16b7eadd8f99fa50e85dbd0d2b3dc61eadc33fe096e3872d1545',
tx = await self.out( 'sd_hash': txo['value']['source']['sd_hash'],
self.daemon.jsonrpc_stream_create( },
'blank-image', '1.0', file_path=file.name 'stream_type': 'image',
) 'image': {
) 'width': 5,
txo = tx['outputs'][0] 'height': 7
self.assertEqual(
txo['value'], {
'source': {
'size': '99',
'name': os.path.basename(file.name),
'media_type': 'image/png',
'hash': '6c7df435d412c603390f593ef658c199817c7830ba3f16b7eadd8f99fa50e85dbd0d2b3dc61eadc33fe096e3872d1545',
'sd_hash': txo['value']['source']['sd_hash'],
},
'stream_type': 'image',
'image': {
'width': 5,
'height': 7
}
} }
) }
)
async def test_automatic_type_and_metadata_detection_for_video(self): async def test_automatic_type_and_metadata_detection_for_video(self):
tx = await self.out( txo = (await self.video_stream_create())['outputs'][0]
self.daemon.jsonrpc_stream_create(
'chrome', '1.0', file_path=self.video_file_name
)
)
txo = tx['outputs'][0]
self.assertEqual( self.assertEqual(
txo['value'], { txo['value'], {
'source': { 'source': {