eodag 2.12.0__py3-none-any.whl → 3.0.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.
- eodag/__init__.py +6 -8
- eodag/api/core.py +654 -538
- eodag/api/product/__init__.py +12 -2
- eodag/api/product/_assets.py +59 -16
- eodag/api/product/_product.py +100 -93
- eodag/api/product/drivers/__init__.py +7 -2
- eodag/api/product/drivers/base.py +0 -3
- eodag/api/product/metadata_mapping.py +192 -96
- eodag/api/search_result.py +69 -10
- eodag/cli.py +55 -25
- eodag/config.py +391 -116
- eodag/plugins/apis/base.py +11 -165
- eodag/plugins/apis/ecmwf.py +36 -25
- eodag/plugins/apis/usgs.py +80 -35
- eodag/plugins/authentication/aws_auth.py +13 -4
- eodag/plugins/authentication/base.py +10 -1
- eodag/plugins/authentication/generic.py +2 -2
- eodag/plugins/authentication/header.py +31 -6
- eodag/plugins/authentication/keycloak.py +17 -84
- eodag/plugins/authentication/oauth.py +3 -3
- eodag/plugins/authentication/openid_connect.py +268 -49
- eodag/plugins/authentication/qsauth.py +4 -1
- eodag/plugins/authentication/sas_auth.py +9 -2
- eodag/plugins/authentication/token.py +98 -47
- eodag/plugins/authentication/token_exchange.py +122 -0
- eodag/plugins/crunch/base.py +3 -1
- eodag/plugins/crunch/filter_date.py +3 -9
- eodag/plugins/crunch/filter_latest_intersect.py +0 -3
- eodag/plugins/crunch/filter_latest_tpl_name.py +1 -4
- eodag/plugins/crunch/filter_overlap.py +4 -8
- eodag/plugins/crunch/filter_property.py +5 -11
- eodag/plugins/download/aws.py +149 -185
- eodag/plugins/download/base.py +88 -97
- eodag/plugins/download/creodias_s3.py +1 -1
- eodag/plugins/download/http.py +638 -310
- eodag/plugins/download/s3rest.py +47 -45
- eodag/plugins/manager.py +228 -88
- eodag/plugins/search/__init__.py +36 -0
- eodag/plugins/search/base.py +239 -30
- eodag/plugins/search/build_search_result.py +382 -37
- eodag/plugins/search/cop_marine.py +441 -0
- eodag/plugins/search/creodias_s3.py +25 -20
- eodag/plugins/search/csw.py +5 -7
- eodag/plugins/search/data_request_search.py +61 -30
- eodag/plugins/search/qssearch.py +713 -255
- eodag/plugins/search/static_stac_search.py +106 -40
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/product_types.yml +1921 -34
- eodag/resources/providers.yml +4091 -3655
- eodag/resources/stac.yml +50 -216
- eodag/resources/stac_api.yml +71 -25
- eodag/resources/stac_provider.yml +5 -0
- eodag/resources/user_conf_template.yml +89 -32
- eodag/rest/__init__.py +6 -0
- eodag/rest/cache.py +70 -0
- eodag/rest/config.py +68 -0
- eodag/rest/constants.py +26 -0
- eodag/rest/core.py +735 -0
- eodag/rest/errors.py +178 -0
- eodag/rest/server.py +264 -431
- eodag/rest/stac.py +442 -836
- eodag/rest/types/collections_search.py +44 -0
- eodag/rest/types/eodag_search.py +238 -47
- eodag/rest/types/queryables.py +164 -0
- eodag/rest/types/stac_search.py +273 -0
- eodag/rest/utils/__init__.py +216 -0
- eodag/rest/utils/cql_evaluate.py +119 -0
- eodag/rest/utils/rfc3339.py +64 -0
- eodag/types/__init__.py +106 -10
- eodag/types/bbox.py +15 -14
- eodag/types/download_args.py +40 -0
- eodag/types/search_args.py +57 -7
- eodag/types/whoosh.py +79 -0
- eodag/utils/__init__.py +110 -91
- eodag/utils/constraints.py +37 -45
- eodag/utils/exceptions.py +39 -22
- eodag/utils/import_system.py +0 -4
- eodag/utils/logging.py +37 -80
- eodag/utils/notebook.py +4 -4
- eodag/utils/repr.py +113 -0
- eodag/utils/requests.py +128 -0
- eodag/utils/rest.py +100 -0
- eodag/utils/stac_reader.py +93 -21
- {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/METADATA +88 -53
- eodag-3.0.0.dist-info/RECORD +109 -0
- {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/WHEEL +1 -1
- {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/entry_points.txt +7 -5
- eodag/plugins/apis/cds.py +0 -540
- eodag/rest/types/stac_queryables.py +0 -134
- eodag/rest/utils.py +0 -1133
- eodag-2.12.0.dist-info/RECORD +0 -94
- {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/LICENSE +0 -0
- {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/top_level.txt +0 -0
eodag/plugins/download/aws.py
CHANGED
|
@@ -34,6 +34,7 @@ from typing import (
|
|
|
34
34
|
Optional,
|
|
35
35
|
Set,
|
|
36
36
|
Tuple,
|
|
37
|
+
TypedDict,
|
|
37
38
|
Union,
|
|
38
39
|
cast,
|
|
39
40
|
)
|
|
@@ -43,6 +44,7 @@ import requests
|
|
|
43
44
|
from botocore.exceptions import ClientError, ProfileNotFound
|
|
44
45
|
from botocore.handlers import disable_signing
|
|
45
46
|
from lxml import etree
|
|
47
|
+
from requests.auth import AuthBase
|
|
46
48
|
from stream_zip import ZIP_AUTO, stream_zip
|
|
47
49
|
|
|
48
50
|
from eodag.api.product.metadata_mapping import (
|
|
@@ -57,6 +59,7 @@ from eodag.utils import (
|
|
|
57
59
|
HTTP_REQ_TIMEOUT,
|
|
58
60
|
USER_AGENT,
|
|
59
61
|
ProgressCallback,
|
|
62
|
+
StreamResponse,
|
|
60
63
|
flatten_top_directories,
|
|
61
64
|
get_bucket_name_and_prefix,
|
|
62
65
|
path_to_uri,
|
|
@@ -66,6 +69,8 @@ from eodag.utils import (
|
|
|
66
69
|
from eodag.utils.exceptions import (
|
|
67
70
|
AuthenticationError,
|
|
68
71
|
DownloadError,
|
|
72
|
+
MisconfiguredError,
|
|
73
|
+
NoMatchingProductType,
|
|
69
74
|
NotAvailableError,
|
|
70
75
|
TimeOutError,
|
|
71
76
|
)
|
|
@@ -76,7 +81,8 @@ if TYPE_CHECKING:
|
|
|
76
81
|
from eodag.api.product import EOProduct
|
|
77
82
|
from eodag.api.search_result import SearchResult
|
|
78
83
|
from eodag.config import PluginConfig
|
|
79
|
-
from eodag.
|
|
84
|
+
from eodag.types.download_args import DownloadConf
|
|
85
|
+
from eodag.utils import DownloadedCallback, Unpack
|
|
80
86
|
|
|
81
87
|
|
|
82
88
|
logger = logging.getLogger("eodag.download.aws")
|
|
@@ -209,59 +215,56 @@ class AwsDownload(Download):
|
|
|
209
215
|
"""Download on AWS using S3 protocol.
|
|
210
216
|
|
|
211
217
|
:param provider: provider name
|
|
212
|
-
:type provider: str
|
|
213
218
|
:param config: Download plugin configuration:
|
|
214
219
|
|
|
215
|
-
* ``config.
|
|
220
|
+
* ``config.s3_endpoint`` (str) - s3 endpoint url
|
|
216
221
|
* ``config.requester_pays`` (bool) - (optional) whether download is done from a
|
|
217
222
|
requester-pays bucket or not
|
|
218
223
|
* ``config.flatten_top_dirs`` (bool) - (optional) flatten directory structure
|
|
219
224
|
* ``config.products`` (dict) - (optional) product_type specific configuration
|
|
220
225
|
* ``config.ignore_assets`` (bool) - (optional) ignore assets and download using downloadLink
|
|
221
|
-
|
|
222
|
-
:type config: :class:`~eodag.config.PluginConfig`
|
|
223
226
|
"""
|
|
224
227
|
|
|
225
228
|
def __init__(self, provider: str, config: PluginConfig) -> None:
|
|
226
229
|
super(AwsDownload, self).__init__(provider, config)
|
|
227
230
|
self.requester_pays = getattr(self.config, "requester_pays", False)
|
|
228
|
-
self.s3_session = None
|
|
231
|
+
self.s3_session: Optional[boto3.session.Session] = None
|
|
229
232
|
|
|
230
233
|
def download(
|
|
231
234
|
self,
|
|
232
235
|
product: EOProduct,
|
|
233
|
-
auth: Optional[
|
|
236
|
+
auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
|
|
234
237
|
progress_callback: Optional[ProgressCallback] = None,
|
|
235
238
|
wait: int = DEFAULT_DOWNLOAD_WAIT,
|
|
236
239
|
timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
237
|
-
**kwargs:
|
|
238
|
-
) -> str:
|
|
240
|
+
**kwargs: Unpack[DownloadConf],
|
|
241
|
+
) -> Optional[str]:
|
|
239
242
|
"""Download method for AWS S3 API.
|
|
240
243
|
|
|
241
244
|
The product can be downloaded as it is, or as SAFE-formatted product.
|
|
242
245
|
SAFE-build is configured for a given provider and product type.
|
|
243
246
|
If the product title is configured to be updated during download and
|
|
244
247
|
SAFE-formatted, its destination path will be:
|
|
245
|
-
`{
|
|
248
|
+
`{output_dir}/{title}`
|
|
246
249
|
|
|
247
250
|
:param product: The EO product to download
|
|
248
|
-
:
|
|
249
|
-
:param auth: (optional) The configuration of a plugin of type Authentication
|
|
250
|
-
:type auth: Union[AuthBase, Dict[str, str]]
|
|
251
|
+
:param auth: (optional) authenticated object
|
|
251
252
|
:param progress_callback: (optional) A method or a callable object
|
|
252
253
|
which takes a current size and a maximum
|
|
253
254
|
size as inputs and handle progress bar
|
|
254
255
|
creation and update to give the user a
|
|
255
256
|
feedback on the download progress
|
|
256
|
-
:
|
|
257
|
-
:param kwargs: `outputs_prefix` (str), `extract` (bool), `delete_archive` (bool)
|
|
257
|
+
:param kwargs: `output_dir` (str), `extract` (bool), `delete_archive` (bool)
|
|
258
258
|
and `dl_url_params` (dict) can be provided as additional kwargs
|
|
259
259
|
and will override any other values defined in a configuration
|
|
260
260
|
file or with environment variables.
|
|
261
|
-
:type kwargs: Union[str, bool, dict]
|
|
262
261
|
:returns: The absolute path to the downloaded product in the local filesystem
|
|
263
|
-
:rtype: str
|
|
264
262
|
"""
|
|
263
|
+
if auth is None:
|
|
264
|
+
auth = {}
|
|
265
|
+
if isinstance(auth, AuthBase):
|
|
266
|
+
raise MisconfiguredError("Please use AwsAuth plugin with AwsDownload")
|
|
267
|
+
|
|
265
268
|
if progress_callback is None:
|
|
266
269
|
logger.info(
|
|
267
270
|
"Progress bar unavailable, please call product.download() instead of plugin.download()"
|
|
@@ -272,7 +275,7 @@ class AwsDownload(Download):
|
|
|
272
275
|
product_local_path, record_filename = self._download_preparation(
|
|
273
276
|
product, progress_callback=progress_callback, **kwargs
|
|
274
277
|
)
|
|
275
|
-
if not record_filename:
|
|
278
|
+
if not record_filename or not product_local_path:
|
|
276
279
|
return product_local_path
|
|
277
280
|
|
|
278
281
|
product_conf = getattr(self.config, "products", {}).get(
|
|
@@ -290,7 +293,7 @@ class AwsDownload(Download):
|
|
|
290
293
|
|
|
291
294
|
# product conf overrides provider conf for "flatten_top_dirs"
|
|
292
295
|
flatten_top_dirs = product_conf.get(
|
|
293
|
-
"flatten_top_dirs", getattr(self.config, "flatten_top_dirs",
|
|
296
|
+
"flatten_top_dirs", getattr(self.config, "flatten_top_dirs", True)
|
|
294
297
|
)
|
|
295
298
|
|
|
296
299
|
# xtra metadata needed for SAFE product
|
|
@@ -328,7 +331,7 @@ class AwsDownload(Download):
|
|
|
328
331
|
product,
|
|
329
332
|
)
|
|
330
333
|
|
|
331
|
-
total_size = sum([p.size for p in unique_product_chunks])
|
|
334
|
+
total_size = sum([p.size for p in unique_product_chunks]) or None
|
|
332
335
|
|
|
333
336
|
# download
|
|
334
337
|
progress_callback.reset(total=total_size)
|
|
@@ -384,21 +387,20 @@ class AwsDownload(Download):
|
|
|
384
387
|
return product_local_path
|
|
385
388
|
|
|
386
389
|
def _download_preparation(
|
|
387
|
-
self,
|
|
388
|
-
|
|
390
|
+
self,
|
|
391
|
+
product: EOProduct,
|
|
392
|
+
progress_callback: ProgressCallback,
|
|
393
|
+
**kwargs: Unpack[DownloadConf],
|
|
394
|
+
) -> Tuple[Optional[str], Optional[str]]:
|
|
389
395
|
"""
|
|
390
396
|
preparation for the download:
|
|
391
397
|
- check if file was already downloaded
|
|
392
398
|
- get file path
|
|
393
399
|
- create directories
|
|
394
400
|
:param product: product to be downloaded
|
|
395
|
-
:type product: EOProduct
|
|
396
401
|
:param progress_callback: progress callback to be used
|
|
397
|
-
:type progress_callback: ProgressCallback
|
|
398
402
|
:param kwargs: additional arguments
|
|
399
|
-
:type kwargs: Any
|
|
400
403
|
:return: local path and file name
|
|
401
|
-
:rtype: Tuple[str, Optional[str]]
|
|
402
404
|
"""
|
|
403
405
|
product_local_path, record_filename = self._prepare_download(
|
|
404
406
|
product, progress_callback=progress_callback, **kwargs
|
|
@@ -420,13 +422,14 @@ class AwsDownload(Download):
|
|
|
420
422
|
"""
|
|
421
423
|
updates the product properties with fetch metadata if safe build is enabled
|
|
422
424
|
:param build_safe: if safe build is enabled
|
|
423
|
-
:type build_safe: bool
|
|
424
425
|
:param product: product to be updated
|
|
425
|
-
:type product: EOProduct
|
|
426
426
|
"""
|
|
427
427
|
product_conf = getattr(self.config, "products", {}).get(
|
|
428
428
|
product.product_type, {}
|
|
429
429
|
)
|
|
430
|
+
ssl_verify = getattr(self.config, "ssl_verify", True)
|
|
431
|
+
timeout = getattr(self.config, "timeout", HTTP_REQ_TIMEOUT)
|
|
432
|
+
|
|
430
433
|
if build_safe and "fetch_metadata" in product_conf.keys():
|
|
431
434
|
fetch_format = product_conf["fetch_metadata"]["fetch_format"]
|
|
432
435
|
update_metadata = product_conf["fetch_metadata"]["update_metadata"]
|
|
@@ -436,10 +439,13 @@ class AwsDownload(Download):
|
|
|
436
439
|
logger.info("Fetching extra metadata from %s" % fetch_url)
|
|
437
440
|
try:
|
|
438
441
|
resp = requests.get(
|
|
439
|
-
fetch_url,
|
|
442
|
+
fetch_url,
|
|
443
|
+
headers=USER_AGENT,
|
|
444
|
+
timeout=timeout,
|
|
445
|
+
verify=ssl_verify,
|
|
440
446
|
)
|
|
441
447
|
except requests.exceptions.Timeout as exc:
|
|
442
|
-
raise TimeOutError(exc, timeout=
|
|
448
|
+
raise TimeOutError(exc, timeout=timeout) from exc
|
|
443
449
|
update_metadata = mtd_cfg_as_conversion_and_querypath(update_metadata)
|
|
444
450
|
if fetch_format == "json":
|
|
445
451
|
json_resp = resp.json()
|
|
@@ -454,18 +460,17 @@ class AwsDownload(Download):
|
|
|
454
460
|
)
|
|
455
461
|
|
|
456
462
|
def _get_bucket_names_and_prefixes(
|
|
457
|
-
self,
|
|
463
|
+
self,
|
|
464
|
+
product: EOProduct,
|
|
465
|
+
asset_filter: Optional[str] = None,
|
|
466
|
+
ignore_assets: Optional[bool] = False,
|
|
458
467
|
) -> List[Tuple[str, Optional[str]]]:
|
|
459
468
|
"""
|
|
460
469
|
retrieves the bucket names and path prefixes for the assets
|
|
461
470
|
:param product: product for which the assets shall be downloaded
|
|
462
|
-
:type product: EOProduct
|
|
463
471
|
:param asset_filter: text for which the assets should be filtered
|
|
464
|
-
:type asset_filter: str
|
|
465
472
|
:param ignore_assets: if product instead of individual assets should be used
|
|
466
|
-
:type ignore_assets: bool
|
|
467
473
|
:return: tuples of bucket names and prefixes
|
|
468
|
-
:rtype: List[Tuple[str, Optional[str]]]
|
|
469
474
|
"""
|
|
470
475
|
# if assets are defined, use them instead of scanning product.location
|
|
471
476
|
if len(product.assets) > 0 and not ignore_assets:
|
|
@@ -483,7 +488,7 @@ class AwsDownload(Download):
|
|
|
483
488
|
rf"No asset key matching re.fullmatch(r'{asset_filter}') was found in {product}"
|
|
484
489
|
)
|
|
485
490
|
else:
|
|
486
|
-
assets_values =
|
|
491
|
+
assets_values = product.assets.values()
|
|
487
492
|
|
|
488
493
|
bucket_names_and_prefixes = []
|
|
489
494
|
for complementary_url in assets_values:
|
|
@@ -501,23 +506,28 @@ class AwsDownload(Download):
|
|
|
501
506
|
def _do_authentication(
|
|
502
507
|
self,
|
|
503
508
|
bucket_names_and_prefixes: List[Tuple[str, Optional[str]]],
|
|
504
|
-
auth: Dict[str, str],
|
|
505
|
-
) -> Tuple[Dict[str, Any], ResourceCollection
|
|
509
|
+
auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
|
|
510
|
+
) -> Tuple[Dict[str, Any], ResourceCollection]:
|
|
506
511
|
"""
|
|
507
512
|
authenticates with s3 and retrieves the available objects
|
|
508
513
|
raises an error when authentication is not possible
|
|
509
514
|
:param bucket_names_and_prefixes: list of bucket names and corresponding path prefixes
|
|
510
|
-
:type bucket_names_and_prefixes: List[Tuple[str, Optional[str]]]
|
|
511
515
|
:param auth: authentication information
|
|
512
|
-
:type auth: Dict[str, str]
|
|
513
516
|
:return: authenticated objects per bucket, list of available objects
|
|
514
|
-
:rtype: Tuple[Dict[str, Any], ResourceCollection[Any]]
|
|
515
517
|
"""
|
|
518
|
+
if not isinstance(auth, (dict, type(None))):
|
|
519
|
+
raise AuthenticationError(
|
|
520
|
+
f"Incompatible authentication information, expected dict or None, got {type(auth)}"
|
|
521
|
+
)
|
|
522
|
+
if auth is None:
|
|
523
|
+
auth = {}
|
|
516
524
|
authenticated_objects: Dict[str, Any] = {}
|
|
517
525
|
auth_error_messages: Set[str] = set()
|
|
518
526
|
for _, pack in enumerate(bucket_names_and_prefixes):
|
|
519
527
|
try:
|
|
520
528
|
bucket_name, prefix = pack
|
|
529
|
+
if not prefix:
|
|
530
|
+
continue
|
|
521
531
|
if bucket_name not in authenticated_objects:
|
|
522
532
|
# get Prefixes longest common base path
|
|
523
533
|
common_prefix = ""
|
|
@@ -532,7 +542,7 @@ class AwsDownload(Download):
|
|
|
532
542
|
[
|
|
533
543
|
p
|
|
534
544
|
for b, p in bucket_names_and_prefixes
|
|
535
|
-
if b == bucket_name and common_prefix in p
|
|
545
|
+
if p and b == bucket_name and common_prefix in p
|
|
536
546
|
]
|
|
537
547
|
)
|
|
538
548
|
< prefixes_in_bucket
|
|
@@ -566,24 +576,18 @@ class AwsDownload(Download):
|
|
|
566
576
|
self,
|
|
567
577
|
bucket_names_and_prefixes: List[Tuple[str, Optional[str]]],
|
|
568
578
|
authenticated_objects: Dict[str, Any],
|
|
569
|
-
asset_filter: str,
|
|
579
|
+
asset_filter: Optional[str],
|
|
570
580
|
ignore_assets: bool,
|
|
571
581
|
product: EOProduct,
|
|
572
582
|
) -> Set[Any]:
|
|
573
583
|
"""
|
|
574
584
|
retrieve unique product chunks based on authenticated objects and asset filters
|
|
575
585
|
:param bucket_names_and_prefixes: list of bucket names and corresponding path prefixes
|
|
576
|
-
:type bucket_names_and_prefixes: List[Tuple[str, Optional[str]]]
|
|
577
586
|
:param authenticated_objects: available objects per bucket
|
|
578
|
-
:type authenticated_objects: Dict[str, Any]
|
|
579
587
|
:param asset_filter: text for which assets should be filtered
|
|
580
|
-
:type asset_filter: str
|
|
581
588
|
:param ignore_assets: if product instead of individual assets should be used
|
|
582
|
-
:type ignore_assets: bool
|
|
583
589
|
:param product: product that shall be downloaded
|
|
584
|
-
:type product: EOProduct
|
|
585
590
|
:return: set of product chunks that can be downloaded
|
|
586
|
-
:rtype: Set[Any]
|
|
587
591
|
"""
|
|
588
592
|
product_chunks: List[Any] = []
|
|
589
593
|
for bucket_name, prefix in bucket_names_and_prefixes:
|
|
@@ -608,52 +612,46 @@ class AwsDownload(Download):
|
|
|
608
612
|
raise NotAvailableError(
|
|
609
613
|
rf"No file basename matching re.fullmatch(r'{asset_filter}') was found in {product.remote_location}"
|
|
610
614
|
)
|
|
615
|
+
|
|
616
|
+
if not unique_product_chunks:
|
|
617
|
+
raise NoMatchingProductType("No product found to download.")
|
|
618
|
+
|
|
611
619
|
return unique_product_chunks
|
|
612
620
|
|
|
613
621
|
def _raise_if_auth_error(self, exception: ClientError) -> None:
|
|
614
622
|
"""Raises an error if given exception is an authentication error"""
|
|
615
|
-
err = exception.response["Error"]
|
|
623
|
+
err = cast(Dict[str, str], exception.response["Error"])
|
|
616
624
|
if err["Code"] in AWS_AUTH_ERROR_MESSAGES and "key" in err["Message"].lower():
|
|
617
625
|
raise AuthenticationError(
|
|
618
|
-
"
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
err["Message"],
|
|
622
|
-
self.provider,
|
|
623
|
-
)
|
|
626
|
+
f"Please check your credentials for {self.provider}.",
|
|
627
|
+
f"HTTP Error {exception.response['ResponseMetadata']['HTTPStatusCode']} returned.",
|
|
628
|
+
err["Code"] + ": " + err["Message"],
|
|
624
629
|
)
|
|
625
630
|
|
|
626
631
|
def _stream_download_dict(
|
|
627
632
|
self,
|
|
628
633
|
product: EOProduct,
|
|
629
|
-
auth: Optional[
|
|
634
|
+
auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
|
|
630
635
|
progress_callback: Optional[ProgressCallback] = None,
|
|
631
636
|
wait: int = DEFAULT_DOWNLOAD_WAIT,
|
|
632
637
|
timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
633
|
-
**kwargs:
|
|
634
|
-
) ->
|
|
638
|
+
**kwargs: Unpack[DownloadConf],
|
|
639
|
+
) -> StreamResponse:
|
|
635
640
|
r"""
|
|
636
|
-
Returns
|
|
641
|
+
Returns dictionary of :class:`~fastapi.responses.StreamingResponse` keyword-arguments.
|
|
637
642
|
It contains a generator to streamed download chunks and the response headers.
|
|
638
643
|
|
|
639
644
|
:param product: The EO product to download
|
|
640
|
-
:type product: :class:`~eodag.api.product._product.EOProduct`
|
|
641
645
|
:param auth: (optional) The configuration of a plugin of type Authentication
|
|
642
|
-
:type auth: :class:`~eodag.config.PluginConfig`
|
|
643
646
|
:param progress_callback: (optional) A progress callback
|
|
644
|
-
:type progress_callback: :class:`~eodag.utils.ProgressCallback`
|
|
645
647
|
:param wait: (optional) If download fails, wait time in minutes between two download tries
|
|
646
|
-
:type wait: int
|
|
647
648
|
:param timeout: (optional) If download fails, maximum time in minutes before stop retrying
|
|
648
649
|
to download
|
|
649
|
-
:
|
|
650
|
-
:param kwargs: `outputs_prefix` (str), `extract` (bool), `delete_archive` (bool)
|
|
650
|
+
:param kwargs: `output_dir` (str), `extract` (bool), `delete_archive` (bool)
|
|
651
651
|
and `dl_url_params` (dict) can be provided as additional kwargs
|
|
652
652
|
and will override any other values defined in a configuration
|
|
653
653
|
file or with environment variables.
|
|
654
|
-
:
|
|
655
|
-
:returns: Dictionnary of :class:`~fastapi.responses.StreamingResponse` keyword-arguments
|
|
656
|
-
:rtype: dict
|
|
654
|
+
:returns: Dictionary of :class:`~fastapi.responses.StreamingResponse` keyword-arguments
|
|
657
655
|
"""
|
|
658
656
|
if progress_callback is None:
|
|
659
657
|
logger.info(
|
|
@@ -725,11 +723,11 @@ class AwsDownload(Download):
|
|
|
725
723
|
if assets_values[0].get("type", None):
|
|
726
724
|
headers["content-type"] = assets_values[0]["type"]
|
|
727
725
|
|
|
728
|
-
return
|
|
726
|
+
return StreamResponse(
|
|
729
727
|
content=chain(iter([first_chunks_tuple]), chunks_tuples),
|
|
730
728
|
headers=headers,
|
|
731
729
|
)
|
|
732
|
-
return
|
|
730
|
+
return StreamResponse(
|
|
733
731
|
content=stream_zip(chunks_tuples),
|
|
734
732
|
media_type="application/zip",
|
|
735
733
|
headers={
|
|
@@ -744,7 +742,7 @@ class AwsDownload(Download):
|
|
|
744
742
|
build_safe: bool,
|
|
745
743
|
progress_callback: ProgressCallback,
|
|
746
744
|
assets_values: List[Dict[str, Any]],
|
|
747
|
-
) -> Iterator[
|
|
745
|
+
) -> Iterator[Any]:
|
|
748
746
|
"""Yield product data chunks"""
|
|
749
747
|
|
|
750
748
|
chunk_size = 4096 * 1024
|
|
@@ -755,7 +753,6 @@ class AwsDownload(Download):
|
|
|
755
753
|
product_chunk: Any, progress_callback: ProgressCallback
|
|
756
754
|
) -> Any:
|
|
757
755
|
try:
|
|
758
|
-
|
|
759
756
|
chunk_start = 0
|
|
760
757
|
chunk_end = chunk_start + chunk_size - 1
|
|
761
758
|
|
|
@@ -779,7 +776,7 @@ class AwsDownload(Download):
|
|
|
779
776
|
product.product_type, {}
|
|
780
777
|
)
|
|
781
778
|
flatten_top_dirs = product_conf.get(
|
|
782
|
-
"flatten_top_dirs", getattr(self.config, "flatten_top_dirs",
|
|
779
|
+
"flatten_top_dirs", getattr(self.config, "flatten_top_dirs", True)
|
|
783
780
|
)
|
|
784
781
|
common_path = ""
|
|
785
782
|
if flatten_top_dirs:
|
|
@@ -832,13 +829,9 @@ class AwsDownload(Download):
|
|
|
832
829
|
"""Get rasterio environment variables needed for data access authentication.
|
|
833
830
|
|
|
834
831
|
:param bucket_name: Bucket containg objects
|
|
835
|
-
:type bucket_name: str
|
|
836
832
|
:param prefix: Prefix used to try auth
|
|
837
|
-
:
|
|
838
|
-
:param auth_dict: Dictionnary containing authentication keys
|
|
839
|
-
:type auth_dict: dict
|
|
833
|
+
:param auth_dict: Dictionary containing authentication keys
|
|
840
834
|
:returns: The rasterio environement variables
|
|
841
|
-
:rtype: dict
|
|
842
835
|
"""
|
|
843
836
|
if self.s3_session is not None:
|
|
844
837
|
if self.requester_pays:
|
|
@@ -863,14 +856,10 @@ class AwsDownload(Download):
|
|
|
863
856
|
Also expose ``s3_session`` as class variable if available.
|
|
864
857
|
|
|
865
858
|
:param bucket_name: Bucket containg objects
|
|
866
|
-
:type bucket_name: str
|
|
867
859
|
:param prefix: Prefix used to filter objects on auth try
|
|
868
860
|
(not used to filter returned objects)
|
|
869
|
-
:
|
|
870
|
-
:param auth_dict: Dictionnary containing authentication keys
|
|
871
|
-
:type auth_dict: dict
|
|
861
|
+
:param auth_dict: Dictionary containing authentication keys
|
|
872
862
|
:returns: The boto3 authenticated objects
|
|
873
|
-
:rtype: :class:`~boto3.resources.collection.s3.Bucket.objectsCollection`
|
|
874
863
|
"""
|
|
875
864
|
auth_methods: List[
|
|
876
865
|
Callable[[str, str, Dict[str, str]], Optional[ResourceCollection]]
|
|
@@ -912,15 +901,15 @@ class AwsDownload(Download):
|
|
|
912
901
|
) -> Optional[ResourceCollection]:
|
|
913
902
|
"""Auth strategy using no-sign-request"""
|
|
914
903
|
|
|
915
|
-
s3_resource = boto3.resource(
|
|
916
|
-
service_name="s3", endpoint_url=getattr(self.config, "
|
|
904
|
+
s3_resource = boto3.resource(
|
|
905
|
+
service_name="s3", endpoint_url=getattr(self.config, "s3_endpoint", None)
|
|
917
906
|
)
|
|
918
|
-
s3_resource.meta.client.meta.events.register(
|
|
907
|
+
s3_resource.meta.client.meta.events.register(
|
|
919
908
|
"choose-signer.s3.*", disable_signing
|
|
920
909
|
)
|
|
921
|
-
objects = s3_resource.Bucket(bucket_name).objects
|
|
922
|
-
list(objects.filter(Prefix=prefix).limit(1))
|
|
923
|
-
return objects
|
|
910
|
+
objects = s3_resource.Bucket(bucket_name).objects
|
|
911
|
+
list(objects.filter(Prefix=prefix).limit(1))
|
|
912
|
+
return objects
|
|
924
913
|
|
|
925
914
|
def _get_authenticated_objects_from_auth_profile(
|
|
926
915
|
self, bucket_name: str, prefix: str, auth_dict: Dict[str, str]
|
|
@@ -928,20 +917,20 @@ class AwsDownload(Download):
|
|
|
928
917
|
"""Auth strategy using RequestPayer=requester and ``aws_profile`` from provided credentials"""
|
|
929
918
|
|
|
930
919
|
if "profile_name" in auth_dict.keys():
|
|
931
|
-
s3_session = boto3.session.Session(profile_name=auth_dict["profile_name"])
|
|
932
|
-
s3_resource = s3_session.resource(
|
|
920
|
+
s3_session = boto3.session.Session(profile_name=auth_dict["profile_name"])
|
|
921
|
+
s3_resource = s3_session.resource(
|
|
933
922
|
service_name="s3",
|
|
934
|
-
endpoint_url=getattr(self.config, "
|
|
923
|
+
endpoint_url=getattr(self.config, "s3_endpoint", None),
|
|
935
924
|
)
|
|
936
925
|
if self.requester_pays:
|
|
937
|
-
objects = s3_resource.Bucket(bucket_name).objects.filter(
|
|
926
|
+
objects = s3_resource.Bucket(bucket_name).objects.filter(
|
|
938
927
|
RequestPayer="requester"
|
|
939
928
|
)
|
|
940
929
|
else:
|
|
941
|
-
objects = s3_resource.Bucket(bucket_name).objects
|
|
942
|
-
list(objects.filter(Prefix=prefix).limit(1))
|
|
943
|
-
self.s3_session = s3_session
|
|
944
|
-
return objects
|
|
930
|
+
objects = s3_resource.Bucket(bucket_name).objects
|
|
931
|
+
list(objects.filter(Prefix=prefix).limit(1))
|
|
932
|
+
self.s3_session = s3_session
|
|
933
|
+
return objects
|
|
945
934
|
else:
|
|
946
935
|
return None
|
|
947
936
|
|
|
@@ -952,23 +941,35 @@ class AwsDownload(Download):
|
|
|
952
941
|
from provided credentials"""
|
|
953
942
|
|
|
954
943
|
if all(k in auth_dict for k in ("aws_access_key_id", "aws_secret_access_key")):
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
944
|
+
S3SessionKwargs = TypedDict(
|
|
945
|
+
"S3SessionKwargs",
|
|
946
|
+
{
|
|
947
|
+
"aws_access_key_id": str,
|
|
948
|
+
"aws_secret_access_key": str,
|
|
949
|
+
"aws_session_token": str,
|
|
950
|
+
},
|
|
951
|
+
total=False,
|
|
958
952
|
)
|
|
959
|
-
|
|
953
|
+
s3_session_kwargs: S3SessionKwargs = {
|
|
954
|
+
"aws_access_key_id": auth_dict["aws_access_key_id"],
|
|
955
|
+
"aws_secret_access_key": auth_dict["aws_secret_access_key"],
|
|
956
|
+
}
|
|
957
|
+
if auth_dict.get("aws_session_token"):
|
|
958
|
+
s3_session_kwargs["aws_session_token"] = auth_dict["aws_session_token"]
|
|
959
|
+
s3_session = boto3.session.Session(**s3_session_kwargs)
|
|
960
|
+
s3_resource = s3_session.resource(
|
|
960
961
|
service_name="s3",
|
|
961
|
-
endpoint_url=getattr(self.config, "
|
|
962
|
+
endpoint_url=getattr(self.config, "s3_endpoint", None),
|
|
962
963
|
)
|
|
963
964
|
if self.requester_pays:
|
|
964
|
-
objects = s3_resource.Bucket(bucket_name).objects.filter(
|
|
965
|
+
objects = s3_resource.Bucket(bucket_name).objects.filter(
|
|
965
966
|
RequestPayer="requester"
|
|
966
967
|
)
|
|
967
968
|
else:
|
|
968
|
-
objects = s3_resource.Bucket(bucket_name).objects
|
|
969
|
-
list(objects.filter(Prefix=prefix).limit(1))
|
|
970
|
-
self.s3_session = s3_session
|
|
971
|
-
return objects
|
|
969
|
+
objects = s3_resource.Bucket(bucket_name).objects
|
|
970
|
+
list(objects.filter(Prefix=prefix).limit(1))
|
|
971
|
+
self.s3_session = s3_session
|
|
972
|
+
return objects
|
|
972
973
|
else:
|
|
973
974
|
return None
|
|
974
975
|
|
|
@@ -977,19 +978,19 @@ class AwsDownload(Download):
|
|
|
977
978
|
) -> Optional[ResourceCollection]:
|
|
978
979
|
"""Auth strategy using RequestPayer=requester and current environment"""
|
|
979
980
|
|
|
980
|
-
s3_session = boto3.session.Session()
|
|
981
|
-
s3_resource = s3_session.resource(
|
|
982
|
-
service_name="s3", endpoint_url=getattr(self.config, "
|
|
981
|
+
s3_session = boto3.session.Session()
|
|
982
|
+
s3_resource = s3_session.resource(
|
|
983
|
+
service_name="s3", endpoint_url=getattr(self.config, "s3_endpoint", None)
|
|
983
984
|
)
|
|
984
985
|
if self.requester_pays:
|
|
985
|
-
objects = s3_resource.Bucket(bucket_name).objects.filter(
|
|
986
|
+
objects = s3_resource.Bucket(bucket_name).objects.filter(
|
|
986
987
|
RequestPayer="requester"
|
|
987
988
|
)
|
|
988
989
|
else:
|
|
989
|
-
objects = s3_resource.Bucket(bucket_name).objects
|
|
990
|
-
list(objects.filter(Prefix=prefix).limit(1))
|
|
991
|
-
self.s3_session = s3_session
|
|
992
|
-
return objects
|
|
990
|
+
objects = s3_resource.Bucket(bucket_name).objects
|
|
991
|
+
list(objects.filter(Prefix=prefix).limit(1))
|
|
992
|
+
self.s3_session = s3_session
|
|
993
|
+
return objects
|
|
993
994
|
|
|
994
995
|
def get_product_bucket_name_and_prefix(
|
|
995
996
|
self, product: EOProduct, url: Optional[str] = None
|
|
@@ -997,11 +998,8 @@ class AwsDownload(Download):
|
|
|
997
998
|
"""Extract bucket name and prefix from product URL
|
|
998
999
|
|
|
999
1000
|
:param product: The EO product to download
|
|
1000
|
-
:type product: :class:`~eodag.api.product._product.EOProduct`
|
|
1001
1001
|
:param url: (optional) URL to use as product.location
|
|
1002
|
-
:type url: str
|
|
1003
1002
|
:returns: bucket_name and prefix as str
|
|
1004
|
-
:rtype: tuple
|
|
1005
1003
|
"""
|
|
1006
1004
|
if url is None:
|
|
1007
1005
|
url = product.location
|
|
@@ -1154,8 +1152,7 @@ class AwsDownload(Download):
|
|
|
1154
1152
|
# S2 L2A Tile files -----------------------------------------------
|
|
1155
1153
|
if matched := S2L2A_TILE_IMG_REGEX.match(chunk.key):
|
|
1156
1154
|
found_dict = matched.groupdict()
|
|
1157
|
-
product_path = "
|
|
1158
|
-
product.properties["title"],
|
|
1155
|
+
product_path = "GRANULE/%s/IMG_DATA/R%s/T%s%s%s_%s_%s_%s.jp2" % (
|
|
1159
1156
|
found_dict["num"],
|
|
1160
1157
|
found_dict["res"],
|
|
1161
1158
|
found_dict["tile1"],
|
|
@@ -1167,16 +1164,14 @@ class AwsDownload(Download):
|
|
|
1167
1164
|
)
|
|
1168
1165
|
elif matched := S2L2A_TILE_AUX_DIR_REGEX.match(chunk.key):
|
|
1169
1166
|
found_dict = matched.groupdict()
|
|
1170
|
-
product_path = "
|
|
1171
|
-
product.properties["title"],
|
|
1167
|
+
product_path = "GRANULE/%s/AUX_DATA/%s" % (
|
|
1172
1168
|
found_dict["num"],
|
|
1173
1169
|
found_dict["file"],
|
|
1174
1170
|
)
|
|
1175
1171
|
# S2 L2A QI Masks
|
|
1176
1172
|
elif matched := S2_TILE_QI_MSK_REGEX.match(chunk.key):
|
|
1177
1173
|
found_dict = matched.groupdict()
|
|
1178
|
-
product_path = "
|
|
1179
|
-
product.properties["title"],
|
|
1174
|
+
product_path = "GRANULE/%s/QI_DATA/MSK_%sPRB_%s" % (
|
|
1180
1175
|
found_dict["num"],
|
|
1181
1176
|
found_dict["file_base"],
|
|
1182
1177
|
found_dict["file_suffix"],
|
|
@@ -1184,8 +1179,7 @@ class AwsDownload(Download):
|
|
|
1184
1179
|
# S2 L2A QI PVI
|
|
1185
1180
|
elif matched := S2_TILE_QI_PVI_REGEX.match(chunk.key):
|
|
1186
1181
|
found_dict = matched.groupdict()
|
|
1187
|
-
product_path = "
|
|
1188
|
-
product.properties["title"],
|
|
1182
|
+
product_path = "GRANULE/%s/QI_DATA/%s_%s_PVI.jp2" % (
|
|
1189
1183
|
found_dict["num"],
|
|
1190
1184
|
title_part3,
|
|
1191
1185
|
title_date1,
|
|
@@ -1193,15 +1187,13 @@ class AwsDownload(Download):
|
|
|
1193
1187
|
# S2 Tile files ---------------------------------------------------
|
|
1194
1188
|
elif matched := S2_TILE_PREVIEW_DIR_REGEX.match(chunk.key):
|
|
1195
1189
|
found_dict = matched.groupdict()
|
|
1196
|
-
product_path = "
|
|
1197
|
-
product.properties["title"],
|
|
1190
|
+
product_path = "GRANULE/%s/preview/%s" % (
|
|
1198
1191
|
found_dict["num"],
|
|
1199
1192
|
found_dict["file"],
|
|
1200
1193
|
)
|
|
1201
1194
|
elif matched := S2_TILE_IMG_REGEX.match(chunk.key):
|
|
1202
1195
|
found_dict = matched.groupdict()
|
|
1203
|
-
product_path = "
|
|
1204
|
-
product.properties["title"],
|
|
1196
|
+
product_path = "GRANULE/%s/IMG_DATA/T%s%s%s_%s_%s" % (
|
|
1205
1197
|
found_dict["num"],
|
|
1206
1198
|
found_dict["tile1"],
|
|
1207
1199
|
found_dict["tile2"],
|
|
@@ -1211,97 +1203,74 @@ class AwsDownload(Download):
|
|
|
1211
1203
|
)
|
|
1212
1204
|
elif matched := S2_TILE_THUMBNAIL_REGEX.match(chunk.key):
|
|
1213
1205
|
found_dict = matched.groupdict()
|
|
1214
|
-
product_path = "
|
|
1215
|
-
product.properties["title"],
|
|
1206
|
+
product_path = "GRANULE/%s/%s" % (
|
|
1216
1207
|
found_dict["num"],
|
|
1217
1208
|
found_dict["file"],
|
|
1218
1209
|
)
|
|
1219
1210
|
elif matched := S2_TILE_MTD_REGEX.match(chunk.key):
|
|
1220
1211
|
found_dict = matched.groupdict()
|
|
1221
|
-
product_path = "
|
|
1222
|
-
product.properties["title"],
|
|
1223
|
-
found_dict["num"],
|
|
1224
|
-
)
|
|
1212
|
+
product_path = "GRANULE/%s/MTD_TL.xml" % found_dict["num"]
|
|
1225
1213
|
elif matched := S2_TILE_AUX_DIR_REGEX.match(chunk.key):
|
|
1226
1214
|
found_dict = matched.groupdict()
|
|
1227
|
-
product_path = "
|
|
1228
|
-
product.properties["title"],
|
|
1215
|
+
product_path = "GRANULE/%s/AUX_DATA/AUX_%s" % (
|
|
1229
1216
|
found_dict["num"],
|
|
1230
1217
|
found_dict["file"],
|
|
1231
1218
|
)
|
|
1232
1219
|
elif matched := S2_TILE_QI_DIR_REGEX.match(chunk.key):
|
|
1233
1220
|
found_dict = matched.groupdict()
|
|
1234
|
-
product_path = "
|
|
1235
|
-
product.properties["title"],
|
|
1221
|
+
product_path = "GRANULE/%s/QI_DATA/%s" % (
|
|
1236
1222
|
found_dict["num"],
|
|
1237
1223
|
found_dict["file"],
|
|
1238
1224
|
)
|
|
1239
1225
|
# S2 Tiles generic
|
|
1240
1226
|
elif matched := S2_TILE_REGEX.match(chunk.key):
|
|
1241
1227
|
found_dict = matched.groupdict()
|
|
1242
|
-
product_path = "
|
|
1243
|
-
product.properties["title"],
|
|
1228
|
+
product_path = "GRANULE/%s/%s" % (
|
|
1244
1229
|
found_dict["num"],
|
|
1245
1230
|
found_dict["file"],
|
|
1246
1231
|
)
|
|
1247
1232
|
# S2 Product files
|
|
1248
1233
|
elif matched := S2_PROD_DS_MTD_REGEX.match(chunk.key):
|
|
1249
1234
|
found_dict = matched.groupdict()
|
|
1250
|
-
product_path = "
|
|
1251
|
-
product.properties["title"],
|
|
1252
|
-
ds_dir,
|
|
1253
|
-
)
|
|
1235
|
+
product_path = "DATASTRIP/%s/MTD_DS.xml" % ds_dir
|
|
1254
1236
|
elif matched := S2_PROD_DS_QI_REPORT_REGEX.match(chunk.key):
|
|
1255
1237
|
found_dict = matched.groupdict()
|
|
1256
|
-
product_path = "
|
|
1257
|
-
product.properties["title"],
|
|
1238
|
+
product_path = "DATASTRIP/%s/QI_DATA/%s.xml" % (
|
|
1258
1239
|
ds_dir,
|
|
1259
1240
|
found_dict["filename"],
|
|
1260
1241
|
)
|
|
1261
1242
|
elif matched := S2_PROD_DS_QI_REGEX.match(chunk.key):
|
|
1262
1243
|
found_dict = matched.groupdict()
|
|
1263
|
-
product_path = "
|
|
1264
|
-
product.properties["title"],
|
|
1244
|
+
product_path = "DATASTRIP/%s/QI_DATA/%s" % (
|
|
1265
1245
|
ds_dir,
|
|
1266
1246
|
found_dict["file"],
|
|
1267
1247
|
)
|
|
1268
1248
|
elif matched := S2_PROD_INSPIRE_REGEX.match(chunk.key):
|
|
1269
1249
|
found_dict = matched.groupdict()
|
|
1270
|
-
product_path = "
|
|
1250
|
+
product_path = "INSPIRE.xml"
|
|
1271
1251
|
elif matched := S2_PROD_MTD_REGEX.match(chunk.key):
|
|
1272
1252
|
found_dict = matched.groupdict()
|
|
1273
|
-
product_path = "
|
|
1274
|
-
product.properties["title"],
|
|
1275
|
-
s2_processing_level,
|
|
1276
|
-
)
|
|
1253
|
+
product_path = "MTD_MSI%s.xml" % s2_processing_level
|
|
1277
1254
|
# S2 Product generic
|
|
1278
1255
|
elif matched := S2_PROD_REGEX.match(chunk.key):
|
|
1279
1256
|
found_dict = matched.groupdict()
|
|
1280
|
-
product_path = "%s
|
|
1281
|
-
product.properties["title"],
|
|
1282
|
-
found_dict["file"],
|
|
1283
|
-
)
|
|
1257
|
+
product_path = "%s" % found_dict["file"]
|
|
1284
1258
|
# S1 --------------------------------------------------------------
|
|
1285
1259
|
elif matched := S1_CALIB_REGEX.match(chunk.key):
|
|
1286
1260
|
found_dict = matched.groupdict()
|
|
1287
|
-
product_path = (
|
|
1288
|
-
"
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
found_dict["file_pol"],
|
|
1295
|
-
|
|
1296
|
-
S1_IMG_NB_PER_POLAR.get(
|
|
1297
|
-
product.properties["polarizationMode"], {}
|
|
1298
|
-
).get(found_dict["file_pol"].upper(), 1),
|
|
1299
|
-
)
|
|
1261
|
+
product_path = "annotation/calibration/%s-%s-%s-grd-%s-%s-%03d.xml" % (
|
|
1262
|
+
found_dict["file_prefix"],
|
|
1263
|
+
product.properties["platformSerialIdentifier"].lower(),
|
|
1264
|
+
found_dict["file_beam"],
|
|
1265
|
+
found_dict["file_pol"],
|
|
1266
|
+
s1_title_suffix,
|
|
1267
|
+
S1_IMG_NB_PER_POLAR.get(product.properties["polarizationMode"], {}).get(
|
|
1268
|
+
found_dict["file_pol"].upper(), 1
|
|
1269
|
+
),
|
|
1300
1270
|
)
|
|
1301
1271
|
elif matched := S1_ANNOT_REGEX.match(chunk.key):
|
|
1302
1272
|
found_dict = matched.groupdict()
|
|
1303
|
-
product_path = "
|
|
1304
|
-
product.properties["title"],
|
|
1273
|
+
product_path = "annotation/%s-%s-grd-%s-%s-%03d.xml" % (
|
|
1305
1274
|
product.properties["platformSerialIdentifier"].lower(),
|
|
1306
1275
|
found_dict["file_beam"],
|
|
1307
1276
|
found_dict["file_pol"],
|
|
@@ -1312,8 +1281,7 @@ class AwsDownload(Download):
|
|
|
1312
1281
|
)
|
|
1313
1282
|
elif matched := S1_MEAS_REGEX.match(chunk.key):
|
|
1314
1283
|
found_dict = matched.groupdict()
|
|
1315
|
-
product_path = "
|
|
1316
|
-
product.properties["title"],
|
|
1284
|
+
product_path = "measurement/%s-%s-grd-%s-%s-%03d.%s" % (
|
|
1317
1285
|
product.properties["platformSerialIdentifier"].lower(),
|
|
1318
1286
|
found_dict["file_beam"],
|
|
1319
1287
|
found_dict["file_pol"],
|
|
@@ -1325,18 +1293,14 @@ class AwsDownload(Download):
|
|
|
1325
1293
|
)
|
|
1326
1294
|
elif matched := S1_REPORT_REGEX.match(chunk.key):
|
|
1327
1295
|
found_dict = matched.groupdict()
|
|
1328
|
-
product_path = "%s.SAFE
|
|
1329
|
-
product.properties["title"],
|
|
1296
|
+
product_path = "%s.SAFE-%s" % (
|
|
1330
1297
|
product.properties["title"],
|
|
1331
1298
|
found_dict["file"],
|
|
1332
1299
|
)
|
|
1333
1300
|
# S1 generic
|
|
1334
1301
|
elif matched := S1_REGEX.match(chunk.key):
|
|
1335
1302
|
found_dict = matched.groupdict()
|
|
1336
|
-
product_path = "%s
|
|
1337
|
-
product.properties["title"],
|
|
1338
|
-
found_dict["file"],
|
|
1339
|
-
)
|
|
1303
|
+
product_path = "%s" % found_dict["file"]
|
|
1340
1304
|
# out of SAFE format
|
|
1341
1305
|
else:
|
|
1342
1306
|
raise NotAvailableError(f"Ignored {chunk.key} out of SAFE matching pattern")
|
|
@@ -1347,12 +1311,12 @@ class AwsDownload(Download):
|
|
|
1347
1311
|
def download_all(
|
|
1348
1312
|
self,
|
|
1349
1313
|
products: SearchResult,
|
|
1350
|
-
auth: Optional[
|
|
1314
|
+
auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
|
|
1351
1315
|
downloaded_callback: Optional[DownloadedCallback] = None,
|
|
1352
1316
|
progress_callback: Optional[ProgressCallback] = None,
|
|
1353
1317
|
wait: int = DEFAULT_DOWNLOAD_WAIT,
|
|
1354
1318
|
timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
1355
|
-
**kwargs:
|
|
1319
|
+
**kwargs: Unpack[DownloadConf],
|
|
1356
1320
|
) -> List[str]:
|
|
1357
1321
|
"""
|
|
1358
1322
|
download_all using parent (base plugin) method
|