How to Create Video Streaming App and Website like Netflix using Google AI Studio for free

Here is a complete guide on how to Create Video Streaming App and Website like Netflix using Google AI Studio for free.

Prompt Used

# AI Prompt: Generate a Complete Netflix-Clone PWA

## Objective
Generate a complete, self-contained, Netflix-like video streaming Progressive Web App (PWA). The application must have separate user and admin interfaces, be built with vanilla JavaScript, HTML, CSS, and use Firebase for the backend and Vimeo for video hosting.

---

## 1. Core Technology Stack
- **Frontend**: Vanilla JavaScript (ES6+), HTML5, CSS3.
  - **Constraint**: No JS frameworks (React, Vue, Angular, etc.).
  - **Constraint**: All JavaScript and CSS must be embedded directly within their respective HTML files (`<script>` and `<style>` tags). Do not generate separate `.js` or `.css` files.
- **Backend**: Firebase
  - **Firebase Authentication**: Email/Password and Google Sign-in.
  - **Cloud Firestore**: Database for all metadata.
  - **Firebase Hosting**: For deployment.
- **Video Hosting**: Vimeo (Free Tier)
  - **Constraint**: Do not use the Vimeo API for uploads or data retrieval. All video links and thumbnail URLs are manually provided by an admin.
- **Image Hosting**: Any external image hosting service (e.g., Imgur).
  - **Constraint**: Thumbnail URLs are direct links to image files (`.jpg`, `.png`) and are manually provided by an admin.

---

## 2. Required Files to Generate
Generate the complete and commented code for the following six files:
1.  `index.html`: The main user-facing application for browsing and watching videos.
2.  `admin.html`: The admin panel for managing content.
3.  `firebase.json`: Configuration for Firebase Hosting.
4.  `firestore.rules`: Security rules for the Firestore database.
5.  `manifest.json`: The PWA manifest file.
6.  `service-worker.js`: The service worker for basic offline caching.
7.  `README.md`: Setup and deployment instructions.

---

## 3. `index.html` - User Interface Features
- **Authentication**:
  - Provide toggleable forms for user sign-up and sign-in with Email/Password.
  - Include a "Sign In with Google" button.
  - On successful sign-up, create a new document in the `users` Firestore collection for the user with the structure: `{ role: "user", watchlist: [] }`.
- **Role-Based Redirection**:
  - After login, check the user's `role` from their document in the `users` collection.
  - If `role` is `"admin"`, immediately redirect to `admin.html`.
  - If `role` is `"user"`, display the main app interface within `index.html`.
- **UI/UX**:
  - A mobile-first, responsive, dark-themed (Netflix-style) interface.
  - Header with the app title ("Streamify") and a "Logout" button.
  - A search bar to filter videos by title in real-time.
  - Display videos in a grid, grouped under their category names (e.g., `<h2>Action</h2>`).
- **Video Playback**:
  - Clicking a video thumbnail must open a full-screen modal overlay.
  - Inside the modal, embed the Vimeo player using an `<iframe>`. The `src` should be `https://player.vimeo.com/video/{vimeo_id}`.
  - The player URL should include parameters for autoplay, volume, and controls (`?autoplay=1&muted=0&controls=1`).
  - The modal must have a large, touch-friendly "X" button to close it and stop the video.
- **Watchlist**:
  - Each video card must have a "+" button to add it to the user's watchlist.
  - If a video is already in the watchlist, the button should show a checkmark ("✓").
  - The `watchlist` is an array of video document IDs stored in the user's document in the `users` collection.

---

## 4. `admin.html` - Admin Interface Features
- **Access Control**:
  - On page load, the script must immediately check the logged-in user's role from Firestore.
  - If the user is not authenticated or their `role` is not `"admin"`, immediately redirect them to `index.html`.
- **UI/UX**:
  - A simple, mobile-friendly dashboard for managing content.
  - Must include a "Logout" button.
- **Category Management**:
  - A form with an input field to add a new category.
  - A list of existing categories, with "Edit" and "Delete" buttons for each.
  - Categories are stored in the `categories` Firestore collection.
- **Video Management**:
  - A form to add/edit videos with the following fields:
    1.  `title` (text input)
    2.  `category` (a `<select>` dropdown populated dynamically from the `categories` collection).
    3.  `vimeo_link` (URL input).
    4.  `thumbnail_url` (URL input for the direct image link, e.g., from Imgur).
  - A list of all existing videos, with "Edit" and "Delete" buttons for each.
- **Vimeo Link Parsing**:
  - The JavaScript must include a function to parse the `vimeo_link` input.
  - It must extract the numerical `vimeo_id` from URLs like `https://vimeo.com/123456789` or `https://player.vimeo.com/video/123456789`.
  - Store only the extracted `vimeo_id` in Firestore.

---

