Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 09:05:19 +08:00
commit 09fec2555b
96 changed files with 24269 additions and 0 deletions

View File

@@ -0,0 +1,420 @@
---
name: google-drive
description: Interact with Google Drive API using PyDrive2 for uploading, downloading, searching, and managing files. Use when working with Google Drive operations including file transfers, metadata queries, search operations, folder management, batch operations, and sharing. Authentication is pre-configured at ~/.gdrivelm/. Includes helper scripts for common operations and comprehensive API references. Helper script automatically detects markdown formatting and sets appropriate MIME types.
---
## Configuration
**Helper script path:**
```
/Users/wz/.claude/plugins/marketplaces/warren-claude-code-plugin-marketplace/claude-context-orchestrator/skills/google-drive/scripts/gdrive_helper.py
```
**Authentication files** (use `~/.gdrivelm/` - expands to home directory):
- Credentials: `~/.gdrivelm/credentials.json`
- Settings: `~/.gdrivelm/settings.yaml`
- Token: `~/.gdrivelm/token.json` (auto-generated)
Load `references/auth_setup.md` for detailed authentication configuration.
## Helper Script Usage
Use `scripts/gdrive_helper.py` for common operations to avoid rewriting authentication and upload/download code.
### Import and Use Functions
```python
import sys
sys.path.insert(0, '/Users/wz/.claude/plugins/marketplaces/warren-claude-code-plugin-marketplace/claude-context-orchestrator/skills/google-drive/scripts')
from gdrive_helper import authenticate, upload_file, download_file, search_files, get_metadata
# Authenticate once
drive = authenticate()
# Upload file
result = upload_file(drive, '/path/to/file.txt', title='My File')
print(f"Uploaded: {result['id']}")
# Search files
results = search_files(drive, "title contains 'report'")
for file in results:
print(f"{file['title']} - {file['id']}")
# Download file
download_file(drive, 'FILE_ID', '/path/to/save.txt')
# Get metadata
metadata = get_metadata(drive, 'FILE_ID')
print(f"Size: {metadata['size']} bytes")
```
### Available Helper Functions
- `authenticate()` - Authenticate and return Drive instance
- `upload_file(drive, local_path, title=None, folder_id=None)` - Upload local file
- `upload_string(drive, content, title, folder_id=None, use_markdown=None)` - Upload string content with auto-markdown detection
- `download_file(drive, file_id, local_path)` - Download file
- `get_file_content(drive, file_id)` - Get file content as string
- `get_metadata(drive, file_id)` - Get file metadata
- `search_files(drive, query, max_results=None)` - Search for files
- `delete_file(drive, file_id, permanent=False)` - Delete or trash file
- `create_folder(drive, folder_name, parent_id=None)` - Create folder
- `list_files_in_folder(drive, folder_id)` - List files in folder
### CLI Usage
The helper script can also be used from command line:
```bash
# Activate environment first
cd ~/Desktop/zPersonalProjects/gdrivelm
source venv/bin/activate
# Run commands
python ~/.claude/plugins/marketplaces/warren-claude-code-plugin-marketplace/claude-context-orchestrator/skills/google-drive/scripts/gdrive_helper.py upload /path/to/file.txt
python ~/.claude/plugins/marketplaces/warren-claude-code-plugin-marketplace/claude-context-orchestrator/skills/google-drive/scripts/gdrive_helper.py search "title contains 'report'"
python ~/.claude/plugins/marketplaces/warren-claude-code-plugin-marketplace/claude-context-orchestrator/skills/google-drive/scripts/gdrive_helper.py download FILE_ID /path/to/save.txt
```
## File Type Handling
Google Drive files require different retrieval methods depending on their type:
### Google Docs/Sheets/Slides (Native Google Formats)
Native Google formats require **export** with a specific MIME type, not direct download:
```python
import sys
sys.path.insert(0, '/Users/wz/.claude/plugins/marketplaces/warren-claude-code-plugin-marketplace/claude-context-orchestrator/skills/google-drive/scripts')
from gdrive_helper import authenticate
drive = authenticate()
# Extract file ID from URL
file_id = '1rX7KHFnHyoAu3KrIvQgv0gJdTvMztWJT-Pkx5x954vc'
# Create file object and fetch metadata
file = drive.CreateFile({'id': file_id})
file.FetchMetadata()
# Export with appropriate MIME type
content = file.GetContentString(mimetype='text/plain') # For Google Docs
# content = file.GetContentString(mimetype='text/csv') # For Google Sheets
# content = file.GetContentString(mimetype='text/plain') # For Google Slides
print(content)
```
### Regular Files (PDF, Images, Text, etc.)
Regular uploaded files can use direct download:
```python
from gdrive_helper import authenticate, get_file_content
drive = authenticate()
content = get_file_content(drive, 'FILE_ID')
```
### Usage Pattern: No Project Directory Required
**Important:** The helper scripts are globally available. You don't need to `cd` into a project directory:
```python
# ✅ CORRECT: Import from global skill path directly
import sys
sys.path.insert(0, '/Users/wz/.claude/plugins/marketplaces/warren-claude-code-plugin-marketplace/claude-context-orchestrator/skills/google-drive/scripts')
from gdrive_helper import authenticate
drive = authenticate()
# Continue with operations...
# ❌ INCORRECT: Don't try to cd into project directory
# cd ~/Desktop/zPersonalProjects/gdrivelm # This may not exist
```
## Common Operations
### Upload File from Local Path
```python
from gdrive_helper import authenticate, upload_file
drive = authenticate()
result = upload_file(drive, '/path/to/document.pdf', title='Important Document')
print(f"File ID: {result['id']}")
print(f"Link: {result['link']}")
```
### Upload String Content
The `upload_string` function automatically detects markdown formatting and sets the appropriate MIME type:
```python
from gdrive_helper import authenticate, upload_string
drive = authenticate()
# Auto-detects markdown based on content
markdown_content = """# My Document
This is a **markdown** formatted document with:
- Lists
- **Bold** text
- [Links](https://example.com)
"""
result = upload_string(drive, markdown_content, 'My Document')
print(f"Created: {result['title']}") # Will be 'My Document.md'
print(f"MIME Type: {result['mimeType']}") # Will be 'text/markdown'
# Force plain text (disable auto-detection)
plain_content = "This is plain text content"
result = upload_string(drive, plain_content, 'note.txt', use_markdown=False)
# Force markdown
result = upload_string(drive, "Simple text", 'doc', use_markdown=True) # Will be 'doc.md'
```
### Search Files
```python
from gdrive_helper import authenticate, search_files
drive = authenticate()
# Search by title
results = search_files(drive, "title contains 'invoice'")
# Search by type
results = search_files(drive, "mimeType = 'application/pdf'")
# Complex search
query = "title contains 'report' and mimeType = 'application/pdf' and trashed = false"
results = search_files(drive, query)
for file in results:
print(f"{file['title']} ({file['id']})")
```
For comprehensive search query syntax and examples, load `references/search_queries.md`.
### Download File
```python
from gdrive_helper import authenticate, download_file
drive = authenticate()
result = download_file(drive, 'FILE_ID_HERE', '/path/to/save/file.txt')
print(f"Downloaded {result['title']} to {result['local_path']}")
```
### Get File Metadata
```python
from gdrive_helper import authenticate, get_metadata
drive = authenticate()
metadata = get_metadata(drive, 'FILE_ID_HERE')
print(f"Title: {metadata['title']}")
print(f"Size: {metadata['size']} bytes")
print(f"Modified: {metadata['modified']}")
print(f"Link: {metadata['link']}")
```
### Create Folder and Upload to It
```python
from gdrive_helper import authenticate, create_folder, upload_file
drive = authenticate()
# Create folder
folder = create_folder(drive, 'My Documents')
print(f"Folder ID: {folder['id']}")
# Upload file to folder
result = upload_file(drive, '/path/to/file.txt', folder_id=folder['id'])
print(f"Uploaded to folder: {result['title']}")
```
## Advanced Usage
For direct PyDrive2 API usage and advanced features, load `references/api_reference.md`.
### Manual Authentication Pattern
If not using the helper script:
```python
from pydrive2.auth import GoogleAuth
from pydrive2.drive import GoogleDrive
gauth = GoogleAuth(settings_file='/Users/wz/.gdrivelm/settings.yaml')
gauth.LoadCredentialsFile('/Users/wz/.gdrivelm/token.json')
if gauth.credentials is None:
gauth.LocalWebserverAuth()
elif gauth.access_token_expired:
gauth.Refresh()
else:
gauth.Authorize()
gauth.SaveCredentialsFile('/Users/wz/.gdrivelm/token.json')
drive = GoogleDrive(gauth)
```
### Batch Operations
```python
from gdrive_helper import authenticate, upload_file
drive = authenticate()
files = [
'/path/to/file1.txt',
'/path/to/file2.pdf',
'/path/to/file3.docx'
]
for file_path in files:
result = upload_file(drive, file_path)
print(f"Uploaded: {result['title']} ({result['id']})")
```
## Search Query Patterns
Common search patterns (load `references/search_queries.md` for complete syntax):
- `title contains 'text'` - Files with title containing text
- `mimeType = 'application/pdf'` - PDF files only
- `'root' in parents` - Files in root directory
- `trashed = false` - Not in trash
- `'me' in owners` - Files you own
- `modifiedDate > '2024-01-01'` - Modified after date
- `fullText contains 'keyword'` - Search file content
Combine with `and`/`or`:
```python
query = "title contains 'report' and mimeType = 'application/pdf' and trashed = false"
```
## Bundled Resources
### Scripts
- `scripts/gdrive_helper.py` - Reusable Python functions for all common operations
### References
- `references/auth_setup.md` - Complete authentication configuration guide
- `references/search_queries.md` - Comprehensive search query syntax and examples
- `references/api_reference.md` - PyDrive2 API method reference with examples
### Assets
- `assets/settings_template.yaml` - Template PyDrive2 settings file
## Workflow Guidelines
1. **Always activate the virtual environment first** before running any Google Drive code
2. **Use the helper script** for common operations to avoid rewriting code
3. **Load reference files as needed** for detailed syntax and advanced features
4. **Test with small operations first** before batch processing
5. **Check file IDs** when downloading or modifying files
## Error Handling
```python
from pydrive2.files import ApiRequestError
from gdrive_helper import authenticate, get_metadata
drive = authenticate()
try:
metadata = get_metadata(drive, 'FILE_ID')
print(metadata['title'])
except ApiRequestError as e:
if e.error['code'] == 404:
print("File not found")
elif e.error['code'] == 403:
print("Permission denied")
else:
print(f"API Error: {e}")
except Exception as e:
print(f"Error: {e}")
```
## Common Pitfalls
### Pitfall 1: FileNotDownloadableError with Google Docs
**Error Message:**
```
pydrive2.files.FileNotDownloadableError: No downloadLink/exportLinks for mimetype found in metadata
```
**Cause:** Using `get_file_content()` or direct download methods on native Google formats (Docs, Sheets, Slides).
**Solution:** Use `GetContentString(mimetype='...')` to export the file:
```python
# ❌ WRONG: This fails for Google Docs
from gdrive_helper import get_file_content
content = get_file_content(drive, 'GOOGLE_DOC_ID')
# ✅ CORRECT: Export with MIME type
file = drive.CreateFile({'id': 'GOOGLE_DOC_ID'})
file.FetchMetadata()
content = file.GetContentString(mimetype='text/plain')
```
### Pitfall 2: Hardcoded Project Paths
**Error Message:**
```
cd: no such file or directory: /Users/wz/Desktop/zPersonalProjects/gdrivelm
```
**Cause:** Assuming the gdrivelm project directory exists in a specific location.
**Solution:** Import from the global skill path directly (no `cd` required):
```python
# ✅ CORRECT: Works from any directory
import sys
sys.path.insert(0, '/Users/wz/.claude/plugins/marketplaces/warren-claude-code-plugin-marketplace/claude-context-orchestrator/skills/google-drive/scripts')
from gdrive_helper import authenticate
# ❌ WRONG: Don't rely on project directory
# cd ~/Desktop/zPersonalProjects/gdrivelm && source venv/bin/activate
```
### Pitfall 3: Extracting File IDs from URLs
**Common patterns:**
```python
# From full URL
url = "https://docs.google.com/document/d/1rX7KHFnHyoAu3KrIvQgv0gJdTvMztWJT-Pkx5x954vc/edit"
file_id = url.split('/d/')[1].split('/')[0] # '1rX7KHFnHyoAu3KrIvQgv0gJdTvMztWJT-Pkx5x954vc'
# From sharing URL
url = "https://drive.google.com/file/d/1ABC123xyz/view"
file_id = url.split('/d/')[1].split('/')[0] # '1ABC123xyz'
```
## Performance Tips
1. Use helper script functions to minimize authentication overhead
2. Authenticate once and reuse the `drive` instance
3. Use specific search queries instead of listing all files
4. Batch operations when uploading/downloading multiple files
5. Cache file IDs for frequently accessed files
## Additional Documentation
For complete setup guide and test results, see:
- Project location: `~/Desktop/zPersonalProjects/gdrivelm/`
- README: `~/Desktop/zPersonalProjects/gdrivelm/README.md`
- Test script: `~/Desktop/zPersonalProjects/gdrivelm/test_gdrive.py`
- HTML documentation: `~/Desktop/zPersonalProjects/gdrivelm/claude_html/google_drive_api_setup.html`

