243 lines
7.5 KiB
PHP
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;
|
||
|
}
|
||
|
}
|