## 5. Firebase and Data Structure
- **Firestore Collections Structure**:
  ```javascript
  // users/{userId}
  {
    role: "user" | "admin",
    watchlist: ["videoId1", "videoId2"]
  }

  // categories/{categoryId}
  {
    name: "Action"
  }

  // videos/{videoId}
  {
    title: "Sample Movie Title",
    category: "Action", // This must match a name in the categories collection
    vimeo_id: "123456789",
    thumbnail_url: "https://i.imgur.com/your-image.jpg"
  }

Firebase Rule

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /videos/{videoId} {
      allow read: if true;
      allow write: if request.auth != null && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == "admin";
    }
    match /categories/{categoryId} {
      allow read: if true;
      allow write: if request.auth != null && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == "admin";
    }
    match /users/{userId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

Codes (index.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>Streamify</title>
    <link rel="manifest" href="/manifest.json">
    <meta name="theme-color" content="#E50914">

    <!-- Embedded CSS -->
    <style>
        :root {
            --primary-color: #E50914;
            --dark-color: #141414;
            --light-color: #f4f4f4;
        }
        body {
            font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
            background: var(--dark-color);
            color: var(--light-color);
            margin: 0;
            padding: 0;
            overscroll-behavior-y: contain;
        }
        /* --- UTILITY --- */
        .hidden { display: none !important; }

        /* --- AUTH CONTAINER --- */
        #auth-container {
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            padding: 20px;
        }
        .auth-form {
            background: rgba(0,0,0,0.8);
            padding: 40px;
            border-radius: 8px;
            width: 100%;
            max-width: 400px;
            box-sizing: border-box;
        }
        .auth-form h1 { margin-top: 0; }
        .auth-form input {
            width: 100%;
            padding: 12px;
            margin-bottom: 15px;
            background: #333;
            border: none;
            border-radius: 4px;
            color: white;
            box-sizing: border-box;
        }
        .auth-form button {
            width: 100%;
            padding: 12px;
            background: var(--primary-color);
            border: none;
            border-radius: 4px;
            color: white;
            font-size: 16px;
            font-weight: bold;
            cursor: pointer;
            margin-bottom: 10px;
        }
        #google-signin-btn { background: #4285F4; }
        .auth-form p {
            margin-top: 20px;
            text-align: center;
        }
        .auth-form a {
            color: var(--primary-color);
            text-decoration: none;
            cursor: pointer;
        }
        #error-message {
            color: orange;
            text-align: center;
            margin-bottom: 10px;
        }

        /* --- MAIN APP CONTAINER --- */
        #app-container {
            padding: 15px;
        }
        header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }
        header h1 {
            color: var(--primary-color);
            margin: 0;
            font-size: 24px;
        }
        #logout-btn {
            background: var(--primary-color);
            color: white;
            border: none;
            padding: 8px 12px;
            border-radius: 4px;
            cursor: pointer;
        }

        /* --- SEARCH & FILTER --- */
        #search-filter-container {
            display: flex;
            flex-direction: column;
            gap: 15px;
            margin-bottom: 25px;
        }
        #search-input {
            width: 100%;
            padding: 10px;
            background: #333;
            border: 1px solid #555;
            border-radius: 4px;
            color: white;
            box-sizing: border-box;
        }

        /* --- VIDEO GRID --- */
        .category-section { margin-bottom: 30px; }
        .category-section h2 { margin-bottom: 15px; }
        .video-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
            gap: 15px;
        }
        .video-card {
            cursor: pointer;
            position: relative;
            overflow: hidden;
            border-radius: 4px;
            transition: transform 0.2s ease-in-out;
        }
        .video-card:active {
            transform: scale(0.95);
        }
        .video-card img {
            width: 100%;
            height: auto;
            display: block;
        }
        .video-card .overlay {
            position: absolute;
            bottom: 0;
            left: 0;
            right: 0;
            background: linear-gradient(to top, rgba(0,0,0,0.9), transparent);
            padding: 10px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .video-card .overlay p {
            margin: 0;
            font-size: 14px;
            flex-grow: 1;
        }
        .watchlist-btn {
            background: rgba(255, 255, 255, 0.2);
            border: 1px solid white;
            color: white;
            border-radius: 50%;
            width: 30px;
            height: 30px;
            font-size: 18px;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .watchlist-btn.in-watchlist {
            background: var(--primary-color);
            border-color: var(--primary-color);
        }

        /* --- VIDEO PLAYER MODAL (MODIFIED) --- */
        #video-player-modal {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.95);
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            z-index: 1000;
        }
        #video-player-container {
            width: 100%; /* Fill the width of the screen */
            max-width: 900px; /* Optional: cap the width on large screens */
            position: relative;
            /* This padding-bottom trick maintains a 16:9 aspect ratio */
            padding-bottom: 56.25%;
            height: 0;
        }
        #video-player-container iframe {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            border: none;
        }
        /* MODIFIED: Larger, more touch-friendly close button */
        #close-player-btn {
            position: absolute;
            top: 15px;
            right: 15px;
            background: rgba(0,0,0,0.5);
            color: white;
            border: 2px solid white;
            border-radius: 50%;
            width: 44px;
            height: 44px;
            font-size: 22px;
            font-weight: bold;
            cursor: pointer;
            z-index: 1001; /* Ensure it's on top of the iframe */
        }

        /* Media Queries for larger screens */
        @media (min-width: 600px) {
            #search-filter-container { flex-direction: row; }
            .video-grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
        }
    </style>
</head>
<body>

    <!-- Authentication -->
    <div id="auth-container">
        <div class="auth-form">
            <!-- Login Form -->
            <form id="login-form">
                <h1>Sign In</h1>
                <div id="error-message"></div>
                <input type="email" id="login-email" placeholder="Email" required>
                <input type="password" id="login-password" placeholder="Password" required>
                <button type="submit">Sign In</button>
                <button type="button" id="google-signin-btn">Sign In with Google</button>
                <p>New to Streamify? <a id="show-signup">Sign up now</a>.</p>
            </form>
            <!-- Signup Form -->
            <form id="signup-form" class="hidden">
                <h1>Sign Up</h1>
                <input type="email" id="signup-email" placeholder="Email" required>
                <input type="password" id="signup-password" placeholder="Password" required>
                <button type="submit">Sign Up</button>
                <p>Already have an account? <a id="show-login">Sign in</a>.</p>
            </form>
        </div>
    </div>

    <!-- Main App -->
    <div id="app-container" class="hidden">
        <header>
            <h1>Streamify</h1>
            <button id="logout-btn">Logout</button>
        </header>

        <div id="search-filter-container">
            <input type="search" id="search-input" placeholder="Search by title...">
        </div>
        
        <main id="video-content">
            <!-- Videos will be rendered here -->
        </main>
    </div>

    <!-- Video Player Modal -->
    <div id="video-player-modal" class="hidden">
        <button id="close-player-btn">X</button>
        <div id="video-player-container"></div>
    </div>

    <!-- Firebase SDKs -->
    <script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-app-compat.js"></script>
    <script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-auth-compat.js"></script>
    <script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore-compat.js"></script>

    <!-- Embedded JavaScript -->
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            // PASTE YOUR FIREBASE CONFIG HERE
            const firebaseConfig = {
          apiKey: "",
                authDomain: "",
                projectId: "",
                storageBucket: "",
                messagingSenderId: "",
                appId: ""
            };

            // Initialize Firebase
            firebase.initializeApp(firebaseConfig);
            const auth = firebase.auth();
            const db = firebase.firestore();
            const googleProvider = new firebase.auth.GoogleAuthProvider();

            // DOM Elements
            const authContainer = document.getElementById('auth-container');
            const appContainer = document.getElementById('app-container');
            const loginForm = document.getElementById('login-form');
            const signupForm = document.getElementById('signup-form');
            const showSignup = document.getElementById('show-signup');
            const showLogin = document.getElementById('show-login');
            const logoutBtn = document.getElementById('logout-btn');
            const googleSigninBtn = document.getElementById('google-signin-btn');
            const errorMessage = document.getElementById('error-message');
            const videoContent = document.getElementById('video-content');
            const searchInput = document.getElementById('search-input');
            const videoPlayerModal = document.getElementById('video-player-modal');
            const videoPlayerContainer = document.getElementById('video-player-container');
            const closePlayerBtn = document.getElementById('close-player-btn');

            let userWatchlist = [];

            // --- AUTHENTICATION ---
            auth.onAuthStateChanged(async user => {
                if (user) {
                    const userDoc = await db.collection('users').doc(user.uid).get();
                    if (userDoc.exists && userDoc.data().role === 'admin') {
                        window.location.href = 'admin.html';
                        return;
                    }
                    authContainer.classList.add('hidden');
                    appContainer.classList.remove('hidden');
                    userWatchlist = userDoc.exists ? userDoc.data().watchlist || [] : [];
                    loadVideos();
                } else {
                    authContainer.classList.remove('hidden');
                    appContainer.classList.add('hidden');
                }
            });

            // Toggle between login and signup forms
            showSignup.addEventListener('click', () => {
                loginForm.classList.add('hidden');
                signupForm.classList.remove('hidden');
                errorMessage.textContent = '';
            });
            showLogin.addEventListener('click', () => {
                signupForm.classList.add('hidden');
                loginForm.classList.remove('hidden');
                errorMessage.textContent = '';
            });
            
            // Handle Login
            loginForm.addEventListener('submit', e => {
                e.preventDefault();
                const email = document.getElementById('login-email').value;
                const password = document.getElementById('login-password').value;
                auth.signInWithEmailAndPassword(email, password)
                    .catch(error => { errorMessage.textContent = error.message; });
            });

            // Handle Signup
            signupForm.addEventListener('submit', e => {
                e.preventDefault();
                const email = document.getElementById('signup-email').value;
                const password = document.getElementById('signup-password').value;
                auth.createUserWithEmailAndPassword(email, password)
                    .then(userCredential => {
                        return db.collection('users').doc(userCredential.user.uid).set({
                            role: 'user', watchlist: []
                        });
                    })
                    .catch(error => { errorMessage.textContent = error.message; });
            });

            // Google Sign-In
            googleSigninBtn.addEventListener('click', () => {
                auth.signInWithPopup(googleProvider)
                    .then(async (result) => {
                        const user = result.user;
                        const userDocRef = db.collection('users').doc(user.uid);
                        const doc = await userDocRef.get();
                        if (!doc.exists) {
                            await userDocRef.set({ role: 'user', watchlist: [] });
                        }
                    }).catch(error => { errorMessage.textContent = error.message; });
            });

            // Handle Logout
            logoutBtn.addEventListener('click', () => auth.signOut());

            // --- VIDEO LOADING & DISPLAY ---
            async function loadVideos() {
                try {
                    const categoriesSnapshot = await db.collection('categories').get();
                    const videosSnapshot = await db.collection('videos').get();
                    const categories = categoriesSnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
                    const videos = videosSnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
                    renderVideos(videos, categories);
                    searchInput.addEventListener('input', (e) => {
                        const searchTerm = e.target.value.toLowerCase();
                        const filteredVideos = videos.filter(video => video.title.toLowerCase().includes(searchTerm));
                        renderVideos(filteredVideos, categories);
                    });
                } catch (error) {
                    console.error("Error loading videos: ", error);
                    videoContent.innerHTML = '<p>Could not load videos. Please try again later.</p>';
                }
            }

            function renderVideos(videos, categories) {
                videoContent.innerHTML = '';
                categories.forEach(category => {
                    const videosInCategory = videos.filter(video => video.category === category.name);
                    if (videosInCategory.length > 0) {
                        const categorySection = document.createElement('div');
                        categorySection.className = 'category-section';
                        categorySection.innerHTML = `<h2>${category.name}</h2>`;
                        const videoGrid = document.createElement('div');
                        videoGrid.className = 'video-grid';
                        videosInCategory.forEach(video => {
                            const isInWatchlist = userWatchlist.includes(video.id);
                            const videoCard = document.createElement('div');
                            videoCard.className = 'video-card';
                            videoCard.dataset.videoId = video.id;
                            videoCard.dataset.vimeoId = video.vimeo_id;
                            videoCard.innerHTML = `
                                <img src="${video.thumbnail_url}" alt="${video.title}">
                                <div class="overlay">
                                    <p>${video.title}</p>
                                    <button class="watchlist-btn ${isInWatchlist ? 'in-watchlist' : ''}" data-video-id="${video.id}">
                                        ${isInWatchlist ? '✓' : '+'}
                                    </button>
                                </div>`;
                            videoGrid.appendChild(videoCard);
                        });
                        categorySection.appendChild(videoGrid);
                        videoContent.appendChild(categorySection);
                    }
                });
            }

            // --- VIDEO PLAYER & WATCHLIST ---
            videoContent.addEventListener('click', e => {
                const target = e.target;
                if (target.classList.contains('watchlist-btn')) {
                    toggleWatchlist(target.dataset.videoId);
                } else {
                    const card = target.closest('.video-card');
                    if (card) {
                        playVideo(card.dataset.vimeoId);
                    }
                }
            });

            // MODIFIED: This function now uses a more robust Vimeo URL
            function playVideo(vimeoId) {
                const vimeoParams = `?autoplay=1&controls=1&muted=0&byline=0&portrait=0&title=0&dnt=1`;
                const vimeoUrl = `https://player.vimeo.com/video/${vimeoId}${vimeoParams}`;
                videoPlayerContainer.innerHTML = `<iframe src="${vimeoUrl}" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen></iframe>`;
                videoPlayerModal.classList.remove('hidden');
            }

            closePlayerBtn.addEventListener('click', () => {
                videoPlayerContainer.innerHTML = ''; // Stop the video by removing the iframe
                videoPlayerModal.classList.add('hidden');
            });

            async function toggleWatchlist(videoId) {
                const user = auth.currentUser;
                if (!user) return;
                const userDocRef = db.collection('users').doc(user.uid);
                const button = document.querySelector(`.watchlist-btn[data-video-id="${videoId}"]`);
                let updatedWatchlist;
                if (userWatchlist.includes(videoId)) {
                    updatedWatchlist = userWatchlist.filter(id => id !== videoId);
                } else {
                    updatedWatchlist = [...userWatchlist, videoId];
                }
                try {
                    await userDocRef.update({ watchlist: updatedWatchlist });
                    userWatchlist = updatedWatchlist;
                    const isInWatchlist = userWatchlist.includes(videoId);
                    button.textContent = isInWatchlist ? '✓' : '+';
                    button.classList.toggle('in-watchlist', isInWatchlist);
                } catch (error) {
                    console.error("Error updating watchlist: ", error);
                }
            }

            // PWA Service Worker
            if ('serviceWorker' in navigator) {
                window.addEventListener('load', () => {
                    navigator.serviceWorker.register('/service-worker.js')
                        .then(registration => console.log('ServiceWorker registration successful with scope: ', registration.scope))
                        .catch(err => console.log('ServiceWorker registration failed: ', err));
                });
            }
        });
    </script>
</body>
</html>

Code 2 (Admin.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Admin Panel</title>
    
    <!-- Embedded CSS -->
    <style>
        :root {
            --primary-color: #E50914;
            --dark-color: #141414;
            --light-color: #f4f4f4;
            --gray-color: #333;
        }
        body {
            font-family: Arial, sans-serif;
            background: var(--dark-color);
            color: var(--light-color);
            margin: 0;
            padding: 20px;
        }
        .hidden { display: none !important; }

        #admin-container {
            max-width: 800px;
            margin: 0 auto;
        }
        header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            border-bottom: 2px solid var(--primary-color);
            padding-bottom: 10px;
            margin-bottom: 30px;
        }
        header h1 { margin: 0; }
        #logout-btn {
            background: var(--primary-color); color: white; border: none;
            padding: 8px 12px; border-radius: 4px; cursor: pointer;
        }

        .admin-section {
            background: var(--gray-color);
            padding: 20px;
            border-radius: 8px;
            margin-bottom: 30px;
        }
        .admin-section h2 { margin-top: 0; }
        
        /* --- FORMS --- */
        form { display: flex; flex-direction: column; gap: 15px; }
        input, select, button {
            width: 100%;
            padding: 12px;
            border-radius: 4px;
            border: 1px solid #555;
            background: #444;
            color: white;
            box-sizing: border-box;
            font-size: 16px;
        }
        button {
            background: var(--primary-color);
            border: none;
            cursor: pointer;
            font-weight: bold;
        }
        button:hover { opacity: 0.9; }

        /* --- LISTS --- */
        ul { list-style: none; padding: 0; margin: 20px 0 0 0; }
        li {
            background: #222;
            padding: 15px;
            border-radius: 4px;
            margin-bottom: 10px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
            gap: 10px;
        }
        li span { flex-grow: 1; }
        .list-buttons { display: flex; gap: 10px; }
        .list-buttons button { width: auto; padding: 8px; }
        .edit-btn { background: #007bff; }
        .delete-btn { background: #dc3545; }

        /* --- NOTIFICATION --- */
        #notification {
            position: fixed;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            background: var(--primary-color);
            color: white;
            padding: 15px 25px;
            border-radius: 8px;
            z-index: 1001;
            opacity: 0;
            transition: opacity 0.5s ease;
        }
        #notification.show { opacity: 1; }
    </style>
</head>
<body>

    <div id="loader">Loading...</div>

    <div id="admin-container" class="hidden">
        <header>
            <h1>Admin Panel</h1>
            <button id="logout-btn">Logout</button>
        </header>

        <!-- Category Management -->
        <section class="admin-section">
            <h2>Manage Categories</h2>
            <form id="category-form">
                <input type="hidden" id="category-id">
                <input type="text" id="category-name" placeholder="Category Name" required>
                <button type="submit">Save Category</button>
            </form>
            <ul id="categories-list"></ul>
        </section>

        <!-- Video Management -->
        <section class="admin-section">
            <h2>Manage Videos</h2>
            <form id="video-form">
                <input type="hidden" id="video-id">
                <input type="text" id="video-title" placeholder="Video Title" required>
                <select id="video-category" required></select>
                <input type="url" id="vimeo-link" placeholder="Vimeo Link (e.g., https://vimeo.com/123456789)" required>
                <!-- MODIFIED PLACEHOLDER TEXT ON THE LINE BELOW -->
                <input type="url" id="thumbnail-url" placeholder="Direct Thumbnail URL (from Imgur, etc.)" required>
                <button type="submit">Save Video</button>
            </form>
            <ul id="videos-list"></ul>
        </section>
    </div>

    <div id="notification"></div>

    <!-- Firebase SDKs -->
    <script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-app-compat.js"></script>
    <script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-auth-compat.js"></script>
    <script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore-compat.js"></script>

    <!-- Embedded JavaScript -->
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            // NOTE: It is not secure to share your full Firebase config publicly.
            // Consider using environment variables for production apps.
            const firebaseConfig = {
                apiKey: "",
                authDomain: "",
                projectId: "",
                storageBucket: "",
                messagingSenderId: "",
                appId: ""
            };

            // Initialize Firebase
            firebase.initializeApp(firebaseConfig);
            const auth = firebase.auth();
            const db = firebase.firestore();

            // DOM Elements
            const loader = document.getElementById('loader');
            const adminContainer = document.getElementById('admin-container');
            const logoutBtn = document.getElementById('logout-btn');
            const categoryForm = document.getElementById('category-form');
            const videoForm = document.getElementById('video-form');
            const categoriesList = document.getElementById('categories-list');
            const videosList = document.getElementById('videos-list');
            const videoCategorySelect = document.getElementById('video-category');
            const notification = document.getElementById('notification');

            // --- AUTH & ROLE CHECK ---
            auth.onAuthStateChanged(async user => {
                if (user) {
                    const userDoc = await db.collection('users').doc(user.uid).get();
                    if (userDoc.exists && userDoc.data().role === 'admin') {
                        loader.classList.add('hidden');
                        adminContainer.classList.remove('hidden');
                        initAdminPanel();
                    } else {
                        // Redirect non-admins
                        window.location.href = 'index.html';
                    }
                } else {
                    // Redirect unauthenticated users
                    window.location.href = 'index.html';
                }
            });

            logoutBtn.addEventListener('click', () => auth.signOut());

            function initAdminPanel() {
                loadCategories();
                loadVideos();
            }

            // --- NOTIFICATION ---
            function showNotification(message) {
                notification.textContent = message;
                notification.classList.add('show');
                setTimeout(() => {
                    notification.classList.remove('show');
                }, 3000);
            }

            // --- CATEGORY MANAGEMENT ---
            async function loadCategories() {
                db.collection('categories').orderBy('name').onSnapshot(snapshot => {
                    categoriesList.innerHTML = '';
                    videoCategorySelect.innerHTML = '<option value="">Select a Category</option>';
                    snapshot.forEach(doc => {
                        const category = { id: doc.id, ...doc.data() };
                        // Populate list
                        const li = document.createElement('li');
                        li.innerHTML = `
                            <span>${category.name}</span>
                            <div class="list-buttons">
                                <button class="edit-btn" data-id="${category.id}" data-name="${category.name}">Edit</button>
                                <button class="delete-btn" data-id="${category.id}">Delete</button>
                            </div>
                        `;
                        categoriesList.appendChild(li);
                        // Populate dropdown
                        const option = document.createElement('option');
                        option.value = category.name;
                        option.textContent = category.name;
                        videoCategorySelect.appendChild(option);
                    });
                });
            }

            categoryForm.addEventListener('submit', async e => {
                e.preventDefault();
                const id = document.getElementById('category-id').value;
                const name = document.getElementById('category-name').value;
                try {
                    if (id) {
                        await db.collection('categories').doc(id).update({ name });
                        showNotification('Category updated!');
                    } else {
                        await db.collection('categories').add({ name });
                        showNotification('Category added!');
                    }
                    categoryForm.reset();
                    document.getElementById('category-id').value = ''; // Clear ID after submit
                } catch (error) {
                    console.error("Error saving category: ", error);
                    showNotification('Error saving category.');
                }
            });

            categoriesList.addEventListener('click', e => {
                const target = e.target;
                const id = target.dataset.id;
                if (target.classList.contains('edit-btn')) {
                    document.getElementById('category-id').value = id;
                    document.getElementById('category-name').value = target.dataset.name;
                } else if (target.classList.contains('delete-btn')) {
                    if (confirm('Are you sure you want to delete this category?')) {
                        db.collection('categories').doc(id).delete()
                            .then(() => showNotification('Category deleted.'))
                            .catch(err => console.error("Error deleting category: ", err));
                    }
                }
            });

            // --- VIDEO MANAGEMENT ---
            async function loadVideos() {
                db.collection('videos').onSnapshot(snapshot => {
                    videosList.innerHTML = '';
                    snapshot.forEach(doc => {
                        const video = { id: doc.id, ...doc.data() };
                        const li = document.createElement('li');
                        li.innerHTML = `
                            <span>${video.title} (${video.category})</span>
                            <div class="list-buttons">
                                <button class="edit-btn" data-id="${video.id}">Edit</button>
                                <button class="delete-btn" data-id="${video.id}">Delete</button>
                            </div>
                        `;
                        videosList.appendChild(li);
                    });
                });
            }

            videoForm.addEventListener('submit', async e => {
                e.preventDefault();
                const id = document.getElementById('video-id').value;
                const vimeoLink = document.getElementById('vimeo-link').value;
                
                const vimeoIdMatch = vimeoLink.match(/(\d+)/);
                if (!vimeoIdMatch) {
                    showNotification('Invalid Vimeo link. Could not find a video ID.');
                    return;
                }
                
                const videoData = {
                    title: document.getElementById('video-title').value,
                    category: document.getElementById('video-category').value,
                    vimeo_id: vimeoIdMatch[0],
                    thumbnail_url: document.getElementById('thumbnail-url').value,
                };
                
                try {
                    if (id) {
                        await db.collection('videos').doc(id).update(videoData);
                        showNotification('Video updated!');
                    } else {
                        await db.collection('videos').add(videoData);
                        showNotification('Video added!');
                    }
                    videoForm.reset();
                    document.getElementById('video-id').value = '';
                } catch (error) {
                    console.error("Error saving video: ", error);
                    showNotification('Error saving video.');
                }
            });

            videosList.addEventListener('click', async e => {
                const target = e.target;
                const id = target.dataset.id;
                if (target.classList.contains('edit-btn')) {
                    const doc = await db.collection('videos').doc(id).get();
                    if (doc.exists) {
                        const video = doc.data();
                        document.getElementById('video-id').value = id;
                        document.getElementById('video-title').value = video.title;
                        document.getElementById('video-category').value = video.category;
                        document.getElementById('vimeo-link').value = `https://vimeo.com/${video.vimeo_id}`;
                        document.getElementById('thumbnail-url').value = video.thumbnail_url;
                    }
                } else if (target.classList.contains('delete-btn')) {
                    if (confirm('Are you sure you want to delete this video? This cannot be undone.')) {
                        db.collection('videos').doc(id).delete()
                            .then(() => showNotification('Video deleted.'))
                            .catch(err => console.error("Error deleting video: ", err));
                    }
                }
            });

        });
    </script>