View File

@@ -0,0 +1,16 @@
# Google Drive API Settings Template
# Copy this to ~/.gdrivelm/settings.yaml and update paths as needed
client_config_backend: file
client_config_file: ~/.gdrivelm/credentials.json
save_credentials: True
save_credentials_backend: file
save_credentials_file: ~/.gdrivelm/token.json
get_refresh_token: True
oauth_scope:
- https://www.googleapis.com/auth/drive
- https://www.googleapis.com/auth/drive.file
- https://www.googleapis.com/auth/drive.metadata.readonly

View File

@@ -0,0 +1,264 @@
# PyDrive2 API Quick Reference
## Core Methods
### Create File Object
```python
# New file
file = drive.CreateFile({'title': 'filename.txt'})
# Existing file by ID
file = drive.CreateFile({'id': 'FILE_ID_HERE'})
# File in specific folder
file = drive.CreateFile({
'title': 'filename.txt',
'parents': [{'id': 'FOLDER_ID'}]
})
```
### Upload Operations
```python
# Upload from local file
file.SetContentFile('/path/to/local/file.txt')
file.Upload()
# Upload from string
file.SetContentString('Hello, World!')
file.Upload()
# Upload from string with markdown MIME type
file = drive.CreateFile({
'title': 'document.md',
'mimeType': 'text/markdown'
})
file.SetContentString('# My Document\n\nMarkdown content here')
file.Upload()
# Upload with metadata update
file['title'] = 'New Title'
file.Upload()
```
### Download Operations
```python
# Download to local file
file.GetContentFile('/path/to/download/location.txt')
# Get content as string
content = file.GetContentString()
# Get content as file object
file_obj = file.GetContentFile() # Returns file object
```
### Metadata Operations
```python
# Fetch metadata
file.FetchMetadata()
# Access metadata fields
title = file['title']
mime_type = file['mimeType']
file_size = file['fileSize']
created = file['createdDate']
modified = file['modifiedDate']
web_link = file['alternateLink']
# Update metadata
file['title'] = 'New Title'
file['description'] = 'Updated description'
file.Upload() # Save changes
```
### List/Search Operations
```python
# List all files
file_list = drive.ListFile().GetList()
# List with query
file_list = drive.ListFile({'q': "title contains 'report'"}).GetList()
# List with pagination
file_list = drive.ListFile({
'q': "trashed = false",
'maxResults': 10
}).GetList()
# Iterate through results
for file in file_list:
print(f"{file['title']} - {file['id']}")
```
### Delete Operations
```python
# Move to trash
file.Trash()
# Permanently delete
file.Delete()
# Restore from trash
file.UnTrash()
```
### Permission Operations
```python
# Get permissions
permissions = file.GetPermissions()
# Share with specific user
permission = file.InsertPermission({
'type': 'user',
'value': 'user@example.com',
'role': 'reader'
})
# Share with anyone (public)
permission = file.InsertPermission({
'type': 'anyone',
'role': 'reader'
})
# Remove permission
file.DeletePermission(permission_id)
```
## Metadata Fields Reference
### Common File Fields
```python
file['id'] # File ID
file['title'] # File name
file['mimeType'] # MIME type
file['description'] # Description
file['createdDate'] # Creation timestamp
file['modifiedDate'] # Last modified timestamp
file['fileSize'] # Size in bytes
file['parents'] # Parent folder IDs
file['owners'] # Owner information
file['alternateLink'] # Web view link
file['downloadUrl'] # Direct download URL
file['thumbnailLink'] # Thumbnail URL
file['shared'] # Shared status (boolean)
file['trashed'] # Trash status (boolean)
```
## Export Google Docs
```python
# Export Google Doc as PDF
file = drive.CreateFile({'id': 'DOC_ID'})
file.GetContentFile('output.pdf', mimetype='application/pdf')
# Export Google Sheet as Excel
file.GetContentFile('output.xlsx',
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
# Export Google Slides as PowerPoint
file.GetContentFile('output.pptx',
mimetype='application/vnd.openxmlformats-officedocument.presentationml.presentation')
```
## Folder Operations
```python
# Create folder
folder = drive.CreateFile({
'title': 'My Folder',
'mimeType': 'application/vnd.google-apps.folder'
})
folder.Upload()
# List files in folder
file_list = drive.ListFile({
'q': f"'{folder['id']}' in parents and trashed = false"
}).GetList()
# Upload file to folder
file = drive.CreateFile({
'title': 'file.txt',
'parents': [{'id': folder['id']}]
})
file.SetContentString('Content')
file.Upload()
```
## Error Handling
```python
from pydrive2.files import ApiRequestError
try:
file = drive.CreateFile({'id': 'FILE_ID'})
file.FetchMetadata()
except ApiRequestError as e:
if e.error['code'] == 404:
print("File not found")
else:
print(f"Error: {e}")
```
## Batch Operations
```python
# Upload multiple files
files_to_upload = [
('file1.txt', '/path/to/file1.txt'),
('file2.txt', '/path/to/file2.txt'),
]
for title, path in files_to_upload:
file = drive.CreateFile({'title': title})
file.SetContentFile(path)
file.Upload()
print(f"Uploaded {title}: {file['id']}")
```
## Advanced Features
### Resumable Upload (for large files)
```python
# PyDrive2 handles resumable uploads automatically
# Just use normal upload for large files
file = drive.CreateFile({'title': 'large_file.zip'})
file.SetContentFile('/path/to/large_file.zip')
file.Upload() # Automatically uses resumable upload
```
### File Revisions
```python
# List revisions
revisions = file.GetRevisions()
for rev in revisions:
print(f"Revision ID: {rev['id']}")
print(f"Modified: {rev['modifiedDate']}")
```
### Copy File
```python
# Copy file
original = drive.CreateFile({'id': 'ORIGINAL_FILE_ID'})
copied = original.Copy()
copied['title'] = 'Copy of ' + original['title']
copied.Upload()
```
## Performance Tips
1. **Fetch only needed fields**: Use `fields` parameter
2. **Batch operations**: Group multiple API calls when possible
3. **Cache metadata**: Store frequently accessed metadata locally
4. **Use file IDs**: Faster than searching by title

