mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-08-22 06:13:25 -07:00
Update cloudinary==1.39.1
This commit is contained in:
parent
edb079279d
commit
f27f5acb64
14 changed files with 605 additions and 181 deletions
|
@ -1,4 +1,4 @@
|
|||
from .core import contents, where
|
||||
|
||||
__all__ = ["contents", "where"]
|
||||
__version__ = "2023.07.22"
|
||||
__version__ = "2024.02.02"
|
||||
|
|
|
@ -245,34 +245,6 @@ mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK
|
|||
4SVhM7JZG+Ju1zdXtg2pEto=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: O=SECOM Trust.net OU=Security Communication RootCA1
|
||||
# Subject: O=SECOM Trust.net OU=Security Communication RootCA1
|
||||
# Label: "Security Communication Root CA"
|
||||
# Serial: 0
|
||||
# MD5 Fingerprint: f1:bc:63:6a:54:e0:b5:27:f5:cd:e7:1a:e3:4d:6e:4a
|
||||
# SHA1 Fingerprint: 36:b1:2b:49:f9:81:9e:d7:4c:9e:bc:38:0f:c6:56:8f:5d:ac:b2:f7
|
||||
# SHA256 Fingerprint: e7:5e:72:ed:9f:56:0e:ec:6e:b4:80:00:73:a4:3f:c3:ad:19:19:5a:39:22:82:01:78:95:97:4a:99:02:6b:6c
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY
|
||||
MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t
|
||||
dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5
|
||||
WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD
|
||||
VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8
|
||||
9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ
|
||||
DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9
|
||||
Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N
|
||||
QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ
|
||||
xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G
|
||||
A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T
|
||||
AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG
|
||||
kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr
|
||||
Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5
|
||||
Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU
|
||||
JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot
|
||||
RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com
|
||||
# Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com
|
||||
# Label: "XRamp Global CA Root"
|
||||
|
@ -881,49 +853,6 @@ Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
|
|||
WD9f
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068
|
||||
# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068
|
||||
# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068"
|
||||
# Serial: 6047274297262753887
|
||||
# MD5 Fingerprint: 73:3a:74:7a:ec:bb:a3:96:a6:c2:e4:e2:c8:9b:c0:c3
|
||||
# SHA1 Fingerprint: ae:c5:fb:3f:c8:e1:bf:c4:e5:4f:03:07:5a:9a:e8:00:b7:f7:b6:fa
|
||||
# SHA256 Fingerprint: 04:04:80:28:bf:1f:28:64:d4:8f:9a:d4:d8:32:94:36:6a:82:88:56:55:3f:3b:14:30:3f:90:14:7f:5d:40:ef
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Izenpe.com O=IZENPE S.A.
|
||||
# Subject: CN=Izenpe.com O=IZENPE S.A.
|
||||
# Label: "Izenpe.com"
|
||||
|
@ -4633,3 +4562,253 @@ o7Ey7Nmj1m+UI/87tyll5gfp77YZ6ufCOB0yiJA8EytuzO+rdwY0d4RPcuSBhPm5
|
|||
dDTedk+SKlOxJTnbPP/lPqYO5Wue/9vsL3SD3460s6neFE3/MaNFcyT6lSnMEpcE
|
||||
oji2jbDwN/zIIX8/syQbPYtuzE2wFg2WHYMfRsCbvUOZ58SWLs5fyQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=TrustAsia Global Root CA G3 O=TrustAsia Technologies, Inc.
|
||||
# Subject: CN=TrustAsia Global Root CA G3 O=TrustAsia Technologies, Inc.
|
||||
# Label: "TrustAsia Global Root CA G3"
|
||||
# Serial: 576386314500428537169965010905813481816650257167
|
||||
# MD5 Fingerprint: 30:42:1b:b7:bb:81:75:35:e4:16:4f:53:d2:94:de:04
|
||||
# SHA1 Fingerprint: 63:cf:b6:c1:27:2b:56:e4:88:8e:1c:23:9a:b6:2e:81:47:24:c3:c7
|
||||
# SHA256 Fingerprint: e0:d3:22:6a:eb:11:63:c2:e4:8f:f9:be:3b:50:b4:c6:43:1b:e7:bb:1e:ac:c5:c3:6b:5d:5e:c5:09:03:9a:08
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFpTCCA42gAwIBAgIUZPYOZXdhaqs7tOqFhLuxibhxkw8wDQYJKoZIhvcNAQEM
|
||||
BQAwWjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dp
|
||||
ZXMsIEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHMzAe
|
||||
Fw0yMTA1MjAwMjEwMTlaFw00NjA1MTkwMjEwMTlaMFoxCzAJBgNVBAYTAkNOMSUw
|
||||
IwYDVQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtU
|
||||
cnVzdEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
|
||||
DwAwggIKAoICAQDAMYJhkuSUGwoqZdC+BqmHO1ES6nBBruL7dOoKjbmzTNyPtxNS
|
||||
T1QY4SxzlZHFZjtqz6xjbYdT8PfxObegQ2OwxANdV6nnRM7EoYNl9lA+sX4WuDqK
|
||||
AtCWHwDNBSHvBm3dIZwZQ0WhxeiAysKtQGIXBsaqvPPW5vxQfmZCHzyLpnl5hkA1
|
||||
nyDvP+uLRx+PjsXUjrYsyUQE49RDdT/VP68czH5GX6zfZBCK70bwkPAPLfSIC7Ep
|
||||
qq+FqklYqL9joDiR5rPmd2jE+SoZhLsO4fWvieylL1AgdB4SQXMeJNnKziyhWTXA
|
||||
yB1GJ2Faj/lN03J5Zh6fFZAhLf3ti1ZwA0pJPn9pMRJpxx5cynoTi+jm9WAPzJMs
|
||||
hH/x/Gr8m0ed262IPfN2dTPXS6TIi/n1Q1hPy8gDVI+lhXgEGvNz8teHHUGf59gX
|
||||
zhqcD0r83ERoVGjiQTz+LISGNzzNPy+i2+f3VANfWdP3kXjHi3dqFuVJhZBFcnAv
|
||||
kV34PmVACxmZySYgWmjBNb9Pp1Hx2BErW+Canig7CjoKH8GB5S7wprlppYiU5msT
|
||||
f9FkPz2ccEblooV7WIQn3MSAPmeamseaMQ4w7OYXQJXZRe0Blqq/DPNL0WP3E1jA
|
||||
uPP6Z92bfW1K/zJMtSU7/xxnD4UiWQWRkUF3gdCFTIcQcf+eQxuulXUtgQIDAQAB
|
||||
o2MwYTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEDk5PIj7zjKsK5Xf/Ih
|
||||
MBY027ySMB0GA1UdDgQWBBRA5OTyI+84yrCuV3/yITAWNNu8kjAOBgNVHQ8BAf8E
|
||||
BAMCAQYwDQYJKoZIhvcNAQEMBQADggIBACY7UeFNOPMyGLS0XuFlXsSUT9SnYaP4
|
||||
wM8zAQLpw6o1D/GUE3d3NZ4tVlFEbuHGLige/9rsR82XRBf34EzC4Xx8MnpmyFq2
|
||||
XFNFV1pF1AWZLy4jVe5jaN/TG3inEpQGAHUNcoTpLrxaatXeL1nHo+zSh2bbt1S1
|
||||
JKv0Q3jbSwTEb93mPmY+KfJLaHEih6D4sTNjduMNhXJEIlU/HHzp/LgV6FL6qj6j
|
||||
ITk1dImmasI5+njPtqzn59ZW/yOSLlALqbUHM/Q4X6RJpstlcHboCoWASzY9M/eV
|
||||
VHUl2qzEc4Jl6VL1XP04lQJqaTDFHApXB64ipCz5xUG3uOyfT0gA+QEEVcys+TIx
|
||||
xHWVBqB/0Y0n3bOppHKH/lmLmnp0Ft0WpWIp6zqW3IunaFnT63eROfjXy9mPX1on
|
||||
AX1daBli2MjN9LdyR75bl87yraKZk62Uy5P2EgmVtqvXO9A/EcswFi55gORngS1d
|
||||
7XB4tmBZrOFdRWOPyN9yaFvqHbgB8X7754qz41SgOAngPN5C8sLtLpvzHzW2Ntjj
|
||||
gKGLzZlkD8Kqq7HK9W+eQ42EVJmzbsASZthwEPEGNTNDqJwuuhQxzhB/HIbjj9LV
|
||||
+Hfsm6vxL2PZQl/gZ4FkkfGXL/xuJvYz+NO1+MRiqzFRJQJ6+N1rZdVtTTDIZbpo
|
||||
FGWsJwt0ivKH
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=TrustAsia Global Root CA G4 O=TrustAsia Technologies, Inc.
|
||||
# Subject: CN=TrustAsia Global Root CA G4 O=TrustAsia Technologies, Inc.
|
||||
# Label: "TrustAsia Global Root CA G4"
|
||||
# Serial: 451799571007117016466790293371524403291602933463
|
||||
# MD5 Fingerprint: 54:dd:b2:d7:5f:d8:3e:ed:7c:e0:0b:2e:cc:ed:eb:eb
|
||||
# SHA1 Fingerprint: 57:73:a5:61:5d:80:b2:e6:ac:38:82:fc:68:07:31:ac:9f:b5:92:5a
|
||||
# SHA256 Fingerprint: be:4b:56:cb:50:56:c0:13:6a:52:6d:f4:44:50:8d:aa:36:a0:b5:4f:42:e4:ac:38:f7:2a:f4:70:e4:79:65:4c
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICVTCCAdygAwIBAgIUTyNkuI6XY57GU4HBdk7LKnQV1tcwCgYIKoZIzj0EAwMw
|
||||
WjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dpZXMs
|
||||
IEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHNDAeFw0y
|
||||
MTA1MjAwMjEwMjJaFw00NjA1MTkwMjEwMjJaMFoxCzAJBgNVBAYTAkNOMSUwIwYD
|
||||
VQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtUcnVz
|
||||
dEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATx
|
||||
s8045CVD5d4ZCbuBeaIVXxVjAd7Cq92zphtnS4CDr5nLrBfbK5bKfFJV4hrhPVbw
|
||||
LxYI+hW8m7tH5j/uqOFMjPXTNvk4XatwmkcN4oFBButJ+bAp3TPsUKV/eSm4IJij
|
||||
YzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUpbtKl86zK3+kMd6Xg1mD
|
||||
pm9xy94wHQYDVR0OBBYEFKW7SpfOsyt/pDHel4NZg6ZvccveMA4GA1UdDwEB/wQE
|
||||
AwIBBjAKBggqhkjOPQQDAwNnADBkAjBe8usGzEkxn0AAbbd+NvBNEU/zy4k6LHiR
|
||||
UKNbwMp1JvK/kF0LgoxgKJ/GcJpo5PECMFxYDlZ2z1jD1xCMuo6u47xkdUfFVZDj
|
||||
/bpV6wfEU6s3qe4hsiFbYI89MvHVI5TWWA==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=CommScope Public Trust ECC Root-01 O=CommScope
|
||||
# Subject: CN=CommScope Public Trust ECC Root-01 O=CommScope
|
||||
# Label: "CommScope Public Trust ECC Root-01"
|
||||
# Serial: 385011430473757362783587124273108818652468453534
|
||||
# MD5 Fingerprint: 3a:40:a7:fc:03:8c:9c:38:79:2f:3a:a2:6c:b6:0a:16
|
||||
# SHA1 Fingerprint: 07:86:c0:d8:dd:8e:c0:80:98:06:98:d0:58:7a:ef:de:a6:cc:a2:5d
|
||||
# SHA256 Fingerprint: 11:43:7c:da:7b:b4:5e:41:36:5f:45:b3:9a:38:98:6b:0d:e0:0d:ef:34:8e:0c:7b:b0:87:36:33:80:0b:c3:8b
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICHTCCAaOgAwIBAgIUQ3CCd89NXTTxyq4yLzf39H91oJ4wCgYIKoZIzj0EAwMw
|
||||
TjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29t
|
||||
bVNjb3BlIFB1YmxpYyBUcnVzdCBFQ0MgUm9vdC0wMTAeFw0yMTA0MjgxNzM1NDNa
|
||||
Fw00NjA0MjgxNzM1NDJaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2Nv
|
||||
cGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgRUNDIFJvb3QtMDEw
|
||||
djAQBgcqhkjOPQIBBgUrgQQAIgNiAARLNumuV16ocNfQj3Rid8NeeqrltqLxeP0C
|
||||
flfdkXmcbLlSiFS8LwS+uM32ENEp7LXQoMPwiXAZu1FlxUOcw5tjnSCDPgYLpkJE
|
||||
hRGnSjot6dZoL0hOUysHP029uax3OVejQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
|
||||
VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSOB2LAUN3GGQYARnQE9/OufXVNMDAKBggq
|
||||
hkjOPQQDAwNoADBlAjEAnDPfQeMjqEI2Jpc1XHvr20v4qotzVRVcrHgpD7oh2MSg
|
||||
2NED3W3ROT3Ek2DS43KyAjB8xX6I01D1HiXo+k515liWpDVfG2XqYZpwI7UNo5uS
|
||||
Um9poIyNStDuiw7LR47QjRE=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=CommScope Public Trust ECC Root-02 O=CommScope
|
||||
# Subject: CN=CommScope Public Trust ECC Root-02 O=CommScope
|
||||
# Label: "CommScope Public Trust ECC Root-02"
|
||||
# Serial: 234015080301808452132356021271193974922492992893
|
||||
# MD5 Fingerprint: 59:b0:44:d5:65:4d:b8:5c:55:19:92:02:b6:d1:94:b2
|
||||
# SHA1 Fingerprint: 3c:3f:ef:57:0f:fe:65:93:86:9e:a0:fe:b0:f6:ed:8e:d1:13:c7:e5
|
||||
# SHA256 Fingerprint: 2f:fb:7f:81:3b:bb:b3:c8:9a:b4:e8:16:2d:0f:16:d7:15:09:a8:30:cc:9d:73:c2:62:e5:14:08:75:d1:ad:4a
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICHDCCAaOgAwIBAgIUKP2ZYEFHpgE6yhR7H+/5aAiDXX0wCgYIKoZIzj0EAwMw
|
||||
TjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29t
|
||||
bVNjb3BlIFB1YmxpYyBUcnVzdCBFQ0MgUm9vdC0wMjAeFw0yMTA0MjgxNzQ0NTRa
|
||||
Fw00NjA0MjgxNzQ0NTNaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2Nv
|
||||
cGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgRUNDIFJvb3QtMDIw
|
||||
djAQBgcqhkjOPQIBBgUrgQQAIgNiAAR4MIHoYx7l63FRD/cHB8o5mXxO1Q/MMDAL
|
||||
j2aTPs+9xYa9+bG3tD60B8jzljHz7aRP+KNOjSkVWLjVb3/ubCK1sK9IRQq9qEmU
|
||||
v4RDsNuESgMjGWdqb8FuvAY5N9GIIvejQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
|
||||
VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTmGHX/72DehKT1RsfeSlXjMjZ59TAKBggq
|
||||
hkjOPQQDAwNnADBkAjAmc0l6tqvmSfR9Uj/UQQSugEODZXW5hYA4O9Zv5JOGq4/n
|
||||
ich/m35rChJVYaoR4HkCMHfoMXGsPHED1oQmHhS48zs73u1Z/GtMMH9ZzkXpc2AV
|
||||
mkzw5l4lIhVtwodZ0LKOag==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=CommScope Public Trust RSA Root-01 O=CommScope
|
||||
# Subject: CN=CommScope Public Trust RSA Root-01 O=CommScope
|
||||
# Label: "CommScope Public Trust RSA Root-01"
|
||||
# Serial: 354030733275608256394402989253558293562031411421
|
||||
# MD5 Fingerprint: 0e:b4:15:bc:87:63:5d:5d:02:73:d4:26:38:68:73:d8
|
||||
# SHA1 Fingerprint: 6d:0a:5f:f7:b4:23:06:b4:85:b3:b7:97:64:fc:ac:75:f5:33:f2:93
|
||||
# SHA256 Fingerprint: 02:bd:f9:6e:2a:45:dd:9b:f1:8f:c7:e1:db:df:21:a0:37:9b:a3:c9:c2:61:03:44:cf:d8:d6:06:fe:c1:ed:81
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=CommScope Public Trust RSA Root-02 O=CommScope
|
||||
# Subject: CN=CommScope Public Trust RSA Root-02 O=CommScope
|
||||
# Label: "CommScope Public Trust RSA Root-02"
|
||||
# Serial: 480062499834624527752716769107743131258796508494
|
||||
# MD5 Fingerprint: e1:29:f9:62:7b:76:e2:96:6d:f3:d4:d7:0f:ae:1f:aa
|
||||
# SHA1 Fingerprint: ea:b0:e2:52:1b:89:93:4c:11:68:f2:d8:9a:ac:22:4c:a3:8a:57:ae
|
||||
# SHA256 Fingerprint: ff:e9:43:d7:93:42:4b:4f:7c:44:0c:1c:3d:64:8d:53:63:f3:4b:82:dc:87:aa:7a:9f:11:8f:c5:de:e1:01:f1
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFbDCCA1SgAwIBAgIUVBa/O345lXGN0aoApYYNK496BU4wDQYJKoZIhvcNAQEL
|
||||
BQAwTjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwi
|
||||
Q29tbVNjb3BlIFB1YmxpYyBUcnVzdCBSU0EgUm9vdC0wMjAeFw0yMTA0MjgxNzE2
|
||||
NDNaFw00NjA0MjgxNzE2NDJaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21t
|
||||
U2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgUlNBIFJvb3Qt
|
||||
MDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDh+g77aAASyE3VrCLE
|
||||
NQE7xVTlWXZjpX/rwcRqmL0yjReA61260WI9JSMZNRTpf4mnG2I81lDnNJUDMrG0
|
||||
kyI9p+Kx7eZ7Ti6Hmw0zdQreqjXnfuU2mKKuJZ6VszKWpCtYHu8//mI0SFHRtI1C
|
||||
rWDaSWqVcN3SAOLMV2MCe5bdSZdbkk6V0/nLKR8YSvgBKtJjCW4k6YnS5cciTNxz
|
||||
hkcAqg2Ijq6FfUrpuzNPDlJwnZXjfG2WWy09X6GDRl224yW4fKcZgBzqZUPckXk2
|
||||
LHR88mcGyYnJ27/aaL8j7dxrrSiDeS/sOKUNNwFnJ5rpM9kzXzehxfCrPfp4sOcs
|
||||
n/Y+n2Dg70jpkEUeBVF4GiwSLFworA2iI540jwXmojPOEXcT1A6kHkIfhs1w/tku
|
||||
FT0du7jyU1fbzMZ0KZwYszZ1OC4PVKH4kh+Jlk+71O6d6Ts2QrUKOyrUZHk2EOH5
|
||||
kQMreyBUzQ0ZGshBMjTRsJnhkB4BQDa1t/qp5Xd1pCKBXbCL5CcSD1SIxtuFdOa3
|
||||
wNemKfrb3vOTlycEVS8KbzfFPROvCgCpLIscgSjX74Yxqa7ybrjKaixUR9gqiC6v
|
||||
wQcQeKwRoi9C8DfF8rhW3Q5iLc4tVn5V8qdE9isy9COoR+jUKgF4z2rDN6ieZdIs
|
||||
5fq6M8EGRPbmz6UNp2YINIos8wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G
|
||||
A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUR9DnsSL/nSz12Vdgs7GxcJXvYXowDQYJ
|
||||
KoZIhvcNAQELBQADggIBAIZpsU0v6Z9PIpNojuQhmaPORVMbc0RTAIFhzTHjCLqB
|
||||
KCh6krm2qMhDnscTJk3C2OVVnJJdUNjCK9v+5qiXz1I6JMNlZFxHMaNlNRPDk7n3
|
||||
+VGXu6TwYofF1gbTl4MgqX67tiHCpQ2EAOHyJxCDut0DgdXdaMNmEMjRdrSzbyme
|
||||
APnCKfWxkxlSaRosTKCL4BWaMS/TiJVZbuXEs1DIFAhKm4sTg7GkcrI7djNB3Nyq
|
||||
pgdvHSQSn8h2vS/ZjvQs7rfSOBAkNlEv41xdgSGn2rtO/+YHqP65DSdsu3BaVXoT
|
||||
6fEqSWnHX4dXTEN5bTpl6TBcQe7rd6VzEojov32u5cSoHw2OHG1QAk8mGEPej1WF
|
||||
sQs3BWDJVTkSBKEqz3EWnzZRSb9wO55nnPt7eck5HHisd5FUmrh1CoFSl+NmYWvt
|
||||
PjgelmFV4ZFUjO2MJB+ByRCac5krFk5yAD9UG/iNuovnFNa2RU9g7Jauwy8CTl2d
|
||||
lklyALKrdVwPaFsdZcJfMw8eD/A7hvWwTruc9+olBdytoptLFwG+Qt81IR2tq670
|
||||
v64fG9PiO/yzcnMcmyiQiRM9HcEARwmWmjgb3bHPDcK0RPOWlc4yOo80nOAXx17O
|
||||
rg3bhzjlP1v9mxnhMUF6cKojawHhRUzNlM47ni3niAIi9G7oyOzWPPO5std3eqx7
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Telekom Security TLS ECC Root 2020 O=Deutsche Telekom Security GmbH
|
||||
# Subject: CN=Telekom Security TLS ECC Root 2020 O=Deutsche Telekom Security GmbH
|
||||
# Label: "Telekom Security TLS ECC Root 2020"
|
||||
# Serial: 72082518505882327255703894282316633856
|
||||
# MD5 Fingerprint: c1:ab:fe:6a:10:2c:03:8d:bc:1c:22:32:c0:85:a7:fd
|
||||
# SHA1 Fingerprint: c0:f8:96:c5:a9:3b:01:06:21:07:da:18:42:48:bc:e9:9d:88:d5:ec
|
||||
# SHA256 Fingerprint: 57:8a:f4:de:d0:85:3f:4e:59:98:db:4a:ea:f9:cb:ea:8d:94:5f:60:b6:20:a3:8d:1a:3c:13:b2:bc:7b:a8:e1
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICQjCCAcmgAwIBAgIQNjqWjMlcsljN0AFdxeVXADAKBggqhkjOPQQDAzBjMQsw
|
||||
CQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBH
|
||||
bWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBFQ0MgUm9vdCAyMDIw
|
||||
MB4XDTIwMDgyNTA3NDgyMFoXDTQ1MDgyNTIzNTk1OVowYzELMAkGA1UEBhMCREUx
|
||||
JzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkGA1UE
|
||||
AwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgRUNDIFJvb3QgMjAyMDB2MBAGByqGSM49
|
||||
AgEGBSuBBAAiA2IABM6//leov9Wq9xCazbzREaK9Z0LMkOsVGJDZos0MKiXrPk/O
|
||||
tdKPD/M12kOLAoC+b1EkHQ9rK8qfwm9QMuU3ILYg/4gND21Ju9sGpIeQkpT0CdDP
|
||||
f8iAC8GXs7s1J8nCG6NCMEAwHQYDVR0OBBYEFONyzG6VmUex5rNhTNHLq+O6zd6f
|
||||
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cA
|
||||
MGQCMHVSi7ekEE+uShCLsoRbQuHmKjYC2qBuGT8lv9pZMo7k+5Dck2TOrbRBR2Di
|
||||
z6fLHgIwN0GMZt9Ba9aDAEH9L1r3ULRn0SyocddDypwnJJGDSA3PzfdUga/sf+Rn
|
||||
27iQ7t0l
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Telekom Security TLS RSA Root 2023 O=Deutsche Telekom Security GmbH
|
||||
# Subject: CN=Telekom Security TLS RSA Root 2023 O=Deutsche Telekom Security GmbH
|
||||
# Label: "Telekom Security TLS RSA Root 2023"
|
||||
# Serial: 44676229530606711399881795178081572759
|
||||
# MD5 Fingerprint: bf:5b:eb:54:40:cd:48:71:c4:20:8d:7d:de:0a:42:f2
|
||||
# SHA1 Fingerprint: 54:d3:ac:b3:bd:57:56:f6:85:9d:ce:e5:c3:21:e2:d4:ad:83:d0:93
|
||||
# SHA256 Fingerprint: ef:c6:5c:ad:bb:59:ad:b6:ef:e8:4d:a2:23:11:b3:56:24:b7:1b:3b:1e:a0:da:8b:66:55:17:4e:c8:97:86:46
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
dTdmQRCsu/WU48IxK63nI1bMNSWSs1A=
|
||||
-----END CERTIFICATE-----
|
||||
|
|
|
@ -5,6 +5,10 @@ certifi.py
|
|||
This module returns the installation location of cacert.pem or its contents.
|
||||
"""
|
||||
import sys
|
||||
import atexit
|
||||
|
||||
def exit_cacert_ctx() -> None:
|
||||
_CACERT_CTX.__exit__(None, None, None) # type: ignore[union-attr]
|
||||
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
|
@ -35,6 +39,7 @@ if sys.version_info >= (3, 11):
|
|||
# we will also store that at the global level as well.
|
||||
_CACERT_CTX = as_file(files("certifi").joinpath("cacert.pem"))
|
||||
_CACERT_PATH = str(_CACERT_CTX.__enter__())
|
||||
atexit.register(exit_cacert_ctx)
|
||||
|
||||
return _CACERT_PATH
|
||||
|
||||
|
@ -70,6 +75,7 @@ elif sys.version_info >= (3, 7):
|
|||
# we will also store that at the global level as well.
|
||||
_CACERT_CTX = get_path("certifi", "cacert.pem")
|
||||
_CACERT_PATH = str(_CACERT_CTX.__enter__())
|
||||
atexit.register(exit_cacert_ctx)
|
||||
|
||||
return _CACERT_PATH
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ CL_BLANK = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAA
|
|||
URI_SCHEME = "cloudinary"
|
||||
API_VERSION = "v1_1"
|
||||
|
||||
VERSION = "1.34.0"
|
||||
VERSION = "1.39.1"
|
||||
|
||||
_USER_PLATFORM_DETAILS = "; ".join((platform(), "Python {}".format(python_version())))
|
||||
|
||||
|
@ -741,7 +741,11 @@ class CloudinaryResource(object):
|
|||
:return: Video tag
|
||||
"""
|
||||
public_id = options.get('public_id', self.public_id)
|
||||
use_fetch_format = options.get('use_fetch_format', config().use_fetch_format)
|
||||
if not use_fetch_format:
|
||||
source = re.sub(r"\.({0})$".format("|".join(self.default_source_types())), '', public_id)
|
||||
else:
|
||||
source = public_id
|
||||
|
||||
custom_attributes = options.pop("attributes", dict())
|
||||
|
||||
|
|
|
@ -14,7 +14,8 @@ from cloudinary import utils
|
|||
from cloudinary.api_client.call_api import (
|
||||
call_api,
|
||||
call_metadata_api,
|
||||
call_json_api
|
||||
call_json_api,
|
||||
_call_v2_api
|
||||
)
|
||||
from cloudinary.exceptions import (
|
||||
BadRequest,
|
||||
|
@ -54,6 +55,19 @@ def usage(**options):
|
|||
return call_api("get", uri, {}, **options)
|
||||
|
||||
|
||||
def config(**options):
|
||||
"""
|
||||
Get account config details.
|
||||
|
||||
:param options: Additional options.
|
||||
:type options: dict, optional
|
||||
:return: Detailed config information.
|
||||
:rtype: Response
|
||||
"""
|
||||
params = only(options, "settings")
|
||||
return call_api("get", ["config"], params, **options)
|
||||
|
||||
|
||||
def resource_types(**options):
|
||||
return call_api("get", ["resources"], {}, **options)
|
||||
|
||||
|
@ -64,24 +78,22 @@ def resources(**options):
|
|||
uri = ["resources", resource_type]
|
||||
if upload_type:
|
||||
uri.append(upload_type)
|
||||
params = only(options, "next_cursor", "max_results", "prefix", "tags",
|
||||
"context", "moderations", "direction", "start_at", "metadata")
|
||||
params = __list_resources_params(**options)
|
||||
params.update(only(options, "prefix", "start_at"))
|
||||
return call_api("get", uri, params, **options)
|
||||
|
||||
|
||||
def resources_by_tag(tag, **options):
|
||||
resource_type = options.pop("resource_type", "image")
|
||||
uri = ["resources", resource_type, "tags", tag]
|
||||
params = only(options, "next_cursor", "max_results", "tags",
|
||||
"context", "moderations", "direction", "metadata")
|
||||
params = __list_resources_params(**options)
|
||||
return call_api("get", uri, params, **options)
|
||||
|
||||
|
||||
def resources_by_moderation(kind, status, **options):
|
||||
resource_type = options.pop("resource_type", "image")
|
||||
uri = ["resources", resource_type, "moderations", kind, status]
|
||||
params = only(options, "next_cursor", "max_results", "tags",
|
||||
"context", "moderations", "direction", "metadata")
|
||||
params = __list_resources_params(**options)
|
||||
return call_api("get", uri, params, **options)
|
||||
|
||||
|
||||
|
@ -89,7 +101,7 @@ def resources_by_ids(public_ids, **options):
|
|||
resource_type = options.pop("resource_type", "image")
|
||||
upload_type = options.pop("type", "upload")
|
||||
uri = ["resources", resource_type, upload_type]
|
||||
params = dict(only(options, "tags", "moderations", "context"), public_ids=public_ids)
|
||||
params = dict(__resources_params(**options), public_ids=public_ids)
|
||||
return call_api("get", uri, params, **options)
|
||||
|
||||
|
||||
|
@ -105,7 +117,7 @@ def resources_by_asset_folder(asset_folder, **options):
|
|||
:rtype: Response
|
||||
"""
|
||||
uri = ["resources", "by_asset_folder"]
|
||||
params = only(options, "max_results", "tags", "moderations", "context", "next_cursor")
|
||||
params = __list_resources_params(**options)
|
||||
params["asset_folder"] = asset_folder
|
||||
return call_api("get", uri, params, **options)
|
||||
|
||||
|
@ -125,7 +137,7 @@ def resources_by_asset_ids(asset_ids, **options):
|
|||
:rtype: Response
|
||||
"""
|
||||
uri = ["resources", 'by_asset_ids']
|
||||
params = dict(only(options, "tags", "moderations", "context"), asset_ids=asset_ids)
|
||||
params = dict(__resources_params(**options), asset_ids=asset_ids)
|
||||
return call_api("get", uri, params, **options)
|
||||
|
||||
|
||||
|
@ -147,15 +159,43 @@ def resources_by_context(key, value=None, **options):
|
|||
"""
|
||||
resource_type = options.pop("resource_type", "image")
|
||||
uri = ["resources", resource_type, "context"]
|
||||
params = only(options, "next_cursor", "max_results", "tags",
|
||||
"context", "moderations", "direction", "metadata")
|
||||
params = __list_resources_params(**options)
|
||||
params["key"] = key
|
||||
if value is not None:
|
||||
params["value"] = value
|
||||
return call_api("get", uri, params, **options)
|
||||
|
||||
|
||||
def visual_search(image_url=None, image_asset_id=None, text=None, **options):
|
||||
def __resources_params(**options):
|
||||
"""
|
||||
Prepares optional parameters for resources_* API calls.
|
||||
|
||||
:param options: Additional options
|
||||
:return: Optional parameters
|
||||
|
||||
:internal
|
||||
"""
|
||||
params = only(options, "tags", "context", "metadata", "moderations")
|
||||
params["fields"] = options.get("fields") and utils.encode_list(utils.build_array(options["fields"]))
|
||||
return params
|
||||
|
||||
|
||||
def __list_resources_params(**options):
|
||||
"""
|
||||
Prepares optional parameters for resources_* API calls.
|
||||
|
||||
:param options: Additional options
|
||||
:return: Optional parameters
|
||||
|
||||
:internal
|
||||
"""
|
||||
resources_params = __resources_params(**options)
|
||||
resources_params.update(only(options, "next_cursor", "max_results", "direction"))
|
||||
|
||||
return resources_params
|
||||
|
||||
|
||||
def visual_search(image_url=None, image_asset_id=None, text=None, image_file=None, **options):
|
||||
"""
|
||||
Find images based on their visual content.
|
||||
|
||||
|
@ -165,14 +205,17 @@ def visual_search(image_url=None, image_asset_id=None, text=None, **options):
|
|||
:type image_asset_id: str
|
||||
:param text: A textual description, e.g., "cat"
|
||||
:type text: str
|
||||
:param image_file: The image file.
|
||||
:type image_file: str|callable|Path|bytes
|
||||
:param options: Additional options
|
||||
:type options: dict, optional
|
||||
:return: Resources (assets) that were found
|
||||
:rtype: Response
|
||||
"""
|
||||
uri = ["resources", "visual_search"]
|
||||
params = {"image_url": image_url, "image_asset_id": image_asset_id, "text": text}
|
||||
return call_api("get", uri, params, **options)
|
||||
params = {"image_url": image_url, "image_asset_id": image_asset_id, "text": text,
|
||||
"image_file": utils.handle_file_parameter(image_file, "file")}
|
||||
return call_api("post", uri, params, **options)
|
||||
|
||||
|
||||
def resource(public_id, **options):
|
||||
|
@ -224,11 +267,11 @@ def update(public_id, **options):
|
|||
if "tags" in options:
|
||||
params["tags"] = ",".join(utils.build_array(options["tags"]))
|
||||
if "face_coordinates" in options:
|
||||
params["face_coordinates"] = utils.encode_double_array(
|
||||
options.get("face_coordinates"))
|
||||
params["face_coordinates"] = utils.encode_double_array(options.get("face_coordinates"))
|
||||
if "custom_coordinates" in options:
|
||||
params["custom_coordinates"] = utils.encode_double_array(
|
||||
options.get("custom_coordinates"))
|
||||
params["custom_coordinates"] = utils.encode_double_array(options.get("custom_coordinates"))
|
||||
if "regions" in options:
|
||||
params["regions"] = utils.json_encode(options.get("regions"))
|
||||
if "context" in options:
|
||||
params["context"] = utils.encode_context(options.get("context"))
|
||||
if "metadata" in options:
|
||||
|
@ -656,9 +699,8 @@ def add_metadata_field(field, **options):
|
|||
|
||||
:rtype: Response
|
||||
"""
|
||||
params = only(field, "type", "external_id", "label", "mandatory",
|
||||
"default_value", "validation", "datasource")
|
||||
return call_metadata_api("post", [], params, **options)
|
||||
|
||||
return call_metadata_api("post", [], __metadata_field_params(field), **options)
|
||||
|
||||
|
||||
def update_metadata_field(field_external_id, field, **options):
|
||||
|
@ -677,8 +719,13 @@ def update_metadata_field(field_external_id, field, **options):
|
|||
:rtype: Response
|
||||
"""
|
||||
uri = [field_external_id]
|
||||
params = only(field, "label", "mandatory", "default_value", "validation")
|
||||
return call_metadata_api("put", uri, params, **options)
|
||||
|
||||
return call_metadata_api("put", uri, __metadata_field_params(field), **options)
|
||||
|
||||
|
||||
def __metadata_field_params(field):
|
||||
return only(field, "type", "external_id", "label", "mandatory", "restrictions",
|
||||
"default_value", "validation", "datasource")
|
||||
|
||||
|
||||
def delete_metadata_field(field_external_id, **options):
|
||||
|
@ -798,3 +845,18 @@ def reorder_metadata_fields(order_by, direction=None, **options):
|
|||
uri = ['order']
|
||||
params = {'order_by': order_by, 'direction': direction}
|
||||
return call_metadata_api('put', uri, params, **options)
|
||||
|
||||
|
||||
def analyze(input_type, analysis_type, uri=None, **options):
|
||||
"""Analyzes an asset with the requested analysis type.
|
||||
|
||||
:param input_type: The type of input for the asset to analyze ('uri').
|
||||
:param analysis_type: The type of analysis to run ('google_tagging', 'captioning', 'fashion').
|
||||
:param uri: The URI of the asset to analyze.
|
||||
:param options: Additional options.
|
||||
|
||||
:rtype: Response
|
||||
"""
|
||||
api_uri = ['analysis', 'analyze', input_type]
|
||||
params = {'analysis_type': analysis_type, 'uri': uri, 'parameters': options.get("parameters")}
|
||||
return _call_v2_api('post', api_uri, params, **options)
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import cloudinary
|
||||
from cloudinary.api_client.execute_request import execute_request
|
||||
from cloudinary.provisioning.account_config import account_config
|
||||
from cloudinary.utils import get_http_connector
|
||||
|
||||
from cloudinary.utils import get_http_connector, normalize_params
|
||||
|
||||
PROVISIONING_SUB_PATH = "provisioning"
|
||||
ACCOUNT_SUB_PATH = "accounts"
|
||||
|
@ -28,7 +27,7 @@ def _call_account_api(method, uri, params=None, headers=None, **options):
|
|||
|
||||
return execute_request(http_connector=_http,
|
||||
method=method,
|
||||
params=params,
|
||||
params=normalize_params(params),
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
api_url=provisioning_api_url,
|
||||
|
|
|
@ -2,8 +2,7 @@ import json
|
|||
|
||||
import cloudinary
|
||||
from cloudinary.api_client.execute_request import execute_request
|
||||
from cloudinary.utils import get_http_connector
|
||||
|
||||
from cloudinary.utils import get_http_connector, normalize_params
|
||||
|
||||
logger = cloudinary.logger
|
||||
_http = get_http_connector(cloudinary.config(), cloudinary.CERT_KWARGS)
|
||||
|
@ -27,6 +26,10 @@ def call_json_api(method, uri, json_body, **options):
|
|||
return _call_api(method, uri, body=data, headers={'Content-Type': 'application/json'}, **options)
|
||||
|
||||
|
||||
def _call_v2_api(method, uri, json_body, **options):
|
||||
return call_json_api(method, uri, json_body=json_body, api_version='v2', **options)
|
||||
|
||||
|
||||
def call_api(method, uri, params, **options):
|
||||
return _call_api(method, uri, params=params, **options)
|
||||
|
||||
|
@ -43,10 +46,11 @@ def _call_api(method, uri, params=None, body=None, headers=None, extra_headers=N
|
|||
oauth_token = options.pop("oauth_token", cloudinary.config().oauth_token)
|
||||
|
||||
_validate_authorization(api_key, api_secret, oauth_token)
|
||||
|
||||
api_url = "/".join([prefix, cloudinary.API_VERSION, cloud_name] + uri)
|
||||
auth = {"key": api_key, "secret": api_secret, "oauth_token": oauth_token}
|
||||
|
||||
api_version = options.pop("api_version", cloudinary.API_VERSION)
|
||||
api_url = "/".join([prefix, api_version, cloud_name] + uri)
|
||||
|
||||
if body is not None:
|
||||
options["body"] = body
|
||||
|
||||
|
@ -55,7 +59,7 @@ def _call_api(method, uri, params=None, body=None, headers=None, extra_headers=N
|
|||
|
||||
return execute_request(http_connector=_http,
|
||||
method=method,
|
||||
params=params,
|
||||
params=normalize_params(params),
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
api_url=api_url,
|
||||
|
|
|
@ -63,9 +63,8 @@ def execute_request(http_connector, method, params, headers, auth, api_url, **op
|
|||
processed_params = process_params(params)
|
||||
|
||||
api_url = smart_escape(unquote(api_url))
|
||||
|
||||
try:
|
||||
response = http_connector.request(method.upper(), api_url, processed_params, req_headers, **kw)
|
||||
response = http_connector.request(method=method.upper(), url=api_url, fields=processed_params, headers=req_headers, **kw)
|
||||
body = response.data
|
||||
except HTTPError as e:
|
||||
raise GeneralError("Unexpected error %s" % str(e))
|
||||
|
|
|
@ -24,7 +24,7 @@ class HttpClient:
|
|||
|
||||
def get_json(self, url):
|
||||
try:
|
||||
response = self._http_client.request("GET", url, timeout=self.timeout)
|
||||
response = self._http_client.request(method="GET", url=url, timeout=self.timeout)
|
||||
body = response.data
|
||||
except HTTPError as e:
|
||||
raise GeneralError("Unexpected error %s" % str(e))
|
||||
|
|
|
@ -2,4 +2,5 @@ from .account_config import AccountConfig, account_config, reset_config
|
|||
from .account import (sub_accounts, create_sub_account, delete_sub_account, sub_account, update_sub_account,
|
||||
user_groups, create_user_group, update_user_group, delete_user_group, user_group,
|
||||
add_user_to_group, remove_user_from_group, user_group_users, user_in_user_groups,
|
||||
users, create_user, delete_user, user, update_user, Role)
|
||||
users, create_user, delete_user, user, update_user, access_keys, generate_access_key,
|
||||
update_access_key, delete_access_key, Role)
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
from cloudinary.api_client.call_account_api import _call_account_api
|
||||
from cloudinary.utils import encode_list
|
||||
|
||||
|
||||
SUB_ACCOUNTS_SUB_PATH = "sub_accounts"
|
||||
USERS_SUB_PATH = "users"
|
||||
USER_GROUPS_SUB_PATH = "user_groups"
|
||||
ACCESS_KEYS = "access_keys"
|
||||
|
||||
|
||||
class Role(object):
|
||||
|
@ -123,7 +123,8 @@ def update_sub_account(sub_account_id, name=None, cloud_name=None, custom_attrib
|
|||
return _call_account_api("put", uri, params=params, **options)
|
||||
|
||||
|
||||
def users(user_ids=None, sub_account_id=None, pending=None, prefix=None, **options):
|
||||
def users(user_ids=None, sub_account_id=None, pending=None, prefix=None, last_login=None, from_date=None, to_date=None,
|
||||
**options):
|
||||
"""
|
||||
List all users
|
||||
:param user_ids: The ids of the users to fetch
|
||||
|
@ -136,6 +137,13 @@ def users(user_ids=None, sub_account_id=None, pending=None, prefix=None, **optio
|
|||
:type pending: bool, optional
|
||||
:param prefix: User prefix
|
||||
:type prefix: str, optional
|
||||
:param last_login: Return only users that last logged in in the specified range of dates (true),
|
||||
users that didn't last logged in in that range (false), or all users (None).
|
||||
:type last_login: bool, optional
|
||||
:param from_date: Last login start date.
|
||||
:type from_date: datetime, optional
|
||||
:param to_date: Last login end date.
|
||||
:type to_date: datetime, optional
|
||||
:param options: Generic advanced options dict, see online documentation.
|
||||
:type options: dict, optional
|
||||
:return: List of users associated with the account
|
||||
|
@ -146,7 +154,10 @@ def users(user_ids=None, sub_account_id=None, pending=None, prefix=None, **optio
|
|||
params = {"ids": user_ids,
|
||||
"sub_account_id": sub_account_id,
|
||||
"pending": pending,
|
||||
"prefix": prefix}
|
||||
"prefix": prefix,
|
||||
"last_login": last_login,
|
||||
"from": from_date,
|
||||
"to": to_date}
|
||||
return _call_account_api("get", uri, params=params, **options)
|
||||
|
||||
|
||||
|
@ -351,7 +362,7 @@ def user_in_user_groups(user_id, **options):
|
|||
"""
|
||||
Get all user groups a user belongs to
|
||||
:param user_id: The id of user
|
||||
:param user_id: str
|
||||
:type user_id: str
|
||||
:param options: Generic advanced options dict, see online documentation
|
||||
:type options: dict, optional
|
||||
:return: List of groups user is in
|
||||
|
@ -359,3 +370,112 @@ def user_in_user_groups(user_id, **options):
|
|||
"""
|
||||
uri = [USER_GROUPS_SUB_PATH, user_id]
|
||||
return _call_account_api("get", uri, {}, **options)
|
||||
|
||||
|
||||
def access_keys(sub_account_id, page_size=None, page=None, sort_by=None, sort_order=None, **options):
|
||||
"""
|
||||
Get sub account access keys.
|
||||
|
||||
:param sub_account_id: The id of the sub account.
|
||||
:type sub_account_id: str
|
||||
:param page_size: How many entries to display on each page.
|
||||
:type page_size: int
|
||||
:param page: Which page to return (maximum pages: 100). **Default**: All pages are returned.
|
||||
:type page: int
|
||||
:param sort_by: Which response parameter to sort by.
|
||||
**Possible values**: `api_key`, `created_at`, `name`, `enabled`.
|
||||
:type sort_by: str
|
||||
:param sort_order: Control the order of returned keys. **Possible values**: `desc` (default), `asc`.
|
||||
:type sort_order: str
|
||||
:param options: Generic advanced options dict, see online documentation.
|
||||
:type options: dict, optional
|
||||
:return: List of access keys
|
||||
:rtype: dict
|
||||
"""
|
||||
uri = [SUB_ACCOUNTS_SUB_PATH, sub_account_id, ACCESS_KEYS]
|
||||
params = {
|
||||
"page_size": page_size,
|
||||
"page": page,
|
||||
"sort_by": sort_by,
|
||||
"sort_order": sort_order,
|
||||
}
|
||||
return _call_account_api("get", uri, params, **options)
|
||||
|
||||
|
||||
def generate_access_key(sub_account_id, name=None, enabled=None, **options):
|
||||
"""
|
||||
Generate a new access key.
|
||||
|
||||
:param sub_account_id: The id of the sub account.
|
||||
:type sub_account_id: str
|
||||
:param name: The name of the new access key.
|
||||
:type name: str
|
||||
:param enabled: Whether the new access key is enabled or disabled.
|
||||
:type enabled: bool
|
||||
:param options: Generic advanced options dict, see online documentation.
|
||||
:type options: dict, optional
|
||||
:return: Access key details
|
||||
:rtype: dict
|
||||
"""
|
||||
uri = [SUB_ACCOUNTS_SUB_PATH, sub_account_id, ACCESS_KEYS]
|
||||
params = {
|
||||
"name": name,
|
||||
"enabled": enabled,
|
||||
}
|
||||
return _call_account_api("post", uri, params, **options)
|
||||
|
||||
|
||||
def update_access_key(sub_account_id, api_key, name=None, enabled=None, dedicated_for=None, **options):
|
||||
"""
|
||||
Update the name and/or status of an existing access key.
|
||||
|
||||
:param sub_account_id: The id of the sub account.
|
||||
:type sub_account_id: str
|
||||
:param api_key: The API key of the access key.
|
||||
:type api_key: str|int
|
||||
:param name: The updated name of the access key.
|
||||
:type name: str
|
||||
:param enabled: Enable or disable the access key.
|
||||
:type enabled: bool
|
||||
:param dedicated_for: Designates the access key for a specific purpose while allowing it to be used for
|
||||
other purposes, as well. This action replaces any previously assigned key.
|
||||
**Possible values**: `webhooks`
|
||||
:type dedicated_for: str
|
||||
:param options: Generic advanced options dict, see online documentation.
|
||||
:type options: dict, optional
|
||||
:return: Access key details
|
||||
:rtype: dict
|
||||
"""
|
||||
uri = [SUB_ACCOUNTS_SUB_PATH, sub_account_id, ACCESS_KEYS, str(api_key)]
|
||||
params = {
|
||||
"name": name,
|
||||
"enabled": enabled,
|
||||
"dedicated_for": dedicated_for,
|
||||
}
|
||||
return _call_account_api("put", uri, params, **options)
|
||||
|
||||
|
||||
def delete_access_key(sub_account_id, api_key=None, name=None, **options):
|
||||
"""
|
||||
Delete an existing access key by api_key or by name.
|
||||
|
||||
:param sub_account_id: The id of the sub account.
|
||||
:type sub_account_id: str
|
||||
:param api_key: The API key of the access key.
|
||||
:type api_key: str|int
|
||||
:param name: The name of the access key.
|
||||
:type name: str
|
||||
:param options: Generic advanced options dict, see online documentation.
|
||||
:type options: dict, optional
|
||||
:return: Operation status.
|
||||
:rtype: dict
|
||||
"""
|
||||
uri = [SUB_ACCOUNTS_SUB_PATH, sub_account_id, ACCESS_KEYS]
|
||||
|
||||
if api_key is not None:
|
||||
uri.append(str(api_key))
|
||||
|
||||
params = {
|
||||
"name": name
|
||||
}
|
||||
return _call_account_api("delete", uri, params, **options)
|
||||
|
|
|
@ -3,8 +3,8 @@ import json
|
|||
|
||||
import cloudinary
|
||||
from cloudinary.api_client.call_api import call_json_api
|
||||
from cloudinary.utils import unique, unsigned_download_url_prefix, build_distribution_domain, base64url_encode, \
|
||||
json_encode, compute_hex_hash, SIGNATURE_SHA256
|
||||
from cloudinary.utils import (unique, build_distribution_domain, base64url_encode, json_encode, compute_hex_hash,
|
||||
SIGNATURE_SHA256, build_array)
|
||||
|
||||
|
||||
class Search(object):
|
||||
|
@ -16,6 +16,7 @@ class Search(object):
|
|||
'sort_by': lambda x: next(iter(x)),
|
||||
'aggregate': None,
|
||||
'with_field': None,
|
||||
'fields': None,
|
||||
}
|
||||
|
||||
_ttl = 300 # Used for search URLs
|
||||
|
@ -57,6 +58,11 @@ class Search(object):
|
|||
self._add("with_field", value)
|
||||
return self
|
||||
|
||||
def fields(self, value):
|
||||
"""Request which fields to return in the result set."""
|
||||
self._add("fields", value)
|
||||
return self
|
||||
|
||||
def ttl(self, ttl):
|
||||
"""
|
||||
Sets the time to live of the search URL.
|
||||
|
@ -133,5 +139,5 @@ class Search(object):
|
|||
def _add(self, name, value):
|
||||
if name not in self.query:
|
||||
self.query[name] = []
|
||||
self.query[name].append(value)
|
||||
self.query[name].extend(build_array(value))
|
||||
return self
|
||||
|
|
|
@ -23,11 +23,6 @@ try: # Python 2.7+
|
|||
except ImportError:
|
||||
from urllib3.packages.ordered_dict import OrderedDict
|
||||
|
||||
try: # Python 3.4+
|
||||
from pathlib import Path as PathLibPathType
|
||||
except ImportError:
|
||||
PathLibPathType = None
|
||||
|
||||
if is_appengine_sandbox():
|
||||
# AppEngineManager uses AppEngine's URLFetch API behind the scenes
|
||||
_http = AppEngineManager()
|
||||
|
@ -503,32 +498,7 @@ def call_api(action, params, http_headers=None, return_error=False, unsigned=Fal
|
|||
|
||||
if file:
|
||||
filename = options.get("filename") # Custom filename provided by user (relevant only for streams and files)
|
||||
|
||||
if PathLibPathType and isinstance(file, PathLibPathType):
|
||||
name = filename or file.name
|
||||
data = file.read_bytes()
|
||||
elif isinstance(file, string_types):
|
||||
if utils.is_remote_url(file):
|
||||
# URL
|
||||
name = None
|
||||
data = file
|
||||
else:
|
||||
# file path
|
||||
name = filename or file
|
||||
with open(file, "rb") as opened:
|
||||
data = opened.read()
|
||||
elif hasattr(file, 'read') and callable(file.read):
|
||||
# stream
|
||||
data = file.read()
|
||||
name = filename or (file.name if hasattr(file, 'name') and isinstance(file.name, str) else "stream")
|
||||
elif isinstance(file, tuple):
|
||||
name, data = file
|
||||
else:
|
||||
# Not a string, not a stream
|
||||
name = filename or "file"
|
||||
data = file
|
||||
|
||||
param_list.append(("file", (name, data) if name else data))
|
||||
param_list.append(("file", utils.handle_file_parameter(file, filename)))
|
||||
|
||||
kw = {}
|
||||
if timeout is not None:
|
||||
|
@ -536,7 +506,7 @@ def call_api(action, params, http_headers=None, return_error=False, unsigned=Fal
|
|||
|
||||
code = 200
|
||||
try:
|
||||
response = _http.request("POST", api_url, param_list, headers, **kw)
|
||||
response = _http.request(method="POST", url=api_url, fields=param_list, headers=headers, **kw)
|
||||
except HTTPError as e:
|
||||
raise Error("Unexpected error - {0!r}".format(e))
|
||||
except socket.error as e:
|
||||
|
|
|
@ -25,6 +25,11 @@ from cloudinary import auth_token
|
|||
from cloudinary.api_client.tcp_keep_alive_manager import TCPKeepAlivePoolManager, TCPKeepAliveProxyManager
|
||||
from cloudinary.compat import PY3, to_bytes, to_bytearray, to_string, string_types, urlparse
|
||||
|
||||
try: # Python 3.4+
|
||||
from pathlib import Path as PathLibPathType
|
||||
except ImportError:
|
||||
PathLibPathType = None
|
||||
|
||||
VAR_NAME_RE = r'(\$\([a-zA-Z]\w+\))'
|
||||
|
||||
urlencode = six.moves.urllib.parse.urlencode
|
||||
|
@ -127,6 +132,7 @@ __SERIALIZED_UPLOAD_PARAMS = [
|
|||
"allowed_formats",
|
||||
"face_coordinates",
|
||||
"custom_coordinates",
|
||||
"regions",
|
||||
"context",
|
||||
"auto_tagging",
|
||||
"responsive_breakpoints",
|
||||
|
@ -181,11 +187,10 @@ def compute_hex_hash(s, algorithm=SIGNATURE_SHA1):
|
|||
|
||||
|
||||
def build_array(arg):
|
||||
if isinstance(arg, list):
|
||||
if isinstance(arg, (list, tuple)):
|
||||
return arg
|
||||
elif arg is None:
|
||||
return []
|
||||
else:
|
||||
return [arg]
|
||||
|
||||
|
||||
|
@ -235,7 +240,6 @@ def encode_double_array(array):
|
|||
array = build_array(array)
|
||||
if len(array) > 0 and isinstance(array[0], list):
|
||||
return "|".join([",".join([str(i) for i in build_array(inner)]) for inner in array])
|
||||
else:
|
||||
return encode_list([str(i) for i in array])
|
||||
|
||||
|
||||
|
@ -246,7 +250,6 @@ def encode_dict(arg):
|
|||
else:
|
||||
items = arg.iteritems()
|
||||
return "|".join((k + "=" + v) for k, v in items)
|
||||
else:
|
||||
return arg
|
||||
|
||||
|
||||
|
@ -288,9 +291,14 @@ def json_encode(value, sort_keys=False):
|
|||
Converts value to a json encoded string
|
||||
|
||||
:param value: value to be encoded
|
||||
:param sort_keys: whether to sort keys
|
||||
|
||||
:return: JSON encoded string
|
||||
"""
|
||||
|
||||
if isinstance(value, str) or value is None:
|
||||
return value
|
||||
|
||||
return json.dumps(value, default=__json_serializer, separators=(',', ':'), sort_keys=sort_keys)
|
||||
|
||||
|
||||
|
@ -309,11 +317,13 @@ def patch_fetch_format(options):
|
|||
"""
|
||||
When upload type is fetch, remove the format options.
|
||||
In addition, set the fetch_format options to the format value unless it was already set.
|
||||
Mutates the options parameter!
|
||||
Mutates the "options" parameter!
|
||||
|
||||
:param options: URL and transformation options
|
||||
"""
|
||||
if options.get("type", "upload") != "fetch":
|
||||
use_fetch_format = options.pop("use_fetch_format", cloudinary.config().use_fetch_format)
|
||||
|
||||
if options.get("type", "upload") != "fetch" and not use_fetch_format:
|
||||
return
|
||||
|
||||
resource_format = options.pop("format", None)
|
||||
|
@ -351,7 +361,6 @@ def generate_transformation_string(**options):
|
|||
def recurse(bs):
|
||||
if isinstance(bs, dict):
|
||||
return generate_transformation_string(**bs)[0]
|
||||
else:
|
||||
return generate_transformation_string(transformation=bs)[0]
|
||||
|
||||
base_transformations = list(map(recurse, base_transformations))
|
||||
|
@ -513,7 +522,6 @@ def split_range(range):
|
|||
return [range[0], range[-1]]
|
||||
elif isinstance(range, string_types) and re.match(RANGE_RE, range):
|
||||
return range.split("..", 1)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
|
@ -570,6 +578,9 @@ def process_params(params):
|
|||
processed_params = {}
|
||||
for key, value in params.items():
|
||||
if isinstance(value, list) or isinstance(value, tuple):
|
||||
if len(value) == 2 and value[0] == "file": # keep file parameter as is.
|
||||
processed_params[key] = value
|
||||
continue
|
||||
value_list = {"{}[{}]".format(key, i): i_value for i, i_value in enumerate(value)}
|
||||
processed_params.update(value_list)
|
||||
elif value is not None:
|
||||
|
@ -578,9 +589,28 @@ def process_params(params):
|
|||
|
||||
|
||||
def cleanup_params(params):
|
||||
"""
|
||||
Cleans and normalizes parameters when calculating signature in Upload API.
|
||||
|
||||
:param params:
|
||||
:return:
|
||||
"""
|
||||
return dict([(k, __safe_value(v)) for (k, v) in params.items() if v is not None and not v == ""])
|
||||
|
||||
|
||||
def normalize_params(params):
|
||||
"""
|
||||
Normalizes Admin API parameters.
|
||||
|
||||
:param params:
|
||||
:return:
|
||||
"""
|
||||
if not params or not isinstance(params, dict):
|
||||
return params
|
||||
|
||||
return dict([(k, __bool_string(v)) for (k, v) in params.items() if v is not None and not v == ""])
|
||||
|
||||
|
||||
def sign_request(params, options):
|
||||
api_key = options.get("api_key", cloudinary.config().api_key)
|
||||
if not api_key:
|
||||
|
@ -1086,6 +1116,7 @@ def build_upload_params(**options):
|
|||
"allowed_formats": options.get("allowed_formats") and encode_list(build_array(options["allowed_formats"])),
|
||||
"face_coordinates": encode_double_array(options.get("face_coordinates")),
|
||||
"custom_coordinates": encode_double_array(options.get("custom_coordinates")),
|
||||
"regions": json_encode(options.get("regions")),
|
||||
"context": encode_context(options.get("context")),
|
||||
"auto_tagging": options.get("auto_tagging") and str(options.get("auto_tagging")),
|
||||
"responsive_breakpoints": generate_responsive_breakpoints_string(options.get("responsive_breakpoints")),
|
||||
|
@ -1101,6 +1132,37 @@ def build_upload_params(**options):
|
|||
return params
|
||||
|
||||
|
||||
def handle_file_parameter(file, filename):
|
||||
if not file:
|
||||
return None
|
||||
|
||||
if PathLibPathType and isinstance(file, PathLibPathType):
|
||||
name = filename or file.name
|
||||
data = file.read_bytes()
|
||||
elif isinstance(file, string_types):
|
||||
if is_remote_url(file):
|
||||
# URL
|
||||
name = None
|
||||
data = file
|
||||
else:
|
||||
# file path
|
||||
name = filename or file
|
||||
with open(file, "rb") as opened:
|
||||
data = opened.read()
|
||||
elif hasattr(file, 'read') and callable(file.read):
|
||||
# stream
|
||||
data = file.read()
|
||||
name = filename or (file.name if hasattr(file, 'name') and isinstance(file.name, str) else "stream")
|
||||
elif isinstance(file, tuple):
|
||||
name, data = file
|
||||
else:
|
||||
# Not a string, not a stream
|
||||
name = filename or "file"
|
||||
data = file
|
||||
|
||||
return (name, data) if name else data
|
||||
|
||||
|
||||
def build_multi_and_sprite_params(**options):
|
||||
"""
|
||||
Build params for multi, download_multi, generate_sprite, and download_generated_sprite methods
|
||||
|
@ -1166,8 +1228,21 @@ def __process_text_options(layer, layer_parameter):
|
|||
|
||||
|
||||
def process_layer(layer, layer_parameter):
|
||||
if isinstance(layer, string_types) and layer.startswith("fetch:"):
|
||||
layer = {"url": layer[len('fetch:'):]}
|
||||
if isinstance(layer, string_types):
|
||||
resource_type = None
|
||||
if layer.startswith("fetch:"):
|
||||
url = layer[len('fetch:'):]
|
||||
elif layer.find(":fetch:", 0, 12) != -1:
|
||||
resource_type, _, url = layer.split(":", 2)
|
||||
else:
|
||||
# nothing to process, a raw string, keep as is.
|
||||
return layer
|
||||
|
||||
# handle remote fetch URL
|
||||
layer = {"url": url, "type": "fetch"}
|
||||
if resource_type:
|
||||
layer["resource_type"] = resource_type
|
||||
|
||||
if not isinstance(layer, dict):
|
||||
return layer
|
||||
|
||||
|
@ -1176,19 +1251,19 @@ def process_layer(layer, layer_parameter):
|
|||
type = layer.get("type")
|
||||
public_id = layer.get("public_id")
|
||||
format = layer.get("format")
|
||||
fetch = layer.get("url")
|
||||
fetch_url = layer.get("url")
|
||||
components = list()
|
||||
|
||||
if text is not None and resource_type is None:
|
||||
resource_type = "text"
|
||||
|
||||
if fetch and resource_type is None:
|
||||
resource_type = "fetch"
|
||||
if fetch_url and type is None:
|
||||
type = "fetch"
|
||||
|
||||
if public_id is not None and format is not None:
|
||||
public_id = public_id + "." + format
|
||||
|
||||
if public_id is None and resource_type != "text" and resource_type != "fetch":
|
||||
if public_id is None and resource_type != "text" and type != "fetch":
|
||||
raise ValueError("Must supply public_id for for non-text " + layer_parameter)
|
||||
|
||||
if resource_type is not None and resource_type != "image":
|
||||
|
@ -1212,8 +1287,6 @@ def process_layer(layer, layer_parameter):
|
|||
|
||||
if text is not None:
|
||||
var_pattern = VAR_NAME_RE
|
||||
match = re.findall(var_pattern, text)
|
||||
|
||||
parts = filter(lambda p: p is not None, re.split(var_pattern, text))
|
||||
encoded_text = []
|
||||
for part in parts:
|
||||
|
@ -1223,11 +1296,9 @@ def process_layer(layer, layer_parameter):
|
|||
encoded_text.append(smart_escape(smart_escape(part, r"([,/])")))
|
||||
|
||||
text = ''.join(encoded_text)
|
||||
# text = text.replace("%2C", "%252C")
|
||||
# text = text.replace("/", "%252F")
|
||||
components.append(text)
|
||||
elif resource_type == "fetch":
|
||||
b64 = base64_encode_url(fetch)
|
||||
elif type == "fetch":
|
||||
b64 = base64url_encode(fetch_url)
|
||||
components.append(b64)
|
||||
else:
|
||||
public_id = public_id.replace("/", ':')
|
||||
|
@ -1359,7 +1430,6 @@ def normalize_expression(expression):
|
|||
result = re.sub(replaceRE, translate_if, result)
|
||||
result = re.sub('[ _]+', '_', result)
|
||||
return result
|
||||
else:
|
||||
return expression
|
||||
|
||||
|
||||
|
@ -1368,7 +1438,6 @@ def __join_pair(key, value):
|
|||
return None
|
||||
elif value is True:
|
||||
return key
|
||||
else:
|
||||
return u"{0}=\"{1}\"".format(key, value)
|
||||
|
||||
|
||||
|
@ -1379,10 +1448,15 @@ def html_attrs(attrs, only=None):
|
|||
def __safe_value(v):
|
||||
if isinstance(v, bool):
|
||||
return "1" if v else "0"
|
||||
else:
|
||||
return v
|
||||
|
||||
|
||||
def __bool_string(v):
|
||||
if isinstance(v, bool):
|
||||
return "true" if v else "false"
|
||||
|
||||
return v
|
||||
|
||||
def __crc(source):
|
||||
return str((zlib.crc32(to_bytearray(source)) & 0xffffffff) % 5 + 1)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue