mapillary-tools 0.14.0a1__py3-none-any.whl → 0.14.0a2__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.
- mapillary_tools/__init__.py +1 -1
- mapillary_tools/api_v4.py +4 -4
- mapillary_tools/camm/camm_parser.py +5 -5
- mapillary_tools/commands/__main__.py +1 -2
- mapillary_tools/config.py +7 -5
- mapillary_tools/constants.py +1 -2
- mapillary_tools/exceptions.py +1 -1
- mapillary_tools/exif_read.py +65 -65
- mapillary_tools/exif_write.py +7 -7
- mapillary_tools/exiftool_read.py +23 -46
- mapillary_tools/exiftool_read_video.py +36 -34
- mapillary_tools/ffmpeg.py +24 -23
- mapillary_tools/geo.py +4 -21
- mapillary_tools/geotag/{geotag_from_generic.py → base.py} +32 -48
- mapillary_tools/geotag/factory.py +27 -34
- mapillary_tools/geotag/geotag_images_from_exif.py +15 -51
- mapillary_tools/geotag/geotag_images_from_exiftool.py +107 -59
- mapillary_tools/geotag/geotag_images_from_gpx.py +20 -10
- mapillary_tools/geotag/geotag_images_from_gpx_file.py +2 -34
- mapillary_tools/geotag/geotag_images_from_nmea_file.py +0 -3
- mapillary_tools/geotag/geotag_images_from_video.py +16 -14
- mapillary_tools/geotag/geotag_videos_from_exiftool.py +97 -0
- mapillary_tools/geotag/geotag_videos_from_gpx.py +14 -115
- mapillary_tools/geotag/geotag_videos_from_video.py +14 -147
- mapillary_tools/geotag/image_extractors/base.py +18 -0
- mapillary_tools/geotag/image_extractors/exif.py +60 -0
- mapillary_tools/geotag/image_extractors/exiftool.py +18 -0
- mapillary_tools/geotag/options.py +1 -0
- mapillary_tools/geotag/utils.py +62 -0
- mapillary_tools/geotag/video_extractors/base.py +18 -0
- mapillary_tools/geotag/video_extractors/exiftool.py +70 -0
- mapillary_tools/{video_data_extraction/extractors/gpx_parser.py → geotag/video_extractors/gpx.py} +57 -39
- mapillary_tools/geotag/video_extractors/native.py +157 -0
- mapillary_tools/gpmf/gpmf_parser.py +16 -16
- mapillary_tools/gpmf/gps_filter.py +5 -3
- mapillary_tools/history.py +4 -2
- mapillary_tools/mp4/construct_mp4_parser.py +9 -8
- mapillary_tools/mp4/mp4_sample_parser.py +27 -27
- mapillary_tools/mp4/simple_mp4_builder.py +10 -9
- mapillary_tools/mp4/simple_mp4_parser.py +13 -12
- mapillary_tools/process_geotag_properties.py +5 -7
- mapillary_tools/process_sequence_properties.py +40 -38
- mapillary_tools/sample_video.py +8 -8
- mapillary_tools/telemetry.py +6 -5
- mapillary_tools/types.py +33 -38
- mapillary_tools/utils.py +16 -18
- {mapillary_tools-0.14.0a1.dist-info → mapillary_tools-0.14.0a2.dist-info}/METADATA +1 -1
- mapillary_tools-0.14.0a2.dist-info/RECORD +72 -0
- mapillary_tools/geotag/__init__.py +0 -1
- mapillary_tools/geotag/geotag_images_from_exiftool_both_image_and_video.py +0 -77
- mapillary_tools/geotag/geotag_videos_from_exiftool_video.py +0 -151
- mapillary_tools/video_data_extraction/cli_options.py +0 -22
- mapillary_tools/video_data_extraction/extract_video_data.py +0 -157
- mapillary_tools/video_data_extraction/extractors/base_parser.py +0 -75
- mapillary_tools/video_data_extraction/extractors/blackvue_parser.py +0 -49
- mapillary_tools/video_data_extraction/extractors/camm_parser.py +0 -62
- mapillary_tools/video_data_extraction/extractors/exiftool_runtime_parser.py +0 -74
- mapillary_tools/video_data_extraction/extractors/exiftool_xml_parser.py +0 -52
- mapillary_tools/video_data_extraction/extractors/generic_video_parser.py +0 -52
- mapillary_tools/video_data_extraction/extractors/gopro_parser.py +0 -58
- mapillary_tools/video_data_extraction/extractors/nmea_parser.py +0 -24
- mapillary_tools/video_data_extraction/video_data_parser_factory.py +0 -39
- mapillary_tools-0.14.0a1.dist-info/RECORD +0 -78
- {mapillary_tools-0.14.0a1.dist-info → mapillary_tools-0.14.0a2.dist-info}/WHEEL +0 -0
- {mapillary_tools-0.14.0a1.dist-info → mapillary_tools-0.14.0a2.dist-info}/entry_points.txt +0 -0
- {mapillary_tools-0.14.0a1.dist-info → mapillary_tools-0.14.0a2.dist-info}/licenses/LICENSE +0 -0
- {mapillary_tools-0.14.0a1.dist-info → mapillary_tools-0.14.0a2.dist-info}/top_level.txt +0 -0
mapillary_tools/sample_video.py
CHANGED
|
@@ -19,7 +19,7 @@ LOG = logging.getLogger(__name__)
|
|
|
19
19
|
|
|
20
20
|
def _normalize_path(
|
|
21
21
|
video_import_path: Path, skip_subfolders: bool
|
|
22
|
-
) ->
|
|
22
|
+
) -> tuple[Path, list[Path]]:
|
|
23
23
|
if video_import_path.is_dir():
|
|
24
24
|
video_list = utils.find_videos(
|
|
25
25
|
[video_import_path], skip_subfolders=skip_subfolders
|
|
@@ -51,7 +51,7 @@ def sample_video(
|
|
|
51
51
|
video_sample_distance=constants.VIDEO_SAMPLE_DISTANCE,
|
|
52
52
|
video_sample_interval=constants.VIDEO_SAMPLE_INTERVAL,
|
|
53
53
|
video_duration_ratio=constants.VIDEO_DURATION_RATIO,
|
|
54
|
-
video_start_time:
|
|
54
|
+
video_start_time: str | None = None,
|
|
55
55
|
skip_sample_errors: bool = False,
|
|
56
56
|
rerun: bool = False,
|
|
57
57
|
) -> None:
|
|
@@ -62,7 +62,7 @@ def sample_video(
|
|
|
62
62
|
f"Expect either non-negative video_sample_distance or positive video_sample_interval but got {video_sample_distance} and {video_sample_interval} respectively"
|
|
63
63
|
)
|
|
64
64
|
|
|
65
|
-
video_start_time_dt:
|
|
65
|
+
video_start_time_dt: datetime.datetime | None = None
|
|
66
66
|
if video_start_time is not None:
|
|
67
67
|
try:
|
|
68
68
|
video_start_time_dt = types.map_capture_time_to_datetime(video_start_time)
|
|
@@ -179,7 +179,7 @@ def _sample_single_video_by_interval(
|
|
|
179
179
|
sample_dir: Path,
|
|
180
180
|
sample_interval: float,
|
|
181
181
|
duration_ratio: float,
|
|
182
|
-
start_time:
|
|
182
|
+
start_time: datetime.datetime | None = None,
|
|
183
183
|
) -> None:
|
|
184
184
|
ffmpeg = ffmpeglib.FFMPEG(constants.FFMPEG_PATH, constants.FFPROBE_PATH)
|
|
185
185
|
|
|
@@ -219,7 +219,7 @@ def _sample_video_stream_by_distance(
|
|
|
219
219
|
points: T.Sequence[geo.Point],
|
|
220
220
|
video_track_parser: mp4_sample_parser.TrackBoxParser,
|
|
221
221
|
sample_distance: float,
|
|
222
|
-
) ->
|
|
222
|
+
) -> dict[int, tuple[mp4_sample_parser.Sample, geo.Point]]:
|
|
223
223
|
"""
|
|
224
224
|
Locate video frames along the track (points), then resample them by the minimal sample_distance, and return the sparse frames.
|
|
225
225
|
"""
|
|
@@ -275,7 +275,7 @@ def _sample_single_video_by_distance(
|
|
|
275
275
|
video_path: Path,
|
|
276
276
|
sample_dir: Path,
|
|
277
277
|
sample_distance: float,
|
|
278
|
-
start_time:
|
|
278
|
+
start_time: datetime.datetime | None = None,
|
|
279
279
|
) -> None:
|
|
280
280
|
ffmpeg = ffmpeglib.FFMPEG(constants.FFMPEG_PATH, constants.FFPROBE_PATH)
|
|
281
281
|
|
|
@@ -290,9 +290,9 @@ def _sample_single_video_by_distance(
|
|
|
290
290
|
|
|
291
291
|
LOG.info("Extracting video metdata")
|
|
292
292
|
|
|
293
|
-
video_metadatas = geotag_videos_from_video.GeotagVideosFromVideo(
|
|
293
|
+
video_metadatas = geotag_videos_from_video.GeotagVideosFromVideo().to_description(
|
|
294
294
|
[video_path]
|
|
295
|
-
)
|
|
295
|
+
)
|
|
296
296
|
assert len(video_metadatas) == 1, "expect 1 video metadata"
|
|
297
297
|
video_metadata = video_metadatas[0]
|
|
298
298
|
if isinstance(video_metadata, types.ErrorMetadata):
|
mapillary_tools/telemetry.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import dataclasses
|
|
2
|
-
import typing as T
|
|
3
4
|
from enum import Enum, unique
|
|
4
5
|
|
|
5
6
|
from .geo import Point
|
|
@@ -26,10 +27,10 @@ class TimestampedMeasurement:
|
|
|
26
27
|
|
|
27
28
|
@dataclasses.dataclass
|
|
28
29
|
class GPSPoint(TimestampedMeasurement, Point):
|
|
29
|
-
epoch_time:
|
|
30
|
-
fix:
|
|
31
|
-
precision:
|
|
32
|
-
ground_speed:
|
|
30
|
+
epoch_time: float | None
|
|
31
|
+
fix: GPSFix | None
|
|
32
|
+
precision: float | None
|
|
33
|
+
ground_speed: float | None
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
@dataclasses.dataclass
|
mapillary_tools/types.py
CHANGED
|
@@ -53,23 +53,20 @@ NATIVE_VIDEO_FILETYPES = {
|
|
|
53
53
|
@dataclasses.dataclass
|
|
54
54
|
class ImageMetadata(geo.Point):
|
|
55
55
|
filename: Path
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
filesize: T.Optional[int] = None
|
|
71
|
-
|
|
72
|
-
def update_md5sum(self, image_data: T.Optional[T.BinaryIO] = None) -> None:
|
|
56
|
+
md5sum: str | None = None
|
|
57
|
+
width: int | None = None
|
|
58
|
+
height: int | None = None
|
|
59
|
+
MAPSequenceUUID: str | None = None
|
|
60
|
+
MAPDeviceMake: str | None = None
|
|
61
|
+
MAPDeviceModel: str | None = None
|
|
62
|
+
MAPGPSAccuracyMeters: float | None = None
|
|
63
|
+
MAPCameraUUID: str | None = None
|
|
64
|
+
MAPOrientation: int | None = None
|
|
65
|
+
MAPMetaTags: dict | None = None
|
|
66
|
+
MAPFilename: str | None = None
|
|
67
|
+
filesize: int | None = None
|
|
68
|
+
|
|
69
|
+
def update_md5sum(self, image_data: T.BinaryIO | None = None) -> None:
|
|
73
70
|
if self.md5sum is None:
|
|
74
71
|
if image_data is None:
|
|
75
72
|
with self.filename.open("rb") as fp:
|
|
@@ -87,13 +84,12 @@ class ImageMetadata(geo.Point):
|
|
|
87
84
|
@dataclasses.dataclass
|
|
88
85
|
class VideoMetadata:
|
|
89
86
|
filename: Path
|
|
90
|
-
# if None or absent, it will be calculated
|
|
91
87
|
filetype: FileType
|
|
92
88
|
points: T.Sequence[geo.Point]
|
|
93
|
-
md5sum:
|
|
94
|
-
make:
|
|
95
|
-
model:
|
|
96
|
-
filesize:
|
|
89
|
+
md5sum: str | None = None
|
|
90
|
+
make: str | None = None
|
|
91
|
+
model: str | None = None
|
|
92
|
+
filesize: int | None = None
|
|
97
93
|
|
|
98
94
|
def update_md5sum(self) -> None:
|
|
99
95
|
if self.md5sum is None:
|
|
@@ -144,7 +140,7 @@ def combine_filetype_filters(
|
|
|
144
140
|
|
|
145
141
|
|
|
146
142
|
class UserItem(TypedDict, total=False):
|
|
147
|
-
MAPOrganizationKey:
|
|
143
|
+
MAPOrganizationKey: int | str
|
|
148
144
|
# Not in use. Keep here for back-compatibility
|
|
149
145
|
MAPSettingsUsername: str
|
|
150
146
|
MAPSettingsUserKey: str
|
|
@@ -183,23 +179,22 @@ class ImageDescription(_SequenceOnly, _Image, MetaProperties, total=True):
|
|
|
183
179
|
# filename is required
|
|
184
180
|
filename: str
|
|
185
181
|
# if None or absent, it will be calculated
|
|
186
|
-
md5sum:
|
|
182
|
+
md5sum: str | None
|
|
187
183
|
filetype: Literal["image"]
|
|
188
|
-
filesize:
|
|
184
|
+
filesize: int | None
|
|
189
185
|
|
|
190
186
|
|
|
191
187
|
class _VideoDescriptionRequired(TypedDict, total=True):
|
|
192
188
|
filename: str
|
|
193
|
-
|
|
194
|
-
md5sum: T.Optional[str]
|
|
189
|
+
md5sum: str | None
|
|
195
190
|
filetype: str
|
|
196
|
-
MAPGPSTrack:
|
|
191
|
+
MAPGPSTrack: list[T.Sequence[float | int | None]]
|
|
197
192
|
|
|
198
193
|
|
|
199
194
|
class VideoDescription(_VideoDescriptionRequired, total=False):
|
|
200
195
|
MAPDeviceMake: str
|
|
201
196
|
MAPDeviceModel: str
|
|
202
|
-
filesize:
|
|
197
|
+
filesize: int | None
|
|
203
198
|
|
|
204
199
|
|
|
205
200
|
class _ErrorDescription(TypedDict, total=False):
|
|
@@ -207,7 +202,7 @@ class _ErrorDescription(TypedDict, total=False):
|
|
|
207
202
|
type: str
|
|
208
203
|
message: str
|
|
209
204
|
# vars is optional
|
|
210
|
-
vars:
|
|
205
|
+
vars: dict
|
|
211
206
|
|
|
212
207
|
|
|
213
208
|
class _ImageDescriptionErrorRequired(TypedDict, total=True):
|
|
@@ -238,7 +233,7 @@ def separate_errors(
|
|
|
238
233
|
|
|
239
234
|
|
|
240
235
|
def _describe_error_desc(
|
|
241
|
-
exc: Exception, filename: Path, filetype:
|
|
236
|
+
exc: Exception, filename: Path, filetype: FileType | None
|
|
242
237
|
) -> ImageDescriptionError:
|
|
243
238
|
err: _ErrorDescription = {
|
|
244
239
|
"type": exc.__class__.__name__,
|
|
@@ -397,7 +392,7 @@ VideoDescriptionSchema = {
|
|
|
397
392
|
}
|
|
398
393
|
|
|
399
394
|
|
|
400
|
-
def merge_schema(*schemas:
|
|
395
|
+
def merge_schema(*schemas: dict) -> dict:
|
|
401
396
|
for s in schemas:
|
|
402
397
|
assert s.get("type") == "object", "must be all object schemas"
|
|
403
398
|
properties = {}
|
|
@@ -507,7 +502,7 @@ def validate_video_desc(desc: T.Any) -> None:
|
|
|
507
502
|
raise exceptions.MapillaryMetadataValidationError(ex.message) from ex
|
|
508
503
|
|
|
509
504
|
|
|
510
|
-
def datetime_to_map_capture_time(time:
|
|
505
|
+
def datetime_to_map_capture_time(time: datetime.datetime | int | float) -> str:
|
|
511
506
|
if isinstance(time, (float, int)):
|
|
512
507
|
dt = datetime.datetime.fromtimestamp(time, datetime.timezone.utc)
|
|
513
508
|
# otherwise it will be assumed to be in local time
|
|
@@ -608,7 +603,7 @@ def from_desc(desc):
|
|
|
608
603
|
|
|
609
604
|
|
|
610
605
|
def _from_image_desc(desc) -> ImageMetadata:
|
|
611
|
-
kwargs:
|
|
606
|
+
kwargs: dict = {}
|
|
612
607
|
for k, v in desc.items():
|
|
613
608
|
if k not in [
|
|
614
609
|
"filename",
|
|
@@ -638,7 +633,7 @@ def _from_image_desc(desc) -> ImageMetadata:
|
|
|
638
633
|
)
|
|
639
634
|
|
|
640
635
|
|
|
641
|
-
def _encode_point(p: geo.Point) -> T.Sequence[
|
|
636
|
+
def _encode_point(p: geo.Point) -> T.Sequence[float | int | None]:
|
|
642
637
|
entry = [
|
|
643
638
|
int(p.time * 1000),
|
|
644
639
|
round(p.lon, _COORDINATES_PRECISION),
|
|
@@ -743,10 +738,10 @@ def desc_file_to_exif(
|
|
|
743
738
|
|
|
744
739
|
|
|
745
740
|
def group_and_sort_images(
|
|
746
|
-
metadatas: T.
|
|
747
|
-
) ->
|
|
741
|
+
metadatas: T.Iterable[ImageMetadata],
|
|
742
|
+
) -> dict[str, list[ImageMetadata]]:
|
|
748
743
|
# group metadatas by uuid
|
|
749
|
-
sequences_by_uuid:
|
|
744
|
+
sequences_by_uuid: dict[str, list[ImageMetadata]] = {}
|
|
750
745
|
missing_sequence_uuid = str(uuid.uuid4())
|
|
751
746
|
for metadata in metadatas:
|
|
752
747
|
if metadata.MAPSequenceUUID is None:
|
mapillary_tools/utils.py
CHANGED
|
@@ -9,9 +9,7 @@ from pathlib import Path
|
|
|
9
9
|
|
|
10
10
|
# Use "hashlib._Hash" instead of hashlib._Hash because:
|
|
11
11
|
# AttributeError: module 'hashlib' has no attribute '_Hash'
|
|
12
|
-
def md5sum_fp(
|
|
13
|
-
fp: T.IO[bytes], md5: T.Optional["hashlib._Hash"] = None
|
|
14
|
-
) -> "hashlib._Hash":
|
|
12
|
+
def md5sum_fp(fp: T.IO[bytes], md5: "hashlib._Hash | None" = None) -> "hashlib._Hash":
|
|
15
13
|
if md5 is None:
|
|
16
14
|
md5 = hashlib.md5()
|
|
17
15
|
while True:
|
|
@@ -93,12 +91,12 @@ def filter_video_samples(
|
|
|
93
91
|
|
|
94
92
|
|
|
95
93
|
def find_all_image_samples(
|
|
96
|
-
image_paths: T.
|
|
97
|
-
) ->
|
|
94
|
+
image_paths: T.Iterable[Path], video_paths: T.Iterable[Path]
|
|
95
|
+
) -> dict[Path, list[Path]]:
|
|
98
96
|
# TODO: not work with the same filenames, e.g. foo/hello.mp4 and bar/hello.mp4
|
|
99
97
|
video_basenames = {path.name: path for path in video_paths}
|
|
100
98
|
|
|
101
|
-
image_samples_by_video_path:
|
|
99
|
+
image_samples_by_video_path: dict[Path, list[Path]] = {}
|
|
102
100
|
for image_path in image_paths:
|
|
103
101
|
# If you want to walk an arbitrary filesystem path upwards,
|
|
104
102
|
# it is recommended to first call Path.resolve() so as to resolve symlinks and eliminate “..” components.
|
|
@@ -115,7 +113,7 @@ def find_all_image_samples(
|
|
|
115
113
|
|
|
116
114
|
|
|
117
115
|
def deduplicate_paths(paths: T.Iterable[Path]) -> T.Generator[Path, None, None]:
|
|
118
|
-
resolved_paths:
|
|
116
|
+
resolved_paths: set[Path] = set()
|
|
119
117
|
for p in paths:
|
|
120
118
|
resolved = p.resolve()
|
|
121
119
|
if resolved not in resolved_paths:
|
|
@@ -124,10 +122,10 @@ def deduplicate_paths(paths: T.Iterable[Path]) -> T.Generator[Path, None, None]:
|
|
|
124
122
|
|
|
125
123
|
|
|
126
124
|
def find_images(
|
|
127
|
-
import_paths: T.
|
|
125
|
+
import_paths: T.Iterable[Path],
|
|
128
126
|
skip_subfolders: bool = False,
|
|
129
|
-
) ->
|
|
130
|
-
image_paths:
|
|
127
|
+
) -> list[Path]:
|
|
128
|
+
image_paths: list[Path] = []
|
|
131
129
|
for path in import_paths:
|
|
132
130
|
if path.is_dir():
|
|
133
131
|
image_paths.extend(
|
|
@@ -142,10 +140,10 @@ def find_images(
|
|
|
142
140
|
|
|
143
141
|
|
|
144
142
|
def find_videos(
|
|
145
|
-
import_paths: T.
|
|
143
|
+
import_paths: T.Iterable[Path],
|
|
146
144
|
skip_subfolders: bool = False,
|
|
147
|
-
) ->
|
|
148
|
-
video_paths:
|
|
145
|
+
) -> list[Path]:
|
|
146
|
+
video_paths: list[Path] = []
|
|
149
147
|
for path in import_paths:
|
|
150
148
|
if path.is_dir():
|
|
151
149
|
video_paths.extend(
|
|
@@ -160,10 +158,10 @@ def find_videos(
|
|
|
160
158
|
|
|
161
159
|
|
|
162
160
|
def find_zipfiles(
|
|
163
|
-
import_paths: T.
|
|
161
|
+
import_paths: T.Iterable[Path],
|
|
164
162
|
skip_subfolders: bool = False,
|
|
165
|
-
) ->
|
|
166
|
-
zip_paths:
|
|
163
|
+
) -> list[Path]:
|
|
164
|
+
zip_paths: list[Path] = []
|
|
167
165
|
for path in import_paths:
|
|
168
166
|
if path.is_dir():
|
|
169
167
|
zip_paths.extend(
|
|
@@ -177,8 +175,8 @@ def find_zipfiles(
|
|
|
177
175
|
return list(deduplicate_paths(zip_paths))
|
|
178
176
|
|
|
179
177
|
|
|
180
|
-
def find_xml_files(import_paths: T.
|
|
181
|
-
xml_paths:
|
|
178
|
+
def find_xml_files(import_paths: T.Iterable[Path]) -> list[Path]:
|
|
179
|
+
xml_paths: list[Path] = []
|
|
182
180
|
for path in import_paths:
|
|
183
181
|
if path.is_dir():
|
|
184
182
|
# XML could be hidden in hidden folders
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
mapillary_tools/__init__.py,sha256=AdfWnUWJogoEJm_EqofQxlG8NYL78h0B0M3Igr2xQpE,21
|
|
2
|
+
mapillary_tools/api_v4.py,sha256=uvz48tb2HaM1wfomaUR0V2HdId39HoxnQAc1WWgptvo,10091
|
|
3
|
+
mapillary_tools/authenticate.py,sha256=KG3p_7D4D7nUvrGRLGp56_ogLy7JUHzkGSpBw8V70LY,11091
|
|
4
|
+
mapillary_tools/blackvue_parser.py,sha256=Gmz3M1cvBmB2W3Pme6x3w2yuixDpKiTOkQe82iPXr2s,3613
|
|
5
|
+
mapillary_tools/config.py,sha256=uoNW5PnB1a2TW959KmNSYgJ1Y4_trn0xxPHJQ6T-cbI,2633
|
|
6
|
+
mapillary_tools/constants.py,sha256=BVnAlqz2XJfodNXsJfsGIV0L4P_3--oOgw1pR38D-Ls,3770
|
|
7
|
+
mapillary_tools/exceptions.py,sha256=P8vEh3pfOqyzJgOiQiRAcWsZTrcawjic7i4v3k335tM,2411
|
|
8
|
+
mapillary_tools/exif_read.py,sha256=gagyRv06RkZPRHQhyV-5-uQzxHgxOsVqUX2aC8QmCyM,32145
|
|
9
|
+
mapillary_tools/exif_write.py,sha256=Ue0ngLKCfuLQM7X4h5ETjSInUDcyEBWOR6v8ko7DJ1o,8769
|
|
10
|
+
mapillary_tools/exiftool_read.py,sha256=5uatYE9mgbg2d9NAnPfX22nSRBjXhJ9ayMqNMd4QwGM,15779
|
|
11
|
+
mapillary_tools/exiftool_read_video.py,sha256=WXpw7CI7B4daJHRWzzVXAyVgDrFN8nPxQ3UPcVQqRJM,14760
|
|
12
|
+
mapillary_tools/exiftool_runner.py,sha256=J04V0Y70kTiGE2q2m5TKWfORd0hmYNmAsmrZxb34S3s,2316
|
|
13
|
+
mapillary_tools/ffmpeg.py,sha256=tRgdChGHAhHXjZPivDuVImubrJWfSDZDBZ7mSzkeABc,15698
|
|
14
|
+
mapillary_tools/geo.py,sha256=MSfkjSAgTAeuBqncQ8-QXvDuJ7ar7bTt5P_e9HDIgVs,10604
|
|
15
|
+
mapillary_tools/history.py,sha256=8ng2WHXadzbE5gnsoik6gbmRoEXTuGPp997RBiiWy08,1649
|
|
16
|
+
mapillary_tools/ipc.py,sha256=DwWQb9hNshx0bg0Fo5NjY0mXjs-FkbR6tIQmjMgMtmg,1089
|
|
17
|
+
mapillary_tools/process_geotag_properties.py,sha256=f0Up7yNzgLn32VBFMzoMT6SCsdzFUFYmloOsVHmK5k0,13920
|
|
18
|
+
mapillary_tools/process_sequence_properties.py,sha256=LSYyCH1nmEcYWWZKF1bOYh4hqjwXdz4FY9TqQQfW7AU,23418
|
|
19
|
+
mapillary_tools/sample_video.py,sha256=MwTr2bY5RFnOFThNPg3YiyXCXzWOlTXKq7EOri_W3Vw,13880
|
|
20
|
+
mapillary_tools/telemetry.py,sha256=lL6qQbtOZft4DZZrCNK3njlwHT_30zLyYS_YRN5pgHY,1568
|
|
21
|
+
mapillary_tools/types.py,sha256=0O4fzbwwoT6Znp2Ag1ptjzBKGb3E_RhGyqUUZu6gkPY,23078
|
|
22
|
+
mapillary_tools/upload.py,sha256=exQeEh3jpys94k-3QDT-4xoeJ_lAHbQBoy749hbPfnA,24878
|
|
23
|
+
mapillary_tools/upload_api_v4.py,sha256=dTEpQDnV11TyPf5hefd-FVeEfw7WEVYyZcIUJttUEp0,6417
|
|
24
|
+
mapillary_tools/uploader.py,sha256=xPEszuXlgcgCklGfsgL8qN1ozsFuU4rLT4jocMD2dVI,18694
|
|
25
|
+
mapillary_tools/utils.py,sha256=ft-TrqgwWq1a-HpQqnCWIortZbjjTOywllPi8HBA3FU,6642
|
|
26
|
+
mapillary_tools/camm/camm_builder.py,sha256=ub6Z9ijep8zAo1NOlU51Gxk95kQ2vfN58YgVCLmNMRk,9211
|
|
27
|
+
mapillary_tools/camm/camm_parser.py,sha256=aNHP65hNXYQBWBTfhaj_S5XYzmAHhjwcAfGhbm83__o,18043
|
|
28
|
+
mapillary_tools/commands/__init__.py,sha256=41CFrPLGlG3566uhxssEF3TGAtSpADFPPcDMHbViU0E,171
|
|
29
|
+
mapillary_tools/commands/__main__.py,sha256=5gwngScbvrSNF2S1CA5w_qZgxW_kFevu5NQ89U9u1ZE,5046
|
|
30
|
+
mapillary_tools/commands/authenticate.py,sha256=yqtpHMYzkyBrrchj6MARxB0ywUTfqCEOPkMbkyaO9Ks,1344
|
|
31
|
+
mapillary_tools/commands/process.py,sha256=Japc6_P0B_8HzoM8_82P3_YAiyBBaQZXS9TZF46pbMM,9771
|
|
32
|
+
mapillary_tools/commands/process_and_upload.py,sha256=-RB_86a5xKfQ7Ye79dh6yyJQpZg2xnJZAWOJsUNbUtQ,1041
|
|
33
|
+
mapillary_tools/commands/sample_video.py,sha256=jtnrZrsqqv5eYV1chNTas7RhfbeKBqbAUDUNRFjF01w,3253
|
|
34
|
+
mapillary_tools/commands/upload.py,sha256=YnA1iSvNcFeV7kMs1p4TdaPBUWUFYb7X-vFhQeSz0eI,2005
|
|
35
|
+
mapillary_tools/commands/video_process.py,sha256=-wQeeIwWXPmy81HQHam5A0huMLRHknkEFa_V1OwElU4,890
|
|
36
|
+
mapillary_tools/commands/video_process_and_upload.py,sha256=hOyq9L9TuD0JcqFSOOxdCdgsBA1iJ6fu1TtDbsUr8sI,1088
|
|
37
|
+
mapillary_tools/commands/zip.py,sha256=DVQuMLpbstwiy5o4pU_fBvM6eORuFjLeySd80AhHKU0,991
|
|
38
|
+
mapillary_tools/geotag/base.py,sha256=JdLBXHlC22gqmZkyfAykv1osrzitWoCK8RFHdCd5qlc,4864
|
|
39
|
+
mapillary_tools/geotag/factory.py,sha256=2HIOnUtfxsgWn2GF0PbbgHq3Tzyssrl4T39YDMtN42U,9564
|
|
40
|
+
mapillary_tools/geotag/geotag_images_from_exif.py,sha256=a5fDOp4tlEoVNwrOCJBrvSPzu1LbyxL4RNrNDD0tPP0,608
|
|
41
|
+
mapillary_tools/geotag/geotag_images_from_exiftool.py,sha256=YxMFwv3A35Hh9tcGX1B1Si_m6TNep54SL5XFAQadQh0,5302
|
|
42
|
+
mapillary_tools/geotag/geotag_images_from_gpx.py,sha256=b_4xKVUt0HLIjNN-v7ZVddXDZ9cdxAfecB5Dj2X6XQE,6353
|
|
43
|
+
mapillary_tools/geotag/geotag_images_from_gpx_file.py,sha256=HYQkwak32YBDuRrTNiIZOmE3iImCRc22HWWb485WRS4,1121
|
|
44
|
+
mapillary_tools/geotag/geotag_images_from_nmea_file.py,sha256=J8xj6ch9bMPRubJtsRGeb3sb9LyB0ZYy65NPEOVkUe8,1597
|
|
45
|
+
mapillary_tools/geotag/geotag_images_from_video.py,sha256=5_qVgLt_DKl7oQrr9kWxi4iM143_HPAx5Lau-3fB7t4,3136
|
|
46
|
+
mapillary_tools/geotag/geotag_videos_from_exiftool.py,sha256=7chO_tb7ECsBlOkFhiXQ4fhomVPkfBOWtdfzXGpsJes,3168
|
|
47
|
+
mapillary_tools/geotag/geotag_videos_from_gpx.py,sha256=k4a1imUVLg2yDqggCwTSqjvY7jXQd3E-vniBuJj2d4M,1017
|
|
48
|
+
mapillary_tools/geotag/geotag_videos_from_video.py,sha256=T8XS4lVF2Wz4eDXNi5Vt076M5dxjxJXibVrWhqVvErs,863
|
|
49
|
+
mapillary_tools/geotag/options.py,sha256=7Fz0QoCMyZZbhkKvY4HPNWYfnKdQSWE2oRXb7jYZhmA,4336
|
|
50
|
+
mapillary_tools/geotag/utils.py,sha256=0DenLVCLP-GZdk0rCFP0V1tjMGK4XAvYro3I_1RKgCA,1733
|
|
51
|
+
mapillary_tools/geotag/image_extractors/base.py,sha256=XoNrLCbJtd-MN-snbhv6zyr6zBfJRoJkntV0ptrh6qg,358
|
|
52
|
+
mapillary_tools/geotag/image_extractors/exif.py,sha256=cCUegbFqWxjAP4oOP1nZmwoJISWeKgjGO8h_t7nucHs,2079
|
|
53
|
+
mapillary_tools/geotag/image_extractors/exiftool.py,sha256=zokJmf-D2rPvASRJs3dZzEu7j82igpMOr4SE6Z1nsVg,497
|
|
54
|
+
mapillary_tools/geotag/video_extractors/base.py,sha256=_3B37T-m2HGy77avAF8i_kEatIyQqFfBu49qVnx_0PU,357
|
|
55
|
+
mapillary_tools/geotag/video_extractors/exiftool.py,sha256=CB49qZqgmxECma-N5757aEn37xXRbCNRcT4o-Ip7dCw,2312
|
|
56
|
+
mapillary_tools/geotag/video_extractors/gpx.py,sha256=Oycdn74CNiq1j8dAq7O9YiVNTun2mFWRH9VTUn_viRk,4114
|
|
57
|
+
mapillary_tools/geotag/video_extractors/native.py,sha256=XChOyDvrf88Vu8ED-FTT_hzax2FllokanszSW4Q2MEA,5702
|
|
58
|
+
mapillary_tools/gpmf/gpmf_gps_filter.py,sha256=7cg8wEjC1DrujKY76FZguXsaPqTRkG9-t32OeuOJQIc,2755
|
|
59
|
+
mapillary_tools/gpmf/gpmf_parser.py,sha256=uOVXkwJxC3Y2YfTdzUDpt7Bh0pdVqa5u0WUuv7pEJEs,24670
|
|
60
|
+
mapillary_tools/gpmf/gps_filter.py,sha256=zhXkuvr0Dd1bxGTYBwvk6P7xasY_RLuWjHaX7CdBayc,3796
|
|
61
|
+
mapillary_tools/mp4/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
62
|
+
mapillary_tools/mp4/construct_mp4_parser.py,sha256=ArJ5C8iWJhHNPFkN048DXh6EwQ8LMfnTMwoNlKV9NoI,17191
|
|
63
|
+
mapillary_tools/mp4/io_utils.py,sha256=KZaJTlgFS27Oh3pcA5MKXYFoCifqgFaEZJyU6lb1jc4,5416
|
|
64
|
+
mapillary_tools/mp4/mp4_sample_parser.py,sha256=0ILTq8M6mXFTI3agKgljpvO9uYa7HXGUGZpdHT8a6ac,11547
|
|
65
|
+
mapillary_tools/mp4/simple_mp4_builder.py,sha256=9TUGk1hzI6mQFN1P30jwHL3dCYz3Zz7rsm8UBvMAqMc,12734
|
|
66
|
+
mapillary_tools/mp4/simple_mp4_parser.py,sha256=g3vvPhBoNu7anhVzC5_XQCV7IwfRWro1vJ6d6GyDkHE,6315
|
|
67
|
+
mapillary_tools-0.14.0a2.dist-info/licenses/LICENSE,sha256=l2D8cKfFmmJq_wcVq_JElPJrlvWQOzNWx7gMLINucxc,1292
|
|
68
|
+
mapillary_tools-0.14.0a2.dist-info/METADATA,sha256=UDVbKn5kqizwZmE70erGYDOnSz8sg9CuTd81GHSWtDI,19782
|
|
69
|
+
mapillary_tools-0.14.0a2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
70
|
+
mapillary_tools-0.14.0a2.dist-info/entry_points.txt,sha256=A3f3LP-BO_P-U8Y29QfpT4jx6Mjk3sXjTi2Yew4bvj8,75
|
|
71
|
+
mapillary_tools-0.14.0a2.dist-info/top_level.txt,sha256=FbDkMgOrt1S70ho1WSBrOwzKOSkJFDwwqFOoY5-527s,16
|
|
72
|
+
mapillary_tools-0.14.0a2.dist-info/RECORD,,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .. import geo # noqa: F401
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
import typing as T
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
|
|
7
|
-
from .. import exiftool_read, types, utils
|
|
8
|
-
from . import (
|
|
9
|
-
geotag_images_from_exiftool,
|
|
10
|
-
geotag_images_from_video,
|
|
11
|
-
geotag_videos_from_exiftool_video,
|
|
12
|
-
)
|
|
13
|
-
from .geotag_from_generic import GeotagImagesFromGeneric
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
LOG = logging.getLogger(__name__)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class GeotagImagesFromExifToolBothImageAndVideo(GeotagImagesFromGeneric):
|
|
20
|
-
def __init__(
|
|
21
|
-
self,
|
|
22
|
-
image_paths: T.Sequence[Path],
|
|
23
|
-
xml_path: Path,
|
|
24
|
-
offset_time: float = 0.0,
|
|
25
|
-
num_processes: int | None = None,
|
|
26
|
-
):
|
|
27
|
-
super().__init__(image_paths, num_processes=num_processes)
|
|
28
|
-
self.xml_path = xml_path
|
|
29
|
-
self.offset_time = offset_time
|
|
30
|
-
|
|
31
|
-
def geotag_samples(self) -> list[types.ImageMetadataOrError]:
|
|
32
|
-
# Find all video paths in self.xml_path
|
|
33
|
-
rdf_description_by_path = exiftool_read.index_rdf_description_by_path(
|
|
34
|
-
[self.xml_path]
|
|
35
|
-
)
|
|
36
|
-
video_paths = utils.find_videos(
|
|
37
|
-
[Path(pathstr) for pathstr in rdf_description_by_path.keys()],
|
|
38
|
-
skip_subfolders=True,
|
|
39
|
-
)
|
|
40
|
-
# Find all video paths that have sample images
|
|
41
|
-
samples_by_video = utils.find_all_image_samples(self.image_paths, video_paths)
|
|
42
|
-
|
|
43
|
-
video_metadata_or_errors = (
|
|
44
|
-
geotag_videos_from_exiftool_video.GeotagVideosFromExifToolVideo(
|
|
45
|
-
list(samples_by_video.keys()),
|
|
46
|
-
self.xml_path,
|
|
47
|
-
num_processes=self.num_processes,
|
|
48
|
-
).to_description()
|
|
49
|
-
)
|
|
50
|
-
sample_paths = sum(samples_by_video.values(), [])
|
|
51
|
-
sample_metadata_or_errors = geotag_images_from_video.GeotagImagesFromVideo(
|
|
52
|
-
sample_paths,
|
|
53
|
-
video_metadata_or_errors,
|
|
54
|
-
offset_time=self.offset_time,
|
|
55
|
-
num_processes=self.num_processes,
|
|
56
|
-
).to_description()
|
|
57
|
-
|
|
58
|
-
return sample_metadata_or_errors
|
|
59
|
-
|
|
60
|
-
def to_description(self) -> list[types.ImageMetadataOrError]:
|
|
61
|
-
sample_metadata_or_errors = self.geotag_samples()
|
|
62
|
-
|
|
63
|
-
sample_paths = set(metadata.filename for metadata in sample_metadata_or_errors)
|
|
64
|
-
|
|
65
|
-
non_sample_paths = [
|
|
66
|
-
path for path in self.image_paths if path not in sample_paths
|
|
67
|
-
]
|
|
68
|
-
|
|
69
|
-
non_sample_metadata_or_errors = (
|
|
70
|
-
geotag_images_from_exiftool.GeotagImagesFromExifTool(
|
|
71
|
-
non_sample_paths,
|
|
72
|
-
self.xml_path,
|
|
73
|
-
num_processes=self.num_processes,
|
|
74
|
-
).to_description()
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
return sample_metadata_or_errors + non_sample_metadata_or_errors
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
import typing as T
|
|
5
|
-
import xml.etree.ElementTree as ET
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
|
-
from .. import constants, exceptions, exiftool_read, geo, types, utils
|
|
9
|
-
from ..exiftool_read_video import ExifToolReadVideo
|
|
10
|
-
from ..exiftool_runner import ExiftoolRunner
|
|
11
|
-
from ..gpmf import gpmf_gps_filter
|
|
12
|
-
from ..telemetry import GPSPoint
|
|
13
|
-
from .geotag_from_generic import GenericVideoExtractor, GeotagVideosFromGeneric
|
|
14
|
-
|
|
15
|
-
LOG = logging.getLogger(__name__)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class VideoExifToolExtractor(GenericVideoExtractor):
|
|
19
|
-
def __init__(self, video_path: Path, element: ET.Element):
|
|
20
|
-
super().__init__(video_path)
|
|
21
|
-
self.element = element
|
|
22
|
-
|
|
23
|
-
def extract(self) -> types.VideoMetadataOrError:
|
|
24
|
-
exif = ExifToolReadVideo(ET.ElementTree(self.element))
|
|
25
|
-
|
|
26
|
-
make = exif.extract_make()
|
|
27
|
-
model = exif.extract_model()
|
|
28
|
-
|
|
29
|
-
is_gopro = make is not None and make.upper() in ["GOPRO"]
|
|
30
|
-
|
|
31
|
-
points = exif.extract_gps_track()
|
|
32
|
-
|
|
33
|
-
# ExifTool has no idea if GPS is not found or found but empty
|
|
34
|
-
if is_gopro:
|
|
35
|
-
if not points:
|
|
36
|
-
raise exceptions.MapillaryGPXEmptyError("Empty GPS data found")
|
|
37
|
-
|
|
38
|
-
# ExifTool (since 13.04) converts GPSSpeed for GoPro to km/h, so here we convert it back to m/s
|
|
39
|
-
for p in points:
|
|
40
|
-
if isinstance(p, GPSPoint) and p.ground_speed is not None:
|
|
41
|
-
p.ground_speed = p.ground_speed / 3.6
|
|
42
|
-
|
|
43
|
-
if isinstance(points[0], GPSPoint):
|
|
44
|
-
points = T.cast(
|
|
45
|
-
T.List[geo.Point],
|
|
46
|
-
gpmf_gps_filter.remove_noisy_points(
|
|
47
|
-
T.cast(T.List[GPSPoint], points)
|
|
48
|
-
),
|
|
49
|
-
)
|
|
50
|
-
if not points:
|
|
51
|
-
raise exceptions.MapillaryGPSNoiseError("GPS is too noisy")
|
|
52
|
-
|
|
53
|
-
if not points:
|
|
54
|
-
raise exceptions.MapillaryVideoGPSNotFoundError(
|
|
55
|
-
"No GPS data found from the video"
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
filetype = types.FileType.GOPRO if is_gopro else types.FileType.VIDEO
|
|
59
|
-
|
|
60
|
-
video_metadata = types.VideoMetadata(
|
|
61
|
-
self.video_path,
|
|
62
|
-
filesize=utils.get_file_size(self.video_path),
|
|
63
|
-
filetype=filetype,
|
|
64
|
-
points=points,
|
|
65
|
-
make=make,
|
|
66
|
-
model=model,
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
return video_metadata
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
class GeotagVideosFromExifToolVideo(GeotagVideosFromGeneric):
|
|
73
|
-
def __init__(
|
|
74
|
-
self,
|
|
75
|
-
video_paths: T.Sequence[Path],
|
|
76
|
-
xml_path: Path,
|
|
77
|
-
num_processes: int | None = None,
|
|
78
|
-
):
|
|
79
|
-
super().__init__(video_paths, num_processes=num_processes)
|
|
80
|
-
self.xml_path = xml_path
|
|
81
|
-
|
|
82
|
-
def _generate_video_extractors(
|
|
83
|
-
self,
|
|
84
|
-
) -> T.Sequence[GenericVideoExtractor | types.ErrorMetadata]:
|
|
85
|
-
rdf_description_by_path = exiftool_read.index_rdf_description_by_path(
|
|
86
|
-
[self.xml_path]
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
results: list[VideoExifToolExtractor | types.ErrorMetadata] = []
|
|
90
|
-
|
|
91
|
-
for path in self.video_paths:
|
|
92
|
-
rdf_description = rdf_description_by_path.get(
|
|
93
|
-
exiftool_read.canonical_path(path)
|
|
94
|
-
)
|
|
95
|
-
if rdf_description is None:
|
|
96
|
-
exc = exceptions.MapillaryEXIFNotFoundError(
|
|
97
|
-
f"The {exiftool_read._DESCRIPTION_TAG} XML element for the video not found"
|
|
98
|
-
)
|
|
99
|
-
results.append(
|
|
100
|
-
types.describe_error_metadata(
|
|
101
|
-
exc, path, filetype=types.FileType.VIDEO
|
|
102
|
-
)
|
|
103
|
-
)
|
|
104
|
-
else:
|
|
105
|
-
results.append(VideoExifToolExtractor(path, rdf_description))
|
|
106
|
-
|
|
107
|
-
return results
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
class GeotagVideosFromExifToolRunner(GeotagVideosFromGeneric):
|
|
111
|
-
def _generate_video_extractors(
|
|
112
|
-
self,
|
|
113
|
-
) -> T.Sequence[GenericVideoExtractor | types.ErrorMetadata]:
|
|
114
|
-
runner = ExiftoolRunner(constants.EXIFTOOL_PATH)
|
|
115
|
-
|
|
116
|
-
LOG.debug(
|
|
117
|
-
"Extracting XML from %d videos with exiftool command: %s",
|
|
118
|
-
len(self.video_paths),
|
|
119
|
-
" ".join(runner._build_args_read_stdin()),
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
try:
|
|
123
|
-
xml = runner.extract_xml(self.video_paths)
|
|
124
|
-
except FileNotFoundError as ex:
|
|
125
|
-
raise exceptions.MapillaryExiftoolNotFoundError(ex) from ex
|
|
126
|
-
|
|
127
|
-
rdf_description_by_path = (
|
|
128
|
-
exiftool_read.index_rdf_description_by_path_from_xml_element(
|
|
129
|
-
ET.fromstring(xml)
|
|
130
|
-
)
|
|
131
|
-
)
|
|
132
|
-
|
|
133
|
-
results: list[VideoExifToolExtractor | types.ErrorMetadata] = []
|
|
134
|
-
|
|
135
|
-
for path in self.video_paths:
|
|
136
|
-
rdf_description = rdf_description_by_path.get(
|
|
137
|
-
exiftool_read.canonical_path(path)
|
|
138
|
-
)
|
|
139
|
-
if rdf_description is None:
|
|
140
|
-
exc = exceptions.MapillaryEXIFNotFoundError(
|
|
141
|
-
f"The {exiftool_read._DESCRIPTION_TAG} XML element for the video not found"
|
|
142
|
-
)
|
|
143
|
-
results.append(
|
|
144
|
-
types.describe_error_metadata(
|
|
145
|
-
exc, path, filetype=types.FileType.VIDEO
|
|
146
|
-
)
|
|
147
|
-
)
|
|
148
|
-
else:
|
|
149
|
-
results.append(VideoExifToolExtractor(path, rdf_description))
|
|
150
|
-
|
|
151
|
-
return results
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import typing as T
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
known_parser_options = ["source", "pattern", "exiftool_path"]
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class CliParserOptions(T.TypedDict, total=False):
|
|
9
|
-
source: str
|
|
10
|
-
pattern: T.Optional[str]
|
|
11
|
-
exiftool_path: T.Optional[Path]
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class CliOptions(T.TypedDict, total=False):
|
|
15
|
-
paths: T.Sequence[Path]
|
|
16
|
-
recursive: bool
|
|
17
|
-
geotag_sources_options: T.Sequence[CliParserOptions]
|
|
18
|
-
geotag_source_path: Path
|
|
19
|
-
exiftool_path: Path
|
|
20
|
-
num_processes: int
|
|
21
|
-
device_make: T.Optional[str]
|
|
22
|
-
device_model: T.Optional[str]
|