View File

@@ -0,0 +1,84 @@
# Google Drive API Authentication Setup
## Quick Setup (Already Configured)
Authentication is already configured at:
- **Credentials**: `~/.gdrivelm/credentials.json`
- **Settings**: `~/.gdrivelm/settings.yaml`
- **Token**: `~/.gdrivelm/token.json` (auto-generated)
- **Virtual Env**: `~/Desktop/zPersonalProjects/gdrivelm/venv/` (update to your path)
## Authentication Code Pattern
```python
from pydrive2.auth import GoogleAuth
from pydrive2.drive import GoogleDrive
import os
def authenticate():
"""Authenticate with Google Drive API"""
settings_path = os.path.expanduser('~/.gdrivelm/settings.yaml')
token_path = os.path.expanduser('~/.gdrivelm/token.json')
gauth = GoogleAuth(settings_file=settings_path)
gauth.LoadCredentialsFile(token_path)
if gauth.credentials is None:
gauth.LocalWebserverAuth()
elif gauth.access_token_expired:
gauth.Refresh()
else:
gauth.Authorize()
gauth.SaveCredentialsFile(token_path)
return GoogleDrive(gauth)
```
## Settings Configuration
Located at `~/.gdrivelm/settings.yaml`:
```yaml
client_config_backend: file
client_config_file: /Users/wz/.gdrivelm/credentials.json
save_credentials: True
save_credentials_backend: file
save_credentials_file: /Users/wz/.gdrivelm/token.json
get_refresh_token: True
oauth_scope:
- https://www.googleapis.com/auth/drive
- https://www.googleapis.com/auth/drive.file
- https://www.googleapis.com/auth/drive.metadata.readonly
```
## OAuth Scopes
- `https://www.googleapis.com/auth/drive` - Full Drive access
- `https://www.googleapis.com/auth/drive.file` - Per-file access
- `https://www.googleapis.com/auth/drive.metadata.readonly` - Metadata reading
## First Run
On first use, the authentication will:
1. Open browser for OAuth consent
2. Save token to `~/.gdrivelm/token.json`
3. Auto-refresh on subsequent uses
## Python Environment
Always activate the virtual environment first:
```bash
cd ~/Desktop/zPersonalProjects/gdrivelm
source venv/bin/activate
```
## Installed Packages
- PyDrive2 v1.21.3
- google-api-python-client v2.187.0
- google-auth-oauthlib v1.2.3
- google-auth-httplib2 v0.2.1

View File

@@ -0,0 +1,135 @@
# Google Drive Search Query Syntax
## Query Operators
| Query | Description |
|-------|-------------|
| `title contains 'text'` | Files with title containing text |
| `fullText contains 'text'` | Search file content |
| `mimeType = 'type'` | Files of specific MIME type |
| `'parent_id' in parents` | Files in specific folder |
| `'root' in parents` | Files in root directory |
| `trashed = false` | Not in trash |
| `trashed = true` | In trash |
| `'me' in owners` | Files you own |
| `starred = true` | Starred files |
| `modifiedDate > 'date'` | Modified after date |
| `modifiedDate < 'date'` | Modified before date |
| `createdDate > 'date'` | Created after date |
## Common MIME Types
| Type | MIME Type |
|------|-----------|
| PDF | `application/pdf` |
| Text | `text/plain` |
| Word | `application/vnd.openxmlformats-officedocument.wordprocessingml.document` |
| Excel | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` |
| PowerPoint | `application/vnd.openxmlformats-officedocument.presentationml.presentation` |
| Google Doc | `application/vnd.google-apps.document` |
| Google Sheet | `application/vnd.google-apps.spreadsheet` |
| Google Slides | `application/vnd.google-apps.presentation` |
| Folder | `application/vnd.google-apps.folder` |
| Image | `image/jpeg`, `image/png`, `image/gif` |
## Search Examples
### Basic Searches
```python
# Files containing "report" in title
file_list = drive.ListFile({'q': "title contains 'report'"}).GetList()
# PDF files only
file_list = drive.ListFile({'q': "mimeType = 'application/pdf'"}).GetList()
# Files in root, not trashed
file_list = drive.ListFile({'q': "'root' in parents and trashed = false"}).GetList()
```
### Complex Queries
```python
# Multiple conditions with AND
query = (
"title contains 'invoice' and "
"mimeType = 'application/pdf' and "
"trashed = false"
)
file_list = drive.ListFile({'q': query}).GetList()
# Files you own
file_list = drive.ListFile({'q': "'me' in owners"}).GetList()
# Modified after specific date
file_list = drive.ListFile({'q': "modifiedDate > '2024-01-01'"}).GetList()
# Folders only
file_list = drive.ListFile({'q': "mimeType = 'application/vnd.google-apps.folder'"}).GetList()
# Starred PDFs
query = "starred = true and mimeType = 'application/pdf'"
file_list = drive.ListFile({'q': query}).GetList()
```
### Content Search
```python
# Search file content (not just title)
file_list = drive.ListFile({'q': "fullText contains 'keyword'"}).GetList()
```
## Date Format
Use ISO 8601 format: `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM:SS`
Examples:
- `'2024-01-01'`
- `'2024-12-31T23:59:59'`
## Combining Conditions
Use `and` and `or` operators:
```python
# Either condition
query = "title contains 'report' or title contains 'summary'"
# Both conditions
query = "title contains 'report' and mimeType = 'application/pdf'"
# Complex combination
query = (
"(title contains 'invoice' or title contains 'receipt') and "
"mimeType = 'application/pdf' and "
"modifiedDate > '2024-01-01'"
)
```
## Special Characters
Escape single quotes in search terms:
```python
# Searching for "O'Brien"
query = "title contains 'O\\'Brien'"
```
## Performance Tips
1. **Be specific**: More specific queries return faster
2. **Limit fields**: Use `fields` parameter to request only needed data
3. **Use pagination**: For large result sets, use `pageToken`
4. **Avoid fullText searches**: They are slower than title searches
## Iterating Results
```python
# Process all results
file_list = drive.ListFile({'q': "title contains 'report'"}).GetList()
for file in file_list:
print(f"{file['title']} (ID: {file['id']})")
print(f" Modified: {file['modifiedDate']}")
print(f" Size: {file.get('fileSize', 'N/A')} bytes")
```

View File

@@ -0,0 +1,345 @@
#!/usr/bin/env python3
"""
Google Drive Helper Script
Provides reusable functions for common Google Drive operations.
"""
from pydrive2.auth import GoogleAuth
from pydrive2.drive import GoogleDrive
import os
import sys
def authenticate():
"""
Authenticate with Google Drive API.
Returns:
GoogleDrive: Authenticated Drive instance
"""
settings_path = os.path.expanduser('~/.gdrivelm/settings.yaml')
token_path = os.path.expanduser('~/.gdrivelm/token.json')
gauth = GoogleAuth(settings_file=settings_path)
gauth.LoadCredentialsFile(token_path)
if gauth.credentials is None:
gauth.LocalWebserverAuth()
elif gauth.access_token_expired:
gauth.Refresh()
else:
gauth.Authorize()
gauth.SaveCredentialsFile(token_path)
return GoogleDrive(gauth)
def upload_file(drive, local_path, title=None, folder_id=None):
"""
Upload a file to Google Drive.
Args:
drive: Authenticated GoogleDrive instance
local_path: Path to local file
title: Optional custom title (defaults to filename)
folder_id: Optional parent folder ID
Returns:
dict: File metadata including ID
"""
if not os.path.exists(local_path):
raise FileNotFoundError(f"File not found: {local_path}")
metadata = {'title': title or os.path.basename(local_path)}
if folder_id:
metadata['parents'] = [{'id': folder_id}]
file = drive.CreateFile(metadata)
file.SetContentFile(local_path)
file.Upload()
return {
'id': file['id'],
'title': file['title'],
'link': file.get('alternateLink', 'N/A')
}
def upload_string(drive, content, title, folder_id=None, use_markdown=None):
"""
Upload string content as a file to Google Drive.
Args:
drive: Authenticated GoogleDrive instance
content: String content to upload
title: File title
folder_id: Optional parent folder ID
use_markdown: If True, upload as markdown. If None, auto-detect based on content/title.
If False, upload as plain text.
Returns:
dict: File metadata including ID
"""
metadata = {'title': title}
if folder_id:
metadata['parents'] = [{'id': folder_id}]
# Auto-detect markdown if not specified
if use_markdown is None:
# Check if title ends with .md or if content has markdown formatting
is_md_file = title.lower().endswith('.md')
has_md_formatting = any([
content.startswith('#'), # Headers
'\n#' in content, # Headers in content
'**' in content, # Bold
'__' in content, # Bold/italic
'- ' in content, # Lists
'* ' in content, # Lists
'```' in content, # Code blocks
'[' in content and '](' in content, # Links
])
use_markdown = is_md_file or has_md_formatting
# Set MIME type based on markdown detection
if use_markdown:
metadata['mimeType'] = 'text/markdown'
# Ensure title has .md extension if it's markdown
if not title.lower().endswith('.md'):
metadata['title'] = title + '.md'
file = drive.CreateFile(metadata)
file.SetContentString(content)
file.Upload()
return {
'id': file['id'],
'title': file['title'],
'link': file.get('alternateLink', 'N/A'),
'mimeType': file.get('mimeType', 'text/plain')
}
def download_file(drive, file_id, local_path):
"""
Download a file from Google Drive.
Args:
drive: Authenticated GoogleDrive instance
file_id: Google Drive file ID
local_path: Path to save downloaded file
Returns:
dict: File metadata
"""
file = drive.CreateFile({'id': file_id})
file.FetchMetadata()
file.GetContentFile(local_path)
return {
'id': file['id'],
'title': file['title'],
'size': file.get('fileSize', 'N/A'),
'local_path': local_path
}
def get_file_content(drive, file_id):
"""
Get file content as string.
Args:
drive: Authenticated GoogleDrive instance
file_id: Google Drive file ID
Returns:
str: File content
"""
file = drive.CreateFile({'id': file_id})
return file.GetContentString()
def get_metadata(drive, file_id):
"""
Get file metadata.
Args:
drive: Authenticated GoogleDrive instance
file_id: Google Drive file ID
Returns:
dict: File metadata
"""
file = drive.CreateFile({'id': file_id})
file.FetchMetadata()
return {
'id': file['id'],
'title': file['title'],
'mimeType': file['mimeType'],
'size': file.get('fileSize', 'N/A'),
'created': file['createdDate'],
'modified': file['modifiedDate'],
'link': file.get('alternateLink', 'N/A'),
'trashed': file.get('trashed', False)
}
def search_files(drive, query, max_results=None):
"""
Search for files in Google Drive.
Args:
drive: Authenticated GoogleDrive instance
query: Search query string
max_results: Optional limit on results
Returns:
list: List of file metadata dicts
"""
params = {'q': query}
if max_results:
params['maxResults'] = max_results
file_list = drive.ListFile(params).GetList()
results = []
for file in file_list:
results.append({
'id': file['id'],
'title': file['title'],
'mimeType': file.get('mimeType', 'N/A'),
'modified': file.get('modifiedDate', 'N/A')
})
return results
def delete_file(drive, file_id, permanent=False):
"""
Delete a file from Google Drive.
Args:
drive: Authenticated GoogleDrive instance
file_id: Google Drive file ID
permanent: If True, permanently delete; if False, move to trash
Returns:
bool: Success status
"""
file = drive.CreateFile({'id': file_id})
if permanent:
file.Delete()
else:
file.Trash()
return True
def create_folder(drive, folder_name, parent_id=None):
"""
Create a folder in Google Drive.
Args:
drive: Authenticated GoogleDrive instance
folder_name: Name for the new folder
parent_id: Optional parent folder ID
Returns:
dict: Folder metadata including ID
"""
metadata = {
'title': folder_name,
'mimeType': 'application/vnd.google-apps.folder'
}
if parent_id:
metadata['parents'] = [{'id': parent_id}]
folder = drive.CreateFile(metadata)
folder.Upload()
return {
'id': folder['id'],
'title': folder['title'],
'link': folder.get('alternateLink', 'N/A')
}
def list_files_in_folder(drive, folder_id):
"""
List all files in a specific folder.
Args:
drive: Authenticated GoogleDrive instance
folder_id: Google Drive folder ID
Returns:
list: List of file metadata dicts
"""
query = f"'{folder_id}' in parents and trashed = false"
return search_files(drive, query)
def main():
"""CLI interface for testing"""
if len(sys.argv) < 2:
print("Usage: gdrive_helper.py <command> [args...]")
print("\nCommands:")
print(" upload <local_path> [title]")
print(" download <file_id> <local_path>")
print(" search <query>")
print(" metadata <file_id>")
print(" delete <file_id>")
print(" create-folder <name>")
sys.exit(1)
command = sys.argv[1]
drive = authenticate()
if command == 'upload':
local_path = sys.argv[2]
title = sys.argv[3] if len(sys.argv) > 3 else None
result = upload_file(drive, local_path, title)
print(f"Uploaded: {result['title']}")
print(f"ID: {result['id']}")
print(f"Link: {result['link']}")
elif command == 'download':
file_id = sys.argv[2]
local_path = sys.argv[3]
result = download_file(drive, file_id, local_path)
print(f"Downloaded: {result['title']}")
print(f"Saved to: {result['local_path']}")
elif command == 'search':
query = sys.argv[2]
results = search_files(drive, query)
print(f"Found {len(results)} results:")
for r in results:
print(f" [{r['id']}] {r['title']}")
elif command == 'metadata':
file_id = sys.argv[2]
metadata = get_metadata(drive, file_id)
for key, value in metadata.items():
print(f"{key}: {value}")
elif command == 'delete':
file_id = sys.argv[2]
delete_file(drive, file_id)
print(f"Deleted file: {file_id}")
elif command == 'create-folder':
name = sys.argv[2]
result = create_folder(drive, name)
print(f"Created folder: {result['title']}")
print(f"ID: {result['id']}")
else:
print(f"Unknown command: {command}")
sys.exit(1)
if __name__ == '__main__':
main()