diff --git a/js/pleroma-adapter.js b/js/pleroma-adapter.js new file mode 100644 index 0000000..7644b2a --- /dev/null +++ b/js/pleroma-adapter.js @@ -0,0 +1,133 @@ +/** + * Pleroma to Mastodon API Response Adapter + * + * This script intercepts fetch requests to Pleroma instances and adapts + * the responses to match the structure expected by mastodon-timeline.umd.js + */ + +(function() { + 'use strict'; + + // Store original fetch + const originalFetch = window.fetch; + + // Override fetch globally + window.fetch = async function(...args) { + const response = await originalFetch.apply(this, args); + + // Only intercept Pleroma API timeline and emoji requests + const url = args[0]; + if (typeof url === 'string' && + (url.includes('/api/v1/timelines/') || + url.includes('/api/v1/accounts/') && url.includes('/statuses'))) { + + // Clone the response so we can modify it + const clonedResponse = response.clone(); + + try { + const data = await clonedResponse.json(); + + // Adapt the response structure + const adaptedData = Array.isArray(data) + ? data.map(adaptPost) + : adaptPost(data); + + // Create a new response with adapted data + return new Response(JSON.stringify(adaptedData), { + status: response.status, + statusText: response.statusText, + headers: response.headers + }); + } catch (error) { + console.warn('Pleroma adapter: Failed to parse response, returning original', error); + return response; + } + } + + return response; + }; + + /** + * Adapt a single Pleroma post to Mastodon format + */ + function adaptPost(post) { + if (!post || typeof post !== 'object') { + return post; + } + + // Create a copy to avoid mutating the original + const adapted = { ...post }; + + // Adapt media attachments + if (adapted.media_attachments && Array.isArray(adapted.media_attachments)) { + adapted.media_attachments = adapted.media_attachments.map(adaptMediaAttachment); + } + + // Adapt reblog if present + if (adapted.reblog) { + adapted.reblog = adaptPost(adapted.reblog); + } + + return adapted; + } + + /** + * Adapt media attachment to include missing meta fields + */ + function adaptMediaAttachment(attachment) { + if (!attachment || typeof attachment !== 'object') { + return attachment; + } + + // If meta already exists and has required fields, return as-is + if (attachment.meta?.original?.width && attachment.meta?.small?.aspect) { + return attachment; + } + + // Create adapted attachment + const adapted = { ...attachment }; + + // Default dimensions (will work for most cases) + const defaultWidth = 1280; + const defaultHeight = 720; + const defaultAspect = defaultWidth / defaultHeight; + + // Try to extract dimensions from Pleroma's meta if available + let width = defaultWidth; + let height = defaultHeight; + + if (attachment.pleroma?.mime_type) { + // For videos, use 16:9 ratio + if (attachment.pleroma.mime_type.startsWith('video/')) { + width = 1920; + height = 1080; + } + // For images, use standard web dimensions + else if (attachment.pleroma.mime_type.startsWith('image/')) { + width = 1200; + height = 800; + } + } + + // Ensure meta structure exists + adapted.meta = adapted.meta || {}; + + // Add original dimensions + adapted.meta.original = adapted.meta.original || { + width: width, + height: height, + aspect: width / height + }; + + // Add small (preview) dimensions + adapted.meta.small = adapted.meta.small || { + width: Math.floor(width / 2), + height: Math.floor(height / 2), + aspect: width / height + }; + + return adapted; + } + + console.log('Pleroma adapter initialized: API responses will be adapted for Mastodon compatibility'); +})();