ayon-python-api 1.2.11__tar.gz → 1.2.12__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/PKG-INFO +1 -1
  2. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/__init__.py +6 -0
  3. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api.py +138 -1
  4. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/bundles_addons.py +15 -0
  5. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/server_api.py +154 -19
  6. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/utils.py +28 -11
  7. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/version.py +1 -1
  8. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_python_api.egg-info/PKG-INFO +1 -1
  9. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/pyproject.toml +3 -3
  10. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/LICENSE +0 -0
  11. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/README.md +0 -0
  12. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/__init__.py +0 -0
  13. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/actions.py +0 -0
  14. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/activities.py +0 -0
  15. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/attributes.py +0 -0
  16. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/base.py +0 -0
  17. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/dependency_packages.py +0 -0
  18. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/events.py +0 -0
  19. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/folders.py +0 -0
  20. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/installers.py +0 -0
  21. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/links.py +0 -0
  22. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/lists.py +0 -0
  23. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/products.py +0 -0
  24. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/projects.py +0 -0
  25. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/representations.py +0 -0
  26. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/secrets.py +0 -0
  27. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/tasks.py +0 -0
  28. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/thumbnails.py +0 -0
  29. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/versions.py +0 -0
  30. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/_api_helpers/workfiles.py +0 -0
  31. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/constants.py +0 -0
  32. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/entity_hub.py +0 -0
  33. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/events.py +0 -0
  34. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/exceptions.py +0 -0
  35. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/graphql.py +0 -0
  36. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/graphql_queries.py +0 -0
  37. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/operations.py +0 -0
  38. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_api/typing.py +0 -0
  39. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_python_api.egg-info/SOURCES.txt +0 -0
  40. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_python_api.egg-info/dependency_links.txt +0 -0
  41. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_python_api.egg-info/requires.txt +0 -0
  42. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/ayon_python_api.egg-info/top_level.txt +0 -0
  43. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/setup.cfg +0 -0
  44. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/setup.py +0 -0
  45. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/tests/test_entity_hub.py +0 -0
  46. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/tests/test_folder_hierarchy.py +0 -0
  47. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/tests/test_get_events.py +0 -0
  48. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/tests/test_graphql_queries.py +0 -0
  49. {ayon_python_api-1.2.11 → ayon_python_api-1.2.12}/tests/test_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ayon_python_api
3
- Version: 1.2.11
3
+ Version: 1.2.12
4
4
  Summary: AYON Python API
5
5
  Home-page: https://github.com/ynput/ayon-python-api
6
6
  Author: ynput.io
