Initial commit
This commit is contained in:
420
skills/google-drive/SKILL.md
Normal file
420
skills/google-drive/SKILL.md
Normal 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`
|
||||
16
skills/google-drive/assets/settings_template.yaml
Normal file
16
skills/google-drive/assets/settings_template.yaml
Normal 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
|
||||
264
skills/google-drive/references/api_reference.md
Normal file
264
skills/google-drive/references/api_reference.md
Normal 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
|
||||
84
skills/google-drive/references/auth_setup.md
Normal file
84
skills/google-drive/references/auth_setup.md
Normal 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
|
||||
135
skills/google-drive/references/search_queries.md
Normal file
135
skills/google-drive/references/search_queries.md
Normal 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")
|
||||
```
|
||||
345
skills/google-drive/scripts/gdrive_helper.py
Executable file
345
skills/google-drive/scripts/gdrive_helper.py
Executable 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()
|
||||
Reference in New Issue
Block a user