</body>
</html>

Code 3 (Single HTML File)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>Streamify</title>
    <link rel="manifest" href="/manifest.json">
    <meta name="theme-color" content="#E50914">

    <!-- Embedded CSS for All Views -->
    <style>
        :root {
            --primary-color: #E50914;
            --dark-color: #141414;
            --light-color: #f4f4f4;
            --gray-color: #333;
        }
        body {
            font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
            background: var(--dark-color);
            color: var(--light-color);
            margin: 0;
            padding: 0;
            overscroll-behavior-y: contain;
        }
        .hidden { display: none !important; }

        /* --- AUTH CONTAINER --- */
        #auth-container {
            display: flex; justify-content: center; align-items: center;
            min-height: 100vh; padding: 20px;
        }
        .auth-form {
            background: rgba(0,0,0,0.8); padding: 40px; border-radius: 8px;
            width: 100%; max-width: 400px; box-sizing: border-box;
        }
        .auth-form h1 { margin-top: 0; }
        .auth-form input {
            width: 100%; padding: 12px; margin-bottom: 15px; background: #333;
            border: none; border-radius: 4px; color: white; box-sizing: border-box;
        }
        .auth-form button {
            width: 100%; padding: 12px; background: var(--primary-color); border: none;
            border-radius: 4px; color: white; font-size: 16px; font-weight: bold;
            cursor: pointer; margin-bottom: 10px;
        }
        #google-signin-btn { background: #4285F4; }
        .auth-form p { margin-top: 20px; text-align: center; }
        .auth-form a { color: var(--primary-color); text-decoration: none; cursor: pointer; }
        #error-message { color: orange; text-align: center; margin-bottom: 10px; }

        /* --- USER APP CONTAINER --- */
        #app-container { padding: 15px; }
        #app-container header, #admin-container header {
            display: flex; justify-content: space-between; align-items: center;
            margin-bottom: 20px;
        }
        #app-container header h1 { color: var(--primary-color); margin: 0; font-size: 24px; }
        .logout-btn {
            background: var(--primary-color); color: white; border: none;
            padding: 8px 12px; border-radius: 4px; cursor: pointer;
        }
        #search-input {
            width: 100%; padding: 10px; background: #333; border: 1px solid #555;
            border-radius: 4px; color: white; box-sizing: border-box; margin-bottom: 25px;
        }
        .category-section { margin-bottom: 30px; }
        .category-section h2 { margin-bottom: 15px; }
        .video-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 15px; }
        .video-card {
            cursor: pointer; position: relative; overflow: hidden;
            border-radius: 4px; transition: transform 0.2s ease-in-out;
        }
        .video-card:active { transform: scale(0.95); }
        .video-card img { width: 100%; height: auto; display: block; }
        .video-card .overlay {
            position: absolute; bottom: 0; left: 0; right: 0;
            background: linear-gradient(to top, rgba(0,0,0,0.9), transparent);
            padding: 10px; display: flex; justify-content: space-between; align-items: center;
        }
        .video-card .overlay p { margin: 0; font-size: 14px; flex-grow: 1; }
        .watchlist-btn {
            background: rgba(255, 255, 255, 0.2); border: 1px solid white; color: white;
            border-radius: 50%; width: 30px; height: 30px; font-size: 18px; cursor: pointer;
            display: flex; align-items: center; justify-content: center;
        }
        .watchlist-btn.in-watchlist { background: var(--primary-color); border-color: var(--primary-color); }
        #video-player-modal {
            position: fixed; top: 0; left: 0; width: 100%; height: 100%;
            background: rgba(0,0,0,0.95); display: flex; flex-direction: column;
            justify-content: center; align-items: center; z-index: 1000;
        }
        #video-player-container {
            width: 100%; max-width: 900px; position: relative;
            padding-bottom: 56.25%; height: 0;
        }
        #video-player-container iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none; }
        #close-player-btn {
            position: absolute; top: 15px; right: 15px; background: rgba(0,0,0,0.5);
            color: white; border: 2px solid white; border-radius: 50%;
            width: 44px; height: 44px; font-size: 22px; font-weight: bold;
            cursor: pointer; z-index: 1001;
        }

        /* --- ADMIN PANEL CONTAINER --- */
        #admin-container { padding: 20px; max-width: 800px; margin: 0 auto; }
        #admin-container header { border-bottom: 2px solid var(--primary-color); padding-bottom: 10px; margin-bottom: 30px; }
        #admin-container header h1 { margin: 0; }
        .admin-section { background: var(--gray-color); padding: 20px; border-radius: 8px; margin-bottom: 30px; }
        .admin-section h2 { margin-top: 0; }
        .admin-section form { display: flex; flex-direction: column; gap: 15px; }
        .admin-section input, .admin-section select, .admin-section button {
            width: 100%; padding: 12px; border-radius: 4px; border: 1px solid #555;
            background: #444; color: white; box-sizing: border-box; font-size: 16px;
        }
        .admin-section button { background: var(--primary-color); border: none; cursor: pointer; font-weight: bold; }
        .admin-section ul { list-style: none; padding: 0; margin: 20px 0 0 0; }
        .admin-section li {
            background: #222; padding: 15px; border-radius: 4px; margin-bottom: 10px;
            display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;
        }
        .admin-section li span { flex-grow: 1; }
        .list-buttons { display: flex; gap: 10px; }
        .list-buttons button { width: auto; padding: 8px; }
        .edit-btn { background: #007bff; }
        .delete-btn { background: #dc3545; }
        #notification {
            position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
            background: var(--primary-color); color: white; padding: 15px 25px; border-radius: 8px;
            z-index: 1001; opacity: 0; transition: opacity 0.5s ease;
        }
        #notification.show { opacity: 1; }
    </style>
</head>
<body>

    <!-- Authentication View -->
    <div id="auth-container">
        <div class="auth-form">
            <form id="login-form">
                <h1>Sign In</h1>
                <div id="error-message"></div>
                <input type="email" id="login-email" placeholder="Email" required>
                <input type="password" id="login-password" placeholder="Password" required>
                <button type="submit">Sign In</button>
                <button type="button" id="google-signin-btn">Sign In with Google</button>
                <p>New to Streamify? <a id="show-signup">Sign up now</a>.</p>
            </form>
            <form id="signup-form" class="hidden">
                <h1>Sign Up</h1>
                <input type="email" id="signup-email" placeholder="Email" required>
                <input type="password" id="signup-password" placeholder="Password" required>
                <button type="submit">Sign Up</button>
                <p>Already have an account? <a id="show-login">Sign in</a>.</p>
            </form>
        </div>
    </div>

    <!-- User App View -->
    <div id="app-container" class="hidden">
        <header>
            <h1>Streamify</h1>
            <button class="logout-btn">Logout</button>
        </header>
        <input type="search" id="search-input" placeholder="Search by title...">
        <main id="video-content"></main>
    </div>

    <!-- Admin Panel View -->
    <div id="admin-container" class="hidden">
        <header>
            <h1>Admin Panel</h1>
            <button class="logout-btn">Logout</button>
        </header>
        <section class="admin-section">
            <h2>Manage Categories</h2>
            <form id="category-form">
                <input type="hidden" id="category-id">
                <input type="text" id="category-name" placeholder="Category Name" required>
                <button type="submit">Save Category</button>
            </form>
            <ul id="categories-list"></ul>
        </section>
        <section class="admin-section">
            <h2>Manage Videos</h2>
            <form id="video-form">
                <input type="hidden" id="video-id">
                <input type="text" id="video-title" placeholder="Video Title" required>
                <select id="video-category" required></select>
                <input type="url" id="vimeo-link" placeholder="Vimeo Link (e.g., https://vimeo.com/123456789)" required>
                <input type="url" id="thumbnail-url" placeholder="Direct Thumbnail URL (from Imgur, etc.)" required>
                <button type="submit">Save Video</button>
            </form>
            <ul id="videos-list"></ul>
        </section>
    </div>

    <!-- Shared Modals & Notifications -->
    <div id="video-player-modal" class="hidden">
        <button id="close-player-btn">X</button>
        <div id="video-player-container"></div>
    </div>
    <div id="notification"></div>

    <!-- Firebase SDKs -->
    <script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-app-compat.js"></script>
    <script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-auth-compat.js"></script>
    <script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore-compat.js"></script>

    <!-- Embedded JavaScript for All Views -->
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const firebaseConfig = {
                apiKey: "",
                authDomain: "",
                projectId: "",
                storageBucket: "",
                messagingSenderId: "",
                appId: ""
            };

            // Initialize Firebase
            firebase.initializeApp(firebaseConfig);
            const auth = firebase.auth();
            const db = firebase.firestore();
            const googleProvider = new firebase.auth.GoogleAuthProvider();

            // --- DOM Elements (for all views) ---
            const authContainer = document.getElementById('auth-container');
            const appContainer = document.getElementById('app-container');
            const adminContainer = document.getElementById('admin-container');

            // Auth Elements
            const loginForm = document.getElementById('login-form');
            const signupForm = document.getElementById('signup-form');
            const showSignup = document.getElementById('show-signup');
            const showLogin = document.getElementById('show-login');
            const googleSigninBtn = document.getElementById('google-signin-btn');
            const errorMessage = document.getElementById('error-message');
            const logoutBtns = document.querySelectorAll('.logout-btn');

            // User App Elements
            const videoContent = document.getElementById('video-content');
            const searchInput = document.getElementById('search-input');
            const videoPlayerModal = document.getElementById('video-player-modal');
            const videoPlayerContainer = document.getElementById('video-player-container');
            const closePlayerBtn = document.getElementById('close-player-btn');
            let userWatchlist = [];

            // Admin Panel Elements
            const categoryForm = document.getElementById('category-form');
            const videoForm = document.getElementById('video-form');
            const categoriesList = document.getElementById('categories-list');
            const videosList = document.getElementById('videos-list');
            const videoCategorySelect = document.getElementById('video-category');
            const notification = document.getElementById('notification');

            // --- CORE APP ROUTER ---
            auth.onAuthStateChanged(async user => {
                if (user) {
                    const userDoc = await db.collection('users').doc(user.uid).get();
                    if (userDoc.exists && userDoc.data().role === 'admin') {
                        // Show Admin Panel
                        authContainer.classList.add('hidden');
                        appContainer.classList.add('hidden');
                        adminContainer.classList.remove('hidden');
                        initAdminPanel();
                    } else {
                        // Show User App
                        authContainer.classList.add('hidden');
                        adminContainer.classList.add('hidden');
                        appContainer.classList.remove('hidden');
                        initUserApp(userDoc);
                    }
                } else {
                    // Show Auth View
                    appContainer.classList.add('hidden');
                    adminContainer.classList.add('hidden');
                    authContainer.classList.remove('hidden');
                }
            });

            // --- AUTHENTICATION LOGIC ---
            showSignup.addEventListener('click', () => { loginForm.classList.add('hidden'); signupForm.classList.remove('hidden'); errorMessage.textContent = ''; });
            showLogin.addEventListener('click', () => { signupForm.classList.add('hidden'); loginForm.classList.remove('hidden'); errorMessage.textContent = ''; });
            
            loginForm.addEventListener('submit', e => {
                e.preventDefault();
                const email = document.getElementById('login-email').value;
                const password = document.getElementById('login-password').value;
                auth.signInWithEmailAndPassword(email, password).catch(error => { errorMessage.textContent = error.message; });
            });

            signupForm.addEventListener('submit', e => {
                e.preventDefault();
                const email = document.getElementById('signup-email').value;
                const password = document.getElementById('signup-password').value;
                auth.createUserWithEmailAndPassword(email, password)
                    .then(cred => db.collection('users').doc(cred.user.uid).set({ role: 'user', watchlist: [] }))
                    .catch(error => { errorMessage.textContent = error.message; });
            });
            
            googleSigninBtn.addEventListener('click', () => {
                auth.signInWithPopup(googleProvider)
                    .then(async result => {
                        const user = result.user;
                        const userDocRef = db.collection('users').doc(user.uid);
                        const doc = await userDocRef.get();
                        if (!doc.exists) { await userDocRef.set({ role: 'user', watchlist: [] }); }
                    }).catch(error => { errorMessage.textContent = error.message; });
            });

            logoutBtns.forEach(btn => btn.addEventListener('click', () => auth.signOut()));

            // --- USER APP LOGIC ---
            function initUserApp(userDoc) {
                userWatchlist = userDoc.exists ? userDoc.data().watchlist || [] : [];
                loadUserVideos();
            }

            async function loadUserVideos() {
                try {
                    const categoriesSnapshot = await db.collection('categories').get();
                    const videosSnapshot = await db.collection('videos').get();
                    const categories = categoriesSnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
                    const videos = videosSnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
                    renderUserVideos(videos, categories);
                    searchInput.addEventListener('input', e => {
                        const filteredVideos = videos.filter(v => v.title.toLowerCase().includes(e.target.value.toLowerCase()));
                        renderUserVideos(filteredVideos, categories);
                    });
                } catch (error) { console.error("Error loading videos:", error); }
            }

            function renderUserVideos(videos, categories) {
                videoContent.innerHTML = '';
                categories.forEach(category => {
                    const videosInCategory = videos.filter(video => video.category === category.name);
                    if (videosInCategory.length > 0) {
                        const categorySection = document.createElement('div');
                        categorySection.className = 'category-section';
                        categorySection.innerHTML = `<h2>${category.name}</h2>`;
                        const videoGrid = document.createElement('div');
                        videoGrid.className = 'video-grid';
                        videosInCategory.forEach(video => {
                            const isInWatchlist = userWatchlist.includes(video.id);
                            videoGrid.innerHTML += `
                                <div class="video-card" data-vimeo-id="${video.vimeo_id}">
                                    <img src="${video.thumbnail_url}" alt="${video.title}">
                                    <div class="overlay">
                                        <p>${video.title}</p>
                                        <button class="watchlist-btn ${isInWatchlist ? 'in-watchlist' : ''}" data-video-id="${video.id}">
                                            ${isInWatchlist ? '✓' : '+'}
                                        </button>
                                    </div>
                                </div>`;
                        });
                        categorySection.appendChild(videoGrid);
                        videoContent.appendChild(categorySection);
                    }
                });
            }

            videoContent.addEventListener('click', e => {
                if (e.target.classList.contains('watchlist-btn')) {
                    toggleWatchlist(e.target.dataset.videoId);
                } else if (e.target.closest('.video-card')) {
                    playVideo(e.target.closest('.video-card').dataset.vimeoId);
                }
            });

            function playVideo(vimeoId) {
                const vimeoUrl = `https://player.vimeo.com/video/${vimeoId}?autoplay=1&controls=1&muted=0&byline=0&portrait=0&title=0&dnt=1`;
                videoPlayerContainer.innerHTML = `<iframe src="${vimeoUrl}" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen></iframe>`;
                videoPlayerModal.classList.remove('hidden');
            }
            closePlayerBtn.addEventListener('click', () => { videoPlayerContainer.innerHTML = ''; videoPlayerModal.classList.add('hidden'); });

            async function toggleWatchlist(videoId) {
                const user = auth.currentUser;
                if (!user) return;
                const userDocRef = db.collection('users').doc(user.uid);
                let updatedWatchlist;
                if (userWatchlist.includes(videoId)) {
                    updatedWatchlist = userWatchlist.filter(id => id !== videoId);
                } else {
                    updatedWatchlist = [...userWatchlist, videoId];
                }
                try {
                    await userDocRef.update({ watchlist: updatedWatchlist });
                    userWatchlist = updatedWatchlist;
                    const button = document.querySelector(`.watchlist-btn[data-video-id="${videoId}"]`);
                    button.textContent = userWatchlist.includes(videoId) ? '✓' : '+';
                    button.classList.toggle('in-watchlist', userWatchlist.includes(videoId));
                } catch (error) { console.error("Error updating watchlist:", error); }
            }

            // --- ADMIN PANEL LOGIC ---
            function initAdminPanel() {
                loadAdminCategories();
                loadAdminVideos();
            }

            function showNotification(message) {
                notification.textContent = message;
                notification.classList.add('show');
                setTimeout(() => notification.classList.remove('show'), 3000);
            }

            function loadAdminCategories() {
                db.collection('categories').orderBy('name').onSnapshot(snapshot => {
                    categoriesList.innerHTML = '';
                    videoCategorySelect.innerHTML = '<option value="">Select a Category</option>';
                    snapshot.forEach(doc => {
                        const cat = { id: doc.id, ...doc.data() };
                        categoriesList.innerHTML += `<li><span>${cat.name}</span><div class="list-buttons"><button class="edit-btn" data-id="${cat.id}" data-name="${cat.name}">Edit</button><button class="delete-btn" data-id="${cat.id}">Delete</button></div></li>`;
                        videoCategorySelect.innerHTML += `<option value="${cat.name}">${cat.name}</option>`;
                    });
                });
            }

            categoryForm.addEventListener('submit', async e => {
                e.preventDefault();
                const id = document.getElementById('category-id').value;
                const name = document.getElementById('category-name').value;
                try {
                    if (id) await db.collection('categories').doc(id).update({ name });
                    else await db.collection('categories').add({ name });
                    showNotification(`Category ${id ? 'updated' : 'added'}!`);
                    categoryForm.reset(); document.getElementById('category-id').value = '';
                } catch (error) { console.error("Error saving category:", error); }
            });

            categoriesList.addEventListener('click', e => {
                const id = e.target.dataset.id;
                if (e.target.classList.contains('edit-btn')) {
                    document.getElementById('category-id').value = id;
                    document.getElementById('category-name').value = e.target.dataset.name;
                } else if (e.target.classList.contains('delete-btn')) {
                    if (confirm('Are you sure?')) db.collection('categories').doc(id).delete();
                }
            });

            function loadAdminVideos() {
                db.collection('videos').onSnapshot(snapshot => {
                    videosList.innerHTML = '';
                    snapshot.forEach(doc => {
                        const vid = { id: doc.id, ...doc.data() };
                        videosList.innerHTML += `<li><span>${vid.title} (${vid.category})</span><div class="list-buttons"><button class="edit-btn" data-id="${vid.id}">Edit</button><button class="delete-btn" data-id="${vid.id}">Delete</button></div></li>`;
                    });
                });
            }
            
            videoForm.addEventListener('submit', async e => {
                e.preventDefault();
                const id = document.getElementById('video-id').value;
                const vimeoLink = document.getElementById('vimeo-link').value;
                const vimeoIdMatch = vimeoLink.match(/(\d+)/);
                if (!vimeoIdMatch) { showNotification('Invalid Vimeo link.'); return; }
                
                const videoData = {
                    title: document.getElementById('video-title').value,
                    category: document.getElementById('video-category').value,
                    vimeo_id: vimeoIdMatch[0],
                    thumbnail_url: document.getElementById('thumbnail-url').value,
                };
                
                try {
                    if (id) await db.collection('videos').doc(id).update(videoData);
                    else await db.collection('videos').add(videoData);
                    showNotification(`Video ${id ? 'updated' : 'added'}!`);
                    videoForm.reset(); document.getElementById('video-id').value = '';
                } catch (error) { console.error("Error saving video:", error); }
            });

            videosList.addEventListener('click', async e => {
                const id = e.target.dataset.id;
                if (e.target.classList.contains('edit-btn')) {
                    const doc = await db.collection('videos').doc(id).get();
                    if (doc.exists) {
                        const vid = doc.data();
                        document.getElementById('video-id').value = id;
                        document.getElementById('video-title').value = vid.title;
                        document.getElementById('video-category').value = vid.category;
                        document.getElementById('vimeo-link').value = `https://vimeo.com/${vid.vimeo_id}`;
                        document.getElementById('thumbnail-url').value = vid.thumbnail_url;
                    }
                } else if (e.target.classList.contains('delete-btn')) {
                    if (confirm('Are you sure?')) db.collection('videos').doc(id).delete();
                }
            });

            // PWA Service Worker
            if ('serviceWorker' in navigator) {
                window.addEventListener('load', () => {
                    navigator.serviceWorker.register('/service-worker.js')
                        .then(reg => console.log('ServiceWorker registration successful.'))
                        .catch(err => console.log('ServiceWorker registration failed:', err));
                });
            }
        });
    </script>
</body>
</html>

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *