GSuite Workspace GAPI - sgml/signature GitHub Wiki

Admin SDK

import os
import time
from googleapiclient.discovery import build
from google.oauth2 import service_account

class DriveRestrictor:
    def __init__(self, creds):
        self.service = build('drive', 'v3', credentials=creds)

    def list_all_files(self):
        files = []
        page_token = None
        while True:
            response = self.service.files().list(
                q="mimeType != 'application/vnd.google-apps.folder'",
                fields="nextPageToken, files(id, name)",
                pageSize=1000,
                pageToken=page_token
            ).execute()
            files.extend(response.get('files', []))
            page_token = response.get('nextPageToken')
            if not page_token:
                break
        return files

    def update_metadata_batch(self, files):
        for i in range(0, len(files), 10):
            batch = files[i:i+10]
            for file in batch:
                try:
                    self.service.files().update(
                        fileId=file['id'],
                        body={"downloadRestrictions": {"itemDownloadRestriction": True}}
                    ).execute()
                    print(f"[Metadata] Updated: {file['name']}")
                except Exception as e:
                    print(f"[Metadata] Error updating {file['name']}: {e}")
            time.sleep(1)

    def list_permissions(self, file_id):
        try:
            return self.service.permissions().list(
                fileId=file_id,
                fields="permissions(id, role, emailAddress)"
            ).execute().get('permissions', [])
        except Exception as e:
            print(f"[Permissions] Error listing for {file_id}: {e}")
            return []

    def downgrade_editors(self, file_id):
        for perm in self.list_permissions(file_id):
            if perm['role'] == 'writer':
                try:
                    self.service.permissions().update(
                        fileId=file_id,
                        permissionId=perm['id'],
                        body={'role': 'reader'},
                        fields='id'
                    ).execute()
                    print(f"[Permissions] Downgraded {perm['emailAddress']} on {file_id}")
                except Exception as e:
                    print(f"[Permissions] Error updating {perm['emailAddress']}: {e}")

    def process_all(self):
        files = self.list_all_files()
        self.update_metadata_batch(files)
        for file in files:
            self.downgrade_editors(file['id'])

class AdminDriveScanner:
    def __init__(self, creds):
        self.creds = creds
        self.admin_service = build('admin', 'directory_v1', credentials=creds)

    def list_users(self, limit=50):
        response = self.admin_service.users().list(
            customer='my_customer',
            maxResults=limit,
            orderBy='email'
        ).execute()
        return [user['primaryEmail'] for user in response.get('users', [])]

    def scan_all_users_files(self):
        all_files = []
        for user_email in self.list_users():
            delegated_creds = self.creds.with_subject(user_email)
            user_drive = build('drive', 'v3', credentials=delegated_creds)
            try:
                page_token = None
                while True:
                    response = user_drive.files().list(
                        q="mimeType != 'application/vnd.google-apps.folder'",
                        fields="nextPageToken, files(id, name)",
                        pageSize=1000,
                        pageToken=page_token
                    ).execute()
                    all_files.extend(response.get('files', []))
                    page_token = response.get('nextPageToken')
                    if not page_token:
                        break
                print(f"[Admin] Retrieved files for {user_email}")
            except Exception as e:
                print(f"[Admin] Error retrieving files for {user_email}: {e}")
        return all_files

if __name__ == "__main__":
    creds_path = os.getenv("GOOGLE_APPLICATION_CREDENTIALS")
    if not creds_path:
        raise EnvironmentError("GOOGLE_APPLICATION_CREDENTIALS not set")
    scopes = [
        'https://www.googleapis.com/auth/drive',
        'https://www.googleapis.com/auth/admin.directory.user.readonly'
    ]
    creds = service_account.Credentials.from_service_account_file(creds_path, scopes=scopes)

    restrictor = DriveRestrictor(creds)
    restrictor.process_all()

    # Optional: scan all users' files
    # scanner = AdminDriveScanner(creds)
    # all_files = scanner.scan_all_users_files()

Video Sharing

Troubleshooting

google_meet_router_optimization:
  qos:
    enabled: true
    prioritize_udp_ports:
      - 19302
      - 19303
      - 3478
      - 443
    dscp_tagging:
      expedited_forwarding: 0xb8
    application_priority:
      domains:
        - meet.google.com
        - googlevideo.com

  firewall:
    outbound_ports:
      udp:
        - 19302
        - 19303
        - 3478
        - 443
      tcp:
        - 443
        - 80

  nat:
    sip_alg_disabled: true
    hardware_nat_enabled: true

  packet_inspection:
    deep_packet_inspection: disabled
    proxy_traffic: disabled

  wireless:
    band_preference: "5GHz"
    wmm_enabled: true
    cell_size_management:
      transmit_power: reduced

  network_management:
    vpn_usage: discouraged_for_meet
    static_ip_reservation: true
    max_device_connections: limited
    browser_acceleration:
      chrome_hardware_acceleration: enabled
      gpu_binding:
        preferred_device: "discrete GPU"

  notes:
    - Avoid sharing protected DRM content to prevent screen blanking.
    - Use diagnostic tools (e.g., chrome://gpu or iperf3) to verify GPU and bandwidth health.
    - Dual-GPU setups may require manual routing of browser processes to discrete hardware.

Backup and Sync

setup both SMS sync and a recovery email which forwards to a third recovery email

Slides

Embedding Videos in Google Slides

Google Slides allows users to embed videos from YouTube and Google Drive, supporting full-screen playback during presentations.

Step-by-Step Process

Step Action Docs Reference
1 Open Google Slides and navigate to the slide where you want to embed the video. Google Slides Help
2 Click on Insert → Video from the toolbar. Insert Media Guide
3 Choose your video source: YouTube, Google Drive, or enter a URL manually. Embedding Options
4 Select the desired video and click Insert to place it into the slide. Google Drive Video Embed
5 Resize and position the video as needed within the slide. Resizing Objects
6 Click on the Format options panel (right-click on the video), then enable "Play full screen" during presentation mode. Format Options
7 Test the video by entering Slideshow Mode (Press Present or F5). Presentation Mode Guide

Footnotes

  1. Video Size Limits

    • Videos uploaded to Google Drive for embedding must be ≤5TB, though practical playback limits depend on device performance.
    • YouTube embeds have no specific size limit, as playback occurs directly from YouTube servers.
  2. Supported File Formats

    • Google Slides supports embedding videos in the following formats via Google Drive:
      • .MP4
      • .MOV
      • .AVI
      • .WMV
      • .FLV
      • .MKV
    • YouTube videos must be in a compatible streaming format (typically .MP4, .WEBM, or .FLV).
  3. Number of Slides Limits

    • A single Google Slides presentation can contain up to 200 slides.
    • Multiple embedded videos may slow performance, especially on low-power devices.

Workspace Console