396 lines
14 KiB
PHP
396 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* Flickr API Client - fetches photos from your Flickr account
|
|
* Compatible with PHP 7.2+
|
|
* Supports OAuth for accessing original quality photos
|
|
*/
|
|
|
|
class FlickrAPI
|
|
{
|
|
private $apiKey;
|
|
private $apiSecret;
|
|
private $userId;
|
|
private $baseUrl = 'https://api.flickr.com/services/rest/';
|
|
private $oauth = null;
|
|
|
|
public function __construct($apiKey, $apiSecret, $userId = '')
|
|
{
|
|
$this->apiKey = $apiKey;
|
|
$this->apiSecret = $apiSecret;
|
|
$this->userId = $userId;
|
|
}
|
|
|
|
/**
|
|
* Set OAuth handler for authenticated requests
|
|
*/
|
|
public function setOAuth($oauth)
|
|
{
|
|
$this->oauth = $oauth;
|
|
}
|
|
|
|
/**
|
|
* Check if OAuth is available
|
|
*/
|
|
public function hasOAuth()
|
|
{
|
|
return $this->oauth !== null && $this->oauth->isAuthorized();
|
|
}
|
|
|
|
/**
|
|
* Set user ID
|
|
*/
|
|
public function setUserId($userId)
|
|
{
|
|
$this->userId = $userId;
|
|
}
|
|
|
|
/**
|
|
* Make API request (with OAuth if available)
|
|
*
|
|
* @param string $method Flickr API method
|
|
* @param array $params Additional parameters
|
|
* @param bool $useOAuth Force OAuth for this request
|
|
* @return array Response data
|
|
*/
|
|
private function request($method, $params = [], $useOAuth = false)
|
|
{
|
|
$params = array_merge([
|
|
'method' => $method,
|
|
'api_key' => $this->apiKey,
|
|
'format' => 'json',
|
|
'nojsoncallback' => 1,
|
|
], $params);
|
|
|
|
// Use OAuth if available and requested
|
|
if (($useOAuth || $this->hasOAuth()) && $this->oauth !== null) {
|
|
return $this->requestWithOAuth($method, $params);
|
|
}
|
|
|
|
$url = $this->baseUrl . '?' . http_build_query($params);
|
|
|
|
$context = stream_context_create([
|
|
'http' => [
|
|
'timeout' => 30,
|
|
'user_agent' => 'VH_Posting_System/1.0',
|
|
],
|
|
]);
|
|
|
|
$response = @file_get_contents($url, false, $context);
|
|
|
|
if ($response === false) {
|
|
throw new RuntimeException('Failed to connect to Flickr API');
|
|
}
|
|
|
|
$data = json_decode($response, true);
|
|
|
|
if ($data === null) {
|
|
throw new RuntimeException('Invalid JSON response from Flickr API');
|
|
}
|
|
|
|
if (isset($data['stat']) && $data['stat'] === 'fail') {
|
|
throw new RuntimeException('Flickr API error: ' . (isset($data['message']) ? $data['message'] : 'Unknown error'));
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Make OAuth-signed API request
|
|
*/
|
|
private function requestWithOAuth($method, $params)
|
|
{
|
|
$params['method'] = $method;
|
|
$params['format'] = 'json';
|
|
$params['nojsoncallback'] = 1;
|
|
|
|
$oauthParams = $this->oauth->signRequest('GET', $this->baseUrl, $params);
|
|
$allParams = array_merge($params, $oauthParams);
|
|
|
|
$url = $this->baseUrl . '?' . http_build_query($allParams);
|
|
|
|
$ch = curl_init();
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_URL => $url,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 30,
|
|
CURLOPT_SSL_VERIFYPEER => true,
|
|
]);
|
|
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($response === false || $httpCode !== 200) {
|
|
throw new RuntimeException('Failed to connect to Flickr API (OAuth)');
|
|
}
|
|
|
|
$data = json_decode($response, true);
|
|
|
|
if ($data === null) {
|
|
throw new RuntimeException('Invalid JSON response from Flickr API');
|
|
}
|
|
|
|
if (isset($data['stat']) && $data['stat'] === 'fail') {
|
|
throw new RuntimeException('Flickr API error: ' . (isset($data['message']) ? $data['message'] : 'Unknown error'));
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Get user's photostream
|
|
*
|
|
* @param int $page Page number
|
|
* @param int $perPage Photos per page (max 500)
|
|
* @return array Photos data
|
|
*/
|
|
public function getPhotos($page = 1, $perPage = 50)
|
|
{
|
|
$response = $this->request('flickr.people.getPhotos', [
|
|
'user_id' => $this->userId ? $this->userId : 'me',
|
|
'page' => $page,
|
|
'per_page' => $perPage,
|
|
'extras' => 'url_sq,url_t,url_s,url_m,url_z,url_l,url_k,url_o,description,date_upload,date_taken,owner_name,original_format',
|
|
]);
|
|
|
|
return $this->normalizePhotosResponse(isset($response['photos']) ? $response['photos'] : []);
|
|
}
|
|
|
|
/**
|
|
* Get user's photosets (albums)
|
|
*
|
|
* @param int $page Page number
|
|
* @param int $perPage Albums per page
|
|
* @return array Photosets data with pagination info
|
|
*/
|
|
public function getPhotosets($page = 1, $perPage = 50)
|
|
{
|
|
$response = $this->request('flickr.photosets.getList', [
|
|
'user_id' => $this->userId,
|
|
'page' => $page,
|
|
'per_page' => $perPage,
|
|
'primary_photo_extras' => 'url_sq,url_t,url_s,url_m',
|
|
]);
|
|
|
|
$photosets = isset($response['photosets']) ? $response['photosets'] : [];
|
|
|
|
return [
|
|
'albums' => isset($photosets['photoset']) ? $photosets['photoset'] : [],
|
|
'page' => (int)($photosets['page'] ?? $page),
|
|
'pages' => (int)($photosets['pages'] ?? 1),
|
|
'perpage' => (int)($photosets['perpage'] ?? $perPage),
|
|
'total' => (int)($photosets['total'] ?? 0),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get photos from a specific photoset (album)
|
|
*
|
|
* @param string $photosetId Photoset ID
|
|
* @param int $page Page number
|
|
* @param int $perPage Photos per page
|
|
* @return array Photos data
|
|
*/
|
|
public function getPhotosetPhotos($photosetId, $page = 1, $perPage = 50)
|
|
{
|
|
$response = $this->request('flickr.photosets.getPhotos', [
|
|
'photoset_id' => $photosetId,
|
|
'user_id' => $this->userId,
|
|
'page' => $page,
|
|
'per_page' => $perPage,
|
|
'extras' => 'url_sq,url_t,url_s,url_m,url_z,url_l,url_k,url_o,description,date_upload,date_taken,original_format,media,path_alias,owner_name',
|
|
]);
|
|
|
|
// Get owner info from photoset response
|
|
$ownername = isset($response['photoset']['ownername']) ? $response['photoset']['ownername'] : '';
|
|
$owner = isset($response['photoset']['owner']) ? $response['photoset']['owner'] : $this->userId;
|
|
|
|
return $this->normalizePhotosResponse(isset($response['photoset']) ? $response['photoset'] : [], $ownername, $owner);
|
|
}
|
|
|
|
/**
|
|
* Get info about a specific photo
|
|
*
|
|
* @param string $photoId Photo ID
|
|
* @return array Photo info
|
|
*/
|
|
public function getPhotoInfo($photoId)
|
|
{
|
|
$response = $this->request('flickr.photos.getInfo', [
|
|
'photo_id' => $photoId,
|
|
]);
|
|
|
|
return isset($response['photo']) ? $response['photo'] : [];
|
|
}
|
|
|
|
/**
|
|
* Get available sizes for a photo (uses OAuth if available)
|
|
*
|
|
* @param string $photoId Photo ID
|
|
* @return array Available sizes
|
|
*/
|
|
public function getPhotoSizes($photoId)
|
|
{
|
|
// getSizes requires OAuth to return Original for private/restricted photos
|
|
$response = $this->request('flickr.photos.getSizes', [
|
|
'photo_id' => $photoId,
|
|
], true); // Force OAuth if available
|
|
|
|
$sizes = [];
|
|
$sizeList = isset($response['sizes']['size']) ? $response['sizes']['size'] : [];
|
|
foreach ($sizeList as $size) {
|
|
$sizes[$size['label']] = [
|
|
'url' => $size['source'],
|
|
'width' => (int)$size['width'],
|
|
'height' => (int)$size['height'],
|
|
];
|
|
}
|
|
|
|
return $sizes;
|
|
}
|
|
|
|
/**
|
|
* Get original URL for a photo (requires OAuth)
|
|
*
|
|
* @param string $photoId Photo ID
|
|
* @return string|null Original URL or null if not available
|
|
*/
|
|
public function getOriginalUrl($photoId)
|
|
{
|
|
try {
|
|
$sizes = $this->getPhotoSizes($photoId);
|
|
|
|
// Try Original first, then fall back to largest available
|
|
if (isset($sizes['Original'])) {
|
|
return $sizes['Original']['url'];
|
|
}
|
|
if (isset($sizes['Large 2048'])) {
|
|
return $sizes['Large 2048']['url'];
|
|
}
|
|
if (isset($sizes['Large 1600'])) {
|
|
return $sizes['Large 1600']['url'];
|
|
}
|
|
if (isset($sizes['Large'])) {
|
|
return $sizes['Large']['url'];
|
|
}
|
|
|
|
return null;
|
|
} catch (Exception $e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Search user's photos
|
|
*
|
|
* @param string $query Search query
|
|
* @param int $page Page number
|
|
* @param int $perPage Photos per page
|
|
* @return array Photos data
|
|
*/
|
|
public function searchPhotos($query, $page = 1, $perPage = 50)
|
|
{
|
|
$response = $this->request('flickr.photos.search', [
|
|
'user_id' => $this->userId ? $this->userId : 'me',
|
|
'text' => $query,
|
|
'page' => $page,
|
|
'per_page' => $perPage,
|
|
'extras' => 'url_sq,url_t,url_s,url_m,url_z,url_l,url_k,url_o,description,date_upload,date_taken,original_format',
|
|
]);
|
|
|
|
return $this->normalizePhotosResponse(isset($response['photos']) ? $response['photos'] : []);
|
|
}
|
|
|
|
/**
|
|
* Find user ID by username
|
|
*
|
|
* @param string $username Flickr username
|
|
* @return string User ID
|
|
*/
|
|
public function findUserByUsername($username)
|
|
{
|
|
$response = $this->request('flickr.people.findByUsername', [
|
|
'username' => $username,
|
|
]);
|
|
|
|
return isset($response['user']['nsid']) ? $response['user']['nsid'] : '';
|
|
}
|
|
|
|
/**
|
|
* Normalize photos response to consistent format
|
|
*/
|
|
private function normalizePhotosResponse($response, $defaultOwnerName = '', $defaultOwner = '')
|
|
{
|
|
$photos = [];
|
|
$photoList = isset($response['photo']) ? $response['photo'] : [];
|
|
|
|
foreach ($photoList as $photo) {
|
|
$farm = isset($photo['farm']) ? $photo['farm'] : '';
|
|
$server = $photo['server'];
|
|
$id = $photo['id'];
|
|
$originalSecret = isset($photo['originalsecret']) ? $photo['originalsecret'] : $photo['secret'];
|
|
$originalFormat = isset($photo['originalformat']) ? $photo['originalformat'] : 'jpg';
|
|
|
|
// Get original URL - ONLY use if API returns it
|
|
// If url_o is not returned, originals are blocked by Flickr privacy settings
|
|
$originalUrl = isset($photo['url_o']) ? $photo['url_o'] : null;
|
|
|
|
// Get large 2048 URL (url_k) - from API or construct it
|
|
// This is the best quality available when originals are blocked
|
|
$large2048Url = isset($photo['url_k']) ? $photo['url_k'] : null;
|
|
|
|
// Construct large2048 URL if not provided (usually works)
|
|
if (!$large2048Url && $server) {
|
|
$large2048Url = "https://live.staticflickr.com/{$server}/{$id}_{$photo['secret']}_k.jpg";
|
|
}
|
|
|
|
// Determine media type (photo or video)
|
|
$mediaType = isset($photo['media']) ? $photo['media'] : 'photo';
|
|
$isVideo = ($mediaType === 'video');
|
|
|
|
// Build page URL - use path_alias, ownername, or owner NSID
|
|
$pathAlias = isset($photo['pathalias']) && $photo['pathalias'] ? $photo['pathalias'] : '';
|
|
$ownerName = isset($photo['ownername']) ? $photo['ownername'] : $defaultOwnerName;
|
|
$owner = isset($photo['owner']) ? $photo['owner'] : ($defaultOwner ? $defaultOwner : $this->userId);
|
|
|
|
// Prefer path_alias, then ownername, then owner NSID (URL encoded)
|
|
$userPath = $pathAlias ? $pathAlias : ($ownerName ? $ownerName : rawurlencode($owner));
|
|
$pageUrl = "https://www.flickr.com/photos/{$userPath}/{$id}/";
|
|
|
|
$photos[] = [
|
|
'id' => $id,
|
|
'secret' => $photo['secret'],
|
|
'server' => $server,
|
|
'farm' => $farm,
|
|
'title' => isset($photo['title']) ? $photo['title'] : 'Untitled',
|
|
'description' => isset($photo['description']['_content']) ? $photo['description']['_content'] : '',
|
|
'date_upload' => isset($photo['dateupload']) ? $photo['dateupload'] : '',
|
|
'date_taken' => isset($photo['datetaken']) ? $photo['datetaken'] : '',
|
|
'original_format' => $originalFormat,
|
|
'original_secret' => $originalSecret,
|
|
'media' => $mediaType,
|
|
'is_video' => $isVideo,
|
|
'urls' => [
|
|
'square' => isset($photo['url_sq']) ? $photo['url_sq'] : null,
|
|
'thumbnail' => isset($photo['url_t']) ? $photo['url_t'] : null,
|
|
'small' => isset($photo['url_s']) ? $photo['url_s'] : null,
|
|
'medium' => isset($photo['url_m']) ? $photo['url_m'] : null,
|
|
'medium640' => isset($photo['url_z']) ? $photo['url_z'] : null,
|
|
'large' => isset($photo['url_l']) ? $photo['url_l'] : null,
|
|
'large2048' => $large2048Url,
|
|
'original' => $originalUrl,
|
|
],
|
|
'page_url' => $pageUrl,
|
|
];
|
|
}
|
|
|
|
return [
|
|
'photos' => $photos,
|
|
'page' => (int)(isset($response['page']) ? $response['page'] : 1),
|
|
'pages' => (int)(isset($response['pages']) ? $response['pages'] : 1),
|
|
'perpage' => (int)(isset($response['perpage']) ? $response['perpage'] : count($photos)),
|
|
'total' => (int)(isset($response['total']) ? $response['total'] : count($photos)),
|
|
];
|
|
}
|
|
}
|