Build a Simple Java YouTube Uploader with Google APIUploading videos to YouTube programmatically can save time, automate workflows, and integrate video publishing into your applications. This guide walks you through building a simple Java YouTube uploader using the official Google APIs. It covers prerequisites, setting up the Google Cloud project, authentication, required dependencies, example code, common pitfalls, and suggestions for production readiness.
What you’ll learn
- How to create and configure a Google Cloud project and enable the YouTube Data API v3
- How to authenticate with OAuth 2.0 in a Java application
- How to install required libraries and build a simple uploader that uploads a video, sets metadata (title, description, tags, privacy), and monitors progress
- Tips for error handling, quota management, and production concerns
Prerequisites
- Java 11 or later installed
- Maven or Gradle for dependency management
- A Google account with access to Google Cloud Console
- Basic familiarity with OAuth 2.0 and Java development
Step 1 — Create and configure a Google Cloud project
- In Google Cloud Console, create a new project or select an existing project.
- Enable the YouTube Data API v3 for the project.
- Create OAuth 2.0 credentials:
- Go to “APIs & Services” → “Credentials” → “Create Credentials” → “OAuth client ID”.
- Choose “Desktop app” for testing or “Web application” if integrating into a web backend.
- Download the client_secret JSON (or copy the client ID and secret) — you’ll need this in your Java app.
- (Optional) Configure the OAuth consent screen with scopes you’ll request (e.g., YouTube upload scope).
Step 2 — Choose scopes
For uploading videos and managing metadata, request the following scope:
You may also request broader scopes if needed:
- https://www.googleapis.com/auth/youtube (manage account)
- https://www.googleapis.com/auth/youtube.force-ssl
Use the minimum scope necessary for better security.
Step 3 — Add dependencies
Using Maven, add dependencies for Google API client libraries and OAuth support. Example pom.xml snippets:
<dependencies> <!-- Google API Client and OAuth2 --> <dependency> <groupId>com.google.api-client</groupId> <artifactId>google-api-client</artifactId> <version>1.35.0</version> </dependency> <!-- Google OAuth Client --> <dependency> <groupId>com.google.oauth-client</groupId> <artifactId>google-oauth-client-jetty</artifactId> <version>1.35.0</version> </dependency> <!-- YouTube Data API v3 --> <dependency> <groupId>com.google.apis</groupId> <artifactId>google-api-services-youtube</artifactId> <version>v3-rev20230320-1.32.1</version> </dependency> <!-- For resumable uploads --> <dependency> <groupId>com.google.http-client</groupId> <artifactId>google-http-client-jackson2</artifactId> <version>1.40.0</version> </dependency> </dependencies>
Adjust versions to the latest stable releases.
Step 4 — Authentication flow (OAuth 2.0)
For a simple desktop or server-side app, use the OAuth installed app flow with a local browser. The Google OAuth client libraries can open a browser for user consent, then exchange the authorization code for tokens and store the refresh token for future uploads.
Key points:
- Store tokens securely (file with restricted permissions, encrypted secrets manager for production).
- Use refresh tokens to avoid repeated consent.
- For headless servers, use the OAuth consent with an out-of-band flow or implement a small web callback endpoint.
Step 5 — The uploader code (example)
Below is a concise Java example demonstrating authentication, building the YouTube client, and uploading a video with metadata. This is a minimal but functional starting point; adapt to your project structure and error handling standards.
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets; import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.http.InputStreamContent; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.client.util.store.FileDataStoreFactory; import com.google.api.services.youtube.YouTube; import com.google.api.services.youtube.model.*; import java.io.*; import java.security.GeneralSecurityException; import java.util.Collections; import java.util.List; public class SimpleYouTubeUploader { private static final String CLIENT_SECRETS= "client_secret.json"; // downloaded from Google Cloud private static final List<String> SCOPES = Collections.singletonList("https://www.googleapis.com/auth/youtube.upload"); private static final String CREDENTIALS_FOLDER = "credentials"; // where tokens are stored private static final JacksonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); private static YouTube getService() throws GeneralSecurityException, IOException { var httpTransport = GoogleNetHttpTransport.newTrustedTransport(); InputStream in = new FileInputStream(CLIENT_SECRETS); GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in)); var flow = new GoogleAuthorizationCodeFlow.Builder( httpTransport, JSON_FACTORY, clientSecrets, SCOPES) .setDataStoreFactory(new FileDataStoreFactory(new java.io.File(CREDENTIALS_FOLDER))) .setAccessType("offline") .build(); var credential = new com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp( flow, new com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver()).authorize("user"); return new YouTube.Builder(httpTransport, JSON_FACTORY, credential) .setApplicationName("simple-java-youtube-uploader") .build(); } public static void uploadVideo(String filePath, String title, String description, List<String> tags, String privacyStatus) throws Exception { YouTube youtubeService = getService(); Video videoObjectDefiningMetadata = new Video(); VideoStatus status = new VideoStatus(); status.setPrivacyStatus(privacyStatus); // "public", "unlisted", "private" videoObjectDefiningMetadata.setStatus(status); VideoSnippet snippet = new VideoSnippet(); snippet.setTitle(title); snippet.setDescription(description); snippet.setTags(tags); videoObjectDefiningMetadata.setSnippet(snippet); File videoFile = new File(filePath); InputStreamContent mediaContent = new InputStreamContent( "video/*", new BufferedInputStream(new FileInputStream(videoFile))); mediaContent.setLength(videoFile.length()); YouTube.Videos.Insert request = youtubeService.videos() .insert("snippet,status", videoObjectDefiningMetadata, mediaContent); // Use a MediaHttpUploader for resumable uploads and progress monitoring request.getMediaHttpUploader().setDirectUploadEnabled(false); request.getMediaHttpUploader().setProgressListener(uploader -> { switch (uploader.getUploadState()) { case INITIATION_STARTED -> System.out.println("Initiation started"); case INITIATION_COMPLETE -> System.out.println("Initiation complete"); case MEDIA_IN_PROGRESS -> System.out.printf("Upload in progress: %.2f%%%n", uploader.getProgress() * 100); case MEDIA_COMPLETE -> System.out.println("Upload Completed!"); case NOT_STARTED -> System.out.println("Upload Not Started"); } }); Video response = request.execute(); System.out.printf("Video uploaded. ID: %s%n", response.getId()); } public static void main(String[] args) throws Exception { // Example usage String filePath = "path/to/video.mp4"; String title = "Test upload via Java"; String description = "Uploaded with SimpleYouTubeUploader"; List<String> tags = List.of("java", "youtube", "api"); String privacy = "unlisted"; uploadVideo(filePath, title, description, tags, privacy); } }
Notes:
- client_secret.json should be the OAuth 2.0 client credentials you downloaded.
- The code uses a local web server (LocalServerReceiver) to complete OAuth. For non-interactive servers, create a web OAuth flow or service account alternative (YouTube Data API does not support direct uploads with service accounts for regular channels; service accounts are for content owners and specific use cases).
Error handling, quotas, and retries
- Implement exponential backoff for 5xx and quota-related errors.
- Watch quota usage: each upload counts against your API quota. Monitor in Cloud Console and request quota increases if needed.
- Handle common errors: invalid credentials (refresh token expired), insufficient permissions (wrong scopes), file too large (use resumable uploads), and rate limits.
Production considerations
- Securely store client secrets and tokens (use a secrets manager).
- Use resumable uploads for large files and unreliable networks.
- Implement logging, alerting, and monitoring for upload failures and quota consumption.
- Respect YouTube’s terms of service and content policies.
- Consider using a backend service to perform uploads rather than exposing credentials client-side.
Testing and validation
- Test with small videos first.
- Verify metadata appears correctly in the YouTube Studio after upload.
- Check privacy setting behavior (uploads set to private/unlisted may still require additional steps to make public later).
Troubleshooting tips
- “Invalid Credentials”: delete stored tokens and re-run consent flow.
- “Quota exceeded”: check Cloud Console quota usage and reduce frequency or request more quota.
- Long upload times: enable resumable uploads and monitor progress.
- HTTP 403: check that the OAuth client and project have YouTube Data API enabled and correct scopes.
Summary
This article provided a complete walkthrough to build a simple Java YouTube uploader using Google’s YouTube Data API v3: from creating a Cloud project and OAuth credentials to implementing an uploader with resumable upload support and progress monitoring. Adapt the sample code for your architecture, secure your credentials, and handle errors and quotas to make your uploader robust for production.
Leave a Reply