Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save klydeinside/c2c4406b0c8b3af86331684a92240267 to your computer and use it in GitHub Desktop.

Select an option

Save klydeinside/c2c4406b0c8b3af86331684a92240267 to your computer and use it in GitHub Desktop.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>SIMKL Share Card</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<style>
body {
margin:0;
background:#111;
font-family:Arial,sans-serif;
color:white;
}
.card {
width:1200px;
height:630px;
margin:40px auto;
position:relative;
overflow:hidden;
border-radius:25px;
background:#181818;
box-shadow:0 20px 60px rgba(0,0,0,.6);
}
.backdrop {
position:absolute;
inset:0;
background-size:cover;
background-position:center;
}
.overlay {
position:absolute;
inset:0;
background:
linear-gradient(
90deg,
rgba(0,0,0,.96),
rgba(0,0,0,.65)
);
}
.content {
position:absolute;
inset:0;
display:flex;
align-items:center;
padding:50px;
}
.poster {
width:260px;
height:390px;
object-fit:cover;
border-radius:20px;
background:#333;
box-shadow:0 15px 40px rgba(0,0,0,.5);
}
.info {
margin-left:45px;
max-width:720px;
text-align:center;
}
.title-row {
display:flex;
align-items:center;
justify-content:center;
gap:20px;
flex-wrap:wrap;
}
.title {
font-size:65px;
font-weight:900;
}
.status {
display:inline-block;
padding:10px 22px;
border-radius:40px;
background:rgba(255,255,255,.15);
backdrop-filter:blur(12px);
border:1px solid rgba(255,255,255,.25);
font-size:20px;
box-shadow:0 0 20px rgba(80,200,120,.25);
}
.status-row {
margin-top:15px;
display:flex;
justify-content:center;
}
.progress {
margin-top:25px;
display:flex;
justify-content:center;
gap:12px;
flex-wrap:wrap;
}
.info-pill {
padding:10px 18px;
border-radius:30px;
background:rgba(255,255,255,.15);
backdrop-filter:blur(12px);
border:1px solid rgba(255,255,255,.25);
font-size:22px;
}
.details {
margin-top:18px;
display:flex;
justify-content:center;
gap:10px;
flex-wrap:wrap;
}
.detail-pill {
padding:8px 18px;
border-radius:30px;
background:rgba(255,255,255,.15);
backdrop-filter:blur(12px);
border:1px solid rgba(255,255,255,.25);
font-size:18px;
}
.network-pill {
padding:8px 16px;
display:flex;
align-items:center;
justify-content:center;
}
.network-logo {
height:22px;
max-width:120px;
object-fit:contain;
}
.ratings {
display:flex;
justify-content:center;
gap:12px;
margin-top:20px;
flex-wrap:wrap;
}
.rating {
padding:10px 16px;
border-radius:30px;
background:rgba(255,255,255,.15);
backdrop-filter:blur(12px);
border:1px solid rgba(255,255,255,.25);
font-size:18px;
}
.overview {
margin-top:25px;
color:#ddd;
font-size:22px;
line-height:1.4;
}
.brand {
margin-top:25px;
opacity:.2;
font-family:"Ink Free", cursive;
font-size:28px;
letter-spacing:1px;
}
#saveCard {
position:fixed;
top:20px;
right:20px;
z-index:99999;
padding:12px 22px;
border-radius:30px;
border:1px solid rgba(255,255,255,.3);
background:rgba(255,255,255,.15);
color:white;
cursor:pointer;
font-size:16px;
backdrop-filter:blur(12px);
}
</style>
</head>
<body>
<button id="saveCard">
🔗 Share Card
</button>
<div class="card">
<div class="backdrop" id="backdrop"></div>
<div class="overlay"></div>
<div class="content">
<img class="poster" id="poster">
<div class="info">
<div class="title-row">
<div class="title" id="title">
Loading...
</div>
</div>
<div class="status-row">
<div class="status" id="status"></div>
</div>
<div class="progress" id="progress"></div>
<div class="details" id="details"></div>
<div class="ratings">
<div class="rating" id="simkl">
🟢 SIMKL --
</div>
<div class="rating" id="tmdb">
🎬 TMDB --
</div>
<div class="rating" id="imdb">
⭐ IMDb --
</div>
<div class="rating" id="trakt">
🔵 Trakt --
</div>
</div>
<div class="overview" id="overview"></div>
<div class="brand">
KLYDE × SIMKL
</div>
</div>
</div>
</div>
<script>
const TMDB_API_KEY = "59dda053896dfdcc5a98b5fec8d65017";
const WORKER_URL = "https://simkl.klyde.workers.dev/";
const params =
new URLSearchParams(location.search);
const raw =
params.get("data");
let cardReady = false;
async function loadCard(){
const item =
JSON.parse(
decodeURIComponent(raw)
);
const show =
item.show;
document.getElementById("title").textContent =
`${show.title} (${show.year})`;
let statusText=item.status;
if(item.status==="watching")
statusText="▶ Currently Watching";
if(item.status==="completed")
statusText="✓ Completed Watching";
if(item.status==="plantowatch")
statusText="+ Plan To Watch";
if(item.status==="dropped")
statusText="× Dropped";
document.getElementById("status").textContent =
statusText;
document.getElementById("progress").innerHTML =
`
<div class="info-pill">
Season ${item.seasons?.[0]?.number || 1} • ${item.last_watched}
</div>
<div class="info-pill">
Watched ${item.watched_episodes_count}/${item.total_episodes_count} Episodes
</div>
`;
const response =
await fetch(
`https://api.themoviedb.org/3/tv/${show.ids.tmdb}?api_key=${TMDB_API_KEY}`
);
const tmdb =
await response.json();
document.getElementById("details").innerHTML =
`
${tmdb.networks?.[0]?.logo_path ?
`
<div class="detail-pill network-pill">
<img class="network-logo"
src="https://images.weserv.nl/?url=${encodeURIComponent(
"https://image.tmdb.org/t/p/original" + tmdb.networks[0].logo_path
)}">
</div>
`
:
item.network ?
`
<div class="detail-pill">
${item.network}
</div>
`
:""
}
${item.genres ?
item.genres.slice(0,4).map(g =>
`<div class="detail-pill">${g}</div>`
).join("")
:""}
`;
if(tmdb.poster_path){
const poster =
`https://image.tmdb.org/t/p/w500${tmdb.poster_path}`;
document.getElementById("poster").src =
`https://images.weserv.nl/?url=${encodeURIComponent(poster)}`;
}
if(tmdb.backdrop_path){
const backdrop =
`https://image.tmdb.org/t/p/original${tmdb.backdrop_path}`;
document.getElementById("backdrop").style.backgroundImage =
`url(https://images.weserv.nl/?url=${encodeURIComponent(backdrop)})`;
}
document.getElementById("tmdb").textContent =
`🎬 TMDB ${tmdb.vote_average.toFixed(1)}`;
if(item.imdb_rating){
document.getElementById("imdb").textContent =
`⭐ IMDb ${item.imdb_rating}`;
}
if(item.simkl_rating){
document.getElementById("simkl").textContent =
`🟢 SIMKL ${item.simkl_rating}`;
}
if(item.trakt_rating){
document.getElementById("trakt").textContent =
`🔵 Trakt ${item.trakt_rating}`;
}
document.getElementById("overview").textContent =
tmdb.overview || "";
setTimeout(()=>{
cardReady=true;
},1500);
}
async function convertImagesForCanvas(){
const images =
document.querySelectorAll(".card img");
for(const img of images){
try{
const response =
await fetch(img.src);
const blob =
await response.blob();
const reader =
new FileReader();
await new Promise(resolve=>{
reader.onloadend = ()=>{
img.src =
reader.result;
resolve();
};
reader.readAsDataURL(blob);
});
}
catch(e){
console.log(
"Image conversion failed",
e
);
}
}
const backdrop =
document.querySelector(".backdrop");
const bg =
getComputedStyle(backdrop).backgroundImage;
if(bg && bg !== "none"){
const url =
bg.replace(/^url\(["']?/, "")
.replace(/["']?\)$/, "");
try{
const response =
await fetch(url);
const blob =
await response.blob();
const reader =
new FileReader();
await new Promise(resolve=>{
reader.onloadend = ()=>{
backdrop.style.backgroundImage =
`url(${reader.result})`;
resolve();
};
reader.readAsDataURL(blob);
});
}
catch(e){
console.log(
"Backdrop conversion failed",
e
);
}
}
}
async function createShareCard(){
let facebookWindow = null;
if(
new URLSearchParams(location.search).get("autoshare") === "1"
){
facebookWindow = window.open(
"about:blank",
"_blank"
);
}
if(!cardReady){
alert(
"Card still loading. Try again in a second."
);
return;
}
const IMGBB_API_KEY =
"0733071f1d8d4150f6d2cfd13aaf954f";
const button =
document.getElementById("saveCard");
button.textContent =
"⏳ Preparing...";
await convertImagesForCanvas();
const card =
document.querySelector(".card");
const canvas =
await html2canvas(card,{
scale:2,
useCORS:false,
allowTaint:false,
backgroundColor:"#181818"
});
button.textContent =
"⏳ Uploading...";
const base64 =
canvas.toDataURL("image/jpeg", 0.95)
.split(",")[1];
const form =
new FormData();
form.append(
"image",
base64
);
try{
const response =
await fetch(
`https://api.imgbb.com/1/upload?key=${IMGBB_API_KEY}`,
{
method:"POST",
body:form
}
);
const result =
await response.json();
if(result.success){
const imgUrl =
result.data.url;
// Build the worker share link — carries both the SIMKL
// data (for title/description) and the rendered PNG url
// (for og:image), so this is the link that actually goes
// on Facebook.
const shareURL =
`${WORKER_URL}?data=${encodeURIComponent(raw)}&img=${encodeURIComponent(imgUrl)}`;
await navigator.clipboard.writeText(shareURL);
window.open(
`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(shareURL)}`,
"facebookShare",
"width=700,height=600,scrollbars=yes,resizable=yes"
);
console.log(
"IMG URL:",
imgUrl
);
console.log(
"SHARE URL:",
shareURL
);
button.textContent =
"✅ URL Copied!";
if(facebookWindow){
facebookWindow.location.href =
`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(shareURL)}`;
}
else{
window.open(
`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(shareURL)}`,
"_blank"
);
}
}
else{
throw new Error(
"ImgBB upload failed"
);
}
}
catch(error){
console.error(
error
);
button.textContent =
"❌ Upload Failed";
}
setTimeout(()=>{
button.textContent =
"🔗 Share Card";
},3000);
}
document.getElementById("saveCard").onclick =
createShareCard;
loadCard();
const auto =
new URLSearchParams(location.search).get("autoshare");
if(auto==="1"){
const check=setInterval(()=>{
if(cardReady){
clearInterval(check);
createShareCard();
}
},250);
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment