mapillary-tools 0.11.0b4__tar.gz → 0.11.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/PKG-INFO +3 -3
  2. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/README.md +1 -1
  3. mapillary_tools-0.11.2/mapillary_tools/__init__.py +1 -0
  4. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/api_v4.py +7 -0
  5. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/authenticate.py +5 -1
  6. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/exif_read.py +3 -3
  7. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/exiftool_read.py +2 -2
  8. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/ffmpeg.py +8 -2
  9. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/process_geotag_properties.py +1 -0
  10. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/process_sequence_properties.py +5 -3
  11. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/sample_video.py +17 -15
  12. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/types.py +5 -10
  13. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/upload.py +13 -6
  14. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/upload_api_v4.py +7 -0
  15. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/uploader.py +8 -2
  16. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/video_data_extraction/cli_options.py +1 -0
  17. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/video_data_extraction/extract_video_data.py +9 -5
  18. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/video_data_extraction/extractors/exiftool_runtime_parser.py +13 -4
  19. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools.egg-info/PKG-INFO +4 -4
  20. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools.egg-info/requires.txt +1 -1
  21. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/requirements.txt +1 -1
  22. mapillary_tools-0.11.0b4/mapillary_tools/__init__.py +0 -1
  23. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/LICENSE +0 -0
  24. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/MANIFEST.in +0 -0
  25. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/commands/__init__.py +0 -0
  26. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/commands/__main__.py +0 -0
  27. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/commands/authenticate.py +0 -0
  28. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/commands/process.py +0 -0
  29. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/commands/process_and_upload.py +0 -0
  30. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/commands/sample_video.py +0 -0
  31. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/commands/upload.py +0 -0
  32. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/commands/upload_blackvue.py +0 -0
  33. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/commands/upload_camm.py +0 -0
  34. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/commands/upload_zip.py +0 -0
  35. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/commands/video_process.py +0 -0
  36. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/commands/video_process_and_upload.py +0 -0
  37. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/commands/zip.py +0 -0
  38. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/config.py +0 -0
  39. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/constants.py +0 -0
  40. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/exceptions.py +0 -0
  41. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/exif_write.py +0 -0
  42. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/exiftool_read_video.py +0 -0
  43. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geo.py +0 -0
  44. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/__init__.py +0 -0
  45. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/blackvue_parser.py +0 -0
  46. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/camm_builder.py +0 -0
  47. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/camm_parser.py +0 -0
  48. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/construct_mp4_parser.py +0 -0
  49. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/geotag_from_generic.py +0 -0
  50. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/geotag_images_from_exif.py +0 -0
  51. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/geotag_images_from_exiftool.py +0 -0
  52. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/geotag_images_from_exiftool_both_image_and_video.py +0 -0
  53. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/geotag_images_from_gpx.py +0 -0
  54. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/geotag_images_from_gpx_file.py +0 -0
  55. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/geotag_images_from_nmea_file.py +0 -0
  56. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/geotag_images_from_video.py +0 -0
  57. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/geotag_videos_from_exiftool_video.py +0 -0
  58. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/geotag_videos_from_video.py +0 -0
  59. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/gpmf_gps_filter.py +0 -0
  60. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/gpmf_parser.py +0 -0
  61. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/gps_filter.py +0 -0
  62. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/io_utils.py +0 -0
  63. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/mp4_sample_parser.py +0 -0
  64. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/simple_mp4_builder.py +0 -0
  65. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/simple_mp4_parser.py +0 -0
  66. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/geotag/utils.py +0 -0
  67. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/history.py +0 -0
  68. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/ipc.py +0 -0
  69. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/process_import_meta_properties.py +0 -0
  70. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/utils.py +0 -0
  71. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/video_data_extraction/extractors/base_parser.py +0 -0
  72. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/video_data_extraction/extractors/blackvue_parser.py +0 -0
  73. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/video_data_extraction/extractors/camm_parser.py +0 -0
  74. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/video_data_extraction/extractors/exiftool_xml_parser.py +0 -0
  75. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/video_data_extraction/extractors/generic_video_parser.py +0 -0
  76. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/video_data_extraction/extractors/gopro_parser.py +0 -0
  77. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/video_data_extraction/extractors/gpx_parser.py +0 -0
  78. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/video_data_extraction/extractors/nmea_parser.py +0 -0
  79. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools/video_data_extraction/video_data_parser_factory.py +0 -0
  80. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools.egg-info/SOURCES.txt +0 -0
  81. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools.egg-info/dependency_links.txt +0 -0
  82. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools.egg-info/entry_points.txt +0 -0
  83. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/mapillary_tools.egg-info/top_level.txt +0 -0
  84. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/schema/image_description_schema.json +0 -0
  85. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/setup.cfg +0 -0
  86. {mapillary_tools-0.11.0b4 → mapillary_tools-0.11.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mapillary_tools
3
- Version: 0.11.0b4
3
+ Version: 0.11.2
4
4
  Summary: Mapillary Image/Video Import Pipeline
5
5
  Home-page: https://github.com/mapillary/mapillary_tools
6
6
  Author: Mapillary
@@ -13,7 +13,7 @@ Requires-Dist: construct<3.0.0,>=2.10.0
13
13
  Requires-Dist: exifread==2.3.2
14
14
  Requires-Dist: piexif==1.1.3
15
15
  Requires-Dist: gpxpy<1.6.0,>=1.5.0
16
- Requires-Dist: pynmea2==1.12.0
16
+ Requires-Dist: pynmea2<2.0.0,>=1.12.0
17
17
  Requires-Dist: requests<3.0.0,>=2.20.0
18
18
  Requires-Dist: tqdm<5.0,>=4.0
19
19
  Requires-Dist: typing_extensions
@@ -130,7 +130,7 @@ A command line program such as Termux is required. Installation can be done with
130
130
  commands will install Python 3, pip3, git, and all required libraries for mapillary_tools on Termux:
131
131
 
132
132
  ```sh
133
- pkg install python git build-essential libgeos openssl libjpeg-turbo
133
+ pkg install python git build-essential libgeos openssl libjpeg-turbo libexpat libexpat-static
134
134
  pip install --upgrade pip wheel
135
135
  pip install --upgrade mapillary_tools
136
136
  ```
@@ -108,7 +108,7 @@ A command line program such as Termux is required. Installation can be done with
108
108
  commands will install Python 3, pip3, git, and all required libraries for mapillary_tools on Termux:
109
109
 
110
110
  ```sh
111
- pkg install python git build-essential libgeos openssl libjpeg-turbo
111
+ pkg install python git build-essential libgeos openssl libjpeg-turbo libexpat libexpat-static
112
112
  pip install --upgrade pip wheel
113
113
  pip install --upgrade mapillary_tools
114
114
  ```
@@ -0,0 +1 @@
1
+ VERSION = "0.11.2"
@@ -9,6 +9,10 @@ MAPILLARY_CLIENT_TOKEN = os.getenv(
9
9
  MAPILLARY_GRAPH_API_ENDPOINT = os.getenv(
10
10
  "MAPILLARY_GRAPH_API_ENDPOINT", "https://graph.mapillary.com"
11
11
  )
12
+ # https://requests.readthedocs.io/en/latest/user/advanced/#ssl-cert-verification
13
+ MAPILLARY__DISABLE_VERIFYING_SSL = (
14
+ os.getenv("MAPILLARY__DISABLE_VERIFYING_SSL") == "TRUE"
15
+ )
12
16
  REQUESTS_TIMEOUT = 60 # 1 minutes
13
17
 
14
18
 
@@ -18,6 +22,7 @@ def get_upload_token(email: str, password: str) -> requests.Response:
18
22
  params={"access_token": MAPILLARY_CLIENT_TOKEN},
19
23
  json={"email": email, "password": password, "locale": "en_US"},
20
24
  timeout=REQUESTS_TIMEOUT,
25
+ verify=not MAPILLARY__DISABLE_VERIFYING_SSL,
21
26
  )
22
27
  resp.raise_for_status()
23
28
  return resp
@@ -35,6 +40,7 @@ def fetch_organization(
35
40
  "Authorization": f"OAuth {user_access_token}",
36
41
  },
37
42
  timeout=REQUESTS_TIMEOUT,
43
+ verify=not MAPILLARY__DISABLE_VERIFYING_SSL,
38
44
  )
39
45
  resp.raise_for_status()
40
46
  return resp
@@ -56,6 +62,7 @@ def logging(action_type: ActionType, properties: T.Dict) -> requests.Response:
56
62
  "Authorization": f"OAuth {MAPILLARY_CLIENT_TOKEN}",
57
63
  },
58
64
  timeout=REQUESTS_TIMEOUT,
65
+ verify=not MAPILLARY__DISABLE_VERIFYING_SSL,
59
66
  )
60
67
  resp.raise_for_status()
61
68
  return resp
@@ -51,7 +51,11 @@ def prompt_user_for_user_items(user_name: str) -> types.UserItem:
51
51
  try:
52
52
  resp = api_v4.get_upload_token(user_email, user_password)
53
53
  except requests.HTTPError as ex:
54
- if 400 <= ex.response.status_code < 500:
54
+ if (
55
+ isinstance(ex, requests.HTTPError)
56
+ and isinstance(ex.response, requests.Response)
57
+ and 400 <= ex.response.status_code < 500
58
+ ):
55
59
  r = ex.response.json()
56
60
  subcode = r.get("error", {}).get("error_subcode")
57
61
  if subcode in [1348028, 1348092, 3404005, 1348131]:
@@ -399,7 +399,7 @@ class ExifReadFromXMP(ExifReadABC):
399
399
 
400
400
  def extract_capture_time(self) -> T.Optional[datetime.datetime]:
401
401
  dt = self.extract_gps_datetime()
402
- if dt is not None:
402
+ if dt is not None and dt.date() != datetime.date(1970, 1, 1):
403
403
  return dt
404
404
 
405
405
  dt = self.extract_exif_datetime()
@@ -554,7 +554,7 @@ class ExifReadFromEXIF(ExifReadABC):
554
554
  return None
555
555
 
556
556
  dt = strptime_alternative_formats(gpsdate, ["%Y:%m:%d", "%Y-%m-%d"])
557
- if dt is None:
557
+ if dt is None or dt == datetime.date(1970, 1, 1):
558
558
  return None
559
559
 
560
560
  gpstimestamp = self.tags.get("GPS GPSTimeStamp")
@@ -632,7 +632,7 @@ class ExifReadFromEXIF(ExifReadABC):
632
632
  gps_dt = self.extract_gps_datetime()
633
633
  except (ValueError, TypeError, ZeroDivisionError):
634
634
  gps_dt = None
635
- if gps_dt is not None:
635
+ if gps_dt is not None and gps_dt.date() != datetime.date(1970, 1, 1):
636
636
  return gps_dt
637
637
 
638
638
  dt = self.extract_exif_datetime()
@@ -266,14 +266,14 @@ class ExifToolRead(exif_read.ExifReadABC):
266
266
  dt = self.extract_gps_datetime()
267
267
  except (ValueError, TypeError, ZeroDivisionError):
268
268
  dt = None
269
- if dt is not None:
269
+ if dt is not None and dt.date() != datetime.date(1970, 1, 1):
270
270
  return dt
271
271
 
272
272
  try:
273
273
  dt = self.extract_gps_datetime_from_xmp()
274
274
  except (ValueError, TypeError, ZeroDivisionError):
275
275
  dt = None
276
- if dt is not None:
276
+ if dt is not None and dt.date() != datetime.date(1970, 1, 1):
277
277
  return dt
278
278
 
279
279
  dt = self.extract_exif_datetime()
@@ -252,12 +252,18 @@ class FFMPEG:
252
252
  *[
253
253
  *["-filter_script:v", select_file.name],
254
254
  # Each frame is passed with its timestamp from the demuxer to the muxer
255
- # vsync is deprecated but -fps_mode is not avaliable on some versions ;(
256
255
  *["-vsync", "0"],
256
+ # vsync is deprecated by fps_mode,
257
+ # but fps_mode is not avaliable on some older versions ;(
257
258
  # *[f"-fps_mode:{stream_specifier}", "passthrough"],
258
259
  # Set the number of video frames to output
259
260
  *[f"-frames:{stream_specifier}", str(len(frame_indices))],
260
- *["-frame_pts", "1"],
261
+ # Disabled because it doesn't always name the sample images as expected
262
+ # For example "select(n\,1)" we expected the first sample to be IMG_001.JPG
263
+ # but it could be IMG_005.JPG
264
+ # https://www.ffmpeg.org/ffmpeg-formats.html#Options-21
265
+ # If set to 1, expand the filename with pts from pkt->pts. Default value is 0.
266
+ # *["-frame_pts", "1"],
261
267
  ],
262
268
  # video quality level (or the alias -q:v)
263
269
  *[f"-qscale:{stream_specifier}", "2"],
@@ -289,6 +289,7 @@ def _process_videos_beta(vars_args: T.Dict):
289
289
  "num_processes": vars_args["num_processes"],
290
290
  "device_make": vars_args["device_make"],
291
291
  "device_model": vars_args["device_model"],
292
+ "check_file_suffix": len(vars_args["filetypes"]) > 1,
292
293
  }
293
294
  extractor = VideoDataExtractor(options)
294
295
  return extractor.process()
@@ -195,9 +195,11 @@ def _interpolate_subsecs_for_sorting(sequence: PointSequence) -> None:
195
195
 
196
196
  t = sequence[gidx].time
197
197
  nt = min(
198
- sequence[gidx + len(group)].time
199
- if gidx + len(group) < len(sequence)
200
- else math.floor(t + 1.0),
198
+ (
199
+ sequence[gidx + len(group)].time
200
+ if gidx + len(group) < len(sequence)
201
+ else math.floor(t + 1.0)
202
+ ),
201
203
  math.floor(t + 1.0),
202
204
  )
203
205
  assert t <= nt, f"expect sorted but got {t} > {nt}"
@@ -320,35 +320,37 @@ def _sample_single_video_by_distance(
320
320
  sample_points_by_frame_idx = _sample_video_stream_by_distance(
321
321
  video_metadata.points, video_track_parser, sample_distance
322
322
  )
323
+ sorted_sample_indices = sorted(sample_points_by_frame_idx.keys())
323
324
 
324
325
  with wip_dir_context(wip_sample_dir(sample_dir), sample_dir) as wip_dir:
325
326
  ffmpeg.extract_specified_frames(
326
327
  video_path,
327
328
  wip_dir,
328
- frame_indices=set(sample_points_by_frame_idx.keys()),
329
+ frame_indices=set(sorted_sample_indices),
329
330
  stream_idx=video_stream_idx,
330
331
  )
331
332
 
332
333
  frame_samples = ffmpeglib.sort_selected_samples(
333
334
  wip_dir, video_path, [video_stream_idx]
334
335
  )
335
- # extract_specified_frames() produces 0-based frame indices
336
- for frame_idx_0based, sample_paths in frame_samples:
337
- assert len(sample_paths) == 1
338
- if sample_paths[0] is None:
339
- continue
340
-
341
- sample_point = sample_points_by_frame_idx.get(frame_idx_0based)
342
- if sample_point is None:
343
- # this should not happen
344
- LOG.warning(
345
- "The specified frame index %d was not extracted from stream index %d",
346
- frame_idx_0based,
347
- video_stream_idx,
336
+ if len(frame_samples) != len(sorted_sample_indices):
337
+ raise exceptions.MapillaryVideoError(
338
+ f"Expect {len(sorted_sample_indices)} samples but extracted {len(frame_samples)} samples"
339
+ )
340
+ for idx, (frame_idx_1based, sample_paths) in enumerate(frame_samples):
341
+ assert (
342
+ len(sample_paths) == 1
343
+ ), "Expect 1 sample path at {frame_idx_1based} but got {sample_paths}"
344
+ if idx + 1 != frame_idx_1based:
345
+ raise exceptions.MapillaryVideoError(
346
+ f"Expect {sample_paths[0]} to be {idx + 1}th sample but got {frame_idx_1based}"
348
347
  )
348
+
349
+ for (_, sample_paths), sample_idx in zip(frame_samples, sorted_sample_indices):
350
+ if sample_paths[0] is None:
349
351
  continue
350
352
 
351
- video_sample, interp = sample_point
353
+ video_sample, interp = sample_points_by_frame_idx[sample_idx]
352
354
  assert (
353
355
  interp.time == video_sample.composition_time_offset
354
356
  ), f"interpolated time {interp.time} should match the video sample time {video_sample.composition_time_offset}"
@@ -456,18 +456,15 @@ def map_capture_time_to_datetime(time: str) -> datetime.datetime:
456
456
 
457
457
 
458
458
  @T.overload
459
- def as_desc(metadata: ImageMetadata) -> ImageDescription:
460
- ...
459
+ def as_desc(metadata: ImageMetadata) -> ImageDescription: ...
461
460
 
462
461
 
463
462
  @T.overload
464
- def as_desc(metadata: ErrorMetadata) -> ImageDescriptionError:
465
- ...
463
+ def as_desc(metadata: ErrorMetadata) -> ImageDescriptionError: ...
466
464
 
467
465
 
468
466
  @T.overload
469
- def as_desc(metadata: VideoMetadata) -> VideoDescription:
470
- ...
467
+ def as_desc(metadata: VideoMetadata) -> VideoDescription: ...
471
468
 
472
469
 
473
470
  def as_desc(metadata):
@@ -524,13 +521,11 @@ def _as_image_desc(metadata: ImageMetadata) -> ImageDescription:
524
521
 
525
522
 
526
523
  @T.overload
527
- def from_desc(metadata: ImageDescription) -> ImageMetadata:
528
- ...
524
+ def from_desc(metadata: ImageDescription) -> ImageMetadata: ...
529
525
 
530
526
 
531
527
  @T.overload
532
- def from_desc(metadata: VideoDescription) -> VideoMetadata:
533
- ...
528
+ def from_desc(metadata: VideoDescription) -> VideoMetadata: ...
534
529
 
535
530
 
536
531
  def from_desc(desc):
@@ -62,12 +62,17 @@ class DirectUploadFileType(enum.Enum):
62
62
 
63
63
 
64
64
  def wrap_http_exception(ex: requests.HTTPError):
65
+ req = ex.request
65
66
  resp = ex.response
66
- lines = [
67
- f"{ex.request.method} {resp.url}",
68
- f"> HTTP Status: {ex.response.status_code}",
69
- str(resp.content),
70
- ]
67
+ if isinstance(resp, requests.Response) and isinstance(req, requests.Request):
68
+ lines = [
69
+ f"{req.method} {resp.url}",
70
+ f"> HTTP Status: {resp.status_code}",
71
+ str(resp.content),
72
+ ]
73
+ else:
74
+ lines = []
75
+
71
76
  return UploadHTTPError("\n".join(lines))
72
77
 
73
78
 
@@ -713,7 +718,9 @@ def upload(
713
718
  if isinstance(inner_ex, requests.Timeout):
714
719
  raise exceptions.MapillaryUploadTimeoutError(str(inner_ex)) from inner_ex
715
720
 
716
- if isinstance(inner_ex, requests.HTTPError):
721
+ if isinstance(inner_ex, requests.HTTPError) and isinstance(
722
+ inner_ex.response, requests.Response
723
+ ):
717
724
  if inner_ex.response.status_code in [400, 401]:
718
725
  try:
719
726
  error_body = inner_ex.response.json()
@@ -15,6 +15,10 @@ MAPILLARY_UPLOAD_ENDPOINT = os.getenv(
15
15
  MAPILLARY_GRAPH_API_ENDPOINT = os.getenv(
16
16
  "MAPILLARY_GRAPH_API_ENDPOINT", "https://graph.mapillary.com"
17
17
  )
18
+ # https://requests.readthedocs.io/en/latest/user/advanced/#ssl-cert-verification
19
+ MAPILLARY__DISABLE_VERIFYING_SSL = (
20
+ os.getenv("MAPILLARY__DISABLE_VERIFYING_SSL") == "TRUE"
21
+ )
18
22
  DEFAULT_CHUNK_SIZE = 1024 * 1024 * 16 # 16MB
19
23
  # According to the docs, UPLOAD_REQUESTS_TIMEOUT can be a tuple of
20
24
  # (connection_timeout, read_timeout): https://requests.readthedocs.io/en/latest/user/advanced/#timeouts
@@ -97,6 +101,7 @@ class UploadService:
97
101
  url,
98
102
  headers=headers,
99
103
  timeout=REQUESTS_TIMEOUT,
104
+ verify=not MAPILLARY__DISABLE_VERIFYING_SSL,
100
105
  )
101
106
  LOG.debug("HTTP response %s: %s", resp.status_code, resp.content)
102
107
  resp.raise_for_status()
@@ -139,6 +144,7 @@ class UploadService:
139
144
  headers=headers,
140
145
  data=chunk,
141
146
  timeout=UPLOAD_REQUESTS_TIMEOUT,
147
+ verify=not MAPILLARY__DISABLE_VERIFYING_SSL,
142
148
  )
143
149
  LOG.debug(
144
150
  "HTTP response %s: %s", resp.status_code, _truncate_end(resp.content)
@@ -185,6 +191,7 @@ class UploadService:
185
191
  headers=headers,
186
192
  json=data,
187
193
  timeout=REQUESTS_TIMEOUT,
194
+ verify=not MAPILLARY__DISABLE_VERIFYING_SSL,
188
195
  )
189
196
  LOG.debug("HTTP response %s: %s", resp.status_code, _truncate_end(resp.content))
190
197
 
@@ -321,7 +321,11 @@ def _extract_upload_md5sum(fp: T.IO[bytes]) -> T.Optional[str]:
321
321
 
322
322
 
323
323
  def _is_immediate_retry(ex: Exception):
324
- if isinstance(ex, requests.HTTPError) and ex.response.status_code == 412:
324
+ if (
325
+ isinstance(ex, requests.HTTPError)
326
+ and isinstance(ex.response, requests.Response)
327
+ and ex.response.status_code == 412
328
+ ):
325
329
  try:
326
330
  resp = ex.response.json()
327
331
  except json.JSONDecodeError:
@@ -334,7 +338,9 @@ def _is_retriable_exception(ex: Exception):
334
338
  if isinstance(ex, (requests.ConnectionError, requests.Timeout)):
335
339
  return True
336
340
 
337
- if isinstance(ex, requests.HTTPError):
341
+ if isinstance(ex, requests.HTTPError) and isinstance(
342
+ ex.response, requests.Response
343
+ ):
338
344
  if 400 <= ex.response.status_code < 500:
339
345
  try:
340
346
  resp = ex.response.json()
@@ -20,3 +20,4 @@ class CliOptions(T.TypedDict, total=False):
20
20
  num_processes: int
21
21
  device_make: T.Optional[str]
22
22
  device_model: T.Optional[str]
23
+ check_file_suffix: bool
@@ -33,7 +33,9 @@ class VideoDataExtractor:
33
33
  def process(self) -> T.List[MetadataOrError]:
34
34
  paths = self.options["paths"]
35
35
  self._check_paths(paths)
36
- video_files = utils.find_videos(paths)
36
+ video_files = utils.find_videos(
37
+ paths, check_file_suffix=self.options["check_file_suffix"]
38
+ )
37
39
  self._check_sources_cardinality(video_files)
38
40
 
39
41
  num_processes = self.options["num_processes"] or None
@@ -102,10 +104,12 @@ class VideoDataExtractor:
102
104
  else:
103
105
  return ErrorMetadata(
104
106
  filename=file,
105
- error=ex
106
- if ex
107
- else exceptions.MapillaryVideoGPSNotFoundError(
108
- "No GPS data found from the video"
107
+ error=(
108
+ ex
109
+ if ex
110
+ else exceptions.MapillaryVideoGPSNotFoundError(
111
+ "No GPS data found from the video"
112
+ )
109
113
  ),
110
114
  filetype=FileType.VIDEO,
111
115
  )
@@ -30,13 +30,17 @@ class ExiftoolRuntimeParser(BaseParser):
30
30
  ):
31
31
  super().__init__(video_path, options, parser_options)
32
32
  exiftool_path = shutil.which(constants.EXIFTOOL_PATH)
33
+
33
34
  if not exiftool_path:
34
35
  raise exceptions.MapillaryExiftoolNotFoundError(
35
36
  "Cannot execute exiftool. Please install it from https://exiftool.org/ or you package manager, or set the environment variable MAPILLARY_TOOLS_EXIFTOOL_PATH"
36
37
  )
37
-
38
38
  if not self.geotag_source_path:
39
39
  return
40
+
41
+ # To handle non-latin1 filenames under Windows, we pass the path
42
+ # via stdin. See https://exiftool.org/faq.html#Q18
43
+ stdin = str(self.geotag_source_path)
40
44
  args = [
41
45
  exiftool_path,
42
46
  "-q",
@@ -46,13 +50,18 @@ class ExiftoolRuntimeParser(BaseParser):
46
50
  "-api",
47
51
  "LargeFileSupport=1",
48
52
  "-X",
49
- str(self.geotag_source_path),
53
+ "-charset",
54
+ "filename=utf8",
55
+ "-@",
56
+ "-",
50
57
  ]
51
58
 
52
- xml_content = subprocess.run(args, capture_output=True, text=True).stdout
59
+ process = subprocess.run(
60
+ args, capture_output=True, text=True, input=stdin, encoding="utf-8"
61
+ )
53
62
 
54
63
  self.exiftoolXmlParser = ExiftoolXmlParser(
55
- video_path, options, parser_options, xml_content
64
+ video_path, options, parser_options, process.stdout
56
65
  )
57
66
 
58
67
  def extract_points(self) -> T.Sequence[geo.Point]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
- Name: mapillary-tools
3
- Version: 0.11.0b4
2
+ Name: mapillary_tools
3
+ Version: 0.11.2
4
4
  Summary: Mapillary Image/Video Import Pipeline
5
5
  Home-page: https://github.com/mapillary/mapillary_tools
6
6
  Author: Mapillary
@@ -13,7 +13,7 @@ Requires-Dist: construct<3.0.0,>=2.10.0
13
13
  Requires-Dist: exifread==2.3.2
14
14
  Requires-Dist: piexif==1.1.3
15
15
  Requires-Dist: gpxpy<1.6.0,>=1.5.0
16
- Requires-Dist: pynmea2==1.12.0
16
+ Requires-Dist: pynmea2<2.0.0,>=1.12.0
17
17
  Requires-Dist: requests<3.0.0,>=2.20.0
18
18
  Requires-Dist: tqdm<5.0,>=4.0
19
19
  Requires-Dist: typing_extensions
@@ -130,7 +130,7 @@ A command line program such as Termux is required. Installation can be done with
130
130
  commands will install Python 3, pip3, git, and all required libraries for mapillary_tools on Termux:
131
131
 
132
132
  ```sh
133
- pkg install python git build-essential libgeos openssl libjpeg-turbo
133
+ pkg install python git build-essential libgeos openssl libjpeg-turbo libexpat libexpat-static
134
134
  pip install --upgrade pip wheel
135
135
  pip install --upgrade mapillary_tools
136
136
  ```
@@ -3,7 +3,7 @@ construct<3.0.0,>=2.10.0
3
3
  exifread==2.3.2
4
4
  piexif==1.1.3
5
5
  gpxpy<1.6.0,>=1.5.0
6
- pynmea2==1.12.0
6
+ pynmea2<2.0.0,>=1.12.0
7
7
  requests<3.0.0,>=2.20.0
8
8
  tqdm<5.0,>=4.0
9
9
  typing_extensions
@@ -3,7 +3,7 @@ construct>=2.10.0,<3.0.0
3
3
  exifread==2.3.2
4
4
  piexif==1.1.3
5
5
  gpxpy>=1.5.0,<1.6.0
6
- pynmea2==1.12.0
6
+ pynmea2>=1.12.0,<2.0.0
7
7
  requests>=2.20.0,<3.0.0
8
8
  tqdm>=4.0,<5.0
9
9
  typing_extensions
@@ -1 +0,0 @@
1
- VERSION = "0.11.0b4"