bluesky-Connector/includes/post-formatter.php
2024-11-21 01:05:37 -08:00

243 lines
7.5 KiB
PHP

<?php
class Post_Formatter {
private $api;
private $max_length = 300;
public function __construct($access_token, $did) {
$this->api = new Bluesky_API($access_token, $did);
}
public function format_and_post($post) {
$content = $this->get_formatted_content($post);
$post_data = array(
'$type' => 'app.bsky.feed.post',
'text' => $content,
'createdAt' => gmdate('c', strtotime($post->post_date_gmt)),
'langs' => array('en')
);
// Add facets for the URL
$post_data['facets'] = $this->parse_facets($post);
// Handle image embed separately from text content
if (has_post_thumbnail($post->ID)) {
$image_data = $this->handle_featured_image($post->ID);
if (!empty($image_data) && !isset($image_data['error'])) {
$post_data['embed'] = $image_data;
}
}
return $this->api->create_post($post_data);
}
private function get_formatted_content($post) {
$format = get_option('bluesky_post_format', 'image-title-excerpt-link');
$include_title = get_option('bluesky_include_title', true);
// Get individual components
$title = $include_title ? $post->post_title : '';
$excerpt = $this->get_excerpt($post);
$url = wp_get_shortlink($post->ID);
// Start building content with explicit line breaks
$content = '';
// Add title with line break if it exists
if (!empty($title)) {
$content .= $title . "\n\n";
}
// Add excerpt if it exists
if (!empty($excerpt)) {
$content .= $excerpt . "\n\n"; // Add double line break after excerpt
}
// Add URL on its own line
if (!empty($url)) {
$content .= $url; // URL starts on new line due to previous \n\n
}
return $content;
}
private function get_excerpt($post) {
$text = wp_strip_all_tags($post->post_excerpt);
if (empty($text)) {
$text = wp_strip_all_tags($post->post_content);
}
$url = wp_get_shortlink($post->ID);
$include_title = get_option('bluesky_include_title', true);
// Calculate available length accounting for spacing and new lines
$available_length = $this->max_length;
$available_length -= strlen($url);
$available_length -= 2; // Account for \n\n after excerpt
if ($include_title) {
$available_length -= strlen($post->post_title);
$available_length -= 2; // Account for \n\n after title
}
if (mb_strlen($text) > $available_length) {
$text = mb_substr($text, 0, $available_length - 3) . '...';
}
return $text;
}
private function parse_facets($post) {
$facets = array();
$content = $this->get_formatted_content($post);
// Add link facet for the post URL
$url = wp_get_shortlink($post->ID);
$text_bytes = mb_convert_encoding($content, 'UTF-8');
$url_position = mb_strrpos($text_bytes, $url);
if ($url_position !== false) {
$facets[] = array(
'index' => array(
'byteStart' => $url_position,
'byteEnd' => $url_position + strlen($url),
),
'features' => array(
array(
'$type' => 'app.bsky.richtext.facet#link',
'uri' => $url,
),
),
);
}
return $facets;
}
private function handle_featured_image($post_id) {
$image_id = get_post_thumbnail_id($post_id);
$image_path = get_attached_file($image_id);
if (!$image_path) {
return array('error' => 'Image file not found');
}
$mime_type = get_post_mime_type($image_id);
// Get image data
$image_data = file_get_contents($image_path);
if ($image_data === false) {
return array('error' => 'Failed to read image file');
}
// Check file size (1MB limit for Bluesky)
if (strlen($image_data) > 1000000) {
// If image is too large, attempt to resize it
$resized = $this->resize_image($image_path);
if ($resized) {
$image_data = file_get_contents($resized);
unlink($resized); // Clean up temporary file
} else {
return array('error' => 'Image file size exceeds 1MB limit and resize failed');
}
}
// Upload image blob
$response = $this->api->upload_blob($image_path, $mime_type);
if (isset($response['error'])) {
return $response;
}
// Get the alt text
$alt_text = get_post_meta($image_id, '_wp_attachment_image_alt', true) ?: '';
return array(
'$type' => 'app.bsky.embed.images',
'images' => array(
array(
'alt' => $alt_text,
'image' => $response['blob'],
),
),
);
}
private function resize_image($image_path) {
// Only proceed if GD is available
if (!function_exists('imagecreatefrompng')) {
return false;
}
$mime_type = mime_content_type($image_path);
list($width, $height) = getimagesize($image_path);
// Calculate new dimensions while maintaining aspect ratio
$max_dimension = 1000; // Reasonable size that should result in < 1MB file
if ($width > $height) {
$new_width = $max_dimension;
$new_height = floor($height * ($max_dimension / $width));
} else {
$new_height = $max_dimension;
$new_width = floor($width * ($max_dimension / $height));
}
// Create new image
$new_image = imagecreatetruecolor($new_width, $new_height);
// Handle different image types
switch ($mime_type) {
case 'image/jpeg':
$source = imagecreatefromjpeg($image_path);
break;
case 'image/png':
$source = imagecreatefrompng($image_path);
// Preserve transparency
imagealphablending($new_image, false);
imagesavealpha($new_image, true);
break;
case 'image/gif':
$source = imagecreatefromgif($image_path);
break;
default:
return false;
}
if (!$source) {
return false;
}
// Resize
imagecopyresampled(
$new_image,
$source,
0, 0, 0, 0,
$new_width,
$new_height,
$width,
$height
);
// Create temporary file
$temp_file = tempnam(sys_get_temp_dir(), 'bluesky_img_');
// Save resized image
switch ($mime_type) {
case 'image/jpeg':
imagejpeg($new_image, $temp_file, 85);
break;
case 'image/png':
imagepng($new_image, $temp_file, 8);
break;
case 'image/gif':
imagegif($new_image, $temp_file);
break;
}
// Clean up
imagedestroy($source);
imagedestroy($new_image);
return $temp_file;
}
}