Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Python: Detect Plugins

    Cinema 4D SDK
    2024 python windows
    3
    12
    2.2k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • J
      JohnThomas
      last edited by

      Hi.

      I am working on a Python project that is browsing through all of the objects and tags in a scene. As part of this I am trying to detect which of the objects and tags are plugins. In addition to this I am trying to detect if any of the things in the scene are Maxon One exclusive content.

      For a brute force method I know that I can compare the id of a given object or tag against a list of ids to tell if one of them is a plugin but I don't have a way of knowing if something is a plugin that I don't know the id of. I'm not overly familiar with Maxon One content but I assume that the same kind of method would apply for brute force detecting.

      So in summary my question is, is there a way to detect if a given object or tag in the scene is a plugin or Maxon One content that is not just comparing a list of ids against all of the content in a scene?

      Any help would be greatly appreciated,

      John Thomas

      1 Reply Last reply Reply Quote 0
      • M
        m_adam
        last edited by

        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.

        MAXON SDK Specialist

        Development Blog, MAXON Registered Developer

        ThomasBT 1 Reply Last reply Reply Quote 0
        • J
          JohnThomas
          last edited by

          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

          1 Reply Last reply Reply Quote 0
          • ThomasBT
            ThomasB @m_adam
            last edited by

            @m_adam
            Sorry, little question, what does the python extenstion "pype" mean?

            Thanks,
            T.S.B

            1 Reply Last reply Reply Quote 0
            • M
              m_adam
              last edited by

              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.

              MAXON SDK Specialist

              Development Blog, MAXON Registered Developer

              1 Reply Last reply Reply Quote 0
              • J
                JohnThomas
                last edited by

                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

                1 Reply Last reply Reply Quote 0
                • J
                  JohnThomas
                  last edited by

                  Any luck with detecting Maxon One?

                  John Thomas

                  1 Reply Last reply Reply Quote 0
                  • J
                    JohnThomas
                    last edited by

                    Any luck with detecting Maxon One?

                    John Thomas

                    1 Reply Last reply Reply Quote 0
                    • M
                      m_adam
                      last edited by m_adam

                      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.

                      MAXON SDK Specialist

                      Development Blog, MAXON Registered Developer

                      J 1 Reply Last reply Reply Quote 1
                      • J
                        JohnThomas @m_adam
                        last edited by

                        @m_adam

                        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

                        1 Reply Last reply Reply Quote 0
                        • M
                          m_adam
                          last edited by

                          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.

                          MAXON SDK Specialist

                          Development Blog, MAXON Registered Developer

                          1 Reply Last reply Reply Quote 0
                          • J
                            JohnThomas
                            last edited by

                            Thanks for the response. I'll see if this does what I need it to

                            John Thomas

                            1 Reply Last reply Reply Quote 0
                            • First post
                              Last post