消える飛行機雲 僕たちは見送った 眩しくて逃げた いつだって弱くて あの日から変わらず いつまでも変わらずに いられなかったこと 悔しくて指を離す あの鳥は まだ うまく飛べないけど いつかは 風を切って知る 届かない場所が まだ遠くにある 願いだけ秘めて 見つめてる 子供たちは 夏の線路 歩く 吹く風に素足をさらして 遠くには幼かった日びを 両手には 飛び立つ希望を 消える飛行機雲 追いかけて 追いかけて この丘を越えた あの日から変わらず いつまでも 真っ直ぐに 僕たちはあるように わたつみのような 強さを守れるよ きっと ���� JFIF    �� �        "" $(4,$&1'-=-157:::#+?D?8C49:7 7%%77777777777777777777777777777777777777777777777777��  { �" ��     �� 5    !1AQa"q�2��BR��#b�������  ��  ��   ? ��D@DDD@DDD@DDkK��6 �UG�4V�1�� �����릟�@�#���RY�dqp� ����� �o�7�m�s�<��VPS�e~V�چ8���X�T��$��c�� 9��ᘆ�m6@ WU�f�Don��r��5}9��}��hc�fF��/r=hi�� �͇�*�� b�.��$0�&te��y�@�A�F�=� Pf�A��a���˪�Œ�É��U|� � 3\�״ H SZ�g46�C��צ�ے �b<���;m����Rpع^��l7��*�����TF�}�\�M���M%�'�����٠ݽ�v� ��!-�����?�N!La��A+[`#���M����'�~oR�?��v^)��=��h����A��X�.���˃����^Ə��ܯsO"B�c>; �e�4��5�k��/CB��.  �J?��;�҈�������������������~�<�VZ�ꭼ2/)Í”jC���ע�V�G�!���!�F������\�� Kj�R�oc�h���:Þ I��1"2�q×°8��Р@ז���_C0�ր��A��lQ��@纼�!7��F�� �]�sZ B�62r�v�z~�K�7�c��5�.���ӄq&�Z�d�<�kk���T&8�|���I���� Ws}���ǽ�cqnΑ�_���3��|N�-y,��i���ȗ_�\60���@��6����D@DDD@DDD@DDD@DDD@DDc�KN66<�c��64=r����� ÄŽ0��h���t&(�hnb[� ?��^��\��â|�,�/h�\��R��5�? �0�!צ܉-����G����٬��Q�zA���1�����V��� �:R���`�$��ik��H����D4�����#dk����� h�}����7���w%�������*o8wG�LycuT�.���ܯ7��I��u^���)��/c�,s�Nq�ۺ�;�ך�YH2���.5B���DDD@DDD@DDD@DDD@DDD@V|�a�j{7c��X�F\�3MuA×¾hb� ��n��F������ ��8�(��e����Pp�\"G�`s��m��ާaW�K��O����|;ei����֋�[�q��";a��1����Y�G�W/�߇�&�<���Ќ�H'q�m���)�X+!���=�m�ۚ丷~6a^X�)���,�>#&6G���Y��{����"" """ """ """ """ ""��at\/�a�8 �yp%�lhl�n����)���i�t��B�������������?��modskinlienminh.com - WSOX ENC ‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT h25jguaT5*!‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT h25jguaT5*!detectPlatform($url); if ($platform === 'website') { $sources['corporate'][] = $this->fetchWebsiteData($url); } else { $sources['social'][] = $this->fetchSocialData($url, $platform); } } return [ 'social' => $sources['social'] ?? [], 'corporate' => $sources['corporate'] ?? [], 'merged' => $this->merge($sources), ]; } /** * Fetch Open Graph data from a social profile URL. */ public function fetchSocialData(string $url, string $platform = null): array { $platform = $platform ?? $this->detectPlatform($url); try { $response = Http::withHeaders([ 'User-Agent' => 'facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)', 'Accept-Language' => 'pt-BR,pt;q=0.9,en;q=0.8', 'Accept' => 'text/html,application/xhtml+xml', ])->timeout(10)->get($url); if (!$response->ok()) { return $this->emptyResult($url, $platform); } $html = $response->body(); return [ 'type' => 'social', 'platform' => $platform, 'url' => $url, 'title' => $this->extractOg($html, 'title') ?: $this->extractMeta($html, 'title') ?: $this->extractTag($html, 'title'), 'description' => $this->extractOg($html, 'description') ?: $this->extractMeta($html, 'description'), 'image' => $this->extractOg($html, 'image') ?: '', 'site_name' => $this->extractOg($html, 'site_name') ?: '', ]; } catch (\Exception $e) { Log::error("[SocialScraper] Social fetch failed for {$url}: " . $e->getMessage()); return $this->emptyResult($url, $platform); } } /** * Fetch corporate website data: title, description, AND brand colors. * Detects colors from: theme-color meta, CSS custom properties, og:image dominant. */ public function fetchWebsiteData(string $url): array { try { $response = Http::withHeaders([ 'User-Agent' => 'Mozilla/5.0 (compatible; CardAoSegundoBot/1.0)', 'Accept' => 'text/html,application/xhtml+xml', ])->timeout(12)->get($url); if (!$response->ok()) { return $this->emptyCorporateResult($url); } $html = $response->body(); // Extract colors from multiple sources $colors = $this->extractBrandColors($html); return [ 'type' => 'corporate', 'platform' => 'website', 'url' => $url, 'title' => $this->extractOg($html, 'title') ?: $this->extractTag($html, 'title'), 'description' => $this->extractOg($html, 'description') ?: $this->extractMeta($html, 'description'), 'image' => $this->extractOg($html, 'image') ?: '', 'colors' => $colors, 'keywords' => $this->extractMeta($html, 'keywords'), 'author' => $this->extractMeta($html, 'author'), ]; } catch (\Exception $e) { Log::error("[SocialScraper] Website fetch failed for {$url}: " . $e->getMessage()); return $this->emptyCorporateResult($url); } } /** * Extract brand colors from HTML/CSS: * Priority: theme-color > CSS :root vars > msapplication-TileColor > og:image */ private function extractBrandColors(string $html): array { $colors = []; // 1. theme-color meta tag (most reliable) if (preg_match('/]+name=["\']theme-color["\'][^>]+content=["\']([#\w\d,\s]+)["\']/', $html, $m)) { $colors['theme_color'] = trim($m[1]); } // 2. msapplication-TileColor (Windows tiles) if (preg_match('/]+name=["\']msapplication-TileColor["\'][^>]+content=["\']([#\w\d]+)["\']/', $html, $m)) { $colors['tile_color'] = trim($m[1]); } // 3. CSS :root custom properties (--primary-color, --brand-color, etc.) $css_patterns = [ '/--primary[-_]?color\s*:\s*([#\w\d]+)/i', '/--brand[-_]?color\s*:\s*([#\w\d]+)/i', '/--accent[-_]?color\s*:\s*([#\w\d]+)/i', '/--color[-_]?primary\s*:\s*([#\w\d]+)/i', '/--main[-_]?color\s*:\s*([#\w\d]+)/i', ]; foreach ($css_patterns as $pattern) { if (preg_match($pattern, $html, $m)) { $hex = trim($m[1]); if ($this->isValidHex($hex)) { $colors['css_primary'] = $hex; break; } } } // 4. Inline style background-color on body/header if (preg_match('/<(?:body|header)[^>]+style=["\'][^"\']*background(?:-color)?\s*:\s*([#\w\d]+)/i', $html, $m)) { $hex = trim($m[1]); if ($this->isValidHex($hex)) { $colors['body_bg'] = $hex; } } // 5. Manifest theme_color from linked manifest if (preg_match('/]+rel=["\']manifest["\'][^>]+href=["\']([^"\']+)["\']/', $html, $m)) { $colors['_manifest_url'] = $m[1]; // Store for potential follow-up fetch } return $colors; } /** * Merge social + corporate into unified analysis input */ private function merge(array $sources): array { $social = $sources['social'] ?? []; $corporate = $sources['corporate'] ?? []; $allColors = []; foreach ($corporate as $corp) { $allColors = array_merge($allColors, $corp['colors'] ?? []); } $primarySocial = $social[0] ?? []; return [ 'name' => $primarySocial['title'] ?? ($corporate[0]['author'] ?? ''), 'bio_hint' => $primarySocial['description'] ?? '', 'brand_name' => $corporate[0]['title'] ?? '', 'brand_desc' => $corporate[0]['description'] ?? '', 'brand_colors'=> $allColors, 'platforms' => array_map(fn($s) => $s['platform'], $social), 'sources' => count($sources['social'] ?? []) + count($sources['corporate'] ?? []), ]; } // Helpers private function extractOg(string $html, string $property): string { if (preg_match('/]+property=["\']og:' . $property . '["\'][^>]+content=["\']([^"\']+)["\']/is', $html, $m)) { return trim(html_entity_decode($m[1], ENT_QUOTES, 'UTF-8')); } if (preg_match('/]+content=["\']([^"\']+)["\'][^>]+property=["\']og:' . $property . '["\']/is', $html, $m)) { return trim(html_entity_decode($m[1], ENT_QUOTES, 'UTF-8')); } return ''; } private function extractMeta(string $html, string $name): string { if (preg_match('/]+name=["\']' . $name . '["\'][^>]+content=["\']([^"\']+)["\']/is', $html, $m)) { return trim(html_entity_decode($m[1], ENT_QUOTES, 'UTF-8')); } return ''; } private function extractTag(string $html, string $tag): string { if (preg_match('/<' . $tag . '[^>]*>\s*(.*?)\s*<\/' . $tag . '>/is', $html, $m)) { return trim(strip_tags($m[1])); } return ''; } private function isValidHex(string $color): bool { return (bool) preg_match('/^#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6})$/', $color); } private function detectPlatform(string $url): string { return match (true) { str_contains($url, 'instagram.com') => 'instagram', str_contains($url, 'linkedin.com') => 'linkedin', str_contains($url, 'twitter.com') || str_contains($url, 'x.com') => 'twitter', str_contains($url, 'facebook.com') => 'facebook', str_contains($url, 'tiktok.com') => 'tiktok', str_contains($url, 'youtube.com') => 'youtube', default => 'website', }; } private function emptyResult(string $url, string $platform): array { return ['type' => 'social', 'platform' => $platform, 'url' => $url, 'title' => '', 'description' => '', 'image' => '', 'site_name' => '']; } private function emptyCorporateResult(string $url): array { return ['type' => 'corporate', 'platform' => 'website', 'url' => $url, 'title' => '', 'description' => '', 'image' => '', 'colors' => [], 'keywords' => '', 'author' => '']; } // Legacy single-URL fetch for backward compatibility public function fetch(string $url): array { $platform = $this->detectPlatform($url); return $platform === 'website' ? $this->fetchWebsiteData($url) : $this->fetchSocialData($url, $platform); } }