# SPDX-License-Identifier: MIT OR GPL-3.0-or-later import contextlib from typing import Optional from urllib.parse import ParseResult, parse_qsl, urlparse from bpy.app.translations import pgettext from ..common.convert import Json class LicenseConfirmationRequiredProp: def __init__( self, url: Optional[str], json_key: Optional[str], message: str, ) -> None: self.url = url self.json_key = json_key self.message = message def description(self) -> str: return f"""class=LicenseConfirmationRequired url={self.url} json_key={self.json_key} message={self.message} """ class LicenseConfirmationRequiredError(Exception): def __init__(self, props: list[LicenseConfirmationRequiredProp]) -> None: self.props = props super().__init__(self.description()) def description(self) -> str: return "\n".join(prop.description() for prop in self.props) def license_confirmations(self) -> list[dict[str, str]]: return [ { "name": "LicenseConfirmation" + str(index), "url": prop.url or "", "json_key": prop.json_key or "", "message": prop.message or "", } for index, prop in enumerate(self.props) ] def validate_license_url( url_str: str, json_key: str, props: list[LicenseConfirmationRequiredProp] ) -> None: if not url_str: return url = None with contextlib.suppress(ValueError): url = urlparse(url_str) if url: query_dict = dict(parse_qsl(url.query)) if validate_vroid_hub_license_url( url, query_dict, json_key, props ) or validate_uni_virtual_license_url(url, query_dict, json_key, props): return props.append( LicenseConfirmationRequiredProp( url_str, json_key, pgettext("This VRM is not allowed to be edited. Please check its license"), ) ) def validate_vroid_hub_license_url( url: ParseResult, query_dict: dict[str, str], json_key: str, props: list[LicenseConfirmationRequiredProp], ) -> bool: # https://hub.vroid.com/en/license?allowed_to_use_user=everyone&characterization_allowed_user=everyone&corporate_commercial_use=allow&credit=unnecessary&modification=allow&personal_commercial_use=profit&redistribution=allow&sexual_expression=allow&version=1&violent_expression=allow if url.hostname != "hub.vroid.com" or not url.path.endswith("/license"): return False if query_dict.get("modification") == "disallow": props.append( LicenseConfirmationRequiredProp( url.geturl(), json_key, pgettext( 'This VRM is licensed by VRoid Hub License "Alterations: No".' ), ) ) return True def validate_uni_virtual_license_url( url: ParseResult, query_dict: dict[str, str], json_key: str, props: list[LicenseConfirmationRequiredProp], ) -> bool: # https://uv-license.com/en/license?utf8=%E2%9C%93&pcu=true if url.hostname != "uv-license.com" or not url.path.endswith("/license"): return False if query_dict.get("remarks") == "true": props.append( LicenseConfirmationRequiredProp( url.geturl(), json_key, pgettext('This VRM is licensed by UV License with "Remarks".'), ) ) return True def validate_vrm1_license( json_dict: dict[str, Json], _confirmations: list[LicenseConfirmationRequiredProp] ) -> None: extensions_dict = json_dict.get("extensions") if not isinstance(extensions_dict, dict): return vrmc_vrm_dict = extensions_dict.get("VRMC_vrm") if not isinstance(vrmc_vrm_dict, dict): return return def validate_vrm0_license( json_dict: dict[str, Json], confirmations: list[LicenseConfirmationRequiredProp] ) -> None: extensions_dict = json_dict.get("extensions") if not isinstance(extensions_dict, dict): return vrm_dict = extensions_dict.get("VRM") if not isinstance(vrm_dict, dict): return meta_dict = vrm_dict.get("meta") if not isinstance(meta_dict, dict): return license_name = meta_dict.get("licenseName") if license_name in [ # https://github.com/vrm-c/vrm-specification/blob/master/specification/0.0/schema/vrm.meta.schema.json#L56 "CC_BY_ND", "CC_BY_NC_ND", ]: confirmations.append( LicenseConfirmationRequiredProp( None, None, pgettext( "This VRM is not allowed to be edited. Please check its license" ), ) ) if license_name == "Other": other_license_url = meta_dict.get("otherLicenseUrl") if other_license_url is None: confirmations.append( LicenseConfirmationRequiredProp( None, None, pgettext( 'The VRM selects "Other" license but no license url is found.' ), ) ) else: validate_license_url( str(other_license_url), "otherLicenseUrl", confirmations ) other_permission_url = meta_dict.get("otherPermissionUrl") if other_permission_url is not None: validate_license_url( str(other_permission_url), "otherPermissionUrl", confirmations, ) def validate_license( json_dict: dict[str, Json], spec_version_number: tuple[int, int] ) -> None: """Validate that the license is not a non-modifiable license, such as CC_ND.""" confirmations: list[LicenseConfirmationRequiredProp] = [] if tuple(spec_version_number) >= (1, 0): validate_vrm1_license(json_dict, confirmations) else: validate_vrm0_license(json_dict, confirmations) if confirmations: raise LicenseConfirmationRequiredError(confirmations)