feat: initial commit for MediaGen AI project

This commit is contained in:
David Frassi
2026-08-21 18:26:12 +02:00
commit d8e0d0f4c1
32 changed files with 18723 additions and 0 deletions
+180
View File
@@ -0,0 +1,180 @@
import { Injectable } from '@angular/core';
import { GeneratedMedia } from '../models/openrouter.models';
@Injectable({
providedIn: 'root'
})
export class StorageService {
private readonly ACTIVE_PROVIDER = 'ai_active_provider';
private readonly API_KEY_OPENROUTER = 'openrouter_api_key';
private readonly API_KEY_REPLICATE = 'replicate_api_key';
private readonly SELECTED_MODEL = 'openrouter_selected_model';
private readonly MEDIA_HISTORY = 'openrouter_media_history';
private readonly DEFAULT_ENV_API_KEY = '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';
// Cache in-memory per preservare i dataURL dei video ed audio per la sessione corrente
private urlCache = new Map<string, string>();
getActiveProvider(): 'openrouter' | 'replicate' {
const p = localStorage.getItem(this.ACTIVE_PROVIDER);
return (p === 'replicate') ? 'replicate' : 'openrouter';
}
setActiveProvider(provider: 'openrouter' | 'replicate'): void {
localStorage.setItem(this.ACTIVE_PROVIDER, provider);
}
getApiKey(provider?: 'openrouter' | 'replicate'): string {
const p = provider || this.getActiveProvider();
if (p === 'replicate') {
return localStorage.getItem(this.API_KEY_REPLICATE) || '';
}
return localStorage.getItem(this.API_KEY_OPENROUTER) || this.DEFAULT_ENV_API_KEY;
}
setApiKey(key: string, provider?: 'openrouter' | 'replicate'): void {
const p = provider || this.getActiveProvider();
if (p === 'replicate') {
localStorage.setItem(this.API_KEY_REPLICATE, key.trim());
} else {
localStorage.setItem(this.API_KEY_OPENROUTER, key.trim());
}
}
private readonly REPLICATE_BUDGET = 'replicate_initial_budget';
private readonly REPLICATE_USAGE = 'replicate_total_usage';
getReplicateBudget(): number {
const val = localStorage.getItem(this.REPLICATE_BUDGET);
return val !== null ? parseFloat(val) : 11.72; // Credito rimanente Replicate
}
setReplicateBudget(budget: number): void {
localStorage.setItem(this.REPLICATE_BUDGET, Math.max(0, budget).toString());
}
getReplicateUsage(): number {
const val = localStorage.getItem(this.REPLICATE_USAGE);
return val ? parseFloat(val) || 0 : 0.07;
}
getReplicateRemainingCredits(): number {
const currentRemaining = localStorage.getItem('replicate_remaining_credits');
if (currentRemaining !== null) {
return Math.max(0, parseFloat(currentRemaining) || 0);
}
const budget = this.getReplicateBudget();
return Math.max(0, budget);
}
setReplicateRemainingCredits(credits: number): void {
localStorage.setItem('replicate_remaining_credits', Math.max(0, credits).toString());
}
addReplicateUsage(amount: number): number {
const currentUsage = this.getReplicateUsage();
const updatedUsage = Math.max(0, currentUsage + amount);
localStorage.setItem(this.REPLICATE_USAGE, updatedUsage.toString());
// Scala direttamente dai crediti rimanenti Replicate
const currentRemaining = this.getReplicateRemainingCredits();
const updatedRemaining = Math.max(0, currentRemaining - amount);
this.setReplicateRemainingCredits(updatedRemaining);
return updatedUsage;
}
getSelectedModel(): string {
return localStorage.getItem(this.SELECTED_MODEL) || 'recraft-ai/recraft-v3';
}
setSelectedModel(model: string): void {
localStorage.setItem(this.SELECTED_MODEL, model);
}
getHistory(): GeneratedMedia[] {
try {
const data = localStorage.getItem(this.MEDIA_HISTORY);
const rawList: GeneratedMedia[] = data ? JSON.parse(data) : [];
return rawList.map(item => {
let costDisplay = item.costDisplay;
// Fix per eventuali elementi salvati precedentemente con il tempo in secondi invece del costo in dollari
if (costDisplay) {
const numMatch = costDisplay.match(/[\d.]+/);
if (numMatch && parseFloat(numMatch[0]) > 20) {
// Era il tempo di predict in secondi (es. 129s) invece del costo reale
const durationEst = 5;
costDisplay = `$${(0.067 * durationEst).toFixed(4)}`;
}
}
return {
...item,
costDisplay: costDisplay || '$0.3350',
url: item.url || this.urlCache.get(item.id) || ''
};
});
} catch (e) {
console.warn('Impossibile leggere lo storico da localStorage', e);
return [];
}
}
saveMediaItem(item: GeneratedMedia): void {
if (item.id && item.url) {
this.urlCache.set(item.id, item.url);
}
let history = this.getHistory();
// Per evitare di superare i 5MB di quota di localStorage, rimuovi la stringa base64 se supera i 50KB nello storage su disco
const cleanItem = { ...item };
if (cleanItem.url && cleanItem.url.startsWith('data:') && cleanItem.url.length > 50000) {
cleanItem.url = '';
}
history.unshift(cleanItem);
if (history.length > 15) {
history = history.slice(0, 15);
}
this.persistHistory(history);
}
private persistHistory(history: GeneratedMedia[]): void {
let list = history.map(item => {
if (item.url && item.url.startsWith('data:') && item.url.length > 50000) {
return { ...item, url: '' };
}
return item;
});
while (list.length > 0) {
try {
localStorage.setItem(this.MEDIA_HISTORY, JSON.stringify(list));
return;
} catch (e) {
list = list.map(item => (item.url && item.url.startsWith('data:') ? { ...item, url: '' } : item));
list.pop();
}
}
try {
localStorage.removeItem(this.MEDIA_HISTORY);
} catch (_) {}
}
clearHistory(): void {
this.urlCache.clear();
try {
localStorage.removeItem(this.MEDIA_HISTORY);
} catch (_) {}
}
deleteItem(id: string): void {
this.urlCache.delete(id);
const history = this.getHistory().filter(item => item.id !== id);
this.persistHistory(history);
}
}