Created
May 31, 2025 14:21
-
-
Save mizner/8e9c5fe56e40c6f1f7c887c352a7875b to your computer and use it in GitHub Desktop.
WP FFMPEG Transcoder .mov to .mp4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Plugin Name: Custom Transcoder (WP Cron + Attachment Update + Email + UI Button) | |
*/ | |
// 1. Schedule transcoding on MOV upload | |
add_filter('wp_handle_upload', function($upload) { | |
$ext = strtolower(wp_check_filetype($upload['file'])['ext']); | |
if ($ext !== 'mov') return $upload; | |
wp_schedule_single_event(time() + 10, 'custom_transcoder_convert_mov', [$upload]); | |
return $upload; | |
}); | |
// 2. Cron job: Convert MOV to MP4 with improved FFmpeg args | |
add_action('custom_transcoder_convert_mov', function($upload) { | |
if (empty($upload['file']) || !file_exists($upload['file'])) { | |
error_log('[Transcoder] File not found'); | |
custom_transcoder_send_email('fail', $upload['file'], 'File not found.'); | |
return; | |
} | |
$original = $upload['file']; | |
$path = pathinfo($original); | |
$mp4 = $path['dirname'] . '/' . $path['filename'] . '.mp4'; | |
$cmd = "ffmpeg -y -i " . escapeshellarg($original) . | |
" -map 0:v:0 -map 0:a:0 -c copy -movflags +faststart " . | |
escapeshellarg($mp4) . " 2>&1"; | |
error_log('[Transcoder] Running: ' . $cmd); | |
exec($cmd, $out, $code); | |
if ($code !== 0 || !file_exists($mp4) || filesize($mp4) < 1000) { | |
error_log('[Transcoder] FFmpeg failed: ' . implode("\n", $out)); | |
custom_transcoder_send_email('fail', $original, implode("\n", $out)); | |
return; | |
} | |
global $wpdb; | |
// Update media library | |
$attachment_id = attachment_url_to_postid($upload['url']); | |
if ($attachment_id) { | |
update_attached_file($attachment_id, $mp4); | |
// ✅ Ensure required media functions are loaded | |
if (!function_exists('wp_generate_attachment_metadata')) { | |
require_once ABSPATH . 'wp-admin/includes/image.php'; | |
} | |
if (!function_exists('wp_read_video_metadata')) { | |
require_once ABSPATH . 'wp-admin/includes/media.php'; | |
} | |
wp_update_post([ | |
'ID' => $attachment_id, | |
'post_mime_type' => 'video/mp4', | |
]); | |
$meta = wp_generate_attachment_metadata($attachment_id, $mp4); | |
if (!empty($meta)) wp_update_attachment_metadata($attachment_id, $meta); | |
// ✅ Also update GUID directly to .mp4 | |
$mp4_url = str_replace('.mov', '.mp4', $upload['url']); | |
$wpdb->update( | |
$wpdb->posts, | |
['guid' => $mp4_url], | |
['ID' => $attachment_id] | |
); | |
error_log("[Transcoder] Updated wp_posts.guid for attachment ID $attachment_id"); | |
} | |
// ✅ Update rtMedia <video src="..."> in activity feed | |
$mov_basename = basename($upload['url']); | |
$mp4_basename = basename($mp4); | |
$like = '%' . $mov_basename . '%'; | |
$activities = $wpdb->get_results($wpdb->prepare( | |
"SELECT id, content FROM {$wpdb->prefix}bp_activity WHERE type = 'rtmedia_update' AND content LIKE %s", | |
$like | |
)); | |
foreach ($activities as $activity) { | |
$new_content = str_replace($mov_basename, $mp4_basename, $activity->content); | |
$wpdb->update( | |
"{$wpdb->prefix}bp_activity", | |
['content' => $new_content], | |
['id' => $activity->id] | |
); | |
error_log("[Transcoder] Updated bp_activity ID {$activity->id} video src to .mp4"); | |
} | |
unlink($original); | |
error_log('[Transcoder] Success: ' . $mp4); | |
custom_transcoder_send_email('success', $mp4); | |
}); | |
// 3. Update metadata post-transcode for existing attachments (fallback only) | |
add_action('add_attachment', function($post_ID) { | |
$file = get_attached_file($post_ID); | |
if (!$file || strtolower(pathinfo($file, PATHINFO_EXTENSION)) !== 'mov') return; | |
$mp4 = preg_replace('/\.mov$/i', '.mp4', $file); | |
if (!file_exists($mp4)) return; | |
update_attached_file($post_ID, $mp4); | |
if (!function_exists('wp_generate_attachment_metadata')) { | |
require_once ABSPATH . 'wp-admin/includes/image.php'; | |
} | |
if (!function_exists('wp_read_video_metadata')) { | |
require_once ABSPATH . 'wp-admin/includes/media.php'; | |
} | |
wp_update_post([ | |
'ID' => $post_ID, | |
'post_mime_type' => 'video/mp4', | |
]); | |
$meta = wp_generate_attachment_metadata($post_ID, $mp4); | |
if (!empty($meta)) wp_update_attachment_metadata($post_ID, $meta); | |
// Also update guid | |
global $wpdb; | |
$wpdb->update( | |
$wpdb->posts, | |
['guid' => str_replace('.mov', '.mp4', wp_get_attachment_url($post_ID))], | |
['ID' => $post_ID] | |
); | |
}); | |
// 4. Email notification | |
function custom_transcoder_send_email($status, $file, $details = '') { | |
$subj = $status === 'success' ? '✅ Video Transcoded' : '❌ Transcoding Failed'; | |
$body = "File: $file\n\n" . ($status === 'success' ? "Transcoding completed successfully." : "Transcoding failed.\n\nDetails:\n$details"); | |
wp_mail('[email protected]', $subj, $body); | |
} | |
// 5. "Transcode Now" button in media editor | |
add_filter('attachment_fields_to_edit', function($fields, $post) { | |
$file = get_attached_file($post->ID); | |
if (!$file || strtolower(pathinfo($file, PATHINFO_EXTENSION)) !== 'mov') return $fields; | |
$nonce = wp_create_nonce('retranscode_' . $post->ID); | |
$fields['retranscode'] = [ | |
'label' => 'Custom Transcoder', | |
'input' => 'html', | |
'html' => '<button type="button" class="button retranscode-mov-button" data-attachment="' . esc_attr($post->ID) . '" data-nonce="' . esc_attr($nonce) . '">Transcode Now</button>' . | |
'<span class="retranscode-status" style="margin-left:10px;"></span>', | |
]; | |
return $fields; | |
}, 10, 2); | |
// 6. Handle AJAX trigger | |
add_action('wp_ajax_custom_transcoder_retranscode', function() { | |
if (!current_user_can('upload_files')) wp_send_json_error(['message' => 'Permission denied.']); | |
$id = intval($_POST['attachment_id'] ?? 0); | |
$nonce = $_POST['nonce'] ?? ''; | |
if (!wp_verify_nonce($nonce, 'retranscode_' . $id)) wp_send_json_error(['message' => 'Invalid nonce.']); | |
if (!get_post($id)) wp_send_json_error(['message' => 'Attachment not found.']); | |
$file = get_attached_file($id); | |
if (!$file || strtolower(pathinfo($file, PATHINFO_EXTENSION)) !== 'mov') { | |
wp_send_json_error(['message' => 'Not a .mov file or file not found.']); | |
} | |
wp_schedule_single_event(time() + 5, 'custom_transcoder_convert_mov', [[ | |
'file' => $file, | |
'type' => get_post_mime_type($id), | |
'url' => wp_get_attachment_url($id), | |
]]); | |
wp_send_json_success(['message' => 'Transcoding job scheduled.']); | |
}); | |
// 7. Inject JS into media screen | |
add_action('admin_footer', function() { | |
$screen = get_current_screen(); | |
if (!$screen || !in_array($screen->id, ['upload', 'media'])) return; | |
?> | |
<script> | |
jQuery(document).on('click', '.retranscode-mov-button', function () { | |
const $btn = jQuery(this); | |
const id = $btn.data('attachment'); | |
const nonce = $btn.data('nonce'); | |
const $status = $btn.siblings('.retranscode-status'); | |
$status.text('Scheduling...'); | |
jQuery.post(ajaxurl, { | |
action: 'custom_transcoder_retranscode', | |
attachment_id: id, | |
nonce: nonce, | |
}).done(r => { | |
$status.text(r.data.message || 'Scheduled.'); | |
}).fail(xhr => { | |
const msg = xhr.responseJSON?.data?.message || 'Error.'; | |
$status.text('Failed: ' + msg); | |
}); | |
}); | |
</script> | |
<?php | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment