mapillary-downloader 0.4.0__py3-none-any.whl → 0.4.2__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.
@@ -56,9 +56,21 @@ def main():
56
56
  action="store_true",
57
57
  help="Don't check if collection exists on Internet Archive before downloading",
58
58
  )
59
+ parser.add_argument(
60
+ "--debug",
61
+ action="store_true",
62
+ help="Enable debug logging (EXIF data, API responses, etc.)",
63
+ )
59
64
 
60
65
  args = parser.parse_args()
61
66
 
67
+ # Set debug logging level if requested
68
+ if args.debug:
69
+ import logging
70
+
71
+ logging.getLogger("mapillary_downloader").setLevel(logging.DEBUG)
72
+ logger.debug("Debug logging enabled")
73
+
62
74
  # Check for token
63
75
  if not args.token:
64
76
  logger.error("Error: Mapillary API token required. Use --token or set MAPILLARY_TOKEN environment variable")
@@ -92,8 +92,10 @@ class MapillaryClient:
92
92
  images = data.get("data", [])
93
93
  total_fetched += len(images)
94
94
  logger.info(f"Fetched metadata for {total_fetched:,} images...")
95
+ logger.debug(f"API response paging: {data.get('paging', {})}")
95
96
 
96
97
  for image in images:
98
+ logger.debug(f"Image metadata: {image}")
97
99
  yield image
98
100
 
99
101
  # Get next page URL
@@ -95,8 +95,9 @@ class MapillaryDownloader:
95
95
  logger.info(f"Staging directory: {self.staging_dir}")
96
96
  logger.info(f"Final destination: {self.final_dir}")
97
97
 
98
- # Set up file logging for archival
99
- log_file = self.output_dir / "download.log"
98
+ # Set up file logging for archival with timestamp for incremental runs
99
+ timestamp = time.strftime("%Y%m%d-%H%M%S")
100
+ log_file = self.output_dir / f"download.log.{timestamp}"
100
101
  add_file_handler(log_file)
101
102
  logger.info(f"Logging to: {log_file}")
102
103
 
@@ -1,8 +1,11 @@
1
1
  """EXIF metadata writer for Mapillary images."""
2
2
 
3
+ import logging
3
4
  import piexif
4
5
  from datetime import datetime
5
6
 
7
+ logger = logging.getLogger("mapillary_downloader")
8
+
6
9
 
7
10
  def decimal_to_dms(decimal):
8
11
  """Convert decimal degrees to degrees, minutes, seconds format for EXIF.
@@ -47,6 +50,9 @@ def write_exif_to_image(image_path, metadata):
47
50
  True if successful, False otherwise
48
51
  """
49
52
  try:
53
+ logger.debug(f"Writing EXIF to {image_path}")
54
+ logger.debug(f"Metadata: {metadata}")
55
+
50
56
  # Load existing EXIF data if any
51
57
  try:
52
58
  exif_dict = piexif.load(str(image_path))
@@ -99,13 +105,17 @@ def write_exif_to_image(image_path, metadata):
99
105
  # GPS Altitude - prefer computed_altitude over altitude
100
106
  altitude = metadata.get("computed_altitude") or metadata.get("altitude")
101
107
  if altitude is not None:
102
- exif_dict["GPS"][piexif.GPSIFD.GPSAltitude] = (int(abs(altitude) * 100), 100)
108
+ altitude_val = int(abs(altitude) * 100)
109
+ logger.debug(f"Raw altitude value: {altitude}, calculated: {altitude_val}")
110
+ exif_dict["GPS"][piexif.GPSIFD.GPSAltitude] = (altitude_val, 100)
103
111
  exif_dict["GPS"][piexif.GPSIFD.GPSAltitudeRef] = 1 if altitude < 0 else 0
104
112
 
105
113
  # GPS Compass direction
106
114
  compass = metadata.get("computed_compass_angle") or metadata.get("compass_angle")
107
115
  if compass is not None:
108
- exif_dict["GPS"][piexif.GPSIFD.GPSImgDirection] = (int(compass * 100), 100)
116
+ # Normalize compass to 0-360 range
117
+ compass_val = int((compass % 360) * 100)
118
+ exif_dict["GPS"][piexif.GPSIFD.GPSImgDirection] = (compass_val, 100)
109
119
  exif_dict["GPS"][piexif.GPSIFD.GPSImgDirectionRef] = b"T" # True north
110
120
 
111
121
  # GPS Version
@@ -115,8 +125,10 @@ def write_exif_to_image(image_path, metadata):
115
125
  exif_bytes = piexif.dump(exif_dict)
116
126
  piexif.insert(exif_bytes, str(image_path))
117
127
 
128
+ logger.debug(f"Successfully wrote EXIF to {image_path}")
118
129
  return True
119
130
 
120
131
  except Exception as e:
121
- print(f"Warning: Failed to write EXIF data to {image_path}: {e}")
132
+ logger.warning(f"Failed to write EXIF data to {image_path}: {e}")
133
+ logger.debug(f"Full metadata: {metadata}")
122
134
  return False
@@ -128,6 +128,10 @@ def generate_ia_metadata(collection_dir):
128
128
  logger.warning("Could not determine date range from metadata")
129
129
  first_date = last_date = "unknown"
130
130
 
131
+ # Detect WebP conversion and tarring
132
+ is_webp = "-webp" in collection_dir.name
133
+ has_tars = len(list(collection_dir.glob("*.tar"))) > 0
134
+
131
135
  # Create .meta directory
132
136
  meta_dir = collection_dir / ".meta"
133
137
  meta_dir.mkdir(exist_ok=True)
@@ -139,14 +143,31 @@ def generate_ia_metadata(collection_dir):
139
143
  f"Mapillary images by {username}",
140
144
  )
141
145
 
146
+ # Build resolution string
147
+ if quality == "original":
148
+ resolution_str = "original resolution"
149
+ else:
150
+ resolution_str = f"{quality}px resolution"
151
+
152
+ # Build description with processing details
142
153
  description = (
143
154
  f"Street-level imagery from Mapillary user '{username}'. "
144
- f"Contains {image_count:,} images captured between {first_date} and {last_date}. "
145
- f"Images are organized by sequence ID and include EXIF metadata with GPS coordinates, "
146
- f"camera information, and compass direction.\n\n"
147
- f"Downloaded using mapillary_downloader (https://bitplane.net/dev/python/mapillary_downloader/). "
148
- f"Uploaded using rip (https://bitplane.net/dev/sh/rip)."
155
+ f"Contains {image_count:,} images in {resolution_str} captured between {first_date} and {last_date}."
149
156
  )
157
+
158
+ if has_tars:
159
+ description += " Sequences have been individually tarred."
160
+
161
+ if is_webp:
162
+ description += " Images were recompressed with WebP."
163
+
164
+ description += (
165
+ " Images are organized by sequence ID and include EXIF metadata with GPS coordinates, "
166
+ "camera information, and compass direction.\n\n"
167
+ "Downloaded using mapillary_downloader (https://bitplane.net/dev/python/mapillary_downloader/). "
168
+ "Uploaded using rip (https://bitplane.net/dev/sh/rip)."
169
+ )
170
+
150
171
  write_meta_tag(meta_dir, "description", description)
151
172
 
152
173
  # Subject tags
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mapillary_downloader
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: Download your Mapillary data before it's gone
5
5
  Author-email: Gareth Davidson <gaz@bitplane.net>
6
6
  Requires-Python: >=3.10
@@ -72,18 +72,17 @@ mapillary-downloader --output ./downloads USERNAME1
72
72
 
73
73
  The downloader will:
74
74
 
75
+ * 🏛️ Check Internet Archive to avoid duplicate downloads
75
76
  * 📷 Download multiple users' images organized by sequence
76
77
  * 📜 Inject EXIF metadata (GPS coordinates, camera info, timestamps,
77
78
  compass direction)