@@ -70,8 +70,11 @@ from ._api import (
70
70
  delete,
71
71
  download_file_to_stream,
72
72
  download_file,
73
+ upload_project_file,
74
+ upload_project_file_from_stream,
73
75
  download_project_file,
74
76
  download_project_file_to_stream,
77
+ delete_project_file,
75
78
  upload_file_from_stream,
76
79
  upload_file,
77
80
  upload_reviewable,
@@ -351,8 +354,11 @@ __all__ = (
351
354
  "delete",
352
355
  "download_file_to_stream",
353
356
  "download_file",
357
+ "upload_project_file",
358
+ "upload_project_file_from_stream",
354
359
  "download_project_file",
355
360
  "download_project_file_to_stream",
361
+ "delete_project_file",
356
362
  "upload_file_from_stream",
357
363
  "upload_file",
358
364
  "upload_reviewable",
@@ -993,6 +993,99 @@ def download_file(
993
993
  )
994
994
 
995
995
 
996
+ def upload_project_file(
997
+ project_name: str,
998
+ filepath: str,
999
+ *,
1000
+ content_type: Optional[str] = None,
1001
+ filename: Optional[str] = None,
1002
+ file_id: Optional[str] = None,
1003
+ activity_id: Optional[str] = None,
1004
+ chunk_size: Optional[int] = None,
1005
+ progress: Optional[TransferProgress] = None,
1006
+ ) -> requests.Response:
1007
+ """Upload project file from a filepath.
1008
+
1009
+ Project files are usually binary files, such as images, videos,
1010
+ or other media files that can be accessed via api endpoint
1011
+ '{server url}/api/projects/{project_name}/files/{file_id}'.
1012
+
1013
+ Args:
1014
+ project_name (str): Project name.
1015
+ filepath (str): Path where file will be downloaded.
1016
+ content_type (Optional[str]): MIME type of file.
1017
+ filename (Optional[str]): Server filename, filename from filepath
1018
+ is used if not passed.
1019
+ file_id (Optional[str]): File id.
1020
+ activity_id (Optional[str]): To which activity is file related.
1021
+ chunk_size (Optional[int]): Size of chunks that are received
1022
+ in single loop.
1023
+ progress (Optional[TransferProgress]): Object that gives ability
1024
+ to track download progress.
1025
+
1026
+ Returns:
1027
+ requests.Response: Requests response.
1028
+
1029
+ """
1030
+ con = get_server_api_connection()
1031
+ return con.upload_project_file(
1032
+ project_name=project_name,
1033
+ filepath=filepath,
1034
+ content_type=content_type,
1035
+ filename=filename,
1036
+ file_id=file_id,
1037
+ activity_id=activity_id,
1038
+ chunk_size=chunk_size,
1039
+ progress=progress,
1040
+ )
1041
+
1042
+
1043
+ def upload_project_file_from_stream(
1044
+ project_name: str,
1045
+ stream: StreamType,
1046
+ filename: str,
1047
+ *,
1048
+ content_type: Optional[str] = None,
1049
+ file_id: Optional[str] = None,
1050
+ activity_id: Optional[str] = None,
1051
+ chunk_size: Optional[int] = None,
1052
+ progress: Optional[TransferProgress] = None,
1053
+ ) -> requests.Response:
1054
+ """Upload project file from a filepath.
1055
+
1056
+ Project files are usually binary files, such as images, videos,
1057
+ or other media files that can be accessed via api endpoint
1058
+ '{server url}/api/projects/{project_name}/files/{file_id}'.
1059
+
1060
+ Args:
1061
+ project_name (str): Project name.
1062
+ stream (StreamType): Stream used as source for upload.
1063
+ filename (str): Name of file on server.
1064
+ content_type (Optional[str]): MIME type of file.
1065
+ file_id (Optional[str]): File id.
1066
+ activity_id (Optional[str]): To which activity is file related.
1067
+ chunk_size (Optional[int]): Size of chunks that are received
1068
+ in single loop.
1069
+ progress (Optional[TransferProgress]): Object that gives ability
1070
+ to track download progress.
1071
+
1072
+ Returns:
1073
+ requests.Response: Requests response.
1074
+
1075
+ """
1076
+ con = get_server_api_connection()
1077
+ return con.upload_project_file_from_stream(
1078
+ project_name=project_name,
1079
+ stream=stream,
1080
+ filename=filename,
1081
+ content_type=content_type,
1082
+ file_id=file_id,
1083
+ activity_id=activity_id,
1084
+ chunk_size=chunk_size,
1085
+ progress=progress,
1086
+ )
1087
+
1088
+
996
1089
  def download_project_file(
997
1090
  project_name: str,
998
1091
  file_id: str,
@@ -1067,11 +1160,27 @@ def download_project_file_to_stream(
1067
1160
  )
1068
1161
 
1069
1162
 
1163
+ def delete_project_file(
1164
+ project_name: str,
1165
+ file_id: str,
1166
+ ) -> None:
1167
+ """Delete project file.
1168
+ """
1169
+ con = get_server_api_connection()
1170
+ return con.delete_project_file(
1171
+ project_name=project_name,
1172
+ file_id=file_id,
1173
+ )
1174
+
1175
+
1070
1176
  def upload_file_from_stream(
1071
1177
  endpoint: str,
1072
1178
  stream: StreamType,
1073
1179
  progress: Optional[TransferProgress] = None,
1074
1180
  request_type: Optional[RequestType] = None,
1181
+ *,
1182
+ content_type: Optional[str] = None,
1183
+ filename: Optional[str] = None,
1075
1184
  **kwargs,
1076
1185
  ) -> requests.Response:
1077
1186
  """Upload file to server from bytes.
@@ -1087,6 +1196,8 @@ def upload_file_from_stream(
1087
1196
  to track upload progress.
1088
1197
  request_type (Optional[RequestType]): Type of request that will
1089
1198
  be used to upload file.
1199
+ content_type (Optional[str]): MIME type of the file.
1200
+ filename (Optional[str]): Filename of file on server.
1090
1201
  **kwargs (Any): Additional arguments that will be passed
1091
1202
  to request function.
1092
1203
 
@@ -1100,6 +1211,8 @@ def upload_file_from_stream(
1100
1211
  stream=stream,
1101
1212
  progress=progress,
1102
1213
  request_type=request_type,
1214
+ content_type=content_type,
1215
+ filename=filename,
1103
1216
  **kwargs,
1104
1217
  )
1105
1218
 
@@ -1109,6 +1222,9 @@ def upload_file(
1109
1222
  filepath: str,
1110
1223
  progress: Optional[TransferProgress] = None,
1111
1224
  request_type: Optional[RequestType] = None,
1225
+ *,
1226
+ content_type: Optional[str] = None,
1227
+ filename: Optional[str] = None,
1112
1228
  **kwargs,
1113
1229
  ) -> requests.Response:
1114
1230
  """Upload file to server.
@@ -1124,6 +1240,8 @@ def upload_file(
1124
1240
  to track upload progress.
1125
1241
  request_type (Optional[RequestType]): Type of request that will
1126
1242
  be used to upload file.
1243
+ content_type (Optional[str]): MIME type of the file.
1244
+ filename (Optional[str]): Filename of file on server.
1127
1245
  **kwargs (Any): Additional arguments that will be passed
1128
1246
  to request function.
1129
1247
 
@@ -1137,6 +1255,8 @@ def upload_file(
1137
1255
  filepath=filepath,
1138
1256
  progress=progress,
1139
1257
  request_type=request_type,
1258
+ content_type=content_type,
1259
+ filename=filename,
1140
1260
  **kwargs,
1141
1261
  )
1142
1262
 
@@ -2889,6 +3009,7 @@ def get_addon_site_settings(
2889
3009
  def get_bundle_settings(
2890
3010
  bundle_name: Optional[str] = None,
2891
3011
  project_name: Optional[str] = None,
3012
+ project_bundle_name: Optional[str] = None,
2892
3013
  variant: Optional[str] = None,
2893
3014
  site_id: Optional[str] = None,
2894
3015
  use_site: bool = True,
@@ -2928,6 +3049,7 @@ def get_bundle_settings(
2928
3049
  return con.get_bundle_settings(
2929
3050
  bundle_name=bundle_name,
2930
3051
  project_name=project_name,
3052
+ project_bundle_name=project_bundle_name,
2931
3053
  variant=variant,
2932
3054
  site_id=site_id,
2933
3055
  use_site=use_site,
@@ -2936,6 +3058,7 @@ def get_bundle_settings(
2936
3058
 
2937
3059
  def get_addons_studio_settings(
2938
3060
  bundle_name: Optional[str] = None,
3061
+ project_bundle_name: Optional[str] = None,
2939
3062
  variant: Optional[str] = None,
2940
3063
  site_id: Optional[str] = None,
2941
3064
  use_site: bool = True,
@@ -2951,6 +3074,8 @@ def get_addons_studio_settings(
2951
3074
  Args:
2952
3075
  bundle_name (Optional[str]): Name of bundle for which should be
2953
3076
  settings received.
3077
+ project_bundle_name (Optional[str]): Project bundle name for
3078
+ which should be settings received.
2954
3079
  variant (Optional[Literal['production', 'staging']]): Name of
2955
3080
  settings variant. Used 'default_settings_variant' by default.
2956
3081
  site_id (Optional[str]): Site id for which want to receive
@@ -2968,6 +3093,7 @@ def get_addons_studio_settings(
2968
3093
  con = get_server_api_connection()
2969
3094
  return con.get_addons_studio_settings(
2970
3095
  bundle_name=bundle_name,
3096
+ project_bundle_name=project_bundle_name,
2971
3097
  variant=variant,
2972
3098
  site_id=site_id,
2973
3099
  use_site=use_site,
@@ -2978,6 +3104,7 @@ def get_addons_studio_settings(
2978
3104
  def get_addons_project_settings(
2979
3105
  project_name: str,
2980
3106
  bundle_name: Optional[str] = None,
3107
+ project_bundle_name: Optional[str] = None,
2981
3108
  variant: Optional[str] = None,
2982
3109
  site_id: Optional[str] = None,
2983
3110
  use_site: bool = True,
@@ -3009,6 +3136,8 @@ def get_addons_project_settings(
3009
3136
  received.
3010
3137
  bundle_name (Optional[str]): Name of bundle for which should be
3011
3138
  settings received.
3139
+ project_bundle_name (Optional[str]): Project bundle name for which
3140
+ should be settings received.
3012
3141
  variant (Optional[Literal['production', 'staging']]): Name of
3013
3142
  settings variant. Used 'default_settings_variant' by default.
3014
3143
  site_id (Optional[str]): Site id for which want to receive
@@ -3028,6 +3157,7 @@ def get_addons_project_settings(
3028
3157
  return con.get_addons_project_settings(
3029
3158
  project_name=project_name,
3030
3159
  bundle_name=bundle_name,
3160
+ project_bundle_name=project_bundle_name,
3031
3161
  variant=variant,
3032
3162
  site_id=site_id,
3033
3163
  use_site=use_site,
@@ -3037,6 +3167,7 @@ def get_addons_project_settings(
3037
3167
 
3038
3168
  def get_addons_settings(
3039
3169
  bundle_name: Optional[str] = None,
3170
+ project_bundle_name: Optional[str] = None,
3040
3171
  project_name: Optional[str] = None,
3041
3172
  variant: Optional[str] = None,
3042
3173
  site_id: Optional[str] = None,
@@ -3056,6 +3187,8 @@ def get_addons_settings(
3056
3187
  Args:
3057
3188
  bundle_name (Optional[str]): Name of bundle for which should be
3058
3189
  settings received.
3190
+ project_bundle_name (Optional[str]): Name of project bundle
3191
+ for which should be settings received.
3059
3192
  project_name (Optional[str]): Name of project for which should be
3060
3193
  settings received.
3061
3194
  variant (Optional[Literal['production', 'staging']]): Name of
@@ -3072,6 +3205,7 @@ def get_addons_settings(
3072
3205
  con = get_server_api_connection()
3073
3206
  return con.get_addons_settings(
3074
3207
  bundle_name=bundle_name,
3208
+ project_bundle_name=project_bundle_name,
3075
3209
  project_name=project_name,
3076
3210
  variant=variant,
3077
3211
  site_id=site_id,
@@ -6887,6 +7021,7 @@ def create_link(
6887
7021
  output_id: str,
6888
7022
  output_type: str,
6889
7023
  link_name: Optional[str] = None,
7024
+ data: Optional[dict[str, Any]] = None,
6890
7025
  ) -> CreateLinkData:
6891
7026
  """Create link between 2 entities.
6892
7027
 
@@ -6906,7 +7041,8 @@ def create_link(
6906
7041
  output_id (str): Output entity id.
6907
7042
  output_type (str): Entity type of output entity.
6908
7043
  link_name (Optional[str]): Name of link.
6909
- Available from server version '1.0.0-rc.6'.
7044
+ data (Optional[dict[str, Any]]): Additional data to be stored
7045
+ with the link.
6910
7046
 
6911
7047
  Returns:
6912
7048
  CreateLinkData: Information about link.
@@ -6924,6 +7060,7 @@ def create_link(
6924
7060
  output_id=output_id,
6925
7061
  output_type=output_type,
6926
7062
  link_name=link_name,
7063
+ data=data,
6927
7064
  )
6928
7065
 
6929
7066
 
@@ -671,6 +671,7 @@ class BundlesAddonsAPI(BaseServerAPI):
671
671
  self,
672
672
  bundle_name: Optional[str] = None,
673
673
  project_name: Optional[str] = None,
674
+ project_bundle_name: Optional[str] = None,
674
675
  variant: Optional[str] = None,
675
676
  site_id: Optional[str] = None,
676
677
  use_site: bool = True,
@@ -714,6 +715,7 @@ class BundlesAddonsAPI(BaseServerAPI):
714
715
  query = prepare_query_string({
715
716
  "project_name": project_name or None,
716
717
  "bundle_name": bundle_name or None,
718
+ "project_bundle_name": project_bundle_name or None,
717
719
  "variant": variant or self.get_default_settings_variant() or None,
718
720
  "site_id": site_id,
719
721
  })
@@ -724,6 +726,7 @@ class BundlesAddonsAPI(BaseServerAPI):
724
726
  def get_addons_studio_settings(
725
727
  self,
726
728
  bundle_name: Optional[str] = None,
729
+ project_bundle_name: Optional[str] = None,
727
730
  variant: Optional[str] = None,
728
731
  site_id: Optional[str] = None,
729
732
  use_site: bool = True,
@@ -739,6 +742,8 @@ class BundlesAddonsAPI(BaseServerAPI):
739
742
  Args:
740
743
  bundle_name (Optional[str]): Name of bundle for which should be
741
744
  settings received.
745
+ project_bundle_name (Optional[str]): Project bundle name for
746
+ which should be settings received.
742
747
  variant (Optional[Literal['production', 'staging']]): Name of
743
748
  settings variant. Used 'default_settings_variant' by default.
744
749
  site_id (Optional[str]): Site id for which want to receive
@@ -755,6 +760,7 @@ class BundlesAddonsAPI(BaseServerAPI):
755
760
  """
756
761
  output = self.get_bundle_settings(
757
762
  bundle_name=bundle_name,
763
+ project_bundle_name=project_bundle_name,
758
764
  variant=variant,
759
765
  site_id=site_id,
760
766
  use_site=use_site
@@ -770,6 +776,7 @@ class BundlesAddonsAPI(BaseServerAPI):
770
776
  self,
771
777
  project_name: str,
772
778
  bundle_name: Optional[str] = None,
779
+ project_bundle_name: Optional[str] = None,
773
780
  variant: Optional[str] = None,
774
781
  site_id: Optional[str] = None,
775
782
  use_site: bool = True,
@@ -801,6 +808,8 @@ class BundlesAddonsAPI(BaseServerAPI):
801
808
  received.
802
809
  bundle_name (Optional[str]): Name of bundle for which should be
803
810
  settings received.
811
+ project_bundle_name (Optional[str]): Project bundle name for which
812
+ should be settings received.
804
813
  variant (Optional[Literal['production', 'staging']]): Name of
805
814
  settings variant. Used 'default_settings_variant' by default.
806
815
  site_id (Optional[str]): Site id for which want to receive
@@ -822,6 +831,7 @@ class BundlesAddonsAPI(BaseServerAPI):
822
831
  output = self.get_bundle_settings(
823
832
  project_name=project_name,
824
833
  bundle_name=bundle_name,
834
+ project_bundle_name=project_bundle_name,
825
835
  variant=variant,
826
836
  site_id=site_id,
827
837
  use_site=use_site
@@ -836,6 +846,7 @@ class BundlesAddonsAPI(BaseServerAPI):
836
846
  def get_addons_settings(
837
847
  self,
838
848
  bundle_name: Optional[str] = None,
849
+ project_bundle_name: Optional[str] = None,
839
850
  project_name: Optional[str] = None,
840
851
  variant: Optional[str] = None,
841
852
  site_id: Optional[str] = None,
@@ -855,6 +866,8 @@ class BundlesAddonsAPI(BaseServerAPI):
855
866
  Args:
856
867
  bundle_name (Optional[str]): Name of bundle for which should be
857
868
  settings received.
869
+ project_bundle_name (Optional[str]): Name of project bundle
870
+ for which should be settings received.
858
871
  project_name (Optional[str]): Name of project for which should be
859
872
  settings received.
860
873
  variant (Optional[Literal['production', 'staging']]): Name of
@@ -871,6 +884,7 @@ class BundlesAddonsAPI(BaseServerAPI):
871
884
  if project_name is None:
872
885
  return self.get_addons_studio_settings(
873
886
  bundle_name=bundle_name,
887
+ project_bundle_name=project_bundle_name,
874
888
  variant=variant,
875
889
  site_id=site_id,
876
890
  use_site=use_site,
@@ -880,6 +894,7 @@ class BundlesAddonsAPI(BaseServerAPI):
880
894
  return self.get_addons_project_settings(
881
895
  project_name=project_name,
882
896
  bundle_name=bundle_name,
897
+ project_bundle_name=project_bundle_name,
883
898
  variant=variant,
884
899
  site_id=site_id,
885
900
  use_site=use_site,
@@ -62,6 +62,7 @@ from .utils import (
62
62
  get_default_site_id,
63
63
  NOT_SET,
64
64
  get_media_mime_type,
65
+ get_media_mime_type_for_stream,
65
66
  get_machine_name,
66
67
  fill_own_attribs,
67
68
  )
@@ -1414,7 +1415,7 @@ class ServerAPI(
1414
1415
  if api_prepended:
1415
1416
  self.log.warning(
1416
1417
  f"Auto-fixed endpoint '{endpoint}' -> 'api/{endpoint}'."
1417
- " Please fix the endpoit passed to the function."
1418
+ " Please fix the endpoint passed to the function."
1418
1419
  )
1419
1420
 
1420
1421
  def download_file_to_stream(
@@ -1517,6 +1518,117 @@ class ServerAPI(
1517
1518
 
1518
1519
  return progress
1519
1520
 
1521
+ def upload_project_file(
1522
+ self,
1523
+ project_name: str,
1524
+ filepath: str,
1525
+ *,
1526
+ content_type: Optional[str] = None,
1527
+ filename: Optional[str] = None,
1528
+ file_id: Optional[str] = None,
1529
+ activity_id: Optional[str] = None,
1530
+ chunk_size: Optional[int] = None,
1531
+ progress: Optional[TransferProgress] = None,
1532
+ ) -> requests.Response:
1533
+ """Upload project file from a filepath.
1534
+
1535
+ Project files are usually binary files, such as images, videos,
1536
+ or other media files that can be accessed via api endpoint
1537
+ '{server url}/api/projects/{project_name}/files/{file_id}'.
1538
+
1539
+ Args:
1540
+ project_name (str): Project name.
1541
+ filepath (str): Path where file will be downloaded.
1542
+ content_type (Optional[str]): MIME type of file.
1543
+ filename (Optional[str]): Server filename, filename from filepath
1544
+ is used if not passed.
1545
+ file_id (Optional[str]): File id.
1546
+ activity_id (Optional[str]): To which activity is file related.
1547
+ chunk_size (Optional[int]): Size of chunks that are received
1548
+ in single loop.
1549
+ progress (Optional[TransferProgress]): Object that gives ability
1550
+ to track download progress.
1551
+
1552
+ Returns:
1553
+ requests.Response: Requests response.
1554
+
1555
+ """
1556
+ if not filename:
1557
+ filename = os.path.basename(filepath)
1558
+
1559
+ if not content_type:
1560
+ content_type = get_media_mime_type(filepath)
1561
+ if not content_type:
1562
+ content_type = "application/octet-stream"
1563
+
1564
+ query = prepare_query_string({
1565
+ "x_file_id": file_id,
1566
+ "x_activity_id": activity_id,
1567
+ })
1568
+ return self.upload_file(
1569
+ f"api/projects/{project_name}/files{query}",
1570
+ filepath,
1571
+ content_type=content_type,
1572
+ filename=filename,
1573
+ chunk_size=chunk_size,
1574
+ progress=progress,
1575
+ request_type=RequestTypes.post,
1576
+ )
1577
+
1578
+ def upload_project_file_from_stream(
1579
+ self,
1580
+ project_name: str,
1581
+ stream: StreamType,
1582
+ filename: str,
1583
+ *,
1584
+ content_type: Optional[str] = None,
1585
+ file_id: Optional[str] = None,
1586
+ activity_id: Optional[str] = None,
1587
+ chunk_size: Optional[int] = None,
1588
+ progress: Optional[TransferProgress] = None,
1589
+ ) -> requests.Response:
1590
+ """Upload project file from a filepath.
1591
+
1592
+ Project files are usually binary files, such as images, videos,
1593
+ or other media files that can be accessed via api endpoint
1594
+ '{server url}/api/projects/{project_name}/files/{file_id}'.
1595
+
1596
+ Args:
1597
+ project_name (str): Project name.
1598
+ stream (StreamType): Stream used as source for upload.
1599
+ filename (str): Name of file on server.
1600
+ content_type (Optional[str]): MIME type of file.
1601
+ file_id (Optional[str]): File id.
1602
+ activity_id (Optional[str]): To which activity is file related.
1603
+ chunk_size (Optional[int]): Size of chunks that are received
1604
+ in single loop.
1605
+ progress (Optional[TransferProgress]): Object that gives ability
1606
+ to track download progress.
1607
+
1608
+ Returns:
1609
+ requests.Response: Requests response.
1610
+
1611
+ """
1612
+ if not content_type:
1613
+ stream.seek(0)
1614
+ content_type = get_media_mime_type_for_stream(stream)
1615
+ if not content_type:
1616
+ content_type = "application/octet-stream"
1617
+
1618
+ query = prepare_query_string({
1619
+ "x_file_id": file_id,
1620
+ "x_activity_id": activity_id,
1621
+ })
1622
+ return self.upload_file_from_stream(
1623
+ f"api/projects/{project_name}/files{query}",
1624
+ stream,
1625
+ content_type=content_type,
1626
+ filename=filename,
1627
+ chunk_size=chunk_size,
1628
+ progress=progress,
1629
+ request_type=RequestTypes.post,
1630
+ )
1631
+
1520
1632
  def download_project_file(
1521
1633
  self,
1522
1634
  project_name: str,
@@ -1587,6 +1699,11 @@ class ServerAPI(
1587
1699
  progress=progress,
1588
1700
  )
1589
1701
 
1702
+ def delete_project_file(self, project_name: str, file_id: str) -> None:
1703
+ """Delete project file."""
1704
+ response = self.delete(f"projects/{project_name}/files/{file_id}")
1705
+ response.raise_for_status()
1706
+
1590
1707
  @staticmethod
1591
1708
  def _upload_chunks_iter(
1592
1709
  file_stream: StreamType,
@@ -1619,6 +1736,9 @@ class ServerAPI(
1619
1736
  progress: TransferProgress,
1620
1737
  request_type: Optional[RequestType] = None,
1621
1738
  chunk_size: Optional[int] = None,
1739
+ *,
1740
+ content_type: Optional[str] = None,
1741
+ filename: Optional[str] = None,
1622
1742
  **kwargs
1623
1743
  ) -> requests.Response:
1624
1744
  """Upload file to server.
@@ -1646,11 +1766,14 @@ class ServerAPI(
1646
1766
  url = self._endpoint_to_url(endpoint, use_rest=False)
1647
1767
  progress.set_destination_url(url)
1648
1768
 
1769
+ headers = kwargs.setdefault("headers", {})
1770
+ headers_keys_by_low_key = {key.lower(): key for key in headers}
1649
1771
  if self._session is None:
1650
- headers = kwargs.setdefault("headers", {})
1651
1772
  for key, value in self.get_headers().items():
1652
- if key not in headers:
1773
+ orig_key = headers_keys_by_low_key.get(key)
1774
+ if not orig_key:
1653
1775
  headers[key] = value
1776
+
1654
1777
  post_func = self._base_functions_mapping[request_type]
1655
1778
  else:
1656
1779
  post_func = self._session_functions_mapping[request_type]
@@ -1658,6 +1781,17 @@ class ServerAPI(
1658
1781
  if not chunk_size:
1659
1782
  chunk_size = self.default_upload_chunk_size
1660
1783
 
1784
+ for key, value in (
1785
+ ("x-file-name", filename),
1786
+ ("Content-Type", content_type),
1787
+ ):
1788
+ if not value:
1789
+ continue
1790
+ orig_key = headers_keys_by_low_key.get(key.lower())
1791
+ if orig_key:
1792
+ headers.pop(orig_key)
1793
+ headers[key] = value
1794
+
1661
1795
  retries = self.get_default_max_retries()
1662
1796
  response = None
1663
1797
 
@@ -1702,7 +1836,7 @@ class ServerAPI(
1702
1836
  if api_prepended:
1703
1837
  self.log.warning(
1704
1838
  f"Auto-fixed endpoint '{endpoint}' -> 'api/{endpoint}'."
1705
- " Please fix the endpoit passed to the function."
1839
+ " Please fix the endpoint passed to the function."
1706
1840
  )
1707
1841
  return response
1708
1842
 
@@ -1712,6 +1846,9 @@ class ServerAPI(
1712
1846
  stream: StreamType,
1713
1847
  progress: Optional[TransferProgress] = None,
1714
1848
  request_type: Optional[RequestType] = None,
1849
+ *,
1850
+ content_type: Optional[str] = None,
1851
+ filename: Optional[str] = None,
1715
1852
  **kwargs
1716
1853
  ) -> requests.Response:
1717
1854
  """Upload file to server from bytes.
@@ -1727,6 +1864,8 @@ class ServerAPI(
1727
1864
  to track upload progress.
1728
1865
  request_type (Optional[RequestType]): Type of request that will
1729
1866
  be used to upload file.
1867
+ content_type (Optional[str]): MIME type of the file.
1868
+ filename (Optional[str]): Filename of file on server.
1730
1869
  **kwargs (Any): Additional arguments that will be passed
1731
1870
  to request function.
1732
1871
 
@@ -1747,6 +1886,8 @@ class ServerAPI(
1747
1886
  stream,
1748
1887
  progress,
1749
1888
  request_type,
1889
+ content_type=content_type,
1890
+ filename=filename,
1750
1891
  **kwargs
1751
1892
  )
1752
1893
 
@@ -1763,6 +1904,9 @@ class ServerAPI(
1763
1904
  filepath: str,
1764
1905
  progress: Optional[TransferProgress] = None,
1765
1906
  request_type: Optional[RequestType] = None,
1907
+ *,
1908
+ content_type: Optional[str] = None,
1909
+ filename: Optional[str] = None,
1766
1910
  **kwargs
1767
1911
  ) -> requests.Response:
1768
1912
  """Upload file to server.
@@ -1778,6 +1922,8 @@ class ServerAPI(
1778
1922
  to track upload progress.
1779
1923
  request_type (Optional[RequestType]): Type of request that will
1780
1924
  be used to upload file.
1925
+ content_type (Optional[str]): MIME type of the file.
1926
+ filename (Optional[str]): Filename of file on server.
1781
1927
  **kwargs (Any): Additional arguments that will be passed
1782
1928
  to request function.
1783
1929
 
@@ -1796,6 +1942,8 @@ class ServerAPI(
1796
1942
  stream,
1797
1943
  progress,
1798
1944
  request_type,
1945
+ content_type=content_type,
1946
+ filename=filename,
1799
1947
  **kwargs
1800
1948
  )
1801
1949
 
@@ -1837,24 +1985,9 @@ class ServerAPI(
1837
1985
  f"Could not determine MIME type of file '{filepath}'"
1838
1986
  )
1839
1987
 
1840
- if headers is None:
1841
- headers = self.get_headers(content_type)
1842
- else:
1843
- # Make sure content-type is filled with file content type
1844
- content_type_key = next(
1845
- (
1846
- key
1847
- for key in headers
1848
- if key.lower() == "content-type"
1849
- ),
1850
- "Content-Type"
1851
- )
1852
- headers[content_type_key] = content_type
1853
-
1854
1988
  # Fill original filename if not explicitly defined
1855
1989
  if not filename:
1856
1990
  filename = os.path.basename(filepath)
1857
- headers["x-file-name"] = filename
1858
1991
 
1859
1992
  query = prepare_query_string({"label": label or None})
1860
1993
  endpoint = (
@@ -1865,6 +1998,8 @@ class ServerAPI(
1865
1998
  endpoint,
1866
1999
  filepath,
1867
2000
  progress=progress,
2001
+ content_type=content_type,
2002
+ filename=filename,
1868
2003
  headers=headers,
1869
2004
  request_type=RequestTypes.post,
1870
2005
  **kwargs
@@ -1118,12 +1118,12 @@ def _get_media_mime_type_for_content_base(content: bytes) -> Optional[str]:
1118
1118
  content_len = len(content)
1119
1119
  # Pre-validation (largest definition check)
1120
1120
  # - hopefully there cannot be media defined in less than 12 bytes
1121
- if content_len < 12:
1121
+ if content_len < 4:
1122
1122
  return None
1123
1123
 
1124
- # FTYP
1125
- if content[4:8] == b"ftyp":
1126
- return _get_media_mime_type_from_ftyp(content)
1124
+ # PDF
1125
+ if content[0:4] == b"%PDF":
1126
+ return "application/pdf"
1127
1127
 
1128
1128
  # BMP
1129
1129
  if content[0:2] == b"BM":
@@ -1162,6 +1162,14 @@ def _get_media_mime_type_for_content_base(content: bytes) -> Optional[str]:
1162
1162
  # with this header
1163
1163
  if content[0:4] == b"\x00\x00\x01\x00":
1164
1164
  return "image/x-icon"
1165
+
1166
+ if content_len < 8:
1167
+ return None
1168
+
1169
+ # FTYP
1170
+ if content[4:8] == b"ftyp":
1171
+ return _get_media_mime_type_from_ftyp(content)
1172
+
1165
1173
  return None
1166
1174
 
1167
1175
 
@@ -1172,23 +1180,32 @@ def _get_svg_mime_type(content: bytes) -> Optional[str]:
1172
1180
  return None
1173
1181
 
1174
1182
 
1183
+ def _get_json_mime_type(content: bytes) -> Optional[str]:
1184
+ # json
1185
+ try:
1186
+ json.loads(content.decode("utf-8"))
1187
+ return "application/json"
1188
+ except (UnicodeDecodeError, ValueError):
1189
+ pass
1190
+ return None
1191
+
1192
+
1175
1193
  def get_media_mime_type_for_content(content: bytes) -> Optional[str]:
1176
1194
  mime_type = _get_media_mime_type_for_content_base(content)
1177
1195
  if mime_type is not None:
1178
1196
  return mime_type
1179
- return _get_svg_mime_type(content)
1197
+ return _get_svg_mime_type(content) or _get_json_mime_type(content)
1180
1198
 
1181
1199
 
1182
1200
  def get_media_mime_type_for_stream(stream: StreamType) -> Optional[str]:
1183
1201
  # Read only 12 bytes to determine mime type
1184
1202
  content = stream.read(12)
1185
- if len(content) < 12:
1186
- return None
1187
1203
  mime_type = _get_media_mime_type_for_content_base(content)
1188
- if mime_type is None:
1189
- content += stream.read()
1190
- mime_type = _get_svg_mime_type(content)
1191
- return mime_type
1204
+ if mime_type is not None:
1205
+ return mime_type
1206
+
1207
+ content += stream.read()
1208
+ return _get_svg_mime_type(content) or _get_json_mime_type(content)
1192
1209
 
1193
1210
 
1194
1211
  def get_media_mime_type(filepath: str) -> Optional[str]:
@@ -1,2 +1,2 @@
1
1
  """Package declaring Python API for AYON server."""
2
- __version__ = "1.2.11"
2
+ __version__ = "1.2.12"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ayon_python_api
3
- Version: 1.2.11
3
+ Version: 1.2.12
4
4
  Summary: AYON Python API
5
5
  Home-page: https://github.com/ynput/ayon-python-api
6
6
  Author: ynput.io
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ayon_python_api"
3
- version = "1.2.11"
3
+ version = "1.2.12"
4
4
  description = "AYON Python API"
5
5
  license = {file = "LICENSE"}
6
6
  readme = {file = "README.md", content-type = "text/markdown"}
@@ -28,7 +28,7 @@ build-backend = "poetry.core.masonry.api"
28
28
 
29
29
  [tool.poetry]
30
30
  name = "ayon_python_api"
31
- version = "1.2.2"
31
+ version = "1.2.12"
32
32
  description = "AYON Python API"
33
33
  authors = [
34
34
  "ynput.io <info@ynput.io>"
@@ -49,4 +49,4 @@ mock = "*"
49
49
  sphinx-autoapi = "*"
50
50
  revitron-sphinx-theme = { git = "https://github.com/revitron/revitron-sphinx-theme.git", branch = "master" }
51
51
  pytest = "^6.2.5"
52
- pydocstyle = "^6.3.0"
52
+ pydocstyle = "^6.3.0"