HEX
Server: LiteSpeed
System: Linux node612.namehero.net 4.18.0-553.121.1.lve.el8.x86_64 #1 SMP Thu Apr 30 16:40:41 UTC 2026 x86_64
User: dfwparty (1186)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/dfwparty/dfwchat.net/wp-content/plugins/buddyboss-platform/bp-video/bp-video-functions.php
<?php
/**
 * BuddyBoss Video Functions.
 *
 * Functions are where all the magic happens in BuddyPress. They will
 * handle the actual saving or manipulation of information. Usually they will
 * hand off to a database class for data access, then return
 * true or false on success or failure.
 *
 * @package BuddyBoss\Video\Functions
 * @since   BuddyBoss 1.7.0
 */

// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;

/**
 * Create and upload the video file
 *
 * @return array|null|WP_Error|WP_Post
 * @since BuddyBoss 1.7.0
 */
function bp_video_upload() {
	/**
	 * Make sure user is logged in
	 */
	if ( ! is_user_logged_in() ) {
		return new WP_Error( 'not_logged_in', __( 'Please login in order to upload file video.', 'buddyboss' ), array( 'status' => 500 ) );
	}

	/**
	 * Hook before video upload.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	do_action( 'bb_before_video_upload_handler' );

	$attachment = bp_video_upload_handler();

	if ( 'in_progress' === $attachment ) {
		return 'in_progress';
	}

	/**
	 * Hook after video upload.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	do_action( 'bb_after_video_upload_handler' );

	if ( is_wp_error( $attachment ) ) {
		return $attachment;
	}

	/**
	 * Hook video upload.
	 *
	 * @param mixed $attachment attachment
	 *
	 * @since BuddyBoss 1.7.0
	 */
	do_action( 'bb_video_upload', $attachment );

	// get saved video id.
	$video_id = (int) get_post_meta( $attachment->ID, 'bp_video_id', true );

	$name = $attachment->post_title;

	// Generate video attachment preview link.
	$attachment_id     = 'forbidden_' . $attachment->ID;
	$attachment_url    = home_url( '/' ) . 'bb-attachment-video-preview/' . base64_encode( $attachment_id );
	$video_message_url = ( isset( $_POST ) && isset( $_POST['thread_id'] ) ? home_url( '/' ) . 'bb-attachment-video-preview/' . base64_encode( $attachment_id ) . '/' . base64_encode( 'thread_' . $_POST['thread_id'] ) : '' );

	$file_url = wp_get_attachment_url( $attachment->ID );
	$filetype = wp_check_filetype( $file_url );
	$ext      = $filetype['ext'];
	if ( empty( $ext ) ) {
		$path = parse_url( $file_url, PHP_URL_PATH );
		$ext  = pathinfo( basename( $path ), PATHINFO_EXTENSION );
	}
	// https://stackoverflow.com/questions/40995987/how-to-play-mov-files-in-video-tag/40999234#40999234.
	// https://stackoverflow.com/a/44858204.
	if ( in_array( $ext, array( 'mov', 'm4v' ), true ) ) {
		$ext = 'mp4';
	}

	if (
		! empty( $video_id ) &&
		(
			bp_is_group_messages() ||
			bp_is_messages_component() ||
			(
				! empty( $_POST['component'] ) &&
				'messages' === $_POST['component']
			)
		)
	) {
		$attachment_url    = bb_video_get_symlink( $video_id );
		$video_message_url = $attachment_url;
	}

	/**
	 * Filter the attachment URL.
	 *
	 * @since BuddyBoss 2.15.0
	 *
	 * @param string $attachment_url Attachment URL.
	 * @param int    $attachment_id Attachment ID.
	 */
	$attachment_url = apply_filters( 'bb_video_get_attachment_url', untrailingslashit( $attachment_url ), $attachment->ID );

	/**
	 * Filter the video message URL.
	 *
	 * @since BuddyBoss 2.15.0
	 *
	 * @param string $video_message_url Video message URL.
	 * @param int    $attachment_id Attachment ID.
	 */
	$video_message_url = apply_filters( 'bb_video_get_video_message_url', untrailingslashit( $video_message_url ), $attachment->ID );

	$result = array(
		'id'          => (int) $attachment->ID,
		'thumb'       => '',
		'url'         => esc_url( $attachment_url ),
		'name'        => esc_attr( $name ),
		'ext'         => esc_attr( $ext ),
		'vid_msg_url' => esc_url( $video_message_url ),
	);

	return $result;
}

/**
 * Video thumbnail upload handler
 *
 * @param string $file_id file.
 *
 * @return array|int|null|WP_Error|WP_Post
 * @since BuddyBoss 1.7.0
 */
function bp_video_thumbnail_upload_handler( $file_id = 'file' ) {

	/**
	 * Include required files
	 */

	if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
		require_once ABSPATH . 'wp-admin/includes/image.php';
		require_once ABSPATH . 'wp-admin/includes/file.php';
		require_once ABSPATH . 'wp-admin/includes/media.php';
	}

	if ( ! function_exists( 'media_handle_upload' ) ) {
		require_once ABSPATH . 'wp-admin/includes/admin.php';
	}

	// Add upload filters.
	bb_video_add_thumb_image_add_upload_filters();

	// Register image sizes.
	bb_video_register_image_sizes();

	/**
	 * Hook before video thumbnail upload.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	do_action( 'bb_before_bp_video_thumbnail_upload_handler' );

	$aid = media_handle_upload(
		$file_id,
		0,
		array(),
		array(
			'test_form'            => false,
			'upload_error_strings' => array(
				false,
				__( 'The uploaded file exceeds ', 'buddyboss' ) . bp_media_file_upload_max_size(),
				__( 'The uploaded file was only partially uploaded.', 'buddyboss' ),
				__( 'No file was uploaded.', 'buddyboss' ),
				'',
				__( 'Missing a temporary folder.', 'buddyboss' ),
				__( 'Failed to write file to disk.', 'buddyboss' ),
				__( 'File upload stopped by extension.', 'buddyboss' ),
			),
		)
	);

	// Deregister image sizes.
	bb_video_deregister_image_sizes();

	// Remove upload filters.
	bb_video_add_thumb_image_remove_upload_filters();

	/**
	 * Hook after video thumbnail upload.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	do_action( 'bb_after_bp_video_thumbnail_upload_handler' );

	// if has wp error then throw it.
	if ( is_wp_error( $aid ) ) {
		return $aid;
	}

	/**
	 * Hook video thumbnail attachment uploaded.
	 *
	 * @param mixed $aid attachment id
	 *
	 * @since BuddyBoss 1.7.0
	 */
	do_action( 'bp_video_thumbnail_attachment_uploaded', $aid );

	$attachment = get_post( $aid );

	if ( ! empty( $attachment ) ) {
		update_post_meta( $attachment->ID, 'bp_video_thumbnail_upload', true );
		update_post_meta( $attachment->ID, 'bp_video_thumbnail_saved', '0' );

		return $attachment;
	}

	return new WP_Error( 'error_uploading', __( 'Error while uploading thumbnail.', 'buddyboss' ), array( 'status' => 500 ) );

}

/**
 * Create and upload the video thumbnail file
 *
 * @return array|null|WP_Error|WP_Post
 * @since BuddyBoss 1.7.0
 */
function bp_video_thumbnail_upload() {
	/**
	 * Make sure user is logged in
	 */
	if ( ! is_user_logged_in() ) {
		return new WP_Error( 'not_logged_in', __( 'Please login in order to upload file media.', 'buddyboss' ), array( 'status' => 500 ) );
	}

	$attachment = bp_video_thumbnail_upload_handler();

	if ( is_wp_error( $attachment ) ) {
		return $attachment;
	}

	$name = $attachment->post_title;

	$thumb_nfo = wp_get_attachment_image_src( $attachment->ID );
	$url_nfo   = wp_get_attachment_image_src( $attachment->ID, 'full' );

	$url       = is_array( $url_nfo ) && ! empty( $url_nfo ) ? $url_nfo[0] : null;
	$thumb_nfo = is_array( $thumb_nfo ) && ! empty( $thumb_nfo ) ? $thumb_nfo[0] : null;

	$result = array(
		'id'    => (int) $attachment->ID,
		'thumb' => esc_url( $thumb_nfo ),
		'url'   => esc_url( $url ),
		'name'  => esc_attr( $name ),
	);

	return $result;
}

/**
 * Mine type for uploader allowed by buddyboss video for security reason
 *
 * @param Array $existing_mimes carry mime information.
 *
 * @return Array $existing_mimes allowed mime types.
 * @since BuddyBoss 1.7.0
 */
function bp_video_allowed_mimes( $existing_mimes = array() ) {

	if ( bp_is_active( 'media' ) ) {
		$existing_mimes = array();
		$all_extensions = bp_video_extensions_list();
		foreach ( $all_extensions as $extension ) {
			if ( isset( $extension['is_active'] ) && true === (bool) $extension['is_active'] ) {
				$extension_name                      = ltrim( $extension['extension'], '.' );
				$existing_mimes[ "$extension_name" ] = $extension['mime_type'];
			}
		}
	}

	return $existing_mimes;
}

/**
 * Video upload handler
 *
 * @param string $file_id file.
 *
 * @return array|int|null|WP_Error|WP_Post
 * @since BuddyBoss 1.7.0
 */
function bp_video_upload_handler( $file_id = 'file' ) {

	/**
	 * Include required files
	 */

	if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
		require_once ABSPATH . 'wp-admin/includes/image.php';
		require_once ABSPATH . 'wp-admin/includes/file.php';
		require_once ABSPATH . 'wp-admin/includes/media.php';
	}

	if ( ! function_exists( 'media_handle_upload' ) ) {
		require_once ABSPATH . 'wp-admin/includes/admin.php';
	}

	// Add upload filters.
	bb_video_add_upload_filters();

	// Initialize variables before foreach to prevent undefined variable warnings.
	$tmp_file_path = '';
	$filename      = '';

	// phpcs:disable WordPress.Security.NonceVerification.Missing
	if ( ! empty( $_FILES ) ) {
		foreach ( $_FILES as $file ) {
			if ( 0 !== $file['error'] ) {
				$errors[] = array(
					'text'  => 'File error',
					'error' => $file['error'],
					'name'  => $file['name'],
				);
				continue;
			}
			if ( empty( $file['tmp_name'] ) ) {
				$errors[] = array(
					'text' => 'Tmp file not found',
					'name' => $file['name'],
				);
				continue;
			}

			$tmp_file_path = $file['tmp_name'];
			$filename      = ( isset( $file['filename'] ) ) ? $file['filename'] : $file['name'];

			if ( ! empty( $_POST['dzuuid'] ) ) {
				$chunks_res = bb_resumable_upload( $tmp_file_path, $filename );

				// Check for errors from chunk assembly.
				if ( ! empty( $chunks_res['errors'] ) ) {
					$error_message = implode( ', ', $chunks_res['errors'] );
					return new WP_Error( 'chunk_upload_failed', $error_message, array( 'status' => 400 ) );
				}

				// If chunk upload is still in progress, return status.
				if ( empty( $chunks_res['final'] ) ) {
					return 'in_progress';
				}

				// Get the path to the assembled file.
				$tmp_file_path = $chunks_res['path'];

				// Validate that the assembled file exists and is not empty.
				if ( ! file_exists( $tmp_file_path ) || 0 === filesize( $tmp_file_path ) ) {
					return new WP_Error(
						'invalid_assembled_file',
						__( 'The assembled video file is invalid or empty.', 'buddyboss' ),
						array( 'status' => 500 )
					);
				}
			}
		}
	}

	$_FILES['async-upload']['tmp_name'] = $tmp_file_path;
	$_FILES['async-upload']['name']     = $filename;
	if ( ! isset( $_FILES['async-upload']['tmp_name'] ) || ! isset( $_FILES['async-upload']['name'] ) ) {
		return new WP_Error( 'error_uploading', __( 'Invalid file upload.', 'buddyboss' ), array( 'status' => 400 ) );
	}
	$tmp_name                       = sanitize_text_field( wp_unslash( $_FILES['async-upload']['tmp_name'] ) );
	$_FILES['async-upload']['size'] = filesize( $tmp_name );
	$name                           = sanitize_text_field( wp_unslash( $_FILES['async-upload']['name'] ) );
	$wp_filetype                    = wp_check_filetype_and_ext( $tmp_name, $name );
	$_FILES['async-upload']['type'] = $wp_filetype['type'];
	// phpcs:enable WordPress.Security.NonceVerification.Missing

	$aid = media_handle_upload(
		'async-upload',
		0,
		array(),
		array(
			'action'               => 'wp_handle_sideload',
			'test_form'            => false,
			'upload_error_strings' => array(
				false,
				__( 'The uploaded file exceeds ', 'buddyboss' ) . bp_video_file_upload_max_size(),
				__( 'The uploaded file was only partially uploaded.', 'buddyboss' ),
				__( 'No file was uploaded.', 'buddyboss' ),
				'',
				__( 'Missing a temporary folder.', 'buddyboss' ),
				__( 'Failed to write file to disk.', 'buddyboss' ),
				__( 'File upload stopped by extension.', 'buddyboss' ),
			),
		)
	);

	// Remove upload filters.
	bb_video_remove_upload_filters();

	// if has wp error then throw it.
	if ( is_wp_error( $aid ) ) {
		// Clean up assembled file on error if it was created from chunks.
		// phpcs:ignore WordPress.Security.NonceVerification.Missing
		if ( ! empty( $_POST['dzuuid'] ) &&
			isset( $tmp_file_path ) &&
			file_exists( $tmp_file_path ) &&
			false !== strpos( $tmp_file_path, 'bb-whole-from-chunks' ) ) {
			wp_delete_file( $tmp_file_path );
		}

		return $aid;
	}

	/**
	 * Hook before video attachment uploaded.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	do_action( 'bp_video_attachment_uploaded', $aid );

	$attachment = get_post( $aid );

	if ( ! empty( $attachment ) ) {
		update_post_meta( $attachment->ID, 'bp_video_upload', 1 );
		update_post_meta( $attachment->ID, 'bp_video_saved', '0' );

		return $attachment;
	}

	return new WP_Error( 'error_uploading', __( 'Error while uploading video.', 'buddyboss' ), array( 'status' => 500 ) );

}

/**
 * Compress the image
 *
 * @param  string $source source path.
 * @param  string $destination destination path.
 * @param int    $quality quality.
 *
 * @return mixed
 * @since BuddyBoss 1.7.0
 */
function bp_video_compress_image( $source, $destination, $quality = 90 ) {

	$info = @getimagesize( $source ); // phpcs:ignore

	if ( 'image/jpeg' === $info['mime'] ) {
		$image = @imagecreatefromjpeg( $source ); // phpcs:ignore
	} elseif ( 'image/gif' === $info['mime'] ) {
		$image = @imagecreatefromgif( $source ); // phpcs:ignore
	} elseif ( 'image/png' === $info['mime'] ) {
		$image = @imagecreatefrompng( $source ); // phpcs:ignore
	}

	@imagejpeg( $image, $destination, $quality ); // phpcs:ignore

	return $destination;
}

/**
 * Get file video upload max size
 *
 * @return string
 * @since BuddyBoss 1.7.0
 */
