@require_optional_import(
[
"googleapiclient",
],
"google-api",
)
def download_file(
service: Any,
file_id: str,
file_name: str,
mime_type: str,
download_folder: Path,
subfolder_path: Optional[str] = None,
) -> str:
"""Download or export file based on its MIME type, optionally saving to a subfolder."""
file_extension = _get_file_extension(mime_type)
if file_extension and (not file_name.lower().endswith(file_extension.lower())):
file_name = f"{file_name}.{file_extension}"
# Define export formats for Google Docs, Sheets, and Slides
export_mime_types = {
"application/vnd.google-apps.document": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", # Google Docs → Word
"application/vnd.google-apps.spreadsheet": "text/csv", # Google Sheets → CSV
"application/vnd.google-apps.presentation": "application/vnd.openxmlformats-officedocument.presentationml.presentation", # Google Slides → PowerPoint
}
# Google Docs, Sheets, and Slides cannot be downloaded directly using service.files().get_media() because they are Google-native files
if mime_type in export_mime_types:
request = service.files().export(fileId=file_id, mimeType=export_mime_types[mime_type])
else:
# Download normal files (videos, images, etc.)
request = service.files().get_media(fileId=file_id)
# Determine the final destination directory
destination_dir = download_folder
if subfolder_path:
destination_dir = download_folder / subfolder_path
# Ensure the subfolder exists, create it if necessary
destination_dir.mkdir(parents=True, exist_ok=True)
# Construct the full path for the file
file_path = destination_dir / file_name
# Save file
try:
with io.BytesIO() as buffer:
downloader = MediaIoBaseDownload(buffer, request)
done = False
while not done:
_, done = downloader.next_chunk()
buffer.seek(0)
with open(file_path, "wb") as f:
f.write(buffer.getvalue())
# Print out the relative path of the downloaded file
relative_path = Path(subfolder_path) / file_name if subfolder_path else Path(file_name)
return f"✅ Downloaded: {relative_path}"
except Exception as e:
# Error message if unable to download
relative_path = Path(subfolder_path) / file_name if subfolder_path else Path(file_name)
return f"❌ FAILED to download {relative_path}: {e}"