BubbleCam/video-upload.js
2025-02-16 15:10:07 +05:30

432 lines
15 KiB
JavaScript

class YouTubeUploader {
constructor() {
this.CLIENT_ID = '728787049181-iq4lnrcks0fifee7r6h57h7h71berii6.apps.googleusercontent.com';
this.SCOPES = ['https://www.googleapis.com/auth/youtube.upload'];
this.initializeUploadButton();
}
initializeUploadButton() {
const uploadButton = document.getElementById('upload-youtube');
console.log('Upload button found:', !!uploadButton);
if (uploadButton) {
uploadButton.addEventListener('click', async (e) => {
console.log('Upload button clicked');
e.preventDefault();
try {
await this.prepareAndUploadVideo();
} catch (error) {
console.error('Complete upload error:', error);
this.showError(`Upload failed: ${error.message}`);
}
});
} else {
console.error('YouTube upload button not found in DOM');
}
}
async prepareAndUploadVideo() {
try {
// Detailed logging for video retrieval
const videoData = await this.getRecordedVideoFromStorage();
console.log('Video data retrieved:', !!videoData);
if (!videoData) {
this.showError('No video available to upload');
return;
}
// Convert base64 to blob with detailed logging
const videoBlob = this.base64ToBlob(videoData);
console.log('Video blob created, size:', videoBlob.size);
// Get authentication token
const token = await this.getAuthToken();
console.log('Authentication token obtained');
// Prepare metadata for the video
const metadata = {
snippet: {
title: `Screen Recording ${new Date().toLocaleString()}`,
description: 'Screen recording uploaded from Chrome Extension',
tags: ['screen recording'],
categoryId: '22' // Category for 'People & Blogs'
},
status: {
privacyStatus: 'private'
}
};
// Perform the upload
const uploadResult = await this.uploadVideo(token, videoBlob, metadata);
console.log('Upload result:', uploadResult);
this.showSuccess('Video uploaded to YouTube successfully!');
} catch (error) {
console.error('Complete YouTube Upload Error:', error);
this.showError(`Upload failed: ${error.message}`);
}
}
getRecordedVideoFromStorage() {
return new Promise((resolve, reject) => {
chrome.storage.local.get(['recordedVideoData'], (result) => {
console.log('Storage retrieval:', result.recordedVideoData ? 'Video found' : 'No video');
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else {
resolve(result.recordedVideoData);
}
});
});
}
getAuthToken() {
return new Promise((resolve, reject) => {
console.log('Attempting to get auth token');
// Fallback authentication method
const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?${new URLSearchParams({
client_id: this.CLIENT_ID,
redirect_uri: chrome.identity.getRedirectURL(),
response_type: 'token',
scope: this.SCOPES.join(' '),
prompt: 'consent'
})}`;
chrome.identity.launchWebAuthFlow(
{ url: authUrl, interactive: true },
(redirectUrl) => {
console.log('Auth flow redirect received');
if (chrome.runtime.lastError) {
console.error('Auth flow error:', chrome.runtime.lastError);
reject(chrome.runtime.lastError);
return;
}
const urlParams = new URLSearchParams(new URL(redirectUrl).hash.slice(1));
const accessToken = urlParams.get('access_token');
if (!accessToken) {
console.error('No access token retrieved');
reject(new Error('Failed to retrieve access token'));
return;
}
console.log('Access token successfully retrieved');
resolve(accessToken);
}
);
});
}
base64ToBlob(base64Data) {
// Remove the data URL prefix if it exists
const base64String = base64Data.replace(/^data:video\/\w+;base64,/, '');
// Decode base64
const byteCharacters = atob(base64String);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += 512) {
const slice = byteCharacters.slice(offset, offset + 512);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
return new Blob(byteArrays, { type: 'video/webm' });
}
async uploadVideo(token, videoBlob, metadata) {
const formData = new FormData();
const metadataBlob = new Blob([JSON.stringify(metadata)], {
type: 'application/json; charset=UTF-8'
});
formData.append('metadata', metadataBlob, 'metadata.json');
formData.append('file', videoBlob, 'screen_recording.webm');
try {
const response = await fetch(
`https://www.googleapis.com/upload/youtube/v3/videos?uploadType=multipart&part=snippet,status`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
},
body: formData
}
);
if (!response.ok) {
const errorBody = await response.text();
console.error('Upload response error:', errorBody);
throw new Error(`Upload failed: ${errorBody}`);
}
const result = await response.json();
console.log('YouTube Upload Success:', result);
return result;
} catch (error) {
console.error('Upload Error Details:', error);
throw error;
}
}
showSuccess(message) {
const notification = document.createElement('div');
notification.textContent = message;
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background-color: green;
color: white;
padding: 10px 20px;
border-radius: 5px;
z-index: 1000;
`;
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 3000);
}
showError(message) {
const notification = document.createElement('div');
notification.textContent = message;
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background-color: red;
color: white;
padding: 10px 20px;
border-radius: 5px;
z-index: 1000;
`;
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 3000);
}
}
// Initialize the uploader when the page loads
document.addEventListener('DOMContentLoaded', () => {
console.log('DOM loaded, initializing YouTube Uploader');
new YouTubeUploader();
});
// Add global error logging
window.addEventListener('error', (event) => {
console.error('Unhandled error:', event.error);
});
class VimeoUploader {
constructor() {
// Replace with your Vimeo API access token
this.ACCESS_TOKEN = 'fad52305c371058da84097cefb9a95a3';
this.initializeUploadButton();
}
initializeUploadButton() {
const uploadButton = document.getElementById('upload-vimeo');
console.log('Vimeo upload button found:', !!uploadButton);
if (uploadButton) {
uploadButton.addEventListener('click', async (e) => {
console.log('Vimeo upload button clicked');
e.preventDefault();
try {
await this.prepareAndUploadVideo();
} catch (error) {
console.error('Vimeo upload error:', error);
this.showError(`Upload failed: ${error.message}`);
}
});
} else {
console.error('Vimeo upload button not found');
}
}
async prepareAndUploadVideo() {
try {
// Retrieve video from storage
const videoData = await this.getRecordedVideoFromStorage();
console.log('Video data retrieved:', !!videoData);
if (!videoData) {
this.showError('No video available to upload');
return;
}
// Convert base64 to blob
const videoBlob = this.base64ToBlob(videoData);
console.log('Video blob created, size:', videoBlob.size);
// Initiate upload and get upload link
const uploadTicket = await this.createUploadTicket(videoBlob.size);
// Upload the video
await this.uploadVideoToVimeo(uploadTicket.upload_link, videoBlob);
this.showSuccess('Video uploaded to Vimeo successfully!');
} catch (error) {
console.error('Vimeo Upload Error:', error);
this.showError(`Upload failed: ${error.message}`);
}
}
async createUploadTicket(fileSize) {
try {
const response = await fetch('https://api.vimeo.com/me/videos', {
method: 'POST',
headers: {
'Authorization': `bearer ${this.ACCESS_TOKEN}`,
'Content-Type': 'application/json',
'Accept': 'application/vnd.vimeo.*+json;version=3.4'
},
body: JSON.stringify({
upload: {
approach: 'tus',
size: fileSize
},
name: `Screen Recording ${new Date().toLocaleString()}`,
description: 'Screen recording uploaded from Chrome Extension'
})
});
if (!response.ok) {
const errorText = await response.text();
console.error('Vimeo API Response:', errorText);
throw new Error(`Failed to create upload ticket: ${errorText}`);
}
const uploadTicket = await response.json();
console.log('Complete Upload Ticket:', uploadTicket);
// Explicitly log the upload link
const uploadLink = uploadTicket.upload?.upload_link;
console.log('Extracted Upload Link:', uploadLink);
if (!uploadLink) {
throw new Error('No upload link found in Vimeo response');
}
return uploadTicket;
} catch (error) {
console.error('Upload Ticket Creation Error:', error);
throw error;
}
}
async uploadVideoToVimeo(uploadTicket, videoBlob) {
const uploadLink = uploadTicket.upload
? uploadTicket.upload.upload_link
: uploadTicket.upload_link || uploadTicket.uri;
console.log('Actual Upload Link:', uploadLink);
if (!uploadLink) {
throw new Error('No upload link found');
}
try {
const response = await fetch(uploadLink, {
method: 'PATCH',
headers: {
'Content-Type': 'application/offset+octet-stream',
'Upload-Offset': '0',
'Tus-Resumable': '1.0.0',
'Authorization': `bearer ${this.ACCESS_TOKEN}`
},
body: videoBlob
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Vimeo upload failed: ${errorText}`);
}
return response;
} catch (error) {
console.error('Upload detailed error:', error);
throw error;
}
}
getRecordedVideoFromStorage() {
return new Promise((resolve, reject) => {
chrome.storage.local.get(['recordedVideoData'], (result) => {
console.log('Storage retrieval:', result.recordedVideoData ? 'Video found' : 'No video');
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else {
resolve(result.recordedVideoData);
}
});
});
}
base64ToBlob(base64Data) {
const base64String = base64Data.replace(/^data:video\/\w+;base64,/, '');
const byteCharacters = atob(base64String);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += 512) {
const slice = byteCharacters.slice(offset, offset + 512);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
return new Blob(byteArrays, { type: 'video/webm' });
}
showSuccess(message) {
const notification = document.createElement('div');
notification.textContent = message;
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background-color: green;
color: white;
padding: 10px 20px;
border-radius: 5px;
z-index: 1000;
`;
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 3000);
}
showError(message) {
const notification = document.createElement('div');
notification.textContent = message;
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background-color: red;
color: white;
padding: 10px 20px;
border-radius: 5px;
z-index: 1000;
`;
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 3000);
}
}
// Initialize the uploader when the page loads
document.addEventListener('DOMContentLoaded', () => {
console.log('DOM loaded, initializing Vimeo Uploader');
new VimeoUploader();
});