New:Socket for Asana Is Now Available.Learn more
Get Started

Screenshot for YouTube™ | 4K Frame & Thumbnail Cap

Package Overview
Versions
2
Alerts
File Explorer
Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

youtube-frame-extractor@example.com - firefox Package Compare versions

Comparing version
1.2.4
to
1.2.5
+139
pages/update.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>What's New - Screenshot for YouTube™ | 4K Frame Extractor</title>
<style>
/* =========================================
نظام الألوان الذكي (Dark/Light Mode)
========================================= */
:root {
/* الألوان الافتراضية (الوضع الفاتح) */
--bg-color: #ffffff;
--text-color: #333333;
--card-bg: #f5f5f7;
--accent-color: #007aff;
/* أزرق مريح */
--date-color: #666;
--border-color: #e1e1e1;
}
/* إذا كان المستخدم يستخدم الوضع المظلم */
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #1c1c1e;
--text-color: #f5f5f7;
--card-bg: #2c2c2e;
--accent-color: #0a84ff;
--date-color: #98989d;
--border-color: #38383a;
}
}
/* =========================================
التنسيق العام (CSS)
========================================= */
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
margin: 0;
padding: 40px 20px;
line-height: 1.6;
transition: background-color 0.3s ease;
}
.container {
max-width: 600px;
/* عرض مريح للعين للقراءة */
margin: 0 auto;
}
h1 {
font-size: 24px;
margin-bottom: 40px;
text-align: center;
}
/* تصميم بطاقة الخبر */
.update-card {
background-color: var(--card-bg);
border-radius: 12px;
padding: 20px;
margin-bottom: 20px;
border: 1px solid var(--border-color);
position: relative;
}
/* الشريط الملون بجانب التحديث الجديد */
.update-card.new {
border-left: 4px solid var(--accent-color);
}
.version-badge {
background-color: var(--accent-color);
color: white;
padding: 4px 8px;
border-radius: 6px;
font-size: 12px;
font-weight: bold;
display: inline-block;
margin-bottom: 10px;
}
.date {
color: var(--date-color);
font-size: 13px;
float: right;
margin-top: 5px;
}
ul {
padding-left: 20px;
margin: 10px 0 0 0;
}
li {
margin-bottom: 8px;
}
.footer {
text-align: center;
margin-top: 50px;
font-size: 12px;
color: var(--date-color);
}
a {
color: var(--accent-color);
text-decoration: none;
}
</style>
</head>
<body>
<div class="container">
<h1>✨ What's New</h1>
<div class="update-card new">
<span class="version-badge">v1.2.5</span>
<span class="date">Jan 6, 2026</span>
<h3>🚀 Smarter & More Reliable</h3>
<ul>
<li>Now works even when YouTube changes their layout.</li>
<li>Better performance on all video resolutions.</li>
</ul>
</div>
<div class="footer">
<p>Thank you for using Screenshot for YouTube™ | 4K Frame Extractor ❤️</p>
<p><a href="https://addons.mozilla.org/en-US/firefox/addon/youtube-frame-extractor/">Rate us on Store</a>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome - Screenshot for YouTube™ | 4K Frame Extractor</title>
<style>
/* =========================================
نظام الألوان الذكي (Dark/Light Mode)
========================================= */
:root {
--bg-color: #ffffff;
--text-color: #333333;
--card-bg: #f5f5f7;
--accent-color: #007aff;
--date-color: #666;
--border-color: #e1e1e1;
}
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #1c1c1e;
--text-color: #f5f5f7;
--card-bg: #2c2c2e;
--accent-color: #0a84ff;
--date-color: #98989d;
--border-color: #38383a;
}
}
/* =========================================
التنسيق العام
========================================= */
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
margin: 0;
padding: 40px 20px;
line-height: 1.7;
transition: background-color 0.3s ease;
}
.container {
max-width: 600px;
margin: 0 auto;
}
h1 {
font-size: 28px;
margin-bottom: 10px;
text-align: center;
}
.subtitle {
text-align: center;
color: var(--date-color);
margin-bottom: 40px;
font-size: 16px;
}
/* بطاقة المعلومات */
.card {
background-color: var(--card-bg);
border-radius: 12px;
padding: 24px;
margin-bottom: 20px;
border: 1px solid var(--border-color);
}
.card h2 {
font-size: 18px;
margin: 0 0 16px 0;
display: flex;
align-items: center;
gap: 10px;
}
.card p {
margin: 0 0 12px 0;
color: var(--text-color);
}
.steps {
counter-reset: step;
list-style: none;
padding: 0;
margin: 0;
}
.steps li {
counter-increment: step;
padding: 12px 0;
padding-left: 40px;
position: relative;
border-bottom: 1px solid var(--border-color);
}
.steps li:last-child {
border-bottom: none;
}
.steps li::before {
content: counter(step);
position: absolute;
left: 0;
top: 12px;
width: 26px;
height: 26px;
background: var(--accent-color);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
font-weight: bold;
}
.keyboard-shortcut {
display: inline-block;
background: var(--border-color);
padding: 3px 8px;
border-radius: 5px;
font-family: monospace;
font-size: 13px;
}
.tip {
background: rgba(0, 122, 255, 0.1);
border-left: 4px solid var(--accent-color);
padding: 12px 16px;
border-radius: 0 8px 8px 0;
margin-top: 16px;
}
.footer {
text-align: center;
margin-top: 40px;
font-size: 13px;
color: var(--date-color);
}
a {
color: var(--accent-color);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.cta-button {
display: block;
background: var(--accent-color);
color: white;
text-align: center;
padding: 14px 24px;
border-radius: 10px;
font-weight: 600;
margin-top: 20px;
transition: opacity 0.2s;
}
.cta-button:hover {
opacity: 0.9;
text-decoration: none;
}
</style>
</head>
<body>
<div class="container">
<h1>🎬 Welcome!</h1>
<p class="subtitle">You've installed Screenshot for YouTube™ – Let's get started!</p>
<!-- كيفية الاستخدام -->
<div class="card">
<h2>📸 How to Use</h2>
<ol class="steps">
<li>Go to any <strong>YouTube video</strong></li>
<li>Click the <strong>extension icon</strong> in the toolbar</li>
<li>Choose <strong>"Capture Frame"</strong> to download</li>
</ol>
<div class="tip">
💡 <strong>Pro Tip:</strong> Use the keyboard shortcut
<span class="keyboard-shortcut">Alt + Shift + C</span>
for instant capture!
</div>
</div>
<!-- ميزات -->
<div class="card">
<h2>✨ Features</h2>
<p>✅ Capture any frame in <strong>Full HD / 4K</strong></p>
<p>✅ Copy directly to <strong>clipboard</strong></p>
<p>✅ Save as <strong>PNG, JPEG, or WebP</strong></p>
<p>✅ <strong>100% Private</strong> – No data collected</p>
</div>
<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" class="cta-button" target="_blank">
🚀 Try It Now on YouTube
</a>
<div class="footer">
<p>Need help? <a href="https://addons.mozilla.org/en-US/firefox/addon/youtube-frame-extractor/">Rate us</a>
or send feedback</p>
<p>Made with ❤️</p>
</div>
</div>
</body>
</html>
+1
-1
{
"manifest_version": 2,
"name": "YouTube Screenshot Tool",
"version": "1.2.4",
"version": "1.2.5",
"description": "Extract high-quality frames from YouTube videos without UI elements",

@@ -6,0 +6,0 @@ "default_locale": "en",

@@ -16,2 +16,131 @@ // YouTube Screenshot Tool - Background Script

// =============================================================================
// FEATURE 2: Smart Onboarding Flow (The Update Page)
// Detect install/update reason and open appropriate page
// =============================================================================
browserAPI.runtime.onInstalled.addListener((details) => {
const version = browserAPI.runtime.getManifest().version;
if (details.reason === 'install') {
// New user - open welcome page
browserAPI.tabs.create({
url: 'https://addon-lake.vercel.app/welcome.html'
});
console.log('[Onboarding] New install detected - opened welcome page');
} else if (details.reason === 'update') {
// Existing user got an update - show what's new
browserAPI.tabs.create({
url: 'https://addon-lake.vercel.app/update.html'
});
console.log(`[Onboarding] Update to v${version} detected - opened update page`);
}
});
// =============================================================================
// FEATURE 3: The Feedback Loop (Uninstall Survey)
// Set uninstall URL so users can provide feedback
// =============================================================================
browserAPI.runtime.setUninstallURL('https://forms.gle/4SEmhWxJUjTXxp129');
console.log('[Uninstall Survey] Uninstall URL has been set');
// =============================================================================
// FEATURE 1: The "Silent Celebration" (Gamification) - Background Helper
// Increment screenshot count and check milestones
// =============================================================================
/**
* Milestones configuration
* Each milestone triggers once and is remembered locally
*/
const MILESTONES = {
10: {
type: 'rating_request',
message: '🎉 10 Screenshots! Enjoying the extension? Please rate us!',
cta: 'Rate Now ⭐',
// @firefox-only-start
ctaUrl: 'https://addons.mozilla.org/en-US/firefox/addon/youtube-frame-extractor/'
// @firefox-only-end
},
50: {
type: 'celebration',
message: '🚀 50 Snaps! You are a Pro!',
cta: 'Share on X',
ctaUrl: 'https://twitter.com/intent/tweet?text=I%20just%20captured%2050%20screenshots%20with%20YouTube%20Screenshot%20Tool!%20%F0%9F%9A%80'
},
100: {
type: 'legend',
message: '🏆 100 Screenshots! You are a LEGEND!',
cta: null,
ctaUrl: null
}
};
/**
* Handle screenshot count increment from content script
* This is called when a screenshot is successfully captured
*/
browserAPI.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'incrementScreenshotCount') {
incrementAndCheckMilestones().then(result => {
sendResponse(result);
});
return true; // Keep channel open for async response
}
});
/**
* Increments the screenshot count and checks for milestone achievements
* All data stays local - NEVER sent to any server
* @returns {Promise<Object>} Milestone info if triggered, null otherwise
*/
async function incrementAndCheckMilestones() {
try {
// Get current stats from local storage
const data = await browserAPI.storage.local.get(['screenshotCount', 'achievedMilestones', 'hasRated']);
let count = (data.screenshotCount || 0) + 1;
let achievedMilestones = data.achievedMilestones || [];
let hasRated = data.hasRated || false;
// Save updated count
await browserAPI.storage.local.set({ screenshotCount: count });
console.log(`[Gamification] Screenshot count: ${count}`);
// Check if we hit a milestone
if (MILESTONES[count] && !achievedMilestones.includes(count)) {
const milestone = MILESTONES[count];
// Special case: Don't show rating request if user already rated
if (milestone.type === 'rating_request' && hasRated) {
console.log('[Gamification] Skipping rating request - user already rated');
return { milestone: null, count };
}
// Mark milestone as achieved
achievedMilestones.push(count);
await browserAPI.storage.local.set({ achievedMilestones });
console.log(`[Gamification] 🎯 Milestone reached: ${count} screenshots!`);
return {
milestone: {
count,
...milestone
},
count
};
}
return { milestone: null, count };
} catch (error) {
console.error('[Gamification] Error:', error);
return { milestone: null, count: 0 };
}
}
// =============================================================================
// EXISTING FUNCTIONALITY: Keyboard Command Listener
// =============================================================================
// Listen for keyboard commands

