Python: Detect Plugins
-
Hi @JohnThomas, I will have a deeper look at it next week, but its seems to me that for the moment is not possible to detect if a capsule is coming from Maxon One.
With that's said, you can detect if a BaseList2D is python based by looking at the file path the plugin is registered from.import c4d def HierarchyIterator(bl2d): while bl2d: yield bl2d for opChild in HierarchyIterator(bl2d.GetDown()): yield opChild bl2d = bl2d.GetNext() def IsPythonPlugin(bl2d): basePlugin = c4d.plugins.FindPlugin(bl2d.GetType()) pluginFile = basePlugin.GetFilename() return pluginFile.endswith("pyp") or pluginFile.endswith("pypv") or pluginFile.endswith("pype") def main() -> None: for obj in HierarchyIterator(doc.GetFirstObject()): print(IsPythonPlugin(obj)) if __name__ == '__main__': main()
Cheers,
Maxime. -
Thanks for the response.
Your code seems to work for Python plugins but is there a way to detect if something is a plugin for an xdl64 or xlib. I know that I could just check if the plugin ends with either of those but a Cube also counts as a plugin with either an xdl64 or xlib. Is there a way to tell if it is a Cinema plugin in this way or a user added one?
With the Maxon One question, is there a way to tell if a scene is Maxon One dependent without being able to detect a Maxon One object specifically?
John Thomas
-
@m_adam
Sorry, little question, what does the python extenstion "pype" mean? -
Hi John Thomas, sorry I missed your reply, you can do the same by checking if the path is not part of the "corelibs" folder or the Redshift one.
Find bellow an example bellow, I just did a string comparison, for a safer approach you will need to compare with the absolute path.import c4d def HierarchyIterator(bl2d): while bl2d: yield bl2d for opChild in HierarchyIterator(bl2d.GetDown()): yield opChild bl2d = bl2d.GetNext() def IsPythonPlugin(bl2d): basePlugin = c4d.plugins.FindPlugin(bl2d.GetType()) pluginFile = basePlugin.GetFilename() return pluginFile.endswith("pyp") or pluginFile.endswith("pypv") or pluginFile.endswith("pype") def isCppPlugins(bl2d): if IsPythonPlugin(bl2d): return False basePlugin = c4d.plugins.FindPlugin(bl2d.GetType()) pluginFile = basePlugin.GetFilename() return not ("corelibs" in pluginFile or "Redshift" in pluginFile) def main() -> None: for obj in HierarchyIterator(doc.GetFirstObject()): print(IsPythonPlugin(obj), isCppPlugins(obj)) if __name__ == '__main__': main()
@ThomasB pype is the old encryption file format for python plugin file (you can't create them since R16 but you can still load them).
Cheers,
Maxime. -
Thanks for the response, the code seems to work as hoped for detecting a plugin.
Have you had a chance to look into detecting a Maxon One capsule?
John Thomas
-
Any luck with detecting Maxon One?
John Thomas
-
Any luck with detecting Maxon One?
John Thomas
-
Hi @JohnThomas first of all you will only be able to add Maxon One assets in the Cinema 4D instance that has Maxon One. If you do not have Maxon One you will still see Maxon One assets in the asset browser, and you can find their asset description and get their meta data but you cant load the asset.
With that's said the license information about an asset is stored in it's asset description meta data. You can get the license information about any asset like so:import c4d import maxon class ASSETLICENSETYPE: ALLBITS = (1 << 30) | ((1 << 0) | (1 << 2) | (1 << 4) | (1 << 6) | (1 << 8) | (1 << 10) | (1 << 12)) | ((1 << 1) | (1 << 3) | (1 << 5) | (1 << 7) | (1 << 9) | (1 << 11) | (1 << 13)) ENABLE_IN_ALL = (1 << 0) | (1 << 2) | (1 << 4) | (1 << 6) | (1 << 8) | (1 << 10) | (1 << 12) ENABLE_IN_CINEWARE = 1 << 12 ENABLE_IN_EDUCATION = 1 << 10 ENABLE_IN_LITE = 1 << 6 ENABLE_IN_PERPETUAL = 1 << 2 ENABLE_IN_STUDENT = 1 << 8 ENABLE_IN_SUBSCRIPTION = 1 << 0 ENABLE_IN_TRIAL = 1 << 4 NONE = 0 ONLY_IN_MAXONONE = 1 << 30 SHOW_IN_ALL = (1 << 1) | (1 << 3) | (1 << 5) | (1 << 7) | (1 << 9) | (1 << 11) | (1 << 13) SHOW_IN_CINEWARE = 1 << 13 SHOW_IN_EDUCATION = 1 << 11 SHOW_IN_LITE = 1 << 7 SHOW_IN_PERPETUAL = 1 << 3 SHOW_IN_STUDENT = 1 << 9 SHOW_IN_SUBSCRIPTION = 1 << 1 SHOW_IN_TRIAL = 1 << 5 def GetAssetDescription(assetId: str) -> maxon.AssetDescription: repository = maxon.AssetInterface.GetUserPrefsRepository() if not repository: raise RuntimeError("Could not access the user preferences repository.") assetDescription = repository.FindLatestAsset(maxon.AssetTypes.File(), assetId, maxon.Id(), maxon.ASSET_FIND_MODE.LATEST) if not assetDescription or assetDescription.IsNullValue(): assetDescription = repository.FindLatestAsset(maxon.AssetTypes.NodeTemplate(), assetId, maxon.Id(), maxon.ASSET_FIND_MODE.LATEST) if not assetDescription: raise RuntimeError(f"Could not find the '{assetId}' asset.") return assetDescription def TestLicenses(licenses, flag): out = [False] * len(licenses) for i, lic in enumerate(licenses): out[i] = bool(lic & flag) return out def main() -> None: everyoneAssetId = "file_faaca247ca4107d0" everyoneAssetDescription = GetAssetDescription(everyoneAssetId) everyoneLicense = everyoneAssetDescription.GetMetaData().Get(maxon.ASSETMETADATA.ValidLicenseType) subscriptionAssetId = "file_e0f13aae45bf949e" subAssetDescription = GetAssetDescription(subscriptionAssetId) subLicense = subAssetDescription.GetMetaData().Get(maxon.ASSETMETADATA.ValidLicenseType) mxOnlyAssetId = "object_6ae75db1dd872ee61a81cc637cf7aa24" mxOnlyAssetDescription = GetAssetDescription(mxOnlyAssetId) mxOnlyLicense = mxOnlyAssetDescription.GetMetaData().Get(maxon.ASSETMETADATA.ValidLicenseType) # Finally test all licenses licenses = [everyoneLicense, subLicense, mxOnlyLicense] inAll = TestLicenses(licenses, ASSETLICENSETYPE.ENABLE_IN_ALL) sub = TestLicenses(licenses, ASSETLICENSETYPE.ENABLE_IN_SUBSCRIPTION) perpetual = TestLicenses(licenses, ASSETLICENSETYPE.ENABLE_IN_PERPETUAL) trial = TestLicenses(licenses, ASSETLICENSETYPE.ENABLE_IN_TRIAL) lite = TestLicenses(licenses, ASSETLICENSETYPE.ENABLE_IN_LITE) student = TestLicenses(licenses, ASSETLICENSETYPE.ENABLE_IN_STUDENT) edu = TestLicenses(licenses, ASSETLICENSETYPE.ENABLE_IN_EDUCATION) cineware = TestLicenses(licenses, ASSETLICENSETYPE.ENABLE_IN_CINEWARE) mxOne = TestLicenses(licenses, ASSETLICENSETYPE.ONLY_IN_MAXONONE) for i, lic in enumerate(licenses): print(f"In All: {inAll[i]}, Subscription: {sub[i]}, Perpetual: {perpetual[i]}, Trial: {trial[i]}, Lite: {lite[i]}, Student: {student[i]}, Education: {edu[i]}, Cineware: {cineware[i]}, Maxon One: {mxOne[i]}") if __name__ == '__main__': main()
With that's said the main issue is to retrieve the asset id from a given capsule in a scene. All capsules (except nodal but I will come to them latter) when they are inserted into a scene, they become part of the scene as native objects and they are not assets anymore so there is no way to tell if it come from an asset or not.
For nodal one it is possible but very hacky for the moment, and while it should work I do not really advice to use it on production. So find bellow a way that will work on 2024.2.0 (only look at the main function the rest are hacks to get it working).import c4d import maxon class ASSETLICENSETYPE: ALLBITS = (1 << 30) | ((1 << 0) | (1 << 2) | (1 << 4) | (1 << 6) | (1 << 8) | (1 << 10) | (1 << 12)) | ((1 << 1) | (1 << 3) | (1 << 5) | (1 << 7) | (1 << 9) | (1 << 11) | (1 << 13)) ENABLE_IN_ALL = (1 << 0) | (1 << 2) | (1 << 4) | (1 << 6) | (1 << 8) | (1 << 10) | (1 << 12) ENABLE_IN_CINEWARE = 1 << 12 ENABLE_IN_EDUCATION = 1 << 10 ENABLE_IN_LITE = 1 << 6 ENABLE_IN_PERPETUAL = 1 << 2 ENABLE_IN_STUDENT = 1 << 8 ENABLE_IN_SUBSCRIPTION = 1 << 0 ENABLE_IN_TRIAL = 1 << 4 NONE = 0 ONLY_IN_MAXONONE = 1 << 30 SHOW_IN_ALL = (1 << 1) | (1 << 3) | (1 << 5) | (1 << 7) | (1 << 9) | (1 << 11) | (1 << 13) SHOW_IN_CINEWARE = 1 << 13 SHOW_IN_EDUCATION = 1 << 11 SHOW_IN_LITE = 1 << 7 SHOW_IN_PERPETUAL = 1 << 3 SHOW_IN_STUDENT = 1 << 9 SHOW_IN_SUBSCRIPTION = 1 << 1 SHOW_IN_TRIAL = 1 << 5 @maxon.MAXON_INTERFACE(maxon.MAXON_REFERENCE_COPY_ON_WRITE, "net.maxon.node.interface.nodetemplate") class NodeTemplateInterface(maxon.AssetInterface): pass @maxon.MAXON_REFERENCE(NodeTemplateInterface) class NodeTemplate(NodeTemplateInterface, maxon.Data): def __new__(cls, *args): return object.__new__(cls) @maxon.MAXON_INTERFACE(maxon.MAXON_REFERENCE_COPY_ON_WRITE, "net.maxon.node.interface.nodesystem") class NodeSystemInterface(maxon.ObjectInterface): @maxon.MAXON_METHOD('net.maxon.node.interface.nodesystem.GetTemplate@c849c0c06c1a1ac5') def GetTemplate(self): pass @maxon.MAXON_METHOD('net.maxon.node.interface.nodesystem.GetAllBases@4292dc8effe69932') def GetAllBases(self): pass @maxon.MAXON_REFERENCE(NodeSystemInterface) class NodeSystem(NodeSystemInterface, maxon.Data): def __new__(cls, *args): return object.__new__(cls) def GetAssetIdFromCapsule(bl2D): allNimbusRef = bl2D.GetAllNimbusRefs() if not allNimbusRef: raise ValueError("The given baselist 2D his is not a capsule") sceneNodeNimbus = allNimbusRef[0][1] graph = sceneNodeNimbus.GetGraph() rootNodeSystem = graph.GetBase(graph.GetRoot()) firstNodeSystemBase = [""] def GetFirstNodeSystemBase(nodeSystem): firstNodeSystemBase[0] = nodeSystem.GetTemplate().GetId() return False rootNodeSystem.GetAllBases(GetFirstNodeSystemBase) if not firstNodeSystemBase[0]: return rootNodeSystem.GetTemplate().GetId() return firstNodeSystemBase[0] def GetAssetDescription(assetId: str) -> maxon.AssetDescription: repository = maxon.AssetInterface.GetUserPrefsRepository() if not repository: raise RuntimeError("Could not access the user preferences repository.") assetDescription = repository.FindLatestAsset(maxon.AssetTypes.File(), assetId, maxon.Id(), maxon.ASSET_FIND_MODE.LATEST) if not assetDescription or assetDescription.IsNullValue(): assetDescription = repository.FindLatestAsset(maxon.AssetTypes.NodeTemplate(), assetId, maxon.Id(), maxon.ASSET_FIND_MODE.LATEST) if not assetDescription: raise RuntimeError(f"Could not find the '{assetId}' asset.") return assetDescription def TestLicenses(lic, flag): return bool(lic & flag) def main() -> None: capsuleAssetId = GetAssetIdFromCapsule(op) capsuleAssetDescription = GetAssetDescription(capsuleAssetId) capsuleLicense = capsuleAssetDescription.GetMetaData().Get(maxon.ASSETMETADATA.ValidLicenseType, ASSETLICENSETYPE.NONE) print(f"Asset Id: {capsuleAssetId}") if capsuleLicense != ASSETLICENSETYPE.NONE: # Test the license inAll = TestLicenses(capsuleLicense, ASSETLICENSETYPE.ENABLE_IN_ALL) sub = TestLicenses(capsuleLicense, ASSETLICENSETYPE.ENABLE_IN_SUBSCRIPTION) perpetual = TestLicenses(capsuleLicense, ASSETLICENSETYPE.ENABLE_IN_PERPETUAL) trial = TestLicenses(capsuleLicense, ASSETLICENSETYPE.ENABLE_IN_TRIAL) lite = TestLicenses(capsuleLicense, ASSETLICENSETYPE.ENABLE_IN_LITE) student = TestLicenses(capsuleLicense, ASSETLICENSETYPE.ENABLE_IN_STUDENT) edu = TestLicenses(capsuleLicense, ASSETLICENSETYPE.ENABLE_IN_EDUCATION) cineware = TestLicenses(capsuleLicense, ASSETLICENSETYPE.ENABLE_IN_CINEWARE) mxOne = TestLicenses(capsuleLicense, ASSETLICENSETYPE.ONLY_IN_MAXONONE) print(f"In All: {inAll}, Subscription: {sub}, Perpetual: {perpetual}, Trial: {trial}, Lite: {lite}, Student: {student}, Education: {edu}, Cineware: {cineware}, Maxon One: {mxOne}") if __name__ == '__main__': main()
For 2024.4 I plan to simplify the asset ID retrieval from a capsule. So thanks for the question and sorry for the delay.
Cheers,
Maxime. -
Thanks for the response.
Going through the code you have the line
assetDescription = repository.FindLatestAsset(maxon.AssetTypes.NodeTemplate(), assetId, maxon.Id(), maxon.ASSET_FIND_MODE.LATEST)
Looking at the sdk the NodeTemplate for AssetTypes first appears in 2024.2. Is there a way to tell if there is a Maxon One asset in 2023 or is this new functionality that isn't possible in older versions?
John Thomas
-
Hi correct in the previous version, there was an issue in our parser so this symbol was not exposed to Python.
But you can add the next line before
maxon.AssetTypes.NodeTemplate = maxon.MAXON_DECLARATION("net.maxon.node.assettype.nodetemplate")
Cheers,
Maxime. -
Thanks for the response. I'll see if this does what I need it to
John Thomas