def _try_parse_value(cls, vals: utils.misc.dotdict, child: lxml.objectify.ObjectifiedElement, tag: str, text: str, custom_types: CustomTypes) -> bool: if tag == 'icon_url': vals.icon_url = text elif tag == 'banner_url': vals.banner_url = text elif tag == 'rating_info': vals.rating_info = custom_types['rating_info']._parse(child) elif tag == 'star_rating_info': vals.rating_stars = custom_types['rating_stars']._parse(child) elif tag == 'release_date_on_eshop': vals.release_date_eshop = text elif tag == 'release_date_on_retail': vals.release_date_retail = text elif tag == 'price_on_retail_detail': xmlutils.validate_schema(child, { 'amount': None, 'currency': None, 'raw_value': None }, True) vals.price_retail = common.SamuraiPrice( float(child.raw_value.text) if hasattr(child, 'raw_value') else None, child.currency) else: return False return True
def _try_parse_value(cls, vals: utils.misc.dotdict, child: lxml.objectify.ObjectifiedElement, tag: str, text: str, custom_types: title_list.CustomTypes) -> bool: if 'is_public' not in vals: xml = child.getparent() vals.is_public = utils.misc.get_bool(xml.get('public')) if tag == 'formal_name': vals.formal_name = text elif tag == 'genres': vals.genres = [] for genre in child.genre: xmlutils.validate_schema(genre, {'name': None}, False) vals.genres.append(common.IDName(int(genre.get('id')), genre.name.text)) elif tag == 'keywords': vals.keywords = [keyword.text for keyword in getattr(child, 'keyword', [])] elif tag == 'ticket_available': vals.has_ticket = utils.misc.get_bool(text) elif tag == 'download_code_sales': vals.sales_download_code = utils.misc.get_bool(text) elif tag == 'download_card_sales': xmlutils.validate_schema(child, {'image_url': None}, True) vals.sales_download_card = SamuraiDownloadCardSales( utils.misc.get_bool(child.get('available')), xmlutils.get_text(child, 'image_url') ) else: return super()._try_parse_value(vals, child, tag, text, custom_types) return True
def _read(self, reader, config): info = xmlutils.read_object(reader) assert info.tag == 'version_list_info' xmlutils.validate_schema(info, {'version': None, 'fqdn': None}, False) self.latest = int(info.version.text) self.fqdn = info.fqdn.text
def _read(self, reader, config): news_xml = xmlutils.load_root(reader, 'news') xmlutils.validate_schema( news_xml, { 'news_entry': { 'headline': None, 'description': None, 'date': None, 'images': { 'image': None } } }, True) self.entries = [] for entry in getattr(news_xml, 'news_entry', []): if hasattr(entry, 'images'): images = [ SamuraiNewsImage(image.get('url'), image.get('type'), int(image.get('index')), int(image.get('width')), int(image.get('height'))) for image in entry.images.image ] else: images = [] self.entries.append( SamuraiNewsEntry(entry.headline.text, entry.description.text, entry.date.text, images))
def _read(self, reader, config): prices = xmlutils.load_root(reader, 'online_prices') xmlutils.validate_schema( prices, { 'online_price': { 'aoc_id': None, 'eshop_sales_status': None, 'price': { 'regular_price': { 'amount': None, 'currency': None, 'raw_value': None } } } }, False) self.prices = {} for price in prices.online_price: assert len(price.price.getchildren()) == 1 regular_price = price.price.regular_price self.prices[ids.ContentID(price.aoc_id.text)] = SamuraiDlcPrice( price.eshop_sales_status.text, regular_price.get('id'), common.SamuraiPrice(float(regular_price.raw_value.text), regular_price.currency.text))
def _try_parse_value(cls, vals: utils.misc.dotdict, child: lxml.objectify.ObjectifiedElement, tag: str, text: str, custom_types: CustomTypes) -> bool: if tag == 'icon_url': vals.icon_url = text elif tag == 'banner_url': vals.banner_url = text elif tag == 'thumbnail_url': vals.thumbnail_url = text elif tag == 'title': xmlutils.validate_schema(child, {'name': None, 'icon_url': None, 'banner_url': None, 'thumbnails': {'thumbnail': None}}, True) if hasattr(child, 'thumbnails'): thumbnails = [common.SamuraiThumbnail._parse(t) for t in child.thumbnails.thumbnail] else: thumbnails = [] vals.title = SamuraiMovieLinkedTitle( ids.ContentID(child.get('id')), child.name.text, xmlutils.get_text(child, 'icon_url'), xmlutils.get_text(child, 'banner_url'), thumbnails ) elif tag == 'rating_info': vals.rating_info = custom_types['rating_info']._parse(child) else: return False return True
def _read(self, reader, config): ec_info = xmlutils.load_root(reader, 'title_ec_info') xmlutils.validate_schema( ec_info, { 'title_id': None, 'content_size': None, 'title_version': None, 'disable_download': None, 'content_lock': { 'seed_published': None, 'external_seed': None, 'playable_date': None } }, True) self.title_id = ids.TitleID(ec_info.title_id.text) self.content_size = int(ec_info.content_size.text) self.version = int(ec_info.title_version.text) self.download_disabled = utils.misc.get_bool( ec_info.disable_download.text) if hasattr(ec_info, 'content_lock'): self.seed_published = utils.misc.get_bool( ec_info.content_lock.seed_published.text) self.external_seed = xmlutils.get_text(ec_info.content_lock, 'external_seed') self.playable_date = xmlutils.get_text(ec_info.content_lock, 'playable_date')
def _read(self, reader, config): aocs = xmlutils.load_root(reader, 'aocs') xmlutils.validate_schema(aocs, {'aoc': {'data_size': None}}, False) self.sizes = { ids.ContentID(aoc.get('id')): int(aoc.data_size.text) for aoc in aocs.aoc }
def test_xml__validate_schema__superset(xml_element): xml.validate_schema(xml_element, {'node': { 'el1': None, 'el2': None, 'el3': None }}, True) with pytest.raises(XmlSchemaError): xml.validate_schema(xml_element, {'node': {'el1': None}}, True)
def _read(self, reader, config): pairs = xmlutils.load_root(reader, 'title_id_pairs') if len(pairs.getchildren()) == 0: raise NoIDPairMatchError xmlutils.validate_schema(pairs, { 'title_id_pair': { 'ns_uid': None, 'title_id': None, 'type': None } }, False) assert len(pairs.getchildren()) == 1 pair = pairs.title_id_pair assert pair.type.text in ( 'T', 'D' ) # 'Title'/'Demo'? # not sure if there are any other types self.content_id = ids.ContentID(pair.ns_uid.text) self.title_id = ids.TitleID(pair.title_id.text)
def _read(self, reader, config): content = xmlutils.load_root(reader, 'content') assert len(content.getchildren()) == 1 demo = content.demo xmlutils.validate_schema( demo, { 'name': None, 'icon_url': None, 'rating_info': common.SamuraiRatingDetailed._get_schema()[0] }, True) for child, tag, text in xmlutils.iter_children(demo): if tag == 'name': self.name = text elif tag == 'icon_url': self.icon_url = text elif tag == 'rating_info': self.rating_info = common.SamuraiRatingDetailed._parse(child) else: raise ValueError(f'unknown tag: {tag}')
def _parse(cls, xml: lxml.objectify.ObjectifiedElement) -> 'SamuraiDlc3DS': vals = utils.misc.dotdict() for child, tag, text in xmlutils.iter_children(xml): if tag == 'name': vals.name = text elif tag == 'price': xmlutils.validate_schema( child, {'regular_price': { 'amount': None, 'currency': None }}, False) vals.price = common.SamuraiPrice( float(child.regular_price.amount.text), child.regular_price.currency.text) else: raise ValueError(f'unknown tag: {tag}') return cls(**vals)
def _parse(cls, xml: lxml.objectify.ObjectifiedElement) -> 'SamuraiDlcWiiU': vals = utils.misc.dotdict() vals.is_new = utils.misc.get_bool(xml.get('new')) vals.content_id = ids.ContentID(xml.get('id')) for child, tag, text in xmlutils.iter_children(xml): if tag == 'name': vals.name = text elif tag == 'item_new_since': vals.release_date = text elif tag == 'icon_url': vals.icon_url = text elif tag == 'screenshots': vals.screenshots = [ common.SamuraiScreenshot._parse(screenshot) for screenshot in child.screenshot ] elif tag == 'promotion_images': vals.promotion_image_urls = [] for image in child.promotion_image: assert set(image.attrib.keys()) == {'index', 'url'} vals.promotion_image_urls.append(image.get('url')) elif tag == 'promotion_movie_url': vals.promotion_video_url = text elif tag == 'content_indexes': xmlutils.validate_schema(child, {'content_index': None}, False) vals.content_indexes = SamuraiDlcContentIndexes( child.get('variation'), [int(i.text) for i in child.content_index]) elif tag == 'description': vals.description = text elif tag == 'disclaimer': vals.disclaimer = text elif tag == 'allow_overlap': vals.allow_overlap = utils.misc.get_bool(text) else: raise ValueError(f'unknown tag: {tag}') return cls(**vals)
def _try_parse_value(cls, vals: utils.misc.dotdict, child: lxml.objectify.ObjectifiedElement, tag: str, text: str, custom_types: CustomTypes) -> bool: # kind of hacky, but it works if 'is_new' not in vals: xml = child.getparent() vals.is_new = utils.misc.get_bool(xml.get('new')) vals.content_id = ids.ContentID(xml.get('id')) if tag == 'product_code': vals.product_code = text elif tag == 'name': vals.name = text elif tag == 'platform': vals.platform = common.SamuraiPlatform._parse(child) elif tag == 'publisher': xmlutils.validate_schema(child, {'name': None}, False) vals.publisher = common.IDName(int(child.get('id')), child.name.text) elif tag == 'display_genre': vals.genre = text elif tag == 'retail_sales': vals.sales_retail = utils.misc.get_bool(text) elif tag == 'eshop_sales': vals.sales_eshop = utils.misc.get_bool(text) elif tag == 'demo_available': vals.has_demo = utils.misc.get_bool(text) elif tag == 'aoc_available': vals.has_dlc__inaccurate = utils.misc.get_bool(text) elif tag == 'in_app_purchase': vals.has_iap__inaccurate = utils.misc.get_bool(text) elif tag == 'release_date_on_original': pass # unused elif tag == 'price_on_retail': pass # unused elif tag == 'tentative_price_on_eshop': pass # unused else: return False return True
def _read(self, reader, config): versionlist = xmlutils.read_object(reader) assert versionlist.tag == 'version_list' xmlutils.validate_schema(versionlist, { 'version': None, 'titles': { 'title': { 'id': None, 'version': None } } }, True) # sanity check if self.__version is not None: assert int(versionlist.version.text) == self.__version if hasattr(versionlist.titles, 'title'): self.updates = [(ids.TitleID(title.id.text), int(title.version.text)) for title in versionlist.titles.title] else: self.updates = []
def _try_parse_value(cls, vals: utils.misc.dotdict, child: lxml.objectify.ObjectifiedElement, tag: str, text: str, custom_types: title_list.CustomTypes) -> bool: if tag == 'package_url': vals.package_url = text elif tag == 'thumbnails': vals.thumbnails = [common.SamuraiThumbnail._parse(t) for t in child.thumbnail] elif tag == 'hero_banner_url': vals.hero_banner_url = text elif tag == 'web_sales': vals.sales_web = utils.misc.get_bool(text) elif tag == 'top_image': xmlutils.validate_schema(child, {'type': None, 'url': None}, False) vals.top_image_type = child.type.text vals.top_image_url = child.url.text elif tag == 'description': vals.description = text elif tag == 'price_description': vals.price_description = text elif tag == 'features': vals.features = [SamuraiTitleFeature._parse(feature) for feature in child.feature] elif tag == 'play_styles': xmlutils.validate_schema(child, {'play_style': { 'controllers': {'controller': {'id': None, 'name': None, 'icons': {'icon': None}}}, 'features': {'feature': SamuraiTitleFeature._get_schema()[0]}} }, True) vals.play_styles = [] for play_style in child.play_style: if hasattr(play_style, 'controllers'): controllers = [ SamuraiTitleController( utils.misc.get_bool(controller.get('required')), int(controller.get('type')), int(controller.id.text), controller.name.text, [common.SamuraiIcon._parse(icon) for icon in controller.icons.icon] if hasattr(controller, 'icons') else None ) for controller in play_style.controllers.controller ] else: controllers = [] if hasattr(play_style, 'features'): features = [SamuraiTitleFeature._parse(feature) for feature in play_style.features.feature] else: features = [] vals.play_styles.append(SamuraiTitlePlayStyle(play_style.get('type'), controllers, features)) elif tag == 'languages': vals.languages = [] for language in child.language: xmlutils.validate_schema(language, {'iso_code': None, 'name': None}, False) vals.languages.append(SamuraiTitleLanguage(language.iso_code.text, language.name.text)) elif tag == 'number_of_players': # sometimes this field contains additional text in a second line, discard it for now and use the first non-empty line cleaned_text = next(line for line in text.replace('<br>', '\n').split('\n') if line) matches = re.search(r'(\d+)(?:\s*-\s*(\d+))?', cleaned_text) if matches: vals.num_players = (int(matches[1]), int(matches[2] or matches[1])) elif cleaned_text.startswith('*'): # disclaimer instead of specific player numbers (example: 50010000037675) vals.num_players = None elif re.search(r'[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uff66-\uff9f]', cleaned_text): # ignore errors if text contains japanese/chinese/korean characters vals.num_players = None else: raise RuntimeError(f'Could not parse player details: {text}') vals.num_players_raw = text elif tag == 'disclaimer': vals.disclaimer = text elif tag == 'copyright': xmlutils.validate_schema(child, {'text': None, 'image_url': None}, True) vals.copyright = SamuraiTitleCopyright(child.find('text').text, xmlutils.get_text(child, 'image_url')) elif tag == 'screenshots': vals.screenshots = [SamuraiTitleScreenshot._parse(screenshot) for screenshot in child.screenshot] elif tag == 'main_images': vals.main_images = [SamuraiTitleScreenshot._parse(image) for image in child.image] elif tag == 'preference': target = child.find('target_player') style = child.find('play_style') vals.preferences = SamuraiTitlePreference( SamuraiTitlePreferenceTarget(int(target.everyone.text), int(target.gamers.text)), SamuraiTitlePreferenceStyle(int(style.casual.text), int(style.intense.text)) ) elif tag == 'web_sites': vals.websites = [] for website in child.web_site: xmlutils.validate_schema(website, {'name': None, 'url': None, 'official': None}, False) vals.websites.append(SamuraiTitleWebsite( website.name.text, website.url.text, utils.misc.get_bool(website.official.text) )) elif tag == 'movies': vals.movies = [movie_list.SamuraiListMovie._parse(m) for m in child.movie] elif tag == 'demo_titles': vals.demos = [ SamuraiTitleLinkedDemo( ids.ContentID(demo.get('id')), demo.name.text, xmlutils.get_text(demo, 'icon_url') ) for demo in child.demo_title ] elif tag == 'peripheral_description': vals.peripheral_description = text elif tag == 'network_feature_description': vals.network_feature_description = text elif tag == 'spec_description': vals.spec_description = text elif tag == 'data_size': vals.size = int(text) elif tag == 'alternate_rating_image_url': vals.rating_info_alternate_image_url = text elif tag == 'save_data_count': vals.save_data_count = text elif tag == 'save_data_volume': vals.save_data_volume = text elif tag == 'catch_copy': vals.catch_copy = text elif tag == 'shared_movies': vals.shared_movies = [SamuraiSharedMovie._parse(movie) for movie in child.shared_movie] elif tag == 'title_notices': xmlutils.validate_schema(child, {'title_notice': {'url': None, 'display_name': None, 'description': None}}, False) vals.title_notices = [ SamuraiTitleNotice( notice.get('type'), notice.url.text, notice.display_name.text, notice.description.text ) for notice in child.title_notice ] elif tag == 'title_metas': xmlutils.validate_schema(child, {'title_meta': {'value': None}}, False) vals.title_metas = [(m.get('type'), m.value.text) for m in child.title_meta] elif tag == 'digital_manuals': xmlutils.validate_schema(child, {'digital_manual': {'name': None, 'url': None}}, False) vals.digital_manuals = [SamuraiDigitalManual(manual.name.text, manual.url.text) for manual in child.digital_manual] elif tag == 'aoc_infos': vals.aoc_infos = text else: return super()._try_parse_value(vals, child, tag, text, custom_types) return True
def _read(self, reader, config): telops_xml = xmlutils.load_root(reader, 'telops') xmlutils.validate_schema(telops_xml, {'telop': None}, True) self.entries = [el.text for el in getattr(telops_xml, 'telop', [])]