From a3bd335f7f3bdce071c673180a6546d0c5bdb666 Mon Sep 17 00:00:00 2001 From: Lex Berezhny Date: Fri, 29 Mar 2019 20:26:10 -0400 Subject: [PATCH] --languages, --locations fully support all attributes, add BTC --- lbrynet/extras/daemon/Daemon.py | 244 +++++++++++++-- .../extras/daemon/json_response_encoder.py | 11 +- lbrynet/schema/claim.py | 296 +++++++++++++++--- lbrynet/schema/compat.py | 9 +- lbrynet/schema/types/v2/claim_pb2.py | 66 ++-- tests/integration/test_claim_commands.py | 19 +- tests/unit/schema/test_claim_from_bytes.py | 10 +- tests/unit/schema/test_models.py | 100 ++++++ 8 files changed, 644 insertions(+), 111 deletions(-) diff --git a/lbrynet/extras/daemon/Daemon.py b/lbrynet/extras/daemon/Daemon.py index 16a716faf..7746ec786 100644 --- a/lbrynet/extras/daemon/Daemon.py +++ b/lbrynet/extras/daemon/Daemon.py @@ -1757,8 +1757,9 @@ class Daemon(metaclass=JSONRPCServerType): Usage: channel_create ( | --name=) ( | --bid=) - [--tags=...] [--allow_duplicate_name=] - [--title=] [--description=<description>] [--language=<language>] + [--allow_duplicate_name=<allow_duplicate_name>] + [--title=<title>] [--description=<description>] + [--tags=<tags>...] [--languages=<languages>...] [--locations=<locations>...] [--contact_email=<contact_email>] [--homepage_url=<homepage_url>] [--thumbnail_url=<thumbnail_url>] [--cover_url=<cover_url>] [--account_id=<account_id>] [--claim_address=<claim_address>] [--preview] @@ -1768,10 +1769,47 @@ class Daemon(metaclass=JSONRPCServerType): --allow_duplicate_name=<allow_duplicate_name> : (bool) create new channel even if one already exists with given name. default: false. --bid=<bid> : (decimal) amount to back the claim - --tags=<tags> : (list) content tags --title=<title> : (str) title of the publication --description=<description> : (str) description of the publication - --language=<language> : (str) primary language of the channel + --tags=<tags> : (list) content tags + --languages=<languages> : (list) languages used by the channel, + using RFC 5646 format, eg: + for English `--languages=en` + for Spanish (Spain) `--languages=es-ES` + for Spanish (Mexican) `--languages=es-MX` + for Chinese (Simplified) `--languages=zh-Hans` + for Chinese (Traditional) `--languages=zh-Hant` + --locations=<locations> : (list) locations of the channel, consisting of 2 letter + `country` code and a `state`, `city` and a postal + `code` along with a `latitude` and `longitude`. + for JSON RPC: pass a dictionary with aforementioned + attributes as keys, eg: + ... + "locations": [{'country': 'US', 'state': 'NH'}] + ... + for command line: pass a colon delimited list + with values in the following order: + + "COUNTRY:STATE:CITY:CODE:LATITUDE:LONGITUDE" + + making sure to include colon for blank values, for + example to provide only the city: + + --locations"::Manchester" + + with all values set: + + --locations="US:NH:Manchester:03101:42.990605:-71.460989" + + optionally, you can just pass the "LATITUDE:LONGITUDE": + + --locations="42.990605:-71.460989" + + finally, you can also pass JSON string of dictionary + on the command line as you would via JSON RPC + + --locations="{'country': 'US', 'state': 'NH'}" + --contact_email=<contact_email>: (str) email of channel owner --homepage_url=<homepage_url> : (str) homepage url --thumbnail_url=<thumbnail_url>: (str) thumbnail url @@ -1825,19 +1863,61 @@ class Daemon(metaclass=JSONRPCServerType): Usage: channel_update (<claim_id> | --claim_id=<claim_id>) [<bid> | --bid=<bid>] - [--tags=<tags>...] [--clear_tags] [--title=<title>] [--description=<description>] - [--language=<language>] [--contact_email=<contact_email>] + [--title=<title>] [--description=<description>] + [--tags=<tags>...] [--clear_tags] + [--languages=<languages>...] [--clear_languages] + [--locations=<locations>...] [--clear_locations] + [--contact_email=<contact_email>] [--homepage_url=<homepage_url>] [--thumbnail_url=<thumbnail_url>] [--cover_url=<cover_url>] [--account_id=<account_id>] [--claim_address=<claim_address>] [--new_signing_key] [--preview] Options: --claim_id=<claim_id> : (str) claim_id of the channel to update --bid=<bid> : (decimal) amount to back the claim - --tags=<tags> : (list) add content tags - --clear_tags : (bool) clear existing tags (prior to adding new ones) --title=<title> : (str) title of the publication --description=<description> : (str) description of the publication - --language=<language> : (str) primary language of the channel + --clear_tags : (bool) clear existing tags (prior to adding new ones) + --tags=<tags> : (list) add content tags + --clear_languages : (bool) clear existing languages (prior to adding new ones) + --languages=<languages> : (list) languages used by the channel, + using RFC 5646 format, eg: + for English `--languages=en` + for Spanish (Spain) `--languages=es-ES` + for Spanish (Mexican) `--languages=es-MX` + for Chinese (Simplified) `--languages=zh-Hans` + for Chinese (Traditional) `--languages=zh-Hant` + --clear_locations : (bool) clear existing locations (prior to adding new ones) + --locations=<locations> : (list) locations of the channel, consisting of 2 letter + `country` code and a `state`, `city` and a postal + `code` along with a `latitude` and `longitude`. + for JSON RPC: pass a dictionary with aforementioned + attributes as keys, eg: + ... + "locations": [{'country': 'US', 'state': 'NH'}] + ... + for command line: pass a colon delimited list + with values in the following order: + + "COUNTRY:STATE:CITY:CODE:LATITUDE:LONGITUDE" + + making sure to include colon for blank values, for + example to provide only the city: + + --locations"::Manchester" + + with all values set: + + --locations="US:NH:Manchester:03101:42.990605:-71.460989" + + optionally, you can just pass the "LATITUDE:LONGITUDE": + + --locations="42.990605:-71.460989" + + finally, you can also pass JSON string of dictionary + on the command line as you would via JSON RPC + + --locations="{'country': 'US', 'state': 'NH'}" + --contact_email=<contact_email>: (str) email of channel owner --homepage_url=<homepage_url> : (str) homepage url --thumbnail_url=<thumbnail_url>: (str) thumbnail url @@ -1968,7 +2048,10 @@ class Daemon(metaclass=JSONRPCServerType): Usage: publish (<name> | --name=<name>) [--bid=<bid>] [--file_path=<file_path>] - [<stream_type> | --stream_type=<stream_type>] [--tags=<tags>...] + [<stream_type> | --stream_type=<stream_type>] + [--tags=<tags>...] [--clear_tags] + [--languages=<languages>...] [--clear_languages] + [--locations=<locations>...] [--clear_locations] [--fee_currency=<fee_currency>] [--fee_amount=<fee_amount>] [--fee_address=<fee_address>] [--title=<title>] [--description=<description>] [--author=<author>] [--language=<language>] [--license=<license>] [--license_url=<license_url>] [--thumbnail_url=<thumbnail_url>] @@ -1984,7 +2067,6 @@ class Daemon(metaclass=JSONRPCServerType): --bid=<bid> : (decimal) amount to back the claim --file_path=<file_path> : (str) path to file to be associated with name. --stream_type=<stream_type> : (str) type of stream - --tags=<tags> : (list) content tags --fee_currency=<fee_currency> : (string) specify fee currency --fee_amount=<fee_amount> : (decimal) content download fee --fee_address=<fee_address> : (str) address where to send fee payments, will use @@ -1996,7 +2078,48 @@ class Daemon(metaclass=JSONRPCServerType): who is not the publisher and is not represented by the channel. For example, a pdf file of 'The Odyssey' has an author of 'Homer' but may by published to a channel such as '@classics', or to no channel at all - --language=<language> : (str) language of the publication + --clear_tags : (bool) clear existing tags (prior to adding new ones) + --tags=<tags> : (list) add content tags + --clear_languages : (bool) clear existing languages (prior to adding new ones) + --languages=<languages> : (list) languages used by the channel, + using RFC 5646 format, eg: + for English `--languages=en` + for Spanish (Spain) `--languages=es-ES` + for Spanish (Mexican) `--languages=es-MX` + for Chinese (Simplified) `--languages=zh-Hans` + for Chinese (Traditional) `--languages=zh-Hant` + --clear_locations : (bool) clear existing locations (prior to adding new ones) + --locations=<locations> : (list) locations of the channel, consisting of 2 letter + `country` code and a `state`, `city` and a postal + `code` along with a `latitude` and `longitude`. + for JSON RPC: pass a dictionary with aforementioned + attributes as keys, eg: + ... + "locations": [{'country': 'US', 'state': 'NH'}] + ... + for command line: pass a colon delimited list + with values in the following order: + + "COUNTRY:STATE:CITY:CODE:LATITUDE:LONGITUDE" + + making sure to include colon for blank values, for + example to provide only the city: + + --locations"::Manchester" + + with all values set: + + --locations="US:NH:Manchester:03101:42.990605:-71.460989" + + optionally, you can just pass the "LATITUDE:LONGITUDE": + + --locations="42.990605:-71.460989" + + finally, you can also pass JSON string of dictionary + on the command line as you would via JSON RPC + + --locations="{'country': 'US', 'state': 'NH'}" + --license=<license> : (str) publication license --license_url=<license_url> : (str) publication license url --thumbnail_url=<thumbnail_url>: (str) thumbnail url @@ -2049,9 +2172,10 @@ class Daemon(metaclass=JSONRPCServerType): Usage: stream_create (<name> | --name=<name>) (<bid> | --bid=<bid>) (<file_path> | --file_path=<file_path>) [<stream_type> | --stream_type=<stream_type>] - [--tags=<tags>...] [--allow_duplicate_name=<allow_duplicate_name>] + [--allow_duplicate_name=<allow_duplicate_name>] + [--tags=<tags>...] [--languages=<languages>...] [--locations=<locations>...] [--fee_currency=<fee_currency>] [--fee_amount=<fee_amount>] [--fee_address=<fee_address>] - [--title=<title>] [--description=<description>] [--author=<author>] [--language=<language>] + [--title=<title>] [--description=<description>] [--author=<author>] [--license=<license>] [--license_url=<license_url>] [--thumbnail_url=<thumbnail_url>] [--release_time=<release_time>] [--video_width=<video_width>] [--video_height=<video_height>] [--video_duration=<video_duration>] @@ -2067,7 +2191,6 @@ class Daemon(metaclass=JSONRPCServerType): --bid=<bid> : (decimal) amount to back the claim --file_path=<file_path> : (str) path to file to be associated with name. --stream_type=<stream_type> : (str) type of stream - --tags=<tags> : (list) content tags --fee_currency=<fee_currency> : (string) specify fee currency --fee_amount=<fee_amount> : (decimal) content download fee --fee_address=<fee_address> : (str) address where to send fee payments, will use @@ -2079,7 +2202,45 @@ class Daemon(metaclass=JSONRPCServerType): who is not the publisher and is not represented by the channel. For example, a pdf file of 'The Odyssey' has an author of 'Homer' but may by published to a channel such as '@classics', or to no channel at all - --language=<language> : (str) language of the publication + --tags=<tags> : (list) add content tags + --languages=<languages> : (list) languages used by the channel, + using RFC 5646 format, eg: + for English `--languages=en` + for Spanish (Spain) `--languages=es-ES` + for Spanish (Mexican) `--languages=es-MX` + for Chinese (Simplified) `--languages=zh-Hans` + for Chinese (Traditional) `--languages=zh-Hant` + --locations=<locations> : (list) locations of the channel, consisting of 2 letter + `country` code and a `state`, `city` and a postal + `code` along with a `latitude` and `longitude`. + for JSON RPC: pass a dictionary with aforementioned + attributes as keys, eg: + ... + "locations": [{'country': 'US', 'state': 'NH'}] + ... + for command line: pass a colon delimited list + with values in the following order: + + "COUNTRY:STATE:CITY:CODE:LATITUDE:LONGITUDE" + + making sure to include colon for blank values, for + example to provide only the city: + + --locations"::Manchester" + + with all values set: + + --locations="US:NH:Manchester:03101:42.990605:-71.460989" + + optionally, you can just pass the "LATITUDE:LONGITUDE": + + --locations="42.990605:-71.460989" + + finally, you can also pass JSON string of dictionary + on the command line as you would via JSON RPC + + --locations="{'country': 'US', 'state': 'NH'}" + --license=<license> : (str) publication license --license_url=<license_url> : (str) publication license url --thumbnail_url=<thumbnail_url>: (str) thumbnail url @@ -2155,8 +2316,10 @@ class Daemon(metaclass=JSONRPCServerType): Update an existing stream claim and if a new file is provided announce it to lbrynet. Usage: - stream_update (<claim_id> | --claim_id=<claim_id>) - [--bid=<bid>] [--file_path=<file_path>] [--tags=<tags>...] [--clear_tags] + stream_update (<claim_id> | --claim_id=<claim_id>) [--bid=<bid>] [--file_path=<file_path>] + [--tags=<tags>...] [--clear_tags] + [--languages=<languages>...] [--clear_languages] + [--locations=<locations>...] [--clear_locations] [--fee_currency=<fee_currency>] [--fee_amount=<fee_amount>] [--fee_address=<fee_address>] [--title=<title>] [--description=<description>] [--author=<author>] [--language=<language>] [--license=<license>] [--license_url=<license_url>] [--thumbnail_url=<thumbnail_url>] @@ -2171,8 +2334,6 @@ class Daemon(metaclass=JSONRPCServerType): --claim_id=<claim_id> : (str) id of the stream claim to update --bid=<bid> : (decimal) amount to back the claim --file_path=<file_path> : (str) path to file to be associated with name. - --tags=<tags> : (list) content tags - --clear_tags : (bool) clear existing tags (prior to adding new ones) --fee_currency=<fee_currency> : (string) specify fee currency --fee_amount=<fee_amount> : (decimal) content download fee --fee_address=<fee_address> : (str) address where to send fee payments, will use @@ -2184,7 +2345,48 @@ class Daemon(metaclass=JSONRPCServerType): who is not the publisher and is not represented by the channel. For example, a pdf file of 'The Odyssey' has an author of 'Homer' but may by published to a channel such as '@classics', or to no channel at all - --language=<language> : (str) language of the publication + --clear_tags : (bool) clear existing tags (prior to adding new ones) + --tags=<tags> : (list) add content tags + --clear_languages : (bool) clear existing languages (prior to adding new ones) + --languages=<languages> : (list) languages used by the channel, + using RFC 5646 format, eg: + for English `--languages=en` + for Spanish (Spain) `--languages=es-ES` + for Spanish (Mexican) `--languages=es-MX` + for Chinese (Simplified) `--languages=zh-Hans` + for Chinese (Traditional) `--languages=zh-Hant` + --clear_locations : (bool) clear existing locations (prior to adding new ones) + --locations=<locations> : (list) locations of the channel, consisting of 2 letter + `country` code and a `state`, `city` and a postal + `code` along with a `latitude` and `longitude`. + for JSON RPC: pass a dictionary with aforementioned + attributes as keys, eg: + ... + "locations": [{'country': 'US', 'state': 'NH'}] + ... + for command line: pass a colon delimited list + with values in the following order: + + "COUNTRY:STATE:CITY:CODE:LATITUDE:LONGITUDE" + + making sure to include colon for blank values, for + example to provide only the city: + + --locations"::Manchester" + + with all values set: + + --locations="US:NH:Manchester:03101:42.990605:-71.460989" + + optionally, you can just pass the "LATITUDE:LONGITUDE": + + --locations="42.990605:-71.460989" + + finally, you can also pass JSON string of dictionary + on the command line as you would via JSON RPC + + --locations="{'country': 'US', 'state': 'NH'}" + --license=<license> : (str) publication license --license_url=<license_url> : (str) publication license url --thumbnail_url=<thumbnail_url>: (str) thumbnail url diff --git a/lbrynet/extras/daemon/json_response_encoder.py b/lbrynet/extras/daemon/json_response_encoder.py index 6679fb0e8..ee5131379 100644 --- a/lbrynet/extras/daemon/json_response_encoder.py +++ b/lbrynet/extras/daemon/json_response_encoder.py @@ -28,10 +28,17 @@ class JSONResponseEncoder(JSONEncoder): claim_dict = obj.to_dict() if obj.is_stream: claim_dict['stream']['sd_hash'] = obj.stream.sd_hash - if 'fee' in claim_dict['stream'] and 'address' in claim_dict['stream']['fee']: - claim_dict['stream']['fee']['address'] = obj.stream.fee.address + fee = claim_dict['stream'].get('fee', {}) + if 'address' in fee: + fee['address'] = obj.stream.fee.address + if 'amount' in fee: + fee['amount'] = obj.stream.fee.amount + if 'languages' in claim_dict['stream']: + claim_dict['stream']['languages'] = obj.stream.langtags elif obj.is_channel: claim_dict['channel']['public_key'] = obj.channel.public_key + if 'languages' in claim_dict['channel']: + claim_dict['channel']['languages'] = obj.channel.langtags return claim_dict if isinstance(obj, datetime): return obj.strftime("%Y%m%dT%H:%M:%S") diff --git a/lbrynet/schema/claim.py b/lbrynet/schema/claim.py index e515f6f26..58cfec4a7 100644 --- a/lbrynet/schema/claim.py +++ b/lbrynet/schema/claim.py @@ -1,5 +1,7 @@ import os.path -from typing import List, Tuple +import json +from string import ascii_letters +from typing import List, Tuple, Iterator, TypeVar, Generic from decimal import Decimal from binascii import hexlify, unhexlify @@ -87,6 +89,39 @@ class Claim(Signable): return claim +I = TypeVar('I') + + +class BaseMessageList(Generic[I]): + + __slots__ = 'message', + + item_class = None + + def __init__(self, message): + self.message = message + + def add(self) -> I: + return self.item_class(self.message.add()) + + def extend(self, values: List[str]): + for value in values: + self.append(value) + + def append(self, value: str): + raise NotImplemented + + def __len__(self): + return len(self.message) + + def __iter__(self) -> Iterator[I]: + for lang in self.message: + yield self.item_class(lang) + + def __getitem__(self, item) -> I: + return self.item_class(self.message[item]) + + class Dimmensional: __slots__ = () @@ -215,6 +250,8 @@ class Fee: def amount(self) -> Decimal: if self.currency == 'LBC': return self.lbc + if self.currency == 'BTC': + return self.btc if self.currency == 'USD': return self.usd @@ -241,6 +278,29 @@ class Fee: self._fee.amount = amount self._fee.currency = FeeMessage.LBC + SATOSHIES = Decimal(COIN) + + @property + def btc(self) -> Decimal: + if self._fee.currency != FeeMessage.BTC: + raise ValueError('BTC can only be returned for BTC fees.') + return Decimal(self._fee.amount / self.SATOSHIES) + + @btc.setter + def btc(self, amount: Decimal): + self.satoshis = int(amount * self.SATOSHIES) + + @property + def satoshis(self) -> int: + if self._fee.currency != FeeMessage.BTC: + raise ValueError('Satoshies can only be returned for BTC fees.') + return self._fee.amount + + @satoshis.setter + def satoshis(self, amount: int): + self._fee.amount = amount + self._fee.currency = FeeMessage.BTC + PENNIES = Decimal(100.0) @property @@ -265,6 +325,176 @@ class Fee: self._fee.currency = FeeMessage.USD +class Language: + + __slots__ = 'message', + + def __init__(self, message): + self.message = message + + @property + def langtag(self) -> str: + langtag = [] + if self.language: + langtag.append(self.language) + if self.script: + langtag.append(self.script) + if self.region: + langtag.append(self.region) + return '-'.join(langtag) + + @langtag.setter + def langtag(self, langtag: str): + parts = langtag.split('-') + self.language = parts.pop(0) + if parts and len(parts[0]) == 4: + self.script = parts.pop(0) + if parts and len(parts[0]) == 2: + self.region = parts.pop(0) + assert not parts, f"Failed to parse language tag: {langtag}" + + @property + def language(self) -> str: + if self.message.language: + return LanguageMessage.Language.Name(self.message.language) + + @language.setter + def language(self, language: str): + self.message.language = LanguageMessage.Language.Value(language) + + @property + def script(self) -> str: + if self.message.script: + return LanguageMessage.Script.Name(self.message.script) + + @script.setter + def script(self, script: str): + self.message.script = LanguageMessage.Script.Value(script) + + @property + def region(self) -> str: + if self.message.region: + return LocationMessage.Country.Name(self.message.region) + + @region.setter + def region(self, region: str): + self.message.region = LocationMessage.Country.Value(region) + + +class LanguageList(BaseMessageList[Language]): + __slots__ = () + item_class = Language + + def append(self, value: str): + self.add().langtag = value + + +class Location: + + __slots__ = 'message', + + def __init__(self, message): + self.message = message + + def from_value(self, value): + if isinstance(value, str) and value.startswith('{'): + value = json.loads(value) + + if isinstance(value, dict): + for key, val in value.items(): + setattr(self, key, val) + + elif isinstance(value, str): + parts = value.split(':') + if len(parts) > 2 or (parts[0] and parts[0][0] in ascii_letters): + country = parts and parts.pop(0) + if country: + self.country = country + state = parts and parts.pop(0) + if state: + self.state = state + city = parts and parts.pop(0) + if city: + self.city = city + code = parts and parts.pop(0) + if code: + self.code = code + latitude = parts and parts.pop(0) + if latitude: + self.latitude = latitude + longitude = parts and parts.pop(0) + if longitude: + self.longitude = longitude + + else: + raise ValueError(f'Could not parse country value: {value}') + + @property + def country(self) -> str: + if self.message.country: + return LocationMessage.Country.Name(self.message.country) + + @country.setter + def country(self, country: str): + self.message.country = LocationMessage.Country.Value(country) + + @property + def state(self) -> str: + return self.message.state + + @state.setter + def state(self, state: str): + self.message.state = state + + @property + def city(self) -> str: + return self.message.city + + @city.setter + def city(self, city: str): + self.message.city = city + + @property + def code(self) -> str: + return self.message.code + + @code.setter + def code(self, code: str): + self.message.code = code + + GPS_PRECISION = Decimal('10000000') + + @property + def latitude(self) -> str: + if self.message.latitude: + return str(Decimal(self.message.latitude) / self.GPS_PRECISION) + + @latitude.setter + def latitude(self, latitude: str): + latitude = Decimal(latitude) + assert -90 <= latitude <= 90, "Latitude must be between -90 and 90 degrees." + self.message.latitude = int(latitude * self.GPS_PRECISION) + + @property + def longitude(self) -> str: + if self.message.longitude: + return str(Decimal(self.message.longitude) / self.GPS_PRECISION) + + @longitude.setter + def longitude(self, longitude: str): + longitude = Decimal(longitude) + assert -180 <= longitude <= 180, "Longitude must be between -180 and 180 degrees." + self.message.longitude = int(longitude * self.GPS_PRECISION) + + +class LocationList(BaseMessageList[Location]): + __slots__ = () + item_class = Location + + def append(self, value): + self.add().from_value(value) + + class BaseClaimSubType: __slots__ = 'claim', 'message' @@ -272,10 +502,6 @@ class BaseClaimSubType: def __init__(self, claim: Claim): self.claim = claim or Claim() - @property - def tags(self) -> List: - return self.message.tags - @property def title(self) -> str: return self.message.title @@ -301,54 +527,36 @@ class BaseClaimSubType: self.message.thumbnail_url = thumbnail_url @property - def language(self) -> str: - if len(self.languages) > 0: - return LanguageMessage.Language.Name(self.languages[0].language) - - @language.setter - def language(self, language: str): - value = LanguageMessage.Language.Value(language) - if len(self.languages) > 0: - self.languages[0].language = value - else: - self.languages.add().language = value + def tags(self) -> List: + return self.message.tags @property - def languages(self): - return self.message.languages + def languages(self) -> LanguageList: + return LanguageList(self.message.languages) @property - def location_country(self) -> str: - if len(self.locations) > 0: - return LocationMessage.Country.Name(self.locations[0].country) - - @location_country.setter - def location_country(self, country: str): - value = LocationMessage.Country.Value(country) - if len(self.locations) > 0: - self.locations[0].location = value - else: - self.locations.add().location = value + def langtags(self) -> List[str]: + return [l.langtag for l in self.languages] @property - def locations(self): - return self.message.locations + def locations(self) -> LocationList: + return LocationList(self.message.locations) def to_dict(self): return MessageToDict(self.message, preserving_proto_field_name=True) - def update(self, tags=None, clear_tags=False, **kwargs): - - if clear_tags: - self.message.ClearField('tags') - - if tags is not None: - if isinstance(tags, str): - self.tags.append(tags) - elif isinstance(tags, list): - self.tags.extend(tags) - else: - raise ValueError(f"Unknown tag type: {tags}") + def update(self, **kwargs): + for l in ('tags', 'languages', 'locations'): + if kwargs.pop(f'clear_{l}', False): + self.message.ClearField('tags') + items = kwargs.pop(l, None) + if items is not None: + if isinstance(items, str): + getattr(self, l).append(items) + elif isinstance(items, list): + getattr(self, l).extend(items) + else: + raise ValueError(f"Unknown {l} value: {items}") for key, value in kwargs.items(): setattr(self, key, value) @@ -454,6 +662,8 @@ class Stream(BaseClaimSubType): self.fee.address = fee_address if fee_currency.lower() == 'lbc': self.fee.lbc = Decimal(fee_amount) + elif fee_currency.lower() == 'btc': + self.fee.btc = Decimal(fee_amount) elif fee_currency.lower() == 'usd': self.fee.usd = Decimal(fee_amount) else: diff --git a/lbrynet/schema/compat.py b/lbrynet/schema/compat.py index 934cbb98b..71bc949ba 100644 --- a/lbrynet/schema/compat.py +++ b/lbrynet/schema/compat.py @@ -2,7 +2,6 @@ import json from decimal import Decimal from lbrynet.schema.types.v1.legacy_claim_pb2 import Claim as OldClaimMessage -from lbrynet.schema.types.v1.metadata_pb2 import Metadata as MetadataMessage from lbrynet.schema.types.v1.certificate_pb2 import KeyType from lbrynet.schema.types.v1.fee_pb2 import Fee as FeeMessage @@ -22,7 +21,7 @@ def from_old_json_schema(claim, payload: bytes): if language.lower() == 'english': language = 'en' try: - stream.language = language + stream.languages.append(language) except: pass stream.sd_hash = value['sources']['lbry_sd_hash'] @@ -35,6 +34,8 @@ def from_old_json_schema(claim, payload: bytes): stream.fee.lbc = Decimal(fee[currency]['amount']) elif currency == 'USD': stream.fee.usd = Decimal(fee[currency]['amount']) + elif currency == 'BTC': + stream.fee.btc = Decimal(fee[currency]['amount']) else: raise ValueError(f'Unknown currency: {currency}') stream.fee.address = fee[currency]['address'] @@ -53,7 +54,7 @@ def from_types_v1(claim, payload: bytes): stream.license_url = old.stream.metadata.licenseUrl stream.thumbnail_url = old.stream.metadata.thumbnail if old.stream.metadata.HasField('language'): - stream.languages.add().language = old.stream.metadata.language + stream.languages.add().message.language = old.stream.metadata.language stream.media_type = old.stream.source.contentType stream.sd_hash_bytes = old.stream.source.source if old.stream.metadata.nsfw: @@ -66,6 +67,8 @@ def from_types_v1(claim, payload: bytes): stream.fee.lbc = Decimal(fee.amount) elif currency == 'USD': stream.fee.usd = Decimal(fee.amount) + elif currency == 'BTC': + stream.fee.btc = Decimal(fee.amount) else: raise ValueError(f'Unsupported currency: {currency}') if old.HasField('publisherSignature'): diff --git a/lbrynet/schema/types/v2/claim_pb2.py b/lbrynet/schema/types/v2/claim_pb2.py index 6af06b0ca..d19f3421f 100644 --- a/lbrynet/schema/types/v2/claim_pb2.py +++ b/lbrynet/schema/types/v2/claim_pb2.py @@ -19,7 +19,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( package='pb', syntax='proto3', serialized_options=None, - serialized_pb=_b('\n\x0b\x63laim.proto\x12\x02pb\"M\n\x05\x43laim\x12\x1c\n\x06stream\x18\x01 \x01(\x0b\x32\n.pb.StreamH\x00\x12\x1e\n\x07\x63hannel\x18\x02 \x01(\x0b\x32\x0b.pb.ChannelH\x00\x42\x06\n\x04type\"\xe8\x01\n\x07\x43hannel\x12\x12\n\npublic_key\x18\x01 \x01(\x0c\x12\r\n\x05title\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x15\n\rthumbnail_url\x18\x04 \x01(\t\x12\x0c\n\x04tags\x18\x05 \x03(\t\x12\x1f\n\tlanguages\x18\x06 \x03(\x0b\x32\x0c.pb.Language\x12\x1f\n\tlocations\x18\x07 \x03(\x0b\x32\x0c.pb.Location\x12\x15\n\rcontact_email\x18\x08 \x01(\t\x12\x14\n\x0chomepage_url\x18\t \x01(\t\x12\x11\n\tcover_url\x18\n \x01(\t\"\x8e\x03\n\x06Stream\x12\x0f\n\x07sd_hash\x18\x01 \x01(\x0c\x12\r\n\x05title\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x15\n\rthumbnail_url\x18\x04 \x01(\t\x12\x0c\n\x04tags\x18\x05 \x03(\t\x12\x1f\n\tlanguages\x18\x06 \x03(\x0b\x32\x0c.pb.Language\x12\x1f\n\tlocations\x18\x07 \x03(\x0b\x32\x0c.pb.Location\x12\x0e\n\x06\x61uthor\x18\x08 \x01(\t\x12\x0f\n\x07license\x18\t \x01(\t\x12\x13\n\x0blicense_url\x18\n \x01(\t\x12\x14\n\x0crelease_time\x18\x0b \x01(\x03\x12\x12\n\nmedia_type\x18\r \x01(\t\x12\x16\n\x04\x66ile\x18\x0e \x01(\x0b\x32\x08.pb.File\x12\x14\n\x03\x66\x65\x65\x18\x0f \x01(\x0b\x32\x07.pb.Fee\x12\x1a\n\x05image\x18\x10 \x01(\x0b\x32\t.pb.ImageH\x00\x12\x1a\n\x05video\x18\x11 \x01(\x0b\x32\t.pb.VideoH\x00\x12\x1a\n\x05\x61udio\x18\x12 \x01(\x0b\x32\t.pb.AudioH\x00\x42\x06\n\x04type\"h\n\x03\x46\x65\x65\x12\"\n\x08\x63urrency\x18\x01 \x01(\x0e\x32\x10.pb.Fee.Currency\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\x0c\x12\x0e\n\x06\x61mount\x18\x03 \x01(\x04\"\x1c\n\x08\x43urrency\x12\x07\n\x03LBC\x10\x00\x12\x07\n\x03USD\x10\x02\"0\n\x04\x46ile\x12\x0c\n\x04hash\x18\x03 \x01(\x0c\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04size\x18\x02 \x01(\x04\"&\n\x05Image\x12\r\n\x05width\x18\x01 \x01(\r\x12\x0e\n\x06height\x18\x02 \x01(\r\"8\n\x05Video\x12\r\n\x05width\x18\x01 \x01(\r\x12\x0e\n\x06height\x18\x02 \x01(\r\x12\x10\n\x08\x64uration\x18\x03 \x01(\r\"\x19\n\x05\x41udio\x12\x10\n\x08\x64uration\x18\x01 \x01(\r\"\xc7\x1d\n\x08Language\x12\'\n\x08language\x18\x01 \x01(\x0e\x32\x15.pb.Language.Language\x12#\n\x06script\x18\x02 \x01(\x0e\x32\x13.pb.Language.Script\x12$\n\x06region\x18\x03 \x01(\x0e\x32\x14.pb.Location.Country\"\x99\x0c\n\x08Language\x12\x14\n\x10UNKNOWN_LANGUAGE\x10\x00\x12\x06\n\x02\x65n\x10\x01\x12\x06\n\x02\x61\x61\x10\x02\x12\x06\n\x02\x61\x62\x10\x03\x12\x06\n\x02\x61\x65\x10\x04\x12\x06\n\x02\x61\x66\x10\x05\x12\x06\n\x02\x61k\x10\x06\x12\x06\n\x02\x61m\x10\x07\x12\x06\n\x02\x61n\x10\x08\x12\x06\n\x02\x61r\x10\t\x12\x06\n\x02\x61s\x10\n\x12\x06\n\x02\x61v\x10\x0b\x12\x06\n\x02\x61y\x10\x0c\x12\x06\n\x02\x61z\x10\r\x12\x06\n\x02\x62\x61\x10\x0e\x12\x06\n\x02\x62\x65\x10\x0f\x12\x06\n\x02\x62g\x10\x10\x12\x06\n\x02\x62h\x10\x11\x12\x06\n\x02\x62i\x10\x12\x12\x06\n\x02\x62m\x10\x13\x12\x06\n\x02\x62n\x10\x14\x12\x06\n\x02\x62o\x10\x15\x12\x06\n\x02\x62r\x10\x16\x12\x06\n\x02\x62s\x10\x17\x12\x06\n\x02\x63\x61\x10\x18\x12\x06\n\x02\x63\x65\x10\x19\x12\x06\n\x02\x63h\x10\x1a\x12\x06\n\x02\x63o\x10\x1b\x12\x06\n\x02\x63r\x10\x1c\x12\x06\n\x02\x63s\x10\x1d\x12\x06\n\x02\x63u\x10\x1e\x12\x06\n\x02\x63v\x10\x1f\x12\x06\n\x02\x63y\x10 \x12\x06\n\x02\x64\x61\x10!\x12\x06\n\x02\x64\x65\x10\"\x12\x06\n\x02\x64v\x10#\x12\x06\n\x02\x64z\x10$\x12\x06\n\x02\x65\x65\x10%\x12\x06\n\x02\x65l\x10&\x12\x06\n\x02\x65o\x10\'\x12\x06\n\x02\x65s\x10(\x12\x06\n\x02\x65t\x10)\x12\x06\n\x02\x65u\x10*\x12\x06\n\x02\x66\x61\x10+\x12\x06\n\x02\x66\x66\x10,\x12\x06\n\x02\x66i\x10-\x12\x06\n\x02\x66j\x10.\x12\x06\n\x02\x66o\x10/\x12\x06\n\x02\x66r\x10\x30\x12\x06\n\x02\x66y\x10\x31\x12\x06\n\x02ga\x10\x32\x12\x06\n\x02gd\x10\x33\x12\x06\n\x02gl\x10\x34\x12\x06\n\x02gn\x10\x35\x12\x06\n\x02gu\x10\x36\x12\x06\n\x02gv\x10\x37\x12\x06\n\x02ha\x10\x38\x12\x06\n\x02he\x10\x39\x12\x06\n\x02hi\x10:\x12\x06\n\x02ho\x10;\x12\x06\n\x02hr\x10<\x12\x06\n\x02ht\x10=\x12\x06\n\x02hu\x10>\x12\x06\n\x02hy\x10?\x12\x06\n\x02hz\x10@\x12\x06\n\x02ia\x10\x41\x12\x06\n\x02id\x10\x42\x12\x06\n\x02ie\x10\x43\x12\x06\n\x02ig\x10\x44\x12\x06\n\x02ii\x10\x45\x12\x06\n\x02ik\x10\x46\x12\x06\n\x02io\x10G\x12\x06\n\x02is\x10H\x12\x06\n\x02it\x10I\x12\x06\n\x02iu\x10J\x12\x06\n\x02ja\x10K\x12\x06\n\x02jv\x10L\x12\x06\n\x02ka\x10M\x12\x06\n\x02kg\x10N\x12\x06\n\x02ki\x10O\x12\x06\n\x02kj\x10P\x12\x06\n\x02kk\x10Q\x12\x06\n\x02kl\x10R\x12\x06\n\x02km\x10S\x12\x06\n\x02kn\x10T\x12\x06\n\x02ko\x10U\x12\x06\n\x02kr\x10V\x12\x06\n\x02ks\x10W\x12\x06\n\x02ku\x10X\x12\x06\n\x02kv\x10Y\x12\x06\n\x02kw\x10Z\x12\x06\n\x02ky\x10[\x12\x06\n\x02la\x10\\\x12\x06\n\x02lb\x10]\x12\x06\n\x02lg\x10^\x12\x06\n\x02li\x10_\x12\x06\n\x02ln\x10`\x12\x06\n\x02lo\x10\x61\x12\x06\n\x02lt\x10\x62\x12\x06\n\x02lu\x10\x63\x12\x06\n\x02lv\x10\x64\x12\x06\n\x02mg\x10\x65\x12\x06\n\x02mh\x10\x66\x12\x06\n\x02mi\x10g\x12\x06\n\x02mk\x10h\x12\x06\n\x02ml\x10i\x12\x06\n\x02mn\x10j\x12\x06\n\x02mr\x10k\x12\x06\n\x02ms\x10l\x12\x06\n\x02mt\x10m\x12\x06\n\x02my\x10n\x12\x06\n\x02na\x10o\x12\x06\n\x02nb\x10p\x12\x06\n\x02nd\x10q\x12\x06\n\x02ne\x10r\x12\x06\n\x02ng\x10s\x12\x06\n\x02nl\x10t\x12\x06\n\x02nn\x10u\x12\x06\n\x02no\x10v\x12\x06\n\x02nr\x10w\x12\x06\n\x02nv\x10x\x12\x06\n\x02ny\x10y\x12\x06\n\x02oc\x10z\x12\x06\n\x02oj\x10{\x12\x06\n\x02om\x10|\x12\x06\n\x02or\x10}\x12\x06\n\x02os\x10~\x12\x06\n\x02pa\x10\x7f\x12\x07\n\x02pi\x10\x80\x01\x12\x07\n\x02pl\x10\x81\x01\x12\x07\n\x02ps\x10\x82\x01\x12\x07\n\x02pt\x10\x83\x01\x12\x07\n\x02qu\x10\x84\x01\x12\x07\n\x02rm\x10\x85\x01\x12\x07\n\x02rn\x10\x86\x01\x12\x07\n\x02ro\x10\x87\x01\x12\x07\n\x02ru\x10\x88\x01\x12\x07\n\x02rw\x10\x89\x01\x12\x07\n\x02sa\x10\x8a\x01\x12\x07\n\x02sc\x10\x8b\x01\x12\x07\n\x02sd\x10\x8c\x01\x12\x07\n\x02se\x10\x8d\x01\x12\x07\n\x02sg\x10\x8e\x01\x12\x07\n\x02si\x10\x8f\x01\x12\x07\n\x02sk\x10\x90\x01\x12\x07\n\x02sl\x10\x91\x01\x12\x07\n\x02sm\x10\x92\x01\x12\x07\n\x02sn\x10\x93\x01\x12\x07\n\x02so\x10\x94\x01\x12\x07\n\x02sq\x10\x95\x01\x12\x07\n\x02sr\x10\x96\x01\x12\x07\n\x02ss\x10\x97\x01\x12\x07\n\x02st\x10\x98\x01\x12\x07\n\x02su\x10\x99\x01\x12\x07\n\x02sv\x10\x9a\x01\x12\x07\n\x02sw\x10\x9b\x01\x12\x07\n\x02ta\x10\x9c\x01\x12\x07\n\x02te\x10\x9d\x01\x12\x07\n\x02tg\x10\x9e\x01\x12\x07\n\x02th\x10\x9f\x01\x12\x07\n\x02ti\x10\xa0\x01\x12\x07\n\x02tk\x10\xa1\x01\x12\x07\n\x02tl\x10\xa2\x01\x12\x07\n\x02tn\x10\xa3\x01\x12\x07\n\x02to\x10\xa4\x01\x12\x07\n\x02tr\x10\xa5\x01\x12\x07\n\x02ts\x10\xa6\x01\x12\x07\n\x02tt\x10\xa7\x01\x12\x07\n\x02tw\x10\xa8\x01\x12\x07\n\x02ty\x10\xa9\x01\x12\x07\n\x02ug\x10\xaa\x01\x12\x07\n\x02uk\x10\xab\x01\x12\x07\n\x02ur\x10\xac\x01\x12\x07\n\x02uz\x10\xad\x01\x12\x07\n\x02ve\x10\xae\x01\x12\x07\n\x02vi\x10\xaf\x01\x12\x07\n\x02vo\x10\xb0\x01\x12\x07\n\x02wa\x10\xb1\x01\x12\x07\n\x02wo\x10\xb2\x01\x12\x07\n\x02xh\x10\xb3\x01\x12\x07\n\x02yi\x10\xb4\x01\x12\x07\n\x02yo\x10\xb5\x01\x12\x07\n\x02za\x10\xb6\x01\x12\x07\n\x02zh\x10\xb7\x01\x12\x07\n\x02zu\x10\xb8\x01\"\xaa\x10\n\x06Script\x12\x12\n\x0eUNKNOWN_SCRIPT\x10\x00\x12\x08\n\x04\x41\x64lm\x10\x01\x12\x08\n\x04\x41\x66\x61k\x10\x02\x12\x08\n\x04\x41ghb\x10\x03\x12\x08\n\x04\x41hom\x10\x04\x12\x08\n\x04\x41rab\x10\x05\x12\x08\n\x04\x41ran\x10\x06\x12\x08\n\x04\x41rmi\x10\x07\x12\x08\n\x04\x41rmn\x10\x08\x12\x08\n\x04\x41vst\x10\t\x12\x08\n\x04\x42\x61li\x10\n\x12\x08\n\x04\x42\x61mu\x10\x0b\x12\x08\n\x04\x42\x61ss\x10\x0c\x12\x08\n\x04\x42\x61tk\x10\r\x12\x08\n\x04\x42\x65ng\x10\x0e\x12\x08\n\x04\x42hks\x10\x0f\x12\x08\n\x04\x42lis\x10\x10\x12\x08\n\x04\x42opo\x10\x11\x12\x08\n\x04\x42rah\x10\x12\x12\x08\n\x04\x42rai\x10\x13\x12\x08\n\x04\x42ugi\x10\x14\x12\x08\n\x04\x42uhd\x10\x15\x12\x08\n\x04\x43\x61km\x10\x16\x12\x08\n\x04\x43\x61ns\x10\x17\x12\x08\n\x04\x43\x61ri\x10\x18\x12\x08\n\x04\x43ham\x10\x19\x12\x08\n\x04\x43her\x10\x1a\x12\x08\n\x04\x43irt\x10\x1b\x12\x08\n\x04\x43opt\x10\x1c\x12\x08\n\x04\x43pmn\x10\x1d\x12\x08\n\x04\x43prt\x10\x1e\x12\x08\n\x04\x43yrl\x10\x1f\x12\x08\n\x04\x43yrs\x10 \x12\x08\n\x04\x44\x65va\x10!\x12\x08\n\x04\x44ogr\x10\"\x12\x08\n\x04\x44srt\x10#\x12\x08\n\x04\x44upl\x10$\x12\x08\n\x04\x45gyd\x10%\x12\x08\n\x04\x45gyh\x10&\x12\x08\n\x04\x45gyp\x10\'\x12\x08\n\x04\x45lba\x10(\x12\x08\n\x04\x45lym\x10)\x12\x08\n\x04\x45thi\x10*\x12\x08\n\x04Geok\x10+\x12\x08\n\x04Geor\x10,\x12\x08\n\x04Glag\x10-\x12\x08\n\x04Gong\x10.\x12\x08\n\x04Gonm\x10/\x12\x08\n\x04Goth\x10\x30\x12\x08\n\x04Gran\x10\x31\x12\x08\n\x04Grek\x10\x32\x12\x08\n\x04Gujr\x10\x33\x12\x08\n\x04Guru\x10\x34\x12\x08\n\x04Hanb\x10\x35\x12\x08\n\x04Hang\x10\x36\x12\x08\n\x04Hani\x10\x37\x12\x08\n\x04Hano\x10\x38\x12\x08\n\x04Hans\x10\x39\x12\x08\n\x04Hant\x10:\x12\x08\n\x04Hatr\x10;\x12\x08\n\x04Hebr\x10<\x12\x08\n\x04Hira\x10=\x12\x08\n\x04Hluw\x10>\x12\x08\n\x04Hmng\x10?\x12\x08\n\x04Hmnp\x10@\x12\x08\n\x04Hrkt\x10\x41\x12\x08\n\x04Hung\x10\x42\x12\x08\n\x04Inds\x10\x43\x12\x08\n\x04Ital\x10\x44\x12\x08\n\x04Jamo\x10\x45\x12\x08\n\x04Java\x10\x46\x12\x08\n\x04Jpan\x10G\x12\x08\n\x04Jurc\x10H\x12\x08\n\x04Kali\x10I\x12\x08\n\x04Kana\x10J\x12\x08\n\x04Khar\x10K\x12\x08\n\x04Khmr\x10L\x12\x08\n\x04Khoj\x10M\x12\x08\n\x04Kitl\x10N\x12\x08\n\x04Kits\x10O\x12\x08\n\x04Knda\x10P\x12\x08\n\x04Kore\x10Q\x12\x08\n\x04Kpel\x10R\x12\x08\n\x04Kthi\x10S\x12\x08\n\x04Lana\x10T\x12\x08\n\x04Laoo\x10U\x12\x08\n\x04Latf\x10V\x12\x08\n\x04Latg\x10W\x12\x08\n\x04Latn\x10X\x12\x08\n\x04Leke\x10Y\x12\x08\n\x04Lepc\x10Z\x12\x08\n\x04Limb\x10[\x12\x08\n\x04Lina\x10\\\x12\x08\n\x04Linb\x10]\x12\x08\n\x04Lisu\x10^\x12\x08\n\x04Loma\x10_\x12\x08\n\x04Lyci\x10`\x12\x08\n\x04Lydi\x10\x61\x12\x08\n\x04Mahj\x10\x62\x12\x08\n\x04Maka\x10\x63\x12\x08\n\x04Mand\x10\x64\x12\x08\n\x04Mani\x10\x65\x12\x08\n\x04Marc\x10\x66\x12\x08\n\x04Maya\x10g\x12\x08\n\x04Medf\x10h\x12\x08\n\x04Mend\x10i\x12\x08\n\x04Merc\x10j\x12\x08\n\x04Mero\x10k\x12\x08\n\x04Mlym\x10l\x12\x08\n\x04Modi\x10m\x12\x08\n\x04Mong\x10n\x12\x08\n\x04Moon\x10o\x12\x08\n\x04Mroo\x10p\x12\x08\n\x04Mtei\x10q\x12\x08\n\x04Mult\x10r\x12\x08\n\x04Mymr\x10s\x12\x08\n\x04Nand\x10t\x12\x08\n\x04Narb\x10u\x12\x08\n\x04Nbat\x10v\x12\x08\n\x04Newa\x10w\x12\x08\n\x04Nkdb\x10x\x12\x08\n\x04Nkgb\x10y\x12\x08\n\x04Nkoo\x10z\x12\x08\n\x04Nshu\x10{\x12\x08\n\x04Ogam\x10|\x12\x08\n\x04Olck\x10}\x12\x08\n\x04Orkh\x10~\x12\x08\n\x04Orya\x10\x7f\x12\t\n\x04Osge\x10\x80\x01\x12\t\n\x04Osma\x10\x81\x01\x12\t\n\x04Palm\x10\x82\x01\x12\t\n\x04Pauc\x10\x83\x01\x12\t\n\x04Perm\x10\x84\x01\x12\t\n\x04Phag\x10\x85\x01\x12\t\n\x04Phli\x10\x86\x01\x12\t\n\x04Phlp\x10\x87\x01\x12\t\n\x04Phlv\x10\x88\x01\x12\t\n\x04Phnx\x10\x89\x01\x12\t\n\x04Plrd\x10\x8a\x01\x12\t\n\x04Piqd\x10\x8b\x01\x12\t\n\x04Prti\x10\x8c\x01\x12\t\n\x04Qaaa\x10\x8d\x01\x12\t\n\x04Qabx\x10\x8e\x01\x12\t\n\x04Rjng\x10\x8f\x01\x12\t\n\x04Rohg\x10\x90\x01\x12\t\n\x04Roro\x10\x91\x01\x12\t\n\x04Runr\x10\x92\x01\x12\t\n\x04Samr\x10\x93\x01\x12\t\n\x04Sara\x10\x94\x01\x12\t\n\x04Sarb\x10\x95\x01\x12\t\n\x04Saur\x10\x96\x01\x12\t\n\x04Sgnw\x10\x97\x01\x12\t\n\x04Shaw\x10\x98\x01\x12\t\n\x04Shrd\x10\x99\x01\x12\t\n\x04Shui\x10\x9a\x01\x12\t\n\x04Sidd\x10\x9b\x01\x12\t\n\x04Sind\x10\x9c\x01\x12\t\n\x04Sinh\x10\x9d\x01\x12\t\n\x04Sogd\x10\x9e\x01\x12\t\n\x04Sogo\x10\x9f\x01\x12\t\n\x04Sora\x10\xa0\x01\x12\t\n\x04Soyo\x10\xa1\x01\x12\t\n\x04Sund\x10\xa2\x01\x12\t\n\x04Sylo\x10\xa3\x01\x12\t\n\x04Syrc\x10\xa4\x01\x12\t\n\x04Syre\x10\xa5\x01\x12\t\n\x04Syrj\x10\xa6\x01\x12\t\n\x04Syrn\x10\xa7\x01\x12\t\n\x04Tagb\x10\xa8\x01\x12\t\n\x04Takr\x10\xa9\x01\x12\t\n\x04Tale\x10\xaa\x01\x12\t\n\x04Talu\x10\xab\x01\x12\t\n\x04Taml\x10\xac\x01\x12\t\n\x04Tang\x10\xad\x01\x12\t\n\x04Tavt\x10\xae\x01\x12\t\n\x04Telu\x10\xaf\x01\x12\t\n\x04Teng\x10\xb0\x01\x12\t\n\x04Tfng\x10\xb1\x01\x12\t\n\x04Tglg\x10\xb2\x01\x12\t\n\x04Thaa\x10\xb3\x01\x12\t\n\x04Thai\x10\xb4\x01\x12\t\n\x04Tibt\x10\xb5\x01\x12\t\n\x04Tirh\x10\xb6\x01\x12\t\n\x04Ugar\x10\xb7\x01\x12\t\n\x04Vaii\x10\xb8\x01\x12\t\n\x04Visp\x10\xb9\x01\x12\t\n\x04Wara\x10\xba\x01\x12\t\n\x04Wcho\x10\xbb\x01\x12\t\n\x04Wole\x10\xbc\x01\x12\t\n\x04Xpeo\x10\xbd\x01\x12\t\n\x04Xsux\x10\xbe\x01\x12\t\n\x04Yiii\x10\xbf\x01\x12\t\n\x04Zanb\x10\xc0\x01\x12\t\n\x04Zinh\x10\xc1\x01\x12\t\n\x04Zmth\x10\xc2\x01\x12\t\n\x04Zsye\x10\xc3\x01\x12\t\n\x04Zsym\x10\xc4\x01\x12\t\n\x04Zxxx\x10\xc5\x01\x12\t\n\x04Zyyy\x10\xc6\x01\x12\t\n\x04Zzzz\x10\xc7\x01\"\xe4\x11\n\x08Location\x12%\n\x07\x63ountry\x18\x01 \x01(\x0e\x32\x14.pb.Location.Country\x12\r\n\x05state\x18\x02 \x01(\t\x12\x0c\n\x04\x63ity\x18\x03 \x01(\t\x12\x0c\n\x04\x63ode\x18\x04 \x01(\t\x12\x10\n\x08latitude\x18\x05 \x01(\x01\x12\x11\n\tlongitude\x18\x06 \x01(\x01\"\xe0\x10\n\x07\x43ountry\x12\x13\n\x0fUNKNOWN_COUNTRY\x10\x00\x12\x06\n\x02\x41\x46\x10\x01\x12\x06\n\x02\x41X\x10\x02\x12\x06\n\x02\x41L\x10\x03\x12\x06\n\x02\x44Z\x10\x04\x12\x06\n\x02\x41S\x10\x05\x12\x06\n\x02\x41\x44\x10\x06\x12\x06\n\x02\x41O\x10\x07\x12\x06\n\x02\x41I\x10\x08\x12\x06\n\x02\x41Q\x10\t\x12\x06\n\x02\x41G\x10\n\x12\x06\n\x02\x41R\x10\x0b\x12\x06\n\x02\x41M\x10\x0c\x12\x06\n\x02\x41W\x10\r\x12\x06\n\x02\x41U\x10\x0e\x12\x06\n\x02\x41T\x10\x0f\x12\x06\n\x02\x41Z\x10\x10\x12\x06\n\x02\x42S\x10\x11\x12\x06\n\x02\x42H\x10\x12\x12\x06\n\x02\x42\x44\x10\x13\x12\x06\n\x02\x42\x42\x10\x14\x12\x06\n\x02\x42Y\x10\x15\x12\x06\n\x02\x42\x45\x10\x16\x12\x06\n\x02\x42Z\x10\x17\x12\x06\n\x02\x42J\x10\x18\x12\x06\n\x02\x42M\x10\x19\x12\x06\n\x02\x42T\x10\x1a\x12\x06\n\x02\x42O\x10\x1b\x12\x06\n\x02\x42Q\x10\x1c\x12\x06\n\x02\x42\x41\x10\x1d\x12\x06\n\x02\x42W\x10\x1e\x12\x06\n\x02\x42V\x10\x1f\x12\x06\n\x02\x42R\x10 \x12\x06\n\x02IO\x10!\x12\x06\n\x02\x42N\x10\"\x12\x06\n\x02\x42G\x10#\x12\x06\n\x02\x42\x46\x10$\x12\x06\n\x02\x42I\x10%\x12\x06\n\x02KH\x10&\x12\x06\n\x02\x43M\x10\'\x12\x06\n\x02\x43\x41\x10(\x12\x06\n\x02\x43V\x10)\x12\x06\n\x02KY\x10*\x12\x06\n\x02\x43\x46\x10+\x12\x06\n\x02TD\x10,\x12\x06\n\x02\x43L\x10-\x12\x06\n\x02\x43N\x10.\x12\x06\n\x02\x43X\x10/\x12\x06\n\x02\x43\x43\x10\x30\x12\x06\n\x02\x43O\x10\x31\x12\x06\n\x02KM\x10\x32\x12\x06\n\x02\x43G\x10\x33\x12\x06\n\x02\x43\x44\x10\x34\x12\x06\n\x02\x43K\x10\x35\x12\x06\n\x02\x43R\x10\x36\x12\x06\n\x02\x43I\x10\x37\x12\x06\n\x02HR\x10\x38\x12\x06\n\x02\x43U\x10\x39\x12\x06\n\x02\x43W\x10:\x12\x06\n\x02\x43Y\x10;\x12\x06\n\x02\x43Z\x10<\x12\x06\n\x02\x44K\x10=\x12\x06\n\x02\x44J\x10>\x12\x06\n\x02\x44M\x10?\x12\x06\n\x02\x44O\x10@\x12\x06\n\x02\x45\x43\x10\x41\x12\x06\n\x02\x45G\x10\x42\x12\x06\n\x02SV\x10\x43\x12\x06\n\x02GQ\x10\x44\x12\x06\n\x02\x45R\x10\x45\x12\x06\n\x02\x45\x45\x10\x46\x12\x06\n\x02\x45T\x10G\x12\x06\n\x02\x46K\x10H\x12\x06\n\x02\x46O\x10I\x12\x06\n\x02\x46J\x10J\x12\x06\n\x02\x46I\x10K\x12\x06\n\x02\x46R\x10L\x12\x06\n\x02GF\x10M\x12\x06\n\x02PF\x10N\x12\x06\n\x02TF\x10O\x12\x06\n\x02GA\x10P\x12\x06\n\x02GM\x10Q\x12\x06\n\x02GE\x10R\x12\x06\n\x02\x44\x45\x10S\x12\x06\n\x02GH\x10T\x12\x06\n\x02GI\x10U\x12\x06\n\x02GR\x10V\x12\x06\n\x02GL\x10W\x12\x06\n\x02GD\x10X\x12\x06\n\x02GP\x10Y\x12\x06\n\x02GU\x10Z\x12\x06\n\x02GT\x10[\x12\x06\n\x02GG\x10\\\x12\x06\n\x02GN\x10]\x12\x06\n\x02GW\x10^\x12\x06\n\x02GY\x10_\x12\x06\n\x02HT\x10`\x12\x06\n\x02HM\x10\x61\x12\x06\n\x02VA\x10\x62\x12\x06\n\x02HN\x10\x63\x12\x06\n\x02HK\x10\x64\x12\x06\n\x02HU\x10\x65\x12\x06\n\x02IS\x10\x66\x12\x06\n\x02IN\x10g\x12\x06\n\x02ID\x10h\x12\x06\n\x02IR\x10i\x12\x06\n\x02IQ\x10j\x12\x06\n\x02IE\x10k\x12\x06\n\x02IM\x10l\x12\x06\n\x02IL\x10m\x12\x06\n\x02IT\x10n\x12\x06\n\x02JM\x10o\x12\x06\n\x02JP\x10p\x12\x06\n\x02JE\x10q\x12\x06\n\x02JO\x10r\x12\x06\n\x02KZ\x10s\x12\x06\n\x02KE\x10t\x12\x06\n\x02KI\x10u\x12\x06\n\x02KP\x10v\x12\x06\n\x02KR\x10w\x12\x06\n\x02KW\x10x\x12\x06\n\x02KG\x10y\x12\x06\n\x02LA\x10z\x12\x06\n\x02LV\x10{\x12\x06\n\x02LB\x10|\x12\x06\n\x02LS\x10}\x12\x06\n\x02LR\x10~\x12\x06\n\x02LY\x10\x7f\x12\x07\n\x02LI\x10\x80\x01\x12\x07\n\x02LT\x10\x81\x01\x12\x07\n\x02LU\x10\x82\x01\x12\x07\n\x02MO\x10\x83\x01\x12\x07\n\x02MK\x10\x84\x01\x12\x07\n\x02MG\x10\x85\x01\x12\x07\n\x02MW\x10\x86\x01\x12\x07\n\x02MY\x10\x87\x01\x12\x07\n\x02MV\x10\x88\x01\x12\x07\n\x02ML\x10\x89\x01\x12\x07\n\x02MT\x10\x8a\x01\x12\x07\n\x02MH\x10\x8b\x01\x12\x07\n\x02MQ\x10\x8c\x01\x12\x07\n\x02MR\x10\x8d\x01\x12\x07\n\x02MU\x10\x8e\x01\x12\x07\n\x02YT\x10\x8f\x01\x12\x07\n\x02MX\x10\x90\x01\x12\x07\n\x02\x46M\x10\x91\x01\x12\x07\n\x02MD\x10\x92\x01\x12\x07\n\x02MC\x10\x93\x01\x12\x07\n\x02MN\x10\x94\x01\x12\x07\n\x02ME\x10\x95\x01\x12\x07\n\x02MS\x10\x96\x01\x12\x07\n\x02MA\x10\x97\x01\x12\x07\n\x02MZ\x10\x98\x01\x12\x07\n\x02MM\x10\x99\x01\x12\x07\n\x02NA\x10\x9a\x01\x12\x07\n\x02NR\x10\x9b\x01\x12\x07\n\x02NP\x10\x9c\x01\x12\x07\n\x02NL\x10\x9d\x01\x12\x07\n\x02NC\x10\x9e\x01\x12\x07\n\x02NZ\x10\x9f\x01\x12\x07\n\x02NI\x10\xa0\x01\x12\x07\n\x02NE\x10\xa1\x01\x12\x07\n\x02NG\x10\xa2\x01\x12\x07\n\x02NU\x10\xa3\x01\x12\x07\n\x02NF\x10\xa4\x01\x12\x07\n\x02MP\x10\xa5\x01\x12\x07\n\x02NO\x10\xa6\x01\x12\x07\n\x02OM\x10\xa7\x01\x12\x07\n\x02PK\x10\xa8\x01\x12\x07\n\x02PW\x10\xa9\x01\x12\x07\n\x02PS\x10\xaa\x01\x12\x07\n\x02PA\x10\xab\x01\x12\x07\n\x02PG\x10\xac\x01\x12\x07\n\x02PY\x10\xad\x01\x12\x07\n\x02PE\x10\xae\x01\x12\x07\n\x02PH\x10\xaf\x01\x12\x07\n\x02PN\x10\xb0\x01\x12\x07\n\x02PL\x10\xb1\x01\x12\x07\n\x02PT\x10\xb2\x01\x12\x07\n\x02PR\x10\xb3\x01\x12\x07\n\x02QA\x10\xb4\x01\x12\x07\n\x02RE\x10\xb5\x01\x12\x07\n\x02RO\x10\xb6\x01\x12\x07\n\x02RU\x10\xb7\x01\x12\x07\n\x02RW\x10\xb8\x01\x12\x07\n\x02\x42L\x10\xb9\x01\x12\x07\n\x02SH\x10\xba\x01\x12\x07\n\x02KN\x10\xbb\x01\x12\x07\n\x02LC\x10\xbc\x01\x12\x07\n\x02MF\x10\xbd\x01\x12\x07\n\x02PM\x10\xbe\x01\x12\x07\n\x02VC\x10\xbf\x01\x12\x07\n\x02WS\x10\xc0\x01\x12\x07\n\x02SM\x10\xc1\x01\x12\x07\n\x02ST\x10\xc2\x01\x12\x07\n\x02SA\x10\xc3\x01\x12\x07\n\x02SN\x10\xc4\x01\x12\x07\n\x02RS\x10\xc5\x01\x12\x07\n\x02SC\x10\xc6\x01\x12\x07\n\x02SL\x10\xc7\x01\x12\x07\n\x02SG\x10\xc8\x01\x12\x07\n\x02SX\x10\xc9\x01\x12\x07\n\x02SK\x10\xca\x01\x12\x07\n\x02SI\x10\xcb\x01\x12\x07\n\x02SB\x10\xcc\x01\x12\x07\n\x02SO\x10\xcd\x01\x12\x07\n\x02ZA\x10\xce\x01\x12\x07\n\x02GS\x10\xcf\x01\x12\x07\n\x02SS\x10\xd0\x01\x12\x07\n\x02\x45S\x10\xd1\x01\x12\x07\n\x02LK\x10\xd2\x01\x12\x07\n\x02SD\x10\xd3\x01\x12\x07\n\x02SR\x10\xd4\x01\x12\x07\n\x02SJ\x10\xd5\x01\x12\x07\n\x02SZ\x10\xd6\x01\x12\x07\n\x02SE\x10\xd7\x01\x12\x07\n\x02\x43H\x10\xd8\x01\x12\x07\n\x02SY\x10\xd9\x01\x12\x07\n\x02TW\x10\xda\x01\x12\x07\n\x02TJ\x10\xdb\x01\x12\x07\n\x02TZ\x10\xdc\x01\x12\x07\n\x02TH\x10\xdd\x01\x12\x07\n\x02TL\x10\xde\x01\x12\x07\n\x02TG\x10\xdf\x01\x12\x07\n\x02TK\x10\xe0\x01\x12\x07\n\x02TO\x10\xe1\x01\x12\x07\n\x02TT\x10\xe2\x01\x12\x07\n\x02TN\x10\xe3\x01\x12\x07\n\x02TR\x10\xe4\x01\x12\x07\n\x02TM\x10\xe5\x01\x12\x07\n\x02TC\x10\xe6\x01\x12\x07\n\x02TV\x10\xe7\x01\x12\x07\n\x02UG\x10\xe8\x01\x12\x07\n\x02UA\x10\xe9\x01\x12\x07\n\x02\x41\x45\x10\xea\x01\x12\x07\n\x02GB\x10\xeb\x01\x12\x07\n\x02US\x10\xec\x01\x12\x07\n\x02UM\x10\xed\x01\x12\x07\n\x02UY\x10\xee\x01\x12\x07\n\x02UZ\x10\xef\x01\x12\x07\n\x02VU\x10\xf0\x01\x12\x07\n\x02VE\x10\xf1\x01\x12\x07\n\x02VN\x10\xf2\x01\x12\x07\n\x02VG\x10\xf3\x01\x12\x07\n\x02VI\x10\xf4\x01\x12\x07\n\x02WF\x10\xf5\x01\x12\x07\n\x02\x45H\x10\xf6\x01\x12\x07\n\x02YE\x10\xf7\x01\x12\x07\n\x02ZM\x10\xf8\x01\x12\x07\n\x02ZW\x10\xf9\x01\x62\x06proto3') + serialized_pb=_b('\n\x0b\x63laim.proto\x12\x02pb\"M\n\x05\x43laim\x12\x1c\n\x06stream\x18\x01 \x01(\x0b\x32\n.pb.StreamH\x00\x12\x1e\n\x07\x63hannel\x18\x02 \x01(\x0b\x32\x0b.pb.ChannelH\x00\x42\x06\n\x04type\"\xe8\x01\n\x07\x43hannel\x12\x12\n\npublic_key\x18\x01 \x01(\x0c\x12\r\n\x05title\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x15\n\rthumbnail_url\x18\x04 \x01(\t\x12\x0c\n\x04tags\x18\x05 \x03(\t\x12\x1f\n\tlanguages\x18\x06 \x03(\x0b\x32\x0c.pb.Language\x12\x1f\n\tlocations\x18\x07 \x03(\x0b\x32\x0c.pb.Location\x12\x15\n\rcontact_email\x18\x08 \x01(\t\x12\x14\n\x0chomepage_url\x18\t \x01(\t\x12\x11\n\tcover_url\x18\n \x01(\t\"\x8e\x03\n\x06Stream\x12\x0f\n\x07sd_hash\x18\x01 \x01(\x0c\x12\r\n\x05title\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x15\n\rthumbnail_url\x18\x04 \x01(\t\x12\x0c\n\x04tags\x18\x05 \x03(\t\x12\x1f\n\tlanguages\x18\x06 \x03(\x0b\x32\x0c.pb.Language\x12\x1f\n\tlocations\x18\x07 \x03(\x0b\x32\x0c.pb.Location\x12\x0e\n\x06\x61uthor\x18\x08 \x01(\t\x12\x0f\n\x07license\x18\t \x01(\t\x12\x13\n\x0blicense_url\x18\n \x01(\t\x12\x14\n\x0crelease_time\x18\x0b \x01(\x03\x12\x12\n\nmedia_type\x18\r \x01(\t\x12\x16\n\x04\x66ile\x18\x0e \x01(\x0b\x32\x08.pb.File\x12\x14\n\x03\x66\x65\x65\x18\x0f \x01(\x0b\x32\x07.pb.Fee\x12\x1a\n\x05image\x18\x10 \x01(\x0b\x32\t.pb.ImageH\x00\x12\x1a\n\x05video\x18\x11 \x01(\x0b\x32\t.pb.VideoH\x00\x12\x1a\n\x05\x61udio\x18\x12 \x01(\x0b\x32\t.pb.AudioH\x00\x42\x06\n\x04type\"\x87\x01\n\x03\x46\x65\x65\x12\"\n\x08\x63urrency\x18\x01 \x01(\x0e\x32\x10.pb.Fee.Currency\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\x0c\x12\x0e\n\x06\x61mount\x18\x03 \x01(\x04\";\n\x08\x43urrency\x12\x14\n\x10UNKNOWN_CURRENCY\x10\x00\x12\x07\n\x03LBC\x10\x01\x12\x07\n\x03\x42TC\x10\x02\x12\x07\n\x03USD\x10\x03\"0\n\x04\x46ile\x12\x0c\n\x04hash\x18\x03 \x01(\x0c\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04size\x18\x02 \x01(\x04\"&\n\x05Image\x12\r\n\x05width\x18\x01 \x01(\r\x12\x0e\n\x06height\x18\x02 \x01(\r\"8\n\x05Video\x12\r\n\x05width\x18\x01 \x01(\r\x12\x0e\n\x06height\x18\x02 \x01(\r\x12\x10\n\x08\x64uration\x18\x03 \x01(\r\"\x19\n\x05\x41udio\x12\x10\n\x08\x64uration\x18\x01 \x01(\r\"\xc7\x1d\n\x08Language\x12\'\n\x08language\x18\x01 \x01(\x0e\x32\x15.pb.Language.Language\x12#\n\x06script\x18\x02 \x01(\x0e\x32\x13.pb.Language.Script\x12$\n\x06region\x18\x03 \x01(\x0e\x32\x14.pb.Location.Country\"\x99\x0c\n\x08Language\x12\x14\n\x10UNKNOWN_LANGUAGE\x10\x00\x12\x06\n\x02\x65n\x10\x01\x12\x06\n\x02\x61\x61\x10\x02\x12\x06\n\x02\x61\x62\x10\x03\x12\x06\n\x02\x61\x65\x10\x04\x12\x06\n\x02\x61\x66\x10\x05\x12\x06\n\x02\x61k\x10\x06\x12\x06\n\x02\x61m\x10\x07\x12\x06\n\x02\x61n\x10\x08\x12\x06\n\x02\x61r\x10\t\x12\x06\n\x02\x61s\x10\n\x12\x06\n\x02\x61v\x10\x0b\x12\x06\n\x02\x61y\x10\x0c\x12\x06\n\x02\x61z\x10\r\x12\x06\n\x02\x62\x61\x10\x0e\x12\x06\n\x02\x62\x65\x10\x0f\x12\x06\n\x02\x62g\x10\x10\x12\x06\n\x02\x62h\x10\x11\x12\x06\n\x02\x62i\x10\x12\x12\x06\n\x02\x62m\x10\x13\x12\x06\n\x02\x62n\x10\x14\x12\x06\n\x02\x62o\x10\x15\x12\x06\n\x02\x62r\x10\x16\x12\x06\n\x02\x62s\x10\x17\x12\x06\n\x02\x63\x61\x10\x18\x12\x06\n\x02\x63\x65\x10\x19\x12\x06\n\x02\x63h\x10\x1a\x12\x06\n\x02\x63o\x10\x1b\x12\x06\n\x02\x63r\x10\x1c\x12\x06\n\x02\x63s\x10\x1d\x12\x06\n\x02\x63u\x10\x1e\x12\x06\n\x02\x63v\x10\x1f\x12\x06\n\x02\x63y\x10 \x12\x06\n\x02\x64\x61\x10!\x12\x06\n\x02\x64\x65\x10\"\x12\x06\n\x02\x64v\x10#\x12\x06\n\x02\x64z\x10$\x12\x06\n\x02\x65\x65\x10%\x12\x06\n\x02\x65l\x10&\x12\x06\n\x02\x65o\x10\'\x12\x06\n\x02\x65s\x10(\x12\x06\n\x02\x65t\x10)\x12\x06\n\x02\x65u\x10*\x12\x06\n\x02\x66\x61\x10+\x12\x06\n\x02\x66\x66\x10,\x12\x06\n\x02\x66i\x10-\x12\x06\n\x02\x66j\x10.\x12\x06\n\x02\x66o\x10/\x12\x06\n\x02\x66r\x10\x30\x12\x06\n\x02\x66y\x10\x31\x12\x06\n\x02ga\x10\x32\x12\x06\n\x02gd\x10\x33\x12\x06\n\x02gl\x10\x34\x12\x06\n\x02gn\x10\x35\x12\x06\n\x02gu\x10\x36\x12\x06\n\x02gv\x10\x37\x12\x06\n\x02ha\x10\x38\x12\x06\n\x02he\x10\x39\x12\x06\n\x02hi\x10:\x12\x06\n\x02ho\x10;\x12\x06\n\x02hr\x10<\x12\x06\n\x02ht\x10=\x12\x06\n\x02hu\x10>\x12\x06\n\x02hy\x10?\x12\x06\n\x02hz\x10@\x12\x06\n\x02ia\x10\x41\x12\x06\n\x02id\x10\x42\x12\x06\n\x02ie\x10\x43\x12\x06\n\x02ig\x10\x44\x12\x06\n\x02ii\x10\x45\x12\x06\n\x02ik\x10\x46\x12\x06\n\x02io\x10G\x12\x06\n\x02is\x10H\x12\x06\n\x02it\x10I\x12\x06\n\x02iu\x10J\x12\x06\n\x02ja\x10K\x12\x06\n\x02jv\x10L\x12\x06\n\x02ka\x10M\x12\x06\n\x02kg\x10N\x12\x06\n\x02ki\x10O\x12\x06\n\x02kj\x10P\x12\x06\n\x02kk\x10Q\x12\x06\n\x02kl\x10R\x12\x06\n\x02km\x10S\x12\x06\n\x02kn\x10T\x12\x06\n\x02ko\x10U\x12\x06\n\x02kr\x10V\x12\x06\n\x02ks\x10W\x12\x06\n\x02ku\x10X\x12\x06\n\x02kv\x10Y\x12\x06\n\x02kw\x10Z\x12\x06\n\x02ky\x10[\x12\x06\n\x02la\x10\\\x12\x06\n\x02lb\x10]\x12\x06\n\x02lg\x10^\x12\x06\n\x02li\x10_\x12\x06\n\x02ln\x10`\x12\x06\n\x02lo\x10\x61\x12\x06\n\x02lt\x10\x62\x12\x06\n\x02lu\x10\x63\x12\x06\n\x02lv\x10\x64\x12\x06\n\x02mg\x10\x65\x12\x06\n\x02mh\x10\x66\x12\x06\n\x02mi\x10g\x12\x06\n\x02mk\x10h\x12\x06\n\x02ml\x10i\x12\x06\n\x02mn\x10j\x12\x06\n\x02mr\x10k\x12\x06\n\x02ms\x10l\x12\x06\n\x02mt\x10m\x12\x06\n\x02my\x10n\x12\x06\n\x02na\x10o\x12\x06\n\x02nb\x10p\x12\x06\n\x02nd\x10q\x12\x06\n\x02ne\x10r\x12\x06\n\x02ng\x10s\x12\x06\n\x02nl\x10t\x12\x06\n\x02nn\x10u\x12\x06\n\x02no\x10v\x12\x06\n\x02nr\x10w\x12\x06\n\x02nv\x10x\x12\x06\n\x02ny\x10y\x12\x06\n\x02oc\x10z\x12\x06\n\x02oj\x10{\x12\x06\n\x02om\x10|\x12\x06\n\x02or\x10}\x12\x06\n\x02os\x10~\x12\x06\n\x02pa\x10\x7f\x12\x07\n\x02pi\x10\x80\x01\x12\x07\n\x02pl\x10\x81\x01\x12\x07\n\x02ps\x10\x82\x01\x12\x07\n\x02pt\x10\x83\x01\x12\x07\n\x02qu\x10\x84\x01\x12\x07\n\x02rm\x10\x85\x01\x12\x07\n\x02rn\x10\x86\x01\x12\x07\n\x02ro\x10\x87\x01\x12\x07\n\x02ru\x10\x88\x01\x12\x07\n\x02rw\x10\x89\x01\x12\x07\n\x02sa\x10\x8a\x01\x12\x07\n\x02sc\x10\x8b\x01\x12\x07\n\x02sd\x10\x8c\x01\x12\x07\n\x02se\x10\x8d\x01\x12\x07\n\x02sg\x10\x8e\x01\x12\x07\n\x02si\x10\x8f\x01\x12\x07\n\x02sk\x10\x90\x01\x12\x07\n\x02sl\x10\x91\x01\x12\x07\n\x02sm\x10\x92\x01\x12\x07\n\x02sn\x10\x93\x01\x12\x07\n\x02so\x10\x94\x01\x12\x07\n\x02sq\x10\x95\x01\x12\x07\n\x02sr\x10\x96\x01\x12\x07\n\x02ss\x10\x97\x01\x12\x07\n\x02st\x10\x98\x01\x12\x07\n\x02su\x10\x99\x01\x12\x07\n\x02sv\x10\x9a\x01\x12\x07\n\x02sw\x10\x9b\x01\x12\x07\n\x02ta\x10\x9c\x01\x12\x07\n\x02te\x10\x9d\x01\x12\x07\n\x02tg\x10\x9e\x01\x12\x07\n\x02th\x10\x9f\x01\x12\x07\n\x02ti\x10\xa0\x01\x12\x07\n\x02tk\x10\xa1\x01\x12\x07\n\x02tl\x10\xa2\x01\x12\x07\n\x02tn\x10\xa3\x01\x12\x07\n\x02to\x10\xa4\x01\x12\x07\n\x02tr\x10\xa5\x01\x12\x07\n\x02ts\x10\xa6\x01\x12\x07\n\x02tt\x10\xa7\x01\x12\x07\n\x02tw\x10\xa8\x01\x12\x07\n\x02ty\x10\xa9\x01\x12\x07\n\x02ug\x10\xaa\x01\x12\x07\n\x02uk\x10\xab\x01\x12\x07\n\x02ur\x10\xac\x01\x12\x07\n\x02uz\x10\xad\x01\x12\x07\n\x02ve\x10\xae\x01\x12\x07\n\x02vi\x10\xaf\x01\x12\x07\n\x02vo\x10\xb0\x01\x12\x07\n\x02wa\x10\xb1\x01\x12\x07\n\x02wo\x10\xb2\x01\x12\x07\n\x02xh\x10\xb3\x01\x12\x07\n\x02yi\x10\xb4\x01\x12\x07\n\x02yo\x10\xb5\x01\x12\x07\n\x02za\x10\xb6\x01\x12\x07\n\x02zh\x10\xb7\x01\x12\x07\n\x02zu\x10\xb8\x01\"\xaa\x10\n\x06Script\x12\x12\n\x0eUNKNOWN_SCRIPT\x10\x00\x12\x08\n\x04\x41\x64lm\x10\x01\x12\x08\n\x04\x41\x66\x61k\x10\x02\x12\x08\n\x04\x41ghb\x10\x03\x12\x08\n\x04\x41hom\x10\x04\x12\x08\n\x04\x41rab\x10\x05\x12\x08\n\x04\x41ran\x10\x06\x12\x08\n\x04\x41rmi\x10\x07\x12\x08\n\x04\x41rmn\x10\x08\x12\x08\n\x04\x41vst\x10\t\x12\x08\n\x04\x42\x61li\x10\n\x12\x08\n\x04\x42\x61mu\x10\x0b\x12\x08\n\x04\x42\x61ss\x10\x0c\x12\x08\n\x04\x42\x61tk\x10\r\x12\x08\n\x04\x42\x65ng\x10\x0e\x12\x08\n\x04\x42hks\x10\x0f\x12\x08\n\x04\x42lis\x10\x10\x12\x08\n\x04\x42opo\x10\x11\x12\x08\n\x04\x42rah\x10\x12\x12\x08\n\x04\x42rai\x10\x13\x12\x08\n\x04\x42ugi\x10\x14\x12\x08\n\x04\x42uhd\x10\x15\x12\x08\n\x04\x43\x61km\x10\x16\x12\x08\n\x04\x43\x61ns\x10\x17\x12\x08\n\x04\x43\x61ri\x10\x18\x12\x08\n\x04\x43ham\x10\x19\x12\x08\n\x04\x43her\x10\x1a\x12\x08\n\x04\x43irt\x10\x1b\x12\x08\n\x04\x43opt\x10\x1c\x12\x08\n\x04\x43pmn\x10\x1d\x12\x08\n\x04\x43prt\x10\x1e\x12\x08\n\x04\x43yrl\x10\x1f\x12\x08\n\x04\x43yrs\x10 \x12\x08\n\x04\x44\x65va\x10!\x12\x08\n\x04\x44ogr\x10\"\x12\x08\n\x04\x44srt\x10#\x12\x08\n\x04\x44upl\x10$\x12\x08\n\x04\x45gyd\x10%\x12\x08\n\x04\x45gyh\x10&\x12\x08\n\x04\x45gyp\x10\'\x12\x08\n\x04\x45lba\x10(\x12\x08\n\x04\x45lym\x10)\x12\x08\n\x04\x45thi\x10*\x12\x08\n\x04Geok\x10+\x12\x08\n\x04Geor\x10,\x12\x08\n\x04Glag\x10-\x12\x08\n\x04Gong\x10.\x12\x08\n\x04Gonm\x10/\x12\x08\n\x04Goth\x10\x30\x12\x08\n\x04Gran\x10\x31\x12\x08\n\x04Grek\x10\x32\x12\x08\n\x04Gujr\x10\x33\x12\x08\n\x04Guru\x10\x34\x12\x08\n\x04Hanb\x10\x35\x12\x08\n\x04Hang\x10\x36\x12\x08\n\x04Hani\x10\x37\x12\x08\n\x04Hano\x10\x38\x12\x08\n\x04Hans\x10\x39\x12\x08\n\x04Hant\x10:\x12\x08\n\x04Hatr\x10;\x12\x08\n\x04Hebr\x10<\x12\x08\n\x04Hira\x10=\x12\x08\n\x04Hluw\x10>\x12\x08\n\x04Hmng\x10?\x12\x08\n\x04Hmnp\x10@\x12\x08\n\x04Hrkt\x10\x41\x12\x08\n\x04Hung\x10\x42\x12\x08\n\x04Inds\x10\x43\x12\x08\n\x04Ital\x10\x44\x12\x08\n\x04Jamo\x10\x45\x12\x08\n\x04Java\x10\x46\x12\x08\n\x04Jpan\x10G\x12\x08\n\x04Jurc\x10H\x12\x08\n\x04Kali\x10I\x12\x08\n\x04Kana\x10J\x12\x08\n\x04Khar\x10K\x12\x08\n\x04Khmr\x10L\x12\x08\n\x04Khoj\x10M\x12\x08\n\x04Kitl\x10N\x12\x08\n\x04Kits\x10O\x12\x08\n\x04Knda\x10P\x12\x08\n\x04Kore\x10Q\x12\x08\n\x04Kpel\x10R\x12\x08\n\x04Kthi\x10S\x12\x08\n\x04Lana\x10T\x12\x08\n\x04Laoo\x10U\x12\x08\n\x04Latf\x10V\x12\x08\n\x04Latg\x10W\x12\x08\n\x04Latn\x10X\x12\x08\n\x04Leke\x10Y\x12\x08\n\x04Lepc\x10Z\x12\x08\n\x04Limb\x10[\x12\x08\n\x04Lina\x10\\\x12\x08\n\x04Linb\x10]\x12\x08\n\x04Lisu\x10^\x12\x08\n\x04Loma\x10_\x12\x08\n\x04Lyci\x10`\x12\x08\n\x04Lydi\x10\x61\x12\x08\n\x04Mahj\x10\x62\x12\x08\n\x04Maka\x10\x63\x12\x08\n\x04Mand\x10\x64\x12\x08\n\x04Mani\x10\x65\x12\x08\n\x04Marc\x10\x66\x12\x08\n\x04Maya\x10g\x12\x08\n\x04Medf\x10h\x12\x08\n\x04Mend\x10i\x12\x08\n\x04Merc\x10j\x12\x08\n\x04Mero\x10k\x12\x08\n\x04Mlym\x10l\x12\x08\n\x04Modi\x10m\x12\x08\n\x04Mong\x10n\x12\x08\n\x04Moon\x10o\x12\x08\n\x04Mroo\x10p\x12\x08\n\x04Mtei\x10q\x12\x08\n\x04Mult\x10r\x12\x08\n\x04Mymr\x10s\x12\x08\n\x04Nand\x10t\x12\x08\n\x04Narb\x10u\x12\x08\n\x04Nbat\x10v\x12\x08\n\x04Newa\x10w\x12\x08\n\x04Nkdb\x10x\x12\x08\n\x04Nkgb\x10y\x12\x08\n\x04Nkoo\x10z\x12\x08\n\x04Nshu\x10{\x12\x08\n\x04Ogam\x10|\x12\x08\n\x04Olck\x10}\x12\x08\n\x04Orkh\x10~\x12\x08\n\x04Orya\x10\x7f\x12\t\n\x04Osge\x10\x80\x01\x12\t\n\x04Osma\x10\x81\x01\x12\t\n\x04Palm\x10\x82\x01\x12\t\n\x04Pauc\x10\x83\x01\x12\t\n\x04Perm\x10\x84\x01\x12\t\n\x04Phag\x10\x85\x01\x12\t\n\x04Phli\x10\x86\x01\x12\t\n\x04Phlp\x10\x87\x01\x12\t\n\x04Phlv\x10\x88\x01\x12\t\n\x04Phnx\x10\x89\x01\x12\t\n\x04Plrd\x10\x8a\x01\x12\t\n\x04Piqd\x10\x8b\x01\x12\t\n\x04Prti\x10\x8c\x01\x12\t\n\x04Qaaa\x10\x8d\x01\x12\t\n\x04Qabx\x10\x8e\x01\x12\t\n\x04Rjng\x10\x8f\x01\x12\t\n\x04Rohg\x10\x90\x01\x12\t\n\x04Roro\x10\x91\x01\x12\t\n\x04Runr\x10\x92\x01\x12\t\n\x04Samr\x10\x93\x01\x12\t\n\x04Sara\x10\x94\x01\x12\t\n\x04Sarb\x10\x95\x01\x12\t\n\x04Saur\x10\x96\x01\x12\t\n\x04Sgnw\x10\x97\x01\x12\t\n\x04Shaw\x10\x98\x01\x12\t\n\x04Shrd\x10\x99\x01\x12\t\n\x04Shui\x10\x9a\x01\x12\t\n\x04Sidd\x10\x9b\x01\x12\t\n\x04Sind\x10\x9c\x01\x12\t\n\x04Sinh\x10\x9d\x01\x12\t\n\x04Sogd\x10\x9e\x01\x12\t\n\x04Sogo\x10\x9f\x01\x12\t\n\x04Sora\x10\xa0\x01\x12\t\n\x04Soyo\x10\xa1\x01\x12\t\n\x04Sund\x10\xa2\x01\x12\t\n\x04Sylo\x10\xa3\x01\x12\t\n\x04Syrc\x10\xa4\x01\x12\t\n\x04Syre\x10\xa5\x01\x12\t\n\x04Syrj\x10\xa6\x01\x12\t\n\x04Syrn\x10\xa7\x01\x12\t\n\x04Tagb\x10\xa8\x01\x12\t\n\x04Takr\x10\xa9\x01\x12\t\n\x04Tale\x10\xaa\x01\x12\t\n\x04Talu\x10\xab\x01\x12\t\n\x04Taml\x10\xac\x01\x12\t\n\x04Tang\x10\xad\x01\x12\t\n\x04Tavt\x10\xae\x01\x12\t\n\x04Telu\x10\xaf\x01\x12\t\n\x04Teng\x10\xb0\x01\x12\t\n\x04Tfng\x10\xb1\x01\x12\t\n\x04Tglg\x10\xb2\x01\x12\t\n\x04Thaa\x10\xb3\x01\x12\t\n\x04Thai\x10\xb4\x01\x12\t\n\x04Tibt\x10\xb5\x01\x12\t\n\x04Tirh\x10\xb6\x01\x12\t\n\x04Ugar\x10\xb7\x01\x12\t\n\x04Vaii\x10\xb8\x01\x12\t\n\x04Visp\x10\xb9\x01\x12\t\n\x04Wara\x10\xba\x01\x12\t\n\x04Wcho\x10\xbb\x01\x12\t\n\x04Wole\x10\xbc\x01\x12\t\n\x04Xpeo\x10\xbd\x01\x12\t\n\x04Xsux\x10\xbe\x01\x12\t\n\x04Yiii\x10\xbf\x01\x12\t\n\x04Zanb\x10\xc0\x01\x12\t\n\x04Zinh\x10\xc1\x01\x12\t\n\x04Zmth\x10\xc2\x01\x12\t\n\x04Zsye\x10\xc3\x01\x12\t\n\x04Zsym\x10\xc4\x01\x12\t\n\x04Zxxx\x10\xc5\x01\x12\t\n\x04Zyyy\x10\xc6\x01\x12\t\n\x04Zzzz\x10\xc7\x01\"\xe4\x11\n\x08Location\x12%\n\x07\x63ountry\x18\x01 \x01(\x0e\x32\x14.pb.Location.Country\x12\r\n\x05state\x18\x02 \x01(\t\x12\x0c\n\x04\x63ity\x18\x03 \x01(\t\x12\x0c\n\x04\x63ode\x18\x04 \x01(\t\x12\x10\n\x08latitude\x18\x05 \x01(\x11\x12\x11\n\tlongitude\x18\x06 \x01(\x11\"\xe0\x10\n\x07\x43ountry\x12\x13\n\x0fUNKNOWN_COUNTRY\x10\x00\x12\x06\n\x02\x41\x46\x10\x01\x12\x06\n\x02\x41X\x10\x02\x12\x06\n\x02\x41L\x10\x03\x12\x06\n\x02\x44Z\x10\x04\x12\x06\n\x02\x41S\x10\x05\x12\x06\n\x02\x41\x44\x10\x06\x12\x06\n\x02\x41O\x10\x07\x12\x06\n\x02\x41I\x10\x08\x12\x06\n\x02\x41Q\x10\t\x12\x06\n\x02\x41G\x10\n\x12\x06\n\x02\x41R\x10\x0b\x12\x06\n\x02\x41M\x10\x0c\x12\x06\n\x02\x41W\x10\r\x12\x06\n\x02\x41U\x10\x0e\x12\x06\n\x02\x41T\x10\x0f\x12\x06\n\x02\x41Z\x10\x10\x12\x06\n\x02\x42S\x10\x11\x12\x06\n\x02\x42H\x10\x12\x12\x06\n\x02\x42\x44\x10\x13\x12\x06\n\x02\x42\x42\x10\x14\x12\x06\n\x02\x42Y\x10\x15\x12\x06\n\x02\x42\x45\x10\x16\x12\x06\n\x02\x42Z\x10\x17\x12\x06\n\x02\x42J\x10\x18\x12\x06\n\x02\x42M\x10\x19\x12\x06\n\x02\x42T\x10\x1a\x12\x06\n\x02\x42O\x10\x1b\x12\x06\n\x02\x42Q\x10\x1c\x12\x06\n\x02\x42\x41\x10\x1d\x12\x06\n\x02\x42W\x10\x1e\x12\x06\n\x02\x42V\x10\x1f\x12\x06\n\x02\x42R\x10 \x12\x06\n\x02IO\x10!\x12\x06\n\x02\x42N\x10\"\x12\x06\n\x02\x42G\x10#\x12\x06\n\x02\x42\x46\x10$\x12\x06\n\x02\x42I\x10%\x12\x06\n\x02KH\x10&\x12\x06\n\x02\x43M\x10\'\x12\x06\n\x02\x43\x41\x10(\x12\x06\n\x02\x43V\x10)\x12\x06\n\x02KY\x10*\x12\x06\n\x02\x43\x46\x10+\x12\x06\n\x02TD\x10,\x12\x06\n\x02\x43L\x10-\x12\x06\n\x02\x43N\x10.\x12\x06\n\x02\x43X\x10/\x12\x06\n\x02\x43\x43\x10\x30\x12\x06\n\x02\x43O\x10\x31\x12\x06\n\x02KM\x10\x32\x12\x06\n\x02\x43G\x10\x33\x12\x06\n\x02\x43\x44\x10\x34\x12\x06\n\x02\x43K\x10\x35\x12\x06\n\x02\x43R\x10\x36\x12\x06\n\x02\x43I\x10\x37\x12\x06\n\x02HR\x10\x38\x12\x06\n\x02\x43U\x10\x39\x12\x06\n\x02\x43W\x10:\x12\x06\n\x02\x43Y\x10;\x12\x06\n\x02\x43Z\x10<\x12\x06\n\x02\x44K\x10=\x12\x06\n\x02\x44J\x10>\x12\x06\n\x02\x44M\x10?\x12\x06\n\x02\x44O\x10@\x12\x06\n\x02\x45\x43\x10\x41\x12\x06\n\x02\x45G\x10\x42\x12\x06\n\x02SV\x10\x43\x12\x06\n\x02GQ\x10\x44\x12\x06\n\x02\x45R\x10\x45\x12\x06\n\x02\x45\x45\x10\x46\x12\x06\n\x02\x45T\x10G\x12\x06\n\x02\x46K\x10H\x12\x06\n\x02\x46O\x10I\x12\x06\n\x02\x46J\x10J\x12\x06\n\x02\x46I\x10K\x12\x06\n\x02\x46R\x10L\x12\x06\n\x02GF\x10M\x12\x06\n\x02PF\x10N\x12\x06\n\x02TF\x10O\x12\x06\n\x02GA\x10P\x12\x06\n\x02GM\x10Q\x12\x06\n\x02GE\x10R\x12\x06\n\x02\x44\x45\x10S\x12\x06\n\x02GH\x10T\x12\x06\n\x02GI\x10U\x12\x06\n\x02GR\x10V\x12\x06\n\x02GL\x10W\x12\x06\n\x02GD\x10X\x12\x06\n\x02GP\x10Y\x12\x06\n\x02GU\x10Z\x12\x06\n\x02GT\x10[\x12\x06\n\x02GG\x10\\\x12\x06\n\x02GN\x10]\x12\x06\n\x02GW\x10^\x12\x06\n\x02GY\x10_\x12\x06\n\x02HT\x10`\x12\x06\n\x02HM\x10\x61\x12\x06\n\x02VA\x10\x62\x12\x06\n\x02HN\x10\x63\x12\x06\n\x02HK\x10\x64\x12\x06\n\x02HU\x10\x65\x12\x06\n\x02IS\x10\x66\x12\x06\n\x02IN\x10g\x12\x06\n\x02ID\x10h\x12\x06\n\x02IR\x10i\x12\x06\n\x02IQ\x10j\x12\x06\n\x02IE\x10k\x12\x06\n\x02IM\x10l\x12\x06\n\x02IL\x10m\x12\x06\n\x02IT\x10n\x12\x06\n\x02JM\x10o\x12\x06\n\x02JP\x10p\x12\x06\n\x02JE\x10q\x12\x06\n\x02JO\x10r\x12\x06\n\x02KZ\x10s\x12\x06\n\x02KE\x10t\x12\x06\n\x02KI\x10u\x12\x06\n\x02KP\x10v\x12\x06\n\x02KR\x10w\x12\x06\n\x02KW\x10x\x12\x06\n\x02KG\x10y\x12\x06\n\x02LA\x10z\x12\x06\n\x02LV\x10{\x12\x06\n\x02LB\x10|\x12\x06\n\x02LS\x10}\x12\x06\n\x02LR\x10~\x12\x06\n\x02LY\x10\x7f\x12\x07\n\x02LI\x10\x80\x01\x12\x07\n\x02LT\x10\x81\x01\x12\x07\n\x02LU\x10\x82\x01\x12\x07\n\x02MO\x10\x83\x01\x12\x07\n\x02MK\x10\x84\x01\x12\x07\n\x02MG\x10\x85\x01\x12\x07\n\x02MW\x10\x86\x01\x12\x07\n\x02MY\x10\x87\x01\x12\x07\n\x02MV\x10\x88\x01\x12\x07\n\x02ML\x10\x89\x01\x12\x07\n\x02MT\x10\x8a\x01\x12\x07\n\x02MH\x10\x8b\x01\x12\x07\n\x02MQ\x10\x8c\x01\x12\x07\n\x02MR\x10\x8d\x01\x12\x07\n\x02MU\x10\x8e\x01\x12\x07\n\x02YT\x10\x8f\x01\x12\x07\n\x02MX\x10\x90\x01\x12\x07\n\x02\x46M\x10\x91\x01\x12\x07\n\x02MD\x10\x92\x01\x12\x07\n\x02MC\x10\x93\x01\x12\x07\n\x02MN\x10\x94\x01\x12\x07\n\x02ME\x10\x95\x01\x12\x07\n\x02MS\x10\x96\x01\x12\x07\n\x02MA\x10\x97\x01\x12\x07\n\x02MZ\x10\x98\x01\x12\x07\n\x02MM\x10\x99\x01\x12\x07\n\x02NA\x10\x9a\x01\x12\x07\n\x02NR\x10\x9b\x01\x12\x07\n\x02NP\x10\x9c\x01\x12\x07\n\x02NL\x10\x9d\x01\x12\x07\n\x02NC\x10\x9e\x01\x12\x07\n\x02NZ\x10\x9f\x01\x12\x07\n\x02NI\x10\xa0\x01\x12\x07\n\x02NE\x10\xa1\x01\x12\x07\n\x02NG\x10\xa2\x01\x12\x07\n\x02NU\x10\xa3\x01\x12\x07\n\x02NF\x10\xa4\x01\x12\x07\n\x02MP\x10\xa5\x01\x12\x07\n\x02NO\x10\xa6\x01\x12\x07\n\x02OM\x10\xa7\x01\x12\x07\n\x02PK\x10\xa8\x01\x12\x07\n\x02PW\x10\xa9\x01\x12\x07\n\x02PS\x10\xaa\x01\x12\x07\n\x02PA\x10\xab\x01\x12\x07\n\x02PG\x10\xac\x01\x12\x07\n\x02PY\x10\xad\x01\x12\x07\n\x02PE\x10\xae\x01\x12\x07\n\x02PH\x10\xaf\x01\x12\x07\n\x02PN\x10\xb0\x01\x12\x07\n\x02PL\x10\xb1\x01\x12\x07\n\x02PT\x10\xb2\x01\x12\x07\n\x02PR\x10\xb3\x01\x12\x07\n\x02QA\x10\xb4\x01\x12\x07\n\x02RE\x10\xb5\x01\x12\x07\n\x02RO\x10\xb6\x01\x12\x07\n\x02RU\x10\xb7\x01\x12\x07\n\x02RW\x10\xb8\x01\x12\x07\n\x02\x42L\x10\xb9\x01\x12\x07\n\x02SH\x10\xba\x01\x12\x07\n\x02KN\x10\xbb\x01\x12\x07\n\x02LC\x10\xbc\x01\x12\x07\n\x02MF\x10\xbd\x01\x12\x07\n\x02PM\x10\xbe\x01\x12\x07\n\x02VC\x10\xbf\x01\x12\x07\n\x02WS\x10\xc0\x01\x12\x07\n\x02SM\x10\xc1\x01\x12\x07\n\x02ST\x10\xc2\x01\x12\x07\n\x02SA\x10\xc3\x01\x12\x07\n\x02SN\x10\xc4\x01\x12\x07\n\x02RS\x10\xc5\x01\x12\x07\n\x02SC\x10\xc6\x01\x12\x07\n\x02SL\x10\xc7\x01\x12\x07\n\x02SG\x10\xc8\x01\x12\x07\n\x02SX\x10\xc9\x01\x12\x07\n\x02SK\x10\xca\x01\x12\x07\n\x02SI\x10\xcb\x01\x12\x07\n\x02SB\x10\xcc\x01\x12\x07\n\x02SO\x10\xcd\x01\x12\x07\n\x02ZA\x10\xce\x01\x12\x07\n\x02GS\x10\xcf\x01\x12\x07\n\x02SS\x10\xd0\x01\x12\x07\n\x02\x45S\x10\xd1\x01\x12\x07\n\x02LK\x10\xd2\x01\x12\x07\n\x02SD\x10\xd3\x01\x12\x07\n\x02SR\x10\xd4\x01\x12\x07\n\x02SJ\x10\xd5\x01\x12\x07\n\x02SZ\x10\xd6\x01\x12\x07\n\x02SE\x10\xd7\x01\x12\x07\n\x02\x43H\x10\xd8\x01\x12\x07\n\x02SY\x10\xd9\x01\x12\x07\n\x02TW\x10\xda\x01\x12\x07\n\x02TJ\x10\xdb\x01\x12\x07\n\x02TZ\x10\xdc\x01\x12\x07\n\x02TH\x10\xdd\x01\x12\x07\n\x02TL\x10\xde\x01\x12\x07\n\x02TG\x10\xdf\x01\x12\x07\n\x02TK\x10\xe0\x01\x12\x07\n\x02TO\x10\xe1\x01\x12\x07\n\x02TT\x10\xe2\x01\x12\x07\n\x02TN\x10\xe3\x01\x12\x07\n\x02TR\x10\xe4\x01\x12\x07\n\x02TM\x10\xe5\x01\x12\x07\n\x02TC\x10\xe6\x01\x12\x07\n\x02TV\x10\xe7\x01\x12\x07\n\x02UG\x10\xe8\x01\x12\x07\n\x02UA\x10\xe9\x01\x12\x07\n\x02\x41\x45\x10\xea\x01\x12\x07\n\x02GB\x10\xeb\x01\x12\x07\n\x02US\x10\xec\x01\x12\x07\n\x02UM\x10\xed\x01\x12\x07\n\x02UY\x10\xee\x01\x12\x07\n\x02UZ\x10\xef\x01\x12\x07\n\x02VU\x10\xf0\x01\x12\x07\n\x02VE\x10\xf1\x01\x12\x07\n\x02VN\x10\xf2\x01\x12\x07\n\x02VG\x10\xf3\x01\x12\x07\n\x02VI\x10\xf4\x01\x12\x07\n\x02WF\x10\xf5\x01\x12\x07\n\x02\x45H\x10\xf6\x01\x12\x07\n\x02YE\x10\xf7\x01\x12\x07\n\x02ZM\x10\xf8\x01\x12\x07\n\x02ZW\x10\xf9\x01\x62\x06proto3') ) @@ -31,18 +31,26 @@ _FEE_CURRENCY = _descriptor.EnumDescriptor( file=DESCRIPTOR, values=[ _descriptor.EnumValueDescriptor( - name='LBC', index=0, number=0, + name='UNKNOWN_CURRENCY', index=0, number=0, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( - name='USD', index=1, number=2, + name='LBC', index=1, number=1, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='BTC', index=2, number=2, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='USD', index=3, number=3, serialized_options=None, type=None), ], containing_type=None, serialized_options=None, - serialized_start=810, - serialized_end=838, + serialized_start=811, + serialized_end=870, ) _sym_db.RegisterEnumDescriptor(_FEE_CURRENCY) @@ -795,8 +803,8 @@ _LANGUAGE_LANGUAGE = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=1145, - serialized_end=2706, + serialized_start=1177, + serialized_end=2738, ) _sym_db.RegisterEnumDescriptor(_LANGUAGE_LANGUAGE) @@ -1609,8 +1617,8 @@ _LANGUAGE_SCRIPT = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=2709, - serialized_end=4799, + serialized_start=2741, + serialized_end=4831, ) _sym_db.RegisterEnumDescriptor(_LANGUAGE_SCRIPT) @@ -2623,8 +2631,8 @@ _LOCATION_COUNTRY = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4934, - serialized_end=7078, + serialized_start=4966, + serialized_end=7110, ) _sym_db.RegisterEnumDescriptor(_LOCATION_COUNTRY) @@ -2951,8 +2959,8 @@ _FEE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=734, - serialized_end=838, + serialized_start=735, + serialized_end=870, ) @@ -2996,8 +3004,8 @@ _FILE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=840, - serialized_end=888, + serialized_start=872, + serialized_end=920, ) @@ -3034,8 +3042,8 @@ _IMAGE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=890, - serialized_end=928, + serialized_start=922, + serialized_end=960, ) @@ -3079,8 +3087,8 @@ _VIDEO = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=930, - serialized_end=986, + serialized_start=962, + serialized_end=1018, ) @@ -3110,8 +3118,8 @@ _AUDIO = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=988, - serialized_end=1013, + serialized_start=1020, + serialized_end=1045, ) @@ -3157,8 +3165,8 @@ _LANGUAGE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1016, - serialized_end=4799, + serialized_start=1048, + serialized_end=4831, ) @@ -3199,15 +3207,15 @@ _LOCATION = _descriptor.Descriptor( serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='latitude', full_name='pb.Location.latitude', index=4, - number=5, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), + number=5, type=17, cpp_type=1, label=1, + has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='longitude', full_name='pb.Location.longitude', index=5, - number=6, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), + number=6, type=17, cpp_type=1, label=1, + has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), @@ -3224,8 +3232,8 @@ _LOCATION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4802, - serialized_end=7078, + serialized_start=4834, + serialized_end=7110, ) _CLAIM.fields_by_name['stream'].message_type = _STREAM diff --git a/tests/integration/test_claim_commands.py b/tests/integration/test_claim_commands.py index a64098781..dca644785 100644 --- a/tests/integration/test_claim_commands.py +++ b/tests/integration/test_claim_commands.py @@ -1,6 +1,7 @@ import hashlib import tempfile from binascii import unhexlify +from decimal import Decimal import ecdsa @@ -73,14 +74,15 @@ class ChannelCommands(CommandTestCase): 'title': "Cool Channel", 'description': "Best channel on LBRY.", 'thumbnail_url': "https://co.ol/thumbnail.png", - 'language': "en", + 'languages': ["en-US"], + 'locations': ['US::Manchester'], 'contact_email': "human@email.com", 'homepage_url': "https://co.ol", 'cover_url': "https://co.ol/cover.png", } fixed_values = values.copy() - del fixed_values['language'] - fixed_values['languages'] = [{'language': 'en'}] + fixed_values['languages'] = ['en-US'] + fixed_values['locations'] = [{'country': 'US', 'city': 'Manchester'}] # create new channel with all fields set tx = await self.out(self.channel_create('@bigchannel', **values)) @@ -259,13 +261,14 @@ class StreamCommands(CommandTestCase): 'hovercraft3', channel_id=baz_id, channel_account_id=[account1_id] ) - async def test_setting_claim_fields(self): + async def test_setting_stream_fields(self): values = { 'tags': ["cool", "awesome"], 'title': "Cool Content", 'description': "Best content on LBRY.", 'thumbnail_url': "https://co.ol/thumbnail.png", - 'language': "en", + 'languages': ["en"], + 'locations': ['{"country": "UA"}'], 'author': "Jules Verne", 'license': 'Public Domain', @@ -280,8 +283,8 @@ class StreamCommands(CommandTestCase): 'video_height': 600 } fixed_values = values.copy() - del fixed_values['language'] - fixed_values['languages'] = [{'language': 'en'}] + fixed_values['languages'] = ['en'] + fixed_values['locations'] = [{'country': 'UA'}] # create new channel with all fields set tx = await self.out(self.stream_create('big', **values)) @@ -293,7 +296,7 @@ class StreamCommands(CommandTestCase): fixed_values['release_time'] = str(values['release_time']) fixed_values['fee'] = { 'address': fixed_values.pop('fee_address'), - 'amount': fixed_values.pop('fee_amount').replace('.', ''), + 'amount': float(fixed_values.pop('fee_amount')), 'currency': fixed_values.pop('fee_currency').upper() } fixed_values['video'] = { diff --git a/tests/unit/schema/test_claim_from_bytes.py b/tests/unit/schema/test_claim_from_bytes.py index 261ea0b44..e6012d710 100644 --- a/tests/unit/schema/test_claim_from_bytes.py +++ b/tests/unit/schema/test_claim_from_bytes.py @@ -20,7 +20,7 @@ class TestOldJSONSchemaCompatibility(TestCase): self.assertEqual(stream.description, '10MB test file to measure download speed on Lbry p2p-network.') self.assertEqual(stream.license, 'None') self.assertEqual(stream.author, 'root') - self.assertEqual(stream.language, 'en') + self.assertEqual(stream.langtags, ['en']) self.assertEqual(stream.media_type, 'application/octet-stream') self.assertEqual(stream.thumbnail_url, '/home/robert/lbry/speed.jpg') self.assertEqual( @@ -51,7 +51,7 @@ class TestOldJSONSchemaCompatibility(TestCase): self.assertEqual(stream.license, 'Creative Commons Attribution 3.0 United States') self.assertEqual(stream.license_url, 'https://creativecommons.org/licenses/by/3.0/us/legalcode') self.assertEqual(stream.author, 'Mii') - self.assertEqual(stream.language, 'en') + self.assertEqual(stream.langtags, ['en']) self.assertEqual(stream.media_type, 'application/x-msdownload') self.assertEqual( stream.sd_hash, @@ -77,7 +77,7 @@ class TestOldJSONSchemaCompatibility(TestCase): self.assertEqual(stream.description, 'asd') self.assertEqual(stream.license, 'Creative Commons Attribution 4.0 International') self.assertEqual(stream.author, 'sgb') - self.assertEqual(stream.language, 'en') + self.assertEqual(stream.langtags, ['en']) self.assertEqual(stream.media_type, 'video/mp4') self.assertEqual( stream.sd_hash, @@ -120,7 +120,7 @@ class TestTypesV1Compatibility(TestCase): ) self.assertEqual(stream.license, 'Copyrighted (contact author)') self.assertEqual(stream.author, 'The Linux Gamer') - self.assertEqual(stream.language, 'en') + self.assertEqual(stream.langtags, ['en']) self.assertEqual(stream.media_type, 'video/mp4') self.assertEqual(stream.thumbnail_url, 'https://berk.ninja/thumbnails/FrTdBCOS_fc') self.assertEqual( @@ -157,7 +157,7 @@ class TestTypesV1Compatibility(TestCase): self.assertEqual(stream.description, 'midi') self.assertEqual(stream.license, 'Creative Commons Attribution 4.0 International') self.assertEqual(stream.author, 'rpg midi') - self.assertEqual(stream.language, 'en') + self.assertEqual(stream.langtags, ['en']) self.assertEqual(stream.media_type, 'application/x-zip-compressed') self.assertEqual( stream.sd_hash, diff --git a/tests/unit/schema/test_models.py b/tests/unit/schema/test_models.py index e8fc3c137..1955a417e 100644 --- a/tests/unit/schema/test_models.py +++ b/tests/unit/schema/test_models.py @@ -61,3 +61,103 @@ class TestFee(TestCase): print(stream.fee.lbc) with self.assertRaisesRegex(ValueError, 'Dewies can only be returned for LBC fees.'): print(stream.fee.dewies) + + +class TestLanguages(TestCase): + + def test_language_successful_parsing(self): + stream = Stream() + + stream.languages.append('en') + self.assertEqual(stream.languages[0].langtag, 'en') + self.assertEqual(stream.languages[0].language, 'en') + self.assertEqual(stream.langtags, ['en']) + + stream.languages.append('en-US') + self.assertEqual(stream.languages[1].langtag, 'en-US') + self.assertEqual(stream.languages[1].language, 'en') + self.assertEqual(stream.languages[1].region, 'US') + self.assertEqual(stream.langtags, ['en', 'en-US']) + + stream.languages.append('en-Latn-US') + self.assertEqual(stream.languages[2].langtag, 'en-Latn-US') + self.assertEqual(stream.languages[2].language, 'en') + self.assertEqual(stream.languages[2].script, 'Latn') + self.assertEqual(stream.languages[2].region, 'US') + self.assertEqual(stream.langtags, ['en', 'en-US', 'en-Latn-US']) + + stream = Stream() + stream.languages.extend(['en-Latn-US', 'es-ES', 'de-DE']) + self.assertEqual(stream.languages[0].language, 'en') + self.assertEqual(stream.languages[1].language, 'es') + self.assertEqual(stream.languages[2].language, 'de') + + def test_language_error_parsing(self): + stream = Stream() + with self.assertRaisesRegex(ValueError, 'Language has no value defined for name zz'): + stream.languages.append('zz') + with self.assertRaisesRegex(ValueError, 'Script has no value defined for name Zabc'): + stream.languages.append('en-Zabc') + with self.assertRaisesRegex(ValueError, 'Country has no value defined for name ZZ'): + stream.languages.append('en-Zzzz-ZZ') + with self.assertRaisesRegex(AssertionError, 'Failed to parse language tag: en-Zzz-US'): + stream.languages.append('en-Zzz-US') + + +class TestLocations(TestCase): + + def test_location_successful_parsing(self): + # from simple string + stream = Stream() + stream.locations.append('US') + self.assertEqual(stream.locations[0].country, 'US') + + # from full string + stream = Stream() + stream.locations.append('US:NH:Manchester:03101:42.990605:-71.460989') + self.assertEqual(stream.locations[0].country, 'US') + self.assertEqual(stream.locations[0].state, 'NH') + self.assertEqual(stream.locations[0].city, 'Manchester') + self.assertEqual(stream.locations[0].code, '03101') + self.assertEqual(stream.locations[0].latitude, '42.990605') + self.assertEqual(stream.locations[0].longitude, '-71.460989') + + # from partial string + stream = Stream() + stream.locations.append('::Manchester:03101:') + self.assertEqual(stream.locations[0].country, None) + self.assertEqual(stream.locations[0].state, '') + self.assertEqual(stream.locations[0].city, 'Manchester') + self.assertEqual(stream.locations[0].code, '03101') + self.assertEqual(stream.locations[0].latitude, None) + self.assertEqual(stream.locations[0].longitude, None) + + # from partial string lat/long + stream = Stream() + stream.locations.append('::::42.990605:-71.460989') + self.assertEqual(stream.locations[0].country, None) + self.assertEqual(stream.locations[0].state, '') + self.assertEqual(stream.locations[0].city, '') + self.assertEqual(stream.locations[0].code, '') + self.assertEqual(stream.locations[0].latitude, '42.990605') + self.assertEqual(stream.locations[0].longitude, '-71.460989') + + # from short circuit lat/long + stream = Stream() + stream.locations.append('42.990605:-71.460989') + self.assertEqual(stream.locations[0].country, None) + self.assertEqual(stream.locations[0].state, '') + self.assertEqual(stream.locations[0].city, '') + self.assertEqual(stream.locations[0].code, '') + self.assertEqual(stream.locations[0].latitude, '42.990605') + self.assertEqual(stream.locations[0].longitude, '-71.460989') + + # from json string + stream = Stream() + stream.locations.append('{"country": "ES"}') + self.assertEqual(stream.locations[0].country, 'ES') + + # from dict + stream = Stream() + stream.locations.append({"country": "UA"}) + self.assertEqual(stream.locations[0].country, 'UA')