function bp_video_file_upload_max_size() {

	/**
	 * Filters file video upload max limit.
	 *
	 * @param mixed $max_size video upload max limit.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_file_upload_max_size', bp_video_allowed_upload_video_size() );
}

/**
 * Format file size units
 *
 * @param   int|float $bytes total bytes.
 * @param bool      $post_string true or not.
 * @param string    $type unit types.
 *
 * @return string
 * @since BuddyBoss 1.7.0
 */
function bp_video_format_size_units( $bytes, $post_string = false, $type = 'bytes' ) {

	if ( $bytes > 0 ) {
		if ( 'GB' === $type && ! $post_string ) {
			return $bytes / 1073741824;
		} elseif ( 'MB' === $type && ! $post_string ) {
			return $bytes / 1048576;
		} elseif ( 'KB' === $type && ! $post_string ) {
			return $bytes / 1024;
		}
	}

	if ( $bytes >= 1073741824 ) {
		$bytes = ( $bytes / 1073741824 ) . ( $post_string ? ' GB' : '' );
	} elseif ( $bytes >= 1048576 ) {
		$bytes = ( $bytes / 1048576 ) . ( $post_string ? ' MB' : '' );
	} elseif ( $bytes >= 1024 ) {
		$bytes = ( $bytes / 1024 ) . ( $post_string ? ' KB' : '' );
	} elseif ( $bytes > 1 ) {
		$bytes = $bytes . ( $post_string ? ' bytes' : '' );
	} elseif ( 1 === $bytes ) {
		$bytes = $bytes . ( $post_string ? ' byte' : '' );
	} else {
		$bytes = '0' . ( $post_string ? ' bytes' : '' );
	}

	return $bytes;
}

/*
 * Business functions are where all the magic happens in BuddyPress. They will
 * handle the actual saving or manipulation of information. Usually they will
 * hand off to a database class for data access, then return
 * true or false on success or failure.
 */

/**
 * Retrieve an video or videos.
 *
 * The bp_video_get() function shares all arguments with BP_Video::get().
 * The following is a list of bp_video_get() parameters that have different
 * default values from BP_Video::get() (value in parentheses is
 * the default for the bp_video_get()).
 *   - 'per_page' (false)
 *
 * @param array|string $args See BP_Video::get() for description.
 *
 * @return array $video See BP_Video::get() for description.
 * @since BuddyBoss 1.7.0
 *
 * @see   BP_Video::get() For more information on accepted arguments
 *      and the format of the returned value.
 */
function bp_video_get( $args = '' ) {

	$r = bp_parse_args(
		$args,
		array(
			'max'              => false,        // Maximum number of results to return.
			'fields'           => 'all',
			'page'             => 1,            // Page 1 without a per_page will result in no pagination.
			'per_page'         => false,        // results per page.
			'sort'             => 'DESC',       // sort ASC or DESC.
			'order_by'         => false,        // order by.

			'scope'            => false,        // public, friends, groups, personal.

			// want to limit the query.
			'user_id'          => false,
			'activity_id'      => false,
			'album_id'         => false,
			'group_id'         => false,
			'search_terms'     => false,        // Pass search terms as a string.
			'privacy'          => false,        // Privacy of video - public, loggedin, onlyme, friends, grouponly, message.
			'exclude'          => false,        // Comma-separated list of IDs to exclude.
			'in'               => false,        // Comma-separated list of IDs to include.
			'moderation_query' => true,         // Filter to include moderation query.
			'count_total'      => false,        // Whether to count the total number of items in the query.
			'status'           => bb_video_get_published_status(),  // Status of the Video scheduled, published.
		),
		'video_get'
	);

	$video = BP_Video::get(
		array(
			'page'             => $r['page'],
			'per_page'         => $r['per_page'],
			'user_id'          => $r['user_id'],
			'activity_id'      => $r['activity_id'],
			'album_id'         => $r['album_id'],
			'group_id'         => $r['group_id'],
			'max'              => $r['max'],
			'sort'             => $r['sort'],
			'order_by'         => $r['order_by'],
			'search_terms'     => $r['search_terms'],
			'scope'            => $r['scope'],
			'privacy'          => $r['privacy'],
			'exclude'          => $r['exclude'],
			'in'               => ! empty( $r['include'] ) ? $r['include'] : $r['in'],
			'count_total'      => $r['count_total'],
			'fields'           => $r['fields'],
			'moderation_query' => $r['moderation_query'],
			'status'           => $r['status'],
		)
	);

	/**
	 * Filters the requested video item(s).
	 *
	 * @param BP_Video $video Requested video object.
	 * @param array    $r     Arguments used for the video query.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters_ref_array( 'bp_video_get', array( &$video, &$r ) );
}

/**
 * Fetch specific video items.
 *
 * @param array|string $args {
 *                           All arguments and defaults are shared with BP_Video::get(),
 *                           except for the following:
 *
 * @type string|int|array Single video ID, comma-separated list of IDs,
 *                            or array of IDs.
 * }
 * @return array $activity See BP_Video::get() for description.
 * @since BuddyBoss 1.7.0
 *
 * @see   BP_Video::get() For more information on accepted arguments.
 */
function bp_video_get_specific( $args = '' ) {

	$r = bp_parse_args(
		$args,
		array(
			'video_ids'        => false,      // A single video_id or array of IDs.
			'max'              => false,      // Maximum number of results to return.
			'page'             => 1,          // Page 1 without a per_page will result in no pagination.
			'per_page'         => false,      // Results per page.
			'sort'             => 'DESC',     // Sort ASC or DESC.
			'order_by'         => false,      // Sort ASC or DESC.
			'privacy'          => false,      // privacy to filter.
			'album_id'         => false,      // Album ID.
			'user_id'          => false,      // User ID.
			'moderation_query' => true,
			'count_total'      => false,
			'status'           => bb_video_get_published_status(),
		),
		'video_get_specific'
	);

	$get_args = array(
		'in'               => $r['video_ids'],
		'max'              => $r['max'],
		'page'             => $r['page'],
		'per_page'         => $r['per_page'],
		'sort'             => $r['sort'],
		'order_by'         => $r['order_by'],
		'privacy'          => $r['privacy'],
		'album_id'         => $r['album_id'],
		'user_id'          => $r['user_id'],
		'moderation_query' => $r['moderation_query'],
		'status'           => $r['status'],
		'count_total'      => $r['count_total'],
	);

	/**
	 * Filters the requested specific video item.
	 *
	 * @param BP_Video $video    Requested video object.
	 * @param array    $args     Original passed in arguments.
	 * @param array    $get_args Constructed arguments used with request.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_get_specific', BP_Video::get( $get_args ), $args, $get_args );
}

/**
 * Add an video item.
 *
 * @param array|string $args         {
 *                                   An array of arguments.
 *
 * @type int|bool      $id           Pass an video ID to update an existing item, or
 *                                       false to create a new item. Default: false.
 * @type int|bool      $blog_id      ID of the blog Default: current blog id.
 * @type int|bool      $attchment_id ID of the attachment Default: false
 * @type int|bool      $user_id      Optional. The ID of the user associated with the activity
 *                                       item. May be set to false or 0 if the item is not related
 *                                       to any user. Default: the ID of the currently logged-in user.
 * @type string        $title        Optional. The title of the video item.
 * @type int           $album_id     Optional. The ID of the associated album.
 * @type int           $group_id     Optional. The ID of a associated group.
 * @type int           $activity_id  Optional. The ID of a associated activity.
 * @type string        $privacy      Optional. Privacy of the video Default: public
 * @type int           $menu_order   Optional. Menu order the video Default: false
 * @type string        $date_created Optional. The GMT time, in Y-m-d h:i:s format, when
 *                                       the item was recorded. Defaults to the current time.
 * @type string        $error_type   Optional. Error type. Either 'bool' or 'wp_error'. Default: 'bool'.
 * }
 * @return WP_Error|bool|int The ID of the video on success. False on error.
 * @since BuddyBoss 1.7.0
 */
function bp_video_add( $args = '' ) {

	$r = bp_parse_args(
		$args,
		array(
			'id'            => false,                   // Pass an existing video ID to update an existing entry.
			'blog_id'       => get_current_blog_id(),   // Blog ID.
			'attachment_id' => false,                   // attachment id.
			'user_id'       => bp_loggedin_user_id(),   // user_id of the uploader.
			'title'         => '',                      // title of video being added.
			'description'   => '',                      // description of video being added.
			'album_id'      => false,                   // Optional: ID of the album.
			'group_id'      => false,                   // Optional: ID of the group.
			'activity_id'   => false,                   // The ID of activity.
			'message_id'    => false,                   // The ID of message.
			'privacy'       => 'public',                // Optional: privacy of the video e.g. public.
			'menu_order'    => 0,                       // Optional:  Menu order.
			'date_created'  => bp_core_current_time(),  // The GMT time that this video was recorded.
			'error_type'    => 'bool',
			'status'        => bb_video_get_published_status(), // Status of video.
		),
		'video_add'
	);

	// Setup video to be added.
	$video                = new BP_Video( $r['id'] );
	$video->blog_id       = $r['blog_id'];
	$video->attachment_id = $r['attachment_id'];
	$video->user_id       = (int) $r['user_id'];
	$video->title         = $r['title'];
	$video->description   = wp_filter_nohtml_kses( $r['description'] );
	$video->album_id      = (int) $r['album_id'];
	$video->group_id      = (int) $r['group_id'];
	$video->activity_id   = (int) $r['activity_id'];
	$video->message_id    = (int) $r['message_id'];
	$video->privacy       = $r['privacy'];
	$video->menu_order    = $r['menu_order'];
	$video->date_created  = $r['date_created'];
	$video->error_type    = $r['error_type'];
	$video->status        = $r['status'];

	// groups document always have privacy to `grouponly`.
	if ( ! empty( $video->privacy ) && ( in_array( $video->privacy, array( 'forums', 'message' ), true ) ) ) {
		$video->privacy = $r['privacy'];
	} elseif ( ! empty( $video->group_id ) ) {
		$video->privacy = 'grouponly';
		// `BP_Activity_Activity` is loaded by the activity component and is
		// NOT in the always-on autoload list. Guard against fatals when
		// activity is deactivated but a group video upload still references
		// an activity_id. Mirrors the gating in bp_media_add().
		if ( ! empty( $video->activity_id ) && bp_is_active( 'activity' ) ) {
			$activity = new BP_Activity_Activity( $video->activity_id );
			if ( ! empty( $activity ) && 'activity_comment' === $activity->type ) {
				$video->privacy = $r['privacy'];
			}
		}
	} elseif ( ! empty( $video->album_id ) ) {
		$album = new BP_Video_Album( $video->album_id );
		if ( ! empty( $album ) ) {
			$video->privacy = $album->privacy;
		}
	}

	$action = bb_filter_input_string( INPUT_POST, 'action' );
	if ( isset( $action ) && 'groups_get_group_members_send_message' === $action ) {
		$video->privacy = 'message';
	}

	// save video.
	$save = $video->save();

	if ( 'wp_error' === $r['error_type'] && is_wp_error( $save ) ) {
		return $save;
	} elseif ( 'bool' === $r['error_type'] && false === $save ) {
		return false;
	}

	// video is saved for attachment.
	update_post_meta( $video->attachment_id, 'bp_video_saved', true );
	update_post_meta( $video->attachment_id, 'bp_video_id', $video->id );

	/**
	 * Fires at the end of the execution of adding a new video item, before returning the new video item ID.
	 *
	 * @param object $video Video object.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	do_action( 'bp_video_add', $video );

	return $video->id;
}

/**
 * Video add handler function
 *
 * @param array  $videos videos array.
 * @param string $privacy privacy.
 * @param string $content content.
 * @param int    $group_id group id.
 * @param int    $album_id album id.
 *
 * @return mixed|void
 * @since BuddyBoss 1.7.0
 */
function bp_video_add_handler( $videos = array(), $privacy = 'public', $content = '', $group_id = false, $album_id = false ) {
	global $bp_video_upload_count, $bp_video_upload_activity_content;
	$video_ids = array();
	$video_id  = 0;

	$privacy = in_array( $privacy, array_keys( bp_video_get_visibility_levels() ), true ) ? $privacy : 'public';

	if ( ! empty( $videos ) && is_array( $videos ) ) {

		// update count of video for later use.
		$bp_video_upload_count = count( $videos );

		// update the content of videos for later use.
		$bp_video_upload_activity_content = $content;

		// save video.
		foreach ( $videos as $video ) {

			// Update video if existing.
			if ( ! empty( $video['video_id'] ) ) {
				$bp_video = new BP_Video( $video['video_id'] );

				if (
					! empty( $bp_video->id ) &&
					(
						bp_loggedin_user_id() === $bp_video->user_id ||
						bp_current_user_can( 'bp_moderate' )
					)
				) {

					if ( bp_is_active( 'activity' ) ) {
						$obj_activity = new BP_Activity_Activity( $bp_video->activity_id );
					}

					$video_id = bp_video_add(
						array(
							'id'            => $bp_video->id,
							'blog_id'       => $bp_video->blog_id,
							'attachment_id' => $bp_video->attachment_id,
							'user_id'       => $bp_video->user_id,
							'title'         => sanitize_text_field( wp_unslash( $bp_video->title ) ),
							'album_id'      => ! empty( $video['album_id'] ) ? $video['album_id'] : $album_id,
							'group_id'      => ! empty( $video['group_id'] ) ? $video['group_id'] : $group_id,
							'activity_id'   => $bp_video->activity_id,
							'message_id'    => ! empty( $bp_video->message_id ) ? $bp_video->message_id : ( ! empty( $video['message_id'] ) ? $video['message_id'] : 0 ),
							'privacy'       => $bp_video->privacy,
							'menu_order'    => ! empty( $video['menu_order'] ) ? $video['menu_order'] : false,
							'date_created'  => ! empty( $video['date_created'] ) ? $video['date_created'] : $bp_video->date_created,
							'status'        => ! empty( $obj_activity ) && function_exists( 'bb_get_activity_published_status' ) && bb_get_activity_published_status() === $obj_activity->status ? bb_video_get_published_status() : $bp_video->status,
						)
					);
				}
			} else {

				// Check if a video is already saved.
				if ( get_post_meta( $video['id'], 'bp_video_id', true ) ) {
					continue;
				}

				$video_id = bp_video_add(
					array(
						'attachment_id' => $video['id'],
						'title'         => sanitize_text_field( wp_unslash( $video['name'] ) ),
						'album_id'      => ! empty( $video['album_id'] ) ? $video['album_id'] : $album_id,
						'group_id'      => ! empty( $video['group_id'] ) ? $video['group_id'] : $group_id,
						'message_id'    => ! empty( $video['message_id'] ) ? $video['message_id'] : 0,
						'menu_order'    => ! empty( $video['menu_order'] ) ? $video['menu_order'] : false,
						'privacy'       => ! empty( $video['privacy'] ) && in_array( $video['privacy'], array_merge( array_keys( bp_video_get_visibility_levels() ), array( 'message' ) ), true ) ? $video['privacy'] : $privacy,
						'status'        => ! empty( $video['status'] ) ? $video['status'] : bb_video_get_published_status(),
						'date_created'  => ! empty( $video['date_created'] ) ? $video['date_created'] : bp_core_current_time(),
					)
				);

				if ( ! empty( $video['js_preview'] ) && ! empty( $video_id ) ) {
					bp_video_preview_image_by_js( $video );
				}

				if ( ! empty( $video_id ) ) {
					bp_video_add_generate_thumb_background_process( $video_id );
				}
			}

			if ( $video_id ) {
				$video_ids[] = $video_id;
			}
		}
	}

	/**
	 * Fires at the end of the execution of adding saving a video item, before returning the new video items in ajax response.
	 *
	 * @param array $video_ids Video IDs.
	 * @param array $videos    Array of video from POST object or in function parameter.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_add_handler', $video_ids, (array) $videos );
}

/**
 * Set the Preview image came via JS.
 *
 * @param array $video video array.
 *
 * @since BuddyBoss 1.7.0
 */
function bp_video_preview_image_by_js( $video ) {

	/**
	 * Hook before video js preview image.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	do_action( 'bb_before_video_preview_image_by_js' );

	// Get Upload directory.
	$upload     = wp_upload_dir();
	$upload_dir = $upload['basedir'];
	$upload_dir = $upload_dir . '/' . $video['id'] . '-video-thumbnail-' . time();

	// If folder not exists then create.
	if ( ! is_dir( $upload_dir ) ) {

		// Create temp folder.
		wp_mkdir_p( $upload_dir );
		chmod( $upload_dir, 0777 );

	}

	// Add upload filters.
	bb_video_image_add_upload_filters();

	// Register image sizes.
	bb_video_register_image_sizes();

	$str         = wp_rand();
	$unique_file = md5( $str );
	$image_name  = preg_replace( '/\\.[^.\\s]{3,4}$/', '', $unique_file );
	$thumbnail   = $upload_dir . '/' . $image_name . '.jpg';
	$file_name   = $image_name . '.jpg';

	$thumbnail = bp_video_base64_to_jpeg( $video['js_preview'], $thumbnail );

	if ( file_exists( $thumbnail ) ) {
		$upload_file = wp_upload_bits( $file_name, null, file_get_contents( $thumbnail ) ); // phpcs:ignore
		if ( ! $upload_file['error'] ) {
			$wp_filetype = wp_check_filetype( $file_name, null );
			$attachment  = array(
				'post_mime_type' => $wp_filetype['type'],
				'post_title'     => sanitize_file_name( $image_name ),
				'post_content'   => '',
				'post_status'    => 'inherit',
			);

			$preview_attachment_id = wp_insert_attachment( $attachment, $upload_file['file'] );
			if ( ! is_wp_error( $preview_attachment_id ) ) {
				if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
					require_once ABSPATH . 'wp-admin/includes/image.php';
					require_once ABSPATH . 'wp-admin/includes/file.php';
					require_once ABSPATH . 'wp-admin/includes/media.php';
				}
				$attach_data = wp_generate_attachment_metadata( $preview_attachment_id, $upload_file['file'] );
				wp_update_attachment_metadata( $preview_attachment_id, $attach_data );
				update_post_meta( $preview_attachment_id, 'is_video_preview_image', true );
				update_post_meta( $preview_attachment_id, 'video_id', $video['id'] );
				update_post_meta( $preview_attachment_id, 'bp_video_upload', 1 );

				update_post_meta( $video['id'], 'bp_video_preview_thumbnail_id', $preview_attachment_id );
				$auto_generated_thumbnails = get_post_meta( $video['id'], 'video_preview_thumbnails', true );
				$default_images            = isset( $auto_generated_thumbnails['default_images'] ) && ! empty( $auto_generated_thumbnails['default_images'] ) ? $auto_generated_thumbnails['default_images'] : array();
				$default_images            = array_merge( $default_images, array( $preview_attachment_id ) );
				$thumbnail_images          = array(
					'default_images' => $default_images,
					'custom_image'   => isset( $auto_generated_thumbnails['custom_image'] ) && ! empty( $auto_generated_thumbnails['custom_image'] ) ? $auto_generated_thumbnails['custom_image'] : array(),
				);
				update_post_meta( $video['id'], 'video_preview_thumbnails', $thumbnail_images );
			}
		}
	}

	// Remove upload filters.
	bb_video_image_remove_upload_filters();

	// Deregister image sizes.
	bb_video_deregister_image_sizes();

	bp_core_remove_temp_directory( $upload_dir );

	/**
	 * Hook after video js preview image.
	 *
	 * @since BuddyBoss 1.7.0
	 * @since BuddyBoss 2.15.0
	 * Introduced to pass the preview attachment ID to the hook.
	 *
	 * @param int $video_id Video ID.
	 * @param int $preview_attachment_id Preview attachment ID.
	 */
	do_action( 'bb_after_video_preview_image_by_js', $video['id'], $preview_attachment_id );

}

/**
 * Put the video in background process to create thumbnails.
 *
 * @param int $video_id Video id.
 *
 * @since BuddyBoss 1.7.0
 */
function bp_video_add_generate_thumb_background_process( $video_id ) {

	if ( class_exists( 'BuddyBossPlatform\FFMpeg\FFMpeg' ) || class_exists( 'FFMpeg\FFMpeg' ) ) {
		global $bb_background_updater;
		$ffmpeg = bb_video_check_is_ffmpeg_binary();

		if ( ! empty( $ffmpeg->error ) && ! empty( trim( $ffmpeg->error ) ) ) {
			return;
		}

		$video = new BP_Video( $video_id );

		if ( ! empty( $video->privacy ) && in_array(
			$video->privacy,
			array(
				'forums',
				'comment',
				'message',
			),
			true
		) ) {
			return;
		}

		/**
		 * Hook for before background process create.
		 *
		 * @since BuddyBoss 1.7.0
		 *
		 * @param BP_Video $video Video details.
		 */
		do_action( 'bp_video_before_background_process_create', $video );

		$is_auto_generated_thumbnails = get_post_meta( $video->attachment_id, 'video_preview_thumbnails', true );
		$is_default_images            = isset( $is_auto_generated_thumbnails['default_images'] ) && ! empty( $is_auto_generated_thumbnails['default_images'] ) ? $is_auto_generated_thumbnails['default_images'] : array();

		if ( count( $is_default_images ) <= 1 ) {

			$bb_background_updater->push_to_queue(
				array(
					'type'     => 'video',
					'group'    => 'video_thumbnail',
					'data_id'  => $video_id,
					'priority' => 5,
					'callback' => 'bp_video_background_create_thumbnail',
					'args'     => array( $video ),
				)
			);

			$bb_background_updater->save()->schedule_event();

		}

		/**
		 * Hook for After background process create.
		 *
		 * @since BuddyBoss 1.7.0
		 *
		 * @param BP_Video $video video detail.
		 */
		do_action( 'bp_video_after_background_process_create', $video );

	}
}

/**
 * Convert base64 to image.
 *
 * @param string $base64_string base64 string.
 * @param string $output_file   path to store the file.
 *
 * @return string image path.
 * @since BuddyBoss 1.7.0
 */
function bp_video_base64_to_jpeg( $base64_string, $output_file ) {
	// open the output file for writing.
	$ifp = fopen( $output_file, 'wb' ); // phpcs:ignore

	// split the string on commas
	// $data[ 0 ] == "data:image/png;base64"
	// $data[ 1 ] == <actual base64 string>.
	$data = explode( ',', $base64_string );

	// we could add validation here with ensuring count( $data ) > 1.
	fwrite( $ifp, base64_decode( $data[1] ) ); // phpcs:ignore

	// clean up the file resource.
	fclose( $ifp ); // phpcs:ignore

	return $output_file;
}

/**
 * Generate the video thumbnail.
 *
 * @param BP_Video $video data of video.
 */
function bp_video_background_create_thumbnail( $video ) {

	$error = '';

	if (
		! (
			(
				class_exists( 'BuddyBossPlatform\FFMpeg\FFMpeg' ) ||
				class_exists( 'FFMpeg\FFMpeg' )
			) &&
			(
				class_exists( 'BuddyBossPlatform\FFMpeg\Coordinate\TimeCode' ) ||
				class_exists( 'FFMpeg\Coordinate\TimeCode' )
			)
		)
	) {
		return;
	} elseif ( class_exists( 'BuddyBossPlatform\FFMpeg\FFMpeg' ) || class_exists( 'FFMpeg\FFMpeg' ) ) {
		$ffmpeg = bb_video_check_is_ffmpeg_binary();
		if ( ! empty( trim( $ffmpeg->error ) ) ) {
			return;
		}
	}

	$video_attachment_id = $video->attachment_id;

	if ( empty( trim( $ffmpeg->error ) ) ) {

		try {

			/**
			 * Hook before video thumbnail create in background.
			 *
			 * @since BuddyBoss 1.7.0
			 */
			do_action( 'bb_try_before_video_background_create_thumbnail', $video );

			$ff_probe = bb_video_check_is_ffprobe_binary();
			$duration = 0;

			if (
				! empty( $ff_probe->ffprob->streams( get_attached_file( $video_attachment_id ) )->videos() ) &&
				! empty( $ff_probe->ffprob->streams( get_attached_file( $video_attachment_id ) )->videos()->first() )
			) {
				$duration = $ff_probe->ffprob->streams( get_attached_file( $video_attachment_id ) )->videos()->first()->get( 'duration' );
			}

			$is_auto_generated_thumbnails = get_post_meta( $video->attachment_id, 'video_preview_thumbnails', true );
			$is_default_images            = isset( $is_auto_generated_thumbnails['default_images'] ) && ! empty( $is_auto_generated_thumbnails['default_images'] ) ? $is_auto_generated_thumbnails['default_images'] : array();

			if ( ! empty( $duration ) && count( $is_default_images ) <= 1 ) {

				/**
				 * Hook for before background thumbnail create.
				 *
				 * @since BuddyBoss 1.7.0
				 *
				 * @param BP_Video $video Video object.
				 */
				do_action( 'bp_video_before_background_create_thumbnail', $video );

				// Update video attachment meta.
				update_post_meta( $video_attachment_id, 'duration', $duration );

				// Generate 2 random images for video cover.
				$numbers = range( 1, (int) $duration );
				shuffle( $numbers );
				$random_seconds = array_slice( $numbers, 0, 2 );

				// Get Upload directory.
				$upload     = wp_upload_dir();
				$upload_dir = $upload['basedir'];
				$upload_dir = $upload_dir . '/' . $video_attachment_id . '-video-thumbnail-' . time();

				// If folder not exists then create.
				if ( ! is_dir( $upload_dir ) ) {

					// Create temp folder.
					wp_mkdir_p( $upload_dir );
					chmod( $upload_dir, 0777 );

				}

				/**
				 * Hook for before background thumbnail create.
				 *
				 * @since BuddyBoss 1.7.0
				 *
				 * @param BP_Video $video Video object.
				 */
				do_action( 'bb_video_before_preview_generate' );

				// Add upload filters.
				bb_video_image_add_upload_filters();

				// Register image sizes.
				bb_video_register_image_sizes();

				$thumbnail_list = array();
				foreach ( $random_seconds as $second ) {

					$str          = wp_rand();
					$unique_file  = md5( $str );
					$image_name   = preg_replace( '/\\.[^.\\s]{3,4}$/', '', $unique_file );
					$thumbnail    = $upload_dir . '/' . $image_name . '.jpg';
					$file_name    = $image_name . '.jpg';
					$thumb_ffmpeg = bb_video_check_is_ffmpeg_binary();
					$video_thumb  = $thumb_ffmpeg->ffmpeg->open( get_attached_file( $video_attachment_id ) );
					$thumb_frame  = $video_thumb->frame( \BuddyBoss\Library\Composer::instance()->ffmpeg_instance()->timecode_from_seconds( $second ) );

					$error = '';
					try {
						$saved = $thumb_frame->save( $thumbnail );
					} catch ( Exception $saved ) {
						$error = 'error';
					}

					unset( $thumb_ffmpeg );
					unset( $video_thumb );
					unset( $thumb_frame );

					if ( file_exists( $thumbnail ) ) {
						$upload_file = wp_upload_bits( $file_name, null, file_get_contents( $thumbnail ) ); // phpcs:ignore
						if ( ! $upload_file['error'] ) {
							$wp_filetype = wp_check_filetype( $file_name, null );
							$attachment  = array(
								'post_mime_type' => $wp_filetype['type'],
								'post_title'     => sanitize_file_name( $image_name ),
								'post_content'   => '',
								'post_status'    => 'inherit',
							);

							$preview_attachment_id = wp_insert_attachment( $attachment, $upload_file['file'] );

							if ( ! is_wp_error( $preview_attachment_id ) ) {
								if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
									require_once ABSPATH . 'wp-admin/includes/image.php';
									require_once ABSPATH . 'wp-admin/includes/file.php';
									require_once ABSPATH . 'wp-admin/includes/media.php';
								}
								$attach_data = wp_generate_attachment_metadata( $preview_attachment_id, $upload_file['file'] );
								wp_update_attachment_metadata( $preview_attachment_id, $attach_data );
								$thumbnail_list[] = $preview_attachment_id;
								update_post_meta( $preview_attachment_id, 'is_video_preview_image', true );
								update_post_meta( $preview_attachment_id, 'video_id', $video->id );
								update_post_meta( $preview_attachment_id, 'bp_video_upload', 1 );
							}
						}
					}
				}

				// Remove upload filters.
				bb_video_image_remove_upload_filters();

				// Deregister image sizes.
				bb_video_deregister_image_sizes();

				bp_core_remove_temp_directory( $upload_dir );

				if ( is_array( $thumbnail_list ) && ! empty( $thumbnail_list ) ) {

					$thumbnail_images = get_post_meta( $video_attachment_id, 'video_preview_thumbnails', true );
					if ( isset( $thumbnail_images['default_images'] ) && ! empty( $thumbnail_images['default_images'] ) ) {
						$thumbnail_list = array_merge( $thumbnail_images['default_images'], $thumbnail_list );
					}
					$updated_thumbnail_images = array(
						'default_images' => $thumbnail_list,
						'custom_image'   => ( isset( $thumbnail_images['custom_image'] ) && ! empty( $thumbnail_images['custom_image'] ) ) ? $thumbnail_images['custom_image'] : '',
					);
					update_post_meta( $video_attachment_id, 'video_preview_thumbnails', $updated_thumbnail_images );
					$get_existing = get_post_meta( $video_attachment_id, 'bp_video_preview_thumbnail_id', true );
					if ( ! $get_existing ) {
						update_post_meta( $video_attachment_id, 'bp_video_preview_thumbnail_id', current( $thumbnail_list ) );
					}
					update_post_meta( $video_attachment_id, 'bb_ffmpeg_preview_generated', 'yes' );
				}

				/**
				 * Hook for After preview generate.
				 *
				 * @since BuddyBoss 1.7.0
				 */
				do_action( 'bb_video_after_preview_generate' );

				/**
				 * Hook for After background thumbnail create.
				 *
				 * @since BuddyBoss 1.7.0
				 *
				 * @param BP_Video $video_id Video object.
				 */
				do_action( 'bp_video_after_background_create_thumbnail', $video );
			} else {
				update_post_meta( $video_attachment_id, 'bb_ffmpeg_preview_generated', ( ! empty( $is_default_images ) ? 'yes' : 'no' ) );
			}

			/**
			 * Hook for After video background thumbnail create.
			 *
			 * @since BuddyBoss 1.7.0
			 *
			 * @param BP_Video $video_id Video object.
			 */
			do_action( 'bb_try_after_video_background_create_thumbnail', $video );

		} catch ( Exception $ex ) {
			update_post_meta( $video_attachment_id, 'bb_ffmpeg_preview_generated', 'no' );
		}
	} else {
		update_post_meta( $video_attachment_id, 'bb_ffmpeg_preview_generated', 'no' );
	}
}

/**
 * Delete video.
 *
 * @param array|string $args To delete specific video items, use
 *                           $args = array( 'id' => $ids ); Otherwise, to use
 *                           filters for item deletion, the argument format is
 *                           the same as BP_Video::get().
 *                           See that method for a description.
 * @param bool         $from Context of deletion from. ex. attachment, activity etc.
 *
 * @return bool|int The ID of the video on success. False on error.
 * @since BuddyBoss 1.7.0
 */
function bp_video_delete( $args = '', $from = false ) {

	// Pass one or more the of following variables to delete by those variables.
	$args = bp_parse_args(
		$args,
		array(
			'id'            => false,
			'blog_id'       => false,
			'attachment_id' => false,
			'user_id'       => false,
			'title'         => false,
			'album_id'      => false,
			'activity_id'   => false,
			'group_id'      => false,
			'privacy'       => false,
			'date_created'  => false,
			'status'        => false,
		)
	);

	/**
	 * Fires before an video item proceeds to be deleted.
	 *
	 * @param array $args Array of arguments to be used with the video deletion.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	do_action( 'bp_before_video_delete', $args );

	$video_ids_deleted = BP_Video::delete( $args, $from );
	if ( empty( $video_ids_deleted ) ) {
		return false;
	}

	/**
	 * Fires after the video item has been deleted.
	 *
	 * @param array $args Array of arguments used with the video deletion.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	do_action( 'bp_video_delete', $args );

	/**
	 * Fires after the video item has been deleted.
	 *
	 * @param array $video_ids_deleted Array of affected video item IDs.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	do_action( 'bp_video_deleted_videos', $video_ids_deleted );

	return true;
}

/**
 * Completely remove a user's video data.
 *
 * @param int $user_id ID of the user whose video is being deleted.
 *
 * @return bool
 * @since BuddyBoss 1.7.0
 */
function bp_video_remove_all_user_data( $user_id = 0 ) {
	if ( empty( $user_id ) ) {
		return false;
	}

	// Clear the user's albums from the sitewide stream and clear their video tables.
	bp_video_album_delete( array( 'user_id' => $user_id ) );

	// Clear the user's video from the sitewide stream and clear their video tables.
	bp_video_delete( array( 'user_id' => $user_id ) );

	/**
	 * Fires after the removal of all of a user's video data.
	 *
	 * @param int $user_id ID of the user being deleted.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	do_action( 'bp_video_remove_all_user_data', $user_id );
}

add_action( 'wpmu_delete_user', 'bp_video_remove_all_user_data' );
add_action( 'delete_user', 'bp_video_remove_all_user_data' );

/**
 * Get video visibility levels out of the $bp global.
 *
 * @return array
 * @since BuddyBoss 1.7.0
 */
function bp_video_get_visibility_levels() {

	/**
	 * Filters the video visibility levels out of the $bp global.
	 *
	 * @param array $visibility_levels Array of visibility levels.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_get_visibility_levels', buddypress()->video->visibility_levels );
}

/**
 * Return the video activity.
 *
 * @param  int $activity_id activity id.
 *
 * @return object|boolean The video activity object or false.
 * @global object $video_template {@link BP_Video_Template}
 *
 * @since BuddyBoss 1.7.0
 */
function bp_video_get_video_activity( $activity_id ) {

	if ( ! bp_is_active( 'activity' ) ) {
		return false;
	}

	$result = bp_activity_get(
		array(
			'in' => $activity_id,
		)
	);

	if ( empty( $result['activities'][0] ) ) {
		return false;
	}

	/**
	 * Filters the video activity object being displayed.
	 *
	 * @param object $activity The video activity.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_get_video_activity', $result['activities'][0] );
}

/**
 * Get the video count of a user.
 *
 * @return int video count of the user.
 * @since BuddyBoss 1.7.0
 */
function bp_video_get_total_video_count() {

	add_filter( 'bp_ajax_querystring', 'bp_video_object_template_results_video_personal_scope', 20 );
	bp_has_video( bp_ajax_querystring( 'video' ) . '&count_total=true&fields=ids' );
	$count = $GLOBALS['video_template']->total_video_count;
	remove_filter( 'bp_ajax_querystring', 'bp_video_object_template_results_video_personal_scope', 20 );

	/**
	 * Filters the total video count for a given user.
	 *
	 * @param int $count Total video count for a given user.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_get_total_video_count', (int) $count );
}

/**
 * Get the groups video count of a given user.
 *
 * @return int video count of the user.
 * @since BuddyBoss 1.7.0
 */
function bp_video_get_user_total_group_video_count() {

	add_filter( 'bp_ajax_querystring', 'bp_video_object_template_results_video_groups_scope', 20 );
	bp_has_video( bp_ajax_querystring( 'groups' ) );
	$count = $GLOBALS['video_template']->total_video_count;
	remove_filter( 'bp_ajax_querystring', 'bp_video_object_template_results_video_groups_scope', 20 );

	/**
	 * Filters the total groups video count for a given user.
	 *
	 * @param int $count Total video count for a given user.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_get_user_total_group_video_count', (int) $count );
}

/**
 * Get the video count of a given group.
 *
 * @param int $group_id ID of the group whose video are being counted.
 *
 * @return int video count of the group.
 * @since BuddyBoss 1.7.0
 */
function bp_video_get_total_group_video_count( $group_id = 0 ) {
	if ( empty( $group_id ) && bp_get_current_group_id() ) {
		$group_id = bp_get_current_group_id();
	}

	$count = wp_cache_get( 'bp_total_video_for_group_' . $group_id, 'bp' );
	if ( false === $count ) {
		$count = BP_Video::total_group_video_count( $group_id );
		wp_cache_set( 'bp_total_video_for_group_' . $group_id, $count, 'bp' );
	}

	/**
	 * Filters the total video count for a given group.
	 *
	 * @param int $count Total video count for a given group.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_get_total_group_video_count', (int) $count );
}

/**
 * Get the album count of a given group.
 *
 * @param int $group_id ID of the group whose album are being counted.
 *
 * @return int album count of the group.
 * @since BuddyBoss 1.7.0
 */
function bp_video_get_total_group_album_count( $group_id = 0 ) {
	if ( empty( $group_id ) && bp_get_current_group_id() ) {
		$group_id = bp_get_current_group_id();
	}

	$count = wp_cache_get( 'bp_total_album_for_group_' . $group_id, 'bp' );

	if ( false === $count ) {
		$count = BP_Video_Album::total_group_album_count( $group_id );
		wp_cache_set( 'bp_total_album_for_group_' . $group_id, $count, 'bp' );
	}

	/**
	 * Filters the total album count for a given group.
	 *
	 * @param int $count Total album count for a given group.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_get_total_group_album_count', (int) $count );
}

/**
 * Return the total video count in your BP instance.
 *
 * @return int Video count.
 * @since BuddyBoss 1.7.0
 */
function bp_get_total_video_count() {

	add_filter( 'bp_ajax_querystring', 'bp_video_object_results_video_all_scope', 20 );
	bp_has_video( bp_ajax_querystring( 'video' ) );
	remove_filter( 'bp_ajax_querystring', 'bp_video_object_results_video_all_scope', 20 );
	$count = $GLOBALS['video_template']->total_video_count;

	/**
	 * Filters the total number of video.
	 *
	 * @param int $count Total number of video.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_get_total_video_count', (int) $count );
}

/**
 * Video results all scope.
 *
 * @since BuddyBoss 1.7.0
 *
 * @param string $querystring query string.
 *
 * @return string
 */
function bp_video_object_results_video_all_scope( $querystring ) {
	$querystring = bp_parse_args( $querystring );

	$querystring['scope']       = 'all';
	$querystring['page']        = 1;
	$querystring['per_page']    = 1;
	$querystring['user_id']     = 0;
	$querystring['count_total'] = true;

	return http_build_query( $querystring );
}


/**
 * Object template results video personal scope.
 *
 * @since BuddyBoss 1.7.0
 *
 * @param string $querystring query string.
 *
 * @return string
 */
function bp_video_object_template_results_video_personal_scope( $querystring ) {
	$querystring = bp_parse_args( $querystring );

	$querystring['scope']       = 'personal';
	$querystring['page']        = 1;
	$querystring['per_page']    = '1';
	$querystring['user_id']     = ( bp_displayed_user_id() ) ? bp_displayed_user_id() : bp_loggedin_user_id();
	$querystring['count_total'] = true;

	return http_build_query( $querystring );
}

/**
 * Object template results video groups scope.
 *
 * @since BuddyBoss 1.7.0
 *
 * @param string $querystring query string.
 *
 * @return string
 */
function bp_video_object_template_results_video_groups_scope( $querystring ) {
	$querystring = bp_parse_args( $querystring );

	$querystring['scope']       = 'groups';
	$querystring['page']        = 1;
	$querystring['per_page']    = 1;
	$querystring['user_id']     = false;
	$querystring['count_total'] = true;

	return http_build_query( $querystring );
}

// ******************** Albums *********************/
/**
 * Retrieve an album or albums.
 *
 * The bp_video_album_get() function shares all arguments with BP_Video_Album::get().
 * The following is a list of bp_video_album_get() parameters that have different
 * default values from BP_Video_Album::get() (value in parentheses is
 * the default for the bp_video_album_get()).
 *   - 'per_page' (false)
 *
 * @param array|string $args See BP_Video_Album::get() for description.
 *
 * @return array $activity See BP_Video_Album::get() for description.
 * @since BuddyBoss 1.7.0
 *
 * @see   BP_Video_Album::get() For more information on accepted arguments
 *      and the format of the returned value.
 */
function bp_video_album_get( $args = '' ) {

	$r = bp_parse_args(
		$args,
		array(
			'max'              => false,           // Maximum number of results to return.
			'fields'           => 'all',
			'page'             => 1,               // Page 1 without a per_page will result in no pagination.
			'per_page'         => false,           // results per page.
			'sort'             => 'DESC',          // sort ASC or DESC.

			'search_terms'     => false,           // Pass search terms as a string.
			'exclude'          => false,           // Comma-separated list of activity IDs to exclude.
			// want to limit the query.
			'user_id'          => false,
			'group_id'         => false,
			'privacy'          => false,           // privacy of album.
			'moderation_query' => true,            // Filter to include moderation query.
			'count_total'      => false,
		),
		'video_album_get'
	);

	$album = BP_Video_Album::get(
		array(
			'page'             => $r['page'],
			'per_page'         => $r['per_page'],
			'user_id'          => $r['user_id'],
			'group_id'         => $r['group_id'],
			'privacy'          => $r['privacy'],
			'max'              => $r['max'],
			'sort'             => $r['sort'],
			'search_terms'     => $r['search_terms'],
			'exclude'          => $r['exclude'],
			'count_total'      => $r['count_total'],
			'fields'           => $r['fields'],
			'moderation_query' => $r['moderation_query'],
		)
	);

	/**
	 * Filters the requested album item(s).
	 *
	 * @param BP_Video $album Requested video object.
	 * @param array    $r     Arguments used for the album query.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters_ref_array( 'bp_video_album_get', array( &$album, &$r ) );
}

/**
 * Fetch specific albums.
 *
 * @param array|string $args {
 *                           All arguments and defaults are shared with BP_Video_Album::get(),
 *                           except for the following:
 *
 * @type string|int|array Single album ID, comma-separated list of IDs,
 *                            or array of IDs.
 * }
 * @return array $albums See BP_Video_Album::get() for description.
 * @since BuddyBoss 1.7.0
 *
 * @see   BP_Video_Album::get() For more information on accepted arguments.
 */
function bp_video_album_get_specific( $args = '' ) {

	$r = bp_parse_args(
		$args,
		array(
			'album_ids'         => false,      // A single album id or array of IDs.
			'max'               => false,      // Maximum number of results to return.
			'page'              => 1,          // Page 1 without a per_page will result in no pagination.
			'per_page'          => false,      // Results per page.
			'sort'              => 'DESC',     // Sort ASC or DESC.
			'update_meta_cache' => true,
			'count_total'       => false,
		),
		'video_get_specific'
	);

	$get_args = array(
		'in'          => $r['album_ids'],
		'max'         => $r['max'],
		'page'        => $r['page'],
		'per_page'    => $r['per_page'],
		'sort'        => $r['sort'],
		'count_total' => $r['count_total'],
	);

	/**
	 * Filters the requested specific album item.
	 *
	 * @param BP_Video $album    Requested video object.
	 * @param array    $args     Original passed in arguments.
	 * @param array    $get_args Constructed arguments used with request.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_album_get_specific', BP_Video_Album::get( $get_args ), $args, $get_args );
}

/**
 * Add album item.
 *
 * @param array|string $args         {
 *                                   An array of arguments.
 *
 * @type int|bool      $id           Pass an activity ID to update an existing item, or
 *                                       false to create a new item. Default: false.
 * @type int|bool      $user_id      Optional. The ID of the user associated with the album
 *                                       item. May be set to false or 0 if the item is not related
 *                                       to any user. Default: the ID of the currently logged-in user.
 * @type int           $group_id     Optional. The ID of the associated group.
 * @type string        $title        The title of album.
 * @type string        $privacy      The privacy of album.
 * @type string        $date_created Optional. The GMT time, in Y-m-d h:i:s format, when
 *                                       the item was recorded. Defaults to the current time.
 * @type string        $error_type   Optional. Error type. Either 'bool' or 'wp_error'. Default: 'bool'.
 * }
 * @return WP_Error|bool|int The ID of the album on success. False on error.
 * @since BuddyBoss 1.7.0
 */
function bp_video_album_add( $args = '' ) {

	$r = bp_parse_args(
		$args,
		array(
			'id'           => false,
			// Pass an existing album ID to update an existing entry.
			'user_id'      => bp_loggedin_user_id(),
			// User ID.
			'group_id'     => false,
			// attachment id.
			'title'        => '',
			// title of album being added.
			'privacy'      => 'public',
			// Optional: privacy of the video e.g. public.
			'date_created' => bp_core_current_time(),
			// The GMT time that this video was recorded.
			'error_type'   => 'bool',
		),
		'album_add'
	);

	// Setup video to be added.
	$album               = new BP_Video_Album( $r['id'] );
	$album->user_id      = (int) $r['user_id'];
	$album->group_id     = (int) $r['group_id'];
	$album->title        = $r['title'];
	$album->privacy      = $r['privacy'];
	$album->date_created = $r['date_created'];
	$album->error_type   = $r['error_type'];

	if ( ! empty( $album->group_id ) ) {
		$album->privacy = 'grouponly';
	}

	$save = $album->save();

	if ( 'wp_error' === $r['error_type'] && is_wp_error( $save ) ) {
		return $save;
	} elseif ( 'bool' === $r['error_type'] && false === $save ) {
		return false;
	}

	/**
	 * Fires at the end of the execution of adding a new album item, before returning the new album item ID.
	 *
	 * @param object $album Album object.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	do_action( 'bp_video_album_add', $album );

	return $album->id;
}

/**
 * Delete album item.
 *
 * @param array|string $args To delete specific album items, use
 *                           $args = array( 'id' => $ids ); Otherwise, to use
 *                           filters for item deletion, the argument format is
 *                           the same as BP_Video_Album::get().
 *                           See that method for a description.
 *
 * @return bool True on Success. False on error.
 * @since BuddyBoss 1.7.0
 */
function bp_video_album_delete( $args ) {

	// Pass one or more the of following variables to delete by those variables.
	$args = bp_parse_args(
		$args,
		array(
			'id'           => false,
			'user_id'      => false,
			'group_id'     => false,
			'date_created' => false,
		)
	);

	/**
	 * Fires before an album item proceeds to be deleted.
	 *
	 * @param array $args Array of arguments to be used with the album deletion.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	do_action( 'bp_before_video_album_delete', $args );

	$album_ids_deleted = BP_Video_Album::delete( $args );
	if ( empty( $album_ids_deleted ) ) {
		return false;
	}

	/**
	 * Fires after the album item has been deleted.
	 *
	 * @param array $args Array of arguments used with the album deletion.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	do_action( 'bp_video_album_delete', $args );

	/**
	 * Fires after the album item has been deleted.
	 *
	 * @param array $album_ids_deleted Array of affected album item IDs.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	do_action( 'bp_video_albums_deleted_albums', $album_ids_deleted );

	return true;
}

/**
 * Fetch a single album object.
 *
 * When calling up a album object, you should always use this function instead
 * of instantiating BP_Video_Album directly, so that you will inherit cache
 * support and pass through the albums_get_album filter.
 *
 * @param int $album_id ID of the album.
 *
 * @return BP_Video_Album $album The album object.
 * @since BuddyBoss 1.7.0
 */
function albums_get_video_album( $album_id ) {

	$album = new BP_Video_Album( $album_id );

	/**
	 * Filters a single album object.
	 *
	 * @param BP_Video_Album $album Single album object.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'albums_get_video_album', $album );
}

/**
 * Check album access for current user or guest
 *
 * @param int $album_id album id.
 *
 * @return bool
 * @since BuddyBoss 1.7.0
 */
function albums_check_video_album_access( $album_id ) {

	$album = albums_get_video_album( $album_id );

	if ( ! empty( $album->group_id ) ) {
		return false;
	}

	if ( ! empty( $album->privacy ) ) {

		if ( 'public' === $album->privacy ) {
			return true;
		}

		if ( 'loggedin' === $album->privacy && is_user_logged_in() ) {
			return true;
		}

		if ( bp_is_active( 'friends' ) && is_user_logged_in() && 'friends' === $album->privacy && friends_check_friendship( get_current_user_id(), $album->user_id ) ) {
			return true;
		}

		if ( bp_is_my_profile() && bp_loggedin_user_id() === (int) $album->user_id && 'onlyme' === $album->privacy ) {
			return true;
		}
	}

	return false;
}

/**
 * Delete orphaned attachments uploaded
 *
 * @since BuddyBoss 1.7.0
 */
function bp_video_delete_orphaned_attachments() {

	/**
	 * Removed the WP_Query because it's conflicting with other plugins which hare using non-standard way using the
	 * pre_get_posts & ajax_query_attachments_args hook & filter and it's getting all the media ids and it will remove
	 * all the media from Media Library.
	 *
	 * @since BuddyBoss 1.7.6
	 */
	global $wpdb;
	$post_table              = $wpdb->posts;
	$postmeta_table          = $wpdb->postmeta;
	$six_hours_ago_timestamp = strtotime( '-6 hours', current_time( 'timestamp', 1 ) );
	$six_hours_ago           = date( 'Y-m-d H:i:s', $six_hours_ago_timestamp );

	$query = "SELECT {$post_table}.ID
				FROM {$post_table}
				LEFT JOIN {$postmeta_table} ON ( {$post_table}.ID = {$postmeta_table}.post_id )
				LEFT JOIN {$postmeta_table} AS mt1
					ON ( {$post_table}.ID = mt1.post_id AND mt1.meta_key = 'bb_media_draft' )
					WHERE 1=1 AND
						( {$post_table}.post_date_gmt < '{$six_hours_ago}' ) AND
						(
							(
								{$postmeta_table}.meta_key = 'bp_video_saved' AND
								{$postmeta_table}.meta_value = '0'
							) AND
							mt1.post_id IS NULL
						) AND
						{$post_table}.post_type = 'attachment' AND
						( {$post_table}.post_status = 'inherit' )
				GROUP BY {$post_table}.ID
				ORDER BY {$post_table}.post_date DESC";

	$video_wp_query_posts = $wpdb->get_col( $query );
	if ( ! empty( $video_wp_query_posts ) ) {
		foreach ( $video_wp_query_posts as $post_id ) {
			wp_delete_attachment( $post_id, true );
		}
	}
}

/**
 * Download an image from the specified URL and attach it to a post.
 *
 * @param string $file The URL of the image to download.
 *
 * @return int|void
 * @since BuddyBoss 1.7.0
 */
function bp_video_sideload_attachment( $file ) {
	if ( empty( $file ) ) {
		return;
	}

	// Set variables for storage, fix file filename for query strings.
	preg_match( '/[^\?]+\.(jpe?g|jpe|gif|png|svg|bmp|mp4)\b/i', $file, $matches );
	$file_array = array();

	if ( empty( $matches ) ) {
		return;
	}

	$file_array['name'] = basename( $matches[0] );

	// Download file to temp location.
	$file = preg_replace( '/^:*?\/\//', $protocol = strtolower( substr( $_SERVER['SERVER_PROTOCOL'], 0, strpos( $_SERVER['SERVER_PROTOCOL'], '/' ) ) ) . '://', $file ); // phpcs:ignore

	if ( ! function_exists( 'download_url' ) ) {
		require_once ABSPATH . 'wp-admin/includes/image.php';
		require_once ABSPATH . 'wp-admin/includes/file.php';
		require_once ABSPATH . 'wp-admin/includes/media.php';
	}
	$file_array['tmp_name'] = download_url( $file );

	// If error storing temporarily, return the error.
	if ( is_wp_error( $file_array['tmp_name'] ) ) {
		return;
	}

	// Do the validation and storage stuff.
	$id = bp_video_handle_sideload( $file_array );

	// If error storing permanently, unlink.
	if ( is_wp_error( $id ) ) {
		return;
	}

	return $id;
}

/**
 * This handles a sideloaded file in the same way as an uploaded file is handled by {@link media_handle_upload()}
 *
 * @param array $file_array Array similar to a {@link $_FILES} upload array.
 * @param array $post_data  allows you to overwrite some of the attachment.
 *
 * @return int|object The ID of the attachment or a WP_Error on failure
 * @since BuddyBoss 1.7.0
 */
function bp_video_handle_sideload( $file_array, $post_data = array() ) {

	$overrides = array( 'test_form' => false );

	$time = current_time( 'mysql' );
	$post = get_post();
	if ( $post ) {
		if ( substr( $post->post_date, 0, 4 ) > 0 ) {
			$time = $post->post_date;
		}
	}

	$file = wp_handle_sideload( $file_array, $overrides, $time );
	if ( isset( $file['error'] ) ) {
		return new WP_Error( 'upload_error', $file['error'] );
	}

	$url     = $file['url'];
	$type    = $file['type'];
	$file    = $file['file'];
	$title   = preg_replace( '/\.[^.]+$/', '', basename( $file ) );
	$content = '';

	// Use image exif/iptc data for title and caption defaults if possible.
	if ( $image_meta = @wp_read_image_metadata( $file ) ) { // phpcs:ignore
		if ( trim( $image_meta['title'] ) && ! is_numeric( sanitize_title( $image_meta['title'] ) ) ) {
			$title = $image_meta['title'];
		}
		if ( trim( $image_meta['caption'] ) ) {
			$content = $image_meta['caption'];
		}
	}

	if ( isset( $desc ) ) {
		$title = $desc;
	}

	// Construct the attachment array.
	$attachment = array_merge(
		array(
			'post_mime_type' => $type,
			'guid'           => $url,
			'post_title'     => $title,
			'post_content'   => $content,
		),
		$post_data
	);

	// This should never be set as it would then overwrite an existing attachment.
	if ( isset( $attachment['ID'] ) ) {
		unset( $attachment['ID'] );
	}

	// Save the attachment metadata.
	$id = wp_insert_attachment( $attachment, $file );

	if ( ! is_wp_error( $id ) ) {
		wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $file ) );
	}

	return $id;
}

/**
 * Function to add the content on top of video listing
 *
 * @since BuddyBoss 1.7.0
 */
function bp_video_directory_page_content() {

	$page_ids = bp_core_get_directory_page_ids();

	if ( ! empty( $page_ids['video'] ) ) {
		$video_page_content = get_post_field( 'post_content', $page_ids['video'] );
		echo apply_filters( 'the_content', $video_page_content ); // phpcs:ignore
	}
}

add_action( 'bp_before_directory_video', 'bp_video_directory_page_content' );

/**
 * Get video id for the attachment.
 *
 * @param int $attachment_id attachment id.
 *
 * @return array|bool
 * @since BuddyBoss 1.7.0
 */
function bp_get_attachment_video_id( $attachment_id = 0 ) {
	global $bp, $wpdb;

	if ( ! $attachment_id ) {
		return false;
	}

	$attachment_video_id = (int) $wpdb->get_var( "SELECT DISTINCT m.id FROM {$bp->video->table_name} m WHERE m.attachment_id = {$attachment_id}" ); //phpcs:ignore

	return $attachment_video_id;
}

/**
 * Get list of privacy based on user and group.
 *
 * @param int    $user_id  User ID.
 * @param int    $group_id Group ID.
 * @param string $scope    Scope query parameter.
 *
 * @return mixed|void
 * @since BuddyBoss 1.7.0
 */
function bp_video_query_privacy( $user_id = 0, $group_id = 0, $scope = '' ) {

	$privacy = array( 'public' );

	if ( is_user_logged_in() ) {
		// User filtering.
		$user_id = (int) (
		empty( $user_id )
			? ( bp_displayed_user_id() ? bp_displayed_user_id() : false )
			: $user_id
		);

		$privacy[] = 'loggedin';

		if ( bp_is_my_profile() || bp_loggedin_user_id() === (int) $user_id ) {
			$privacy[] = 'onlyme';

			if ( bp_is_active( 'friends' ) ) {
				$privacy[] = 'friends';
			}
		}

		if ( ! in_array( 'friends', $privacy ) && bp_is_active( 'friends' ) ) { // phpcs:ignore

			// get the login user id.
			$current_user_id = bp_loggedin_user_id();

			// check if the login user is friends of the display user.
			$is_friend = friends_check_friendship( $current_user_id, $user_id );

			/**
			 * Check if the login user is friends of the display user
			 * OR check if the login user and the display user is the same
			 */
			if ( $is_friend ) {
				$privacy[] = 'friends';
			}
		}
	}

	if (
		bp_is_group()
		|| ( bp_is_active( 'groups' ) && ! empty( $group_id ) )
		|| ( ! empty( $scope ) && 'groups' === $scope )
	) {
		$privacy = array( 'grouponly' );
	}

	/**
	 * Filter to video query privacy.
	 *
	 * @param array  $privacy  proivacy array
	 * @param int    $user_id  user id
	 * @param int    $group_id group id
	 * @param string $scope    scope
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_query_privacy', $privacy, $user_id, $group_id, $scope );
}

/**
 * Update activity video privacy based on activity.
 *
 * @param int    $activity_id Activity ID.
 * @param string $privacy     Privacy.
 *
 * @since BuddyBoss 1.7.0
 */
function bp_video_update_activity_privacy( $activity_id = 0, $privacy = '' ) {
	global $wpdb, $bp;

	if ( empty( $activity_id ) || empty( $privacy ) ) {
		return;
	}

	// Update privacy for the video which are uploaded in activity.
	$video_ids = bp_activity_get_meta( $activity_id, 'bp_video_ids', true );
	if ( ! empty( $video_ids ) ) {
		$video_ids = explode( ',', $video_ids );
		if ( ! empty( $video_ids ) ) {
			foreach ( $video_ids as $id ) {
				$video = new BP_Video( $id );
				if ( ! empty( $video->id ) ) {
					$video->privacy = $privacy;
					$video->save();
				}
			}
		}
	}
}

/**
 * Get default scope for the video.
 *
 * @param string $scope Default scope.
 *
 * @return string
 * @since BuddyBoss 1.7.0
 */
function bp_video_default_scope( $scope = 'all' ) {

	$new_scope = array();

	$allowed_scopes = array( 'public', 'all' );
	if ( is_user_logged_in() && bp_is_active( 'friends' ) && bp_is_profile_video_support_enabled() ) {
		$allowed_scopes[] = 'friends';
	}

	if ( bp_is_active( 'groups' ) && bp_is_group_video_support_enabled() ) {
		$allowed_scopes[] = 'groups';
	}

	if ( ( is_user_logged_in() || bp_is_user_video() ) && bp_is_profile_video_support_enabled() ) {
		$allowed_scopes[] = 'personal';
	}

	if ( ( 'all' === $scope || empty( $scope ) ) && bp_is_video_directory() ) {

		$new_scope[] = 'public';

		if ( is_user_logged_in() && bp_is_active( 'friends' ) && bp_is_profile_video_support_enabled() ) {
			$new_scope[] = 'friends';
		}

		if ( bp_is_active( 'groups' ) && bp_is_group_video_support_enabled() ) {
			$new_scope[] = 'groups';
		}

		if ( is_user_logged_in() && bp_is_profile_video_support_enabled() ) {
			$new_scope[] = 'personal';
		}
	} elseif ( bp_is_user_video() && ( 'all' === $scope || empty( $scope ) ) && bp_is_profile_video_support_enabled() ) {
		$new_scope[] = 'personal';
	} elseif ( bp_is_active( 'groups' ) && bp_is_group() && ( 'all' === $scope || empty( $scope ) ) ) {
		$new_scope[] = 'groups';
	}

	if ( empty( $new_scope ) ) {
		$new_scope = (array) ( ! is_array( $scope ) ? explode( ',', trim( $scope ) ) : $scope );
	}

	// Remove duplicate scope if added.
	$new_scope = array_unique( $new_scope );

	// Remove all unwanted scope.
	$new_scope = array_intersect( $allowed_scopes, $new_scope );

	/**
	 * Filter to update default scope.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	$new_scope = apply_filters( 'bp_video_default_scope', $new_scope );

	return implode( ',', $new_scope );

}

/**
 * Get the thread id based on video.
 *
 * @param string|int $video_id video id.
 *
 * @return mixed|void
 */
function bp_video_get_thread_id( $video_id ) {

	$thread_id = 0;

	if ( bp_is_active( 'messages' ) ) {
		$meta = array(
			array(
				'key'     => 'bp_video_ids',
				'value'   => $video_id,
				'compare' => 'LIKE',
			),
		);

		// Check if there is already previously individual group thread created.
		if ( bp_has_message_threads( array( 'meta_query' => $meta ) ) ) { // phpcs:ignore
			while ( bp_message_threads() ) {
				bp_message_thread();
				$thread_id = bp_get_message_thread_id();
				if ( $thread_id ) {
					break;
				}
			}
		}

		if ( empty( $thread_id ) ) {
			$video_object = new BP_Video( $video_id );
			if ( ! empty( $video_object->attachment_id ) ) {
				$thread_id = get_post_meta( $video_object->attachment_id, 'thread_id', true );
			}
		}
	}

	/**
	 * Filter to get video thread id.
	 *
	 * @param int $thread_id  user id
	 * @param int $video_id   video id
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_get_thread_id', $thread_id, $video_id );

}

/**
 * Return download link of the video.
 *
 * @param int $attachment_id attachment id.
 * @param int $video_id video id.
 *
 * @return mixed|void
 * @since BuddyBoss 1.7.0
 */
function bp_video_download_link( $attachment_id, $video_id ) {

	if ( empty( $attachment_id ) ) {
		return;
	}

	$link = site_url() . '/?attachment_id=' . $attachment_id . '&video_type=video&download_video_file=1&video_file=' . $video_id;

	/**
	 * Filter to get video download link.
	 *
	 * @param string $link  download link
	 * @param int    $attachment_id   atttachment id
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_download_link', $link, $attachment_id );

}

/**
 * Check if user have a access to download the file. If not redirect to homepage.
 *
 * @since BuddyBoss 1.7.0
 */
function bp_video_download_url_file() {
	$attachment_id       = bb_filter_input_string( INPUT_GET, 'attachment_id' );
	$download_video_file = bb_filter_input_string( INPUT_GET, 'download_video_file' );
	$video_file          = bb_filter_input_string( INPUT_GET, 'video_file' );
	$video_type          = bb_filter_input_string( INPUT_GET, 'video_type' );
	$can_download_btn    = false;

	if ( isset( $attachment_id ) && isset( $download_video_file ) && isset( $video_file ) && isset( $video_type ) ) { // phpcs:ignore WordPress.Security.NonceVerification
		if ( 'album' !== $video_type ) {

			// Remove action to remove meta query for forums while download check.
			remove_action( 'pre_get_posts', 'bbp_pre_get_posts_normalize_forum_visibility', 4 );

			$video_privacy    = bb_media_user_can_access( $video_file, 'video', $attachment_id ); // phpcs:ignore WordPress.Security.NonceVerification
			$can_download_btn = true === (bool) $video_privacy['can_download'];

			// Add pre_get_posts action hook back.
			add_action( 'pre_get_posts', 'bbp_pre_get_posts_normalize_forum_visibility', 4 );
		}
		if ( $can_download_btn ) {
			bp_video_download_file( $attachment_id, $video_type ); // phpcs:ignore WordPress.Security.NonceVerification
		} else {
			wp_safe_redirect( site_url() );
			exit();
		}
	}
}

/**
 * Filter headers for IE to fix issues over SSL.
 *
 * IE bug prevents download via SSL when Cache Control and Pragma no-cache headers set.
 *
 * @param array $headers HTTP headers.
 *
 * @return array
 */
function bp_video_ie_nocache_headers_fix( $headers ) {
	if ( is_ssl() && ! empty( $GLOBALS['is_IE'] ) ) {
		$headers['Cache-Control'] = 'private';
		unset( $headers['Pragma'] );
	}

	return $headers;
}

/**
 * Get the frorum id based on video.
 *
 * @param int|string $video_id video id.
 *
 * @return mixed|void
 */
function bp_video_get_forum_id( $video_id ) {

	if ( ! bp_is_active( 'forums' ) ) {
		return;
	}

	$forum_id           = 0;
	$forums_video_query = new WP_Query(
		array(
			'post_type'      => bbp_get_forum_post_type(),
			'fields'         => 'ids',
			'posts_per_page' => - 1,
			'meta_query'     => array(
				array(
					'key'     => 'bp_video_ids',
					'value'   => $video_id,
					'compare' => 'LIKE',
				),
			),
		)
	);

	if ( ! empty( $forums_video_query->found_posts ) && ! empty( $forums_video_query->posts ) ) {

		foreach ( $forums_video_query->posts as $post_id ) {
			$video_ids = get_post_meta( $post_id, 'bp_video_ids', true );

			if ( ! empty( $video_ids ) ) {
				$video_ids = explode( ',', $video_ids );
				if ( in_array( $video_id, $video_ids ) ) { // phpcs:ignore
					$forum_id = $post_id;
					break;
				}
			}
		}
	}

	if ( ! $forum_id ) {
		$topics_video_query = new WP_Query(
			array(
				'post_type'      => bbp_get_topic_post_type(),
				'fields'         => 'ids',
				'posts_per_page' => - 1,
				'meta_query'     => array(
					array(
						'key'     => 'bp_video_ids',
						'value'   => $video_id,
						'compare' => 'LIKE',
					),
				),
			)
		);

		if ( ! empty( $topics_video_query->found_posts ) && ! empty( $topics_video_query->posts ) ) {

			foreach ( $topics_video_query->posts as $post_id ) {
				$video_ids = get_post_meta( $post_id, 'bp_video_ids', true );

				if ( ! empty( $video_ids ) ) {
					$video_ids = explode( ',', $video_ids );
					if ( in_array( $video_id, $video_ids ) ) { // phpcs:ignore
						$forum_id = bbp_get_topic_forum_id( $post_id );
						break;
					}
				}
			}
		}
	}

	if ( ! $forum_id ) {
		$reply_video_query = new WP_Query(
			array(
				'post_type'      => bbp_get_reply_post_type(),
				'fields'         => 'ids',
				'posts_per_page' => - 1,
				'meta_query'     => array(
					array(
						'key'     => 'bp_video_ids',
						'value'   => $video_id,
						'compare' => 'LIKE',
					),
				),
			)
		);

		if ( ! empty( $reply_video_query->found_posts ) && ! empty( $reply_video_query->posts ) ) {

			foreach ( $reply_video_query->posts as $post_id ) {
				$video_ids = get_post_meta( $post_id, 'bp_video_ids', true );

				if ( ! empty( $video_ids ) ) {
					$video_ids = explode( ',', $video_ids );
					foreach ( $video_ids as $video_id ) {
						if ( in_array( $video_id, $video_ids ) ) { // phpcs:ignore
							$forum_id = bbp_get_reply_forum_id( $post_id );
							break;
						}
					}
				}
			}
		}
	}

	/**
	 * Filter to get video forum id.
	 *
	 * @param int $forum_id  forum id
	 * @param int $video_id  video id
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_get_forum_id', $forum_id, $video_id );

}


/**
 * Return the extension of the attachment.
 *
 * @param int $attachment_id attachment id.
 *
 * @return mixed|string
 * @since BuddyBoss 1.7.0
 */
function bp_video_mime_type( $attachment_id ) {

	$type = get_post_mime_type( $attachment_id );

	return $type;

}

/**
 * Return the icon based on the extension.
 *
 * @param string $extension extension.
 * @param int    $attachment_id attachment id.
 * @param string $type font.
 *
 * @return mixed|void
 * @since BuddyBoss 1.7.0
 */
function bp_video_svg_icon( $extension, $attachment_id = 0, $type = 'font' ) {

	if ( $attachment_id > 0 && '' !== $extension ) {
		$mime_type     = bp_video_mime_type( $attachment_id );
		$existing_list = bp_video_extensions_list();
		$new_extension = '.' . $extension;
		$result_array  = bp_video_multi_array_search(
			$existing_list,
			array(
				'extension' => $new_extension,
				'mime_type' => $mime_type,
			)
		);
		if ( $result_array && isset( $result_array[0] ) && ! empty( $result_array[0] ) ) {
			$icon = $existing_list[ $result_array[0] ]['icon'];
			if ( '' !== $icon ) {

				// added svg icon support.
				if ( 'svg' === $type ) {
					$svg_icons = array_column( bp_video_svg_icon_list(), 'svg', 'icon' );
					$icon      = $svg_icons[ $icon ];
				}

				return apply_filters( 'bp_video_svg_icon', $icon, $extension );
			}
		}
	}

	$svg = array(
		'font' => '',
		'svg'  => '',
	);

	switch ( $extension ) {
		case '7z':
			$svg = array(
				'font' => 'bb-icon-file-7z',
				'svg'  => '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="32" viewBox="0 0 24 32"><title>file-7z</title><path d="M13.728 0.032c0.992 0 1.984 0.384 2.72 1.056l0.16 0.16 6.272 6.496c0.672 0.672 1.056 1.6 1.12 2.528v17.76c0 2.144-1.696 3.904-3.808 4h-16.192c-2.144 0-3.904-1.664-4-3.808v-24.192c0-2.144 1.696-3.904 3.808-4h9.92zM13.728 2.048h-9.728c-1.056 0-1.92 0.8-1.984 1.824v24.16c0 1.056 0.8 1.92 1.824 2.016h16.16c1.056 0 1.92-0.832 1.984-1.856l0.032-0.16v-17.504c0-0.48-0.16-0.896-0.448-1.28l-0.128-0.128-6.272-6.464c-0.384-0.416-0.896-0.608-1.44-0.608zM16.992 14.528c0.576 0 1.024 0.448 1.024 0.992 0 0.512-0.416 0.96-0.896 0.992l-0.128 0.032v0.512l-1.984 0.992v1.184l1.984-0.992v-1.184h0.032v1.184h-0.032v0.832l-1.984 0.992v1.152l1.984-0.992v-1.152h0.032v1.152h-0.032v0.832l-1.984 0.992v1.184l1.984-0.992v-1.184h0.032v1.184h-0.032v2.304h0.128c0.48 0.064 0.896 0.48 0.896 0.992s-0.416 0.928-0.896 0.992h-1.984c-0.544 0-0.992-0.448-0.992-0.992 0-0.512 0.384-0.928 0.864-0.992v-0.704l-2.592 0.672c-0.096 0.032-0.192 0.032-0.256 0-0.064 0.032-0.096 0.032-0.16 0.032h-5.984c-0.576 0-1.024-0.448-1.024-1.024v-5.984c0-0.544 0.448-0.992 1.024-0.992h5.984c0.096 0 0.16 0 0.256 0.032h0.16l2.592 0.704v-0.768c-0.48-0.064-0.864-0.48-0.864-0.992s0.384-0.928 0.864-0.992h1.984zM12 17.536h-5.984v5.984h5.984v-5.984zM10.496 21.344c0.288 0 0.512 0.224 0.512 0.512s-0.224 0.512-0.512 0.512h-3.008c-0.256 0-0.48-0.224-0.48-0.512s0.224-0.512 0.48-0.512h3.008zM10.496 18.752c0.288 0 0.512 0.224 0.512 0.512 0 0.256-0.224 0.48-0.512 0.48h-3.008c-0.256 0-0.48-0.224-0.48-0.48 0-0.288 0.224-0.512 0.48-0.512h3.008z"></path></svg>',
			);
			break;
		case 'download':
			$svg = array(
				'font' => 'bb-icon-download',
				'svg'  => '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><title>download</title><path d="M2.656 22.656v4c0 2.209 1.791 4 4 4v0h18.688c2.209 0 4-1.791 4-4v0-4c0-0.742-0.602-1.344-1.344-1.344s-1.344 0.602-1.344 1.344v0 4c0 0 0 0 0 0 0 0.731-0.584 1.326-1.31 1.344l-0.002 0h-18.688c-0.728-0.018-1.312-0.613-1.312-1.344 0-0 0-0 0-0v0-4c0-0.742-0.602-1.344-1.344-1.344s-1.344 0.602-1.344 1.344v0zM16 19.456l-4.384-4.416c-0.248-0.312-0.628-0.51-1.054-0.51-0.742 0-1.344 0.602-1.344 1.344 0 0.426 0.198 0.805 0.507 1.052l0.003 0.002 5.344 5.344c0.243 0.239 0.576 0.387 0.944 0.387s0.701-0.148 0.944-0.387l-0 0 5.312-5.344c0.181-0.227 0.29-0.518 0.29-0.834 0-0.742-0.602-1.344-1.344-1.344-0.316 0-0.607 0.109-0.837 0.292l0.003-0.002-4.384 4.416zM14.656 2.656v18.688c0 0.742 0.602 1.344 1.344 1.344s1.344-0.602 1.344-1.344v0-18.688c0-0.742-0.602-1.344-1.344-1.344s-1.344 0.602-1.344 1.344v0z"></path></svg>',
			);
			break;
		default:
			$svg = array(
				'font' => 'bb-icon-file',
				'svg'  => '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="32" viewBox="0 0 24 32"><title>file-default</title><path d="M13.728 0h-9.728c-2.208 0-4 1.792-4 4v24c0 2.208 1.792 4 4 4h16c2.208 0 4-1.792 4-4v-17.504c0-1.056-0.416-2.048-1.12-2.784l-6.272-6.496c-0.768-0.768-1.792-1.216-2.88-1.216zM4 1.984h9.728c0.544 0 1.056 0.224 1.44 0.64l6.272 6.464c0.352 0.384 0.576 0.896 0.576 1.408v17.504c0 1.12-0.896 2.016-2.016 2.016h-16c-1.088 0-1.984-0.896-1.984-2.016v-24c0-1.12 0.896-2.016 1.984-2.016z"></path></svg>',
			);
	}

	/**
	 * Filter to get video svg icon.
	 *
	 * @param string $sbg_type  svg type
	 * @param string $extension file extension
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_svg_icon', $svg[ $type ], $extension );
}

/**
 * Return the icon list.
 *
 * @return mixed|void
 * @since BuddyBoss 1.7.0
 */
function bp_video_svg_icon_list() {

	$icons = array(
		'default_1'  => array(
			'icon'  => 'bb-icon-file',
			'title' => __( 'Default', 'buddyboss' ),
			'svg'   => '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="32" viewBox="0 0 24 32"><title>file-default</title><path d="M13.728 0h-9.728c-2.208 0-4 1.792-4 4v24c0 2.208 1.792 4 4 4h16c2.208 0 4-1.792 4-4v-17.504c0-1.056-0.416-2.048-1.12-2.784l-6.272-6.496c-0.768-0.768-1.792-1.216-2.88-1.216zM4 1.984h9.728c0.544 0 1.056 0.224 1.44 0.64l6.272 6.464c0.352 0.384 0.576 0.896 0.576 1.408v17.504c0 1.12-0.896 2.016-2.016 2.016h-16c-1.088 0-1.984-0.896-1.984-2.016v-24c0-1.12 0.896-2.016 1.984-2.016z"></path></svg>',
		),
		'default_2'  => array(
			'icon'  => 'bb-icon-file-archive',
			'title' => __( 'Archive', 'buddyboss' ),
			'svg'   => '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="32" viewBox="0 0 24 32"><title>file-zip</title><path d="M13.728 0c1.088 0 2.112 0.448 2.88 1.216v0l6.272 6.496c0.704 0.736 1.12 1.728 1.12 2.784v0 17.504c0 2.208-1.792 4-4 4v0h-16c-2.208 0-4-1.792-4-4v0-24c0-2.208 1.792-4 4-4v0h9.728zM13.728 1.984h-9.728c-1.088 0-1.984 0.896-1.984 2.016v0 24c0 1.12 0.896 2.016 1.984 2.016v0h2.976v-1.984h2.016v-1.92h-2.016v-2.080h2.016v2.016h1.984v2.048h-1.984v1.92h11.008c1.056 0 1.92-0.832 1.984-1.856l0.032-0.16v-17.504c0-0.512-0.224-1.024-0.576-1.408v0l-6.272-6.464c-0.384-0.416-0.896-0.64-1.44-0.64v0zM10.976 21.952v2.048h-1.984v-2.048h1.984zM11.008 16c0.544 0 0.992 0.448 0.992 0.992v3.008c0 0.544-0.448 0.992-0.992 0.992h-4c-0.544 0-0.992-0.448-0.992-0.992v-3.008c0-0.544 0.448-0.992 0.992-0.992h4zM10.592 16.992h-3.2c-0.192 0-0.352 0.128-0.384 0.32v1.28c0 0.192 0.128 0.352 0.32 0.384l0.064 0.032h3.2c0.192 0 0.352-0.16 0.416-0.32v-1.28c0-0.224-0.192-0.416-0.416-0.416z"></path></svg>',
		),
		'default_3'  => array(
			'icon'  => 'bb-icon-file-mp3',
			'title' => __( 'Audio', 'buddyboss' ),
			'svg'   => '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="32" viewBox="0 0 24 32"><title>file-mp3</title><path d="M13.728 0c1.088 0 2.112 0.448 2.88 1.216v0l6.272 6.496c0.704 0.736 1.12 1.728 1.12 2.784v0 17.504c0 2.208-1.792 4-4 4v0h-16c-2.208 0-4-1.792-4-4v0-24c0-2.208 1.792-4 4-4v0zM13.728 1.984h-9.728c-1.088 0-1.984 0.896-1.984 2.016v0 24c0 1.12 0.896 2.016 1.984 2.016v0h16c1.12 0 2.016-0.896 2.016-2.016v0-17.504c0-0.512-0.224-1.024-0.576-1.408v0l-6.272-6.464c-0.384-0.416-0.896-0.64-1.44-0.64v0zM16.96 16c0.128 0 0.288 0.032 0.384 0.128s0.16 0.224 0.16 0.352v0 7.744c0 0.96-0.672 1.824-1.664 2.048-0.96 0.224-1.984-0.192-2.464-1.056-0.448-0.832-0.288-1.888 0.448-2.528s1.856-0.736 2.688-0.224v0-3.040l-6.624 0.704v4.768c0 0.96-0.704 1.792-1.664 2.048-0.96 0.224-1.984-0.192-2.464-1.056-0.48-0.832-0.288-1.888 0.448-2.528s1.824-0.736 2.688-0.224v0-5.856c0-0.256 0.192-0.448 0.448-0.48v0z"></path></svg>',
		),
		'default_4'  => array(
			'icon'  => 'bb-icon-file-html',
			'title' => __( 'Code', 'buddyboss' ),
			'svg'   => '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="32" viewBox="0 0 24 32"><title>file-html</title><path d="M13.728 0c1.088 0 2.112 0.448 2.88 1.216v0l6.272 6.496c0.736 0.736 1.12 1.728 1.12 2.784v0 17.504c0 2.208-1.792 4-4 4v0h-16c-2.208 0-4-1.792-4-4v0-24c0-2.208 1.792-4 4-4v0h9.728zM13.728 1.984h-9.728c-1.088 0-1.984 0.896-1.984 2.016v0 24c0 1.12 0.896 2.016 1.984 2.016v0h16c1.12 0 2.016-0.896 2.016-2.016v0-17.504c0-0.512-0.224-1.024-0.576-1.408v0l-6.272-6.464c-0.384-0.416-0.896-0.64-1.44-0.64v0zM9.536 20.416c0.192 0.224 0.224 0.544 0.032 0.8l-0.032 0.064-2.112 2.048 2.112 2.112c0.192 0.224 0.224 0.544 0.032 0.768l-0.032 0.064c-0.224 0.224-0.544 0.256-0.768 0.064l-0.096-0.064-2.496-2.528c-0.224-0.192-0.224-0.544-0.064-0.768l0.064-0.064 2.528-2.496c0.224-0.224 0.608-0.224 0.832 0zM14.080 20.416c0.224-0.224 0.608-0.224 0.832 0v0l2.528 2.496 0.032 0.064c0.192 0.224 0.16 0.576-0.032 0.768v0l-2.592 2.592c-0.224 0.192-0.544 0.16-0.768-0.064v0l-0.064-0.064c-0.16-0.224-0.16-0.544 0.064-0.768v0l2.080-2.112-2.144-2.112c-0.16-0.256-0.16-0.576 0.064-0.8zM12.768 20.032c0.288 0.096 0.48 0.384 0.416 0.672l-0.032 0.064-1.664 5.28c-0.096 0.32-0.448 0.48-0.736 0.384s-0.448-0.384-0.416-0.672l0.032-0.064 1.664-5.28c0.096-0.32 0.448-0.48 0.736-0.384zM10.496 15.008c0.288 0 0.512 0.224 0.512 0.48v1.024c0 0.256-0.224 0.48-0.512 0.48h-4c-0.256 0-0.48-0.224-0.48-0.48v-1.024c0-0.256 0.224-0.48 0.48-0.48h4z"></path></svg>',
		),
		'default_5'  => array(
			'icon'  => 'bb-icon-file-psd',
			'title' => __( 'Design', 'buddyboss' ),
			'svg'   => '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="32" viewBox="0 0 24 32"><title>file-psd</title><path d="M13.728 0c1.088 0 2.112 0.448 2.88 1.216v0l6.272 6.496c0.704 0.736 1.12 1.728 1.12 2.784v0 17.504c0 2.208-1.792 4-4 4v0h-16c-2.208 0-4-1.792-4-4v0-24c0-2.208 1.792-4 4-4v0zM13.728 1.984h-9.728c-1.088 0-1.984 0.896-1.984 2.016v0 24c0 1.12 0.896 2.016 1.984 2.016v0h16c1.12 0 2.016-0.896 2.016-2.016v0-17.504c0-0.512-0.224-1.024-0.576-1.408v0l-6.272-6.464c-0.384-0.416-0.896-0.64-1.44-0.64v0zM7.072 22.336l4.416 2.528c0.32 0.192 0.704 0.192 0.992 0.032l4.448-2.528 0.896 0.704c0.16 0.16 0.192 0.416 0.064 0.576-0.032 0.032-0.064 0.064-0.128 0.096l-5.536 3.2c-0.128 0.064-0.288 0.064-0.416 0.032l-0.096-0.032-5.44-3.2c-0.192-0.128-0.256-0.352-0.128-0.544 0-0.064 0.032-0.096 0.064-0.096l0.864-0.768zM7.104 20l4.416 2.56c0.32 0.16 0.704 0.16 0.992 0l4.448-2.528 0.896 0.736c0.16 0.128 0.192 0.384 0.064 0.544-0.032 0.064-0.064 0.096-0.128 0.128l-5.536 3.168c-0.128 0.064-0.288 0.096-0.448 0.032l-0.064-0.032-5.44-3.2c-0.192-0.096-0.256-0.352-0.128-0.544 0-0.032 0.032-0.064 0.064-0.096l0.864-0.768zM12.16 15.040l0.064 0.032 5.472 3.104c0.192 0.128 0.288 0.32 0.288 0.544 0 0.16-0.064 0.32-0.192 0.416l-0.096 0.064-5.44 3.104c-0.128 0.096-0.288 0.096-0.448 0.032l-0.064-0.032-5.44-3.104c-0.224-0.096-0.32-0.32-0.288-0.544 0-0.16 0.064-0.32 0.192-0.416l0.096-0.064 5.408-3.104c0.128-0.096 0.288-0.096 0.448-0.032zM11.968 16.256l-4.256 2.432 0.8 0.448 1.856-0.704c0.032 0 0.032 0 0.064 0 0.128-0.032 0.256 0.064 0.32 0.192v0.064l0.064 0.352 1.408-0.352c0.096-0.032 0.16 0 0.224 0 0.128 0.064 0.192 0.224 0.16 0.352l-0.032 0.064-0.896 1.824 0.32 0.192 4.288-2.432-4.32-2.432zM13.408 17.696c0 0.288-0.384 0.512-0.896 0.576-0.512 0.032-0.928-0.16-0.928-0.416-0.032-0.288 0.352-0.544 0.864-0.576s0.928 0.16 0.96 0.416z"></path></svg>',
		),
		'default_6'  => array(
			'icon'  => 'bb-icon-file-png',
			'title' => __( 'Image', 'buddyboss' ),
			'svg'   => '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="32" viewBox="0 0 24 32"><title>file-png</title><path d="M13.728 0c1.088 0 2.112 0.448 2.88 1.216v0l6.272 6.496c0.704 0.736 1.12 1.728 1.12 2.784v0 17.504c0 2.208-1.792 4-4 4v0h-16c-2.208 0-4-1.792-4-4v0-24c0-2.208 1.792-4 4-4v0h9.728zM13.728 1.984h-9.728c-1.12 0-2.016 0.896-2.016 2.016v0 24c0 1.12 0.896 2.016 2.016 2.016v0h16c1.12 0 1.984-0.896 1.984-2.016v0-17.504c0-0.512-0.192-1.024-0.544-1.408v0l-6.272-6.496c-0.384-0.384-0.896-0.608-1.44-0.608v0zM15.68 15.008c1.28 0 2.304 1.024 2.304 2.304v0 7.392c0 1.248-1.024 2.304-2.304 2.304v0h-7.36c-1.28 0-2.336-1.056-2.336-2.304v0-7.392c0-1.28 1.056-2.304 2.336-2.304v0h7.36zM9.152 21.376l-0.096 0.064-2.144 1.6v1.664c0 0.704 0.544 1.312 1.248 1.376h7.52c0.768 0 1.408-0.64 1.408-1.376v0-3.136c-0.896 0.416-2.080 0.992-3.52 1.728-0.448 0.224-0.992 0.192-1.408-0.064l-0.128-0.096-2.368-1.728c-0.16-0.096-0.352-0.096-0.512-0.032zM15.68 15.936h-7.36c-0.768 0-1.408 0.608-1.408 1.376v0 4.48c0.416-0.32 0.96-0.704 1.536-1.152 0.512-0.384 1.152-0.416 1.664-0.096l0.128 0.064 2.368 1.728c0.128 0.096 0.288 0.128 0.448 0.096l0.096-0.064 3.936-1.92v-3.136c0-0.736-0.576-1.312-1.248-1.376h-0.16zM13.376 17.792c0.672 0 1.248 0.544 1.248 1.248s-0.576 1.248-1.248 1.248c-0.704 0-1.248-0.544-1.248-1.248s0.544-1.248 1.248-1.248z"></path></svg>',
		),
		'default_7'  => array(
			'icon'  => 'bb-icon-file-pptx',
			'title' => __( 'Presentation', 'buddyboss' ),
			'svg'   => '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="32" viewBox="0 0 24 32"><title>file-pptx</title><path d="M13.728 0c1.088 0 2.112 0.448 2.88 1.216v0l6.272 6.496c0.704 0.736 1.12 1.728 1.12 2.784v0 17.504c0 2.208-1.792 4-4 4v0h-16c-2.208 0-4-1.792-4-4v0-24c0-2.208 1.792-4 4-4v0zM13.728 1.984h-9.728c-1.088 0-1.984 0.896-1.984 2.016v0 24c0 1.12 0.896 2.016 1.984 2.016v0h16c1.12 0 2.016-0.896 2.016-2.016v0-17.504c0-0.512-0.224-1.024-0.576-1.408v0l-6.272-6.464c-0.384-0.416-0.896-0.64-1.44-0.64v0zM17.504 24.992c0.288 0 0.512 0.224 0.512 0.512v0.992c0 0.288-0.224 0.512-0.512 0.512h-11.008c-0.256 0-0.48-0.224-0.48-0.512v-0.992c0-0.288 0.224-0.512 0.48-0.512h11.008zM11.84 18.4l4.608 2.848c-0.992 1.376-2.624 2.24-4.448 2.24-2.048 0-3.872-1.12-4.8-2.816l-0.128-0.192 4.768-2.080zM12.224 12.512c2.944 0.096 5.28 2.528 5.28 5.472 0 0.864-0.192 1.632-0.512 2.368l-0.16 0.256-4.608-2.784v-5.312zM11.52 12.512v5.312l-4.704 2.016c-0.192-0.576-0.32-1.216-0.32-1.856 0-2.848 2.208-5.216 5.024-5.472z"></path></svg>',
		),
		'default_8'  => array(
			'icon'  => 'bb-icon-file-xlsx',
			'title' => __( 'Spreadsheet', 'buddyboss' ),
			'svg'   => '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="32" viewBox="0 0 24 32"><title>file-xlsx</title><path d="M13.728 0c1.088 0 2.112 0.448 2.88 1.216v0l6.272 6.496c0.704 0.736 1.12 1.728 1.12 2.784v0 17.504c0 2.208-1.792 4-4 4v0h-16c-2.208 0-4-1.792-4-4v0-24c0-2.208 1.792-4 4-4v0h9.728zM13.728 1.984h-9.728c-1.088 0-1.984 0.896-1.984 2.016v0 24c0 1.12 0.896 2.016 1.984 2.016v0h16c1.12 0 2.016-0.896 2.016-2.016v0-17.504c0-0.512-0.224-1.024-0.576-1.408v0l-6.272-6.464c-0.384-0.416-0.896-0.64-1.44-0.64v0zM15.488 13.984c1.408 0 2.528 1.12 2.528 2.528v0 8c0 1.376-1.12 2.496-2.528 2.496v0h-8c-1.376 0-2.496-1.12-2.496-2.496v0-8c0-1.408 1.12-2.528 2.496-2.528v0h8zM9.024 23.072h-3.008v1.44c0 0.768 0.576 1.408 1.344 1.472h1.664v-2.912zM16.992 23.072h-6.976v2.912h5.472c0.8 0 1.44-0.576 1.504-1.344v-1.568zM9.024 19.072h-3.008v3.008h3.008v-3.008zM16.992 19.072h-6.976v3.008h6.976v-3.008zM9.024 15.008h-1.536c-0.8 0-1.472 0.672-1.472 1.504v0 1.568h3.008v-3.072zM15.488 15.008h-5.472v3.072h6.976v-1.568c0-0.8-0.576-1.44-1.344-1.504h-0.16z"></path></svg>',
		),
		'default_9'  => array(
			'icon'  => 'bb-icon-file-txt',
			'title' => __( 'Text', 'buddyboss' ),
			'svg'   => '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="32" viewBox="0 0 24 32"><title>file-txt</title><path d="M13.728 0c1.088 0 2.112 0.448 2.88 1.216v0l6.272 6.496c0.704 0.736 1.12 1.728 1.12 2.784v0 17.504c0 2.208-1.792 4-4 4v0h-16c-2.208 0-4-1.792-4-4v0-24c0-2.208 1.792-4 4-4v0zM13.728 1.984h-9.728c-1.088 0-1.984 0.896-1.984 2.016v0 24c0 1.12 0.896 2.016 1.984 2.016v0h16c1.12 0 2.016-0.896 2.016-2.016v0-17.504c0-0.512-0.224-1.024-0.576-1.408v0l-6.272-6.464c-0.384-0.416-0.896-0.64-1.44-0.64v0zM15.488 24c0.288 0 0.512 0.224 0.512 0.512v0.992c0 0.256-0.224 0.48-0.512 0.48h-8.992c-0.256 0-0.48-0.224-0.48-0.48v-0.992c0-0.288 0.224-0.512 0.48-0.512h8.992zM13.504 20.512c0.288 0 0.512 0.224 0.512 0.48v0.992c0 0.288-0.224 0.512-0.512 0.512h-7.008c-0.256 0-0.48-0.224-0.48-0.512v-0.992c0-0.256 0.224-0.48 0.48-0.48h7.008zM17.504 16.992c0.288 0 0.512 0.224 0.512 0.512v0.992c0 0.288-0.224 0.512-0.512 0.512h-11.008c-0.256 0-0.48-0.224-0.48-0.512v-0.992c0-0.288 0.224-0.512 0.48-0.512h11.008z"></path></svg>',
		),
		'default_10' => array(
			'icon'  => 'bb-icon-file-video',
			'title' => __( 'Video', 'buddyboss' ),
			'svg'   => '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="32" viewBox="0 0 24 32"><title>file-video</title><path d="M13.728 0.096c1.088 0 2.112 0.448 2.88 1.216v0l6.272 6.496c0.704 0.736 1.12 1.728 1.12 2.784v0 17.504c0 2.208-1.792 4-4 4v0h-16c-2.208 0-4-1.792-4-4v0-24c0-2.208 1.792-4 4-4v0h9.728zM13.728 2.080h-9.728c-1.088 0-1.984 0.896-1.984 2.016v0 24c0 1.088 0.896 1.984 1.984 1.984v0h16c1.12 0 2.016-0.896 2.016-1.984v0-17.504c0-0.544-0.224-1.024-0.576-1.408v0l-6.272-6.496c-0.384-0.384-0.896-0.608-1.44-0.608v0zM12.704 18.080c1.28 0 2.304 1.056 2.304 2.304v0l1.472-1.088c0.448-0.352 1.056-0.256 1.408 0.192 0.128 0.16 0.192 0.384 0.192 0.608v4.992c0 0.544-0.448 0.992-0.992 0.992-0.224 0-0.448-0.064-0.608-0.192l-1.472-1.088c0 1.248-1.056 2.304-2.304 2.304v0h-5.408c-1.248 0-2.304-1.056-2.304-2.336v0-4.384c0-1.248 1.056-2.304 2.304-2.304v0h5.408zM12.704 19.008h-5.408c-0.736 0-1.376 0.64-1.376 1.376v0 4.384c0 0.768 0.64 1.408 1.376 1.408v0h5.408c0.768 0 1.376-0.64 1.376-1.408v0-4.384c0-0.736-0.608-1.376-1.376-1.376v0zM17.088 20.096l-2.016 1.472v2.016l2.016 1.504v-4.992z"></path></svg>',
		),
	);

	/**
	 * Filter to get svg icon lists.
	 *
	 * @param array $icons  svg icon list
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_svg_icon_list', $icons );
}

/**
 * Search from the array.
 *
 * @param array $array  array to be search.
 * @param array $search array to search.
 *
 * @return array
 */
function bp_video_multi_array_search( $array, $search ) {

	// Create the result array.
	$result = array();

	// Iterate over each array element.
	foreach ( $array as $key => $value ) {

		// Iterate over each search condition.
		foreach ( $search as $k => $v ) {

			// If the array element does not meet the search condition then continue to the next element.
			if ( ! isset( $value[ $k ] ) || $value[ $k ] !== $v ) {
				continue 2;
			}
		}

		// Add the array element's key to the result array.
		$result[] = $key;

	}

	// Return the result array.
	return $result;

}

/**
 * Return all the allowed video extensions.
 *
 * @return array
 * @since BuddyBoss 1.7.0
 */
function bp_video_get_allowed_extension() {
	$extensions     = array();
	$all_extensions = bp_video_extensions_list();
	foreach ( $all_extensions as $extension ) {
		if ( isset( $extension['is_active'] ) && true === (bool) $extension['is_active'] ) {
			$extensions[] = $extension['extension'];
		}
	}

	/**
	 * Filter to get allowed extension.
	 *
	 * @param array $extensions  extensions list
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_get_allowed_extension', $extensions );
}

/**
 * Return the breadcrumbs.
 *
 * @param int $user_id user id.
 * @param int $group_id group id.
 *
 * @return string
 * @since BuddyBoss 1.7.0
 */
function bp_video_user_video_album_tree_view_li_html( $user_id = 0, $group_id = 0 ) {

	global $wpdb, $bp;

	$video_album_table = $bp->video->table_name_albums;

	$cache_key = 'bp_video_user_video_album_' . $user_id . '_' . $group_id;
	$data      = wp_cache_get( $cache_key, 'bp_video_album' );

	if ( false === $data ) {
		if ( 0 === $group_id ) {
			$group_id = ( function_exists( 'bp_get_current_group_id' ) ) ? bp_get_current_group_id() : 0;
		}

		if ( $group_id > 0 ) {
			$video_album_query = $wpdb->prepare( "SELECT * FROM {$video_album_table} WHERE group_id = %d ORDER BY id DESC", $group_id ); // phpcs:ignore
		} else {
			$video_album_query = $wpdb->prepare( "SELECT * FROM {$video_album_table} WHERE user_id = %d AND group_id = %d ORDER BY id DESC", $user_id, $group_id ); // phpcs:ignore
		}

		$data = $wpdb->get_results( $video_album_query, ARRAY_A ); // phpcs:ignore
		wp_cache_set( $cache_key, $data, 'bp_video_album' );
	}

	// Build array of item references.
	foreach ( $data as $key => &$item ) {
		$items_by_reference[ $item['id'] ] = &$item;
		// Children array.
		$items_by_reference[ $item['id'] ]['children'] = array();
		// Empty data class (so that json_encode adds "data: {}" ).
		$items_by_reference[ $item['id'] ]['data'] = new StdClass();
	}

	// Set items as children of the relevant parent item.
	foreach ( $data as $key => &$item ) {
		if ( isset( $item['parent'] ) && $item['parent'] && isset( $items_by_reference[ $item['parent'] ] ) ) {
			$items_by_reference [ $item['parent'] ]['children'][] = &$item;
		}
	}

	// Remove items that were added to parents elsewhere.
	foreach ( $data as $key => &$item ) {
		if ( isset( $item['parent'] ) && $item['parent'] && isset( $items_by_reference[ $item['parent'] ] ) ) {
			unset( $data[ $key ] );
		}
	}

	return bp_video_album_recursive_li_list( $data, false );

}

/**
 * This function will give the breadcrumbs ul li html.
 *
 * @param array $array  list array.
 * @param bool  $first is first li.
 *
 * @return string
 * @since BuddyBoss 1.7.0
 */
function bp_video_album_recursive_li_list( $array, $first = false ) {

	// Base case: an empty array produces no list.
	if ( empty( $array ) ) {
		return '';
	}

	// Recursive Step: make a list with child lists.
	if ( $first ) {
		$output = '<ul class="">';
	} else {
		$output = '<ul class="location-album-list">';
	}

	foreach ( $array as $item ) {
		$output .= '<li data-id="' . $item['id'] . '" data-privacy="' . $item['privacy'] . '"><span id="' . $item['id'] . '" data-id="' . $item['id'] . '">' . stripslashes( $item['title'] ) . '</span>' . bp_video_album_recursive_li_list( $item['children'], true ) . '</li>';
	}
	$output .= '</ul>';

	return $output;
}

/**
 * This function will video into the album.
 *
 * @param int $video_id video id.
 * @param int $album_id album id.
 * @param int $group_id group id.
 *
 * @return bool|int
 * @since BuddyBoss 1.7.0
 */
function bp_video_move_video_to_album( $video_id = 0, $album_id = 0, $group_id = 0 ) {

	if ( 0 === $video_id ) {
		return false;
	}

	if ( (int) $video_id > 0 ) {
		$has_access = bp_video_user_can_edit( $video_id );
		if ( ! $has_access ) {
			return false;
		}
	}

	if ( (int) $album_id > 0 ) {
		$has_access = bp_video_album_user_can_edit( $album_id );
		if ( ! $has_access ) {
			return false;
		}
	}

	if ( ! $group_id ) {
		$get_video = new BP_Video( $video_id );
		if ( $get_video->group_id > 0 ) {
			$group_id = $get_video->group_id;
		}
	}

	if ( $group_id > 0 ) {
		$destination_privacy = 'public';
	} elseif ( $album_id > 0 ) {
		$destination_album = BP_Video_Album::get_album_data( array( $album_id ) );
		$destination_album = ( ! empty( $destination_album ) ) ? current( $destination_album ) : array();
		if ( empty( $destination_album ) ) {
			return false;
		}
		$destination_privacy = $destination_album->privacy;
		// Update modify date for destination album.
		$destination_album_update               = new BP_Video_Album( $album_id );
		$destination_album_update->date_created = bp_core_current_time();
		$destination_album_update->save();
	} else {
		// Keep the destination privacy same as the previous privacy.
		$video_object        = new BP_Video( $video_id );
		$destination_privacy = $video_object->privacy;
	}

	if ( empty( $destination_privacy ) ) {
		$destination_privacy = 'loggedin';
	}

	$video               = new BP_Video( $video_id );
	$video->album_id     = $album_id;
	$video->group_id     = $group_id;
	$video->date_created = bp_core_current_time();
	$video->privacy      = ( $group_id > 0 ) ? 'grouponly' : $destination_privacy;
	$video->menu_order   = 0;
	$video->save();

	// Update video activity privacy.
	if ( ! empty( $video ) && ! empty( $video->attachment_id ) ) {

		$video_attachment   = $video->attachment_id;
		$parent_activity_id = get_post_meta( $video_attachment, 'bp_video_parent_activity_id', true );

		// If found need to make this activity to main activity.
		$child_activity_id = get_post_meta( $video_attachment, 'bp_video_activity_id', true );

		if ( bp_is_active( 'activity' ) ) {

			// Single video upload.
			if ( empty( $child_activity_id ) ) {
				$activity = new BP_Activity_Activity( (int) $parent_activity_id );
				// Update activity data.
				if ( bp_activity_user_can_delete( $activity ) ) {
					// Make the activity video own.
					$status                      = bp_is_active( 'groups' ) ? bp_get_group_status( groups_get_group( $activity->item_id ) ) : '';
					$activity->hide_sitewide     = ( 'groups' === $activity->component && ( 'hidden' === $status || 'private' === $status ) ) ? 1 : $activity->hide_sitewide;
					$activity->secondary_item_id = 0;
					$activity->privacy           = $destination_privacy;
					$activity->title_required    = false;
					$activity->save();
				}

				// Delete the meta if uploaded to root so no need bp_video_album_activity meta.
				bp_activity_delete_meta( (int) $parent_activity_id, 'bp_video_album_activity' );

				if ( $album_id > 0 ) {
					// Update to moved album id.
					bp_activity_update_meta( (int) $parent_activity_id, 'bp_video_album_activity', (int) $album_id );
				}

				// We have to change child activity privacy when we move the video while at a time multiple video uploaded.
			} else {

				$parent_activity_video_ids = bp_activity_get_meta( $parent_activity_id, 'bp_video_ids', true );

				// Get the parent activity.
				$parent_activity = new BP_Activity_Activity( (int) $parent_activity_id );

				if ( bp_activity_user_can_delete( $parent_activity ) && ! empty( $parent_activity_video_ids ) ) {
					$parent_activity_video_ids = explode( ',', $parent_activity_video_ids );

					// Do the changes if only one video is attached to a activity.
					if ( 1 === count( $parent_activity_video_ids ) ) {

						// Get the video object.
						$video = new BP_Video( $video_id );

						// Need to delete child activity.
						$need_delete = $video->activity_id;

						$video_album = (int) $video->album_id;

						// Update video activity id to parent activity id.
						$video->activity_id  = $parent_activity_id;
						$video->date_created = bp_core_current_time();
						$video->save();

						bp_activity_update_meta( $parent_activity_id, 'bp_video_ids', $video_id );

						// Update attachment meta.
						delete_post_meta( $video->attachment_id, 'bp_video_activity_id' );
						update_post_meta( $video->attachment_id, 'bp_video_parent_activity_id', $parent_activity_id );
						update_post_meta( $video->attachment_id, 'bp_video_upload', 1 );
						update_post_meta( $video->attachment_id, 'bp_video_saved', 1 );

						bp_activity_delete_meta( $parent_activity_id, 'bp_video_album_activity' );
						if ( $video_album > 0 ) {
							bp_activity_update_meta( $parent_activity_id, 'bp_video_album_activity', $video_album );
						}

						// Update the activity meta first otherwise it will delete the video.
						bp_activity_update_meta( $need_delete, 'bp_video_ids', '' );

						// Delete child activity no need anymore because assigned all the data to parent activity.
						bp_activity_delete( array( 'id' => $need_delete ) );

						// Update parent activity privacy to destination privacy.
						$parent_activity->privacy        = $destination_privacy;
						$parent_activity->title_required = false;
						$parent_activity->save();

					} elseif ( count( $parent_activity_video_ids ) > 1 ) {

						// Get the child activity.
						$activity = new BP_Activity_Activity( (int) $child_activity_id );

						// Update activity data.
						if ( bp_activity_user_can_delete( $activity ) ) {

							// Make the activity video own.
							$status                      = bp_is_active( 'groups' ) ? bp_get_group_status( groups_get_group( $activity->item_id ) ) : '';
							$activity->hide_sitewide     = ( 'groups' === $activity->component && ( 'hidden' === $status || 'private' === $status ) ) ? 1 : 0;
							$activity->secondary_item_id = 0;
							$activity->privacy           = $destination_privacy;
							$activity->title_required    = false;
							$activity->save();

							bp_activity_update_meta( (int) $child_activity_id, 'bp_video_ids', $video_id );

							// Update attachment meta.
							delete_post_meta( $video_attachment, 'bp_video_activity_id' );
							update_post_meta( $video_attachment, 'bp_video_parent_activity_id', $child_activity_id );
							update_post_meta( $video_attachment, 'bp_video_upload', 1 );
							update_post_meta( $video_attachment, 'bp_video_saved', 1 );

							// Make the child activity as parent activity.
							bp_activity_delete_meta( $child_activity_id, 'bp_video_activity' );

							bp_activity_delete_meta( (int) $child_activity_id, 'bp_video_album_activity' );
							if ( $album_id > 0 ) {
								bp_activity_update_meta( (int) $child_activity_id, 'bp_video_album_activity', (int) $album_id );
							}

							// Remove the video id from the parent activity meta.
							$key = array_search( $video_id, $parent_activity_video_ids ); // phpcs:ignore
							if ( false !== $key ) {
								unset( $parent_activity_video_ids[ $key ] );
							}

							// Update the activity meta.
							if ( ! empty( $parent_activity_video_ids ) ) {
								$activity_video_ids = implode( ',', $parent_activity_video_ids );
								bp_activity_update_meta( $parent_activity_id, 'bp_video_ids', $activity_video_ids );
							} else {
								bp_activity_update_meta( $parent_activity_id, 'bp_video_ids', '' );
							}
						}
					}
				}
			}
		}
	}

	return $video_id;
}

/**
 * Get video based on activity id.
 *
 * @param int $activity_id activity id.
 *
 * @return array|void
 */
function bp_video_get_activity_video( $activity_id ) {

	$video_content      = '';
	$video_activity_ids = '';
	$response           = array();
	if ( bp_is_active( 'activity' ) && ! empty( $activity_id ) ) {

		$activity_metas     = bb_activity_get_metadata( $activity_id );
		$video_activity_ids = $activity_metas['bp_video_ids'][0] ?? '';

		global $video_template;
		// Add Video to single activity page..
		$video_activity = bp_activity_get_meta( $activity_id, 'bp_video_activity', true );
		if ( bp_is_single_activity() && ! empty( $video_activity ) && '1' === $video_activity && empty( $video_activity_ids ) ) {
			$video_ids = BP_Video::get_activity_video_id( $activity_id );
		} else {
			$video_ids = $video_activity_ids;
		}

		if ( empty( $video_ids ) ) {
			return;
		}

		$args = array(
			'include'  => $video_ids,
			'order_by' => 'menu_order',
			'sort'     => 'ASC',
			'user_id'  => false,
			'per_page' => 0,
		);

		$activity = new BP_Activity_Activity( (int) $activity_id );
		if ( bp_is_active( 'groups' ) && buddypress()->groups->id === $activity->component ) {
			if ( bp_is_group_video_support_enabled() ) {
				$args['privacy'] = array( 'grouponly' );
				if ( ! bp_is_group_albums_support_enabled() ) {
					$args['album_id'] = 'existing-video';
				}
			} else {
				$args['privacy']  = array( '0' );
				$args['album_id'] = 'existing-video';
			}
		} else {
			$args['privacy'] = bp_video_query_privacy( $activity->user_id, 0, $activity->component );
			if ( ! bp_is_profile_video_support_enabled() ) {
				$args['user_id'] = 'null';
			}
			if ( ! bp_is_profile_albums_support_enabled() ) {
				$args['album_id'] = 'existing-video';
			}
		}

		$is_forum_activity = false;
		if ( bp_is_active( 'forums' ) && in_array(
			$activity->type,
			array(
				'bbp_forum_create',
				'bbp_topic_create',
				'bbp_reply_create',
			),
			true
		) && bp_is_forums_video_support_enabled() ) {
			$is_forum_activity = true;
			$args['privacy'][] = 'forums';
		}

		if ( bp_has_video( $args ) ) {

			ob_start();
			?>
			<div class="bb-activity-video-wrap
			<?php
			echo esc_attr( 'bb-video-length-' . $video_template->video_count );
			echo $video_template->video_count > 5 ? esc_attr( ' bb-video-length-more' ) : '';
			echo true === $is_forum_activity ? esc_attr( ' forums-video-wrap' ) : '';
			?>
			">
				<?php
				bp_get_template_part( 'video/video-move' );
				bp_get_template_part( 'video/add-video-thumbnail' );
				while ( bp_video() ) {
					bp_the_video();
					bp_get_template_part( 'video/activity-entry' );
				}
				?>
			</div>
			<?php
			$video_content = ob_get_contents();
			ob_end_clean();
		}
	}

	$response['content']            = $video_content;
	$response['video_activity_ids'] = $video_activity_ids;

	return $response;
}

/**
 * Set bb_videos folder for the video upload directory.
 *
 * @param array $pathdata upload path.
 *
 * @return mixed
 * @since BuddyBoss 1.7.0
 */
function bp_video_upload_dir( $pathdata ) {

	$action = bb_filter_input_string( INPUT_POST, 'action' );

	if ( isset( $action ) && ( 'video_upload' === $action || 'video_thumbnail_upload' === $action ) ) { // WPCS: CSRF ok, input var ok.
		if ( empty( $pathdata['subdir'] ) ) {
			$pathdata['path']   = $pathdata['path'] . '/bb_videos';
			$pathdata['url']    = $pathdata['url'] . '/bb_videos';
			$pathdata['subdir'] = '/bb_videos';
		} else {
			$new_subdir         = '/bb_videos' . $pathdata['subdir'];
			$pathdata['path']   = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['path'] );
			$pathdata['url']    = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['url'] );
			$pathdata['subdir'] = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['subdir'] );
		}
	}

	return $pathdata;
}

/**
 * Set bb_videos folder for the video upload directory.
 *
 * @param array $pathdata upload path.
 *
 * @return mixed
 * @since BuddyBoss 1.7.0
 */
function bp_video_upload_dir_script( $pathdata ) {

	if ( empty( $pathdata['subdir'] ) ) {
		$pathdata['path']   = $pathdata['path'] . '/bb_videos';
		$pathdata['url']    = $pathdata['url'] . '/bb_videos';
		$pathdata['subdir'] = '/bb_videos';
	} else {
		$new_subdir         = '/bb_videos' . $pathdata['subdir'];
		$pathdata['path']   = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['path'] );
		$pathdata['url']    = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['url'] );
		$pathdata['subdir'] = str_replace( $pathdata['subdir'], $new_subdir, $pathdata['subdir'] );
	}

	return $pathdata;
}

/**
 * Check given video is activity comment video.
 *
 * @since BuddyBoss 1.7.0
 *
 * @param int|object $video video id or object.
 *
 * @return bool
 */
function bp_video_is_activity_comment_video( $video ) {

	$is_comment_video = false;

	if ( is_object( $video ) ) {
		$video_activity_id = $video->activity_id;
	} else {
		$video             = new BP_Video( $video );
		$video_activity_id = $video->activity_id;
	}

	if ( bp_is_active( 'activity' ) ) {
		$activity = new BP_Activity_Activity( $video_activity_id );

		if ( $activity ) {
			if ( 'activity_comment' === $activity->type ) {
				$is_comment_video = true;
			}
			if ( $activity->secondary_item_id ) {
				$load_parent_activity = new BP_Activity_Activity( $activity->secondary_item_id );
				if ( $load_parent_activity ) {
					if ( 'activity_comment' === $load_parent_activity->type ) {
						$is_comment_video = true;
					}
				}
			}
		}
	} elseif ( $video_activity_id ) {
		$is_comment_video = true;
	}

	return $is_comment_video;
}

/**
 * Return download link of the album.
 *
 * @param int $album_id album id.
 *
 * @return mixed|void
 * @since BuddyBoss 1.7.0
 */
function bp_video_album_download_link( $album_id ) {

	if ( empty( $album_id ) ) {
		return;
	}

	$link = site_url() . '/?attachment=' . $album_id . '&video_type=album&download_video_file=1&video_file=' . $album_id;

	/**
	 * Filter to get video album download link.
	 *
	 * @param string $link  downlod link
	 * @param int    $album_id  album id
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bp_video_album_download_link', $link, $album_id );
}

/**
 * Function to get video report link
 *
 * @since BuddyBoss 1.7.0
 *
 * @param array $args button arguments.
 *
 * @return mixed|void
 */
function bp_video_get_report_link( $args = array() ) {

	if ( ! bp_is_active( 'moderation' ) || ! is_user_logged_in() ) {
		return false;
	}

	$report_btn = bp_moderation_get_report_button(
		array(
			'id'                => 'video_report',
			'component'         => 'moderation',
			'must_be_logged_in' => true,
			'button_attr'       => array(
				'data-bp-content-id'   => ! empty( $args['id'] ) ? $args['id'] : 0,
				'data-bp-content-type' => BP_Moderation_Video::$moderation_type,
			),
		),
		true
	);

	/**
	 * Filter to return video report link
	 *
	 * @since BuddyBoss 1.7.0
	 *
	 * @param string $report_btn button.
	 * @param array $args button arguments.
	 */
	return apply_filters( 'bp_video_get_report_link', $report_btn, $args );
}

/**
 * Check if FFMPEG installed.
 */
function bb_video_is_ffmpeg_installed() {

	if ( ! ( class_exists( 'BuddyBossPlatform\FFMpeg\FFMpeg' ) || class_exists( 'FFMpeg\FFMpeg' ) ) ) {
		return false;
	} elseif ( class_exists( 'BuddyBossPlatform\FFMpeg\FFMpeg' ) || class_exists( 'FFMpeg\FFMpeg' ) ) {
		$ffmpeg = bb_video_check_is_ffmpeg_binary();
		if ( ! empty( $ffmpeg->error ) && ! empty( trim( $ffmpeg->error ) ) ) {
			return false;
		} else {
			return true;
		}
	}

	return false;
}

/**
 * Function to get video attachments
 *
 * @since BuddyBoss 1.7.0
 *
 * @param int $video_attachment_id video attachment id.
 * @param int $video_id            video id.
 *
 * @return mixed
 */
function bp_video_get_attachments( $video_attachment_id, $video_id = 0 ) {

	$attachment_urls           = array();
	$auto_generated_thumbnails = get_post_meta( $video_attachment_id, 'video_preview_thumbnails', true );
	$preview_thumbnail_id      = get_post_meta( $video_attachment_id, 'bp_video_preview_thumbnail_id', true );
	if ( $auto_generated_thumbnails ) {
		$auto_generated_thumbnails_arr = isset( $auto_generated_thumbnails['default_images'] ) && ! empty( $auto_generated_thumbnails['default_images'] ) ? $auto_generated_thumbnails['default_images'] : array();
		if ( $auto_generated_thumbnails_arr ) {
			foreach ( $auto_generated_thumbnails_arr as $auto_generated_thumbnail ) {
				$attachment_urls['default_images'][] = array(
					'id'  => $auto_generated_thumbnail,
					'url' => wp_get_attachment_image_url( $auto_generated_thumbnail, 'full' ),
				);
			}
		}
	}

	if ( $preview_thumbnail_id ) {
		$attachment_urls['selected_id'] = array(
			'id'  => $preview_thumbnail_id,
			'url' => wp_get_attachment_image_url( $preview_thumbnail_id, 'full' ),
		);
	}

	if ( isset( $auto_generated_thumbnails['custom_image'] ) && ! empty( $auto_generated_thumbnails['custom_image'] ) ) {
		$id                         = ( $video_id ) ? $video_id : bp_get_video_id();
		$video                      = new BP_Video( $id );
		$attachment_urls['preview'] = array(
			'id'            => $id,
			'attachment_id' => $auto_generated_thumbnails['custom_image'],
			'thumb'         => wp_get_attachment_image_url( $auto_generated_thumbnails['custom_image'], 'bp-media-thumbnail' ),
			'url'           => wp_get_attachment_image_url( $auto_generated_thumbnails['custom_image'], 'full' ),
			'name'          => $video->title,
			'saved'         => true,
			'dropzone'      => true,
		);
	}

	return $attachment_urls;
}

/**
 * Whether user can show the document upload button.
 *
 * @param int $user_id  given user id.
 * @param int $group_id given group id.
 *
 * @since BuddyBoss 1.7.0
 *
 * @return bool
 */
function bb_video_user_can_upload( $user_id = 0, $group_id = 0 ) {

	if ( ( empty( $user_id ) && empty( $group_id ) ) || empty( $user_id ) ) {
		return false;
	}

	if ( ! empty( $group_id ) && bp_is_group_video_support_enabled() ) {
		return groups_can_user_manage_video( $user_id, $group_id );
	}

	if ( bp_is_profile_video_support_enabled() && bb_user_can_create_video() ) {
		return true;
	}

	return false;
}

/**
 * Return the video symlink path.
 *
 * @return string The symlink path.
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_symlink_path() {

	$upload_dir = wp_upload_dir();
	$upload_dir = $upload_dir['basedir'];

	$platform_previews_path = $upload_dir . '/bb-platform-previews';
	if ( ! is_dir( $platform_previews_path ) ) {
		wp_mkdir_p( $platform_previews_path );
		chmod( $platform_previews_path, 0755 );
	}

	$video_symlinks_path = $platform_previews_path . '/' . md5( 'bb-videos' );
	if ( ! is_dir( $video_symlinks_path ) ) {
		wp_mkdir_p( $video_symlinks_path );
		chmod( $video_symlinks_path, 0755 );
	}

	return $video_symlinks_path;
}

/**
 * Delete video previews/symlinks.
 *
 * @since BuddyBoss 1.7.0
 */
function bp_video_delete_video_previews() {
	$upload_dir = wp_upload_dir();
	$upload_dir = $upload_dir['basedir'];

	$parent_directory_name = $upload_dir . '/bb-platform-previews';
	if ( ! is_dir( $parent_directory_name ) ) {
		return;
	}

	$inner_directory_name = $parent_directory_name . '/' . md5( 'bb-videos' );
	if ( ! is_dir( $inner_directory_name ) ) {
		return;
	}

	$dir = opendir( $inner_directory_name );

	if ( false === $dir ) {
		return;
	}

	$five_minutes = strtotime( '-5 minutes' );
	while ( false != ( $file = readdir( $dir ) ) ) { // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition, WordPress.PHP.StrictComparisons.LooseComparison
		if ( file_exists( $inner_directory_name . '/' . $file ) && is_writable( $inner_directory_name . '/' . $file ) && filemtime( $inner_directory_name . '/' . $file ) < $five_minutes ) {
			@unlink( $inner_directory_name . '/' . $file ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
		}
	}
	closedir( $dir );
}

/**
 * Return the preview url of the file.
 *
 * @since BuddyBoss 1.7.0
 *
 * @param int    $video_id      Video ID.
 * @param int    $attachment_id Attachment ID.
 * @param string $size          Size of preview.
 * @param bool   $generate      Generate Symlink or not.
 * @param int    $receiver_id   Receiver user ID.
 *
 * @return mixed|void
 */
function bb_video_get_thumb_url( $video_id, $attachment_id, $size = 'bb-video-activity-image', $generate = true, $receiver_id = 0 ) {

	$attachment_url = '';

	/**
	 * Filter here to allow/disallow video thumb symlinks.
	 *
	 * @param bool   $do_symlink    Default true.
	 * @param int    $video_id      Video id
	 * @param int    $attachment_id Document attachment id.
	 * @param string $size          Size.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	$do_symlink = apply_filters( 'bb_video_create_thumb_symlinks', true, $video_id, $attachment_id, $size );

	if ( $do_symlink ) {

		$video = new BP_Video( $video_id );
		if ( bb_enable_symlinks() ) {
			$attachment_url = bb_video_get_attachment_symlink( $video, $attachment_id, $size, $generate );
		} else {
			$video_id       = 'forbidden_' . $video_id;
			$attachment_id  = 'forbidden_' . $attachment_id;
			$attachment_url = home_url( '/' ) . 'bb-video-thumb-preview/' . base64_encode( $attachment_id ) . '/' . base64_encode( $video_id ) . '/' . $size;

			if ( 0 < $receiver_id ) {
				$attachment_url = $attachment_url . '/' . base64_encode( 'receiver_' . $receiver_id );
			}
		}

		if ( empty( $attachment_url ) ) {
			$attachment_url = bb_get_video_default_placeholder_image();
		}
	} else {
		$attachment_url = wp_get_attachment_url( $attachment_id );
	}

	$attachment_url = ! empty( $attachment_url ) && ! bb_enable_symlinks() ? untrailingslashit( $attachment_url ) : $attachment_url;

	/**
	 * Filter to get video thumb url.
	 *
	 * @param int    $attachment_url attachment link
	 * @param int    $video_id       video id
	 * @param string $size           size
	 * @param int    $attachment_id  attachment id
	 * @param bool   $do_symlink     symlink used or not
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bb_video_get_thumb_url', $attachment_url, $video_id, $size, $attachment_id, $do_symlink );
}

/**
 * Return the video thumbnail attachment id.
 *
 * @param int $video_attachment_id Video attachment id.
 *
 * @return int|mixed|string return the video attachment id.
 *
 * @since BuddyBoss 1.7.0
 */
function bb_get_video_thumb_id( $video_attachment_id ) {

	$get_video_thumb_ids = get_post_meta( $video_attachment_id, 'video_preview_thumbnails', true );
	$get_video_thumb_id  = get_post_meta( $video_attachment_id, 'bp_video_preview_thumbnail_id', true );

	if ( $get_video_thumb_id ) {
		return $get_video_thumb_id;
	} elseif ( $get_video_thumb_ids ) {
		$get_video_thumb_ids_arr = isset( $get_video_thumb_ids['default_images'] ) ? $get_video_thumb_ids['default_images'] : array();
		if ( $get_video_thumb_ids_arr ) {
			return current( $get_video_thumb_ids_arr );
		}
		if ( isset( $get_video_thumb_ids['custom_image'] ) && ! empty( $get_video_thumb_ids['custom_image'] ) ) {
			return $get_video_thumb_ids['custom_image'];
		}
	}

	return 0;
}

/**
 * Return the video auto generated preview ids.
 *
 * @param int $video_attachment_id video attachment id.
 *
 * @return array attachment ids array.
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_get_auto_generated_preview_ids( $video_attachment_id ) {

	$get_video_thumb_ids = get_post_meta( $video_attachment_id, 'video_preview_thumbnails', true );
	if ( ! $get_video_thumb_ids ) {
		return array();
	}

	$default_images = isset( $get_video_thumb_ids['default_images'] ) && ! empty( $get_video_thumb_ids['default_images'] ) ? $get_video_thumb_ids['default_images'] : array();

	return $default_images;
}

/**
 * Return the default placeholder image.
 *
 * @return string Return the default video placeholder image.
 */
function bb_get_video_default_placeholder_image() {
	return buddypress()->plugin_url . 'bp-templates/bp-nouveau/images/video-placeholder.jpg';
}

/**
 * Regenerate video poster attachment thumbnails
 *
 * @param int $attachment_id Attachment ID.
 *
 * @since BuddyBoss 1.7.0
 */
function bp_video_regenerate_attachment_thumbnails( $attachment_id ) {

	// Add upload filters.
	bb_video_image_add_upload_filters();

	// Register image sizes.
	bb_video_register_image_sizes();

	// Regenerate attachment thumbnails.
	bp_core_regenerate_attachment_thumbnails( $attachment_id );

	// Remove upload filters.
	bb_video_image_remove_upload_filters();

	// Deregister image sizes.
	bb_video_deregister_image_sizes();
}

/**
 * Delete the symlink for given thumb id.
 *
 * @param int|object $video           video id or video object.
 * @param int        $delete_thumb_id thumb id to delete.
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_delete_thumb_symlink( $video, $delete_thumb_id ) {

	// Check if video is id of video, create video object.
	if ( $video instanceof BP_Video ) {
		$video_id = $video->id;
	} elseif ( is_int( $video ) ) {
		$video_id = $video;
	} elseif ( is_string( $video ) ) {
		$video_id = $video;
	}

	if ( empty( $video_id ) ) {
		return;
	}

	// Get video previews symlink directory path.
	$video_symlinks_path = bb_video_symlink_path();
	$video               = new BP_Video( $video_id );
	$all_attachments     = array();

	// Delete the thumb symlink.
	$privacy         = $video->privacy;
	$attachment_path = $video_symlinks_path . '/' . md5( $video->id . $delete_thumb_id . $privacy );
	if ( $video->group_id > 0 && bp_is_active( 'groups' ) ) {
		$group_object    = groups_get_group( $video->group_id );
		$group_status    = bp_get_group_status( $group_object );
		$attachment_path = $video_symlinks_path . '/' . md5( $video->id . $delete_thumb_id . $group_status . $privacy );
	}
	$all_attachments[] = $attachment_path;

	$attachment_path = $video_symlinks_path . '/' . md5( $video->id . $delete_thumb_id . $privacy . 'medium' );
	if ( $video->group_id > 0 && bp_is_active( 'groups' ) ) {
		$group_object    = groups_get_group( $video->group_id );
		$group_status    = bp_get_group_status( $group_object );
		$attachment_path = $video_symlinks_path . '/' . md5( $video->id . $delete_thumb_id . $group_status . $privacy . 'medium' );
	}
	$all_attachments[] = $attachment_path;

	$attachment_path = $video_symlinks_path . '/' . md5( $video->id . $delete_thumb_id . $privacy . 'large' );
	if ( $video->group_id > 0 && bp_is_active( 'groups' ) ) {
		$group_object    = groups_get_group( $video->group_id );
		$group_status    = bp_get_group_status( $group_object );
		$attachment_path = $video_symlinks_path . '/' . md5( $video->id . $delete_thumb_id . $group_status . $privacy . 'large' );
	}
	$all_attachments[] = $attachment_path;

	$attachment_path = $video_symlinks_path . '/' . md5( $video->id . $delete_thumb_id . $privacy . 'full' );
	if ( $video->group_id > 0 && bp_is_active( 'groups' ) ) {
		$group_object    = groups_get_group( $video->group_id );
		$group_status    = bp_get_group_status( $group_object );
		$attachment_path = $video_symlinks_path . '/' . md5( $video->id . $delete_thumb_id . $group_status . $privacy . 'full' );
	}

	if ( file_exists( $attachment_path ) || is_link( $attachment_path ) ) {
		unlink( $attachment_path );
	}

	$image_sizes = bb_video_get_image_sizes();
	if ( ! empty( $image_sizes ) ) {
		foreach ( $image_sizes as $name => $image_size ) {
			if ( ! empty( $image_size['width'] ) && ! empty( $image_size['height'] ) && 0 < (int) $image_size['width'] && 0 < $image_size['height'] ) {
				$attachment_path = $video_symlinks_path . '/' . md5( $video->id . $delete_thumb_id . $privacy . sanitize_key( $name ) );
				if ( $video->group_id > 0 && bp_is_active( 'groups' ) ) {
					$group_object    = groups_get_group( $video->group_id );
					$group_status    = bp_get_group_status( $group_object );
					$attachment_path = $video_symlinks_path . '/' . md5( $video->id . $delete_thumb_id . $group_status . $privacy . sanitize_key( $name ) );
				}
				$all_attachments[] = $attachment_path;
			}
		}
	}

	// Get video preview attachment ID.
	$preview_attached_file  = get_attached_file( $delete_thumb_id );
	$preview_file_extension = ! empty( $preview_attached_file ) ? pathinfo( $preview_attached_file, PATHINFO_EXTENSION ) : '';

	if ( ! empty( $all_attachments ) ) {
		foreach ( array_unique( $all_attachments ) as $attachment ) {
			$preview_attachment = ! empty( $preview_file_extension ) ? $attachment . '.' . $preview_file_extension : $attachment;

			// Delete the symlink if video preview file exists.
			if ( file_exists( $preview_attachment ) || is_link( $preview_attachment ) ) {
				unlink( $preview_attachment );
			}

			// Delete the symlink if video/preview file exists.
			if ( file_exists( $attachment ) || is_link( $attachment ) ) {
				unlink( $attachment );
			}
		}
	}
}

/**
 * Delete symlink for a video.
 *
 * @param object $video BP_Video Object.
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_delete_symlinks( $video ) {
	// Check if video is id of video, create video object.
	if ( $video instanceof BP_Video ) {
		$video_id = $video->id;
	} elseif ( is_int( $video ) ) {
		$video_id = $video;
	}

	$old_video = new BP_Video( $video_id );

	// Return if no video found.
	if ( empty( $old_video ) ) {
		return;
	}

	// Get video previews symlink directory path.
	$video_symlinks_path = bb_video_symlink_path();
	$attachment_id       = $old_video->attachment_id;
	$all_attachments     = array();

	// Delete the video symlink.
	$privacy         = $old_video->privacy;
	$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $privacy );
	if ( $old_video->group_id > 0 && bp_is_active( 'groups' ) ) {
		$group_object    = groups_get_group( $old_video->group_id );
		$group_status    = bp_get_group_status( $group_object );
		$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $group_status . $privacy );
	}
	$all_attachments[] = $attachment_path;

	// Remove symlinks that is created randomly.
	$get_existings = get_post_meta( $old_video->attachment_id, 'bb_video_symlinks_arr', true );
	if ( $get_existings ) {
		$all_attachments = array_merge( $all_attachments, $get_existings );
	}

	// Delete the video main preview link.
	$attachment_id   = bb_get_video_thumb_id( $old_video->attachment_id );
	$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $privacy . 'medium' );
	if ( $old_video->group_id > 0 && bp_is_active( 'groups' ) ) {
		$group_object    = groups_get_group( $old_video->group_id );
		$group_status    = bp_get_group_status( $group_object );
		$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $group_status . $privacy . 'medium' );
	}
	$all_attachments[] = $attachment_path;

	$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $privacy . 'large' );
	if ( $old_video->group_id > 0 && bp_is_active( 'groups' ) ) {
		$group_object    = groups_get_group( $old_video->group_id );
		$group_status    = bp_get_group_status( $group_object );
		$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $group_status . $privacy . 'large' );
	}
	$all_attachments[] = $attachment_path;

	$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $privacy . 'full' );
	if ( $old_video->group_id > 0 && bp_is_active( 'groups' ) ) {
		$group_object    = groups_get_group( $old_video->group_id );
		$group_status    = bp_get_group_status( $group_object );
		$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $group_status . $privacy . 'full' );
	}
	$all_attachments[] = $attachment_path;

	$image_sizes = bb_video_get_image_sizes();
	if ( ! empty( $image_sizes ) ) {
		foreach ( $image_sizes as $name => $image_size ) {
			if ( ! empty( $image_size['width'] ) && ! empty( $image_size['height'] ) && 0 < (int) $image_size['width'] && 0 < $image_size['height'] ) {
				$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $privacy . sanitize_key( $name ) );
				if ( $old_video->group_id > 0 && bp_is_active( 'groups' ) ) {
					$group_object    = groups_get_group( $old_video->group_id );
					$group_status    = bp_get_group_status( $group_object );
					$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $group_status . $privacy . sanitize_key( $name ) );
				}
				$all_attachments[] = $attachment_path;
			}
		}
	}

	// Delete the extra preview images symlink.
	$extra_preview_ids = bb_video_get_auto_generated_preview_ids( $old_video->attachment_id );
	if ( ! empty( $extra_preview_ids ) ) {
		foreach ( $extra_preview_ids as $attachment_id ) {

			$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $privacy . 'medium' );
			if ( $old_video->group_id > 0 && bp_is_active( 'groups' ) ) {
				$group_object    = groups_get_group( $old_video->group_id );
				$group_status    = bp_get_group_status( $group_object );
				$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $group_status . $privacy . 'medium' );
			}
			$all_attachments[] = $attachment_path;

			$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $privacy . 'large' );
			if ( $old_video->group_id > 0 && bp_is_active( 'groups' ) ) {
				$group_object    = groups_get_group( $old_video->group_id );
				$group_status    = bp_get_group_status( $group_object );
				$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $group_status . $privacy . 'large' );
			}
			$all_attachments[] = $attachment_path;

			$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $privacy . 'full' );
			if ( $old_video->group_id > 0 && bp_is_active( 'groups' ) ) {
				$group_object    = groups_get_group( $old_video->group_id );
				$group_status    = bp_get_group_status( $group_object );
				$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $group_status . $privacy . 'full' );
			}
			$all_attachments[] = $attachment_path;

			if ( ! empty( $image_sizes ) ) {
				foreach ( $image_sizes as $name => $image_size ) {
					if ( ! empty( $image_size['width'] ) && ! empty( $image_size['height'] ) && 0 < (int) $image_size['width'] && 0 < $image_size['height'] ) {
						$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $privacy . sanitize_key( $name ) );

						if ( $old_video->group_id > 0 && bp_is_active( 'groups' ) ) {
							$group_object    = groups_get_group( $old_video->group_id );
							$group_status    = bp_get_group_status( $group_object );
							$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $attachment_id . $group_status . $privacy . sanitize_key( $name ) );
						}
						$all_attachments[] = $attachment_path;
					}
				}
			}
		}
	}

	$get_video_thumb_id = get_post_meta( $attachment_id, 'bp_video_preview_thumbnail_id', true );
	if ( ! empty( $image_sizes ) ) {
		foreach ( $image_sizes as $name => $image_size ) {
			if ( ! empty( $image_size['width'] ) && ! empty( $image_size['height'] ) && 0 < (int) $image_size['width'] && 0 < $image_size['height'] ) {
				$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $get_video_thumb_id . $privacy . sanitize_key( $name ) );

				if ( $old_video->group_id > 0 && bp_is_active( 'groups' ) ) {
					$group_object    = groups_get_group( $old_video->group_id );
					$group_status    = bp_get_group_status( $group_object );
					$attachment_path = $video_symlinks_path . '/' . md5( $old_video->id . $get_video_thumb_id . $group_status . $privacy . sanitize_key( $name ) );
				}
				$all_attachments[] = $attachment_path;
			}
		}
	}

	// Get video attachment.
	$attached_file = get_attached_file( $old_video->attachment_id );

	// Get video preview attachment ID.
	$preview_attachment_id = bb_get_video_thumb_id( $old_video->attachment_id );
	$preview_attached_file = get_attached_file( $preview_attachment_id );

	// Get a video file extension.
	$video_file_extension = ! empty( $attached_file ) ? pathinfo( $attached_file, PATHINFO_EXTENSION ) : '';

	// Get a video preview file extension.
	$preview_file_extension = ! empty( $preview_attached_file ) ? pathinfo( $preview_attached_file, PATHINFO_EXTENSION ) : '';

	if ( ! empty( $all_attachments ) ) {
		foreach ( array_unique( $all_attachments ) as $attachment ) {
			$video_attachment   = ! empty( $video_file_extension ) ? $attachment . '.' . $video_file_extension : $attachment;
			$preview_attachment = ! empty( $preview_file_extension ) ? $attachment . '.' . $preview_file_extension : $attachment;

			// Delete the symlink if video file exists.
			if ( file_exists( $video_attachment ) ) {
				unlink( $video_attachment );
			}

			// Delete the symlink if video preview file exists.
			if ( file_exists( $preview_attachment ) ) {
				unlink( $preview_attachment );
			}

			// Delete the symlink if video/preview file exists.
			if ( file_exists( $attachment ) ) {
				unlink( $attachment );
			}
		}
	}
}

/**
 * Create symlink for a video.
 *
 * @param object $video BP_Video Object.
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_create_symlinks( $video ) {
	// Check if video is id of video, create video object.
	if ( ! $video instanceof BP_Video && is_int( $video ) ) {
		$video = new BP_Video( $video );
	}

	// Return if no video found.
	if ( empty( $video ) ) {
		return;
	}

	/**
	 * Filter here to allow/disallow video symlinks.
	 *
	 * @param bool   $do_symlink    Default true.
	 * @param int    $video_id      Video id
	 * @param int    $attachment_id Document attachment id.
	 * @param string $size          Size.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	$do_symlink = apply_filters( 'bb_video_do_symlink', true, $video->id, $video->attachment_id, '' );

	if ( $do_symlink ) {
		bb_video_get_symlink( $video );
	}
}

/**
 * Create symlink for a video.
 *
 * @param object $video    BP_Video Object.
 * @param bool   $generate Generate Symlink or not.
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_get_symlink( $video, $generate = true ) {
	// Check if video is id of video, create video object.
	if ( ! $video instanceof BP_Video && is_int( $video ) ) {
		$video = new BP_Video( $video );
	}

	// Return if no video found.
	if ( empty( $video ) ) {
		return;
	}

	$attachment_url = '';
	$video_id       = $video->id;
	$attachment_id  = $video->attachment_id;

	/**
	 * Filter here to allow/disallow video symlinks.
	 *
	 * @param bool   $do_symlink    Default true.
	 * @param int    $video_id      Video id
	 * @param int    $attachment_id Video attachment id.
	 * @param string $size          Size.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	$do_symlink = apply_filters( 'bb_video_do_symlink', true, $video_id, $attachment_id, '' );

	if ( $do_symlink ) {

		if ( ! bb_enable_symlinks() || bb_check_ios_device() ) {
			$video_id        = 'forbidden_' . $video->id;
			$attachment_id   = 'forbidden_' . $video->attachment_id;
			$output_file_src = get_attached_file( $video->attachment_id );
			if ( ! empty( $attachment_id ) && ! empty( $video_id ) && file_exists( $output_file_src ) ) {
				$attachment_url = home_url( '/' ) . 'bb-video-preview/' . base64_encode( $attachment_id ) . '/' . base64_encode( $video_id );
			}
		} else {
			// Get videos previews symlink directory path.
			$video_symlinks_path = bb_video_symlink_path();
			$attached_file       = get_attached_file( $attachment_id );
			$privacy             = $video->privacy;
			$upload_directory    = wp_get_upload_dir();
			$time                = time();
			$attachment_path     = $video_symlinks_path . '/' . md5( $video->id . $attachment_id . $privacy . $time );

			if ( $video->group_id > 0 && bp_is_active( 'groups' ) ) {
				$group_object    = groups_get_group( $video->group_id );
				$group_status    = bp_get_group_status( $group_object );
				$attachment_path = $video_symlinks_path . '/' . md5( $video->id . $attachment_id . $group_status . $privacy . $time );
			}

			if ( ! empty( $attached_file ) ) {
				$file_extension  = pathinfo( $attached_file, PATHINFO_EXTENSION );
				$attachment_path = ! empty( $file_extension ) ? $attachment_path . '.' . $file_extension : $attachment_path;
			}

			if ( ! empty( $attached_file ) && file_exists( $attached_file ) && is_file( $attached_file ) && ! is_dir( $attached_file ) && ! file_exists( $attachment_path ) ) {
				if ( ! is_link( $attachment_path ) && ! file_exists( $attachment_path ) ) {
					$get_existing = get_post_meta( $video->attachment_id, 'bb_video_symlinks_arr', true );
					if ( ! $get_existing ) {
						update_post_meta( $video->attachment_id, 'bb_video_symlinks_arr', array( $attachment_path ) );
					} else {
						$get_existing[] = array_push( $get_existing, $attachment_path );
						update_post_meta( $video->attachment_id, 'bb_video_symlinks_arr', $get_existing );
					}

					if ( $generate ) {
						// Generate Video Symlink.
						bb_core_symlink_generator( 'video', $video, $time, array(), $attached_file, $attachment_path );
					}
				}
			}

			$attachment_url = bb_core_symlink_absolute_path( $attachment_path, $upload_directory );

			/**
			 * Filters the attachment URL.
			 *
			 * @since BuddyBoss 2.6.10
			 *
			 * @param string $attachment_url URL for the given attachment.
			 * @param int    $attachment_id  Attachment post ID.
			 */
			$attachment_url = apply_filters( 'wp_get_attachment_url', $attachment_url, $attachment_id );

			/**
			 * Filter for the after thumb symlink generate.
			 *
			 * @param string $attachment_url Attachment URL.
			 * @param object $video          Video Object.
			 *
			 * @since BuddyBoss 1.7.0.1
			 */
			$attachment_url = apply_filters( 'bb_video_after_get_symlink', $attachment_url, $video );
		}
	}

	/**
	 * Filter for the video symlink url.
	 *
	 * @param string $attachment_url Attachment URL.
	 * @param int    $video_id       Video id.
	 * @param int    $attachment_id  Attachment id.
	 * @param bool   $do_symlink     Symlink used or not.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return apply_filters( 'bb_video_get_symlink', $attachment_url, $video_id, $attachment_id, $do_symlink );
}

/**
 * A simple function that uses mtime to delete files older than a given age (in seconds)
 * Very handy to rotate backup or log files, for example...
 *
 * @return array|void the list of deleted files
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_delete_older_symlinks() {

	if ( ! bb_enable_symlinks() ) {
		return;
	}

	// Get videos previews symlink directory path.
	$dir     = bb_video_symlink_path();
	$max_age = apply_filters( 'bb_video_delete_older_symlinks_time', 3600 * 24 * 15 ); // Delete the file older than 15 day.
	$list    = array();
	$limit   = time() - $max_age;
	$dir     = realpath( $dir );

	if ( ! is_dir( $dir ) ) {
		return;
	}

	$dh = opendir( $dir );
	if ( false === $dh ) {
		return;
	}

	while ( ( $file = readdir( $dh ) ) !== false ) {
		if ( '.' === $file || '..' === $file ) {
			continue;
		}

		$file      = $dir . '/' . $file;
		$file_time = lstat( $file );
		$file_time = isset( $file_time['ctime'] ) ? (int) $file_time['ctime'] : filemtime( $file );

		if ( file_exists( $file ) && $file_time < $limit ) {
			$list[] = $file;
			unlink( $file );
		}
	}
	closedir( $dh );

	if ( ! empty( $list ) ) {
		/**
		 * Hook after delete older symlinks.
		 *
		 * @since BuddyBoss 1.7.0
		 */
		do_action( 'bb_video_delete_older_symlinks' );
	}

	return $list;

}

add_action(
	'bp_init',
	function () {
		bp_core_schedule_cron( 'bb_video_deleter_older_symlink', 'bb_video_delete_older_symlinks', 'bb_schedule_15days' );
	}
);

/**
 * Function to get video attachments symlinks.
 *
 * @since BuddyBoss 1.7.0
 *
 * @param int $video_attachment_id video attachment id.
 * @param int $video_id            video id.
 *
 * @return mixed
 */
function bb_video_get_attachments_symlinks( $video_attachment_id, $video_id = 0 ) {

	$attachment_urls           = array();
	$video                     = new BP_Video( $video_id );
	$auto_generated_thumbnails = get_post_meta( $video_attachment_id, 'video_preview_thumbnails', true );
	$preview_thumbnail_id      = get_post_meta( $video_attachment_id, 'bp_video_preview_thumbnail_id', true );

	if ( $auto_generated_thumbnails ) {
		$auto_generated_thumbnails_arr = isset( $auto_generated_thumbnails['default_images'] ) && ! empty( $auto_generated_thumbnails['default_images'] ) ? $auto_generated_thumbnails['default_images'] : array();
		if ( $auto_generated_thumbnails_arr ) {
			foreach ( $auto_generated_thumbnails_arr as $auto_generated_thumbnail ) {
				$attachment_urls['default_images'][] = array(
					'id'  => $auto_generated_thumbnail,
					'url' => bb_video_get_attachment_symlink( $video, $auto_generated_thumbnail, 'bb-video-profile-album-add-thumbnail-directory-poster-image' ),
				);
			}
		} else {
			$is_ffmpeg_preview_generated = get_post_meta( $video_attachment_id, 'bb_ffmpeg_preview_generated', true );
			if ( 'no' === $is_ffmpeg_preview_generated ) {
				$attachment_urls['default_images'][] = array();
			}
		}
	} else {
		$is_ffmpeg_preview_generated = get_post_meta( $video_attachment_id, 'bb_ffmpeg_preview_generated', true );
		if ( 'no' === $is_ffmpeg_preview_generated ) {
			$attachment_urls['default_images'][] = array();
		}
	}

	if ( $preview_thumbnail_id ) {
		$attachment_urls['selected_id'] = array(
			'id'  => $preview_thumbnail_id,
			'url' => bb_video_get_attachment_symlink( $video, $preview_thumbnail_id, 'bb-video-profile-album-add-thumbnail-directory-poster-image' ),
		);
	}

	if ( isset( $auto_generated_thumbnails['custom_image'] ) && ! empty( $auto_generated_thumbnails['custom_image'] ) ) {

		$id                         = ( $video_id ) ? $video_id : bp_get_video_id();
		$video                      = new BP_Video( $id );
		$url                        = bb_video_get_attachment_symlink( $video, $auto_generated_thumbnails['custom_image'], 'bb-video-profile-album-add-thumbnail-directory-poster-image' );
		$attachment_urls['preview'] = array(
			'id'            => $id,
			'attachment_id' => $auto_generated_thumbnails['custom_image'],
			'thumb'         => $url,
			'url'           => $url,
			'name'          => $video->title,
			'saved'         => true,
			'dropzone'      => true,
		);
	}

	// Include ffmpeg thumbnail generation status.
	// Only expose this metadata if user has edit permission for the video.
	if ( $video_id && bp_video_user_can_edit( $video_id ) ) {
		$attachment_urls['ffmpeg_generated'] = get_post_meta( $video_attachment_id, 'bb_ffmpeg_preview_generated', true );
	} else {
		$attachment_urls['ffmpeg_generated'] = '';
	}

	return $attachment_urls;
}

/**
 * Use the ffmpeg binary if constant is defined.
 *
 * @return \FFMpeg\FFMpeg
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_check_is_ffmpeg_binary() {

	$retval = array(
		'ffmpeg' => null,
		'error'  => null,
	);

	if ( class_exists( 'BuddyBossPlatform\FFMpeg\FFMpeg' ) || class_exists( 'FFMpeg\FFMpeg' ) ) {
		try {
			if ( defined( 'BB_FFMPEG_BINARY_PATH' ) && defined( 'BB_FFPROBE_BINARY_PATH' ) ) {
				$retval['ffmpeg'] = \BuddyBoss\Library\Composer::instance()->ffmpeg_instance()->ffmpeg_create(
					array(
						'ffmpeg.binaries'  => BB_FFMPEG_BINARY_PATH,
						'ffprobe.binaries' => BB_FFPROBE_BINARY_PATH,
						'timeout'          => 3600, // The timeout for the underlying process.
						'ffmpeg.threads'   => 12,   // The number of threads that FFMpeg should use.
					)
				);

			} else {
				$retval['ffmpeg'] = \BuddyBoss\Library\Composer::instance()->ffmpeg_instance()->ffmpeg_create();
			}
		} catch ( Exception $e ) {
			$retval['error'] = $e->getMessage();
		}
	} else {
		$retval['error'] = __( 'FFMpeg\FFMpeg class not found', 'buddyboss' );
	}

	return (object) $retval;
}

/**
 * Use the ffprobe binary if constant is defined.
 *
 * @return \FFMpeg\FFProbe
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_check_is_ffprobe_binary() {

	$retval = array(
		'ffprob' => null,
		'error'  => null,
	);

	if (
		(
			class_exists( 'BuddyBossPlatform\FFMpeg\FFMpeg' ) ||
			class_exists( 'FFMpeg\FFMpeg' )
		) &&
		(
			class_exists( 'BuddyBossPlatform\FFMpeg\FFProbe' ) ||
			class_exists( 'FFMpeg\FFProbe' )
		)
	) {
		try {
			if ( defined( 'BB_FFMPEG_BINARY_PATH' ) && defined( 'BB_FFPROBE_BINARY_PATH' ) ) {
				$retval['ffprob'] = \BuddyBoss\Library\Composer::instance()->ffmpeg_instance()->ffprobe_create(
					array(
						'ffmpeg.binaries'  => BB_FFMPEG_BINARY_PATH,
						'ffprobe.binaries' => BB_FFPROBE_BINARY_PATH,
						'timeout'          => 3600, // The timeout for the underlying process.
						'ffmpeg.threads'   => 12,   // The number of threads that FFMpeg should use.
					)
				);

			} else {
				$retval['ffprob'] = \BuddyBoss\Library\Composer::instance()->ffmpeg_instance()->ffprobe_create();
			}
		} catch ( Exception $e ) {
			$retval['error'] = $e->getMessage();
		}
	} else {
		$retval['error'] = __( 'FFMpeg\FFProbe class not found', 'buddyboss' );
	}

	return (object) $retval;
}

/**
 * Get video thumbnail image sizes to register.
 *
 * @return array Image sizes.
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_get_image_sizes() {
	$image_sizes = array(
		'bb-video-profile-album-add-thumbnail-directory-poster-image' => array(
			'height' => 267,
			'width'  => 400,
		),
		'bb-video-poster-popup-image' => array(
			'height' => 900,
			'width'  => 1500,
		),
		'bb-video-activity-image'     => array(
			'height' => 400,
			'width'  => 640,
		),
	);

	/**
	 * Filter here to video image sizes.
	 *
	 * @param array $image_sizes Image sizes.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	return (array) apply_filters( 'bb_video_get_image_sizes', $image_sizes );
}

/**
 * Add video upload filters.
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_add_upload_filters() {
	add_filter( 'upload_dir', 'bp_video_upload_dir' );
	add_filter( 'upload_mimes', 'bp_video_allowed_mimes', 9, 1 );
}

/**
 * Remove video upload filters.
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_remove_upload_filters() {
	remove_filter( 'upload_dir', 'bp_video_upload_dir' );
	remove_filter( 'upload_mimes', 'bp_video_allowed_mimes' );
}

/**
 * Add video upload filters.
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_image_add_upload_filters() {
	add_filter( 'upload_dir', 'bp_video_upload_dir_script' );
	add_filter( 'intermediate_image_sizes_advanced', 'bb_video_remove_default_image_sizes' );
	add_filter( 'upload_mimes', 'bb_video_thumb_cover_allowed_mimes', 9, 1 );
	add_filter( 'big_image_size_threshold', '__return_false' );
}

/**
 * Remove video upload filters.
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_image_remove_upload_filters() {
	remove_filter( 'upload_dir', 'bp_video_upload_dir_script' );
	remove_filter( 'intermediate_image_sizes_advanced', 'bb_video_remove_default_image_sizes' );
	remove_filter( 'upload_mimes', 'bb_video_thumb_cover_allowed_mimes' );
	remove_filter( 'big_image_size_threshold', '__return_false' );
}

/**
 * This will remove the default image sizes registered.
 *
 * @param array $sizes Image sizes registered.
 *
 * @return array Empty array.
 * @since BuddyBoss 1.7.0
 */
function bb_video_remove_default_image_sizes( $sizes ) {

	if ( isset( $sizes['bb-video-profile-album-add-thumbnail-directory-poster-image'] ) && isset( $sizes['bb-video-poster-popup-image'] ) && isset( $sizes['bb-video-activity-image'] ) ) {
		return array(
			'bb-video-profile-album-add-thumbnail-directory-poster-image' => $sizes['bb-video-profile-album-add-thumbnail-directory-poster-image'],
			'bb-video-poster-popup-image' => $sizes['bb-video-poster-popup-image'],
			'bb-video-activity-image'     => $sizes['bb-video-activity-image'],
		);
	}

	return array();
}

/**
 * Mine type for uploader allowed by buddyboss video for security reason.
 *
 * @param Array $mime_types Mime type information.
 *
 * @return Array
 * @since BuddyBoss 1.7.0
 */
function bb_video_thumb_cover_allowed_mimes( $mime_types ) {

	// Creating a new array will reset the allowed filetypes.
	$mime_types = array(
		'jpg|jpeg|jpe' => 'image/jpeg',
		'png'          => 'image/png',
		'bmp'          => 'image/bmp',
	);

	return $mime_types;
}

/**
 * Register video image sizes.
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_register_image_sizes() {
	$image_sizes = bb_video_get_image_sizes();
	if ( ! empty( $image_sizes ) ) {
		foreach ( $image_sizes as $name => $image_size ) {
			if ( ! empty( $image_size['width'] ) && ! empty( $image_size['height'] ) && 0 < (int) $image_size['width'] && 0 < $image_size['height'] ) {
				add_image_size( sanitize_key( $name ), $image_size['width'], $image_size['height'] );
			}
		}
	}
}

/**
 * Deregister video image sizes.
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_deregister_image_sizes() {
	$image_sizes = bb_video_get_image_sizes();
	if ( ! empty( $image_sizes ) ) {
		foreach ( $image_sizes as $name => $image_size ) {
			remove_image_size( sanitize_key( $name ) );
		}
	}
}

/**
 * Add video upload filters.
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_add_thumb_image_add_upload_filters() {
	add_filter( 'upload_dir', 'bp_video_upload_dir' );
	add_filter( 'intermediate_image_sizes_advanced', 'bb_video_remove_default_image_sizes' );
	add_filter( 'upload_mimes', 'bb_video_thumb_cover_allowed_mimes', 9, 1 );
	add_filter( 'big_image_size_threshold', '__return_false' );
}

/**
 * Remove video upload filters.
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_add_thumb_image_remove_upload_filters() {
	remove_filter( 'upload_dir', 'bp_video_upload_dir' );
	remove_filter( 'intermediate_image_sizes_advanced', 'bb_video_remove_default_image_sizes' );
	remove_filter( 'upload_mimes', 'bb_video_thumb_cover_allowed_mimes' );
	remove_filter( 'big_image_size_threshold', '__return_false' );
}

/**
 * Return the video attachment symlink.
 *
 * @param int|object $video         Video id or a video object.
 * @param int        $attachment_id Attachment id.
 * @param string     $size          Size to get symlink.
 * @param bool       $generate      Generate Symlink or not.
 *
 * @return array|false|string|string[]|void
 *
 * @since BuddyBoss 1.7.0
 */
function bb_video_get_attachment_symlink( $video, $attachment_id, $size, $generate = true ) {

	// Check if video is id of video, create video object.
	if ( ! $video instanceof BP_Video && is_int( $video ) ) {
		$video = new BP_Video( $video );
	}

	// Return if no video found.
	if ( empty( $video ) || empty( $attachment_id ) || empty( $size ) ) {
		return;
	}

	/**
	 * Filter here to allow/disallow video symlinks.
	 *
	 * @param bool   $do_symlink    Default true.
	 * @param int    $video_id      Video id
	 * @param int    $attachment_id Document attachment id.
	 * @param string $size          Size.
	 *
	 * @since BuddyBoss 1.7.0
	 */
	$do_symlink = apply_filters( 'bb_video_create_thumb_symlinks', true, $video->id, $attachment_id, $size );

	if ( $do_symlink ) {

		if ( ! bb_enable_symlinks() ) {
			$video_id       = 'forbidden_' . $video->id;
			$attachment_id  = 'forbidden_' . $attachment_id;
			$attachment_url = untrailingslashit( home_url( '/' ) . 'bb-video-thumb-preview/' . base64_encode( $attachment_id ) . '/' . base64_encode( $video_id ) . '/' . $size );

		} else {

			$upload_dir = wp_upload_dir();
			$upload_dir = $upload_dir['basedir'];

			// Get video previews symlink directory path.
			$video_symlinks_path = bb_video_symlink_path();
			$privacy             = $video->privacy;
			$output_file_src     = '';
			$attachment_path     = $video_symlinks_path . '/' . md5( $video->id . $attachment_id . $privacy . $size );

			if ( $video->group_id > 0 && bp_is_active( 'groups' ) ) {
				$group_object    = groups_get_group( $video->group_id );
				$group_status    = bp_get_group_status( $group_object );
				$attachment_path = $video_symlinks_path . '/' . md5( $video->id . $attachment_id . $group_status . $privacy . $size );
			}

			$file = image_get_intermediate_size( $attachment_id, $size );

			if ( $file && ! empty( $file['path'] ) ) {
				$output_file_src = $upload_dir . '/' . $file['path'];

				// Regenerate attachment thumbnails.
				if ( ! file_exists( $output_file_src ) ) {
					bp_video_regenerate_attachment_thumbnails( $attachment_id );
				}
			} elseif ( ! $file ) {

				bp_video_regenerate_attachment_thumbnails( $attachment_id );

				$file = image_get_intermediate_size( $attachment_id, $size );

				if ( $file && ! empty( $file['path'] ) ) {

					$output_file_src = $upload_dir . '/' . $file['path'];

				} elseif ( wp_get_attachment_image_src( $attachment_id ) ) {

					$output_file_src = get_attached_file( $attachment_id );

					// Regenerate attachment thumbnails.
					if ( ! file_exists( $output_file_src ) ) {
						bp_video_regenerate_attachment_thumbnails( $attachment_id );
						$file = image_get_intermediate_size( $attachment_id, $size );
					}
				}
			} elseif ( wp_get_attachment_image_src( $attachment_id ) ) {

				$output_file_src = get_attached_file( $attachment_id );

				// Regenerate attachment thumbnails.
				if ( ! file_exists( $output_file_src ) ) {
					bp_video_regenerate_attachment_thumbnails( $attachment_id );
					$file = image_get_intermediate_size( $attachment_id, $size );
				}
			}

			// Override the video attachment id to given thumbnail id.
			$video->attachment_id = $attachment_id;

			if ( $generate ) {
				$file_extension  = pathinfo( $output_file_src, PATHINFO_EXTENSION );
				$attachment_path = $attachment_path . '.' . $file_extension;

				// Generate Video Thumb Symlink.
				bb_core_symlink_generator( 'video_thumb', $video, $size, $file, $output_file_src, $attachment_path );
			}

			$upload_directory = wp_get_upload_dir();
			$attachment_url   = bb_core_symlink_absolute_path( $attachment_path, $upload_directory );

			/**
			 * Filters the attachment URL.
			 * Added support for CDN URL.
			 *
			 * @since BuddyBoss 2.6.10
			 *
			 * @param string $attachment_url URL for the given attachment.
			 * @param int    $attachment_id  Attachment post ID.
			 */
			$attachment_url = apply_filters( 'wp_get_attachment_url', $attachment_url, $attachment_id );

			/**
			 * Filter for the after thumb symlink generate.
			 *
			 * @param string $attachment_url Attachment URL.
			 * @param object $video          Video Object.
			 *
			 * @since BuddyBoss 1.7.0.1
			 */
			$attachment_url = apply_filters( 'bb_video_after_get_attachment_symlink', $attachment_url, $video );
		}
	} else {
		$attachment_url = wp_get_attachment_url( $attachment_id );
	}

	return $attachment_url;
}

/**
 * Get activity video.
 *
 * @BuddyBoss 2.5.50
 *
 * @param object|null $activity Activity object.
 * @param array|null  $args     Extra argument to fetch media.
 *
 * @return string|bool
 */
function bb_video_get_activity_video( $activity = '', $args = array() ) {
	global $video_template;

	if ( empty( $activity ) ) {
		global $activities_template;
		$activity = $activities_template->activity ?? '';
	}

	if ( empty( $activity ) ) {
		return false;
	}

	// Get activity metas.
	$activity_metas = bb_activity_get_metadata( $activity->id );
	$video_ids      = '';
	if ( ! empty( $activity_metas['bp_video_ids'][0] ) ) {
		$video_ids = $activity_metas['bp_video_ids'][0];
	} elseif ( ! empty( $activity_metas['bp_video_id'][0] ) ) {
		$video_ids = $activity_metas['bp_video_id'][0];
	}

	if ( empty( $video_ids ) ) {
		return false;
	}

	if (
		(
			buddypress()->activity->id === $activity->component &&
			! bp_is_profile_video_support_enabled()
		) ||
		(
			bp_is_active( 'groups' ) &&
			buddypress()->groups->id === $activity->component &&
			! bp_is_group_video_support_enabled()
		)
	) {
		return false;
	}

	$video_args = array(
		'include'  => $video_ids,
		'order_by' => 'menu_order',
		'sort'     => 'ASC',
		'per_page' => 0,
	);

	$video_args = bp_parse_args(
		$args,
		$video_args,
		'activity_video'
	);

	if ( bp_is_active( 'activity' ) && bb_get_activity_scheduled_status() === $activity->status ) {
		$video_args['status'] = bb_video_get_scheduled_status();
	}

	if ( bp_is_active( 'groups' ) && buddypress()->groups->id === $activity->component ) {
		if ( bp_is_group_video_support_enabled() ) {
			$video_args['privacy'] = array( 'grouponly' );
			if ( ! bp_is_group_albums_support_enabled() ) {
				$video_args['album_id'] = 'existing-video';
			}
		} else {
			$video_args['privacy']  = array( '0' );
			$video_args['album_id'] = 'existing-video';
		}
	} else {
		$video_args['privacy'] = bp_video_query_privacy( $activity->user_id, 0, $activity->component );

		if ( 'activity_comment' === $activity->type ) {
			$video_args['privacy'][] = 'comment';
		}

		if ( ! bp_is_profile_video_support_enabled() ) {
			$video_args['user_id'] = 'null';
		}
		if ( ! bp_is_profile_albums_support_enabled() ) {
			$video_args['album_id'] = 'existing-video';
		}
	}

	$is_forum_activity = false;
	if (
		bp_is_active( 'forums' )
		&& in_array( $activity->type, array( 'bbp_forum_create', 'bbp_topic_create', 'bbp_reply_create' ), true )
		&& bp_is_forums_video_support_enabled()
	) {
		$is_forum_activity       = true;
		$video_args['privacy'][] = 'forums';
	}

	/**
	 * If the content has been changed by these filters bb_moderation_has_blocked_message,
	 * bb_moderation_is_blocked_message, bb_moderation_is_suspended_message then
	 * it will hide video content which is created by blocked/blocked/suspended member.
	 */
	$hide_forum_activity = function_exists( 'bb_moderation_to_hide_forum_activity' ) && bb_moderation_to_hide_forum_activity( $activity->id );

	if ( true === $hide_forum_activity ) {
		return false;
	}

	$content = '';
	if ( bp_has_video( $video_args ) ) {
		$classes = array(
			esc_attr( 'bb-video-length-' . $video_template->video_count ),
			( $video_template->video_count > 5 ? esc_attr( ' bb-video-length-more' ) : '' ),
			( true === $is_forum_activity ? esc_attr( ' forums-video-wrap' ) : '' ),
		);
		ob_start();
		?>
		<div class="bb-activity-video-wrap <?php echo esc_attr( implode( ' ', array_filter( $classes ) ) ); ?>">
			<?php
			bp_get_template_part( 'video/add-video-thumbnail' );
			bp_get_template_part( 'video/video-move' );
			while ( bp_video() ) {
				bp_the_video();
				bp_get_template_part( 'video/activity-entry' );
			}
			?>
		</div>
		<?php
		$content = ob_get_clean();
	}

	return $content;
}

/**
 * Maximum video thumbnail length in comment.
 *
 * @since BuddyBoss 2.5.80
 *
 * @return int
 */
function bb_video_get_activity_comment_max_thumb_length() {
	return (int) apply_filters( 'bb_video_get_activity_comment_max_thumb_length', 2 );
}

/**
 * Maximum video thumbnail length in activity.
 *
 * @since BuddyBoss 2.5.80
 *
 * @return int
 */
function bb_video_get_activity_max_thumb_length() {
	return (int) apply_filters( 'bb_video_get_activity_max_thumb_length', 3 );
}

/**
 * Return the video published status.
 *
 * @since BuddyBoss 2.6.10
 *
 * @return string
 */
function bb_video_get_published_status() {
	return buddypress()->video->published_status;
}

/**
 * Return the video scheduled status.
 *
 * @since BuddyBoss 2.6.10
 *
 * @return string
 */
function bb_video_get_scheduled_status() {
	return buddypress()->video->scheduled_status;
}

/**
 * Handle resumable file upload by processing chunks.
 *
 * This function processes individual chunks of a resumable upload and manages
 * the temporary storage of chunks until all parts are received.
 *
 * @since BuddyBoss 2.15.0
 *
 * @param string $tmp_file_path Temporary file path of the uploaded chunk.
 * @param string $filename     Original filename of the file being uploaded.
 *
 * @return array {
 *     Array containing upload status and results.
 *
 *     @type bool   $final     Whether all chunks have been received.
 *     @type string $path      Final file path (only if upload is complete).
 *     @type array  $successes Array of success messages.
 *     @type array  $errors    Array of error messages.
 *     @type array  $warnings  Array of warning messages.
 * }
 */
function bb_resumable_upload( $tmp_file_path, $filename ) {
	$successes = array();
	$errors    = array();
	$warnings  = array();

	$dir = apply_filters( 'bfu_temp_dir', wp_upload_dir()['basedir'] . '/bb-large-upload' );
	$dir = trailingslashit( $dir );

	// phpcs:ignore WordPress.Security.NonceVerification.Missing
	$identifier = isset( $_POST['dzuuid'] ) ? trim( sanitize_text_field( wp_unslash( $_POST['dzuuid'] ) ) ) : '';

	// Security: Validate identifier format to prevent path traversal.
	if ( empty( $identifier ) || ! preg_match( '/^[a-zA-Z0-9\-]+$/', $identifier ) ) {
		$errors[] = __( 'Invalid upload identifier', 'buddyboss' );

		return array(
			'final'     => false,
			'successes' => $successes,
			'errors'    => $errors,
			'warnings'  => $warnings,
		);
	}

	$file_chunks_folder = $dir . $identifier;

	// Create directory with secure permissions.
	if ( ! is_dir( $file_chunks_folder ) ) {
		if ( ! wp_mkdir_p( $file_chunks_folder ) ) {
			$errors[] = __( 'Failed to create upload directory', 'buddyboss' );

			return array(
				'final'     => false,
				'successes' => $successes,
				'errors'    => $errors,
				'warnings'  => $warnings,
			);
		}
		chmod( $file_chunks_folder, 0755 );
	}

	// Remove problematic symbols from filename.
	$filename  = str_replace( array( ' ', '(', ')' ), '_', $filename );
	$info      = pathinfo( $filename );
	$extension = isset( $info['extension'] ) ? '.' . strtolower( $info['extension'] ) : '';
	$filename  = sanitize_file_name( $info['filename'] );

	// Validate file extension against allowed types.
	$allowed_extensions = apply_filters( 'bfu_allowed_extensions', array(
		'mp4',
		'mov',
		'avi',
		'wmv',
		'flv',
		'mkv',
		'webm',
	) );
	$ext_without_dot    = ltrim( $extension, '.' );
	if ( ! empty( $extension ) && ! in_array( $ext_without_dot, $allowed_extensions, true ) ) {
		$errors[] = sprintf(
		/* translators: %s: file extension */
			__( 'File type %s is not allowed', 'buddyboss' ),
			$extension
		);

		return array(
			'final'     => false,
			'successes' => $successes,
			'errors'    => $errors,
			'warnings'  => $warnings,
		);
	}

	// phpcs:disable WordPress.Security.NonceVerification.Missing
	$total_size   = isset( $_POST['dztotalfilesize'] ) ? sanitize_text_field( wp_unslash( $_POST['dztotalfilesize'] ) ) : '0';
	$total_chunks = isset( $_POST['dztotalchunkcount'] ) ? (int) sanitize_text_field( wp_unslash( $_POST['dztotalchunkcount'] ) ) : 0;
	$chunk_index  = isset( $_POST['dzchunkindex'] ) ? (int) sanitize_text_field( wp_unslash( $_POST['dzchunkindex'] ) ) : 0;
	$chunk_size   = isset( $_POST['dzchunksize'] ) ? sanitize_text_field( wp_unslash( $_POST['dzchunksize'] ) ) : '0';
	$start_byte   = isset( $_POST['dzchunkbyteoffset'] ) ? sanitize_text_field( wp_unslash( $_POST['dzchunkbyteoffset'] ) ) : '0';
	// phpcs:enable WordPress.Security.NonceVerification.Missing

	// Validate chunk parameters.
	if ( $total_chunks <= 0 || $chunk_index < 0 || $chunk_index >= $total_chunks ) {
		$errors[] = __( 'Invalid chunk parameters', 'buddyboss' );

		return array(
			'final'     => false,
			'successes' => $successes,
			'errors'    => $errors,
			'warnings'  => $warnings,
		);
	}

	// Validate file size limits (using string comparison for large files).
	$max_upload_size = apply_filters( 'bfu_max_upload_size', wp_max_upload_size() );
	if ( (float) $total_size > $max_upload_size ) {
		$errors[] = sprintf(
		/* translators: %s: file size */
			__( 'File size exceeds maximum allowed size of %s', 'buddyboss' ),
			size_format( $max_upload_size )
		);

		return array(
			'final'     => false,
			'successes' => $successes,
			'errors'    => $errors,
			'warnings'  => $warnings,
		);
	}

	// Validate chunk file size.
	$uploaded_chunk_size = file_exists( $tmp_file_path ) ? filesize( $tmp_file_path ) : 0;
	if ( $uploaded_chunk_size <= 0 ) {
		$errors[] = __( 'Uploaded chunk is empty', 'buddyboss' );

		return array(
			'final'     => false,
			'successes' => $successes,
			'errors'    => $errors,
			'warnings'  => $warnings,
		);
	}

	// Security: Validate total accumulated size doesn't exceed declared size.
	$current_total  = 0;
	$existing_parts = glob( $file_chunks_folder . '/' . $filename . '.part*' );
	if ( is_array( $existing_parts ) ) {
		foreach ( $existing_parts as $part ) {
			$current_total += filesize( $part );
		}
	}

	$tolerance_margin = (float) $chunk_size * 0.1; // 10% tolerance.
	if ( $current_total + $uploaded_chunk_size > (float) $total_size + $tolerance_margin ) {
		$errors[] = __( 'Total uploaded size exceeds declared file size', 'buddyboss' );

		return array(
			'final'     => false,
			'successes' => $successes,
			'errors'    => $errors,
			'warnings'  => $warnings,
		);
	}

	$chunk_file = $file_chunks_folder . '/' . $filename . '.part' . $chunk_index;

	// Move uploaded chunk to destination.
	if ( ! move_uploaded_file( $tmp_file_path, $chunk_file ) ) {
		$errors[] = sprintf(
		/* translators: %d: chunk index */
			__( 'Failed to save chunk %d', 'buddyboss' ),
			$chunk_index,
		);

		return array(
			'final'     => false,
			'successes' => $successes,
			'errors'    => $errors,
			'warnings'  => $warnings,
		);
	}

	// Set secure file permissions.
	chmod( $chunk_file, 0644 );

	$check_args = array(
		'file_chunks_folder' => $file_chunks_folder,
		'filename'           => $filename,
		'extension'          => $extension,
		'total_size'         => $total_size,
		'total_chunks'       => $total_chunks,
		'successes'          => &$successes,
		'errors'             => &$errors,
		'warnings'           => &$warnings,
	);
	$new_path   = bb_check_all_parts( $check_args );

	if ( 0 === count( $errors ) && ! empty( $new_path ) ) {
		return array(
			'final'     => true,
			'path'      => $new_path,
			'successes' => $successes,
			'errors'    => $errors,
			'warnings'  => $warnings,
		);
	}

	return array(
		'final'     => false,
		'successes' => $successes,
		'errors'    => $errors,
		'warnings'  => $warnings,
	);
}

/**
 * Check if all file chunks have been uploaded and create final file.
 *
 * This function verifies that all chunks of a resumable upload have been
 * received and creates the final file by combining all chunks.
 *
 * @since BuddyBoss 2.15.0
 *
 * @param array $args {
 *     Array of arguments.
 *
 *     @type string $file_chunks_folder Path to the folder containing file chunks.
 *     @type string $filename          Base filename without extension.
 *     @type string $extension         File extension with leading dot.
 *     @type string $total_size        Expected total file size in bytes (string for large files).
 *     @type int    $total_chunks      Total number of chunks expected.
 *     @type array  $successes        Array of success messages (passed by reference).
 *     @type array  $errors           Array of error messages (passed by reference).
 *     @type array  $warnings         Array of warning messages (passed by reference).
 * }
 *
 * @throws Exception If file cannot be created.
 *
 * @return string|false Final file path if successful, false otherwise.
 */
function bb_check_all_parts( $args ) {
	// Extract variables from array.
	$file_chunks_folder = $args['file_chunks_folder'];
	$filename           = $args['filename'];
	$extension          = $args['extension'];
	$total_size         = $args['total_size'];
	$total_chunks       = $args['total_chunks'];
	$successes          = &$args['successes'];
	$errors             = &$args['errors'];
	$warnings           = &$args['warnings'];
	// Use file locking to prevent race conditions during chunk verification.
	$lock_file = $file_chunks_folder . '/.lock';
	$lock_fp   = fopen( $lock_file, 'c+' );

	if ( false === $lock_fp ) {
		$errors[] = __( 'Failed to create lock file', 'buddyboss' );

		return false;
	}

	// Acquire exclusive lock.
	if ( ! flock( $lock_fp, LOCK_EX ) ) {
		fclose( $lock_fp );
		$errors[] = __( 'Failed to acquire lock', 'buddyboss' );

		return false;
	}

	// Count all the parts of this file.
	$parts = glob( $file_chunks_folder . '/' . $filename . '.part*' );

	if ( ! is_array( $parts ) ) {
		$parts = array();
	}

	// Remove lock file from parts array if present.
	$parts = array_filter(
		$parts,
		function ( $part ) {
			return false === strpos( $part, '.lock' );
		}
	);

	$parts_count = count( $parts );
	$successes[] = sprintf(
	/* translators: %1$d: parts count, %2$d: total chunks */
		__( '%1$d of %2$d parts received', 'buddyboss' ),
		$parts_count,
		$total_chunks
	);

	// Check if all the parts are present.
	if ( $parts_count !== $total_chunks ) {
		flock( $lock_fp, LOCK_UN );
		fclose( $lock_fp );

		return false;
	}

	// Verify all expected parts exist (no missing indices).
	for ( $i = 0; $i < $total_chunks; $i ++ ) {
		$expected_part = $file_chunks_folder . '/' . $filename . '.part' . $i;
		if ( ! file_exists( $expected_part ) ) {
			flock( $lock_fp, LOCK_UN );
			fclose( $lock_fp );
			$errors[] = sprintf(
			/* translators: %d: chunk index */
				__( 'Missing chunk %d', 'buddyboss' ),
				$i
			);

			return false;
		}
	}

	// Verify total size matches.
	$loaded_size = 0;
	foreach ( $parts as $file ) {
		$loaded_size += filesize( $file );
	}

	// Allow small tolerance for size differences (1%).
	$tolerance = (float) $total_size * 0.01;
	if ( abs( $loaded_size - (float) $total_size ) > $tolerance ) {
		flock( $lock_fp, LOCK_UN );
		fclose( $lock_fp );
		$errors[] = sprintf(
		/* translators: %1$s: expected size, %2$s: got size */
			__( 'File size mismatch. Expected: %1$s, Got: %2$s', 'buddyboss' ),
			size_format( $total_size ),
			size_format( $loaded_size )
		);

		return false;
	}

	// Create final file from chunks.
	$args     = array(
		'file_chunks_folder' => $file_chunks_folder,
		'file_name'          => $filename,
		'extension'          => $extension,
		'total_size'         => $total_size,
		'total_chunks'       => $total_chunks,
		'successes'          => &$successes,
		'errors'             => &$errors,
		'warnings'           => &$warnings,
	);
	$new_path = bb_create_file_from_chunks( $args );

	// Release lock and close lock file.
	flock( $lock_fp, LOCK_UN );
	fclose( $lock_fp );

	if ( ! empty( $new_path ) && 0 === count( $errors ) ) {
		bb_clean_up( $file_chunks_folder );

		return $new_path;
	}

	return false;
}

/**
 * Create final file by combining all uploaded chunks.
 *
 * This function reads all chunk files and combines them into a single
 * final file in the correct order using buffered reading to avoid memory issues.
 *
 * @since BuddyBoss 2.15.0
 *
 * @param array $args {
 *     Array of arguments.
 *
 *     @type string $file_chunks_folder Path to the folder containing file chunks.
 *     @type string $file_name         Base filename without extension.
 *     @type string $extension         File extension with leading dot.
 *     @type string $total_size        Expected total file size in bytes (string for large files).
 *     @type int    $total_chunks      Total number of chunks to combine.
 *     @type array  $successes        Array of success messages (passed by reference).
 *     @type array  $errors           Array of error messages (passed by reference).
 *     @type array  $warnings         Array of warning messages (passed by reference).
 * }
 *
 * @throws Exception If file cannot be created.
 *
 * @return string|false Final file path if successful, false otherwise.
 */
function bb_create_file_from_chunks( $args ) {
	// Extract variables from array.
	$file_chunks_folder = $args['file_chunks_folder'];
	$file_name          = $args['file_name'];
	$extension          = $args['extension'];
	$total_size         = $args['total_size'];
	$total_chunks       = $args['total_chunks'];
	$successes          = &$args['successes'];
	$errors             = &$args['errors'];
	$warnings           = &$args['warnings'];

	/**
	 * Filters the temporary directory for creating the final file.
	 *
	 * Use uploads directory to ensure chunks and final file are on the same filesystem
	 * for efficient file operations (rename instead of copy).
	 *
	 * @since 2.15.0
	 *
	 * @param string $rel_path Temporary directory path.
	 */
	$rel_path = apply_filters( 'bfu_temp_dir', wp_upload_dir()['basedir'] . '/bb-whole-from-chunks' );
	$rel_path = trailingslashit( $rel_path );

	// Ensure destination directory exists.
	if ( ! is_dir( $rel_path ) ) {
		if ( ! wp_mkdir_p( $rel_path ) ) {
			$errors[] = __( 'Cannot create destination directory', 'buddyboss' );

			return false;
		}
		chmod( $rel_path, 0755 );
	}

	$filename_args = array(
		'rel_path'       => $rel_path,
		'orig_file_name' => $file_name,
		'extension'      => $extension,
		'errors'         => &$errors,
	);
	$save_name     = bb_get_next_available_filename( $filename_args );

	if ( ! $save_name ) {
		return false;
	}

	$final_file_path = $rel_path . $save_name . $extension;
	$fp              = fopen( $final_file_path, 'wb' );

	if ( false === $fp ) {
		$errors[] = __( 'Cannot create the destination file', 'buddyboss' );

		return false;
	}

	// Use try-finally pattern for resource cleanup (PHP 5.5+).
	$success = false;

	try {
		// Combine chunks using buffered reading to avoid memory exhaustion.
		for ( $i = 0; $i < $total_chunks; $i ++ ) {
			$chunk_path = $file_chunks_folder . '/' . $file_name . '.part' . $i;

			if ( ! file_exists( $chunk_path ) ) {
				$errors[] = sprintf(
				/* translators: %d: chunk index */
					__( 'Chunk %d not found', 'buddyboss' ),
					$i
				);
				throw new Exception( 'Missing chunk' );
			}

			$chunk_fp = fopen( $chunk_path, 'rb' );

			if ( false === $chunk_fp ) {
				$errors[] = sprintf(
				/* translators: %d: chunk index */
					__( 'Failed to open chunk %d', 'buddyboss' ),
					$i
				);
				throw new Exception( 'Cannot open chunk' );
			}

			// Stream chunk data in 8KB buffers to minimize memory usage.
			while ( ! feof( $chunk_fp ) ) {
				$buffer = fread( $chunk_fp, 8192 );

				if ( false === $buffer ) {
					fclose( $chunk_fp );
					$errors[] = sprintf(
					/* translators: %d: chunk index */
						__( 'Failed to read chunk %d', 'buddyboss' ),
						$i
					);
					throw new Exception( 'Read error' );
				}

				$written = fwrite( $fp, $buffer );

				if ( false === $written ) {
					fclose( $chunk_fp );
					$errors[] = sprintf(
					/* translators: %d: chunk index */
						__( 'Failed to write data from chunk %d', 'buddyboss' ),
						$i
					);
					throw new Exception( 'Write error' );
				}
			}

			fclose( $chunk_fp );
		}

		$success = true;

	} catch ( Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
		// Error already added to $errors array.
	} finally {
		// Always close the main file handle.
		if ( is_resource( $fp ) ) {
			fclose( $fp );
		}
	}

	// Clean up partial file on failure.
	if ( ! $success ) {
		if ( file_exists( $final_file_path ) ) {
			unlink( $final_file_path );
		}

		return false;
	}

	// Verify final file size.
	clearstatcache( true, $final_file_path );
	$final_size = filesize( $final_file_path );
	$tolerance  = (float) $total_size * 0.01;

	if ( abs( $final_size - (float) $total_size ) > $tolerance ) {
		unlink( $final_file_path );
		$errors[] = sprintf(
		/* translators: %1$s: expected size, %2$s: got size */
			__( 'Final file size mismatch. Expected: %1$s, Got: %2$s', 'buddyboss' ),
			size_format( $total_size ),
			size_format( $final_size )
		);

		return false;
	}

	// Set secure file permissions.
	chmod( $final_file_path, 0644 );

	$successes[] = sprintf(
	/* translators: %s: file name */
		__( 'File successfully created: %s', 'buddyboss' ),
		basename( $final_file_path )
	);

	return $final_file_path;
}

/**
 * Get next available filename to avoid conflicts.
 *
 * This function generates a unique filename by appending a number if
 * a file with the same name already exists.
 *
 * @since BuddyBoss 2.15.0
 *
 * @param array $args {
 *     Array of arguments.
 *
 *     @type string $rel_path        Directory path where file will be saved.
 *     @type string $orig_file_name Original filename without extension.
 *     @type string $extension      File extension with leading dot.
 *     @type array  $errors         Array of error messages (passed by reference).
 * }
 *
 * @return string|false Unique filename without extension, or false on error.
 */
function bb_get_next_available_filename( $args ) {
	// Extract variables from array.
	$rel_path       = $args['rel_path'];
	$orig_file_name = $args['orig_file_name'];
	$extension      = $args['extension'];
	$errors         = &$args['errors'];
	// Sanitize filename.
	$orig_file_name = sanitize_file_name( $orig_file_name );

	if ( empty( $orig_file_name ) ) {
		$errors[] = __( 'Invalid filename', 'buddyboss' );

		return false;
	}

	$full_path = $rel_path . $orig_file_name . $extension;

	// If file doesn't exist, use original name.
	if ( ! file_exists( $full_path ) ) {
		return $orig_file_name;
	}

	// Find next available filename with reasonable limit.
	$max_attempts = 1000; // Reduced from 10000 for better performance.
	$i            = 0;

	while ( $i < $max_attempts ) {
		++$i;
		$new_name = $orig_file_name . '_' . $i;
		$new_path = $rel_path . $new_name . $extension;

		if ( ! file_exists( $new_path ) ) {
			return $new_name;
		}
	}

	// If we couldn't find available name after max attempts, use timestamp.
	$timestamp_name = $orig_file_name . '_' . time() . '_' . wp_rand( 1000, 9999 );
	$timestamp_path = $rel_path . $timestamp_name . $extension;

	if ( ! file_exists( $timestamp_path ) ) {
		return $timestamp_name;
	}

	$errors[] = sprintf(
	/* translators: %1$s: file name, %2$s: file extension */
		__( 'Cannot create unique name for saving file %1$s%2$s', 'buddyboss' ),
		$orig_file_name,
		$extension
	);

	return false;
}

/**
 * Recursively remove directory and all its contents.
 *
 * This function removes a directory and all files and subdirectories
 * within it recursively with improved error handling.
 *
 * @since BuddyBoss 2.15.0
 *
 * @param string $dir Directory path to remove.
 *
 * @return bool True on success, false on failure.
 */
function bb_rrmdir( $dir ) {
	if ( ! is_dir( $dir ) ) {
		return false;
	}

	// Validate path to prevent accidental deletion of system directories.
	$wp_content_dir = wp_normalize_path( WP_CONTENT_DIR );
	$normalized_dir = wp_normalize_path( $dir );

	if ( false === strpos( $normalized_dir, $wp_content_dir ) ) {
		// Prevent deletion outside WP_CONTENT_DIR.
		return false;
	}

	$objects = scandir( $dir );

	if ( false === $objects ) {
		return false;
	}

	foreach ( $objects as $object ) {
		if ( '.' === $object || '..' === $object ) {
			continue;
		}

		$path = $dir . '/' . $object;

		if ( is_dir( $path ) ) {
			// Recursively delete subdirectory.
			if ( ! bb_rrmdir( $path ) ) {
				return false;
			}
		} elseif ( ! unlink( $path ) ) {
			return false;
		}
	}

	return rmdir( $dir );
}

/**
 * Clean up temporary chunk files and directories.
 *
 * This function safely removes the temporary directory containing
 * file chunks by renaming it first to avoid concurrent access issues.
 *
 * @since BuddyBoss 2.15.0
 *
 * @param string $file_chunks_folder Path to the folder containing file chunks.
 *
 * @return bool True on success, false on failure.
 */
function bb_clean_up( $file_chunks_folder ) {
	if ( ! is_dir( $file_chunks_folder ) ) {
		return false;
	}

	// Generate unique temporary name to avoid conflicts.
	$temp_name = $file_chunks_folder . '_UNUSED_' . time() . '_' . wp_rand( 1000, 9999 );

	// Rename the temporary directory to avoid access from other concurrent chunks uploads.
	if ( rename( $file_chunks_folder, $temp_name ) ) {
		return bb_rrmdir( $temp_name );
	}

	// If rename failed, try direct deletion (less safe but better than leaving files).
	return bb_rrmdir( $file_chunks_folder );
}