mirror of
https://github.com/mudabbir-ahmad/Media-Aggregation-and-Sorting-System.git
synced 2026-10-07 19:40:20 +00:00
feat: integrate device media service for local photo access and update service icons/names
This commit is contained in:
1 parent
e5dbdfeba3
commit
0ef59ba5d3
9 files changed
+313
-94
No files matched your search
@@ -0,0 +1,140 @@
|
||||
import * as MediaLibrary from 'expo-media-library';
|
||||
|
||||
/**
|
||||
* Fetches photos and videos directly from the Android device media store.
|
||||
*
|
||||
* This replaces the deprecated Google Photos Library REST API.
|
||||
* On Android, the media store includes all photos visible to the device —
|
||||
* camera roll, screenshots, downloads, and images synced by the Google
|
||||
* Photos app. No OAuth token is needed; only the MEDIA_LIBRARY permission.
|
||||
*/
|
||||
const DeviceMediaService = {
|
||||
/**
|
||||
* Request media-library permission (camera roll / storage).
|
||||
* Returns true if granted.
|
||||
*/
|
||||
requestPermission: async () => {
|
||||
const { status } = await MediaLibrary.requestPermissionsAsync();
|
||||
if (status !== 'granted') {
|
||||
console.warn('Media library permission denied');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch a page of media assets from the device.
|
||||
*
|
||||
* @param {number} pageSize Number of items per page (default 100).
|
||||
* @param {string} after Cursor returned by a previous call (for pagination).
|
||||
* @param {string} mediaType 'photo' | 'video' | 'all' (default 'all').
|
||||
* @returns {{ mediaItems: Array, endCursor: string|null, hasNextPage: boolean }}
|
||||
*/
|
||||
getPhotos: async (pageSize = 100, after = null, mediaType = 'all') => {
|
||||
const hasPermission = await DeviceMediaService.requestPermission();
|
||||
if (!hasPermission) {
|
||||
return { mediaItems: [], endCursor: null, hasNextPage: false };
|
||||
}
|
||||
|
||||
const mediaTypeFilter =
|
||||
mediaType === 'photo' ? MediaLibrary.MediaType.photo
|
||||
: mediaType === 'video' ? MediaLibrary.MediaType.video
|
||||
: [MediaLibrary.MediaType.photo, MediaLibrary.MediaType.video];
|
||||
|
||||
const options = {
|
||||
first: pageSize,
|
||||
mediaType: mediaTypeFilter,
|
||||
sortBy: [[MediaLibrary.SortBy.creationTime, false]], // newest first
|
||||
};
|
||||
if (after) options.after = after;
|
||||
|
||||
const result = await MediaLibrary.getAssetsAsync(options);
|
||||
|
||||
const mediaItems = result.assets.map((asset) => ({
|
||||
source_service: 'DEVICE',
|
||||
source_id: asset.id,
|
||||
title: asset.filename || 'Untitled',
|
||||
description: '',
|
||||
local_path: asset.uri,
|
||||
remote_path: '',
|
||||
media_type: asset.mediaType === 'video' ? 'video' : 'photo',
|
||||
created_date: asset.creationTime
|
||||
? new Date(asset.creationTime).toISOString()
|
||||
: null,
|
||||
width: asset.width,
|
||||
height: asset.height,
|
||||
duration: asset.duration || 0,
|
||||
}));
|
||||
|
||||
return {
|
||||
mediaItems,
|
||||
endCursor: result.endCursor || null,
|
||||
hasNextPage: result.hasNextPage || false,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch all albums from the device (including smart albums).
|
||||
*/
|
||||
getAlbums: async () => {
|
||||
const hasPermission = await DeviceMediaService.requestPermission();
|
||||
if (!hasPermission) return [];
|
||||
|
||||
const albums = await MediaLibrary.getAlbumsAsync({
|
||||
includeSmartAlbums: true,
|
||||
});
|
||||
|
||||
return albums.map((album) => ({
|
||||
id: album.id,
|
||||
title: album.title,
|
||||
count: album.assetCount,
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch assets inside a specific album.
|
||||
*/
|
||||
getAlbumPhotos: async (albumId, pageSize = 100, after = null) => {
|
||||
const hasPermission = await DeviceMediaService.requestPermission();
|
||||
if (!hasPermission) {
|
||||
return { mediaItems: [], endCursor: null, hasNextPage: false };
|
||||
}
|
||||
|
||||
const album = await MediaLibrary.getAlbumAsync(albumId);
|
||||
if (!album) return { mediaItems: [], endCursor: null, hasNextPage: false };
|
||||
|
||||
const options = {
|
||||
first: pageSize,
|
||||
album,
|
||||
sortBy: [[MediaLibrary.SortBy.creationTime, false]],
|
||||
};
|
||||
if (after) options.after = after;
|
||||
|
||||
const result = await MediaLibrary.getAssetsAsync(options);
|
||||
|
||||
const mediaItems = result.assets.map((asset) => ({
|
||||
source_service: 'DEVICE',
|
||||
source_id: asset.id,
|
||||
title: asset.filename || 'Untitled',
|
||||
description: '',
|
||||
local_path: asset.uri,
|
||||
remote_path: '',
|
||||
media_type: asset.mediaType === 'video' ? 'video' : 'photo',
|
||||
created_date: asset.creationTime
|
||||
? new Date(asset.creationTime).toISOString()
|
||||
: null,
|
||||
width: asset.width,
|
||||
height: asset.height,
|
||||
duration: asset.duration || 0,
|
||||
}));
|
||||
|
||||
return {
|
||||
mediaItems,
|
||||
endCursor: result.endCursor || null,
|
||||
hasNextPage: result.hasNextPage || false,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export default DeviceMediaService;
|
||||
|
||||
Reference in new issue
Block a user