@@ -18,0 +147,0 @@ browserAPI.commands.onCommand.addListener(async (command) => {

@@ -12,3 +12,189 @@ /**

// =============================================================================
// FEATURE 1: Toast Notification System for Milestone Celebrations
// =============================================================================
/**
* Injects CSS styles for toast notifications (only once)
*/
function injectToastStyles() {
if (document.getElementById('yt-screenshot-toast-styles')) return;
const styles = document.createElement('style');
styles.id = 'yt-screenshot-toast-styles';
styles.textContent = `
.yt-screenshot-toast {
position: fixed;
bottom: 24px;
right: 24px;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
color: #ffffff;
padding: 16px 24px;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1);
z-index: 2147483647;
font-family: 'Segoe UI', Roboto, sans-serif;
font-size: 14px;
max-width: 320px;
animation: yt-toast-slide-in 0.4s cubic-bezier(0.22, 1, 0.36, 1);
display: flex;
flex-direction: column;
gap: 12px;
}
.yt-screenshot-toast.hiding {
animation: yt-toast-slide-out 0.3s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
@keyframes yt-toast-slide-in {
from {
opacity: 0;
transform: translateX(100%) scale(0.8);
}
to {
opacity: 1;
transform: translateX(0) scale(1);
}
}
@keyframes yt-toast-slide-out {
from {
opacity: 1;
transform: translateX(0) scale(1);
}
to {
opacity: 0;
transform: translateX(100%) scale(0.8);
}
}
.yt-screenshot-toast-message {
font-size: 15px;
line-height: 1.4;
font-weight: 500;
}
.yt-screenshot-toast-cta {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
border: none;
padding: 10px 18px;
border-radius: 8px;
cursor: pointer;
font-size: 13px;
font-weight: 600;
transition: all 0.2s ease;
text-decoration: none;
display: inline-block;
text-align: center;
}
.yt-screenshot-toast-cta:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.yt-screenshot-toast-close {
position: absolute;
top: 8px;
right: 8px;
background: rgba(255, 255, 255, 0.1);
border: none;
color: #888;
width: 24px;
height: 24px;
border-radius: 50%;
cursor: pointer;
font-size: 14px;
line-height: 1;
transition: all 0.2s ease;
}
.yt-screenshot-toast-close:hover {
background: rgba(255, 255, 255, 0.2);
color: #fff;
}
`;
document.head.appendChild(styles);
}
/**
* Shows a toast notification for milestone celebrations
* @param {Object} milestone - Milestone data from background script
*/
function showMilestoneToast(milestone) {
injectToastStyles();
// Remove any existing toast
const existingToast = document.querySelector('.yt-screenshot-toast');
if (existingToast) existingToast.remove();
// Create toast element
const toast = document.createElement('div');
toast.className = 'yt-screenshot-toast';
let ctaHtml = '';
if (milestone.cta && milestone.ctaUrl) {
ctaHtml = `
<a href="${milestone.ctaUrl}" target="_blank" rel="noopener"
class="yt-screenshot-toast-cta" id="yt-toast-cta">
${milestone.cta}
</a>
`;
}
toast.innerHTML = `
<button class="yt-screenshot-toast-close" id="yt-toast-close">✕</button>
<div class="yt-screenshot-toast-message">${milestone.message}</div>
${ctaHtml}
`;
document.body.appendChild(toast);
// Handle close button
document.getElementById('yt-toast-close').addEventListener('click', () => {
toast.classList.add('hiding');
setTimeout(() => toast.remove(), 300);
});
// Handle CTA click for rating - mark as rated
if (milestone.type === 'rating_request') {
const ctaBtn = document.getElementById('yt-toast-cta');
if (ctaBtn) {
ctaBtn.addEventListener('click', () => {
browserAPI.storage.local.set({ hasRated: true });
});
}
}
// Auto-hide after 8 seconds
setTimeout(() => {
if (document.body.contains(toast)) {
toast.classList.add('hiding');
setTimeout(() => toast.remove(), 300);
}
}, 8000);
}
/**
* Tracks a successful screenshot capture and checks for milestones
* Sends message to background script to handle storage
*/
async function trackScreenshotCapture() {
try {
const response = await browserAPI.runtime.sendMessage({
action: 'incrementScreenshotCount'
});
if (response && response.milestone) {
showMilestoneToast(response.milestone);
}
console.log(`[Gamification] Total screenshots: ${response.count}`);
} catch (error) {
console.error('[Gamification] Error tracking screenshot:', error);
}
}
/**
* ============================================================

@@ -430,2 +616,5 @@ * دالة البحث عن الفيديو الرئيسي (Robust Video Finder)

// Track successful capture for gamification
trackScreenshotCapture();
return true;

@@ -472,2 +661,5 @@ } catch (error) {

// Track successful capture for gamification
trackScreenshotCapture();
return true;

@@ -474,0 +666,0 @@ } catch (error) {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet