thumbor-libs-blackhand 0.2.0__py3-none-any.whl → 0.5.0__py3-none-any.whl
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.
- thumbor_libs_blackhand/result_storages/hybrid_result_storage.py +12 -9
- thumbor_libs_blackhand/result_storages/{mongodb_result_storage.py → mongodb_result_storage_v3.py} +23 -0
- thumbor_libs_blackhand/result_storages/mongodb_result_storage_v4.py +209 -0
- thumbor_libs_blackhand/result_storages/mongodb_result_storage_v5.py +178 -0
- {thumbor_libs_blackhand-0.2.0.dist-info → thumbor_libs_blackhand-0.5.0.dist-info}/METADATA +6 -3
- {thumbor_libs_blackhand-0.2.0.dist-info → thumbor_libs_blackhand-0.5.0.dist-info}/RECORD +9 -9
- {thumbor_libs_blackhand-0.2.0.dist-info → thumbor_libs_blackhand-0.5.0.dist-info}/WHEEL +1 -1
- thumbor_libs_blackhand/result_storages/mongodb_legacy_result_storage.py +0 -105
- thumbor_libs_blackhand/result_storages/mongodb_webp_result_storage.py +0 -103
- {thumbor_libs_blackhand-0.2.0.dist-info → thumbor_libs_blackhand-0.5.0.dist-info}/LICENSE +0 -0
- {thumbor_libs_blackhand-0.2.0.dist-info → thumbor_libs_blackhand-0.5.0.dist-info}/top_level.txt +0 -0
|
@@ -47,7 +47,7 @@ class Storage(BaseStorage):
|
|
|
47
47
|
|
|
48
48
|
db_name = self.context.config.MONGO_RESULT_STORAGE_SERVER_DB
|
|
49
49
|
col_name = self.context.config.MONGO_RESULT_STORAGE_SERVER_COLLECTION
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
try:
|
|
52
52
|
uri = self.context.config.MONGO_RESULT_STORAGE_URI
|
|
53
53
|
except AttributeError:
|
|
@@ -111,13 +111,13 @@ class Storage(BaseStorage):
|
|
|
111
111
|
#max_age = self.get_max_age()
|
|
112
112
|
#result_ttl = self.get_max_age()
|
|
113
113
|
try:
|
|
114
|
-
CACHE_PATH = self.context.config.CACHE_PATH
|
|
114
|
+
CACHE_PATH = self.context.config.CACHE_PATH
|
|
115
115
|
except AttributeError:
|
|
116
116
|
raise
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
+ datetime.now().strftime('%m') + '/' + datetime.now().strftime('%d')
|
|
117
|
+
imgpath = ( datetime.now().strftime('%Y') + '/'
|
|
118
|
+
+ datetime.now().strftime('%m') + '/' + datetime.now().strftime('%d')
|
|
120
119
|
+ '/' + datetime.now().strftime('%H'))
|
|
120
|
+
mkpath = (CACHE_PATH + '/' + imgpath)
|
|
121
121
|
self.ensure_dir(mkpath)
|
|
122
122
|
|
|
123
123
|
#os.makedirs(mkpath)
|
|
@@ -144,7 +144,7 @@ class Storage(BaseStorage):
|
|
|
144
144
|
'content_type': BaseEngine.get_mimetype(image_bytes),
|
|
145
145
|
'ref_id': ref_img2,
|
|
146
146
|
'content_length' : len(image_bytes),
|
|
147
|
-
'cache_path':
|
|
147
|
+
'cache_path': imgpath,
|
|
148
148
|
'cache_id' : cache_id
|
|
149
149
|
}
|
|
150
150
|
doc_cpm = dict(doc)
|
|
@@ -165,7 +165,10 @@ class Storage(BaseStorage):
|
|
|
165
165
|
|
|
166
166
|
key = self.get_key_from_request()
|
|
167
167
|
logger.debug("[RESULT_STORAGE] image not found at %s", key)
|
|
168
|
-
|
|
168
|
+
try:
|
|
169
|
+
CACHE_PATH = self.context.config.CACHE_PATH
|
|
170
|
+
except AttributeError:
|
|
171
|
+
raise
|
|
169
172
|
age = datetime.utcnow() - timedelta(
|
|
170
173
|
seconds=self.get_max_age()
|
|
171
174
|
)
|
|
@@ -195,8 +198,8 @@ class Storage(BaseStorage):
|
|
|
195
198
|
metadata['Cache-Control'] = "max-age=60,public"
|
|
196
199
|
metadata['ContentLength'] = stored['content_length']
|
|
197
200
|
metadata['ContentType'] = stored['content_type']
|
|
198
|
-
cachefile = stored['cache_path'] + "/" + stored['cache_id']
|
|
199
|
-
|
|
201
|
+
cachefile = CACHE_PATH + '/' + stored['cache_path'] + "/" + stored['cache_id']
|
|
202
|
+
|
|
200
203
|
fichier = open(cachefile, "rb")
|
|
201
204
|
try:
|
|
202
205
|
tosend = fichier.read()
|
thumbor_libs_blackhand/result_storages/{mongodb_result_storage.py → mongodb_result_storage_v3.py}
RENAMED
|
@@ -158,6 +158,29 @@ class Storage(BaseStorage):
|
|
|
158
158
|
|
|
159
159
|
if not stored:
|
|
160
160
|
return None
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
#self.context.config.MONGO_RESULT_STORAGE_MAXCACHESIZE
|
|
164
|
+
dedup = self.context.config.MONGO_RESULT_STORAGE_DEDUP
|
|
165
|
+
except:
|
|
166
|
+
dedup = False
|
|
167
|
+
|
|
168
|
+
if not dedup:
|
|
169
|
+
logger.debug("Deduplication OFF")
|
|
170
|
+
else:
|
|
171
|
+
filter={
|
|
172
|
+
'path': key
|
|
173
|
+
}
|
|
174
|
+
sort=list({
|
|
175
|
+
'created_at': -1
|
|
176
|
+
}.items())
|
|
177
|
+
skip=1
|
|
178
|
+
obj = self.storage.find(filter=filter, skip=skip)
|
|
179
|
+
async for doc in obj:
|
|
180
|
+
logger.info("Deduplication %s", key)
|
|
181
|
+
self.storage.delete_one({"_id": doc["_id"]})
|
|
182
|
+
|
|
183
|
+
|
|
161
184
|
metadata = stored['metadata']
|
|
162
185
|
metadata['LastModified'] = stored['created_at'].replace(
|
|
163
186
|
tzinfo=pytz.utc
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Blackhand library for Thumbor
|
|
3
|
+
# Licensed under the MIT license:
|
|
4
|
+
# http://www.opensource.org/licenses/mit-license
|
|
5
|
+
|
|
6
|
+
from urllib import parse #, request; error
|
|
7
|
+
#from motor.motor_tornado import MotorGridFSBucket
|
|
8
|
+
#from pymongo.errors import PyMongoError
|
|
9
|
+
from thumbor_libs_blackhand.mongodb.pool_result_storage import MongoConnector
|
|
10
|
+
from datetime import datetime, timedelta
|
|
11
|
+
from thumbor.result_storages import BaseStorage, ResultStorageResult
|
|
12
|
+
from thumbor.engines import BaseEngine
|
|
13
|
+
from thumbor.utils import logger
|
|
14
|
+
from bson.binary import Binary
|
|
15
|
+
import pytz
|
|
16
|
+
import re
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Storage(BaseStorage):
|
|
20
|
+
|
|
21
|
+
def __init__(self, context):
|
|
22
|
+
BaseStorage.__init__(self, context)
|
|
23
|
+
self.database, self.storage = self.__conn__()
|
|
24
|
+
super(Storage, self).__init__(context)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def __conn__(self):
|
|
29
|
+
'''Return the MongoDB params.
|
|
30
|
+
:returns: MongoDB DB and Collection
|
|
31
|
+
:rtype: pymongo.database.Database, pymongo.database.Collection
|
|
32
|
+
'''
|
|
33
|
+
|
|
34
|
+
password = parse.quote_plus(self.context.config.MONGO_RESULT_STORAGE_SERVER_PASSWORD)
|
|
35
|
+
user = parse.quote_plus(self.context.config.MONGO_RESULT_STORAGE_SERVER_USER)
|
|
36
|
+
if not self.context.config.MONGO_RESULT_STORAGE_SERVER_REPLICASET:
|
|
37
|
+
uric = ('mongodb://'+ user +':' + password + '@' + self.context.config.MONGO_RESULT_STORAGE_SERVER_HOSTS
|
|
38
|
+
+ '/?authSource=' + self.context.config.MONGO_RESULT_STORAGE_SERVER_DB)
|
|
39
|
+
else:
|
|
40
|
+
uric = ('mongodb://'+ user +':' + password + '@' + self.context.config.MONGO_RESULT_STORAGE_SERVER_HOSTS
|
|
41
|
+
+ '/?authSource=' + self.context.config.MONGO_RESULT_STORAGE_SERVER_DB
|
|
42
|
+
+ "&replicaSet=" + self.context.config.MONGO_RESULT_STORAGE_SERVER_REPLICASET
|
|
43
|
+
+ "&readPreference=" + self.context.config.MONGO_RESULT_STORAGE_SERVER_READ_PREFERENCE)
|
|
44
|
+
|
|
45
|
+
db_name = self.context.config.MONGO_RESULT_STORAGE_SERVER_DB
|
|
46
|
+
col_name = self.context.config.MONGO_RESULT_STORAGE_SERVER_COLLECTION
|
|
47
|
+
host = None
|
|
48
|
+
port = None
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
uri = self.context.config.MONGO_RESULT_STORAGE_URI
|
|
52
|
+
except AttributeError:
|
|
53
|
+
uri=uric
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
host = self.context.config.MONGO_RESULT_STORAGE_SERVER_HOST
|
|
57
|
+
port = self.context.config.MONGO_RESULT_STORAGE_SERVER_PORT
|
|
58
|
+
except AttributeError:
|
|
59
|
+
host=None
|
|
60
|
+
port=None
|
|
61
|
+
|
|
62
|
+
mongo_conn = MongoConnector(
|
|
63
|
+
db_name=db_name,
|
|
64
|
+
col_name=col_name,
|
|
65
|
+
uri=uri,
|
|
66
|
+
host=host,
|
|
67
|
+
port=port,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
database = mongo_conn.db_conn
|
|
71
|
+
storage = mongo_conn.col_conn
|
|
72
|
+
|
|
73
|
+
return database, storage
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def is_auto_webp(self):
|
|
77
|
+
'''Return if webp.
|
|
78
|
+
:return: If the file is a webp
|
|
79
|
+
:rettype: boolean
|
|
80
|
+
'''
|
|
81
|
+
|
|
82
|
+
return self.context.config.AUTO_WEBP \
|
|
83
|
+
and self.context.request.accepts_webp
|
|
84
|
+
|
|
85
|
+
def get_key_from_request(self):
|
|
86
|
+
'''Return a path key for the current request url.
|
|
87
|
+
:return: The storage key for the current url
|
|
88
|
+
:rettype: string
|
|
89
|
+
'''
|
|
90
|
+
|
|
91
|
+
path = f"result:{self.context.request.url}"
|
|
92
|
+
|
|
93
|
+
if self.is_auto_webp:
|
|
94
|
+
return f'{path}/webp'
|
|
95
|
+
|
|
96
|
+
return path
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def get_max_age(self):
|
|
100
|
+
|
|
101
|
+
return self.context.config.RESULT_STORAGE_EXPIRATION_SECONDS
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
async def put(self, image_bytes):
|
|
105
|
+
key = self.get_key_from_request()
|
|
106
|
+
#max_age = self.get_max_age()
|
|
107
|
+
#result_ttl = self.get_max_age()
|
|
108
|
+
ref_img = ''
|
|
109
|
+
ref_img = re.findall(r'/[a-zA-Z0-9]{24}(?:$|/)', key)
|
|
110
|
+
if ref_img:
|
|
111
|
+
ref_img2 = ref_img[0].replace('/','')
|
|
112
|
+
else:
|
|
113
|
+
ref_img2 = 'undef'
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
if self.context.config.get("MONGO_STORE_METADATA", False):
|
|
117
|
+
metadata = dict(self.context.headers)
|
|
118
|
+
else:
|
|
119
|
+
metadata = {}
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
listeProduit = self.context.config.PRODUIT
|
|
123
|
+
produit=''
|
|
124
|
+
for s in listeProduit:
|
|
125
|
+
if key.find(s) >= 1:
|
|
126
|
+
produit=s.replace('/', '')
|
|
127
|
+
break
|
|
128
|
+
else:
|
|
129
|
+
produit="info"
|
|
130
|
+
except AttributeError:
|
|
131
|
+
produit = 'undef'
|
|
132
|
+
|
|
133
|
+
doc = {
|
|
134
|
+
'path': key,
|
|
135
|
+
'created_at': datetime.utcnow(),
|
|
136
|
+
'data': Binary(image_bytes),
|
|
137
|
+
'metadata': metadata,
|
|
138
|
+
'content_type': BaseEngine.get_mimetype(image_bytes),
|
|
139
|
+
'ref_id': ref_img2,
|
|
140
|
+
'content_length' : len(image_bytes),
|
|
141
|
+
'produit': produit
|
|
142
|
+
}
|
|
143
|
+
doc_cpm = dict(doc)
|
|
144
|
+
|
|
145
|
+
await self.storage.insert_one(doc_cpm)
|
|
146
|
+
#return self.context.request.url
|
|
147
|
+
return key
|
|
148
|
+
|
|
149
|
+
async def get(self):
|
|
150
|
+
key = self.get_key_from_request()
|
|
151
|
+
logger.debug("[RESULT_STORAGE] image not found at %s", key)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
age = datetime.utcnow() - timedelta(
|
|
155
|
+
seconds=self.get_max_age()
|
|
156
|
+
)
|
|
157
|
+
stored = await self.storage.find_one({
|
|
158
|
+
'path': key,
|
|
159
|
+
'created_at': {
|
|
160
|
+
'$gte': age
|
|
161
|
+
},
|
|
162
|
+
}, {
|
|
163
|
+
'ref_id': True,
|
|
164
|
+
'created_at': True,
|
|
165
|
+
'metadata': True,
|
|
166
|
+
'content_type': True,
|
|
167
|
+
'data' : True,
|
|
168
|
+
'content_length': True,
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
if not stored:
|
|
173
|
+
return None
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
#self.context.config.MONGO_RESULT_STORAGE_MAXCACHESIZE
|
|
177
|
+
dedup = self.context.config.MONGO_RESULT_STORAGE_DEDUP
|
|
178
|
+
except:
|
|
179
|
+
dedup = False
|
|
180
|
+
|
|
181
|
+
if not dedup:
|
|
182
|
+
logger.debug("Deduplication OFF")
|
|
183
|
+
else:
|
|
184
|
+
filter={
|
|
185
|
+
'path': key
|
|
186
|
+
}
|
|
187
|
+
sort=list({
|
|
188
|
+
'created_at': -1
|
|
189
|
+
}.items())
|
|
190
|
+
skip=1
|
|
191
|
+
obj = self.storage.find(filter=filter, skip=skip)
|
|
192
|
+
async for doc in obj:
|
|
193
|
+
logger.info("Deduplication %s", key)
|
|
194
|
+
self.storage.delete_one({"_id": doc["_id"]})
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
metadata = stored['metadata']
|
|
198
|
+
metadata['LastModified'] = stored['created_at'].replace(
|
|
199
|
+
tzinfo=pytz.utc
|
|
200
|
+
)
|
|
201
|
+
metadata['Cache-Control'] = "max-age=60,public"
|
|
202
|
+
metadata['ContentLength'] = stored['content_length']
|
|
203
|
+
metadata['ContentType'] = stored['content_type']
|
|
204
|
+
|
|
205
|
+
return ResultStorageResult(
|
|
206
|
+
buffer=stored['data'],
|
|
207
|
+
metadata=metadata,
|
|
208
|
+
successful=True
|
|
209
|
+
)
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Blackhand library for Thumbor
|
|
3
|
+
# Licensed under the MIT license:
|
|
4
|
+
# http://www.opensource.org/licenses/mit-license
|
|
5
|
+
|
|
6
|
+
from urllib import parse #, request; error
|
|
7
|
+
from thumbor_libs_blackhand.mongodb.pool_result_storage import MongoConnector
|
|
8
|
+
from datetime import datetime, timedelta
|
|
9
|
+
from thumbor.result_storages import BaseStorage, ResultStorageResult
|
|
10
|
+
from thumbor.engines import BaseEngine
|
|
11
|
+
from thumbor.utils import logger
|
|
12
|
+
from bson.binary import Binary
|
|
13
|
+
import pytz
|
|
14
|
+
import re
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Storage(BaseStorage):
|
|
18
|
+
|
|
19
|
+
def __init__(self, context):
|
|
20
|
+
BaseStorage.__init__(self, context)
|
|
21
|
+
self.database, self.storage = self.__conn__()
|
|
22
|
+
super(Storage, self).__init__(context)
|
|
23
|
+
|
|
24
|
+
def __conn__(self):
|
|
25
|
+
try:
|
|
26
|
+
uri = self.context.config.MONGO_RESULT_STORAGE_SERVER_URI
|
|
27
|
+
db_name=self.context.config.MONGO_RESULT_STORAGE_SERVER_DB
|
|
28
|
+
col_name=self.context.config.MONGO_RESULT_STORAGE_SERVER_COLLECTION
|
|
29
|
+
except AttributeError:
|
|
30
|
+
logger.error("[RESULT_STORAGE] Parametres databases incomplet")
|
|
31
|
+
|
|
32
|
+
mongo_conn = MongoConnector(
|
|
33
|
+
db_name=db_name,
|
|
34
|
+
col_name=col_name,
|
|
35
|
+
uri=uri,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
database = mongo_conn.db_conn
|
|
39
|
+
storage = mongo_conn.col_conn
|
|
40
|
+
|
|
41
|
+
return database, storage
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def is_auto_webp(self):
|
|
46
|
+
'''Return if webp.
|
|
47
|
+
:return: If the file is a webp
|
|
48
|
+
:rettype: boolean
|
|
49
|
+
'''
|
|
50
|
+
|
|
51
|
+
return self.context.config.AUTO_WEBP \
|
|
52
|
+
and self.context.request.accepts_webp
|
|
53
|
+
|
|
54
|
+
def get_key_from_request(self):
|
|
55
|
+
'''Return a path key for the current request url.
|
|
56
|
+
:return: The storage key for the current url
|
|
57
|
+
:rettype: string
|
|
58
|
+
'''
|
|
59
|
+
|
|
60
|
+
path = f"result:{self.context.request.url}"
|
|
61
|
+
|
|
62
|
+
if self.is_auto_webp:
|
|
63
|
+
return f'{path}/webp'
|
|
64
|
+
|
|
65
|
+
return path
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def get_max_age(self):
|
|
69
|
+
|
|
70
|
+
return self.context.config.RESULT_STORAGE_EXPIRATION_SECONDS
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
async def put(self, image_bytes):
|
|
74
|
+
key = self.get_key_from_request()
|
|
75
|
+
#max_age = self.get_max_age()
|
|
76
|
+
#result_ttl = self.get_max_age()
|
|
77
|
+
ref_img = ''
|
|
78
|
+
ref_img = re.findall(r'/[a-zA-Z0-9]{24}(?:$|/)', key)
|
|
79
|
+
if ref_img:
|
|
80
|
+
ref_img2 = ref_img[0].replace('/','')
|
|
81
|
+
else:
|
|
82
|
+
ref_img2 = 'undef'
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
if self.context.config.get("MONGO_STORE_METADATA", False):
|
|
86
|
+
metadata = dict(self.context.headers)
|
|
87
|
+
else:
|
|
88
|
+
metadata = {}
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
listeProduit = self.context.config.PRODUIT
|
|
92
|
+
produit=''
|
|
93
|
+
for s in listeProduit:
|
|
94
|
+
if key.find(s) >= 1:
|
|
95
|
+
produit=s.replace('/', '')
|
|
96
|
+
break
|
|
97
|
+
else:
|
|
98
|
+
produit="info"
|
|
99
|
+
except AttributeError:
|
|
100
|
+
produit = 'undef'
|
|
101
|
+
|
|
102
|
+
doc = {
|
|
103
|
+
'path': key,
|
|
104
|
+
'created_at': datetime.utcnow(),
|
|
105
|
+
'data': Binary(image_bytes),
|
|
106
|
+
'metadata': metadata,
|
|
107
|
+
'content_type': BaseEngine.get_mimetype(image_bytes),
|
|
108
|
+
'ref_id': ref_img2,
|
|
109
|
+
'content_length' : len(image_bytes),
|
|
110
|
+
'produit': produit
|
|
111
|
+
}
|
|
112
|
+
doc_cpm = dict(doc)
|
|
113
|
+
|
|
114
|
+
await self.storage.insert_one(doc_cpm)
|
|
115
|
+
#return self.context.request.url
|
|
116
|
+
return key
|
|
117
|
+
|
|
118
|
+
async def get(self):
|
|
119
|
+
key = self.get_key_from_request()
|
|
120
|
+
logger.debug("[RESULT_STORAGE] image not found at %s", key)
|
|
121
|
+
|
|
122
|
+
print(key)
|
|
123
|
+
age = datetime.utcnow() - timedelta(
|
|
124
|
+
seconds=self.get_max_age()
|
|
125
|
+
)
|
|
126
|
+
stored = await self.storage.find_one({
|
|
127
|
+
'path': key,
|
|
128
|
+
'created_at': {
|
|
129
|
+
'$gte': age
|
|
130
|
+
},
|
|
131
|
+
}, {
|
|
132
|
+
'ref_id': True,
|
|
133
|
+
'created_at': True,
|
|
134
|
+
'metadata': True,
|
|
135
|
+
'content_type': True,
|
|
136
|
+
'data' : True,
|
|
137
|
+
'content_length': True,
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
if not stored:
|
|
142
|
+
return None
|
|
143
|
+
|
|
144
|
+
try:
|
|
145
|
+
#self.context.config.MONGO_RESULT_STORAGE_MAXCACHESIZE
|
|
146
|
+
dedup = self.context.config.MONGO_RESULT_STORAGE_DEDUP
|
|
147
|
+
except:
|
|
148
|
+
dedup = False
|
|
149
|
+
|
|
150
|
+
if not dedup:
|
|
151
|
+
logger.debug("Deduplication OFF")
|
|
152
|
+
else:
|
|
153
|
+
filter={
|
|
154
|
+
'path': key
|
|
155
|
+
}
|
|
156
|
+
sort=list({
|
|
157
|
+
'created_at': -1
|
|
158
|
+
}.items())
|
|
159
|
+
skip=1
|
|
160
|
+
obj = self.storage.find(filter=filter, skip=skip)
|
|
161
|
+
async for doc in obj:
|
|
162
|
+
logger.info("Deduplication %s", key)
|
|
163
|
+
self.storage.delete_one({"_id": doc["_id"]})
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
metadata = stored['metadata']
|
|
167
|
+
metadata['LastModified'] = stored['created_at'].replace(
|
|
168
|
+
tzinfo=pytz.utc
|
|
169
|
+
)
|
|
170
|
+
metadata['Cache-Control'] = "max-age=60,public"
|
|
171
|
+
metadata['ContentLength'] = stored['content_length']
|
|
172
|
+
metadata['ContentType'] = stored['content_type']
|
|
173
|
+
|
|
174
|
+
return ResultStorageResult(
|
|
175
|
+
buffer=stored['data'],
|
|
176
|
+
metadata=metadata,
|
|
177
|
+
successful=True
|
|
178
|
+
)
|
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: thumbor-libs-blackhand
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: libs thumbor
|
|
5
5
|
Home-page: https://github.com/Bkhand/thumbor_libs_blackhand
|
|
6
6
|
Author: Bertrand Thill
|
|
7
7
|
Author-email: github@blackhand.org
|
|
8
8
|
License: GNU
|
|
9
9
|
Keywords: thumbor,fallback,images,nfs,mongodb
|
|
10
|
+
Platform: UNKNOWN
|
|
10
11
|
Classifier: Development Status :: 4 - Beta
|
|
11
12
|
Classifier: Intended Audience :: Developers
|
|
12
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
13
14
|
Classifier: Natural Language :: French
|
|
14
15
|
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
17
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
17
18
|
Classifier: Topic :: Multimedia :: Graphics :: Presentation
|
|
18
19
|
License-File: LICENSE
|
|
19
|
-
Requires-Dist: thumbor (>=7.
|
|
20
|
+
Requires-Dist: thumbor (>=7.7.0)
|
|
20
21
|
Requires-Dist: pymongo (>=4.2.0)
|
|
21
22
|
|
|
22
23
|
This module enable mongodb support and fallback for thumbor.
|
|
24
|
+
|
|
25
|
+
|
|
@@ -5,16 +5,16 @@ thumbor_libs_blackhand/loaders/specb_file_fallback_file_loader.py,sha256=842cYNb
|
|
|
5
5
|
thumbor_libs_blackhand/metrics/__init__.py,sha256=5izS_bmGrlIsSPAU1JYYvHuAaq2qHHanGXr_dPPtyuo,138
|
|
6
6
|
thumbor_libs_blackhand/mongodb/pool_result_storage.py,sha256=ZHARWEFGacLXjtxqVxVmwFGOS94w0SkhkwC96VM00LE,1781
|
|
7
7
|
thumbor_libs_blackhand/result_storages/__init__.py,sha256=5izS_bmGrlIsSPAU1JYYvHuAaq2qHHanGXr_dPPtyuo,138
|
|
8
|
-
thumbor_libs_blackhand/result_storages/hybrid_result_storage.py,sha256=
|
|
9
|
-
thumbor_libs_blackhand/result_storages/
|
|
10
|
-
thumbor_libs_blackhand/result_storages/
|
|
11
|
-
thumbor_libs_blackhand/result_storages/
|
|
8
|
+
thumbor_libs_blackhand/result_storages/hybrid_result_storage.py,sha256=wFUzMeZxPzU_BvFjNJWZPh7bCla0x-s7Iwj6TiFOgDE,6672
|
|
9
|
+
thumbor_libs_blackhand/result_storages/mongodb_result_storage_v3.py,sha256=e1wIBcLx6lN_gDDPaJTelQdKyB3zyxPQPjCMLugx-uY,6075
|
|
10
|
+
thumbor_libs_blackhand/result_storages/mongodb_result_storage_v4.py,sha256=rTGtpfVZamErPfCA-z3-3vgf5uP_HDH3f3Sq8WOg19w,6462
|
|
11
|
+
thumbor_libs_blackhand/result_storages/mongodb_result_storage_v5.py,sha256=_wElwxgrF-QYLc5VyKMdUxsknc403QihGwP1AFaVQ4U,5068
|
|
12
12
|
thumbor_libs_blackhand/storages/__init__.py,sha256=5izS_bmGrlIsSPAU1JYYvHuAaq2qHHanGXr_dPPtyuo,138
|
|
13
13
|
thumbor_libs_blackhand/storages/mongodb_webp_storage.py,sha256=QSLAkoiqXRQXDq-2HUWGUTsz9hNyatKQg7_LB6ikN6o,4401
|
|
14
14
|
thumbor_libs_blackhand/url_signers/__init__.py,sha256=5izS_bmGrlIsSPAU1JYYvHuAaq2qHHanGXr_dPPtyuo,138
|
|
15
15
|
thumbor_libs_blackhand/url_signers/base64_hmac_sha1_trim.py,sha256=4TNCfNqBRRQgTo90N3Ej2SDo2jYzzDm31hOFrk0BsVc,583
|
|
16
|
-
thumbor_libs_blackhand-0.
|
|
17
|
-
thumbor_libs_blackhand-0.
|
|
18
|
-
thumbor_libs_blackhand-0.
|
|
19
|
-
thumbor_libs_blackhand-0.
|
|
20
|
-
thumbor_libs_blackhand-0.
|
|
16
|
+
thumbor_libs_blackhand-0.5.0.dist-info/LICENSE,sha256=H3K2DdEDDUNhtBvyWkPh8SodWqMuiOefZWwdDxA98VE,1079
|
|
17
|
+
thumbor_libs_blackhand-0.5.0.dist-info/METADATA,sha256=C7NPwUArCyGScWcXtJO_xI9VJMTvZmc4UrF35wliD0k,831
|
|
18
|
+
thumbor_libs_blackhand-0.5.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
|
|
19
|
+
thumbor_libs_blackhand-0.5.0.dist-info/top_level.txt,sha256=qs2ZPZFH1pQYGF-qqZzRbRbPHdtBugtefP7mUslef90,23
|
|
20
|
+
thumbor_libs_blackhand-0.5.0.dist-info/RECORD,,
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
# Blackhand library for Thumbor
|
|
3
|
-
# Licensed under the MIT license:
|
|
4
|
-
# http://www.opensource.org/licenses/mit-license
|
|
5
|
-
import urllib.request, urllib.parse, urllib.error
|
|
6
|
-
import re
|
|
7
|
-
from datetime import datetime, timedelta
|
|
8
|
-
#from io import StringIO
|
|
9
|
-
from thumbor.result_storages import BaseStorage
|
|
10
|
-
from thumbor.utils import logger
|
|
11
|
-
from bson.binary import Binary
|
|
12
|
-
from pymongo.mongo_client import MongoClient
|
|
13
|
-
from sys import getsizeof
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class Storage(BaseStorage):
|
|
17
|
-
@property
|
|
18
|
-
def is_auto_webp(self):
|
|
19
|
-
return self.context.config.AUTO_WEBP and self.context.request.accepts_webp
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def __conn__(self):
|
|
23
|
-
#server_api = ServerApi('1', strict=True)
|
|
24
|
-
#client = MongoClient(self.context.config.MONGO_RESULT_STORAGE_URI) #, server_api=server_api)
|
|
25
|
-
#db = client[self.context.config.MONGO_RESULT_STORAGE_SERVER_DB]
|
|
26
|
-
#storage = self.context.config.MONGO_RESULT_STORAGE_SERVER_COLLECTION
|
|
27
|
-
#return db, storage
|
|
28
|
-
|
|
29
|
-
password = urllib.parse.quote_plus(self.context.config.MONGO_RESULT_STORAGE_SERVER_PASSWORD)
|
|
30
|
-
user = urllib.parse.quote_plus(self.context.config.MONGO_RESULT_STORAGE_SERVER_USER)
|
|
31
|
-
if not self.context.config.MONGO_RESULT_STORAGE_SERVER_REPLICASET:
|
|
32
|
-
uri = 'mongodb://'+ user +':' + password + '@' + self.context.config.MONGO_RESULT_STORAGE_SERVER_HOST + '/?authSource=' + self.context.config.MONGO_RESULT_STORAGE_SERVER_DB
|
|
33
|
-
else:
|
|
34
|
-
uri = 'mongodb://'+ user +':' + password + '@' + self.context.config.MONGO_RESULT_STORAGE_SERVER_HOST + '/?authSource=' + self.context.config.MONGO_RESULT_STORAGE_SERVER_DB + "&replicaSet=" + self.context.config.MONGO_RESULT_STORAGE_SERVER_REPLICASET + "&readPreference=" + self.context.config.MONGO_RESULT_STORAGE_SERVER_READ_PREFERENCE
|
|
35
|
-
client = MongoClient(uri)
|
|
36
|
-
db = client[self.context.config.MONGO_RESULT_STORAGE_SERVER_DB]
|
|
37
|
-
storage = self.context.config.MONGO_RESULT_STORAGE_SERVER_COLLECTION
|
|
38
|
-
return db, storage
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def get_max_age(self):
|
|
42
|
-
default_ttl = self.context.config.RESULT_STORAGE_EXPIRATION_SECONDS
|
|
43
|
-
if self.context.request.max_age == 0:
|
|
44
|
-
return self.context.request.max_age
|
|
45
|
-
return default_ttl
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def get_key_from_request(self):
|
|
49
|
-
path = "result:%s" % self.context.request.url
|
|
50
|
-
return path
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
async def put(self, image_bytes):
|
|
54
|
-
db, storage = self.__conn__()
|
|
55
|
-
key = self.get_key_from_request()
|
|
56
|
-
#max_age = self.get_max_age()
|
|
57
|
-
#result_ttl = self.get_max_age()
|
|
58
|
-
ref_img = ''
|
|
59
|
-
ref_img = re.findall(r'/[a-zA-Z0-9]{24}(?:$|/)', key)
|
|
60
|
-
if ref_img:
|
|
61
|
-
ref_img2 = ref_img[0].replace('/','')
|
|
62
|
-
else:
|
|
63
|
-
ref_img2 = 'undef'
|
|
64
|
-
|
|
65
|
-
if self.is_auto_webp:
|
|
66
|
-
content_t = 'webp'
|
|
67
|
-
else:
|
|
68
|
-
content_t = 'default'
|
|
69
|
-
doc = {
|
|
70
|
-
'path': key,
|
|
71
|
-
'created_at': datetime.utcnow(),
|
|
72
|
-
'data': Binary(image_bytes),
|
|
73
|
-
'content-type': content_t,
|
|
74
|
-
'ref_id': ref_img2
|
|
75
|
-
}
|
|
76
|
-
doc_cpm = dict(doc)
|
|
77
|
-
|
|
78
|
-
try:
|
|
79
|
-
#self.context.config.MONGO_RESULT_STORAGE_MAXCACHESIZE
|
|
80
|
-
maxs = self.context.config.MONGO_RESULT_STORAGE_MAXCACHESIZE
|
|
81
|
-
except:
|
|
82
|
-
maxs = 16000000
|
|
83
|
-
|
|
84
|
-
amd = getsizeof(bytes)
|
|
85
|
-
if amd > maxs:
|
|
86
|
-
logger.warning(u"OVERSIZE %s: %s > %s pas de mise en cache possible", key, amd, maxs)
|
|
87
|
-
return None
|
|
88
|
-
else:
|
|
89
|
-
db[storage].insert_one(doc_cpm)
|
|
90
|
-
return key
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
async def get(self):
|
|
96
|
-
db, storage = self.__conn__()
|
|
97
|
-
key = self.get_key_from_request()
|
|
98
|
-
logger.debug("[RESULT_STORAGE] image not found at %s", key)
|
|
99
|
-
|
|
100
|
-
result = db[storage].find_one({'path': key }) #, 'content-type': "default"})
|
|
101
|
-
|
|
102
|
-
if not result:
|
|
103
|
-
return None
|
|
104
|
-
tosend = result['data']
|
|
105
|
-
return tosend
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
# Blackhand library for Thumbor
|
|
3
|
-
# Licensed under the MIT license:
|
|
4
|
-
# http://www.opensource.org/licenses/mit-license
|
|
5
|
-
import urllib.request, urllib.parse, urllib.error
|
|
6
|
-
import re
|
|
7
|
-
from datetime import datetime, timedelta
|
|
8
|
-
#from io import StringIO
|
|
9
|
-
from thumbor.result_storages import BaseStorage
|
|
10
|
-
from thumbor.utils import logger
|
|
11
|
-
from bson.binary import Binary
|
|
12
|
-
from pymongo.mongo_client import MongoClient
|
|
13
|
-
from sys import getsizeof
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class Storage(BaseStorage):
|
|
17
|
-
@property
|
|
18
|
-
def is_auto_webp(self):
|
|
19
|
-
return self.context.config.AUTO_WEBP and self.context.request.accepts_webp
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def __conn__(self):
|
|
23
|
-
#server_api = ServerApi('1', strict=True)
|
|
24
|
-
#client = MongoClient(self.context.config.MONGO_RESULT_STORAGE_URI) #, server_api=server_api)
|
|
25
|
-
#db = client[self.context.config.MONGO_RESULT_STORAGE_SERVER_DB]
|
|
26
|
-
#storage = self.context.config.MONGO_RESULT_STORAGE_SERVER_COLLECTION
|
|
27
|
-
#return db, storage
|
|
28
|
-
|
|
29
|
-
password = urllib.parse.quote_plus(self.context.config.MONGO_RESULT_STORAGE_SERVER_PASSWORD)
|
|
30
|
-
user = urllib.parse.quote_plus(self.context.config.MONGO_RESULT_STORAGE_SERVER_USER)
|
|
31
|
-
if not self.context.config.MONGO_RESULT_STORAGE_SERVER_REPLICASET:
|
|
32
|
-
uri = 'mongodb://'+ user +':' + password + '@' + self.context.config.MONGO_RESULT_STORAGE_SERVER_HOST + '/?authSource=' + self.context.config.MONGO_RESULT_STORAGE_SERVER_DB
|
|
33
|
-
else:
|
|
34
|
-
uri = 'mongodb://'+ user +':' + password + '@' + self.context.config.MONGO_RESULT_STORAGE_SERVER_HOST + '/?authSource=' + self.context.config.MONGO_RESULT_STORAGE_SERVER_DB + "&replicaSet=" + self.context.config.MONGO_RESULT_STORAGE_SERVER_REPLICASET + "&readPreference=" + self.context.config.MONGO_RESULT_STORAGE_SERVER_READ_PREFERENCE
|
|
35
|
-
client = MongoClient(uri)
|
|
36
|
-
db = client[self.context.config.MONGO_RESULT_STORAGE_SERVER_DB]
|
|
37
|
-
storage = self.context.config.MONGO_RESULT_STORAGE_SERVER_COLLECTION
|
|
38
|
-
return db, storage
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def get_max_age(self):
|
|
42
|
-
default_ttl = self.context.config.RESULT_STORAGE_EXPIRATION_SECONDS
|
|
43
|
-
if self.context.request.max_age == 0:
|
|
44
|
-
return self.context.request.max_age
|
|
45
|
-
return default_ttl
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def get_key_from_request(self):
|
|
49
|
-
path = "result:%s" % self.context.request.url
|
|
50
|
-
return path
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
async def put(self, image_bytes):
|
|
54
|
-
db, storage = self.__conn__()
|
|
55
|
-
key = self.get_key_from_request()
|
|
56
|
-
#max_age = self.get_max_age()
|
|
57
|
-
#result_ttl = self.get_max_age()
|
|
58
|
-
ref_img = ''
|
|
59
|
-
ref_img = re.findall(r'/[a-zA-Z0-9]{24}(?:$|/)', key)
|
|
60
|
-
if ref_img:
|
|
61
|
-
ref_img2 = ref_img[0].replace('/','')
|
|
62
|
-
else:
|
|
63
|
-
ref_img2 = 'undef'
|
|
64
|
-
|
|
65
|
-
if self.is_auto_webp:
|
|
66
|
-
content_t = 'webp'
|
|
67
|
-
else:
|
|
68
|
-
content_t = 'default'
|
|
69
|
-
doc = {
|
|
70
|
-
'path': key,
|
|
71
|
-
'created_at': datetime.utcnow(),
|
|
72
|
-
'data': Binary(image_bytes),
|
|
73
|
-
'content-type': content_t,
|
|
74
|
-
'ref_id': ref_img2
|
|
75
|
-
}
|
|
76
|
-
doc_cpm = dict(doc)
|
|
77
|
-
|
|
78
|
-
try:
|
|
79
|
-
#self.context.config.MONGO_RESULT_STORAGE_MAXCACHESIZE
|
|
80
|
-
maxs = self.context.config.MONGO_RESULT_STORAGE_MAXCACHESIZE
|
|
81
|
-
except:
|
|
82
|
-
maxs = 16000000
|
|
83
|
-
|
|
84
|
-
amd = getsizeof(bytes)
|
|
85
|
-
if amd > maxs:
|
|
86
|
-
logger.warning(u"OVERSIZE %s: %s > %s pas de mise en cache possible", key, amd, maxs)
|
|
87
|
-
return None
|
|
88
|
-
else:
|
|
89
|
-
db[storage].insert_one(doc_cpm)
|
|
90
|
-
return key
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
async def get(self):
|
|
94
|
-
db, storage = self.__conn__()
|
|
95
|
-
key = self.get_key_from_request()
|
|
96
|
-
logger.debug("[RESULT_STORAGE] image not found at %s", key)
|
|
97
|
-
|
|
98
|
-
result = db[storage].find_one({'path': key }) #, 'content-type': "default"})
|
|
99
|
-
|
|
100
|
-
if not result:
|
|
101
|
-
return None
|
|
102
|
-
tosend = result['data']
|
|
103
|
-
return tosend
|
|
File without changes
|
{thumbor_libs_blackhand-0.2.0.dist-info → thumbor_libs_blackhand-0.5.0.dist-info}/top_level.txt
RENAMED
|
File without changes
|