mirror of
https://github.com/mudabbir-ahmad/Media-Aggregation-and-Sorting-System.git
synced 2026-10-08 03:40:20 +00:00
feat: enhance database service with centralized error logging and improve media handling across services
This commit is contained in:
1 parent
04046702d3
commit
ad0cc1a7f5
5 files changed
+384
-165
No files matched your search
+81
-5
@@ -1,6 +1,7 @@
|
||||
import * as AuthSession from 'expo-auth-session';
|
||||
import * as WebBrowser from 'expo-web-browser';
|
||||
import * as Application from 'expo-application';
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import { AuthServiceBase } from './AuthServiceBase';
|
||||
import { logError } from '../services/ErrorHandler';
|
||||
|
||||
@@ -29,6 +30,8 @@ const SCOPES = [
|
||||
|
||||
const PICKER_API = 'https://photospicker.googleapis.com/v1';
|
||||
|
||||
const CACHE_DIR = `${FileSystem.cacheDirectory}google_photos/`;
|
||||
|
||||
const REDIRECT_URI = `${Application.applicationId}:/oauthredirect`;
|
||||
|
||||
class GoogleAUTH extends AuthServiceBase {
|
||||
@@ -276,6 +279,10 @@ class GoogleAUTH extends AuthServiceBase {
|
||||
|
||||
/**
|
||||
* Normalise Picker API media items to the app's standard format.
|
||||
*
|
||||
* NOTE: The Picker API's baseUrl requires an Authorization header to
|
||||
* fetch the image. We store it in `remote_path` / `picker_base_url`
|
||||
* so the caller can download it to a local cache file for rendering.
|
||||
*/
|
||||
_normalisePickerItems(items) {
|
||||
return (items || []).map((item) => {
|
||||
@@ -286,14 +293,80 @@ class GoogleAUTH extends AuthServiceBase {
|
||||
source_id: item.id || file.id,
|
||||
title: file.filename || 'Untitled',
|
||||
description: '',
|
||||
local_path: file.baseUrl ? `${file.baseUrl}=w400-h400` : null,
|
||||
// local_path is null until we download it with the auth token
|
||||
local_path: null,
|
||||
remote_path: file.baseUrl || '',
|
||||
picker_base_url: file.baseUrl || null,
|
||||
media_type: isVideo ? 'video' : 'photo',
|
||||
mime_type: file.mimeType || null,
|
||||
created_date: file.mediaFileMetadata?.creationTime || null,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Download Picker images to local cache so <Image> can render them.
|
||||
*
|
||||
* The Picker API's baseUrl requires an Authorization header. React
|
||||
* Native's <Image> can't add custom headers reliably, so we download
|
||||
* each photo to a cache file and set local_path to the file:// URI.
|
||||
*
|
||||
* @param {Array} items — normalised items from _normalisePickerItems
|
||||
* @returns {Array} — same items with local_path populated
|
||||
*/
|
||||
async downloadPickerPhotos(items) {
|
||||
const token = await this.getAccessToken();
|
||||
|
||||
// Ensure cache directory exists
|
||||
const dirInfo = await FileSystem.getInfoAsync(CACHE_DIR);
|
||||
if (!dirInfo.exists) {
|
||||
await FileSystem.makeDirectoryAsync(CACHE_DIR, { intermediates: true });
|
||||
}
|
||||
|
||||
const results = [];
|
||||
for (const item of items) {
|
||||
if (!item.picker_base_url) {
|
||||
results.push(item);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// Use the source_id as a safe filename
|
||||
const safeId = (item.source_id || 'photo').replace(/[^a-zA-Z0-9_-]/g, '_');
|
||||
const ext = item.mime_type?.includes('png') ? '.png'
|
||||
: item.mime_type?.includes('gif') ? '.gif'
|
||||
: item.mime_type?.includes('webp') ? '.webp'
|
||||
: '.jpg';
|
||||
const localUri = `${CACHE_DIR}${safeId}${ext}`;
|
||||
|
||||
// Skip download if already cached
|
||||
const fileInfo = await FileSystem.getInfoAsync(localUri);
|
||||
if (fileInfo.exists && fileInfo.size > 0) {
|
||||
results.push({ ...item, local_path: localUri });
|
||||
continue;
|
||||
}
|
||||
|
||||
// Download with auth header — append =d to get the full-res download
|
||||
const downloadUrl = `${item.picker_base_url}=d`;
|
||||
const download = await FileSystem.downloadAsync(downloadUrl, localUri, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
if (download.status === 200) {
|
||||
results.push({ ...item, local_path: download.uri });
|
||||
} else {
|
||||
logError('Download Picker Photo', `HTTP ${download.status} for ${item.title}`);
|
||||
results.push(item);
|
||||
}
|
||||
} catch (err) {
|
||||
logError('Download Picker Photo', err);
|
||||
results.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Full interactive flow: create session → open picker in browser →
|
||||
* wait for user to select photos → fetch the selected items.
|
||||
@@ -339,11 +412,13 @@ class GoogleAUTH extends AuthServiceBase {
|
||||
};
|
||||
}
|
||||
|
||||
// 4. Fetch selected items
|
||||
// 4. Fetch selected items and download to local cache
|
||||
const result = await this._fetchPickerMediaItems(session.id, 100, null);
|
||||
const mediaItems = this._normalisePickerItems(result.mediaItems);
|
||||
let mediaItems = this._normalisePickerItems(result.mediaItems);
|
||||
|
||||
console.log(`Picker returned ${mediaItems.length} selected items`);
|
||||
console.log(`Picker returned ${mediaItems.length} selected items, downloading…`);
|
||||
mediaItems = await this.downloadPickerPhotos(mediaItems);
|
||||
console.log(`Downloaded ${mediaItems.filter((m) => m.local_path).length} photos to cache`);
|
||||
|
||||
return {
|
||||
mediaItems,
|
||||
@@ -366,7 +441,8 @@ class GoogleAUTH extends AuthServiceBase {
|
||||
}
|
||||
|
||||
const result = await this._fetchPickerMediaItems(sessionId, pageSize, pageToken);
|
||||
const mediaItems = this._normalisePickerItems(result.mediaItems);
|
||||
let mediaItems = this._normalisePickerItems(result.mediaItems);
|
||||
mediaItems = await this.downloadPickerPhotos(mediaItems);
|
||||
|
||||
return {
|
||||
mediaItems,
|
||||
|
||||
Reference in new issue
Block a user