@musallam/lightroom-client
    Preparing search index...

    @musallam/lightroom-client

    @musallam/lightroom-client

    TypeScript client library for the Adobe Lightroom API - programmatically apply Lightroom settings and image adjustments to photos.

    • 🚀 Full TypeScript support with auto-generated types from OpenAPI spec
    • 📦 Complete API coverage for all Lightroom endpoints
    • 🔄 Job polling utilities for async operations
    • 🔐 Built-in authentication via IMS client
    • 📝 Comprehensive documentation and examples
    npm install @musallam/lightroom-client
    
    import {
    LightroomClient,
    LIGHTROOM_AXIOS_INSTANCE,
    TokenIMSClient,
    pollLightroomJob,
    } from '@musallam/lightroom-client';

    // 1. Setup authentication
    const imsClient = new TokenIMSClient({
    clientId: 'YOUR_CLIENT_ID',
    clientSecret: 'YOUR_CLIENT_SECRET',
    scopes: ['openid', 'AdobeID', 'firefly_api', 'ff_apis'],
    });

    // 2. Configure axios instance
    LIGHTROOM_AXIOS_INSTANCE.interceptors.request.use(async (config) => {
    const token = await imsClient.getAccessToken();
    config.headers.Authorization = `Bearer ${token}`;
    config.headers['x-api-key'] = 'YOUR_CLIENT_ID';
    return config;
    });

    // 3. Apply auto-tone to an image
    const job = await LightroomClient.autoTone({
    inputs: {
    href: 'https://your-bucket.s3.amazonaws.com/image.jpg',
    storage: 'external',
    },
    outputs: [
    {
    href: 'https://your-bucket.s3.amazonaws.com/output.jpg',
    storage: 'external',
    },
    ],
    });

    // 4. Poll for results
    const result = await pollLightroomJob(job, {
    onProgress: (status) => console.log(`Status: ${status.status}`),
    });

    console.log('Processed image:', result.outputs?.[0]?.href);

    The client provides full TypeScript support for:

    • presets() - Apply Lightroom preset
    • xmp() - Apply XMP settings
    • autoStraighten() - Auto-straighten image
    • autoTone() - Auto-adjust image tone
    • edit() - Apply manual image edits
    • acrstatus() - Get job status
    • pollLightroomJob() - Poll until completion (utility)
    const job = await LightroomClient.presets({
    inputs: {
    source: {
    href: 'https://your-bucket.s3.amazonaws.com/photo.jpg',
    storage: 'external',
    },
    presets: [
    {
    href: 'https://your-bucket.s3.amazonaws.com/preset.xmp',
    storage: 'external',
    },
    ],
    },
    outputs: [
    {
    href: 'https://your-bucket.s3.amazonaws.com/result.jpg',
    storage: 'external',
    },
    ],
    });

    const result = await pollLightroomJob(job);
    console.log('Processed:', result.outputs?.[0]?.href);
    const job = await LightroomClient.xmp({
    inputs: {
    source: {
    href: 'https://your-bucket.s3.amazonaws.com/photo.jpg',
    storage: 'external',
    },
    xmp: {
    href: 'https://your-bucket.s3.amazonaws.com/settings.xmp',
    storage: 'external',
    },
    },
    outputs: [
    {
    href: 'https://your-bucket.s3.amazonaws.com/edited.jpg',
    storage: 'external',
    },
    ],
    });
    const job = await LightroomClient.autoStraighten({
    inputs: {
    href: 'https://your-bucket.s3.amazonaws.com/crooked.jpg',
    storage: 'external',
    },
    outputs: [
    {
    href: 'https://your-bucket.s3.amazonaws.com/straightened.jpg',
    storage: 'external',
    },
    ],
    });

    const result = await pollLightroomJob(job, {
    intervalMs: 2000,
    maxAttempts: 60,
    });
    const job = await LightroomClient.autoTone({
    inputs: {
    href: 'https://your-bucket.s3.amazonaws.com/photo.jpg',
    storage: 'external',
    },
    outputs: [
    {
    href: 'https://your-bucket.s3.amazonaws.com/auto-toned.jpg',
    storage: 'external',
    },
    ],
    });
    const job = await LightroomClient.edit({
    inputs: {
    source: {
    href: 'https://your-bucket.s3.amazonaws.com/photo.jpg',
    storage: 'external',
    },
    edits: {
    href: 'https://your-bucket.s3.amazonaws.com/edits.json',
    storage: 'external',
    },
    },
    outputs: [
    {
    href: 'https://your-bucket.s3.amazonaws.com/edited.jpg',
    storage: 'external',
    },
    ],
    });

    For async operations, use the polling utility:

    import { pollLightroomJob, PollingTimeoutError } from '@musallam/lightroom-client';

    try {
    const result = await pollLightroomJob(jobResult, {
    intervalMs: 2000, // Poll every 2 seconds
    maxAttempts: 60, // Try up to 60 times
    timeoutMs: 300000, // Or timeout after 5 minutes
    onProgress: (status) => {
    console.log(`Status: ${status.status}`);
    if (status.status === 'running') {
    console.log('Job is processing...');
    }
    },
    });

    console.log('Job completed:', result.outputs);
    } catch (error) {
    if (error instanceof PollingTimeoutError) {
    console.error('Job timed out');
    } else {
    console.error('Job failed:', error);
    }
    }
    import axios from 'axios';

    try {
    const job = await LightroomClient.autoTone(request);
    } catch (error) {
    if (axios.isAxiosError(error)) {
    const status = error.response?.status;
    const data = error.response?.data;

    switch (status) {
    case 400:
    console.error('Bad Request:', data.details?.reason);
    break;
    case 402:
    console.error('Trial Limit Exceeded');
    break;
    case 403:
    console.error('Forbidden:', data.details?.reason);
    break;
    case 404:
    console.error('Resource Not Found');
    break;
    case 409:
    console.error('File Overwrite Error');
    break;
    case 410:
    console.error('Asset Link Invalid or Expired');
    break;
    default:
    console.error('Error:', data);
    }
    }
    }
    import { LIGHTROOM_AXIOS_INSTANCE } from '@musallam/lightroom-client';

    // Add custom headers
    LIGHTROOM_AXIOS_INSTANCE.defaults.headers.common['X-Custom-Header'] = 'value';

    // Add request interceptor
    LIGHTROOM_AXIOS_INSTANCE.interceptors.request.use((config) => {
    console.log(`Making request to ${config.url}`);
    return config;
    });

    // Add response interceptor
    LIGHTROOM_AXIOS_INSTANCE.interceptors.response.use(
    (response) => response,
    (error) => {
    console.error('Request failed:', error.message);
    return Promise.reject(error);
    }
    );
    LIGHTROOM_AXIOS_INSTANCE.defaults.timeout = 30000; // 30 seconds
    
    LIGHTROOM_AXIOS_INSTANCE.interceptors.request.use(async (config) => {
    const token = await imsClient.getAccessToken();
    config.headers.Authorization = `Bearer ${token}`;
    config.headers['x-api-key'] = 'YOUR_CLIENT_ID';
    config.headers['x-gw-ims-org-id'] = 'YOUR_ORG_ID'; // Optional, for events
    return config;
    });

    All types are automatically generated from the OpenAPI spec:

    import type {
    PostPresetsSchema,
    PostXmpSchema,
    PostAutoStraightenSchema,
    PostAutoToneSchema,
    PostImageEditSchema,
    GetStatusSchema,
    ApiResponseSuccessSchema,
    } from '@musallam/lightroom-client';

    const request: PostAutoToneSchema = {
    inputs: {
    href: 'https://example.com/image.jpg',
    storage: 'external',
    },
    outputs: [
    {
    href: 'https://example.com/output.jpg',
    storage: 'external',
    },
    ],
    };

    const job = await LightroomClient.autoTone(request);

    The Lightroom API supports various storage options:

    • external - Pre-signed URLs to external storage (AWS S3, Azure, etc.)
    • adobe - Adobe's cloud storage (requires different authentication)

    Example with external storage:

    const job = await LightroomClient.autoTone({
    inputs: {
    href: 'https://my-bucket.s3.amazonaws.com/input.jpg',
    storage: 'external',
    },
    outputs: [
    {
    href: 'https://my-bucket.s3.amazonaws.com/output.jpg',
    storage: 'external',
    },
    ],
    });
    import {
    LightroomClient,
    LIGHTROOM_AXIOS_INSTANCE,
    TokenIMSClient,
    pollLightroomJob,
    } from '@musallam/lightroom-client';

    async function processImage() {
    // Setup authentication
    const imsClient = new TokenIMSClient({
    clientId: process.env.ADOBE_CLIENT_ID!,
    clientSecret: process.env.ADOBE_CLIENT_SECRET!,
    scopes: ['openid', 'AdobeID', 'firefly_api', 'ff_apis'],
    });

    LIGHTROOM_AXIOS_INSTANCE.interceptors.request.use(async (config) => {
    const token = await imsClient.getAccessToken();
    config.headers.Authorization = `Bearer ${token}`;
    config.headers['x-api-key'] = process.env.ADOBE_CLIENT_ID!;
    return config;
    });

    try {
    // Apply auto-tone
    console.log('Applying auto-tone...');
    const job = await LightroomClient.autoTone({
    inputs: {
    href: 'https://my-bucket.s3.amazonaws.com/photo.jpg',
    storage: 'external',
    },
    outputs: [
    {
    href: 'https://my-bucket.s3.amazonaws.com/auto-toned.jpg',
    storage: 'external',
    },
    ],
    });

    // Poll for completion
    const result = await pollLightroomJob(job, {
    intervalMs: 2000,
    onProgress: (status) => {
    console.log(`Job status: ${status.status}`);
    },
    });

    console.log('✓ Processing complete!');
    console.log('Output:', result.outputs?.[0]?.href);
    } catch (error) {
    console.error('Error processing image:', error);
    }
    }

    processImage();

    MIT