[![Python](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![Downloads](https://static.pepy.tech/badge/pydrive4)](https://pepy.tech/project/pydrive4)
[![Downloads](https://static.pepy.tech/badge/pydrive4/month)](https://pepy.tech/project/pydrive4)
[![Downloads](https://static.pepy.tech/badge/pydrive4/week)](https://pepy.tech/project/pydrive4)

# PyDrive4

**PyDrive4 is a wrapper library of google-api-python-client that simplifies many common Google Drive API V3 tasks.**

A modern, actively maintained Python library for Google Drive. Inspired by [PyDrive](https://pypi.python.org/pypi/PyDrive) and [PyDrive2](https://github.com/iterative/PyDrive2), rebuilt from scratch for Google Drive API V3 with simplified authentication and cleaner API.

PyDrive4 makes it easy to interact with Google Drive from Python. It wraps the `google-api-python-client` to provide a clean, intuitive API for common file and folder operations.

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Authentication Methods](#authentication-methods)
  - [Method 1: Application Default Credentials](#method-1-application-default-credentials-recommended)
  - [Method 2: Service Account](#method-2-service-account-for-automation)
  - [Method 3: OAuth2 Client](#method-3-oauth2-client-credentials-recommended-for-personal-use)
- [API Reference](#api-reference)
- [Usage Examples](#usage-examples)
- [Advanced: GoogleAuth](#advanced-googleauth)
- [Error Handling](#error-handling)
- [Requirements](#requirements)

## Features

- 🔐 **Multiple Auth Methods** - ADC, OAuth2, Service Account with auto-detection
- 📁 **File Management** - Upload, download, list, search files
- 📂 **Folder Operations** - Create, list, search folders
- 🔄 **Folder Upload** - Upload entire directories recursively
- 🗑️ **Delete Operations** - Trash or permanently delete items
- 📄 **Google Drive API v3** - Uses the latest API version
- ✨ **Clean Error Messages** - User-friendly one-line errors

## Installation

```bash
pip install pydrive4
```

Or with UV (recommended):

```bash
uv add pydrive4
```

---

## Quick Start

```python
from pydrive4 import GoogleDrive

# Auto-authenticate (uses best available method)
client = GoogleDrive()

# List files
files = client.list_files()
print(f"Found {files['count']} files")
```

---

## Authentication Methods

PyDrive4 supports multiple authentication methods, ordered by recommendation:

| Method                                 | Best For                       | Setup Required      |
| -------------------------------------- | ------------------------------ | ------------------- |
| **1. Application Default Credentials** | Local dev, Google Cloud        | `gcloud` CLI        |
| **2. Service Account**                 | Servers, automation, bots      | JSON key file       |
| **3. OAuth2 Client**                   | Personal scripts, desktop apps | JSON + browser auth |

---

### Method 1: Application Default Credentials (Recommended)

The **easiest** method for local development. No JSON files to manage!

#### Setup (One-time)

1. Install [Google Cloud SDK](https://cloud.google.com/sdk/docs/install)

2. Login with your Google account:

```bash
gcloud auth application-default login
```

3. That's it! Now just use:

```python
from pydrive4 import GoogleDrive

client = GoogleDrive()  # Works automatically!
files = client.list_files()
```

#### How it Works

ADC (Application Default Credentials) checks these locations in order:

1. `GOOGLE_APPLICATION_CREDENTIALS` environment variable
2. `gcloud auth application-default login` credentials
3. Google Cloud metadata service (on GCE, Cloud Run, etc.)

---

### Method 2: Service Account (For Automation)

Best for servers, bots, and CI/CD pipelines. **No user interaction needed.**

#### Step-by-Step Setup

**Step 1: Create a Google Cloud Project**

1. Go to [Google Cloud Console](https://console.cloud.google.com/)
2. Click the project dropdown at the top → **New Project**
3. Enter a project name (e.g., "My Drive App") → **Create**
4. Wait for the project to be created, then select it

**Step 2: Enable Google Drive API**

1. Go to **APIs & Services** → **Library** (or [click here](https://console.cloud.google.com/apis/library))
2. Search for "**Google Drive API**"
3. Click on it → Click **Enable**

**Step 3: Create Service Account**

1. Go to **IAM & Admin** → **Service Accounts** (or [click here](https://console.cloud.google.com/iam-admin/serviceaccounts))
2. Click **+ Create Service Account**
3. Enter a name (e.g., "drive-bot") → Click **Create and Continue**
4. Skip the optional steps → Click **Done**

**Step 4: Create JSON Key**

1. Click on the service account you just created
2. Go to the **Keys** tab
3. Click **Add Key** → **Create new key**
4. Select **JSON** → Click **Create**
5. A JSON file downloads automatically
6. Rename it to `service_account.json` and move to your project folder

#### Troubleshooting: Organization Policy Error

If you see this error:

```
An organisation policy that blocks service accounts key creation has been enforced
Policy: iam.disableServiceAccountKeyCreation
```

**This means your organization has disabled service account key creation for security.**

**Solutions:**

1. **Use OAuth2 instead** (Method 3 below) - Recommended for personal use
2. **Use Application Default Credentials** (Method 1) - Run `gcloud auth application-default login`
3. **Contact your admin** to request an exception for the `iam.disableServiceAccountKeyCreation` policy

#### Usage

```python
from pydrive4 import GoogleDrive

# Auto-detect (if file is named service_account.json in current dir)
drive = GoogleDrive()

# Or explicit
drive = GoogleDrive(
    credentials_name="service_account.json",
    service_account=True
)
```

#### What is `service_account=True`?

| Aspect         | OAuth2              | Service Account             |
| -------------- | ------------------- | --------------------------- |
| Browser needed | Yes (first time)    | No                          |
| Whose Drive?   | Your personal Drive | Service account's own Drive |
| Best for       | Personal scripts    | Servers, automation         |
| Access         | All your files      | Only files shared with it   |

> **Important**: Service accounts have their own empty Drive. To access your files, share folders with the service account email (e.g., `my-bot@my-project.iam.gserviceaccount.com`).

---

### Method 3: OAuth2 Client Credentials (Recommended for Personal Use)

For desktop apps where you want to access **your own Google Drive files**. Opens browser once for authorization.

#### Step-by-Step Setup

**Step 1: Create a Google Cloud Project**

1. Go to [Google Cloud Console](https://console.cloud.google.com/)
2. Click the project dropdown at the top → **New Project**
3. Enter a project name → **Create**
4. Wait for the project to be created, then select it

**Step 2: Enable Google Drive API**

1. Go to **APIs & Services** → **Library**
2. Search for "**Google Drive API**"
3. Click on it → Click **Enable**

**Step 3: Configure OAuth Consent Screen**

1. Go to **APIs & Services** → **OAuth consent screen**
2. Select **External** (or Internal if using Google Workspace) → **Create**
3. Fill in required fields:
   - App name: Your app name
   - User support email: Your email
   - Developer contact: Your email
4. Click **Save and Continue**
5. On Scopes page → Click **Save and Continue**
6. On Test users page → Click **Add Users** → Add your email → **Save and Continue**
7. Click **Back to Dashboard**

**Step 4: Create OAuth Client ID**

1. Go to **APIs & Services** → **Credentials**
2. Click **+ Create Credentials** → **OAuth client ID**
3. Application type: **Desktop app**
4. Name: Any name (e.g., "PyDrive4")
5. Click **Create**
6. Click **Download JSON** (or the download icon)
7. Rename to `client_secrets.json` and move to your project folder

#### Usage

```python
from pydrive4 import GoogleDrive

# Auto-detect (if file is in current directory)
drive = GoogleDrive()

# Or explicit
drive = GoogleDrive(credentials_name="client_secrets.json")
```

**First run**: A browser window opens asking you to authorize the app. After authorizing, tokens are saved to `token.json` for future use (no browser needed again).

---

### Environment Variable

You can also set `GOOGLE_APPLICATION_CREDENTIALS`:

```bash
# Linux/macOS
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/credentials.json"

# Windows PowerShell
$env:GOOGLE_APPLICATION_CREDENTIALS="C:\path\to\credentials.json"

# Windows CMD
set GOOGLE_APPLICATION_CREDENTIALS=C:\path\to\credentials.json
```

Then:

```python
client = GoogleDrive()  # Automatically uses the env variable
```

---

### Auto-Detection Priority

When you call `GoogleDrive()` without arguments, it tries:

1. **Application Default Credentials** (gcloud login or env variable)
2. **Service account files** in current directory:
   - `service_account.json`
   - `service_account_key.json`
3. **OAuth2 files** in current directory:
   - `client_secrets.json`
   - `credentials.json`

---

## API Reference

### GoogleDrive

```python
GoogleDrive(
    credentials_name: str = None,      # Path to credentials JSON
    service_account: bool = None,      # True/False/None (auto-detect)
    token_file: str = "token.json",    # Where to cache OAuth tokens
    readonly: bool = False             # Request read-only access
)
```

### Methods

| Method                                    | Description               | Returns                                            |
| ----------------------------------------- | ------------------------- | -------------------------------------------------- |
| `list_files(folder_id, trashed)`          | List files in a folder    | `{"success": bool, "files": list, "count": int}`   |
| `list_folders(parent_id, trashed)`        | List folders              | `{"success": bool, "folders": list, "count": int}` |
| `search_files(query, folder_id)`          | Search files by name      | `{"success": bool, "files": list, "count": int}`   |
| `search_folders(query, parent_id)`        | Search folders by name    | `{"success": bool, "folders": list, "count": int}` |
| `get_folder(name, parent_id)`             | Get folder by exact name  | `{"success": bool, "folder": dict, "found": bool}` |
| `create_folder(name, parent_id)`          | Create a folder           | `{"success": bool, "folder": dict, "id": str}`     |
| `upload_file(path, folder_id, overwrite)` | Upload a file             | `{"success": bool, "file": dict, "id": str}`       |
| `download_file(file_id, output_path)`     | Download a file           | `{"success": bool, "path": str, "size": int}`      |
| `upload_folder(path, parent_id)`          | Upload folder recursively | `{"success": bool, "files_uploaded": int}`         |
| `delete_file(file_id, permanently)`       | Delete/trash a file       | `{"success": bool}`                                |
| `delete_folder(folder_id, permanently)`   | Delete/trash a folder     | `{"success": bool}`                                |
| `delete_item(item_id, permanently)`       | Delete/trash any item     | `{"success": bool}`                                |

---

## Usage Examples

### List Files and Folders

```python
from pydrive4 import GoogleDrive

client = GoogleDrive()

# List files in root
files = client.list_files()
for f in files["files"]:
    print(f"📄 {f['name']}")

# List folders
folders = client.list_folders()

# List in specific folder
files = client.list_files(folder_id="folder_id_here")
```

### Search

```python
# Search files by name
results = client.search_files("report")
print(f"Found {results['count']} files")

# Search folders
folders = client.search_folders("Projects")
```

### Create Folders

```python
# Create in root
folder = client.create_folder("My Folder")
print(f"Created: {folder['id']}")

# Create nested
parent = client.create_folder("Parent")
child = client.create_folder("Child", parent_id=parent["id"])
```

### Upload Files

```python
# Upload to root
result = client.upload_file("document.pdf")

# Upload to folder
result = client.upload_file("report.pdf", folder_id="folder_id")

# Overwrite existing
result = client.upload_file("report.pdf", folder_id="folder_id", overwrite=True)
```

### Download Files

```python
# Download with custom path
result = client.download_file("file_id", "local.pdf")

# Download with original name
result = client.download_file("file_id")
```

### Upload Folder

```python
result = client.upload_folder("./my_project")
print(f"Uploaded {result['files_uploaded']} files")
```

### Delete

```python
# Delete a file (move to trash)
drive.delete_file("file_id")

# Delete a file permanently
drive.delete_file("file_id", permanently=True)

# Delete a folder (move to trash)
drive.delete_folder("folder_id")

# Delete a folder permanently (deletes all contents!)
drive.delete_folder("folder_id", permanently=True)
```

---

## Advanced: GoogleAuth

### Do I Need GoogleAuth?

**Most users don't need it!** `GoogleDrive()` auto-authenticates for you.

| Approach                                         | When to use                                           |
| ------------------------------------------------ | ----------------------------------------------------- |
| `drive = GoogleDrive()`                          | ✅ **Recommended** - handles everything automatically |
| `auth = GoogleAuth()` + `GoogleDrive(auth=auth)` | Only for advanced control                             |

### When to Use GoogleAuth Separately

Use `GoogleAuth` directly when you need to:

1. **Check authentication status before operations**
2. **Switch between multiple accounts**
3. **Revoke tokens programmatically**
4. **Get the raw Drive API service**

### GoogleAuth Examples

```python
from pydrive4 import GoogleAuth, GoogleDrive

# Create auth with specific credentials
auth = GoogleAuth(credentials_file="client_secrets.json")
auth.authenticate()

# Check status
print(f"Authenticated: {auth.is_authenticated}")
print(f"Using ADC: {auth.is_adc}")
print(f"Using Service Account: {auth.is_service_account}")

# Pass to drive client
drive = GoogleDrive(auth=auth)
```

### Revoke Tokens

```python
auth = GoogleAuth()
auth.authenticate()

# Later, revoke access and delete cached token
auth.revoke()
```

### Get Raw Drive API Service

For direct API access without the PyDrive4 wrapper:

```python
from pydrive4 import GoogleAuth

auth = GoogleAuth()
auth.authenticate()

# Get the raw googleapiclient service
service = auth.get_drive_service()

# Use raw API
results = service.files().list(pageSize=10).execute()
```

---

## Error Handling

All methods return a dict with `success` key:

```python
result = client.upload_file("document.pdf")

if result["success"]:
    print(f"Uploaded: {result['id']}")
else:
    print(f"Error: {result['error']}")
```

Clean error messages:

```
❌ InvalidCredentialsError: No credentials available. Options:
  1. Run: gcloud auth application-default login
  2. Place 'client_secrets.json' in current directory
  3. Set GOOGLE_APPLICATION_CREDENTIALS environment variable
```

---

## Requirements

- Python 3.10+
- google-api-python-client >= 2.100.0
- google-auth >= 2.15.0
- google-auth-oauthlib >= 1.0.0
- google-auth-httplib2 >= 0.2.0

---

## License

MIT License

---

## Contributing

Contributions are welcome! Please submit a Pull Request.

---

## Acknowledgments

Inspired by [PyDrive2](https://github.com/iterative/PyDrive2), updated for Google Drive API v3.

<p align="center">
  <a href="https://www.star-history.com/#SSujitX/PyDrive4&Date">
    <img src="https://api.star-history.com/svg?repos=SSujitX/PyDrive4&type=Date" width="500" alt="Star History">
  </a>
</p>

<p align="center">
  <a href="https://visitorbadge.io/status?path=https%3A%2F%2Fgithub.com%2FSSujitX%2FPyDrive4"><img src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Fgithub.com%2FSSujitX%2FPyDrive4&countColor=%23263759" /></a>
</p>
