mirror of
https://gitlab.com/octospacc/Snippets.git
synced 2025-04-16 00:07:22 +02:00
Auto-Backup $'Tue Oct 15 2024 01:18:33 GMT+0200 (Central European Summer Time)'
This commit is contained in:
parent
d22046e730
commit
f85fdd6c17
550
HyperTextMetaLister.php
Normal file
550
HyperTextMetaLister.php
Normal file
@ -0,0 +1,550 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<?php
|
||||||
|
$APP_NAME = 'HyperTextMetaLister';
|
||||||
|
$APP_PATH = "./{$APP_NAME}.php";
|
||||||
|
?>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title><?php echo $APP_NAME; ?></title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
background-color: white;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 10px;
|
||||||
|
width: auto;
|
||||||
|
min-width: 400px;
|
||||||
|
/* max-width: 100%; */
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
input[type="url"] {
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
input[type="submit"] {
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
input[type="submit"]:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
table-layout: fixed;
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
table, th, td {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 10px;
|
||||||
|
text-align: left;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure proper box-sizing for all elements */
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Container styling for overall layout */
|
||||||
|
.container {
|
||||||
|
/* max-width: 1200px; */ /* Limit max width for larger screens */
|
||||||
|
margin: 0 auto; /* Center the container */
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Meta table styling */
|
||||||
|
.meta-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 20px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-table th, .meta-table td {
|
||||||
|
padding: 12px 15px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-table th {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Title styling with hover effect for description */
|
||||||
|
.meta-title {
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-title > .title {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-title:hover::after {
|
||||||
|
content: attr(title);
|
||||||
|
position: absolute;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 4px;
|
||||||
|
left: 0;
|
||||||
|
top: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 1;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styles for both meta and link tables */
|
||||||
|
.meta-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 20px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-table th, .meta-table td {
|
||||||
|
padding: 12px 15px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-table th {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-table a {
|
||||||
|
color: #1a0dab;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-table a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive design for larger screens */
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-cards {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr; /*repeat(auto-fit, minmax(300px, 1fr));*/
|
||||||
|
gap: 20px;
|
||||||
|
max-width: 100vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive design for smaller screens */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-cards {
|
||||||
|
grid-template-columns: 1fr; /* Stacks cards vertically on small screens */
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-card {
|
||||||
|
flex-direction: column; /* Align content vertically on smaller screens */
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-card img, .preview-card video {
|
||||||
|
width: 100%;
|
||||||
|
height: auto; /* Allow media to scale with the card */
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Preview card style */
|
||||||
|
.preview-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: #fafafa;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-card img, .preview-card video {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
margin-right: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content area of the card */
|
||||||
|
.preview-card-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.platform {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-description {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #555;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover effect for cards */
|
||||||
|
.preview-card:hover {
|
||||||
|
transform: scale(1.03);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h2><a style="color: initial; text-decoration: none;" href="<?php echo $APP_PATH; ?>"><?php echo $APP_NAME; ?></a></h2>
|
||||||
|
|
||||||
|
<form method="GET">
|
||||||
|
<input type="url" name="url" placeholder="Enter URL" required="required" value="<?php echo $_GET['url']; ?>">
|
||||||
|
<input type="submit" value="Get Meta Details">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
function fetch_page($url, $maxRedirects = 5) {
|
||||||
|
$currentUrl = $url;
|
||||||
|
$redirectCount = 0;
|
||||||
|
|
||||||
|
while ($redirectCount < $maxRedirects) {
|
||||||
|
// Initialize cURL to fetch the content
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $currentUrl);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); // Disable auto-following redirects by cURL
|
||||||
|
curl_setopt($ch, CURLOPT_HEADER, true); // Include headers in the response
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30); // Timeout after 30 seconds
|
||||||
|
curl_setopt($ch, CURLOPT_MAXREDIRS, $maxRedirects); // Set a limit for redirects
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$error = curl_error($ch);
|
||||||
|
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
||||||
|
$headers = substr($response, 0, $headerSize);
|
||||||
|
$body = substr($response, $headerSize);
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
|
||||||
|
// Ensure that non-ASCII characters are correctly interpreted as UTF-8
|
||||||
|
$body = mb_convert_encoding($body, 'HTML-ENTITIES', 'UTF-8');
|
||||||
|
|
||||||
|
// Get the final URL after HTTP redirection
|
||||||
|
$effectiveUrl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
|
||||||
|
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
// Check for HTTP redirect (status codes 3xx)
|
||||||
|
if (preg_match('/^HTTP\/\d\.\d\s+3\d{2}/', $headers)) {
|
||||||
|
// Parse 'Location' header for the new URL
|
||||||
|
if (preg_match('/Location:\s*(.*?)\s*$/mi', $headers, $matches)) {
|
||||||
|
$newUrl = trim($matches[1]);
|
||||||
|
|
||||||
|
// Resolve relative URLs
|
||||||
|
if (!filter_var($newUrl, FILTER_VALIDATE_URL)) {
|
||||||
|
$newUrl = rtrim($currentUrl, '/') . '/' . ltrim($newUrl, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentUrl = $newUrl;
|
||||||
|
$redirectCount++;
|
||||||
|
continue; // Follow the HTTP redirect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load HTML content into DOMDocument to check for meta refresh
|
||||||
|
$doc = new DOMDocument();
|
||||||
|
libxml_use_internal_errors(true); // Ignore HTML warnings
|
||||||
|
$doc->loadHTML($body);
|
||||||
|
libxml_clear_errors();
|
||||||
|
|
||||||
|
// Check for meta refresh redirects in HTML
|
||||||
|
$metaTags = $doc->getElementsByTagName('meta');
|
||||||
|
foreach ($metaTags as $meta) {
|
||||||
|
if (strtolower($meta->getAttribute('http-equiv')) === 'refresh') {
|
||||||
|
$content = $meta->getAttribute('content');
|
||||||
|
if (preg_match('/\d+;\s*url=(.*)/i', $content, $matches)) {
|
||||||
|
$metaRedirectUrl = trim($matches[1], '"');
|
||||||
|
|
||||||
|
// Resolve relative URLs for meta refresh
|
||||||
|
if (!filter_var($metaRedirectUrl, FILTER_VALIDATE_URL)) {
|
||||||
|
$metaRedirectUrl = rtrim($currentUrl, '/') . '/' . ltrim($metaRedirectUrl, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentUrl = $metaRedirectUrl;
|
||||||
|
$redirectCount++;
|
||||||
|
continue 2; // Follow the meta redirect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no more redirects, return the final URL and content
|
||||||
|
return ['url' => $currentUrl, 'content' => $body, 'error' => $error];
|
||||||
|
}
|
||||||
|
|
||||||
|
// If max redirects reached, return an error
|
||||||
|
return ['url' => $currentUrl, 'content' => null, 'error' => 'Too many redirects'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractLinkTags($html) {
|
||||||
|
$doc = new DOMDocument();
|
||||||
|
@$doc->loadHTML($html); // Suppress warnings due to malformed HTML
|
||||||
|
|
||||||
|
$linkTags = [];
|
||||||
|
foreach ($doc->getElementsByTagName('link') as $link) {
|
||||||
|
$rel = $link->getAttribute('rel') ?: $link->getAttribute('itemprop');
|
||||||
|
$href = $link->getAttribute('href') ?: $link->getAttribute('content');
|
||||||
|
|
||||||
|
if ($rel && $href) {
|
||||||
|
if (!array_key_exists($rel, $linkTags)) {
|
||||||
|
$linkTags[$rel] = $href;
|
||||||
|
} elseif (!in_array($content, explode('<br />', $metaData[$name]))) {
|
||||||
|
$linkTags[$rel] .= '<br />' . $href;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $linkTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['url'])) {
|
||||||
|
// Get the submitted URL
|
||||||
|
$url = filter_input(INPUT_GET, 'url', FILTER_VALIDATE_URL);
|
||||||
|
|
||||||
|
if (!$url) {
|
||||||
|
echo "<p style='color: red;'>Invalid URL. Please enter a valid URL.</p>";
|
||||||
|
} else {
|
||||||
|
$url_tokens = parse_url($url);
|
||||||
|
if ($url_tokens['scheme'] !== 'https' || filter_var($url_tokens['host'], FILTER_VALIDATE_IP)) {
|
||||||
|
echo "<p style='color: red;'>Invalid URL. Please enter a valid URL.</p>";
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Fetch the page and follow redirects
|
||||||
|
$result = fetch_page($url);
|
||||||
|
$finalUrl = $result['url'];
|
||||||
|
$htmlContent = $result['content'];
|
||||||
|
$error = $result['error'];
|
||||||
|
|
||||||
|
if ($htmlContent === null) {
|
||||||
|
echo "<p style='color: red;'>Error: $error</p>";
|
||||||
|
} else {
|
||||||
|
// Notify the user of the final URL after following redirects
|
||||||
|
if ($url !== $finalUrl) {
|
||||||
|
$finalUrlEncoded = urlencode($finalUrl);
|
||||||
|
echo "<p style='color: orange;'>Note: The request was redirected to <strong><a href='{$APP_PATH}?url={$finalUrlEncoded}'>{$finalUrl}</a></strong>.</p>";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load HTML content into DOMDocument
|
||||||
|
$doc = new DOMDocument();
|
||||||
|
libxml_use_internal_errors(true); // Ignore HTML warnings
|
||||||
|
$doc->loadHTML($htmlContent);
|
||||||
|
libxml_clear_errors();
|
||||||
|
|
||||||
|
// Look for <meta http-equiv="refresh"> tag
|
||||||
|
$metaTags = $doc->getElementsByTagName('meta');
|
||||||
|
$metaData = [];
|
||||||
|
|
||||||
|
foreach ($metaTags as $meta) {
|
||||||
|
$name = $meta->getAttribute('name') ?: $meta->getAttribute('property') ?: $meta->getAttribute('itemprop');
|
||||||
|
$content = $meta->getAttribute('content');
|
||||||
|
if ($name && $content) {
|
||||||
|
if (!array_key_exists($name, $metaData)) {
|
||||||
|
$metaData[$name] = $content;
|
||||||
|
} elseif (!in_array($content, explode('<br />', $metaData[$name]))) {
|
||||||
|
//$metaData["item-{$name}"] = $content;
|
||||||
|
$metaData[$name] .= '<br />' . $content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($metaData) {
|
||||||
|
// Define names, emoji icons, and descriptions for common meta tags
|
||||||
|
$metaFriendlyNames = [
|
||||||
|
// HTML Standard Meta Tags
|
||||||
|
'title' => ['name' => 'Page Title', 'icon' => '📄', 'description' => 'The title of the webpage, shown on the browser tab.'],
|
||||||
|
'description' => ['name' => 'Description', 'icon' => '📝', 'description' => 'A short summary of the page content.'],
|
||||||
|
'keywords' => ['name' => 'Keywords', 'icon' => '🔑', 'description' => 'A list of keywords relevant to the page, used for SEO.'],
|
||||||
|
'author' => ['name' => 'Author', 'icon' => '✍️', 'description' => 'The author of the webpage.'],
|
||||||
|
'robots' => ['name' => 'Robots', 'icon' => '🤖', 'description' => 'Instructions for search engines on whether to index the page.'],
|
||||||
|
'viewport' => ['name' => 'Viewport', 'icon' => '📱', 'description' => 'Defines the visible area of the webpage on different devices.'],
|
||||||
|
'charset' => ['name' => 'Charset', 'icon' => '🔠', 'description' => 'Character encoding used by the page.'],
|
||||||
|
|
||||||
|
// Open Graph Meta Tags
|
||||||
|
'og:title' => ['name' => 'OG Title', 'icon' => '📄', 'description' => 'The title of the page when shared on social platforms.'],
|
||||||
|
'og:description' => ['name' => 'OG Description', 'icon' => '📝', 'description' => 'A brief description for social sharing.'],
|
||||||
|
'og:image' => ['name' => 'OG Image', 'icon' => '🖼️', 'description' => 'The image displayed when the page is shared.'],
|
||||||
|
'og:url' => ['name' => 'OG URL', 'icon' => '🔗', 'description' => 'Canonical URL of the page being shared.'],
|
||||||
|
'og:type' => ['name' => 'OG Type', 'icon' => '🔗', 'description' => 'The type of content (e.g., article, website, video).'],
|
||||||
|
'og:site_name' => ['name' => 'OG Site Name', 'icon' => '🏠', 'description' => 'The name of the website.'],
|
||||||
|
'og:locale' => ['name' => 'OG Locale', 'icon' => '🌍', 'description' => 'The locale of the page (e.g., en_US).'],
|
||||||
|
|
||||||
|
// Twitter Meta Tags
|
||||||
|
'twitter:title' => ['name' => 'Twitter Title', 'icon' => '🐦', 'description' => 'The title of the page when shared on Twitter.'],
|
||||||
|
'twitter:description' => ['name' => 'Twitter Description', 'icon' => '🐦📝', 'description' => 'A brief description for Twitter sharing.'],
|
||||||
|
'twitter:image' => ['name' => 'Twitter Image', 'icon' => '🐦🖼️', 'description' => 'The image displayed when the page is shared on Twitter.'],
|
||||||
|
'twitter:card' => ['name' => 'Twitter Card', 'icon' => '🐦🃏', 'description' => 'The type of Twitter card to display (e.g., summary, player).'],
|
||||||
|
|
||||||
|
// Other common meta tags
|
||||||
|
'canonical' => ['name' => 'Canonical URL', 'icon' => '🔗', 'description' => 'The preferred URL for the page, used to avoid duplicate content.'],
|
||||||
|
];
|
||||||
|
|
||||||
|
// Display meta tags in table format with names, icons, and raw names
|
||||||
|
echo "<table class='meta-table'>";
|
||||||
|
echo "<tr><th>Meta Tag</th><th>Value</th></tr>";
|
||||||
|
|
||||||
|
foreach ($metaData as $key => $value) {
|
||||||
|
$friendlyName = $metaFriendlyNames[$key]['name'] ?? ''; // Use friendly name if available
|
||||||
|
$icon = $metaFriendlyNames[$key]['icon'] ?? 'ℹ️'; // Use icon if available
|
||||||
|
|
||||||
|
// Update table row to include icon, friendly name, and raw name
|
||||||
|
echo "<tr>
|
||||||
|
<td>
|
||||||
|
<span class='meta-icon'>$icon</span>
|
||||||
|
<span class='meta-title' title='{$metaFriendlyNames[$key]['description']}'><span class='title'>{$friendlyName}</span> (<code>$key</code>)</span>
|
||||||
|
</td>
|
||||||
|
<td>$value</td>
|
||||||
|
</tr>";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "</table>";
|
||||||
|
} else {
|
||||||
|
echo "<p>No meta tags found on the provided URL.</p>";
|
||||||
|
}
|
||||||
|
|
||||||
|
$linkTags = extractLinkTags($htmlContent);
|
||||||
|
|
||||||
|
if ($linkTags) {
|
||||||
|
// Display the table with <link> elements
|
||||||
|
echo "<table class='meta-table'>";
|
||||||
|
echo "<tr><th>Link Reference (rel)</th><th>Link Href</th></tr>";
|
||||||
|
|
||||||
|
foreach ($linkTags as $key => $value) {
|
||||||
|
echo "<tr>
|
||||||
|
<td>{$key}</td>
|
||||||
|
<td>{$value}</td>
|
||||||
|
</tr>";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "</table>";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract important meta information for social media previews
|
||||||
|
$title = $metaData['og:title'] ?? $metaData['twitter:title'] ?? "Untitled";
|
||||||
|
$description = $metaData['og:description'] ?? $metaData['twitter:description'] ?? "No description available.";
|
||||||
|
$image = $metaData['og:image'] ?? $metaData['twitter:image'] ?? "";
|
||||||
|
$ogType = $metaData['og:type'] ?? '';
|
||||||
|
|
||||||
|
// Only show video if `og:type` is not `video.other`
|
||||||
|
if ($ogType !== 'video.other') {
|
||||||
|
$video = $metaData['og:video'] ?? $metaData['twitter:player'] ?? "";
|
||||||
|
} else {
|
||||||
|
$video = null; // Ignore video.other type
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "<div class='preview-cards'>
|
||||||
|
<h3>Link Preview on Social Platforms</h3>";
|
||||||
|
|
||||||
|
// Display cards for different platforms
|
||||||
|
$platforms = [
|
||||||
|
['name' => 'WhatsApp', 'color' => '#25D366'],
|
||||||
|
['name' => 'Telegram', 'color' => '#0088cc'],
|
||||||
|
['name' => 'Facebook', 'color' => '#3b5998'],
|
||||||
|
['name' => 'Instagram', 'color' => '#e4405f'],
|
||||||
|
['name' => 'Pinterest', 'color' => '#bd081c'],
|
||||||
|
['name' => 'Discord', 'color' => '#7289DA'],
|
||||||
|
['name' => 'X (formerly Twitter)', 'color' => '#1DA1F2'],
|
||||||
|
['name' => 'iMessage', 'color' => '#34C759'],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($platforms as $platform) {
|
||||||
|
echo "<div class='preview-card' style='border-left: 5px solid {$platform['color']}'>";
|
||||||
|
|
||||||
|
// Display video if present and og:type is allowed
|
||||||
|
if ($video) {
|
||||||
|
echo "<video controls width='100' height='100'>
|
||||||
|
<source src='$video' type='video/mp4'>
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>";
|
||||||
|
}
|
||||||
|
// Display image if available
|
||||||
|
elseif ($image) {
|
||||||
|
echo "<img src='$image' alt='Preview Image' width='100' height='100' style='border-radius: 5px; object-fit: cover;'>";
|
||||||
|
}
|
||||||
|
// Graceful fallback to placeholder
|
||||||
|
else {
|
||||||
|
echo "<img src='https://via.placeholder.com/100' alt='Placeholder Image' width='100' height='100' style='border-radius: 5px;'>";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display title and description
|
||||||
|
echo "<div class='preview-card-content'>
|
||||||
|
<span class='platform' style='color: {$platform['color']}; font-weight: bold;'>{$platform['name']}</span>
|
||||||
|
<span class='preview-title'>$title</span>
|
||||||
|
<span class='preview-description'>$description</span>
|
||||||
|
</div>";
|
||||||
|
|
||||||
|
echo "</div>"; // End of preview card
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "</div>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
281
ViewUltra.php
Normal file
281
ViewUltra.php
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<?php
|
||||||
|
$APP_NAME = 'ViewUltra';
|
||||||
|
$APP_PATH = "./{$APP_NAME}.php";
|
||||||
|
$RESULT_COUNT = 35;
|
||||||
|
|
||||||
|
$url = $_GET['url'];
|
||||||
|
$omni = $_GET['omni'];
|
||||||
|
$embed = $_GET['embed'];
|
||||||
|
$search = $_GET['search'];
|
||||||
|
|
||||||
|
$url0 = $url;
|
||||||
|
$platform = null;
|
||||||
|
$metadata = [];
|
||||||
|
|
||||||
|
require 'Res/http_build_url.php';
|
||||||
|
|
||||||
|
function parse_url_sure ( $url ) {
|
||||||
|
if ( gettype($url) == 'string' ) {
|
||||||
|
$url = parse_url($url);
|
||||||
|
}
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $api = $_GET['api'] ) {
|
||||||
|
$search = urlencode($search);
|
||||||
|
switch ($api) {
|
||||||
|
case 'related':
|
||||||
|
echo file_get_contents("https://www.bing.com/videos/api/custom/details?vdpp=rvrv&mmcaptn=Bing.Video&skey=LpjYneKXrGPhU1rWE1pUvUE7_tPlNQt3oAXWj_pFOfk&safesearch=Moderate&mkt=it-it&setlang=en-gb&iid=cusvdp&sfx=1&q={$search}&modules=videoresult,videoreco,queryresultvideos,relatedsearches,relatedads,videochat&channels=relatedvideos&recocount={$RESULT_COUNT}&recomidboost=true&recooffset=0&chaidx=-1&armk=ra&mmscn=Default");
|
||||||
|
case 'search':
|
||||||
|
//echo file_get_contents("https://duckduckgo.com/v.js?l=wt-wt&o=json&sr=1&q={$search}&vqd=4-179096413383356441354158635150751105868&f=,,,&p=-1");
|
||||||
|
echo file_get_contents("https://www.bing.com/videos/async/rankedans?q={$search}&mmasync=1&varh=VideoResultInfiniteScroll&vdpp=VideoResultAsync&count={$RESULT_COUNT}&first=0&IID=vrpalis&SFX=1");
|
||||||
|
case 'trending':
|
||||||
|
echo file_get_contents("https://www.bing.com/videos/api/custom/trending?mmcaptn=Bing.VideoOneColumnFeeds&recopid=VideoLandingPage&channels=trending&offset=0&videocount={$RESULT_COUNT}&skey=LpjYneKXrGPhU1rWE1pUvUE7_tPlNQt3oAXWj_pFOfk&safesearch=Moderate&iid=vlp&sfx=1");
|
||||||
|
default:
|
||||||
|
http_response_code(400);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( !$embed ) {
|
||||||
|
if ( $omni ) {
|
||||||
|
$url = parse_url_sure($omni);
|
||||||
|
if ( !$search && !array_key_exists('host', parse_url_sure($url)) ) {
|
||||||
|
$url = null;
|
||||||
|
$search = $omni;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( $url ) {
|
||||||
|
$url = parse_url_sure($url);
|
||||||
|
switch ($url['host']) {
|
||||||
|
case 'youtu.be':
|
||||||
|
$url = http_build_url($url['scheme'] . '://youtube.com/watch?v=' . substr($url['path'], 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$href = parse_url_sure($url);
|
||||||
|
$path = explode('/', rtrim($url['path'], '/'));
|
||||||
|
parse_str($href['query'], $query);
|
||||||
|
switch ($href['host']) {
|
||||||
|
case 'bilibili.com':
|
||||||
|
case 'www.bilibili.com':
|
||||||
|
$href = ($href['scheme'] . '://player.bilibili.com/player.html?isOutside=true&p=1&bvid=' . end($path));
|
||||||
|
$platform = 'bilibili';
|
||||||
|
break;
|
||||||
|
case 'tiktok.com':
|
||||||
|
case 'www.tiktok.com':
|
||||||
|
$href = ($href['scheme'] . '://www.tiktok.com/embed/v3/' . end($path) . '?mute=0');
|
||||||
|
$platform = 'tiktok';
|
||||||
|
break;
|
||||||
|
case 'youtube.com':
|
||||||
|
case 'www.youtube.com':
|
||||||
|
// case 'youtube-nocookie.com':
|
||||||
|
// case 'www.youtube-nocookie.com':
|
||||||
|
$href = ($href['scheme'] . '://www.youtube-nocookie.com/embed/' . $query['v']);
|
||||||
|
$platform = 'youtube';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$url = http_build_url($url);
|
||||||
|
$embed = http_build_url($href);
|
||||||
|
if ( $platform ) {
|
||||||
|
$doc = new DOMDocument();
|
||||||
|
libxml_use_internal_errors(true);
|
||||||
|
$doc->loadHTML(mb_convert_encoding(file_get_contents($url), 'HTML-ENTITIES', 'UTF-8'));
|
||||||
|
libxml_clear_errors();
|
||||||
|
foreach ( $doc->getElementsByTagName('meta') as $metaEl ) {
|
||||||
|
$key = $metaEl->getAttribute('name')
|
||||||
|
?: $metaEl->getAttribute('property')
|
||||||
|
?: $metaEl->getAttribute('itemprop');
|
||||||
|
$value = $metaEl->getAttribute('content');
|
||||||
|
if ( !$key && !$value ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!array_key_exists($key, $metadata)) {
|
||||||
|
$metadata[$key] = $value;
|
||||||
|
} elseif (!in_array($content, explode('\n', $metadata[$key]))) {
|
||||||
|
$metadata[$key] .= "\n{$value}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$description = 'View ' . ($metadata['og:title']?: 'this content') . ' on ' . $APP_NAME;
|
||||||
|
?>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title><?php echo $metadata['og:title'] ?> | <?php echo $APP_NAME ?></title>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
|
<meta name="description" content="<?php echo $description ?>"/>
|
||||||
|
<meta property="og:site_name" content="<?php echo $APP_NAME ?>"/>
|
||||||
|
<meta property="og:title" content="<?php echo $metadata['og:title'] ?>"/>
|
||||||
|
<meta property="og:image" content="<?php echo $metadata['og:image'] ?>"/>
|
||||||
|
<meta property="og:image:width" content="<?php echo $metadata['og:image:width'] ?>"/>
|
||||||
|
<meta property="og:image:height" content="<?php echo $metadata['og:image:height'] ?>"/>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
table td > * {
|
||||||
|
height: 2em;
|
||||||
|
}
|
||||||
|
table td.full,
|
||||||
|
table td.full > * {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
body.vu-app {
|
||||||
|
color: white;
|
||||||
|
background-color: black;
|
||||||
|
position: absolute;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: hotpink;
|
||||||
|
}
|
||||||
|
.font-mega {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: large;
|
||||||
|
}
|
||||||
|
.vu-main {
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: row;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.vu-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
.vu-content > iframe {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.vu-details > summary {
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
/* .vu-details > summary > * {
|
||||||
|
display: inline-block;
|
||||||
|
} */
|
||||||
|
@media (min-width: 600px) {
|
||||||
|
.vu-main {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.vu-content {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.vu-related {
|
||||||
|
height: 0;
|
||||||
|
width: 30%;
|
||||||
|
min-width: 150px;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
.vu-details > div {
|
||||||
|
position: absolute;
|
||||||
|
width: calc(100vw - 30%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.vu-related .mc_fgvc,
|
||||||
|
.vu-related .mc_fgvc_u,
|
||||||
|
.vu-related .tvsr .vsb_vid_cat,
|
||||||
|
.vu-related .isvsr .vsb_vid_cat {
|
||||||
|
width: 100% !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
.vu-related .mc_fgvc_u * {
|
||||||
|
border-radius: revert !important;
|
||||||
|
}
|
||||||
|
.vu-related .mc_fgvc_u .mc_vtvc_meta {
|
||||||
|
background-color: gray !important;
|
||||||
|
}
|
||||||
|
.vu-related .mc_fgvc_u .mc_vtvc_meta * {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
.vu-related #mm_vidreco_sublabel,
|
||||||
|
.vu-related #fbdialog,
|
||||||
|
.vu-related .fbdialog.b_cards {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="vu-app">
|
||||||
|
<!--<form style="display: flex; height: 2em; margin-bottom: 8px;">
|
||||||
|
<a href="./ViewUltra.php">ViewUltra</a>
|
||||||
|
<input type="text" name="omnibox" style="flex: auto; margin-right: 8px;"/>
|
||||||
|
<input type="submit" value="Go ↩️"/>
|
||||||
|
</form>-->
|
||||||
|
<form>
|
||||||
|
<table><!--<tbody><tr>-->
|
||||||
|
<td><a class="font-mega" href="<?php echo $APP_PATH ?>"><?php echo $APP_NAME ?></a></td>
|
||||||
|
<td class="full"><input type="text" name="omni" placeholder="🔍️ Search or input URL..." value="<?php echo current(array_filter([$omni, $search, $url0, $embed])) ?>"/></td>
|
||||||
|
<td><input type="submit" value="Go ↩️"/></td>
|
||||||
|
<!--</tr></tbody>--></table>
|
||||||
|
</form>
|
||||||
|
<div class="vu-main">
|
||||||
|
<?php if ( $embed ) {
|
||||||
|
?>
|
||||||
|
<div class="vu-content">
|
||||||
|
<iframe src="<?php echo $embed ?>" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="allowfullscreen"></iframe>
|
||||||
|
<details class="vu-details" open="open">
|
||||||
|
<summary>
|
||||||
|
<span class="font-mega"><?php echo $metadata['og:title'] ?></span>
|
||||||
|
(<?php echo $metadata['og:site_name'] ?>)
|
||||||
|
<?php echo $metadata['datePublished'] ?>
|
||||||
|
</summary>
|
||||||
|
<div>
|
||||||
|
<p><?php echo $metadata['og:description'] ?></p>
|
||||||
|
<p><a href="<?php echo $url ?>"><?php echo $url ?></a></p>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
<?php } elseif ( !$search ) { ?>
|
||||||
|
<p>It works!!! (semicit.)<br/>Work in progress.</p>
|
||||||
|
<?php } ?>
|
||||||
|
<div class="vu-related">
|
||||||
|
<p>Please enable JavaScript to view related content.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>(function(){
|
||||||
|
var $APP_PATH = <?php echo json_encode($APP_PATH) ?>;
|
||||||
|
var $metadata = <?php echo json_encode($metadata) ?>;
|
||||||
|
var $search = ($metadata['og:title'] || <?php echo json_encode($search) ?>);
|
||||||
|
var searchEncoded = encodeURIComponent($search || '');
|
||||||
|
//var getParams = (new URLSearchParams(window.location.search));
|
||||||
|
var relatedEl = document.querySelector('.vu-related');
|
||||||
|
relatedEl.innerHTML = '';
|
||||||
|
if (!$search) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
function fetchRelated () {
|
||||||
|
relatedEl.innerHTML = '<p>Loading...</p>';
|
||||||
|
fetch($APP_PATH + '?api=search&search=' + searchEncoded)
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(html => {
|
||||||
|
var fragmentEl = Object.assign(document.createElement('div'), { innerHTML: html });
|
||||||
|
Array.from(fragmentEl.querySelectorAll('script')).forEach(scriptEl => scriptEl.remove());
|
||||||
|
Array.from(fragmentEl.querySelectorAll('div.mc_fgvc_u a[href]')).forEach(linkEl => {
|
||||||
|
var targetUrl = (linkEl.getAttribute('ourl') || linkEl.querySelector('[ourl]').getAttribute('ourl'));
|
||||||
|
linkEl.href = ($APP_PATH + '?url=' + encodeURIComponent(targetUrl) + '&search=' + searchEncoded);
|
||||||
|
});
|
||||||
|
relatedEl.innerHTML = fragmentEl.innerHTML;
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
relatedEl.innerHTML = ('<p>' + err + '</p><p><button>Retry</button></p>');
|
||||||
|
relatedEl.querySelector('button').onclick = fetchRelated;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fetchRelated();
|
||||||
|
window.ViewUltra = $metadata;
|
||||||
|
})();</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
x
Reference in New Issue
Block a user