├── HelveticaNeue-Regular.ttf ├── README.md ├── class.envato-basic.php ├── config.php ├── functions.php ├── gif.php ├── gif_5_star_ratings.php ├── gif_currently_viewing.php ├── gif_last_purchased.php ├── gif_recent_sales.php ├── icon_cart.png ├── icon_eye.png ├── icon_star.png ├── icon_trending.png ├── index.php ├── template.png ├── template.xcf ├── template_305.xcf ├── template_305_bg.png ├── template_305_frame.png ├── template_bg.png ├── template_blank.png ├── template_mask.png ├── test.html ├── test_mjpeg_stream.php ├── test_notification.php └── test_text.txt /HelveticaNeue-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtbaker/envato-live-sales-gif/437519643fcd86f91d54f3d0d1f83950524fbf34/HelveticaNeue-Regular.ttf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # envato-live-sales-gif 2 | 3 | More details on the forums: http://codecanyon.net/forums/thread/sales-notifications-for-item-pages/186647 4 | 5 | A live sales notification GIF you can place on Envato item pages. 6 | 7 | This probably won't work if you have nginx or some other sort of caching infront of apache. 8 | 9 | PHP needs to stream data straight to the browser as soon as it's generated. 10 | 11 | Tested and works on a stock standard Ubuntu Server AWS instance under Apache. 12 | 13 | needs php write permissions to create cache_statement.json in the same folder. 14 | -------------------------------------------------------------------------------- /class.envato-basic.php: -------------------------------------------------------------------------------- 1 | _client_id = $token; 29 | } 30 | public function set_client_secret($token){ 31 | $this->_client_secret = $token; 32 | } 33 | public function set_personal_token($token){ 34 | $this->_personal_token = $token; 35 | } 36 | public function set_redirect_url($token){ 37 | $this->_redirect_url = $token; 38 | } 39 | public function set_cookie($cookie){ 40 | $this->_cookie = $cookie; 41 | } 42 | public function api($endpoint, $params=array(), $personal = true){ 43 | $ch = curl_init($this->_api_url . $endpoint); 44 | if($personal && !empty($this->_personal_token)){ 45 | curl_setopt($ch,CURLOPT_HTTPHEADER,array('Authorization: ' . 'Bearer ' . $this->_personal_token)); 46 | }else if(!empty($this->token['access_token'])){ 47 | curl_setopt($ch,CURLOPT_HTTPHEADER,array('Authorization: ' . 'Bearer ' . $this->token['access_token'])); 48 | } 49 | curl_setopt($ch,CURLOPT_USERAGENT,'dtbaker Envato Item Sales'); 50 | curl_setopt($ch,CURLOPT_RETURNTRANSFER,true); 51 | $response = curl_exec($ch); 52 | return @json_decode($response,true); 53 | } 54 | 55 | 56 | 57 | private $ch = false; 58 | private $cookies = array(); 59 | private $cookie_file = false; 60 | public function curl_init($cookies = true) { 61 | if ( ! function_exists( 'curl_init' ) ) { 62 | echo 'Please contact hosting provider and enable CURL for PHP'; 63 | 64 | return false; 65 | } 66 | $this->ch = curl_init(); 67 | curl_setopt( $this->ch, CURLOPT_RETURNTRANSFER, true ); 68 | @curl_setopt( $this->ch, CURLOPT_FOLLOWLOCATION, true ); 69 | curl_setopt( $this->ch, CURLOPT_CONNECTTIMEOUT, 10 ); 70 | curl_setopt( $this->ch, CURLOPT_TIMEOUT, 20 ); 71 | curl_setopt( $this->ch, CURLOPT_HEADER, false ); 72 | curl_setopt( $this->ch, CURLOPT_USERAGENT, "Support Hub dtbaker" ); 73 | if ( $cookies ) { 74 | if ( ! $this->cookie_file ) { 75 | $this->cookie_file = tempnam( sys_get_temp_dir(), 'SupportHub' ); 76 | } 77 | curl_setopt( $this->ch, CURLOPT_COOKIEJAR, $this->cookie_file ); 78 | curl_setopt( $this->ch, CURLOPT_COOKIEFILE, $this->cookie_file ); 79 | curl_setopt( $this->ch, CURLOPT_HEADERFUNCTION, array( $this, "curl_header_callback" ) ); 80 | } 81 | } 82 | public function curl_done(){ 83 | @unlink($this->cookie_file); 84 | } 85 | public function get_url($url, $post = false, $extra_headers = array(), $cookies = true) { 86 | 87 | if($this->ch){ 88 | curl_close($this->ch); 89 | } 90 | $this->curl_init($cookies); 91 | 92 | if($cookies) { 93 | $cookies = ''; 94 | $this->cookies['envatosession'] = $this->_cookie; 95 | foreach ( $this->cookies as $key => $val ) { 96 | if ( ! strpos( $url, 'account.envato' ) && $key == 'envatosession' ) { 97 | continue; 98 | } 99 | $cookies = $cookies . $key . '=' . $val . '; '; 100 | } 101 | curl_setopt( $this->ch, CURLOPT_COOKIE, $cookies ); 102 | } 103 | 104 | curl_setopt( $this->ch, CURLOPT_URL, $url ); 105 | if($extra_headers){ 106 | curl_setopt( $this->ch, CURLOPT_HTTPHEADER, $extra_headers); 107 | } 108 | 109 | if ( is_string( $post ) && strlen( $post ) ) { 110 | curl_setopt( $this->ch, CURLOPT_POST, true ); 111 | curl_setopt( $this->ch, CURLOPT_POSTFIELDS, $post ); 112 | }else if ( is_array( $post ) && count( $post ) ) { 113 | curl_setopt( $this->ch, CURLOPT_POST, true ); 114 | curl_setopt( $this->ch, CURLOPT_POSTFIELDS, $post ); 115 | } else { 116 | curl_setopt( $this->ch, CURLOPT_POST, 0 ); 117 | } 118 | // safe mode redirection hack 119 | if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off')) { 120 | 121 | }else{ 122 | $mr = 6; 123 | $newurl = curl_getinfo($this->ch, CURLINFO_EFFECTIVE_URL); 124 | $rch = curl_copy_handle($this->ch); 125 | curl_setopt($rch, CURLOPT_HEADER, true); 126 | curl_setopt($rch, CURLOPT_NOBODY, true); 127 | curl_setopt($rch, CURLOPT_FORBID_REUSE, false); 128 | curl_setopt($rch, CURLOPT_RETURNTRANSFER, true); 129 | do { 130 | curl_setopt($rch, CURLOPT_URL, $newurl); 131 | $header = curl_exec($rch); 132 | if (curl_errno($rch)) { 133 | $code = 0; 134 | } else { 135 | $code = curl_getinfo($rch, CURLINFO_HTTP_CODE); 136 | if ($code == 301 || $code == 302) { 137 | preg_match('/Location:(.*?)\n/', $header, $matches); 138 | $newurl = trim(array_pop($matches)); 139 | } else { 140 | $code = 0; 141 | } 142 | } 143 | } while ($code && --$mr); 144 | curl_close($rch); 145 | if (!$mr) { 146 | return false; 147 | } 148 | curl_setopt($this->ch, CURLOPT_URL, $newurl); 149 | 150 | } 151 | return curl_exec( $this->ch ); 152 | } 153 | 154 | public function curl_header_callback($ch, $headerLine) { 155 | //echo $headerLine."\n"; 156 | if (preg_match('/^Set-Cookie:\s*([^;]*)/mi', $headerLine, $cookie) == 1){ 157 | $bits = explode('=',$cookie[1]); 158 | $this->cookies[$bits[0]] = $bits[1]; 159 | } 160 | return strlen($headerLine); // Needed by curl 161 | } 162 | 163 | /** 164 | * OAUTH STUFF 165 | */ 166 | 167 | public function get_authorization_url() { 168 | return 'https://api.envato.com/authorization?response_type=code&client_id='.$this->_client_id."&redirect_uri=".urlencode($this->_redirect_url); 169 | } 170 | public function get_token_url() { 171 | return 'https://api.envato.com/token'; 172 | } 173 | public function get_authentication($code) { 174 | $url = $this->get_token_url(); 175 | $parameters = array(); 176 | $parameters['grant_type'] = "authorization_code"; 177 | $parameters['code'] = $code; 178 | $parameters['redirect_uri'] = $this->_redirect_url; 179 | $parameters['client_id'] = $this->_client_id; 180 | $parameters['client_secret'] = $this->_client_secret; 181 | $fields_string = ''; 182 | foreach ( $parameters as $key => $value ) { 183 | $fields_string .= $key . '=' . urlencode($value) . '&'; 184 | } 185 | try { 186 | $response = $this->get_url($url, $fields_string, false, false); 187 | } catch ( EnvatoException $e ) { 188 | return false; 189 | } 190 | $this->token = json_decode( $response, true ); 191 | return $this->token; 192 | } 193 | protected function refresh_token(){ 194 | $url = $this->get_token_url(); 195 | 196 | $parameters = array(); 197 | $parameters['grant_type'] = "refresh_token"; 198 | 199 | $parameters['refresh_token'] = $this->token['refresh_token']; 200 | $parameters['redirect_uri'] = $this->_redirect_url; 201 | $parameters['client_id'] = $this->_client_id; 202 | $parameters['client_secret'] = $this->_client_secret; 203 | 204 | $fields_string = ''; 205 | foreach ( $parameters as $key => $value ) { 206 | $fields_string .= $key . '=' . urlencode($value) . '&'; 207 | } 208 | try { 209 | $response = $this->get_url($url, $fields_string, false, false); 210 | } 211 | catch (EnvatoException $e) { 212 | return false; 213 | } 214 | $new_token = json_decode($response, true); 215 | $this->token['access_token'] = $new_token['access_token']; 216 | return $this->token['access_token']; 217 | } 218 | 219 | 220 | 221 | } -------------------------------------------------------------------------------- /config.php: -------------------------------------------------------------------------------- 1 | 0; $y-=$move_pixels){ 23 | $this_options = $options; 24 | $this_options['offset_y'] = $y; 25 | //$this_options['text'] .= ' ('.$frame_delay.')'; 26 | $image_data = build_an_image($this_options); 27 | $image_data_framed = add_frame_to_image_data($image_data); 28 | $result .= get_frame_from_image_data($image_data_framed,floor($frame_delay)); 29 | $frame_delay = $frame_delay + $frame_inc; 30 | } 31 | $this_options = $options; 32 | $this_options['offset_y'] = 0; 33 | //$this_options['text'] .= ' ('.$frame_delay.')'; 34 | $image_data = build_an_image($this_options); 35 | $image_data_framed = add_frame_to_image_data($image_data); 36 | $result .= get_frame_from_image_data($image_data_framed,$options['pause']); 37 | 38 | $move_by = 50; 39 | $move_pixels = 2; 40 | for($y = 0; $y > -$move_by; $y-=$move_pixels){ 41 | $this_options = $options; 42 | $this_options['offset_y'] = $y; 43 | //$this_options['text'] .= ' ('.$frame_delay.')'; 44 | $image_data = build_an_image($this_options); 45 | $image_data_framed = add_frame_to_image_data($image_data); 46 | $result .= get_frame_from_image_data($image_data_framed,ceil($frame_delay)); 47 | $frame_delay = $frame_delay - $frame_inc; 48 | } 49 | break; 50 | case 'fade_in': 51 | for($x=1;$x<100;$x+=10){ 52 | $this_options = $options; 53 | $this_options['opacity'] = $x; 54 | $image_data = build_an_image($this_options); 55 | $image_data_framed = add_frame_to_image_data($image_data); 56 | $result .= get_frame_from_image_data($image_data_framed,8); 57 | } 58 | $image_data = build_an_image($options); 59 | $image_data_framed = add_frame_to_image_data($image_data); 60 | $result .= get_frame_from_image_data($image_data_framed,$options['pause']); 61 | 62 | break; 63 | } 64 | return $result; 65 | } 66 | function add_frame_to_image_data($image_data,$offset_y=0){ 67 | $bg = imagecreatefromstring($image_data); 68 | $mask = imagecreatefrompng(_ENVATO_LSG_TEMPLATE_MASK); 69 | imagecopymerge_alpha($bg, $mask, 0, 0, 0, 0, imagesx($mask), imagesy($mask), 100); 70 | ob_start(); 71 | imagegif($bg); 72 | return ob_get_clean(); 73 | } 74 | /** 75 | * 76 | * Takes some simply options and builds up an image using php gd functions 77 | * 78 | * @param $options 79 | * @return string raw gif image data 80 | */ 81 | function build_an_image($options){ 82 | 83 | $font_size = 12; 84 | $im = imagecreatefrompng(_ENVATO_LSG_TEMPLATE); 85 | imagetruecolortopalette($im, true, 256); 86 | $white = imagecolorallocate($im, 255, 255, 255); 87 | imagecolortransparent($im, $white); 88 | 89 | $image_width = imagesx($im); 90 | $image_height = imagesy($im); 91 | // Get Bounding Box Size 92 | $text_box = imagettfbbox($font_size, 0, _ENVATO_LSG_FONT, $options['text']); 93 | // Get your Text Width and Height 94 | $text_width = $text_box[2] - $text_box[0]; 95 | $text_height = $text_box[3] - $text_box[1]; 96 | $y = ($image_height / 2) - ($text_height / 2); 97 | $y+=6; 98 | 99 | if(!empty($options['offset_y'])){ 100 | $y += $options['offset_y']; 101 | } 102 | 103 | if(empty($options['icon'])) { 104 | // if there is no logo position the text in the middle of the image (http://stackoverflow.com/a/14517450/457850) 105 | // Calculate coordinates of the text 106 | $x = ($image_width / 2) - ($text_width / 2); 107 | $font_color = imagecolorallocate($im, 102, 102, 102); 108 | imagettftext($im, $font_size, 0, $x, $y, $font_color, _ENVATO_LSG_FONT, $options['text']); 109 | }else{ 110 | // add our icon to the image. 111 | $icon = imagecreatefrompng($options['icon']); 112 | $icon_width = imagesx($icon); 113 | $icon_height = imagesy($icon); 114 | $icon_y = ($image_height - $icon_height) / 2; 115 | if(!empty($options['offset_y'])) { 116 | $icon_y += $options['offset_y']; 117 | } 118 | // we have a logo, put the text near it on the left. 119 | $x = 60; 120 | // build up a temporary icon/text image so we can apply alpha to it if needed 121 | $logo_and_text = imagecreatetruecolor($image_width, $image_height); 122 | imagealphablending($logo_and_text, false); 123 | imagesavealpha($logo_and_text, true); 124 | $trans_colour = imagecolorallocatealpha($logo_and_text, 0, 0, 0, 127); 125 | imagefill($logo_and_text, 0, 0, $trans_colour); 126 | imagecolortransparent($logo_and_text, $trans_colour); 127 | 128 | $font_color = imagecolorallocate($logo_and_text, 102, 102, 102); 129 | imagettftext($logo_and_text, $font_size, 0, $x, $y, $font_color, _ENVATO_LSG_FONT, $options['text']); 130 | imagecopy($logo_and_text, $icon, 10, $icon_y, 0, 0, $icon_width, $icon_height); 131 | imagecopymerge_alpha($im, $logo_and_text, 0, 0, 0, 0, $image_width, $image_height, isset($options['opacity']) ? $options['opacity'] : 100); 132 | } 133 | ob_start(); 134 | imagegif($im); 135 | $image_data = ob_get_clean(); 136 | imagedestroy($im); 137 | return $image_data; 138 | } 139 | 140 | 141 | /** 142 | * 143 | * Builds up a new "animated" gif header based on a provided static gif image 144 | * This lets us send the first frame to the browser while we generate our other frames 145 | * 146 | * @param $contents - raw gif data from imagegif() output 147 | */ 148 | function get_animated_gif_header($contents) { 149 | if (strpos($contents, 'GIF89a') === 0) { 150 | 151 | /* 152 | * header: 6 153 | * logical screen descriptor: 7 (width and height) 154 | * global color table: (calculated based on logical screen desctirotp) 155 | * graphics control extension: 8 bytes 156 | * image descriptor: 10 bytes 157 | */ 158 | 159 | $first_13 = substr($contents, 0, 13); 160 | // find out teh size of our global colour table. 161 | $packed = base_convert(ord($first_13[10]), 10, 2); 162 | $global_color = substr($packed, 1, 3); 163 | $global_color_number = (int)base_convert($global_color, 2, 10); 164 | $global_color_count = pow(2, $global_color_number + 1); 165 | $global_color_bytes = 3 * $global_color_count; 166 | $first_13[10] = chr(0); // reset packed field 167 | $animated_extension = ''; 168 | $animated_extension .= hex2bin('21'); 169 | $animated_extension .= hex2bin('ff'); 170 | $animated_extension .= hex2bin('0b'); 171 | $animated_extension .= 'NETSCAPE2.0'; 172 | $animated_extension .= hex2bin('03'); 173 | $animated_extension .= hex2bin('01'); 174 | $animated_extension .= hex2bin('00');// number of times to loop. 0 infinite. 175 | $animated_extension .= hex2bin('00'); 176 | $animated_extension .= hex2bin('00'); 177 | return $first_13 . $animated_extension; 178 | } 179 | return false; 180 | } 181 | 182 | 183 | /** 184 | * This grabs the raw header and image data out of a static GIF image. 185 | * Formats this header/data in a way that can be appended to a streaming GIF animation. 186 | * Yes this can be simplified! I spent a lot of time digging around the GIF header offsets to get it right. Someone please make this more awesome. 187 | * Inspiration from this python script: https://github.com/jbochi/gifstreaming/blob/master/transform.py 188 | * 189 | * @param $contents - raw gif data from imagegif() output 190 | * @param $delay - number of seconds to show this frame in the animation 191 | * 192 | */ 193 | function get_frame_from_image_data($contents, $delay){ 194 | $frame = ''; 195 | // $delay_in_hex = str_pad(base_convert($delay, 10, 16), 4, '0', STR_PAD_LEFT); 196 | // $hex_bits = str_split($delay_in_hex,2); 197 | // $binary = chr($delay); echo "delay $delay is $delay_in_hex and should be ".bin2hex($binary); print_r($hex_bits);exit; 198 | 199 | if(strpos($contents,'GIF89a') === 0){ 200 | $first_13 = substr($contents,0,13); 201 | // find out teh size of our global colour table. 202 | $packed = base_convert(ord($first_13[10]),10,2); 203 | $global_color = substr($packed,5,3); 204 | $global_color_number = (int)base_convert($global_color,2,10); 205 | $global_color_count = pow(2, $global_color_number+1); 206 | $global_color_bytes = 3 * $global_color_count; 207 | // check if our gif has an extension already. 208 | $image_start = 13 + $global_color_bytes; 209 | if(bin2hex($contents[$image_start]) == '21'){ 210 | // our gif has an extension, find out how long it is. 211 | $size = (int)base_convert(bin2hex($contents[$image_start + 2]),16,10); 212 | $image_start = $image_start + $size + 4; 213 | } 214 | if(bin2hex($contents[$image_start]) == '2c') { 215 | // found the image desc:10 bytes 216 | $color_table = substr($contents, 13, $global_color_bytes); 217 | $image_descriptor = substr($contents, $image_start, 9); 218 | $new_image_descriptor = $image_descriptor . chr(base_convert('10000' . $global_color, 2, 10)); 219 | $data = substr($contents, $image_start + 10); 220 | $data = substr($data, 0, strlen($data) - 1); 221 | $frame = ''; 222 | $frame .= hex2bin('21'); 223 | $frame .= hex2bin('f9'); 224 | $frame .= hex2bin('04'); 225 | $frame .= hex2bin('04'); 226 | // $frame .= chr($delay); //delay? 227 | // $frame .= hex2bin('00'); 228 | $delay_in_hex = str_pad(base_convert($delay, 10, 16), 4, '0', STR_PAD_LEFT); 229 | $hex_bits = str_split($delay_in_hex,2); 230 | $frame .= hex2bin($hex_bits[1]); 231 | $frame .= hex2bin($hex_bits[0]); 232 | $frame .= hex2bin('1f'); 233 | $frame .= hex2bin('00'); 234 | $frame .= $new_image_descriptor; 235 | $frame .= $color_table; 236 | $frame .= $data; 237 | } 238 | } 239 | return $frame; 240 | } 241 | 242 | function flush_the_pipes(){ 243 | while (@ob_get_level()) @ob_end_flush(); 244 | @flush(); 245 | @ob_flush(); 246 | } 247 | 248 | // copied from stackoverflow somewhere. 249 | function prettyDate($time,$suffix=' ago'){ 250 | $now = time(); 251 | $ago = $now - $time; 252 | if($ago < 60){ 253 | $when = round($ago); 254 | $s = ($when == 1)?"second":"seconds"; 255 | return "$when $s".$suffix; 256 | }elseif($ago < 3600){ 257 | $when = round($ago / 60); 258 | $m = ($when == 1)?"minute":"minutes"; 259 | return "$when $m".$suffix; 260 | }elseif($ago >= 3600 && $ago < 86400){ 261 | $when = round($ago / 60 / 60); 262 | $h = ($when == 1)?"hour":"hours"; 263 | return "$when $h".$suffix; 264 | }elseif($ago >= 86400 && $ago < 2629743.83){ 265 | $when = round($ago / 60 / 60 / 24); 266 | $d = ($when == 1)?"day":"days"; 267 | return "$when $d".$suffix; 268 | }elseif($ago >= 2629743.83 && $ago < 31556926){ 269 | $when = round($ago / 60 / 60 / 24 / 30.4375); 270 | $m = ($when == 1)?"month":"months"; 271 | return "$when $m".$suffix; 272 | }else{ 273 | $when = round($ago / 60 / 60 / 24 / 365); 274 | $y = ($when == 1)?"year":"years"; 275 | return "$when $y".$suffix; 276 | } 277 | } 278 | 279 | // from: http://php.net/manual/en/function.imagecopymerge.php 280 | function imagecopymerge_alpha($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct){ 281 | // creating a cut resource 282 | $cut = imagecreatetruecolor($src_w, $src_h); 283 | 284 | // copying relevant section from background to the cut resource 285 | imagecopy($cut, $dst_im, 0, 0, $dst_x, $dst_y, $src_w, $src_h); 286 | 287 | // copying relevant section from watermark to the cut resource 288 | imagecopy($cut, $src_im, 0, 0, $src_x, $src_y, $src_w, $src_h); 289 | 290 | // insert cut resource to destination image 291 | imagecopymerge($dst_im, $cut, $dst_x, $dst_y, 0, 0, $src_w, $src_h, $pct); 292 | } 293 | function imagecopymerge_alpha_old($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct){ 294 | if(!isset($pct)){ 295 | return false; 296 | } 297 | $pct /= 100; 298 | // Get image width and height 299 | $w = imagesx( $src_im ); 300 | $h = imagesy( $src_im ); 301 | // Turn alpha blending off 302 | imagealphablending( $src_im, false ); 303 | // Find the most opaque pixel in the image (the one with the smallest alpha value) 304 | $minalpha = 127; 305 | for( $x = 0; $x < $w; $x++ ) 306 | for( $y = 0; $y < $h; $y++ ){ 307 | $alpha = ( imagecolorat( $src_im, $x, $y ) >> 24 ) & 0xFF; 308 | if( $alpha < $minalpha ){ 309 | $minalpha = $alpha; 310 | } 311 | } 312 | //loop through image pixels and modify alpha for each 313 | for( $x = 0; $x < $w; $x++ ){ 314 | for( $y = 0; $y < $h; $y++ ){ 315 | //get current alpha value (represents the TANSPARENCY!) 316 | $colorxy = imagecolorat( $src_im, $x, $y ); 317 | $alpha = ( $colorxy >> 24 ) & 0xFF; 318 | //calculate new alpha 319 | if( $minalpha !== 127 ){ 320 | $alpha = 127 + 127 * $pct * ( $alpha - 127 ) / ( 127 - $minalpha ); 321 | } else { 322 | $alpha += 127 * $pct; 323 | } 324 | //get the color index with new alpha 325 | $alphacolorxy = imagecolorallocatealpha( $src_im, ( $colorxy >> 16 ) & 0xFF, ( $colorxy >> 8 ) & 0xFF, $colorxy & 0xFF, $alpha ); 326 | //set pixel with the new color + opacity 327 | if( !imagesetpixel( $src_im, $x, $y, $alphacolorxy ) ){ 328 | return false; 329 | } 330 | } 331 | } 332 | // The image copy 333 | imagecopy($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h); 334 | } 335 | 336 | 337 | function filter_opacity( &$img, $opacity ) //params: image resource id, opacity in percentage (eg. 80) 338 | { 339 | $opacity /= 100; 340 | 341 | //get image width and height 342 | $w = imagesx( $img ); 343 | $h = imagesy( $img ); 344 | 345 | //turn alpha blending off 346 | imagealphablending( $img, false ); 347 | 348 | //find the most opaque pixel in the image (the one with the smallest alpha value) 349 | $minalpha = 127; 350 | for( $x = 0; $x < $w; $x++ ) 351 | for( $y = 0; $y < $h; $y++ ) 352 | { 353 | $alpha = ( imagecolorat( $img, $x, $y ) >> 24 ) & 0xFF; 354 | if( $alpha < $minalpha ) 355 | { $minalpha = $alpha; } 356 | } 357 | 358 | //loop through image pixels and modify alpha for each 359 | for( $x = 0; $x < $w; $x++ ) 360 | { 361 | for( $y = 0; $y < $h; $y++ ) 362 | { 363 | //get current alpha value (represents the TANSPARENCY!) 364 | $colorxy = imagecolorat( $img, $x, $y ); 365 | $alpha = ( $colorxy >> 24 ) & 0xFF; 366 | //calculate new alpha 367 | if( $minalpha !== 127 ) 368 | { $alpha = 127 + 127 * $opacity * ( $alpha - 127 ) / ( 127 - $minalpha ); } 369 | else 370 | { $alpha += 127 * $opacity; } 371 | //get the color index with new alpha 372 | $alphacolorxy = imagecolorallocatealpha( $img, ( $colorxy >> 16 ) & 0xFF, ( $colorxy >> 8 ) & 0xFF, $colorxy & 0xFF, $alpha ); 373 | //set pixel with the new color + opacity 374 | if( !imagesetpixel( $img, $x, $y, $alphacolorxy ) ) 375 | { return false; } 376 | } 377 | } 378 | return true; 379 | } 380 | 381 | function envato_get_item_details($item_id){ 382 | require_once 'class.envato-basic.php'; 383 | 384 | $item_id = (int)$item_id; 385 | if(!$item_id)return array(); 386 | 387 | $current_item = @json_decode(file_get_contents(_ENVATO_API_ITEM_CACHE_FILE),true); 388 | // when was the last time we got the statement? 389 | if(!$current_item || filemtime(_ENVATO_API_ITEM_CACHE_FILE) < (time() - _ENVATO_API_ITEM_CACHE_TIMEOUT)){ 390 | // grab the new statement from envato API 391 | // create a lock file so we don't do this at the same time. 392 | if(file_exists(_ENVATO_API_ITEM_CACHE_FILE.'.lock') && filemtime(_ENVATO_API_ITEM_CACHE_FILE.'.lock') < strtotime('-5 minutes') ){ 393 | // lock file failed. remove it and start over 394 | @unlink(_ENVATO_API_ITEM_CACHE_FILE.'.lock'); 395 | } 396 | if(!file_exists(_ENVATO_API_ITEM_CACHE_FILE.'.lock')) { 397 | touch(_ENVATO_API_ITEM_CACHE_FILE . '.lock'); 398 | $current_item = array(); 399 | $envato = new envato_api_basic(); 400 | $envato->set_personal_token(_ENVATO_PERSONAL_TOKEN); 401 | $data = $envato->api('v2/market/catalog/item?id='.$item_id); 402 | if (!empty($data) && isset($data['id']) && $data['id'] == $item_id && $data['author_username'] == _ENVATO_USERNAME) { 403 | file_put_contents(_ENVATO_API_ITEM_CACHE_FILE, json_encode($data)); 404 | $current_item = $data; 405 | } 406 | @unlink(_ENVATO_API_ITEM_CACHE_FILE.'.lock'); 407 | } 408 | } 409 | return $current_item; 410 | } 411 | function envato_get_statement(){ 412 | 413 | // now we start doing some calculations and build up additional gif frames to send to the browser. 414 | // these take long as we might have to do an API call or three 415 | require_once 'class.envato-basic.php'; 416 | 417 | $current_statement = @json_decode(file_get_contents(_ENVATO_STATEMENT_CACHE_FILE),true); 418 | // when was the last time we got the statement? 419 | if(!$current_statement || filemtime(_ENVATO_STATEMENT_CACHE_FILE) < (time() - _ENVATO_STATEMENT_CACHE_TIMEOUT)){ 420 | // grab the new statement from envato API 421 | // create a lock file so we don't do this at the same time. 422 | if(file_exists(_ENVATO_STATEMENT_CACHE_FILE.'.lock') && filemtime(_ENVATO_STATEMENT_CACHE_FILE.'.lock') < strtotime('-5 minutes') ){ 423 | // lock file failed. remove it and start over 424 | @unlink(_ENVATO_STATEMENT_CACHE_FILE.'.lock'); 425 | } 426 | if(!file_exists(_ENVATO_STATEMENT_CACHE_FILE.'.lock')) { 427 | touch(_ENVATO_STATEMENT_CACHE_FILE . '.lock'); 428 | $current_statement = array(); 429 | $envato = new envato_api_basic(); 430 | $envato->set_personal_token(_ENVATO_PERSONAL_TOKEN); 431 | $data = $envato->api('v1/market/private/user/statement.json'); 432 | if (!empty($data['statement'])) { 433 | file_put_contents(_ENVATO_STATEMENT_CACHE_FILE, json_encode($data['statement'])); 434 | $current_statement = $data['statement']; 435 | } 436 | @unlink(_ENVATO_STATEMENT_CACHE_FILE.'.lock'); 437 | } 438 | } 439 | return $current_statement; 440 | } 441 | 442 | function envato_get_item_ratings($item_id){ 443 | 444 | // now we start doing some calculations and build up additional gif frames to send to the browser. 445 | // these take long as we might have to do an API call or three 446 | require_once 'class.envato-basic.php'; 447 | 448 | $current_html = file_get_contents(_ENVATO_ITEM_CACHE_FILE); 449 | // when was the last time we got the statement? 450 | if(!$current_html || filemtime(_ENVATO_ITEM_CACHE_FILE) < (time() - _ENVATO_ITEM_CACHE_TIMEOUT)){ 451 | // grab the new statement from envato API 452 | // create a lock file so we don't do this at the same time. 453 | if(file_exists(_ENVATO_ITEM_CACHE_FILE.'.lock') && filemtime(_ENVATO_ITEM_CACHE_FILE.'.lock') < strtotime('-5 minutes') ){ 454 | // lock file failed. remove it and start over 455 | @unlink(_ENVATO_ITEM_CACHE_FILE.'.lock'); 456 | } 457 | if(!file_exists(_ENVATO_ITEM_CACHE_FILE.'.lock')) { 458 | touch(_ENVATO_ITEM_CACHE_FILE . '.lock'); 459 | $current_html = ''; 460 | $envato = new envato_api_basic(); 461 | $current_html = preg_replace('#\s+#',' ',$envato->get_url('http://themeforest.net/item/x/'.$item_id)); 462 | file_put_contents(_ENVATO_ITEM_CACHE_FILE, $current_html); 463 | @unlink(_ENVATO_ITEM_CACHE_FILE.'.lock'); 464 | } 465 | } 466 | $ratings = array(); 467 | // we are looking for this HTML 468 | /* 5 Star 469 |
470 |
471 |
472 | 80% 473 |
474 |
475 |
476 | 315 */ 477 | if(preg_match('#meta itemprop="ratingCount" content="(\d+)"#',$current_html,$matches)){ 478 | $ratings['total'] = $matches[1]; 479 | } 480 | if(preg_match_all('#class="rating-breakdown__key">(\d) Star.*class="rating-breakdown__count">(\d+)#imsU',$current_html,$matches)){ 481 | foreach($matches[1] as $key=>$val){ 482 | $ratings[$val] = $matches[2][$key]; 483 | } 484 | } 485 | return $ratings; 486 | } 487 | function envato_get_item_reviews($item_id){ 488 | 489 | // now we start doing some calculations and build up additional gif frames to send to the browser. 490 | // these take long as we might have to do an API call or three 491 | require_once 'class.envato-basic.php'; 492 | 493 | $current_html = file_get_contents(_ENVATO_ITEM_CACHE_FILE); 494 | // when was the last time we got the statement? 495 | if(!$current_html || filemtime(_ENVATO_ITEM_CACHE_FILE) < (time() - _ENVATO_ITEM_CACHE_TIMEOUT)){ 496 | // grab the new statement from envato API 497 | // create a lock file so we don't do this at the same time. 498 | if(file_exists(_ENVATO_ITEM_CACHE_FILE.'.lock') && filemtime(_ENVATO_ITEM_CACHE_FILE.'.lock') < strtotime('-5 minutes') ){ 499 | // lock file failed. remove it and start over 500 | @unlink(_ENVATO_ITEM_CACHE_FILE.'.lock'); 501 | } 502 | if(!file_exists(_ENVATO_ITEM_CACHE_FILE.'.lock')) { 503 | touch(_ENVATO_ITEM_CACHE_FILE . '.lock'); 504 | $current_html = ''; 505 | $envato = new envato_api_basic(); 506 | $current_html = preg_replace('#\s+#',' ',$envato->get_url('http://themeforest.net/item/x/'.$item_id)); 507 | file_put_contents(_ENVATO_ITEM_CACHE_FILE, $current_html); 508 | @unlink(_ENVATO_ITEM_CACHE_FILE.'.lock'); 509 | } 510 | } 511 | $ratings = array(); 512 | // we are looking for this HTML 513 | /* 5 Star 514 |
515 |
516 |
517 | 80% 518 |
519 |
520 |
521 | 315 */ 522 | if(preg_match('#meta itemprop="ratingCount" content="(\d+)"#',$current_html,$matches)){ 523 | $ratings['total'] = $matches[1]; 524 | } 525 | if(preg_match_all('#class="rating-breakdown__key">(\d) Star.*class="rating-breakdown__count">(\d+)#imsU',$current_html,$matches)){ 526 | foreach($matches[1] as $key=>$val){ 527 | $ratings[$val] = $matches[2][$key]; 528 | } 529 | } 530 | return $ratings; 531 | } 532 | 533 | function get_reviews(){ // todo: $datefrom=false,$type=1,$item_id=false 534 | 535 | if(!$this->logged_in)return array(); 536 | 537 | $reviews = array(); 538 | 539 | if(_DTBAKER_DEBUG_MODE){ 540 | echo 'grabbing reviews...'; 541 | } 542 | $page_number = 1; 543 | while(true){ 544 | $data = $this->get_url( $this->main_marketplace . "/reviews?page=".$page_number); 545 | if(!$data || strpos($data,'Page Not Found'))break; 546 | $data = preg_replace('#\s+#',' ',$data); 547 | if(preg_match_all('#id="review_\d+".*
(.*) on ]+>([^<]+).*]+>([^<]+).*
$match){ 549 | 550 | $this_review = array( 551 | 'rating_id' => $matches[5][$match_id], 552 | 'rating_url' => '/ratings/'.$matches[5][$match_id], 553 | 'buyer' => strip_tags($matches[1][$match_id]), 554 | 'stars' => '', 555 | 'review' => '', 556 | 'item_id' => $matches[3][$match_id], 557 | 'item_name' => $matches[4][$match_id], 558 | 'item_url' => $matches[2][$match_id].$matches[3][$match_id], 559 | 'date' => $matches[6][$match_id], 560 | 'date_estimate' => date('Y-m-d',strtotime('-'.str_replace(' ago','',$matches[6][$match_id]),time())), 561 | ); 562 | 563 | if(preg_match_all('#alt="Star-on" class="rating-basic#',$match,$stars)){ 564 | $this_review['stars'] = count($stars[0]); 565 | } 566 | if(preg_match('#

Extra comments from the buyer:
(.*)

#imsU',$match,$comments)){ 567 | $this_review['review'] = trim($comments[1]); 568 | } 569 | 570 | $reviews[] = $this_review; 571 | 572 | } 573 | 574 | }else{ 575 | echo 'no matches'; 576 | } 577 | $page_number++; 578 | } 579 | 580 | return $reviews; 581 | } 582 | 583 | 584 | if ( !function_exists( 'hex2bin' ) ) { 585 | function hex2bin( $str ) { 586 | $sbin = ""; 587 | $len = strlen( $str ); 588 | for ( $i = 0; $i < $len; $i += 2 ) { 589 | $sbin .= pack( "H*", substr( $str, $i, 2 ) ); 590 | } 591 | 592 | return $sbin; 593 | } 594 | } -------------------------------------------------------------------------------- /gif.php: -------------------------------------------------------------------------------- 1 | time() - _GIF_CACHE_TIMEOUT && !isset($_REQUEST['refresh'])){ 17 | header("Content-type: image/gif"); 18 | if(!@readfile($cache_gif_file)){ 19 | echo file_get_contents($cache_gif_file); 20 | } 21 | exit; 22 | } 23 | 24 | $cache_gif_content = ''; 25 | 26 | $current_statement = array(); 27 | 28 | // now we start doing the fun stuff: 29 | 30 | // first build up a quick "Loading" gif frame and send that to the browser as quickly as possible so it can render an empty image the correct size. 31 | global $blank_image_data; 32 | $blank_image_data = build_an_image(array( 33 | 'text' => '', 34 | 'icon' => false, 35 | )); 36 | 37 | 38 | if(!_DTBAKER_DEBUG_MODE){ 39 | header("Content-type: image/gif"); 40 | $header = get_animated_gif_header($blank_image_data); 41 | $cache_gif_content .= $header; 42 | echo $header; 43 | } // send special "I'm an animating gif" header 44 | $image_data_framed = add_frame_to_image_data($blank_image_data); 45 | if(!_DTBAKER_DEBUG_MODE){ 46 | $first_frame = get_frame_from_image_data($image_data_framed,2); 47 | $cache_gif_content .= $first_frame; 48 | echo $first_frame; 49 | } // show "Loading" really quicky 50 | flush_the_pipes(); 51 | 52 | 53 | 54 | // adding a really quick and dodgy "currently viewing" count to the stats: 55 | $viewer_ip = $_SERVER['REMOTE_ADDR']; // todo - look for x_forward etc.. 56 | $viewer_database = @json_decode(file_get_contents(_VIEWER_CACHE_FILE),true); 57 | if(!$viewer_database)$viewer_database = array(); 58 | $now = time(); 59 | $viewer_database[$viewer_ip] = $now; 60 | foreach($viewer_database as $ip=>$time){ 61 | if($time < $now - _VIEWER_CACHE_TIMEOUT){ 62 | unset($viewer_database[$ip]); 63 | } 64 | } 65 | file_put_contents(_VIEWER_CACHE_FILE,json_encode($viewer_database)); 66 | // don't want to show "1 person viewing" that sounds lame. 67 | $viewer_count = max(2,count($viewer_database)); 68 | $animate_image = animate_image_data(array( 69 | 'text' => $viewer_count.' people currently viewing', 70 | 'icon' => 'icon_eye.png', 71 | 'pause' => 200, 72 | )); 73 | $cache_gif_content .= $animate_image; 74 | echo $animate_image; 75 | $cache_gif_content .= animation_pause(); 76 | 77 | $first_sale = $last_sale = false; 78 | $sale_count = 0; 79 | 80 | $item_details = envato_get_item_details(_ENVATO_ITEM_ID); 81 | if($item_details) { 82 | $current_statement = envato_get_statement(); 83 | 84 | foreach ($current_statement as $item) { 85 | if (!empty($item['kind']) && $item['kind'] == 'sale' && $item['description'] == $item_details['name']) { 86 | if (_DTBAKER_DEBUG_MODE) { 87 | echo "Found a match."; 88 | print_r($item); 89 | } 90 | if (!$last_sale) $last_sale = strtotime($item['occured_at']); 91 | $first_sale = strtotime($item['occured_at']); 92 | $sale_count++; 93 | } 94 | } 95 | if ($last_sale) { 96 | $animate_image = animate_image_data(array( 97 | 'text' => 'Last purchased ' . prettyDate($last_sale, ' ago'), 98 | 'icon' => 'icon_cart.png', 99 | 'pause' => 200, 100 | )); 101 | $cache_gif_content .= $animate_image; 102 | echo $animate_image; 103 | $cache_gif_content .= animation_pause(); 104 | } 105 | 106 | 107 | if ($first_sale && $sale_count && $first_sale != $last_sale) { 108 | $animate_image = animate_image_data(array( 109 | 'text' => $sale_count . ' purchases in the last ' . prettyDate($first_sale, ''), 110 | 'icon' => 'icon_trending.png', 111 | 'pause' => 200, 112 | )); 113 | $cache_gif_content .= $animate_image; 114 | echo $animate_image; 115 | $cache_gif_content .= animation_pause(); 116 | } 117 | 118 | $ratings = envato_get_item_ratings(_ENVATO_ITEM_ID); 119 | if($ratings && !empty($ratings[5])){ 120 | $animate_image = animate_image_data(array( 121 | 'text' => $ratings[5].' five star ratings received', 122 | 'icon' => 'icon_star.png', 123 | 'pause' => 200, 124 | )); 125 | $cache_gif_content .= $animate_image; 126 | echo $animate_image; 127 | $cache_gif_content .= animation_pause(); 128 | } 129 | } 130 | 131 | 132 | echo ';';// end gif animation. commence loop. 133 | 134 | 135 | $cache_gif_content .= ';'; 136 | 137 | file_put_contents($cache_gif_file,$cache_gif_content); -------------------------------------------------------------------------------- /gif_5_star_ratings.php: -------------------------------------------------------------------------------- 1 | time() - _GIF_CACHE_TIMEOUT && !isset($_REQUEST['refresh'])){ 19 | header("Content-type: image/gif"); 20 | if(!@readfile($cache_gif_file)){ 21 | echo file_get_contents($cache_gif_file); 22 | } 23 | exit; 24 | } 25 | 26 | 27 | include('functions.php'); 28 | 29 | // now we start doing the fun stuff: 30 | 31 | // first build up a quick "Loading" gif frame and send that to the browser as quickly as possible so it can render an empty image the correct size. 32 | global $blank_image_data; 33 | $blank_image_data = build_an_image(array( 34 | 'text' => '', 35 | 'icon' => false, 36 | )); 37 | 38 | $cache_gif_content = ''; 39 | 40 | if(!_DTBAKER_DEBUG_MODE){ 41 | header("Content-type: image/gif"); 42 | $header = get_animated_gif_header($blank_image_data); 43 | $cache_gif_content .= $header; 44 | echo $header; 45 | } // send special "I'm an animating gif" header 46 | $image_data_framed = add_frame_to_image_data($blank_image_data); 47 | if(!_DTBAKER_DEBUG_MODE){ 48 | $first_frame = get_frame_from_image_data($image_data_framed,2); 49 | $cache_gif_content .= $first_frame; 50 | echo $first_frame; 51 | } // show "Loading" really quicky 52 | flush_the_pipes(); 53 | 54 | $item_details = envato_get_item_details(_ENVATO_ITEM_ID); 55 | if($item_details) { 56 | $ratings = envato_get_item_ratings(_ENVATO_ITEM_ID); 57 | if ($ratings && !empty($ratings[5])) { 58 | $animate_image = animate_image_data(array( 59 | 'text' => $ratings[5] . ' five star ratings received', 60 | 'icon' => 'icon_star.png', 61 | 'pause' => 2000, 62 | 'type' => 'fade_in', 63 | )); 64 | $cache_gif_content .= $animate_image; 65 | echo $animate_image; 66 | } 67 | flush_the_pipes(); 68 | } 69 | 70 | echo ';';// end gif animation. commence loop. 71 | $cache_gif_content .= ';'; 72 | 73 | file_put_contents($cache_gif_file,$cache_gif_content); -------------------------------------------------------------------------------- /gif_currently_viewing.php: -------------------------------------------------------------------------------- 1 | time() - _GIF_CACHE_TIMEOUT && !isset($_REQUEST['refresh'])){ 19 | header("Content-type: image/gif"); 20 | if(!@readfile($cache_gif_file)){ 21 | echo file_get_contents($cache_gif_file); 22 | } 23 | exit; 24 | } 25 | 26 | 27 | include('functions.php'); 28 | 29 | // now we start doing the fun stuff: 30 | 31 | // first build up a quick "Loading" gif frame and send that to the browser as quickly as possible so it can render an empty image the correct size. 32 | global $blank_image_data; 33 | $blank_image_data = build_an_image(array( 34 | 'text' => '', 35 | 'icon' => false, 36 | )); 37 | 38 | $cache_gif_content = ''; 39 | 40 | if(!_DTBAKER_DEBUG_MODE){ 41 | header("Content-type: image/gif"); 42 | $header = get_animated_gif_header($blank_image_data); 43 | $cache_gif_content .= $header; 44 | echo $header; 45 | } // send special "I'm an animating gif" header 46 | $image_data_framed = add_frame_to_image_data($blank_image_data); 47 | if(!_DTBAKER_DEBUG_MODE){ 48 | $first_frame = get_frame_from_image_data($image_data_framed,2); 49 | $cache_gif_content .= $first_frame; 50 | echo $first_frame; 51 | flush_the_pipes(); 52 | } // show "Loading" really quicky 53 | 54 | 55 | 56 | // adding a really quick and dodgy "currently viewing" count to the stats: 57 | $viewer_ip = $_SERVER['REMOTE_ADDR']; // todo - look for x_forward etc.. 58 | $viewer_database = @json_decode(file_get_contents(_VIEWER_CACHE_FILE),true); 59 | if(!$viewer_database)$viewer_database = array(); 60 | $now = time(); 61 | $viewer_database[$viewer_ip] = $now; 62 | foreach($viewer_database as $ip=>$time){ 63 | if($time < $now - _VIEWER_CACHE_TIMEOUT){ 64 | unset($viewer_database[$ip]); 65 | } 66 | } 67 | file_put_contents(_VIEWER_CACHE_FILE,json_encode($viewer_database)); 68 | // don't want to show "1 person viewing" that sounds lame. 69 | $viewer_count = max(2,count($viewer_database)); 70 | $animate_image = animate_image_data(array( 71 | 'text' => $viewer_count.' people currently viewing', 72 | 'icon' => 'icon_eye.png', 73 | 'pause' => 2000, 74 | 'type' => 'fade_in', 75 | )); 76 | $cache_gif_content .= $animate_image; 77 | echo $animate_image; 78 | 79 | 80 | flush_the_pipes(); 81 | 82 | echo ';';// end gif animation. commence loop. 83 | $cache_gif_content .= ';'; 84 | 85 | file_put_contents($cache_gif_file,$cache_gif_content); -------------------------------------------------------------------------------- /gif_last_purchased.php: -------------------------------------------------------------------------------- 1 | time() - _GIF_CACHE_TIMEOUT && !isset($_REQUEST['refresh'])){ 19 | header("Content-type: image/gif"); 20 | if(!@readfile($cache_gif_file)){ 21 | echo file_get_contents($cache_gif_file); 22 | } 23 | exit; 24 | } 25 | 26 | 27 | include('functions.php'); 28 | 29 | // now we start doing the fun stuff: 30 | 31 | // first build up a quick "Loading" gif frame and send that to the browser as quickly as possible so it can render an empty image the correct size. 32 | global $blank_image_data; 33 | $blank_image_data = build_an_image(array( 34 | 'text' => '', 35 | 'icon' => false, 36 | )); 37 | 38 | $cache_gif_content = ''; 39 | 40 | if(!_DTBAKER_DEBUG_MODE){ 41 | header("Content-type: image/gif"); 42 | $header = get_animated_gif_header($blank_image_data); 43 | $cache_gif_content .= $header; 44 | echo $header; 45 | } // send special "I'm an animating gif" header 46 | $image_data_framed = add_frame_to_image_data($blank_image_data); 47 | if(!_DTBAKER_DEBUG_MODE){ 48 | $first_frame = get_frame_from_image_data($image_data_framed,2); 49 | $cache_gif_content .= $first_frame; 50 | echo $first_frame; 51 | } // show "Loading" really quicky 52 | flush_the_pipes(); 53 | 54 | 55 | $first_sale = $last_sale = false; 56 | $sale_count = 0; 57 | 58 | $item_details = envato_get_item_details(_ENVATO_ITEM_ID); 59 | if($item_details) { 60 | $current_statement = envato_get_statement(); 61 | 62 | foreach ($current_statement as $item) { 63 | if (!empty($item['kind']) && $item['kind'] == 'sale' && strpos($item['description'], $item_details['name']) !== false && strpos($item['description'], "included support") === false) { 64 | if (_DTBAKER_DEBUG_MODE) { 65 | echo "Found a match."; 66 | print_r($item); 67 | } 68 | if (!$last_sale) $last_sale = strtotime($item['occured_at']); 69 | $first_sale = strtotime($item['occured_at']); 70 | $sale_count++; 71 | } 72 | } 73 | if ($last_sale) { 74 | $animate_image = animate_image_data(array( 75 | 'text' => 'Last purchased ' . prettyDate($last_sale, ' ago'), 76 | 'icon' => 'icon_cart.png', 77 | 'pause' => 2000, 78 | 'type' => 'fade_in', 79 | )); 80 | $cache_gif_content .= $animate_image; 81 | echo $animate_image; 82 | } 83 | flush_the_pipes(); 84 | } 85 | 86 | echo ';';// end gif animation. commence loop. 87 | $cache_gif_content .= ';'; 88 | 89 | file_put_contents($cache_gif_file,$cache_gif_content); -------------------------------------------------------------------------------- /gif_recent_sales.php: -------------------------------------------------------------------------------- 1 | time() - _GIF_CACHE_TIMEOUT && !isset($_REQUEST['refresh'])){ 19 | header("Content-type: image/gif"); 20 | if(!@readfile($cache_gif_file)){ 21 | echo file_get_contents($cache_gif_file); 22 | } 23 | exit; 24 | } 25 | 26 | 27 | include('functions.php'); 28 | 29 | // now we start doing the fun stuff: 30 | 31 | // first build up a quick "Loading" gif frame and send that to the browser as quickly as possible so it can render an empty image the correct size. 32 | global $blank_image_data; 33 | $blank_image_data = build_an_image(array( 34 | 'text' => '', 35 | 'icon' => false, 36 | )); 37 | 38 | $cache_gif_content = ''; 39 | 40 | if(!_DTBAKER_DEBUG_MODE){ 41 | header("Content-type: image/gif"); 42 | $header = get_animated_gif_header($blank_image_data); 43 | $cache_gif_content .= $header; 44 | echo $header; 45 | } // send special "I'm an animating gif" header 46 | $image_data_framed = add_frame_to_image_data($blank_image_data); 47 | if(!_DTBAKER_DEBUG_MODE){ 48 | $first_frame = get_frame_from_image_data($image_data_framed,2); 49 | $cache_gif_content .= $first_frame; 50 | echo $first_frame; 51 | } // show "Loading" really quicky 52 | flush_the_pipes(); 53 | 54 | 55 | $first_sale = $last_sale = false; 56 | $sale_count = 0; 57 | 58 | $item_details = envato_get_item_details(_ENVATO_ITEM_ID); 59 | if($item_details) { 60 | $current_statement = envato_get_statement(); 61 | 62 | 63 | foreach ($current_statement as $item) { 64 | if (!empty($item['kind']) && $item['kind'] == 'sale' && strpos($item['description'], $item_details['name']) !== false && strpos($item['description'], "included support") === false) { 65 | if (_DTBAKER_DEBUG_MODE) { 66 | echo "Found a match."; 67 | print_r($item); 68 | } 69 | if (!$last_sale) $last_sale = strtotime($item['occured_at']); 70 | $first_sale = strtotime($item['occured_at']); 71 | $sale_count++; 72 | } 73 | } 74 | 75 | if ($first_sale && $sale_count && $first_sale != $last_sale) { 76 | $animate_image = animate_image_data(array( 77 | 'text' => $sale_count . ' purchases in the last ' . prettyDate($first_sale, ''), 78 | 'icon' => 'icon_trending.png', 79 | 'pause' => 2000, 80 | 'type' => 'fade_in', 81 | )); 82 | $cache_gif_content .= $animate_image; 83 | echo $animate_image; 84 | } 85 | flush_the_pipes(); 86 | } 87 | 88 | echo ';';// end gif animation. commence loop. 89 | $cache_gif_content .= ';'; 90 | 91 | file_put_contents($cache_gif_file,$cache_gif_content); -------------------------------------------------------------------------------- /icon_cart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtbaker/envato-live-sales-gif/437519643fcd86f91d54f3d0d1f83950524fbf34/icon_cart.png -------------------------------------------------------------------------------- /icon_eye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtbaker/envato-live-sales-gif/437519643fcd86f91d54f3d0d1f83950524fbf34/icon_eye.png -------------------------------------------------------------------------------- /icon_star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtbaker/envato-live-sales-gif/437519643fcd86f91d54f3d0d1f83950524fbf34/icon_star.png -------------------------------------------------------------------------------- /icon_trending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtbaker/envato-live-sales-gif/437519643fcd86f91d54f3d0d1f83950524fbf34/icon_trending.png -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtbaker/envato-live-sales-gif/437519643fcd86f91d54f3d0d1f83950524fbf34/template.png -------------------------------------------------------------------------------- /template.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtbaker/envato-live-sales-gif/437519643fcd86f91d54f3d0d1f83950524fbf34/template.xcf -------------------------------------------------------------------------------- /template_305.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtbaker/envato-live-sales-gif/437519643fcd86f91d54f3d0d1f83950524fbf34/template_305.xcf -------------------------------------------------------------------------------- /template_305_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtbaker/envato-live-sales-gif/437519643fcd86f91d54f3d0d1f83950524fbf34/template_305_bg.png -------------------------------------------------------------------------------- /template_305_frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtbaker/envato-live-sales-gif/437519643fcd86f91d54f3d0d1f83950524fbf34/template_305_frame.png -------------------------------------------------------------------------------- /template_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtbaker/envato-live-sales-gif/437519643fcd86f91d54f3d0d1f83950524fbf34/template_bg.png -------------------------------------------------------------------------------- /template_blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtbaker/envato-live-sales-gif/437519643fcd86f91d54f3d0d1f83950524fbf34/template_blank.png -------------------------------------------------------------------------------- /template_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtbaker/envato-live-sales-gif/437519643fcd86f91d54f3d0d1f83950524fbf34/template_mask.png -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test_mjpeg_stream.php: -------------------------------------------------------------------------------- 1 | array( 29 | 'icon' => 'icon_cart.png', 30 | 'text' => 'Last purchased x ago', 31 | ), 32 | ); 33 | 34 | 35 | $current_statement = @json_decode(file_get_contents($statement_cache),true); 36 | 37 | // base our generic stats off the cached statement then query the api for new data and then generate the updated stats. 38 | 39 | 40 | // when was the last time we got the statement? 41 | if(!$current_statement || filemtime($statement_cache) < (time() - $statement_cache_timeout)){ 42 | // grab the new statement from envato API 43 | $current_statement = array(); 44 | $envato = new envato_api_basic(); 45 | $envato->set_personal_token('pemOG07IWPuvb8GCSum9lwvCVpkgHwj9'); 46 | $data = $envato->api('v1/market/private/user/statement.json'); 47 | if(!empty($data['statement'])){ 48 | file_put_contents($statement_cache,json_encode($data['statement'])); 49 | touch($statement_cache); 50 | if($debug)echo "GETTING NEW STATEMENT\n\n"; 51 | $current_statement = $data['statement']; 52 | } 53 | } 54 | 55 | 56 | // Used to separate multipart 57 | $boundary = "envatosalesgraphic"; 58 | // We start with the standard headers. PHP allows us this much 59 | if(!$debug){ 60 | header("Cache-Control: no-cache"); 61 | header("Cache-Control: private"); 62 | header("Pragma: no-cache"); 63 | header("Content-type: multipart/x-mixed-replace; boundary=$boundary"); 64 | } 65 | 66 | // send a blank image to start with: 67 | for ($y = 1; $y < 3; $y++) { 68 | $im = imagecreatefrompng($base_image_blank); 69 | imagetruecolortopalette($im, true, 256); 70 | $white = imagecolorallocate($im, 255, 255, 255); 71 | imagecolortransparent($im, $white); 72 | ob_start(); 73 | imagejpeg($im, null, 100); 74 | $last_image = ob_get_clean(); 75 | if(!$debug)echo $last_image; 76 | imagedestroy($im); 77 | echo "--$boundary\n"; 78 | // Per-image header, note the two new-lines 79 | echo "Content-type: image/jpeg\n\n"; 80 | while (@ob_get_level()) @ob_end_flush(); 81 | @flush(); 82 | @ob_flush(); 83 | $last_sent = time(); 84 | } 85 | function prettyDate($time){ 86 | $now = time(); 87 | $ago = $now - $time; 88 | if($ago < 60){ 89 | $when = round($ago); 90 | $s = ($when == 1)?"second":"seconds"; 91 | return "$when $s ago"; 92 | }elseif($ago < 3600){ 93 | $when = round($ago / 60); 94 | $m = ($when == 1)?"minute":"minutes"; 95 | return "$when $m ago"; 96 | }elseif($ago >= 3600 && $ago < 86400){ 97 | $when = round($ago / 60 / 60); 98 | $h = ($when == 1)?"hour":"hours"; 99 | return "$when $h ago"; 100 | }elseif($ago >= 86400 && $ago < 2629743.83){ 101 | $when = round($ago / 60 / 60 / 24); 102 | $d = ($when == 1)?"day":"days"; 103 | return "$when $d ago"; 104 | }elseif($ago >= 2629743.83 && $ago < 31556926){ 105 | $when = round($ago / 60 / 60 / 24 / 30.4375); 106 | $m = ($when == 1)?"month":"months"; 107 | return "$when $m ago"; 108 | }else{ 109 | $when = round($ago / 60 / 60 / 24 / 365); 110 | $y = ($when == 1)?"year":"years"; 111 | return "$when $y ago"; 112 | } 113 | } 114 | 115 | $displayed_sale = false; 116 | $last_sale = false; 117 | while(true) { 118 | $current_statement = @json_decode(file_get_contents($statement_cache),true); 119 | // when was the last time we got the statement? 120 | if(!$current_statement || filemtime($statement_cache) < (time() - $statement_cache_timeout)){ 121 | // grab the new statement from envato API 122 | $current_statement = array(); 123 | $envato = new envato_api_basic(); 124 | $envato->set_personal_token('pemOG07IWPuvb8GCSum9lwvCVpkgHwj9'); 125 | $data = $envato->api('v1/market/private/user/statement.json'); 126 | if(!empty($data['statement'])){ 127 | file_put_contents($statement_cache,json_encode($data['statement'])); 128 | touch($statement_cache); 129 | if($debug)echo "GETTING NEW STATEMENT\n\n"; 130 | $current_statement = $data['statement']; 131 | } 132 | } 133 | foreach($current_statement as $item){ 134 | if($debug){ 135 | echo "Checking if ".$item['description']." matches '".$file_name."'\n"; 136 | } 137 | if(!empty($item['kind']) && $item['kind'] == 'sale' && $item['description'] == $file_name){ 138 | $last_sale = strtotime($item['occured_at']); 139 | break; 140 | } 141 | } 142 | if($last_sent < time() - $timeout){ 143 | // send last image again. 144 | if(!$debug)echo $last_image; 145 | echo "--$boundary\n"; 146 | // Per-image header, note the two new-lines 147 | echo "Content-type: image/jpeg\n\n"; 148 | while (@ob_get_level()) @ob_end_flush(); 149 | @flush(); 150 | @ob_flush(); 151 | $last_sent = time(); 152 | } 153 | if($last_sale && $last_sale != $displayed_sale){ 154 | $displayed_sale = $last_sale; 155 | for ($y = 1; $y < 3; $y++) { 156 | $im = imagecreatefrompng($base_image); 157 | imagetruecolortopalette($im, true, 256); 158 | $white = imagecolorallocate($im, 255, 255, 255); 159 | imagecolortransparent($im, $white); 160 | $font_color = imagecolorallocate($im, 102, 102, 102); 161 | if($debug)echo "Last purchased ".prettyDate($last_sale)."\n\n"; 162 | imagettftext($im, 12, 0, 80, 43, $font_color, $font_file, "Last purchased ".prettyDate($last_sale)); 163 | ob_start(); 164 | imagejpeg($im, null, 100); 165 | $last_image = ob_get_clean(); 166 | if(!$debug)echo $last_image; 167 | imagedestroy($im); 168 | 169 | echo "--$boundary\n"; 170 | // Per-image header, note the two new-lines 171 | echo "Content-type: image/jpeg\n\n"; 172 | while (@ob_get_level()) @ob_end_flush(); 173 | @flush(); 174 | @ob_flush(); 175 | $last_sent = time(); 176 | } 177 | } 178 | sleep(1); 179 | if($debug)echo 'Loop \n'; 180 | // break; 181 | } 182 | -------------------------------------------------------------------------------- /test_text.txt: -------------------------------------------------------------------------------- 1 | Last purchased 5 minutes ago --------------------------------------------------------------------------------