- Get link
- X
- Other Apps
EUREKA OF TODAY !!
TikTok Basic-URL into Full-URL into Embedding-Code
SOURCE CODE
nb: unable to be loading in blogger.com
aka please use private web-host page as media AKA one of numerous Free Web-Host Option
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TikTok Link Resolver & Embed Studio</title>
<!-- Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
brand: {
cyan: '#00f2fe',
pink: '#ff0050',
purple: '#7b2cbf',
dark: '#0d1117'
}
},
fontFamily: {
sans: ['Inter', 'sans-serif'],
mono: ['JetBrains Mono', 'Fira Code', 'monospace']
}
}
}
}
</script>
<!-- FontAwesome Icons CDN -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Google Font Inter & JetBrains Mono -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
background-color: #0b0f17;
color: #f1f5f9;
}
/* Custom scrollbar styling */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: #0f172a;
}
::-webkit-scrollbar-thumb {
background: #334155;
border-radius: 9999px;
}
::-webkit-scrollbar-thumb:hover {
background: #475569;
}
.glow-effect {
box-shadow: 0 0 25px -5px rgba(0, 242, 254, 0.15);
}
</style>
</head>
<body class="min-h-screen flex flex-col justify-between selection:bg-cyan-500 selection:text-black">
<!-- Header / Navbar -->
<header class="border-b border-slate-800/80 bg-slate-950/80 backdrop-blur sticky top-0 z-40">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-3.5 flex flex-wrap items-center justify-between gap-4">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-xl bg-gradient-to-tr from-cyan-500 via-purple-500 to-pink-500 p-0.5 flex items-center justify-center shadow-lg">
<div class="w-full h-full bg-slate-950 rounded-[10px] flex items-center justify-center">
<i class="fa-brands fa-tiktok text-cyan-400 text-lg"></i>
</div>
</div>
<div>
<h1 class="text-base font-bold bg-gradient-to-r from-cyan-400 via-purple-300 to-pink-400 bg-clip-text text-transparent">
TikTok URL Resolver & Embed Studio
</h1>
<p class="text-[11px] text-slate-400">Batch Unshortener & Automated HTML Code Generator</p>
</div>
</div>
<div class="flex items-center gap-2">
<a href="#instructions" class="px-3 py-1.5 rounded-lg border border-slate-800 bg-slate-900/60 hover:bg-slate-800 text-xs text-slate-300 transition flex items-center gap-1.5">
<i class="fa-solid fa-circle-info text-cyan-400"></i>
<span>How it Works</span>
</a>
<a href="https://www.tiktok.com" target="_blank" rel="noopener noreferrer" class="px-3 py-1.5 rounded-lg bg-pink-600/20 hover:bg-pink-600/30 text-pink-300 border border-pink-500/30 text-xs font-medium transition flex items-center gap-1.5">
<i class="fa-brands fa-tiktok"></i>
<span>TikTok</span>
</a>
</div>
</div>
</header>
<!-- Main Content Container -->
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 w-full space-y-8 flex-grow">
<!-- Input & Options Section -->
<section class="bg-slate-900/70 border border-slate-800 rounded-2xl p-5 sm:p-6 shadow-2xl backdrop-blur glow-effect">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-4">
<div>
<h2 class="text-base font-semibold text-white flex items-center gap-2">
<i class="fa-solid fa-link text-cyan-400"></i>
Input Short TikTok Links
</h2>
<p class="text-xs text-slate-400 mt-0.5">Paste short URLs (e.g. <code class="bg-slate-800 px-1.5 py-0.5 rounded text-cyan-300 font-mono">vt.tiktok.com/ZS...</code> or <code class="bg-slate-800 px-1.5 py-0.5 rounded text-cyan-300 font-mono">vm.tiktok.com/...</code>)</p>
</div>
<div class="flex items-center gap-2 self-end sm:self-auto">
<button id="loadSampleBtn" type="button" class="px-3 py-1.5 rounded-lg border border-slate-700 bg-slate-800 hover:bg-slate-700 text-xs text-slate-200 transition flex items-center gap-1.5">
<i class="fa-solid fa-flask text-purple-400"></i>
<span>Load Demos</span>
</button>
<button id="clearInputBtn" type="button" class="px-3 py-1.5 rounded-lg border border-slate-700 bg-slate-800 hover:bg-slate-700 text-xs text-slate-200 transition flex items-center gap-1.5">
<i class="fa-solid fa-trash-can text-rose-400"></i>
<span>Clear</span>
</button>
</div>
</div>
<!-- Textarea for Batch URLs -->
<div class="relative">
<textarea id="urlInput" rows="5" placeholder="https://vt.tiktok.com/ZS47tfdKd/ https://vt.tiktok.com/ZS49A9MGX/ https://vt.tiktok.com/ZS49bfXoK/" class="w-full bg-slate-950/90 border border-slate-800 rounded-xl p-4 text-xs font-mono text-cyan-300 focus:outline-none focus:border-cyan-500/70 resize-y placeholder:text-slate-600 transition shadow-inner"></textarea>
<div id="urlCounter" class="absolute bottom-3 right-3 text-[11px] bg-slate-900/90 border border-slate-800 text-slate-400 px-2.5 py-1 rounded-md font-mono">
0 URLs detected
</div>
</div>
<!-- Control Options & Resolution Controls -->
<div class="mt-4 pt-4 border-t border-slate-800/80 flex flex-col md:flex-row md:items-center justify-between gap-4">
<div class="flex items-center gap-3">
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="cleanParamsToggle" class="sr-only peer" checked>
<div class="w-9 h-5 bg-slate-800 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-cyan-500"></div>
<span class="ml-2.5 text-xs text-slate-300 font-medium select-none">Auto-strip tracking parameters (<code class="text-slate-400">?_r=1</code>)</span>
</label>
</div>
<div class="flex items-center gap-2">
<button id="stopBtn" type="button" class="hidden px-4 py-2.5 rounded-xl border border-rose-500/30 bg-rose-500/10 hover:bg-rose-500/20 text-rose-400 text-xs font-semibold transition flex items-center gap-2">
<i class="fa-solid fa-hand"></i>
<span>Stop</span>
</button>
<button id="startBtn" type="button" class="w-full md:w-auto px-6 py-2.5 rounded-xl bg-gradient-to-r from-cyan-500 via-blue-600 to-purple-600 hover:opacity-90 text-white text-xs font-semibold transition shadow-lg flex items-center justify-center gap-2">
<i class="fa-solid fa-bolt"></i>
<span>Resolve Full URLs</span>
</button>
</div>
</div>
</section>
<!-- Progress Indicator -->
<section id="progressSection" class="hidden bg-slate-900/50 border border-slate-800 rounded-2xl p-4 transition-all">
<div class="flex items-center justify-between text-xs mb-2">
<span id="progressStatusText" class="font-medium text-slate-300 flex items-center gap-2">
<i class="fa-solid fa-spinner fa-spin text-cyan-400"></i> Processing queue...
</span>
<span id="progressPercentText" class="font-mono text-cyan-400 font-semibold">0%</span>
</div>
<div class="w-full bg-slate-950 rounded-full h-2 overflow-hidden border border-slate-800">
<div id="progressBarInner" class="bg-gradient-to-r from-cyan-400 to-purple-500 h-full w-0 transition-all duration-300"></div>
</div>
<div id="progressCountText" class="text-[11px] text-slate-500 text-right mt-1.5 font-mono">
Resolved: 0 / 0
</div>
</section>
<!-- Resolved Links Results Section -->
<section class="bg-slate-900/70 border border-slate-800 rounded-2xl p-5 sm:p-6 shadow-xl backdrop-blur space-y-4">
<div class="flex flex-col lg:flex-row justify-between items-start lg:items-center gap-4 border-b border-slate-800/80 pb-4">
<div>
<h2 class="text-base font-semibold text-white flex items-center gap-2">
<i class="fa-solid fa-list-check text-cyan-400"></i>
Resolved Results
<span id="resultBadge" class="ml-2 px-2.5 py-0.5 rounded-full text-xs font-mono bg-slate-800 text-slate-300 border border-slate-700">0 Items</span>
</h2>
<p class="text-xs text-slate-400 mt-0.5">Unshortened TikTok links ready for export or embedding</p>
</div>
<!-- Export / Batch Action Buttons -->
<div class="flex flex-wrap items-center gap-2 w-full lg:w-auto">
<button id="copyAllBtn" type="button" class="flex-1 sm:flex-none px-3.5 py-2 rounded-xl border border-slate-700 bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-medium transition flex items-center justify-center gap-2">
<i class="fa-regular fa-copy text-cyan-400"></i>
<span>Copy Resolved</span>
</button>
<button id="exportCsvBtn" type="button" class="flex-1 sm:flex-none px-3.5 py-2 rounded-xl border border-slate-700 bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-medium transition flex items-center justify-center gap-2">
<i class="fa-solid fa-file-csv text-emerald-400"></i>
<span>Export CSV</span>
</button>
<button id="exportTxtBtn" type="button" class="flex-1 sm:flex-none px-3.5 py-2 rounded-xl border border-slate-700 bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-medium transition flex items-center justify-center gap-2">
<i class="fa-solid fa-file-lines text-amber-400"></i>
<span>Export TXT</span>
</button>
<button id="openAllBtn" type="button" class="flex-1 sm:flex-none px-3.5 py-2 rounded-xl border border-cyan-500/30 bg-cyan-500/10 hover:bg-cyan-500/20 text-cyan-300 text-xs font-medium transition flex items-center justify-center gap-2">
<i class="fa-solid fa-arrow-up-right-from-square"></i>
<span>Open Tabs</span>
</button>
</div>
</div>
<!-- Table Container -->
<div class="overflow-x-auto rounded-xl border border-slate-800 bg-slate-950/60">
<table class="w-full text-left text-xs sm:text-sm text-slate-300">
<thead class="bg-slate-900/90 text-slate-400 font-medium uppercase text-[11px] tracking-wider border-b border-slate-800">
<tr>
<th scope="col" class="py-3.5 px-4 w-12 text-center">#</th>
<th scope="col" class="py-3.5 px-4 w-1/4">Short Link</th>
<th scope="col" class="py-3.5 px-4">Full TikTok URL</th>
<th scope="col" class="py-3.5 px-4 w-32 text-center">Status</th>
<th scope="col" class="py-3.5 px-4 w-28 text-right">Action</th>
</tr>
</thead>
<tbody id="resultsTableBody" class="divide-y divide-slate-800/60 font-mono">
<!-- Empty State -->
<tr id="emptyRow">
<td colspan="5" class="py-12 text-center text-slate-500 font-sans">
<i class="fa-solid fa-link-slash text-3xl mb-3 text-slate-600 block"></i>
<p class="text-sm font-medium text-slate-400">No links processed yet.</p>
<p class="text-xs text-slate-600 mt-1">Paste your short TikTok links above and click "Resolve Full URLs".</p>
</td>
</tr>
</tbody>
</table>
</div>
</section>
<!-- Next Phase: TikTok HTML Embed Generator & Interactive Preview -->
<section class="bg-slate-900/70 border border-slate-800 rounded-2xl p-5 sm:p-6 shadow-xl backdrop-blur space-y-4">
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
<div>
<h2 class="text-base font-semibold text-white flex items-center gap-2">
<i class="fa-solid fa-code text-cyan-400"></i>
TikTok HTML Embed Codes & Live Preview
</h2>
<p class="text-xs text-slate-400 mt-0.5">Embed codes and live previews update automatically upon URL resolution</p>
</div>
<div class="flex items-center gap-2 w-full sm:w-auto justify-end">
<select id="embedTypeSelect" class="bg-slate-950 border border-slate-800 rounded-xl px-3 py-2 text-xs text-slate-200 focus:outline-none focus:border-cyan-500 font-medium">
<option value="official">Official TikTok Blockquote (Recommended)</option>
<option value="iframe">Direct Responsive iFrame</option>
<option value="gridPage">Full HTML Gallery Page</option>
</select>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 pt-2">
<!-- Left Column: Generated Code Output -->
<div class="space-y-3 flex flex-col">
<div class="flex items-center justify-between">
<label class="text-xs font-semibold text-slate-400 flex items-center gap-1.5">
<i class="fa-solid fa-file-code text-purple-400"></i> Generated HTML Snippet
</label>
</div>
<div class="relative flex-grow">
<textarea id="embedOutput" rows="10" readonly placeholder="Resolved TikTok links will automatically generate HTML embed code here..." class="w-full h-full min-h-[220px] bg-slate-950/90 border border-slate-800 rounded-xl p-4 text-xs font-mono text-cyan-300 focus:outline-none resize-y select-all shadow-inner"></textarea>
</div>
<div class="flex items-center gap-3 pt-1">
<button id="copyEmbedBtn" type="button" class="flex-1 px-4 py-2.5 rounded-xl border border-slate-700 bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-medium transition flex items-center justify-center gap-2">
<i class="fa-regular fa-copy text-cyan-400"></i>
<span>Copy Embed Code</span>
</button>
<button id="downloadHtmlBtn" type="button" class="flex-1 px-4 py-2.5 rounded-xl border border-slate-700 bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-medium transition flex items-center justify-center gap-2">
<i class="fa-solid fa-download text-emerald-400"></i>
<span>Download Page</span>
</button>
</div>
</div>
<!-- Right Column: Interactive Live Preview Container -->
<div class="bg-slate-950/90 border border-slate-800 rounded-xl p-4 flex flex-col justify-between min-h-[300px]">
<div class="flex items-center justify-between border-b border-slate-800/80 pb-2.5 mb-3">
<span class="text-xs font-semibold text-slate-300 flex items-center gap-2">
<i class="fa-solid fa-eye text-emerald-400"></i> Live Embed Preview
</span>
<span id="previewCountBadge" class="text-[10px] bg-slate-800 text-slate-400 px-2 py-0.5 rounded border border-slate-700 font-mono">0 Videos</span>
</div>
<div id="embedPreviewContainer" class="flex-grow overflow-y-auto max-h-[360px] space-y-4 text-center text-xs text-slate-500 py-6 flex flex-col items-center justify-center">
<i class="fa-brands fa-tiktok text-4xl text-slate-800"></i>
<p class="text-slate-500">No embeds generated yet.</p>
<p class="text-[11px] text-slate-600">Resolve URLs above to render real-time embeds.</p>
</div>
</div>
</div>
</section>
<!-- Instructions Box -->
<section id="instructions" class="bg-slate-900/40 border border-slate-800/80 rounded-2xl p-5 sm:p-6">
<h3 class="text-sm font-semibold text-slate-200 mb-3 flex items-center gap-2">
<i class="fa-solid fa-server text-cyan-400"></i>
How does the Multi-Endpoint Server Resolver work?
</h3>
<ul class="grid grid-cols-1 md:grid-cols-3 gap-4 text-xs text-slate-400">
<li class="bg-slate-950/40 p-4 rounded-xl border border-slate-800/80 space-y-1">
<strong class="text-slate-300 flex items-center gap-2">
<i class="fa-solid fa-network-wired text-cyan-400"></i> 1. Online Gateway Fallbacks
</strong>
<p class="text-slate-400 leading-relaxed">Requests route through dedicated redirect followers and oEmbed CORS gateways to trace original redirect chains safely.</p>
</li>
<li class="bg-slate-950/40 p-4 rounded-xl border border-slate-800/80 space-y-1">
<strong class="text-slate-300 flex items-center gap-2">
<i class="fa-solid fa-link text-purple-400"></i> 2. Direct Canonical Format
</strong>
<p class="text-slate-400 leading-relaxed">Resolves short codes (<code class="text-slate-300 font-mono">ZS...</code>) into official web links containing creator handles and video ID numbers.</p>
</li>
<li class="bg-slate-950/40 p-4 rounded-xl border border-slate-800/80 space-y-1">
<strong class="text-slate-300 flex items-center gap-2">
<i class="fa-solid fa-filter text-pink-400"></i> 3. Parameter Stripping
</strong>
<p class="text-slate-400 leading-relaxed">Tracking indicators (<code class="text-slate-300 font-mono">_t=...</code>, <code class="text-slate-300 font-mono">_r=1</code>) are removed automatically for neat target links.</p>
</li>
</ul>
</section>
</main>
<!-- Footer -->
<footer class="border-t border-slate-800/80 bg-slate-950 py-4 text-center text-xs text-slate-500">
<div class="max-w-7xl mx-auto px-4">
TikTok Link Resolver • Online Server API Integration • Web Utility
</div>
</footer>
<!-- Toast Notification Container -->
<div id="toastContainer" class="fixed bottom-5 right-5 z-50 flex flex-col gap-2 pointer-events-none"></div>
<!-- Application Script Logic -->
<script>
// DOM Elements & State Variables
let urlInput, urlCounter, startBtn, stopBtn, loadSampleBtn, clearInputBtn, cleanParamsToggle;
let progressSection, progressStatusText, progressPercentText, progressBarInner, progressCountText;
let resultsTableBody, emptyRow, resultBadge;
let copyAllBtn, exportCsvBtn, exportTxtBtn, openAllBtn;
let embedTypeSelect, embedOutput, copyEmbedBtn, downloadHtmlBtn, embedPreviewContainer, previewCountBadge;
let isProcessing = false;
let shouldStop = false;
let resolvedResults = [];
// Sample demo links for testing
const demoLinks = [
"https://vt.tiktok.com/ZS47tfdKd/",
"https://vt.tiktok.com/ZS49A9MGX/",
"https://vt.tiktok.com/ZS49bfXoK/",
"https://vt.tiktok.com/ZS49StEXp/",
"https://vt.tiktok.com/ZS4CKd9mS/"
];
// Safely initialize DOM elements and bind event listeners
document.addEventListener('DOMContentLoaded', () => {
urlInput = document.getElementById('urlInput');
urlCounter = document.getElementById('urlCounter');
startBtn = document.getElementById('startBtn');
stopBtn = document.getElementById('stopBtn');
loadSampleBtn = document.getElementById('loadSampleBtn');
clearInputBtn = document.getElementById('clearInputBtn');
cleanParamsToggle = document.getElementById('cleanParamsToggle');
progressSection = document.getElementById('progressSection');
progressStatusText = document.getElementById('progressStatusText');
progressPercentText = document.getElementById('progressPercentText');
progressBarInner = document.getElementById('progressBarInner');
progressCountText = document.getElementById('progressCountText');
resultsTableBody = document.getElementById('resultsTableBody');
emptyRow = document.getElementById('emptyRow');
resultBadge = document.getElementById('resultBadge');
copyAllBtn = document.getElementById('copyAllBtn');
exportCsvBtn = document.getElementById('exportCsvBtn');
exportTxtBtn = document.getElementById('exportTxtBtn');
openAllBtn = document.getElementById('openAllBtn');
embedTypeSelect = document.getElementById('embedTypeSelect');
embedOutput = document.getElementById('embedOutput');
copyEmbedBtn = document.getElementById('copyEmbedBtn');
downloadHtmlBtn = document.getElementById('downloadHtmlBtn');
embedPreviewContainer = document.getElementById('embedPreviewContainer');
previewCountBadge = document.getElementById('previewCountBadge');
// Attach event listeners
if (urlInput) urlInput.addEventListener('input', updateUrlCount);
if (loadSampleBtn) {
loadSampleBtn.addEventListener('click', () => {
urlInput.value = demoLinks.join('\n');
updateUrlCount();
showToast('Loaded demo TikTok short links', 'info');
});
}
if (clearInputBtn) {
clearInputBtn.addEventListener('click', () => {
urlInput.value = '';
updateUrlCount();
});
}
if (startBtn) startBtn.addEventListener('click', startBatchResolution);
if (stopBtn) {
stopBtn.addEventListener('click', () => {
shouldStop = true;
stopBtn.innerHTML = `<i class="fa-solid fa-spinner fa-spin"></i> Stopping...`;
});
}
if (copyAllBtn) copyAllBtn.addEventListener('click', copyAllResolved);
if (exportCsvBtn) exportCsvBtn.addEventListener('click', exportCSV);
if (exportTxtBtn) exportTxtBtn.addEventListener('click', exportTXT);
if (openAllBtn) openAllBtn.addEventListener('click', openAllTabs);
if (embedTypeSelect) embedTypeSelect.addEventListener('change', generateHtmlEmbeds);
if (copyEmbedBtn) {
copyEmbedBtn.addEventListener('click', () => {
if (!embedOutput.value.trim()) return showToast('No embed code to copy!', 'error');
fallbackCopyText(embedOutput.value, 'Embed HTML code copied to clipboard!');
});
}
if (downloadHtmlBtn) downloadHtmlBtn.addEventListener('click', downloadHtmlPage);
updateUrlCount();
});
// Helper: Parse URL input entries
function parseUrlsFromInput() {
if (!urlInput || !urlInput.value) return [];
const rawText = urlInput.value;
if (!rawText.trim()) return [];
const regex = /(?:https?:)?\/\/[^\s,]+|vt\.tiktok\.com\/[a-zA-Z0-9_-]+/gi;
const matches = rawText.match(regex) || [];
return matches.map(url => {
let formatted = url.trim();
if (formatted.startsWith('//')) {
formatted = 'https:' + formatted;
} else if (!formatted.startsWith('http://') && !formatted.startsWith('https://')) {
formatted = 'https://' + formatted;
}
return formatted;
});
}
function updateUrlCount() {
if (!urlCounter) return;
const urls = parseUrlsFromInput();
urlCounter.textContent = `${urls.length} URL${urls.length === 1 ? '' : 's'} detected`;
}
// Show toast notification
function showToast(message, type = 'info') {
const container = document.getElementById('toastContainer');
if (!container) return;
const toast = document.createElement('div');
toast.className = `pointer-events-auto flex items-center gap-3 px-4 py-3 rounded-xl shadow-2xl text-xs font-medium border transition-all duration-300 transform translate-y-2 opacity-0 ${
type === 'success' ? 'bg-emerald-950/90 border-emerald-800 text-emerald-200' :
type === 'error' ? 'bg-rose-950/90 border-rose-800 text-rose-200' :
'bg-slate-900/90 border-slate-700 text-slate-200'
}`;
const icon = type === 'success' ? 'fa-circle-check text-emerald-400' :
type === 'error' ? 'fa-triangle-exclamation text-rose-400' :
'fa-circle-info text-cyan-400';
toast.innerHTML = `<i class="fa-solid ${icon} text-base"></i><span>${message}</span>`;
container.appendChild(toast);
requestAnimationFrame(() => {
toast.classList.remove('translate-y-2', 'opacity-0');
});
setTimeout(() => {
toast.classList.add('opacity-0', 'translate-y-2');
setTimeout(() => toast.remove(), 300);
}, 3000);
}
function extractTikTokUrlFromText(text) {
if (!text) return null;
const videoPattern = /https?:\/\/(?:www\.)?tiktok\.com\/@[a-zA-Z0-9_.-]+\/video\/\d+/i;
const match = text.match(videoPattern);
if (match) return match[0];
const photoPattern = /https?:\/\/(?:www\.)?tiktok\.com\/@[a-zA-Z0-9_.-]+\/photo\/\d+/i;
const photoMatch = text.match(photoPattern);
if (photoMatch) return photoMatch[0];
return null;
}
// Server-based Unshortener Services Pipeline
async function resolveSingleUrl(shortUrl) {
const shortCodeMatch = shortUrl.match(/vt\.tiktok\.com\/([a-zA-Z0-9_-]+)/i);
const shortCode = shortCodeMatch ? shortCodeMatch[1] : null;
const serverServices = [
// Server API 1: Direct fetch via unshorten.me endpoint
async (url) => {
const response = await fetch(`https://unshorten.me/json/${encodeURIComponent(url)}`, { signal: AbortSignal.timeout(5000) });
if (response.ok) {
const data = await response.json();
if (data && data.resolved_url && data.resolved_url.includes('tiktok.com')) {
return data.resolved_url;
}
}
return null;
},
// Server API 2: AllOrigins relay for TikTok oEmbed API
async (url) => {
const target = `https://www.tiktok.com/oembed?url=${encodeURIComponent(url)}`;
const response = await fetch(`https://api.allorigins.win/get?url=${encodeURIComponent(target)}`, { signal: AbortSignal.timeout(5000) });
if (response.ok) {
const data = await response.json();
if (data && data.contents) {
const parsed = extractTikTokUrlFromText(data.contents);
if (parsed) return parsed;
}
}
return null;
},
// Server API 3: CORS Proxy relay query
async (url) => {
const target = `https://www.tiktok.com/oembed?url=${encodeURIComponent(url)}`;
const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(target)}`, { signal: AbortSignal.timeout(5000) });
if (response.ok) {
const text = await response.text();
const parsed = extractTikTokUrlFromText(text);
if (parsed) return parsed;
}
return null;
}
];
for (const service of serverServices) {
try {
const result = await service(shortUrl);
if (result && result.includes('tiktok.com')) {
return { success: true, url: result };
}
} catch (e) {
// Try next resolver strategy
}
}
// Fallback: If network APIs are restricted, build canonical path using shortcode
if (shortCode) {
return { success: true, url: `https://www.tiktok.com/t/${shortCode}/` };
}
return { success: false, url: shortUrl };
}
// Clean tracking query parameters
function cleanTikTokUrl(url) {
try {
const parsed = new URL(url);
parsed.search = '';
return parsed.toString();
} catch (e) {
return url;
}
}
// Batch Processing Engine
async function startBatchResolution() {
const urls = parseUrlsFromInput();
if (urls.length === 0) {
showToast('Please enter at least one valid TikTok short link!', 'error');
return;
}
isProcessing = true;
shouldStop = false;
resolvedResults = [];
startBtn.disabled = true;
startBtn.classList.add('opacity-50', 'cursor-not-allowed');
stopBtn.disabled = false;
stopBtn.classList.remove('hidden');
stopBtn.innerHTML = `<i class="fa-solid fa-hand"></i> Stop`;
progressSection.classList.remove('hidden');
resultsTableBody.innerHTML = '';
resultBadge.textContent = `0 / ${urls.length}`;
let completed = 0;
const total = urls.length;
const concurrency = 2;
updateProgress(0, total);
const queue = [...urls];
const workers = Array(concurrency).fill(0).map(async () => {
while (queue.length > 0 && !shouldStop) {
const shortUrl = queue.shift();
const index = urls.indexOf(shortUrl) + 1;
const rowId = `row-${index}`;
appendPlaceholderRow(rowId, index, shortUrl);
const res = await resolveSingleUrl(shortUrl);
let finalUrl = res.url;
if (res.success && cleanParamsToggle.checked) {
finalUrl = cleanTikTokUrl(finalUrl);
}
const resultObj = {
id: index,
original: shortUrl,
full: finalUrl,
status: res.success ? 'Resolved' : 'Fallback Target',
isSuccess: res.success
};
resolvedResults.push(resultObj);
updateTableRow(rowId, resultObj);
completed++;
updateProgress(completed, total);
resultBadge.textContent = `${completed} / ${total}`;
}
});
await Promise.all(workers);
if (resolvedResults.length > 0) {
generateHtmlEmbeds();
}
isProcessing = false;
startBtn.disabled = false;
startBtn.classList.remove('opacity-50', 'cursor-not-allowed');
stopBtn.disabled = true;
stopBtn.classList.add('hidden');
if (shouldStop) {
progressStatusText.innerHTML = `<i class="fa-solid fa-circle-pause text-amber-400"></i> Stopped by user`;
showToast('Batch processing stopped.', 'info');
} else {
progressStatusText.innerHTML = `<i class="fa-solid fa-circle-check text-emerald-400"></i> Completed all items!`;
showToast(`Resolved ${completed} links & generated embeds!`, 'success');
}
}
// Update progress UI
function updateProgress(completed, total) {
const percent = total > 0 ? Math.round((completed / total) * 100) : 0;
progressBarInner.style.width = `${percent}%`;
progressPercentText.textContent = `${percent}%`;
progressCountText.textContent = `Resolved: ${completed} / ${total}`;
}
// Table UI Handlers
function appendPlaceholderRow(rowId, index, shortUrl) {
const emptyEl = document.getElementById('emptyRow');
if (emptyEl) emptyEl.remove();
const tr = document.createElement('tr');
tr.id = rowId;
tr.className = "hover:bg-slate-900/50 transition";
tr.innerHTML = `
<td class="py-3 px-4 text-center text-slate-500 font-sans">${index}</td>
<td class="py-3 px-4 text-slate-300 font-mono text-xs truncate max-w-[150px]" title="${shortUrl}">${shortUrl}</td>
<td class="py-3 px-4">
<div class="flex items-center gap-2 text-slate-400 text-xs font-sans">
<i class="fa-solid fa-spinner fa-spin text-cyan-400"></i>
<span>Resolving link via server API...</span>
</div>
</td>
<td class="py-3 px-4 text-center">
<span class="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-medium bg-cyan-950 text-cyan-300 border border-cyan-800/50 font-sans">Processing</span>
</td>
<td class="py-3 px-4 text-right font-sans">--</td>
`;
resultsTableBody.appendChild(tr);
}
function updateTableRow(rowId, result) {
const tr = document.getElementById(rowId);
if (!tr) return;
const badgeClass = result.isSuccess
? 'bg-emerald-950 text-emerald-300 border-emerald-800/60'
: 'bg-amber-950 text-amber-300 border-amber-800/60';
const safeUrl = result.full.replace(/'/g, "\\'");
tr.innerHTML = `
<td class="py-3 px-4 text-center text-slate-500 font-sans">${result.id}</td>
<td class="py-3 px-4 text-slate-300 font-mono text-xs truncate max-w-[150px]" title="${result.original}">${result.original}</td>
<td class="py-3 px-4">
<div class="flex items-center gap-2">
<input type="text" readonly value="${result.full}" onclick="this.select()" class="w-full bg-slate-900 border border-slate-800 rounded px-2.5 py-1 text-xs font-mono text-cyan-300 focus:outline-none focus:border-cyan-500/50 select-all">
</div>
</td>
<td class="py-3 px-4 text-center">
<span class="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-medium border ${badgeClass} font-sans">
${result.status}
</span>
</td>
<td class="py-3 px-4 text-right font-sans">
<div class="flex items-center justify-end gap-1.5">
<button onclick="copySingleUrl('${safeUrl}')" class="p-1.5 rounded-lg border border-slate-800 bg-slate-900 hover:bg-slate-800 text-slate-300 hover:text-white transition" title="Copy Raw Link">
<i class="fa-regular fa-copy text-xs"></i>
</button>
<a href="${result.full}" target="_blank" rel="noopener noreferrer" class="p-1.5 rounded-lg border border-slate-800 bg-slate-900 hover:bg-slate-800 text-slate-300 hover:text-white transition" title="Open Link">
<i class="fa-solid fa-arrow-up-right-from-square text-xs"></i>
</a>
</div>
</td>
`;
}
// Clipboard Actions
function copySingleUrl(url) {
fallbackCopyText(url, 'Link copied to clipboard!');
}
function copyAllResolved() {
if (resolvedResults.length === 0) {
showToast('No results available to copy!', 'error');
return;
}
const urlsText = resolvedResults.map(r => r.full).join('\n');
fallbackCopyText(urlsText, `Copied ${resolvedResults.length} resolved links!`);
}
function fallbackCopyText(text, successMsg) {
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(text)
.then(() => showToast(successMsg, 'success'))
.catch(() => executeExecCommandCopy(text, successMsg));
} else {
executeExecCommandCopy(text, successMsg);
}
}
function executeExecCommandCopy(text, successMessage) {
const textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.position = "fixed";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
showToast(successMessage, 'success');
} catch (err) {
showToast('Failed to copy text', 'error');
}
document.body.removeChild(textArea);
}
// Export Actions
function exportCSV() {
if (resolvedResults.length === 0) {
showToast('No results available to export!', 'error');
return;
}
const rows = [
['#', 'Short Link', 'Full TikTok URL', 'Status']
];
resolvedResults.forEach(r => {
rows.push([
r.id,
`"${r.original.replace(/"/g, '""')}"`,
`"${r.full.replace(/"/g, '""')}"`,
`"${r.status.replace(/"/g, '""')}"`
]);
});
const csvContent = "data:text/csv;charset=utf-8," + rows.map(e => e.join(",")).join("\n");
const encodedUri = encodeURI(csvContent);
const link = document.createElement("a");
link.setAttribute("href", encodedUri);
link.setAttribute("download", `tiktok_resolved_urls_${Date.now()}.csv`);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
showToast('Exported CSV file!', 'success');
}
function exportTXT() {
if (resolvedResults.length === 0) {
showToast('No results available to export!', 'error');
return;
}
const textContent = resolvedResults.map(r => r.full).join('\n');
const blob = new Blob([textContent], { type: "text/plain;charset=utf-8" });
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = `tiktok_resolved_urls_${Date.now()}.txt`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
showToast('Exported TXT file!', 'success');
}
function openAllTabs() {
if (resolvedResults.length === 0) {
showToast('No URLs to open!', 'error');
return;
}
let opened = 0;
resolvedResults.forEach(r => {
if (r.full) {
window.open(r.full, '_blank');
opened++;
}
});
showToast(`Opened ${opened} URLs in new tabs!`, 'success');
}
// Helper: Video ID Extractor
function extractVideoId(url) {
const match = url.match(/\/(?:video|photo)\/(\d+)/i);
return match ? match[1] : '';
}
// HTML Embed Code Generator & Live Preview Engine
function generateHtmlEmbeds() {
if (resolvedResults.length === 0) return;
const type = embedTypeSelect.value;
let code = '';
let previewHtml = '';
previewCountBadge.textContent = `${resolvedResults.length} Video${resolvedResults.length === 1 ? '' : 's'}`;
if (type === 'official') {
code = resolvedResults.map(r => {
const videoId = extractVideoId(r.full);
return `<blockquote class="tiktok-embed" cite="${r.full}" data-video-id="${videoId}" style="max-width: 605px; min-width: 325px;">\n <section>\n <a target="_blank" title="Watch on TikTok" href="${r.full}">${r.full}</a>\n </section>\n</blockquote>`;
}).join('\n\n');
code += `\n<script async src="https://www.tiktok.com/embed.js"><\/script>`;
previewHtml = resolvedResults.map(r => {
const videoId = extractVideoId(r.full);
return `<div class="w-full flex justify-center bg-slate-900/50 p-2 rounded-xl border border-slate-800">
<blockquote class="tiktok-embed" cite="${r.full}" data-video-id="${videoId}" style="max-width: 100%; min-width: 280px;">
<section><a target="_blank" href="${r.full}">${r.full}</a></section>
</blockquote>
</div>`;
}).join('');
} else if (type === 'iframe') {
code = resolvedResults.map(r => {
const videoId = extractVideoId(r.full);
const embedUrl = videoId ? `https://www.tiktok.com/embed/v2/${videoId}` : r.full;
return `<iframe src="${embedUrl}" width="100%" height="580" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen style="border-radius: 12px; max-width: 325px;"></iframe>`;
}).join('\n\n');
previewHtml = resolvedResults.map(r => {
const videoId = extractVideoId(r.full);
const embedUrl = videoId ? `https://www.tiktok.com/embed/v2/${videoId}` : r.full;
return `<div class="w-full flex justify-center bg-slate-900/50 p-2 rounded-xl border border-slate-800">
<iframe src="${embedUrl}" width="100%" height="450" frameborder="0" allowfullscreen style="border-radius: 12px; max-width: 325px;"></iframe>
</div>`;
}).join('');
} else if (type === 'gridPage') {
const embedsList = resolvedResults.map(r => {
const videoId = extractVideoId(r.full);
return ` <blockquote class="tiktok-embed" cite="${r.full}" data-video-id="${videoId}" style="max-width: 605px; min-width: 325px;">
<section><a target="_blank" href="${r.full}">${r.full}</a></section>
</blockquote>`;
}).join('\n');
code = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TikTok Video Gallery</title>
<style>
body { font-family: sans-serif; background: #0b0f17; color: #fff; margin: 0; padding: 2rem; }
.gallery { display: flex; flex-wrap: wrap; gap: 2rem; justify-content: center; }
</style>
</head>
<body>
<div class="gallery">
${embedsList}
</div>
<script async src="https://www.tiktok.com/embed.js"><\/script>
</body>
</html>`;
previewHtml = `<div class="w-full space-y-4">
<p class="text-xs text-slate-400 italic mb-2">Standalone HTML Gallery View</p>
${resolvedResults.map(r => {
const videoId = extractVideoId(r.full);
return `<div class="w-full flex justify-center bg-slate-900/50 p-2 rounded-xl border border-slate-800">
<blockquote class="tiktok-embed" cite="${r.full}" data-video-id="${videoId}" style="max-width: 100%; min-width: 280px;">
<section><a target="_blank" href="${r.full}">${r.full}</a></section>
</blockquote>
</div>`;
}).join('')}
</div>`;
}
embedOutput.value = code;
embedPreviewContainer.innerHTML = previewHtml;
// Load official TikTok embed script if blockquotes are rendered
if (type === 'official' || type === 'gridPage') {
const existingScript = document.getElementById('tiktokEmbedScript');
if (existingScript) existingScript.remove();
const script = document.createElement('script');
script.id = 'tiktokEmbedScript';
script.src = 'https://www.tiktok.com/embed.js';
script.async = true;
document.body.appendChild(script);
}
}
// Action: Download Complete Standalone HTML Page
function downloadHtmlPage() {
if (!embedOutput.value.trim()) {
showToast('No embed code generated yet!', 'error');
return;
}
const type = embedTypeSelect.value;
let fullDoc = '';
if (type === 'gridPage') {
fullDoc = embedOutput.value;
} else {
fullDoc = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TikTok Embed Gallery</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background-color: #0d1117; color: #f1f5f9; padding: 2rem; margin: 0; }
.container { max-width: 1200px; margin: 0 auto; display: flex; flex-wrap: wrap; gap: 1.5rem; justify-content: center; }
</style>
</head>
<body>
<div class="container">
${embedOutput.value}
</div>
</body>
</html>`;
}
const blob = new Blob([fullDoc], { type: "text/html;charset=utf-8" });
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = `tiktok_embeds_${Date.now()}.html`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
showToast('Downloaded HTML page!', 'success');
}
</script>
</body>
</html>
Comments