Complete PHP-based workflow application for Ferrero DAM system: - OAuth2 authentication with automatic token management - Campaign discovery and filtering - Folder structure navigation - Asset download (individual and bulk) - Metadata extraction and display - Clean step-by-step web interface Status: Fully functional and production-ready 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
319 lines
No EOL
10 KiB
PHP
319 lines
No EOL
10 KiB
PHP
<?php
|
|
|
|
require_once 'ApiClient.php';
|
|
|
|
class AssetDownloader
|
|
{
|
|
private $apiClient;
|
|
private $downloadPath;
|
|
|
|
public function __construct($apiClient, $downloadPath = 'downloads/')
|
|
{
|
|
$this->apiClient = $apiClient;
|
|
$this->downloadPath = $downloadPath;
|
|
$this->ensureDownloadDirectory();
|
|
}
|
|
|
|
private function ensureDownloadDirectory()
|
|
{
|
|
if (!is_dir($this->downloadPath)) {
|
|
mkdir($this->downloadPath, 0755, true);
|
|
}
|
|
}
|
|
|
|
public function downloadAsset($assetId, $filename = null)
|
|
{
|
|
// First get asset details to determine filename and content URL
|
|
$assetDetails = $this->getAssetDetails($assetId);
|
|
|
|
if (!$assetDetails['success']) {
|
|
return [
|
|
'success' => false,
|
|
'error' => 'Failed to get asset details: ' . $assetDetails['error']
|
|
];
|
|
}
|
|
|
|
// Handle direct content download
|
|
if (isset($assetDetails['direct_content']) && $assetDetails['direct_content']) {
|
|
$safeFilename = $this->sanitizeFilename($filename ?: "asset_{$assetId}");
|
|
|
|
// Add extension based on content type
|
|
$contentType = $assetDetails['content_type'];
|
|
if (strpos($contentType, 'image/jpeg') !== false && !pathinfo($safeFilename, PATHINFO_EXTENSION)) {
|
|
$safeFilename .= '.jpg';
|
|
} elseif (strpos($contentType, 'image/png') !== false && !pathinfo($safeFilename, PATHINFO_EXTENSION)) {
|
|
$safeFilename .= '.png';
|
|
}
|
|
|
|
$fullPath = $this->downloadPath . $safeFilename;
|
|
$bytesWritten = file_put_contents($fullPath, $assetDetails['content_data']);
|
|
|
|
if ($bytesWritten === false) {
|
|
return [
|
|
'success' => false,
|
|
'error' => 'Failed to write file to disk'
|
|
];
|
|
}
|
|
|
|
return [
|
|
'success' => true,
|
|
'filename' => $safeFilename,
|
|
'filepath' => $fullPath,
|
|
'size' => $bytesWritten,
|
|
'asset_id' => $assetId,
|
|
'download_url' => "/v6/assets/{$assetId}/content",
|
|
'content_type' => $contentType
|
|
];
|
|
}
|
|
|
|
// Extract filename and content info for URL-based downloads
|
|
$downloadInfo = $this->extractDownloadInfo($assetDetails['data'], $filename);
|
|
|
|
if (!$downloadInfo['success']) {
|
|
return [
|
|
'success' => false,
|
|
'error' => 'Could not determine download URL: ' . $downloadInfo['error']
|
|
];
|
|
}
|
|
|
|
// Download the actual file content
|
|
return $this->downloadFile($downloadInfo['url'], $downloadInfo['filename'], $assetId);
|
|
}
|
|
|
|
private function getAssetDetails($assetId)
|
|
{
|
|
// Try multiple endpoints to get asset content info
|
|
$endpoints = [
|
|
"/v6/assets/{$assetId}/contents", // CORRECT content endpoint from Postman
|
|
"/v6/assets/{$assetId}?load_type=full",
|
|
"/v6/assets/{$assetId}?load_type=content",
|
|
"/v6/assets/{$assetId}"
|
|
];
|
|
|
|
$debugInfo = [];
|
|
|
|
foreach ($endpoints as $endpoint) {
|
|
$request = [
|
|
'method' => 'GET',
|
|
'url' => $endpoint
|
|
];
|
|
|
|
$response = $this->apiClient->executeRequest($request);
|
|
|
|
$debugInfo[] = [
|
|
'endpoint' => $endpoint,
|
|
'success' => $response['success'],
|
|
'http_code' => $response['http_code'] ?? 0,
|
|
'error' => $response['error'] ?? null,
|
|
'response_size' => strlen($response['body'] ?? '')
|
|
];
|
|
|
|
if ($response['success']) {
|
|
$data = json_decode($response['body'], true);
|
|
|
|
// If this is a direct content response, try to download it directly
|
|
if ($endpoint === "/v6/assets/{$assetId}/contents") {
|
|
return [
|
|
'success' => true,
|
|
'direct_content' => true,
|
|
'content_data' => $response['body'],
|
|
'content_type' => $response['headers']['Content-Type'] ?? 'application/octet-stream',
|
|
'debug_info' => $debugInfo
|
|
];
|
|
}
|
|
|
|
if ($data) {
|
|
return [
|
|
'success' => true,
|
|
'data' => $data,
|
|
'debug_info' => $debugInfo
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
return [
|
|
'success' => false,
|
|
'error' => 'Could not retrieve asset details from any endpoint',
|
|
'debug_info' => $debugInfo
|
|
];
|
|
}
|
|
|
|
private function extractDownloadInfo($assetData, $preferredFilename = null)
|
|
{
|
|
$filename = $preferredFilename;
|
|
$downloadUrl = null;
|
|
|
|
// Try to extract filename from various fields
|
|
if (!$filename) {
|
|
$filename = $assetData['name'] ??
|
|
$assetData['asset_content_info']['master_content']['content_file_name'] ??
|
|
$assetData['master_content_info']['content_file_name'] ??
|
|
'asset_' . ($assetData['asset_id'] ?? 'unknown');
|
|
}
|
|
|
|
// Try to find download URL from various content sources - prioritize signed URLs
|
|
$urlFields = [
|
|
// Signed URLs (most reliable)
|
|
'delivery_service_url',
|
|
'progressive_download_url',
|
|
'content_signed_url',
|
|
// Content info URLs
|
|
'asset_content_info.master_content.content_signed_url',
|
|
'asset_content_info.master_content.url',
|
|
'master_content_info.content_signed_url',
|
|
'master_content_info.url',
|
|
// Rendition URLs
|
|
'rendition_content.preview_content.content_signed_url',
|
|
'rendition_content.preview_content.url',
|
|
'rendition_content.thumbnail_content.content_signed_url',
|
|
'rendition_content.thumbnail_content.url',
|
|
// Generic URLs
|
|
'url',
|
|
'content_url'
|
|
];
|
|
|
|
foreach ($urlFields as $field) {
|
|
$url = $this->getNestedValue($assetData, $field);
|
|
if (!empty($url) && $url !== 'string') { // Filter out Postman placeholder values
|
|
$downloadUrl = $url;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!$downloadUrl) {
|
|
return [
|
|
'success' => false,
|
|
'error' => 'No download URL found in asset data',
|
|
'available_fields' => array_keys($assetData),
|
|
'debug_data' => $assetData
|
|
];
|
|
}
|
|
|
|
return [
|
|
'success' => true,
|
|
'url' => $downloadUrl,
|
|
'filename' => $filename
|
|
];
|
|
}
|
|
|
|
private function getNestedValue($array, $path)
|
|
{
|
|
$keys = explode('.', $path);
|
|
$current = $array;
|
|
|
|
foreach ($keys as $key) {
|
|
if (is_array($current) && isset($current[$key])) {
|
|
$current = $current[$key];
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return $current;
|
|
}
|
|
|
|
private function downloadFile($url, $filename, $assetId)
|
|
{
|
|
// Ensure filename has extension
|
|
if (!pathinfo($filename, PATHINFO_EXTENSION)) {
|
|
$filename .= '.jpg'; // Default for images
|
|
}
|
|
|
|
// Create safe filename
|
|
$safeFilename = $this->sanitizeFilename($filename);
|
|
$fullPath = $this->downloadPath . $safeFilename;
|
|
|
|
// Make the download request
|
|
$request = [
|
|
'method' => 'GET',
|
|
'url' => $url
|
|
];
|
|
|
|
$response = $this->apiClient->executeRequest($request);
|
|
|
|
if (!$response['success']) {
|
|
return [
|
|
'success' => false,
|
|
'error' => 'Download request failed: ' . ($response['error'] ?? 'Unknown error'),
|
|
'http_code' => $response['http_code'] ?? 0
|
|
];
|
|
}
|
|
|
|
// Save file to disk
|
|
$bytesWritten = file_put_contents($fullPath, $response['body']);
|
|
|
|
if ($bytesWritten === false) {
|
|
return [
|
|
'success' => false,
|
|
'error' => 'Failed to write file to disk'
|
|
];
|
|
}
|
|
|
|
return [
|
|
'success' => true,
|
|
'filename' => $safeFilename,
|
|
'filepath' => $fullPath,
|
|
'size' => $bytesWritten,
|
|
'asset_id' => $assetId,
|
|
'download_url' => $url
|
|
];
|
|
}
|
|
|
|
private function sanitizeFilename($filename)
|
|
{
|
|
// Remove dangerous characters and limit length
|
|
$safe = preg_replace('/[^a-zA-Z0-9._-]/', '_', $filename);
|
|
$safe = substr($safe, 0, 200); // Limit length
|
|
|
|
// Ensure it doesn't start with a dot
|
|
if (strpos($safe, '.') === 0) {
|
|
$safe = 'file_' . $safe;
|
|
}
|
|
|
|
return $safe;
|
|
}
|
|
|
|
public function downloadMultipleAssets($assetIds, $folderName = 'bulk_download')
|
|
{
|
|
$results = [];
|
|
$successCount = 0;
|
|
$totalSize = 0;
|
|
|
|
// Create subfolder for this download batch
|
|
$batchPath = $this->downloadPath . $this->sanitizeFilename($folderName) . '/';
|
|
if (!is_dir($batchPath)) {
|
|
mkdir($batchPath, 0755, true);
|
|
}
|
|
|
|
$originalPath = $this->downloadPath;
|
|
$this->downloadPath = $batchPath;
|
|
|
|
foreach ($assetIds as $assetId) {
|
|
$result = $this->downloadAsset($assetId);
|
|
$results[] = $result;
|
|
|
|
if ($result['success']) {
|
|
$successCount++;
|
|
$totalSize += $result['size'];
|
|
}
|
|
}
|
|
|
|
$this->downloadPath = $originalPath; // Restore original path
|
|
|
|
return [
|
|
'success' => $successCount > 0,
|
|
'total_assets' => count($assetIds),
|
|
'successful_downloads' => $successCount,
|
|
'failed_downloads' => count($assetIds) - $successCount,
|
|
'total_size' => $totalSize,
|
|
'download_folder' => $batchPath,
|
|
'individual_results' => $results
|
|
];
|
|
}
|
|
|
|
public function getDownloadPath()
|
|
{
|
|
return $this->downloadPath;
|
|
}
|
|
} |