79
+ * 🗜️ Convert to WebP (by default) to save ~70% disk space
78
80
  * 🛟 Save progress so you can safely resume if interrupted
79
- * 🗜️ Convert to WebP by default to save ~70% disk space
80
- * 📦 Tar sequence directories for faster uploads
81
- * 🏛️ Check Internet Archive to avoid duplicate downloads
82
- * 💾 Stage downloads in cache, move atomically when complete
81
+ * 📦 Tar sequence directories (by default) for faster uploads to Internet Archive
83
82
 
84
83
  ## WebP Conversion
85
84
 
86
- WebP conversion is **enabled by default** (saves ~70% disk space). You'll need the `cwebp` binary installed:
85
+ You'll need the `cwebp` binary installed:
87
86
 
88
87
  ```bash
89
88
  # Debian/Ubuntu
@@ -105,18 +104,13 @@ By default, sequence directories are automatically tarred after download because
105
104
  if they weren't, you'd spend more time setting up upload metadata than actually
106
105
  uploading files to IA.
107
106
 
108
- To keep individual files instead of creating tars, use the `--no-tar` flag:
109
-
110
- ```bash
111
- mapillary-downloader --no-tar USERNAME
112
- ```
107
+ To keep individual files instead of creating tars, use the `--no-tar` flag.
113
108
 
114
109
  ## Internet Archive upload
115
110
 
116
111
  I've written a bash tool to rip media then tag, queue, and upload to The
117
- Internet Archive. The metadata is in the same format. If you copy completed
118
- download dirs into the `4.ship` dir, they'll find their way into an
119
- appropriately named item.
112
+ Internet Archive. The metadata is in the same format. If you symlink your
113
+ `./mapillary_data` dir to `rip`'s `4.ship` dir, they'll be queued for upload.
120
114
 
121
115
  See inlay for details:
122
116
 
@@ -138,7 +132,7 @@ make help # See other make options
138
132
  * [📖 pydoc](https://bitplane.net/dev/python/mapillary_downloader/pydoc)
139
133
  * [🐍 pypi](https://pypi.org/project/mapillary-downloader)
140
134
  * [🐱 github](https://github.com/bitplane/mapillary_downloader)
141
- * [📀 rip](https://bitplane.net/dev/sh/rip
135
+ * [📀 rip](https://bitplane.net/dev/sh/rip)
142
136
 
143
137
  ## License
144
138
 
@@ -0,0 +1,17 @@
1
+ mapillary_downloader/__init__.py,sha256=KEjiBRghXDeA7E15RJeLBfQm-yNJkowZarL59QOh_1w,120
2
+ mapillary_downloader/__main__.py,sha256=Kjfx2woMyCvAxYAdqvtXtYJknCMviV_K2PSo0cDc8Hg,4320
3
+ mapillary_downloader/client.py,sha256=a5n43FLHP45EHodEjl0ieziBK-b6Ey-rZJwYB6EFhNI,4743
4
+ mapillary_downloader/downloader.py,sha256=cVV24uIc3nQ_YXzqpwdVSr-L4fkME3sXq3pCfFS-0Ls,12476
5
+ mapillary_downloader/exif_writer.py,sha256=K_441EG1siWyNMmFGZSfnORUCjBThkeg4JFtbg9AOsA,5120
6
+ mapillary_downloader/ia_check.py,sha256=L2MEbG_KmlAd5NLmo2HQkO8HWvRN0brE5wXXoyNMbq8,1100
7
+ mapillary_downloader/ia_meta.py,sha256=78rcybHIPnQDsF02KGj6RYmDXzYzrU8sdVx4Q9Y0sfI,6266
8
+ mapillary_downloader/logging_config.py,sha256=Z-wNq34nt7aIhJWdeKc1feTY46P9-Or7HtiX7eUFjEI,2324
9
+ mapillary_downloader/tar_sequences.py,sha256=mqs5p3N7osV_bxTkw6i34GVmxCBBEbIiKKxeh-fWNdU,4430
10
+ mapillary_downloader/utils.py,sha256=yzVgS1mwsklDAqrimaFafgTTXtRYQUbKP98Xgh9d2KA,1174
11
+ mapillary_downloader/webp_converter.py,sha256=vYLLQxDmdnqRz0nm7wXwRUd4x9mQZNah-DrncpA8sNs,1901
12
+ mapillary_downloader/worker.py,sha256=eqaBhP5NE_VoJSTZfFb4OAqGyVX65xyoVUp2vosYBM8,3722
13
+ mapillary_downloader-0.4.2.dist-info/entry_points.txt,sha256=PdYtxOXHMJrUhmiPO4G-F98VuhUI4MN9D_T4KPrVZ5w,75
14
+ mapillary_downloader-0.4.2.dist-info/licenses/LICENSE.md,sha256=7_BIuQ-veOrsF-WarH8kTkm0-xrCLvJ1PFE1C4Ebs64,146
15
+ mapillary_downloader-0.4.2.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
16
+ mapillary_downloader-0.4.2.dist-info/METADATA,sha256=hPFWZByequcnTqySES4YavjWUAvMbTj1u0-Xd3AZ51U,4982
17
+ mapillary_downloader-0.4.2.dist-info/RECORD,,
@@ -1,17 +0,0 @@
1
- mapillary_downloader/__init__.py,sha256=KEjiBRghXDeA7E15RJeLBfQm-yNJkowZarL59QOh_1w,120
2
- mapillary_downloader/__main__.py,sha256=avh546grDz379HbA4JOOH2ovSH64Z69okGZO8LKciJ8,3964
3
- mapillary_downloader/client.py,sha256=O7JgshaM3QKUv0xXuBbe_uPqsTr4lgyuVUHYndvXTfA,4611
4
- mapillary_downloader/downloader.py,sha256=ZtuKiXDnIWfLsF_67Rz_UDeLnLCWcIRe1CvPOnBjdZM,12376
5
- mapillary_downloader/exif_writer.py,sha256=Bn1u3QULfHtae86FnUGcqN450NccJwtwW9wVaSRyx9E,4615
6
- mapillary_downloader/ia_check.py,sha256=L2MEbG_KmlAd5NLmo2HQkO8HWvRN0brE5wXXoyNMbq8,1100
7
- mapillary_downloader/ia_meta.py,sha256=u6tXdg2_eefj4vSRekZQjORutKs4g47FpVnbmE5pElk,5694
8
- mapillary_downloader/logging_config.py,sha256=Z-wNq34nt7aIhJWdeKc1feTY46P9-Or7HtiX7eUFjEI,2324
9
- mapillary_downloader/tar_sequences.py,sha256=mqs5p3N7osV_bxTkw6i34GVmxCBBEbIiKKxeh-fWNdU,4430
10
- mapillary_downloader/utils.py,sha256=yzVgS1mwsklDAqrimaFafgTTXtRYQUbKP98Xgh9d2KA,1174
11
- mapillary_downloader/webp_converter.py,sha256=vYLLQxDmdnqRz0nm7wXwRUd4x9mQZNah-DrncpA8sNs,1901
12
- mapillary_downloader/worker.py,sha256=eqaBhP5NE_VoJSTZfFb4OAqGyVX65xyoVUp2vosYBM8,3722
13
- mapillary_downloader-0.4.0.dist-info/entry_points.txt,sha256=PdYtxOXHMJrUhmiPO4G-F98VuhUI4MN9D_T4KPrVZ5w,75
14
- mapillary_downloader-0.4.0.dist-info/licenses/LICENSE.md,sha256=7_BIuQ-veOrsF-WarH8kTkm0-xrCLvJ1PFE1C4Ebs64,146
15
- mapillary_downloader-0.4.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
16
- mapillary_downloader-0.4.0.dist-info/METADATA,sha256=Lb_alO7CXwH9E1NTEM0NfDX0YoQb4aJSNWD5eBx_O44,5146
17
- mapillary_downloader-0.4.0.dist-info/RECORD,,