$value ) { $existing_mimes[ $key ] = $value; } // Make sure that videopress mimes are considered videos. $existing_mimes['videopress'] = 'video/videopress'; return $existing_mimes; } /** * Filter designed to get rid of non video mime types. * * @param string $value Mime type to filter. * @return int */ public static function filter_video_mimes( $value ) { return preg_match( '@^video/@', $value ); } /** * Attempts to delete a VideoPress video from wp.com. * Will block the deletion from continuing if certain errors return from the wp.com API. * * @param Boolean $delete if the deletion should occur or not (unused). * @param WP_Post $post the post object. * * @return null|WP_Error|Boolean null if deletion should continue. */ public static function delete_video_wpcom( $delete, $post ) { if ( ! is_videopress_attachment( $post->ID ) ) { return null; } $guid = get_post_meta( $post->ID, 'videopress_guid', true ); if ( empty( $guid ) ) { self::delete_video_poster_attachment( $post->ID ); return null; } // Phone home and have wp.com delete the VideoPress entry and files. $wpcom_response = Client::wpcom_json_api_request_as_blog( sprintf( '/videos/%s/delete', $guid ), '1.1', array( 'method' => 'POST' ), array( 'user_id' => get_current_user_id() ) ); if ( is_wp_error( $wpcom_response ) ) { return $wpcom_response; } // Upon success or a 404 (video already deleted on wp.com), return null to allow the deletion to continue. if ( 200 === $wpcom_response['response']['code'] || 404 === $wpcom_response['response']['code'] ) { self::delete_video_poster_attachment( $post->ID ); return null; } // Otherwise we stop the deletion from proceeding. return false; } /** * Deletes a video poster attachment if it exists. * * @param int $attachment_id the WP attachment id. */ private static function delete_video_poster_attachment( $attachment_id ) { $thumbnail_id = get_post_meta( $attachment_id, '_thumbnail_id', true ); if ( ! empty( $thumbnail_id ) ) { // Let's ensure this is a VP poster image before we delete it. if ( '1' === get_post_meta( $thumbnail_id, 'videopress_poster_image', true ) ) { // This call triggers the `delete_video_wpcom` filter again but it bails early at the is_videopress_attachment() check. wp_delete_attachment( $thumbnail_id ); } } } /** * Filter the mime type icon. * * @param string $icon Icon path. * @param string $mime Mime type. * @param int $post_id Post ID. * * @return string */ public static function wp_mime_type_icon( $icon, $mime, $post_id ) { if ( $mime !== 'video/videopress' ) { return $icon; } $status = get_post_meta( $post_id, 'videopress_status', true ); if ( $status === 'complete' ) { return $icon; } return 'https://wordpress.com/wp-content/mu-plugins/videopress/images/media-video-processing-icon.png'; } /** * Filter the list of supported video formats. * * @param array $extensions Supported video formats. * * @return array */ public static function add_videopress_extenstion( $extensions ) { $extensions[] = 'videopress'; return $extensions; } /** * Make sure that any Video that has a VideoPress GUID passes that data back. * * @param array $post Attachment data array. * @return array */ public static function prepare_attachment_for_js( $post ) { if ( 'video' === $post['type'] ) { $guid = get_post_meta( $post['id'], 'videopress_guid', true ); if ( $guid ) { $post['videopress_guid'] = $guid; } $status = get_post_meta( $post['id'], 'videopress_status', true ); if ( $status ) { $post['videopress_status'] = $status; } } return $post; } /** * Media Grid: * Filter out any videopress video posters that we've downloaded, * so that they don't seem to display twice. * * @param array $args Query variables. */ public static function ajax_query_attachments_args( $args ) { $meta_query = array( array( 'key' => 'videopress_poster_image', 'compare' => 'NOT EXISTS', ), ); // If there was already a meta query, let's AND it via // nesting it with our new one. No need to specify the // relation, as it defaults to AND. if ( ! empty( $args['meta_query'] ) ) { $meta_query[] = $args['meta_query']; } $args['meta_query'] = $meta_query; return $args; } /** * Media List: * Do the same as `videopress_ajax_query_attachments_args()` but for the list view. * * @param array $query WP_Query instance. */ public static function media_list_table_query( $query ) { if ( ! function_exists( 'get_current_screen' ) || get_current_screen() === null ) { return; } if ( is_admin() && $query->is_main_query() && ( 'upload' === get_current_screen()->id ) ) { $meta_query = array( array( 'key' => 'videopress_poster_image', 'compare' => 'NOT EXISTS', ), ); $old_meta_query = $query->get( 'meta_query' ); if ( $old_meta_query ) { $meta_query[] = $old_meta_query; } $query->set( 'meta_query', $meta_query ); } } /** * Filter to disable the `delete_post` capability * for VideoPress attachments if the current user is * not connected. * * @param array $allcaps All the capabilities of the user. * @param array $cap [0] Required capability. * @param array $args [0] Requested capability. * [1] User ID. * [2] Associated object ID. * @return array the filtered array of capabilities. */ public static function disable_delete_if_disconnected( $allcaps, $cap, $args ) { // Only apply this filter to `delete_post` checks if ( ! isset( $args[0] ) || 'delete_post' !== $args[0] ) { return $allcaps; } // Only apply this filter to VideoPress attachments if ( ! isset( $args[2] ) || ! is_videopress_attachment( $args[2] ) ) { return $allcaps; } // Set the capability to false if the user can't perform the actions if ( ! Data::can_perform_action() ) { $allcaps[ $cap[0] ] = false; } return $allcaps; } /** * Enqueues the script that hooks into WP Heartbeat to refresh * processing VideoPress video data in the media library grid. * * @since 0.35.4 * * @return void */ public static function enqueue_media_library_poll() { wp_enqueue_script( 'videopress-media-library-poll', plugins_url( 'js/media-library-poll.js', __FILE__ ), array( 'jquery', 'heartbeat', 'media-grid' ), Package_Version::PACKAGE_VERSION, true ); add_filter( 'heartbeat_settings', array( __CLASS__, 'heartbeat_settings' ) ); } /** * Adds inline styles for the media library grid on the upload page. * * @since 0.35.4 * * @return void */ public static function enqueue_media_library_styles() { // Constrain poster images so they fit within the media library grid cell. wp_add_inline_style( 'media-views', '.attachment-preview.type-video .thumbnail .centered img.thumbnail { max-width: 100%; max-height: 100%; }' ); } /** * Lowers the Heartbeat minimum interval on the media library page * so the polling script can request a faster tick rate. * * @since 0.35.4 * * @param array $settings Heartbeat settings. * @return array */ public static function heartbeat_settings( $settings ) { $settings['minimalInterval'] = 5; return $settings; } /** * Heartbeat API handler that checks the processing status of VideoPress videos. * * @since 0.35.4 * * @param array $response The Heartbeat response. * @param array $data The data sent with the Heartbeat request. * @return array */ public static function heartbeat_received( $response, $data ) { if ( empty( $data['videopress_processing_ids'] ) || ! is_array( $data['videopress_processing_ids'] ) ) { return $response; } $statuses = array(); foreach ( $data['videopress_processing_ids'] as $id ) { $id = (int) $id; if ( ! current_user_can( 'edit_post', $id ) ) { continue; } $status = get_post_meta( $id, 'videopress_status', true ); if ( $status ) { $statuses[ $id ] = $status; } } if ( ! empty( $statuses ) ) { $response['videopress_processing_status'] = $statuses; } return $response; } }