$status]); } function picsart_video_configured() { return function_exists('openssl_encrypt'); } function picsart_video_permission() { return current_user_can('upload_files') ? true : picsart_video_error(__('Media permission required.','picsart-commerce'),403); } add_action('rest_api_init', function () { register_rest_route('picsart/v1', '/studio', ['methods'=>['GET','POST'], 'permission_callback'=>'picsart_video_permission', 'callback'=>'picsart_video_proxy']); }); function picsart_video_proxy(WP_REST_Request $request) { return picsart_runtime_request($request); } function picsart_video_product($id) { if (!function_exists('wc_get_product')) return picsart_video_error(__('Install WooCommerce to browse products.','picsart-commerce'),409); if (!current_user_can('edit_post',$id)) return picsart_video_error(__('You cannot edit this product.','picsart-commerce'),403); $p=wc_get_product($id); if (!$p) return picsart_video_error(__('Product not found.','picsart-commerce'),404); $photos=[]; foreach (array_unique(array_merge([$p->get_image_id()],$p->get_gallery_image_ids())) as $attachment) { $image=wp_get_attachment_image_src($attachment,'full'); if (!$image) continue; $photos[]=['id'=>"$id:$attachment",'url'=>$image[0],'width'=>$image[1],'height'=>$image[2],'name'=>get_the_title($attachment),'productId'=>(string)$id,'productName'=>$p->get_name(),'sku'=>$p->get_sku(),'instanceId'=>picsart_runtime_instance()]; } return ['id'=>(string)$id,'name'=>$p->get_name(),'sku'=>$p->get_sku(),'photos'=>$photos]; } function picsart_video_internal(WP_REST_Request $request) { $b=$request->get_json_params(); $action=$b['action']??''; if ($action==='apply-image') return picsart_commerce_apply($b); if ($action==='blog-draft') return picsart_commerce_draft($b); if ($action==='notification-preferences') return picsart_notification_preferences($b); if ($action==='notification') return picsart_send_notification($b); if ($action==='products') { if (!function_exists('wc_get_products')) return ['products'=>[]]; $page=max(1,absint($b['cursor']??1)); $rows=wc_get_products(['limit'=>50,'page'=>$page,'status'=>['publish','draft','private'],'orderby'=>'ID','order'=>'ASC']); $products=[]; foreach ($rows as $p) { $item=picsart_video_product($p->get_id()); if (!is_wp_error($item)) $products[]=$item; } return ['products'=>$products,'nextCursor'=>count($rows)===50?(string)($page+1):null]; } if ($action==='product') return picsart_video_product(absint($b['id']??0)); if ($action==='source') { require_once ABSPATH.'wp-admin/includes/file.php'; $product=picsart_video_product(absint($b['productId']??0)); if (is_wp_error($product)) return $product; $photo=null; foreach ($product['photos'] as $candidate) if ($candidate['id']===($b['sourceId']??'')) $photo=$candidate; if (!$photo) return picsart_video_error(__('Product photo changed. Select it again.','picsart-commerce'),409); $parts=explode(':',$photo['id']); $path=get_attached_file(absint($parts[1])); if (!$path || !is_file($path) || filesize($path)>15*1024*1024) return picsart_video_error(__('Product photo is unavailable or exceeds 15 MB.','picsart-commerce')); $editor=wp_get_image_editor($path); if (is_wp_error($editor)) return picsart_video_error(__('This photo cannot be prepared.','picsart-commerce')); $size=$editor->get_size(); if (min($size['width'],$size['height'])<640 || max($size['width'],$size['height'])>8192) return picsart_video_error(__('Choose a photo from 640 to 8192 pixels.','picsart-commerce')); $mime=wp_get_image_mime($path); $extensions=['image/jpeg'=>'jpg','image/png'=>'png','image/webp'=>'webp']; if (!isset($extensions[$mime])) return picsart_video_error(__('Choose a JPEG, PNG or WebP source.','picsart-commerce')); $tmp=wp_tempnam('picsart-source'); $saved=$editor->save($tmp,$mime); if (is_wp_error($saved)) { wp_delete_file($tmp); return picsart_video_error(__('Could not prepare the product photo.','picsart-commerce')); } $bytes=file_get_contents($saved['path']); wp_delete_file($saved['path']); if ($tmp!==$saved['path']) wp_delete_file($tmp); return ['base64'=>base64_encode($bytes),'name'=>'product-photo.'.$extensions[$mime]]; } if ($action==='import') return picsart_video_import($b); if ($action==='file' || $action==='download') { $id=absint($b['id']??0); if (get_post_type($id)!=='attachment' || !current_user_can('edit_post',$id)) return picsart_video_error(__('Media not available.','picsart-commerce'),404); $url=wp_get_attachment_url($id); return $action==='download' ? ['downloadUrls'=>[['url'=>$url]]] : ['_id'=>(string)$id,'operationStatus'=>'READY','private'=>false]; } if ($action==='detach') { $id=absint($b['productId']??0); $media=absint($b['mediaId']??0); $p=picsart_video_product($id); if (is_wp_error($p)) return $p; if ($p['sku']!==($b['sku']??'')) return picsart_video_error(__('Product identity changed. Review it again.','picsart-commerce'),409); if (get_post_type($media)!=='attachment' || !current_user_can('edit_post',$media) || !str_starts_with((string)get_post_mime_type($media),'video/')) return picsart_video_error(__('Choose a video from your Media Library.','picsart-commerce'),403); $native=picsart_native_product_video($id,$media,true);if (is_wp_error($native)) return $native; $ids=get_post_meta($id,'_picsart_video_ids',true); if (!is_array($ids)) $ids=[]; $remaining=array_values(array_filter($ids,fn($existing)=>absint($existing)!==$media)); if ($remaining!==$ids) update_post_meta($id,'_picsart_video_ids',$remaining); return ['ok'=>true,'alreadyRemoved'=>!in_array($media,array_map('absint',$ids),true)]; } if ($action==='attach') { $id=absint($b['productId']??0); $media=absint($b['mediaId']??0); $p=picsart_video_product($id); if (is_wp_error($p)) return $p; if ($p['sku']!==($b['sku']??'')) return picsart_video_error(__('Product identity changed. Review it again.','picsart-commerce'),409); if (get_post_type($media)!=='attachment' || !current_user_can('edit_post',$media) || !str_starts_with((string)get_post_mime_type($media),'video/')) return picsart_video_error(__('Choose a video from your Media Library.','picsart-commerce')); $native=picsart_native_product_video($id,$media);if (is_wp_error($native)) return $native; $ids=get_post_meta($id,'_picsart_video_ids',true); if (!is_array($ids)) $ids=[]; if (!in_array($media,$ids,true)) { $ids[]=$media; update_post_meta($id,'_picsart_video_ids',$ids); } return ['ok'=>true]; } return picsart_video_error(__('Unknown platform action.','picsart-commerce'),404); } /** Release a claim only after a confirmed failure before an attachment exists. */ function picsart_video_import_failed($receipt, $error) { foreach (get_posts(['post_type'=>'picsart_receipt','post_status'=>'private','title'=>$receipt,'numberposts'=>-1,'fields'=>'ids']) as $id) wp_delete_post($id,true); delete_option($receipt); return $error; } function picsart_video_import($b) { if (!empty($b['options']['private'])) return picsart_video_error(__('Private legacy imports are unsupported.','picsart-commerce')); $url=esc_url_raw($b['url']??''); if (wp_parse_url($url,PHP_URL_SCHEME)!=='https' || !wp_http_validate_url($url)) return picsart_video_error(__('Media requires a public HTTPS URL.','picsart-commerce')); $key=hash('sha256',get_current_user_id().'|'.$url.'|'.($b['options']['displayName']??'')); $receipt='_ppv_import_'.$key; $old=get_option($receipt); if (is_array($old) && !empty($old['id'])) { if (get_post_type($old['id']) !== 'attachment' || !current_user_can('edit_post',$old['id']) || !is_file(get_attached_file($old['id']))) return picsart_video_error(__('The previous WordPress copy is no longer available. Inspect Media Library before choosing another copy.', 'picsart-commerce'),409); return ['file'=>['_id'=>(string)$old['id']]]; } if (!picsart_receipt_add($receipt,['status'=>'started'],'',false)) return picsart_video_error(__('Import outcome is uncertain. Inspect Media Library before retrying.','picsart-commerce'),409); require_once ABSPATH.'wp-admin/includes/file.php'; require_once ABSPATH.'wp-admin/includes/image.php'; $tmp=wp_tempnam(); $limit=100*1024*1024; $r=wp_safe_remote_get($url,['timeout'=>60,'stream'=>true,'filename'=>$tmp,'limit_response_size'=>$limit+1,'redirection'=>3]); if (is_wp_error($r) || wp_remote_retrieve_response_code($r)!==200 || filesize($tmp)>$limit) { wp_delete_file($tmp); return picsart_video_import_failed($receipt,picsart_video_error(__('Could not download media within the 100 MB limit.','picsart-commerce'),502)); } $mime=(new finfo(FILEINFO_MIME_TYPE))->file($tmp); $extensions=['video/mp4'=>'mp4','video/webm'=>'webm','image/jpeg'=>'jpg','image/png'=>'png','image/webp'=>'webp']; if (!isset($extensions[$mime])) { wp_delete_file($tmp); return picsart_video_import_failed($receipt,picsart_video_error(__('Unsupported media format.','picsart-commerce'))); } $name=sanitize_file_name(pathinfo($b['options']['displayName']??'Picsart',PATHINFO_FILENAME)).'.'.$extensions[$mime]; $upload=['name'=>$name,'tmp_name'=>$tmp,'type'=>$mime,'error'=>0,'size'=>filesize($tmp)]; $file=wp_handle_sideload($upload,['test_form'=>false]); if (isset($file['error'])) { wp_delete_file($tmp); return picsart_video_import_failed($receipt,picsart_video_error($file['error'])); } $id=wp_insert_attachment(['post_mime_type'=>$file['type'],'post_title'=>pathinfo($name,PATHINFO_FILENAME),'post_status'=>'inherit','guid'=>$file['url']],$file['file'],0,true); if (is_wp_error($id)) { wp_delete_file($file['file']); return picsart_video_import_failed($receipt,$id); } update_post_meta($id,'_picsart_created',1); // Commit the durable native receipt before expensive thumbnail processing. update_option($receipt,['status'=>'ready','id'=>$id],false); if (str_starts_with($mime,'image/')) { $size=wp_getimagesize($file['file']); if ($size) wp_update_attachment_metadata($id,['width'=>$size[0],'height'=>$size[1],'file'=>_wp_relative_upload_path($file['file']),'sizes'=>[],'image_meta'=>wp_read_image_metadata($file['file']) ?: []]); } wp_schedule_single_event(time()+1,'picsart_import_metadata',[$id,0]); return ['file'=>['_id'=>(string)$id]]; } /** Thumbnails are resumable; a host timeout must not lose the saved original. */ function picsart_import_metadata($id, $attempt=0) { if (get_post_type($id)!=='attachment' || !get_post_meta($id,'_picsart_created',true) || !is_file(get_attached_file($id))) return; require_once ABSPATH.'wp-admin/includes/image.php'; // Video metadata helpers are used by wp_generate_attachment_metadata(). require_once ABSPATH.'wp-admin/includes/media.php'; if ($attempt<2) wp_schedule_single_event(time()+120,'picsart_import_metadata',[$id,$attempt+1]); wp_raise_memory_limit('image'); if (wp_attachment_is_image($id)) $metadata=wp_update_image_subsizes($id); else $metadata=wp_generate_attachment_metadata($id,get_attached_file($id)); if (is_wp_error($metadata)) return; if (is_array($metadata)) wp_update_attachment_metadata($id,$metadata); if ($attempt<2 && (!wp_attachment_is_image($id) || !wp_get_missing_image_subsizes($id))) wp_clear_scheduled_hook('picsart_import_metadata',[$id,$attempt+1]); } add_action('picsart_import_metadata','picsart_import_metadata',10,2); /** Write a portable video into the merchant-approved product description. */ function picsart_native_product_video($id,$media,$remove=false) { global $wpdb;$post=get_post($id);$before=$post->post_content;$key='_picsart_native_video_markup_'.$media;$markup=get_post_meta($id,$key,true); if (!$markup) {if ($remove) return true;$url=wp_get_attachment_url($media);if (!$url) return picsart_video_error(__('Video file unavailable.','picsart-commerce'),409);$markup='
';update_post_meta($id,$key,$markup);} if ($remove) {$after=str_replace($markup,'',$before);if ($after===$before) return true;} else {if (str_contains($before,$markup)) return true;$after=$before."\n".$markup;} // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Conditional content write avoids overwriting simultaneous merchant edits; cache cleared below. $changed=$wpdb->update($wpdb->posts,['post_content'=>$after,'post_modified'=>current_time('mysql'),'post_modified_gmt'=>current_time('mysql',true)],['ID'=>$id,'post_content'=>$before]);clean_post_cache($id); if ($changed!==1) return picsart_video_error(__('Product content changed. Reload and review it before trying again.','picsart-commerce'),409); if (function_exists('wc_delete_product_transients')) wc_delete_product_transients($id);return true; }