Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/SlasshyOverhere/StreamVault/llms.txt

Use this file to discover all available pages before exploring further.

Overview

StreamVault integrates with Google Drive to:
  • Index your entire cloud media library
  • Stream videos directly without downloading
  • Monitor for new content in real-time (5-second polling)
  • Cache frequently watched content locally
Google Drive integration uses OAuth 2.0 authentication handled securely by the StreamVault backend server.

OAuth Flow Architecture

The authentication process uses a backend proxy to protect OAuth credentials:

Why Use a Backend Server?

From gdrive.rs:14-17:
// Backend auth server URL (handles OAuth securely)
// This keeps client_id and client_secret on the server
const AUTH_SERVER_URL: &str = "https://streamvault-backend-server.onrender.com";
Security: Never embed OAuth client_secret in desktop apps! The backend proxy prevents secret exposure while maintaining secure authentication.

Connecting Google Drive

1

Open Settings

Click the Settings gear icon → Cloud Storage section
2

Click Connect

Click Connect Google Drive button
3

Browser Opens Automatically

StreamVault opens your default browser to Google’s OAuth consent screen
4

Sign In to Google

  • Select or sign in to your Google account
  • Review the permissions StreamVault is requesting
5

Grant Permissions

Click Allow to grant StreamVault:
  • Read access to your Google Drive files (drive.readonly scope)
  • Basic profile information (email, name)
6

Automatic Redirect

After authorization:
  • Production: Redirects to https://indexer-oauth-callback.vercel.app/
  • Development: Redirects to http://localhost:8085/callback
The redirect page automatically sends the auth code back to StreamVault
7

Confirmation

You’ll see a success toast:
Connected!
Signed in as user@gmail.com

Connection UI Implementation

From GoogleDriveSettings.tsx:49-80:
const handleConnect = async () => {
  setIsConnecting(true)
  try {
    // Start OAuth - this opens the browser
    await startGDriveAuth()

    toast({
      title: "Authorization Started",
      description: "Complete sign-in in your browser. You'll be redirected back automatically."
    })

    // Wait for the OAuth callback (automatic via deep link or localhost)
    const info = await completeGDriveAuth()

    setIsConnected(true)
    setAccountInfo(info)

    toast({
      title: "Connected!",
      description: `Signed in as ${info.email}`
    })
  } catch (error) {
    console.error('[GDrive] Auth failed:', error)
    toast({
      title: "Connection Failed",
      description: String(error),
      variant: "destructive"
    })
  } finally {
    setIsConnecting(false)
  }
}

OAuth Scopes

StreamVault requests these Google API scopes:
ScopePurpose
https://www.googleapis.com/auth/drive.readonlyRead-only access to Drive files (default)
https://www.googleapis.com/auth/driveFull access (required for delete operations)
https://www.googleapis.com/auth/userinfo.emailUser email address
https://www.googleapis.com/auth/userinfo.profileUser profile information
By default, StreamVault uses read-only scope (drive.readonly). Full access is only requested when you use delete operations.

Re-Authentication for Delete Permissions

When you delete media from StreamVault that needs to be removed from Drive:
1

Initial Scope Change

StreamVault detects it needs the drive scope (not just drive.readonly)
2

Automatic Re-Auth Prompt

The app prompts you to re-authenticate with the new scope
3

Grant Additional Permission

Google’s consent screen shows:
  • ✓ See and download all your Google Drive files
  • Delete files and folders in your Google Drive (new)
4

Updated Token

After granting permission, StreamVault receives a new access token with the expanded scope

Token Refresh

Access tokens expire after 1 hour. StreamVault automatically refreshes them using the refresh token. From gdrive.rs:106-127:
pub async fn get_access_token(&self) -> Result<String, String> {
    let tokens = self.tokens.lock().unwrap().clone();

    match tokens {
        Some(t) => {
            // Check if token is expired
            if let Some(expires_at) = t.expires_at {
                let now = chrono::Utc::now().timestamp();
                if now >= expires_at - 60 {
                    // Token expired or about to expire, refresh it
                    if let Some(refresh_token) = &t.refresh_token {
                        return self.refresh_access_token(refresh_token).await;
                    }
                    return Err("Token expired and no refresh token available".to_string());
                }
            }
            Ok(t.access_token)
        }
        None => Err("Not authenticated".to_string()),
    }
}

Refresh Flow

From gdrive.rs:130-167:
async fn refresh_access_token(&self, refresh_token: &str) -> Result<String, String> {
    let response = self.http_client
        .post(format!("{}/auth/refresh", AUTH_SERVER_URL))
        .json(&serde_json::json!({
            "refresh_token": refresh_token
        }))
        .send()
        .await
        .map_err(|e| format!("Failed to refresh token: {}", e))?;

    let token_response: serde_json::Value = response.json().await?;

    let access_token = token_response["access_token"]
        .as_str()
        .ok_or("Missing access_token in response")?
        .to_string();

    let expires_in = token_response["expires_in"].as_i64().unwrap_or(3600);
    let expires_at = chrono::Utc::now().timestamp() + expires_in;

    // Update stored tokens
    let mut tokens = self.tokens.lock().unwrap();
    if let Some(ref mut t) = *tokens {
        t.access_token = access_token.clone();
        t.expires_at = Some(expires_at);
        save_tokens(t).ok();
    }

    Ok(access_token)
}
Refresh tokens are long-lived (valid until revoked). StreamVault stores them securely in %APPDATA%/StreamVault/gdrive_tokens.json.

Disconnecting Google Drive

From GoogleDriveSettings.tsx:82-103:
const handleDisconnect = async () => {
  setIsDisconnecting(true)
  try {
    await disconnectGDrive()
    setIsConnected(false)
    setAccountInfo(null)

    toast({
      title: "Disconnected",
      description: "Google Drive has been disconnected"
    })
  } catch (error) {
    console.error('[GDrive] Disconnect failed:', error)
    toast({
      title: "Error",
      description: "Failed to disconnect",
      variant: "destructive"
    })
  } finally {
    setIsDisconnecting(false)
  }
}
Data Retention: Disconnecting Google Drive removes tokens but does not delete your local database or cached media. To fully reset, use Settings → Advanced → Reset Application.

Account Information Display

After connecting, StreamVault displays:
  • Email: Google account email address
  • Storage Used: Current Drive storage usage
  • Storage Limit: Total Drive storage quota
  • Storage Bar: Visual progress indicator
From GoogleDriveSettings.tsx:150-177:
{isConnected && accountInfo && (
  <div className="space-y-3">
    {/* Account Info */}
    <div className="flex items-center gap-2">
      <User className="w-4 h-4" />
      <span>{accountInfo.email}</span>
    </div>

    {/* Storage Info */}
    <div className="space-y-2">
      <div className="flex items-center gap-2">
        <HardDrive className="w-4 h-4" />
        <span>
          {formatStorageSize(accountInfo.storage_used)} of{' '}
          {formatStorageSize(accountInfo.storage_limit)} used
        </span>
      </div>
      <div className="h-2 rounded-full bg-muted overflow-hidden">
        <div
          className="h-full bg-gradient-to-r from-white to-gray-400"
          style={{ width: `${Math.min(100, (storage_used / storage_limit) * 100)}%` }}
        />
      </div>
    </div>
  </div>
)}

Indexing Your Drive

After connecting:
1

Open Sidebar

Navigate to the Cloud tab in the sidebar
2

Click Index Drive

Click the Index Drive button to scan your entire Google Drive
3

Monitor Progress

Watch the progress indicator as StreamVault:
  • Scans all folders recursively
  • Identifies movie and TV show files
  • Fetches metadata from TMDB
  • Downloads posters and thumbnails
4

View Library

Your indexed media appears in the Movies and TV Shows tabs
Indexing is incremental - only new files not already in the database are processed.

Real-Time Change Detection

StreamVault monitors Google Drive for changes every 5 seconds using the Changes API. From config.rs:194-208:
// Cloud auto-scan interval in minutes (default 5 minutes)
#[serde(default = "default_cloud_scan_interval_minutes")]
pub cloud_scan_interval_minutes: u32,

fn default_cloud_scan_interval_minutes() -> u32 {
    5 // Scan every 5 minutes by default
}

How It Works

  1. Initial Scan: Gets a startPageToken from Google Drive Changes API
  2. Store Token: Saves token in SQLite database
  3. Poll for Changes: Every 5 seconds, requests changes since last token
  4. Process New Files: Indexes newly added videos
  5. Update Token: Updates stored token for next poll
Change detection runs even when minimized to system tray. You’ll receive Windows notifications when new media is added.

Environment Variables

For self-hosting or forking the backend, configure these in .env.example:66-78:
# Your Google OAuth 2.0 Client ID
# Example: 123456789-abcdefghijklmnop.apps.googleusercontent.com
GDRIVE_CLIENT_ID=YOUR_CLIENT_ID.apps.googleusercontent.com

# Your Google OAuth 2.0 Client Secret
# Example: GOCSPX-abcdefghijklmnop123456
GDRIVE_CLIENT_SECRET=YOUR_CLIENT_SECRET

# OAuth Redirect URI (optional - has sensible defaults)
# Default in production: https://indexer-oauth-callback.vercel.app/
# Default in development: http://localhost:8085/callback
GDRIVE_REDIRECT_URI=https://indexer-oauth-callback.vercel.app/

Troubleshooting

  • Check if your default browser is set correctly
  • Try copying the OAuth URL from the console and opening it manually
  • Ensure firewall isn’t blocking localhost:8085 (dev mode)
StreamVault automatically refreshes tokens. If this fails:
  • Disconnect and reconnect Google Drive
  • Check internet connectivity
  • Verify refresh token wasn’t revoked in Google Account settings
  • Check Google Drive API quota (not exceeded)
  • Ensure TMDB API key is configured
  • Monitor Developer Console for errors
  • Try indexing a smaller folder first

Next Steps

TMDB Setup

Configure TMDB API for metadata

Player Setup

Install MPV for playback

Cloud Streaming

Start streaming from Drive

Library Management

Organize your media library