feat: complete migration logic

This commit is contained in:
sethburkart123
2024-11-13 09:27:14 +11:00
parent 172021d0d0
commit e5c05c0dca
7 changed files with 242 additions and 69 deletions
+4
View File
@@ -38,6 +38,7 @@ import documentLoadCSS from '@/css/documentload.scss?inline'
import renderSvelte from '@/interface/main' import renderSvelte from '@/interface/main'
import Settings from '@/interface/pages/settings.svelte' import Settings from '@/interface/pages/settings.svelte'
import { settingsPopup } from './interface/hooks/SettingsPopup' import { settingsPopup } from './interface/hooks/SettingsPopup'
import { migrateBackgrounds } from './seqta/utils/migrateBackgrounds'
let SettingsClicked = false let SettingsClicked = false
export let MenuOptionsOpen = false export let MenuOptionsOpen = false
@@ -458,7 +459,10 @@ export async function finishLoad() {
if (settingsState.justupdated && !document.getElementById('whatsnewbk')) { if (settingsState.justupdated && !document.getElementById('whatsnewbk')) {
OpenWhatsNewPopup(); OpenWhatsNewPopup();
/* Background Migration script */
} }
migrateBackgrounds();
} }
async function DeleteWhatsNew() { async function DeleteWhatsNew() {
-67
View File
@@ -1,20 +1,5 @@
import browser from 'webextension-polyfill' import browser from 'webextension-polyfill'
import type { SettingsState } from "@/types/storage"; import type { SettingsState } from "@/types/storage";
import { blobToBase64 } from './seqta/utils/blobToBase64';
interface Data {
id: string;
blob: Blob;
type: 'image' | 'video';
}
interface DatabaseEventTarget extends EventTarget {
result: IDBDatabase;
}
interface DatabaseEvent extends Event {
target: DatabaseEventTarget;
}
export const openDB = () => { export const openDB = () => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@@ -284,63 +269,11 @@ async function UpdateCurrentValues() {
} }
} }
const getAllBackgrounds = async (): Promise<Data[]> => {
const db = await openDB() as IDBDatabase;
const tx = db.transaction('backgrounds', 'readonly');
const store = tx.objectStore('backgrounds');
const request = store.getAll();
return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
};
const sendBackgroundToTab = async (background: Data): Promise<void> => {
const base64Data = await blobToBase64(background.blob);
// Get the current active tab
const [tab] = await browser.tabs.query({ active: true, currentWindow: true });
if (!tab?.id) return;
// Send message to the tab
await browser.tabs.sendMessage(tab.id, {
type: 'MIGRATE_BACKGROUND',
payload: {
id: background.id,
data: base64Data,
mediaType: background.type
}
});
};
const migrateBackgrounds = async (): Promise<void> => {
try {
console.log('Starting background migration...');
const backgrounds = await getAllBackgrounds();
console.log(`Found ${backgrounds.length} backgrounds to migrate`);
// Process backgrounds sequentially
for (const background of backgrounds) {
console.log(`Migrating background: ${background.id}`);
await sendBackgroundToTab(background);
console.log(`Successfully migrated background: ${background.id}`);
}
console.log('Migration completed successfully');
} catch (error) {
console.error('Migration failed:', error);
throw error;
}
};
browser.runtime.onInstalled.addListener(function (event) { browser.runtime.onInstalled.addListener(function (event) {
browser.storage.local.remove(['justupdated']); browser.storage.local.remove(['justupdated']);
browser.storage.local.remove(['data']); browser.storage.local.remove(['data']);
UpdateCurrentValues(); UpdateCurrentValues();
migrateBackgrounds();
if ( event.reason == 'install', event.reason == 'update' ) { if ( event.reason == 'install', event.reason == 'update' ) {
browser.storage.local.set({ justupdated: true }); browser.storage.local.set({ justupdated: true });
} }
+1 -1
View File
@@ -1,5 +1,5 @@
const base64ToBlob = (base64: string, contentType: string = ''): Blob => { const base64ToBlob = (base64: string, contentType: string = ''): Blob => {
const byteCharacters = atob(base64.split(',')[1]); const byteCharacters = atob(base64);
const byteArrays: Uint8Array[] = []; const byteArrays: Uint8Array[] = [];
for (let offset = 0; offset < byteCharacters.length; offset += 512) { for (let offset = 0; offset < byteCharacters.length; offset += 512) {
+122
View File
@@ -0,0 +1,122 @@
import browser from 'webextension-polyfill';
import base64ToBlob from './base64ToBlob';
import { delay } from './delay';
import { openDatabase, writeData } from '@/interface/hooks/BackgroundDataLoader';
import { backgroundUpdates } from '@/interface/hooks/BackgroundUpdates';
const MIGRATION_STATE_KEY = 'background_migration_state';
interface MigrationState {
lastProcessedId: string | null;
total: number;
processed: number;
completed: boolean;
}
export const migrateBackgrounds = async (): Promise<void> => {
console.info('Migrating backgrounds...');
const savedState = localStorage.getItem(MIGRATION_STATE_KEY);
const migrationState: MigrationState = savedState
? JSON.parse(savedState)
: { lastProcessedId: null, total: 0, processed: 0, completed: false };
if (migrationState.completed) {
console.info('Migration already completed');
return;
}
return new Promise((resolve, reject) => {
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
const handleMessage = async (event: MessageEvent) => {
if (event.source !== iframe.contentWindow) return;
switch (event.data.type) {
case 'GET_LAST_PROCESSED_ID':
iframe.contentWindow?.postMessage({
type: 'LAST_PROCESSED_ID',
id: migrationState.lastProcessedId
}, '*');
break;
case 'BACKGROUND_DATA':
try {
const { id, data, mediaType, total, processed } = event.data.payload;
const mimeType = mediaType === 'image' ? 'image/png' : 'video/mp4';
const blob = base64ToBlob(data, mimeType);
await storeBackground({
id,
blob,
type: mediaType
});
migrationState.lastProcessedId = id;
migrationState.total = total;
migrationState.processed = processed;
localStorage.setItem(MIGRATION_STATE_KEY, JSON.stringify(migrationState));
console.log(`Migrated background: ${id} (${processed}/${total})`);
} catch (error) {
console.error('Error handling background data:', error);
}
break;
case 'MIGRATION_COMPLETE':
console.info('Migration completed successfully');
migrationState.completed = true;
localStorage.setItem(MIGRATION_STATE_KEY, JSON.stringify(migrationState));
window.removeEventListener('message', handleMessage);
iframe.remove();
resolve();
break;
case 'MIGRATION_ERROR':
console.error('Migration failed:', event.data.error);
window.removeEventListener('message', handleMessage);
iframe.remove();
reject(new Error(event.data.error));
break;
}
};
window.addEventListener('message', handleMessage);
const startPinging = () => {
const pingInterval = setInterval(() => {
iframe.contentWindow?.postMessage({ type: 'PING' }, '*');
}, 500);
const messageHandler = (event: MessageEvent) => {
if (event.source === iframe.contentWindow) {
clearInterval(pingInterval);
window.removeEventListener('message', messageHandler);
iframe.contentWindow?.postMessage({ type: 'START_MIGRATION' }, '*');
}
};
window.addEventListener('message', messageHandler);
};
iframe.src = browser.runtime.getURL('seqta/utils/migration/migrate.html');
document.body.appendChild(iframe);
startPinging();
});
};
const storeBackground = async (data: {
id: string;
blob: Blob;
type: 'image' | 'video';
}): Promise<void> => {
try {
await openDatabase();
await writeData(data.id, data.type, data.blob);
backgroundUpdates.triggerUpdate();
} catch (error) {
console.error('Error storing background:', error);
throw error;
}
};
+10
View File
@@ -0,0 +1,10 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Background Migration</title>
</head>
<body>
<script type="module" src="./migration-iframe.ts"></script>
</body>
</html>
@@ -0,0 +1,103 @@
// This goes in your migration.html's script
interface Data {
id: string;
blob: Blob;
type: 'image' | 'video';
}
const openDB = (): Promise<IDBDatabase> => {
return new Promise((resolve, reject) => {
const request = indexedDB.open('MyDatabase', 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
});
};
const blobToBase64 = (blob: Blob): Promise<string> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => {
const base64 = reader.result as string;
resolve(base64.split(',')[1]); // Remove data URL prefix
};
reader.onerror = () => reject(reader.error);
reader.readAsDataURL(blob);
});
};
const getAllBackgrounds = async (): Promise<Data[]> => {
const db = await openDB();
const tx = db.transaction('backgrounds', 'readonly');
const store = tx.objectStore('backgrounds');
const request = store.getAll();
return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
};
const startMigration = async () => {
try {
console.info('Starting background extraction...');
const backgrounds = await getAllBackgrounds();
console.info(`Found ${backgrounds.length} backgrounds`);
window.parent.postMessage({ type: 'GET_LAST_PROCESSED_ID' }, '*');
const lastProcessedId = await new Promise<string | null>(resolve => {
const handler = (event: MessageEvent) => {
if (event.data.type === 'LAST_PROCESSED_ID') {
window.removeEventListener('message', handler);
resolve(event.data.id);
}
};
window.addEventListener('message', handler);
});
const remainingBackgrounds = lastProcessedId
? backgrounds.slice(backgrounds.findIndex(b => b.id === lastProcessedId) + 1)
: backgrounds;
console.info(`Processing ${remainingBackgrounds.length} remaining backgrounds`);
for (let i = 0; i < remainingBackgrounds.length; i++) {
const background = remainingBackgrounds[i];
const base64Data = await blobToBase64(background.blob);
window.parent.postMessage({
type: 'BACKGROUND_DATA',
payload: {
id: background.id,
data: base64Data,
mediaType: background.type,
total: backgrounds.length,
processed: i + 1
}
}, '*');
await new Promise(resolve => setTimeout(resolve, 100));
}
window.parent.postMessage({ type: 'MIGRATION_COMPLETE' }, '*');
} catch (error: any) {
console.error('Extraction failed:', error);
window.parent.postMessage({
type: 'MIGRATION_ERROR',
error: error.message || 'Unknown error'
}, '*');
}
};
window.addEventListener('message', (event) => {
switch (event.data.type) {
case 'PING':
window.parent.postMessage({ type: 'PONG' }, '*');
break;
case 'START_MIGRATION':
startMigration();
break;
}
});
+2 -1
View File
@@ -60,7 +60,8 @@ export default defineConfig({
minify: false, minify: false,
rollupOptions: { rollupOptions: {
input: { input: {
settings: join(__dirname, 'src', 'interface', 'index.html') settings: join(__dirname, 'src', 'interface', 'index.html'),
migration: join(__dirname, 'src', 'seqta', 'utils', 'migration', 'migrate.html')
} }
} }
} }