> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/whiskeysockets/Baileys/llms.txt
> Use this file to discover all available pages before exploring further.

# Downloading Media

> Download and process media from received WhatsApp messages

## Overview

Baileys provides utilities to download media (images, videos, audio, documents) from received messages. Media is stored encrypted on WhatsApp's servers and must be downloaded and decrypted.

## Basic Media Download

Download media from a message:

```typescript theme={null}
import { downloadMediaMessage } from '@whiskeysockets/baileys'
import { createWriteStream } from 'fs'

sock.ev.on('messages.upsert', async ({ messages }) => {
  for (const m of messages) {
    if (!m.message) continue
    
    // Download as buffer
    const buffer = await downloadMediaMessage(
      m,
      'buffer',
      { },
      {
        logger,
        reuploadRequest: sock.updateMediaMessage
      }
    )
    
    // Save to file
    await fs.writeFile('./downloaded-media.jpg', buffer)
  }
})
```

## Download as Stream

For large files, use stream to avoid loading everything into memory:

```typescript theme={null}
import { downloadMediaMessage } from '@whiskeysockets/baileys'
import { createWriteStream } from 'fs'

sock.ev.on('messages.upsert', async ({ messages }) => {
  for (const m of messages) {
    if (!m.message) continue
    
    // Download as stream
    const stream = await downloadMediaMessage(
      m,
      'stream',
      { },
      {
        logger,
        reuploadRequest: sock.updateMediaMessage
      }
    )
    
    // Pipe to file
    const writeStream = createWriteStream('./my-download.jpeg')
    stream.pipe(writeStream)
  }
})
```

## Check Message Type

Before downloading, check if the message contains media:

```typescript theme={null}
import { getContentType } from '@whiskeysockets/baileys'

sock.ev.on('messages.upsert', async ({ messages }) => {
  for (const m of messages) {
    if (!m.message) continue
    
    const messageType = getContentType(m.message)
    
    if (messageType === 'imageMessage') {
      // Download image
      const buffer = await downloadMediaMessage(m, 'buffer', {}, ctx)
      console.log('Downloaded image, size:', buffer.length)
    }
    else if (messageType === 'videoMessage') {
      // Download video
      const buffer = await downloadMediaMessage(m, 'buffer', {}, ctx)
      console.log('Downloaded video, size:', buffer.length)
    }
    else if (messageType === 'audioMessage') {
      // Download audio
      const buffer = await downloadMediaMessage(m, 'buffer', {}, ctx)
      console.log('Downloaded audio, size:', buffer.length)
    }
    else if (messageType === 'documentMessage') {
      // Download document
      const buffer = await downloadMediaMessage(m, 'buffer', {}, ctx)
      console.log('Downloaded document, size:', buffer.length)
    }
  }
})
```

## Download Options

Control the download behavior:

```typescript theme={null}
type MediaDownloadOptions = {
  // Start downloading from a specific byte
  startByte?: number
  
  // End downloading at a specific byte
  endByte?: number
  
  // Custom fetch options (headers, etc.)
  options?: RequestInit
}

// Download only part of a file
const stream = await downloadMediaMessage(
  message,
  'stream',
  {
    startByte: 0,
    endByte: 1024 * 100 // First 100KB
  },
  ctx
)
```

## Media Reupload

WhatsApp automatically removes old media from servers. If a download fails, request a reupload:

```typescript theme={null}
const ctx = {
  logger,
  reuploadRequest: sock.updateMediaMessage
}

try {
  const buffer = await downloadMediaMessage(
    message,
    'buffer',
    {},
    ctx // Context with reupload function
  )
} catch (error) {
  if (error.status === 404 || error.status === 410) {
    console.log('Media expired, reupload was requested automatically')
  }
}
```

### Manual Reupload Request

```typescript theme={null}
try {
  const buffer = await downloadMediaMessage(message, 'buffer', {}, ctx)
} catch (error) {
  if (error.status === 404) {
    // Request reupload manually
    await sock.updateMediaMessage(message)
    
    // Try download again
    const buffer = await downloadMediaMessage(message, 'buffer', {}, ctx)
  }
}
```

## Download from Content

Directly download from message content:

```typescript theme={null}
import { downloadContentFromMessage } from '@whiskeysockets/baileys'

const message = m.message?.imageMessage

if (message) {
  const stream = await downloadContentFromMessage(
    message,
    'image'
  )
  
  // Convert to buffer
  const chunks: Buffer[] = []
  for await (const chunk of stream) {
    chunks.push(chunk)
  }
  const buffer = Buffer.concat(chunks)
}
```

## Media Types

Supported media types for download:

```typescript theme={null}
type MediaType = 
  | 'image'
  | 'video' 
  | 'audio'
  | 'document'
  | 'sticker'
  | 'thumbnail-link'
```

## Get File Extension

Determine the correct file extension:

```typescript theme={null}
import { extensionForMediaMessage } from '@whiskeysockets/baileys'

const extension = extensionForMediaMessage(m.message!)
const filename = `downloaded-${Date.now()}.${extension}`

await fs.writeFile(filename, buffer)
```

## Complete Download Example

Full example with type checking, error handling, and file saving:

```typescript theme={null}
import { 
  downloadMediaMessage,
  getContentType,
  extensionForMediaMessage 
} from '@whiskeysockets/baileys'
import { writeFile } from 'fs/promises'

sock.ev.on('messages.upsert', async ({ messages }) => {
  for (const m of messages) {
    if (!m.message) continue
    
    const messageType = getContentType(m.message)
    
    // Check if it's a media message
    const mediaTypes = ['imageMessage', 'videoMessage', 'audioMessage', 'documentMessage', 'stickerMessage']
    
    if (messageType && mediaTypes.includes(messageType)) {
      try {
        console.log('Downloading media...')
        
        const buffer = await downloadMediaMessage(
          m,
          'buffer',
          {},
          {
            logger,
            reuploadRequest: sock.updateMediaMessage
          }
        )
        
        // Get appropriate file extension
        const extension = extensionForMediaMessage(m.message)
        const filename = `media-${Date.now()}.${extension}`
        
        // Save to file
        await writeFile(filename, buffer)
        
        console.log(`Saved media to ${filename}, size: ${buffer.length} bytes`)
        
      } catch (error) {
        console.error('Failed to download media:', error)
      }
    }
  }
})
```

## Stream Processing Example

Process large media files without loading into memory:

```typescript theme={null}
import { createWriteStream } from 'fs'
import { pipeline } from 'stream/promises'

sock.ev.on('messages.upsert', async ({ messages }) => {
  for (const m of messages) {
    if (!m.message?.videoMessage) continue
    
    const stream = await downloadMediaMessage(
      m,
      'stream',
      {},
      { logger, reuploadRequest: sock.updateMediaMessage }
    )
    
    const filename = `video-${Date.now()}.mp4`
    const writeStream = createWriteStream(filename)
    
    await pipeline(stream, writeStream)
    
    console.log(`Video saved to ${filename}`)
  }
})
```

## Thumbnail Download

Some messages have thumbnails that can be extracted:

```typescript theme={null}
const message = m.message?.imageMessage

if (message?.jpegThumbnail) {
  // Thumbnail is already in the message
  const thumbnail = Buffer.from(message.jpegThumbnail)
  await writeFile('thumbnail.jpg', thumbnail)
}
```

## Media Message Properties

Useful properties available on media messages:

```typescript theme={null}
const imageMsg = m.message?.imageMessage

if (imageMsg) {
  console.log('MIME type:', imageMsg.mimetype)
  console.log('File size:', imageMsg.fileLength)
  console.log('Width:', imageMsg.width)
  console.log('Height:', imageMsg.height)
  console.log('Caption:', imageMsg.caption)
  console.log('Media key:', imageMsg.mediaKey)
  console.log('Direct path:', imageMsg.directPath)
  console.log('URL:', imageMsg.url)
  console.log('SHA256:', imageMsg.fileSha256)
}
```

## Direct Download from URL

If you have the direct path and media key:

```typescript theme={null}
import { 
  downloadContentFromMessage,
  getMediaKeys 
} from '@whiskeysockets/baileys'

const { directPath, mediaKey } = message.imageMessage!

const stream = await downloadContentFromMessage(
  { directPath, mediaKey },
  'image'
)

const chunks: Buffer[] = []
for await (const chunk of stream) {
  chunks.push(chunk)
}
const buffer = Buffer.concat(chunks)
```

## Error Handling

```typescript theme={null}
async function downloadMedia(message: WAMessage) {
  try {
    const buffer = await downloadMediaMessage(
      message,
      'buffer',
      {},
      { logger, reuploadRequest: sock.updateMediaMessage }
    )
    return buffer
  } catch (error: any) {
    if (error.status === 404) {
      console.error('Media not found (may have expired)')
    } else if (error.status === 410) {
      console.error('Media gone (expired and cannot be reuploaded)')
    } else if (error.message?.includes('Invalid media message')) {
      console.error('Not a media message')
    } else {
      console.error('Download failed:', error.message)
    }
    throw error
  }
}
```

## Best Practices

1. **Use Streams**: For files larger than 10MB, use stream mode to avoid memory issues
2. **Type Checking**: Always check message type before downloading
3. **Error Handling**: Implement retry logic for failed downloads
4. **Reupload Support**: Always provide `reuploadRequest` function
5. **File Extensions**: Use `extensionForMediaMessage()` for correct extensions
6. **Cleanup**: Remove downloaded files when no longer needed

## Performance Tips

```typescript theme={null}
// ❌ Bad - loads entire file into memory
const buffer = await downloadMediaMessage(message, 'buffer', {}, ctx)
processLargeVideo(buffer)

// ✅ Good - streams the file
const stream = await downloadMediaMessage(message, 'stream', {}, ctx)
processLargeVideoStream(stream)
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Media Messages" icon="image" href="/messaging/media-messages">
    Learn how to send media messages
  </Card>

  <Card title="Handling Events" icon="bell" href="/guides/handling-events">
    Handle incoming messages
  </Card>
</CardGroup>
