{
  "manifest_version": 3,
  "name": "Sync Waves",
  "version": "1.0",
  "description": "Synchronize audio and video with custom start times.",
  "permissions": [
    "tabs"
  ],
  "action": {
    "default_title": "Sync Waves",
    "default_icon": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  },
  "background": {
    "service_worker": "background.js"
  },
  "icons": {
    "16": "icons/icon16.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  },
  "content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self'"
  }
}






<!DOCTYPE html>
<html lang="en"><head>
  <meta charset="UTF-8">
  <title>Sync Waves</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <h1>Sync Waves</h1>
  
  <div class="container">
    <!-- Audio Section -->
    <div class="section" id="audio-section">
      <h2>Audio</h2>
      
      <div class="audio-content">
        <!-- Inputs and Load Button -->
        <div class="inputs">
          
          
          
          <label for="audio-start-time">Start Time (seconds):</label><input type="number" id="audio-start-time" placeholder="Start Time (seconds)" min="0"><label for="audio-file">Load Local Audio File:</label>
          <div class="inner-column">
            <div class="left">
              <input type="file" id="audio-file" accept="audio/*">
            </div>
            <div class="right">
              <audio id="audio-player" controls=""></audio>
            </div>
          </div>
          
          
          
          
          
        </div>
      </div>
    </div>
    
    <!-- Video Section -->
    <div class="section" id="video-section">
      <h2>Video</h2>
      
      <div class="video-content">
        <div class="inputs">
          
          
          
          <label for="video-start-time">Start Time (seconds):</label><input type="number" id="video-start-time" placeholder="Start Time (seconds)" min="0"><label for="video-file">Load Local Video File:</label>
          <input type="file" id="video-file" accept="video/*">
          
          
          
          
          
        </div>
      </div>
    </div>
  </div>
  
  <!-- Control Buttons -->
  <div class="controls">
    <button id="play">Play</button>
    <button id="pause">Pause</button>
    <button id="resume">Resume</button>
  </div>
  
  <!-- Media Elements -->
  
  
  <script src="script.js"></script>

</body></html>




chrome.runtime.onInstalled.addListener(() => {
  console.log('Sync Waves Extension Installed');
});

// Listen for the extension icon click
chrome.action.onClicked.addListener(() => {
  // Open index.html in a new tab
  chrome.tabs.create({ url: chrome.runtime.getURL("index.html") });
});




document.addEventListener('DOMContentLoaded', () => {
  // Audio Elements and Controls
  const audioUrlInput = document.getElementById('audio-url');
  const audioFileInput = document.getElementById('audio-file');
  const audioStartTimeInput = document.getElementById('audio-start-time');
  const loadAudioButton = document.getElementById('load-audio');
  
  // Video Elements and Controls
  const videoUrlInput = document.getElementById('video-url');
  const videoFileInput = document.getElementById('video-file');
  const videoStartTimeInput = document.getElementById('video-start-time');
  const loadVideoButton = document.getElementById('load-video');
  
  // Playback Control Buttons
  const playButton = document.getElementById('play');
  const pauseButton = document.getElementById('pause');
  const resumeButton = document.getElementById('resume');
  
  // Media Players
  const audioPlayer = document.getElementById('audio-player');
  const videoPlayer = document.getElementById('video-player');
  
  // Load Audio Functionality
  loadAudioButton.addEventListener('click', () => {
    const youtubeUrl = audioUrlInput.value.trim();
    const file = audioFileInput.files[0];
    const startTime = parseInt(audioStartTimeInput.value, 10) || 0;
    
    if (youtubeUrl) {
      // Load YouTube Audio via YouTube API
      loadYouTubeAudio(youtubeUrl, audioPlayer, startTime);
    } else if (file) {
      const reader = new FileReader();
      reader.onload = function(e) {
        audioPlayer.src = e.target.result;
        audioPlayer.load();
        audioPlayer.addEventListener('canplay', () => {
          audioPlayer.currentTime = startTime;
        }, { once: true });
      };
      reader.readAsDataURL(file);
    } else {
      alert('Please provide a YouTube URL or select an audio file.');
    }
  });
  
  // Load Video Functionality
  loadVideoButton.addEventListener('click', () => {
    const youtubeUrl = videoUrlInput.value.trim();
    const file = videoFileInput.files[0];
    const startTime = parseInt(videoStartTimeInput.value, 10) || 0;
    
    if (youtubeUrl) {
      // Load YouTube Video via YouTube Embed
      const videoID = extractYouTubeID(youtubeUrl);
      if (videoID) {
        videoPlayer.src = `https://www.youtube.com/embed/${videoID}?autoplay=1&controls=1&enablejsapi=1&origin=${window.location.origin}`;
        videoPlayer.addEventListener('load', () => {
          // Attempt to set start time using YouTube API after iframe is ready
          setTimeout(() => {
            videoPlayer.contentWindow.postMessage(JSON.stringify({
              event: 'command',
              func: 'seekTo',
              args: [startTime, true]
            }), '*');
          }, 500);
        }, { once: true });
      } else {
        alert('Invalid YouTube URL.');
      }
    } else if (file) {
      const reader = new FileReader();
      reader.onload = function(e) {
        videoPlayer.src = e.target.result;
        videoPlayer.load();
        videoPlayer.addEventListener('canplay', () => {
          videoPlayer.currentTime = startTime;
        }, { once: true });
      };
      reader.readAsDataURL(file);
    } else {
      alert('Please provide a YouTube URL or select a video file.');
    }
  });
  
  // Playback Control Functionality
  playButton.addEventListener('click', () => {
    audioPlayer.play();
    videoPlayer.play();
  });
  
  pauseButton.addEventListener('click', () => {
    audioPlayer.pause();
    videoPlayer.pause();
  });
  
  resumeButton.addEventListener('click', () => {
    audioPlayer.play();
    videoPlayer.play();
  });
  
  // Helper Function to Extract YouTube Video ID
  function extractYouTubeID(url) {
    const regex = /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w\-]{11})/;
    const match = url.match(regex);
    return match ? match[1] : null;
  }
  
  // Placeholder Function for YouTube Audio Loading
  function loadYouTubeAudio(url, player, startTime) {
    alert('Loading audio from YouTube is not supported in this version.');
    // Future implementation can use YouTube Data API and OAuth for advanced features
  }
});




* {
  box-sizing: border-box;
}

body {
  font-family: Arial, sans-serif;
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 15px;
  background-color: #f9f9f9;
  overflow-x: hidden; /* Prevent horizontal scrollbar */
}

h1 {
  margin-top: 0;
  text-align: center;
  margin-bottom: 15px;
  color: #333;
}

/* Container for Audio and Video Sections */
.container {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* Individual Section Styling */
.section {
  background-color: #fff;
  padding: 15px;
  border-radius: 8px;
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
}

.section h2 {
  margin-top: unset;
  text-align: center;
  color: #555;
}

/* Audio and Video Content Styling */
.audio-content,
.video-content {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* Inputs Styling */
.inputs {
  display: flex;
  flex-direction: column;
  gap: 15px;
}

.inputs label {
  font-weight: bold;
  color: #444;
}

.inputs input,
.inputs button {
  padding: 10px;
  font-size: 1em;
  width: 100%;
  max-width: 100%;
  border: 1px solid #ccc;
  border-radius: 4px;
}

.inputs input:focus {
  border-color: #007BFF;
  outline: none;
}

.inputs button {
  background-color: #007BFF;
  color: #fff;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.inputs button:hover {
  background-color: #0056b3;
}

/* Inner Column for Audio Player Positioning */
.inner-column {
  display: flex;
  gap: 15px;
  align-items: center;
  flex-wrap: wrap;
}

.left {
  flex: 1 1 200px;
}

.right {
  flex: 1 1 200px;
  display: flex;
  justify-content: center;
}

.right audio {
  width: 100%;
  border-radius: 4px;
}

/* Control Buttons Styling */
.controls {
  display: flex;
  justify-content: center;
  gap: 15px;
  margin: 15px 0 0 0;
  flex-wrap: wrap;
}

.controls button {
  padding: 12px 25px;
  font-size: 1em;
  border: none;
  border-radius: 4px;
  background-color: #28a745;
  color: #fff;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.controls button:hover {
  background-color: #218838;
}

/* Specific Styles for Pause and Resume Buttons */
#pause {
  background-color: #dc3545;
}

#pause:hover {
  background-color: #c82333;
}

#resume {
  background-color: #17a2b8;
}

#resume:hover {
  background-color: #138496;
}

/* Media Container Styling */
.media-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
}

.media-container video {
  width: 100%;
  max-width: 800px;
  height: auto;
  border-radius: 8px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

/* Responsive Design */
@media (max-width: 768px) {
  .inner-column {
    flex-direction: column;
    align-items: stretch;
  }

  .right audio {
    max-width: 100%;
  }

  .media-container video {
    max-width: 100%;
  }

  h1 {
    margin-bottom: 15px;
  }

  .right {
    flex: 1 1;
  }

  .left {
    flex: 1 1;
  }
}

@media (max-width: 480px) {
  .controls {
    flex-direction: column;
    gap: 15px;
  }

  .controls button {
    width: 100%;
  }
}




Errors
Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')
Context
index.html
Stack Trace
script.js:24 (anonymous function)
document.addEventListener('DOMContentLoaded', () => {
  // Audio Elements and Controls
  const audioUrlInput = document.getElementById('audio-url');
  const audioFileInput = document.getElementById('audio-file');
  const audioStartTimeInput = document.getElementById('audio-start-time');
  const loadAudioButton = document.getElementById('load-audio');
  
  // Video Elements and Controls
  const videoUrlInput = document.getElementById('video-url');
  const videoFileInput = document.getElementById('video-file');
  const videoStartTimeInput = document.getElementById('video-start-time');
  const loadVideoButton = document.getElementById('load-video');
  
  // Playback Control Buttons
  const playButton = document.getElementById('play');
  const pauseButton = document.getElementById('pause');
  const resumeButton = document.getElementById('resume');
  
  // Media Players
  const audioPlayer = document.getElementById('audio-player');
  const videoPlayer = document.getElementById('video-player');
  
  // Load Audio Functionality
  loadAudioButton.addEventListener('click', () => {
    const youtubeUrl = audioUrlInput.value.trim();
    const file = audioFileInput.files[0];
    const startTime = parseInt(audioStartTimeInput.value, 10) || 0;
    
    if (youtubeUrl) {
      // Load YouTube Audio via YouTube API
      loadYouTubeAudio(youtubeUrl, audioPlayer, startTime);
    } else if (file) {
      const reader = new FileReader();
      reader.onload = function(e) {
        audioPlayer.src = e.target.result;
        audioPlayer.load();
        audioPlayer.addEventListener('canplay', () => {
          audioPlayer.currentTime = startTime;
        }, { once: true });
      };
      reader.readAsDataURL(file);
    } else {
      alert('Please provide a YouTube URL or select an audio file.');
    }
  });
  
  // Load Video Functionality
  loadVideoButton.addEventListener('click', () => {
    const youtubeUrl = videoUrlInput.value.trim();
    const file = videoFileInput.files[0];
    const startTime = parseInt(videoStartTimeInput.value, 10) || 0;
    
    if (youtubeUrl) {
      // Load YouTube Video via YouTube Embed
      const videoID = extractYouTubeID(youtubeUrl);
      if (videoID) {
        videoPlayer.src = `https://www.youtube.com/embed/${videoID}?autoplay=1&controls=1&enablejsapi=1&origin=${window.location.origin}`;
        videoPlayer.addEventListener('load', () => {
          // Attempt to set start time using YouTube API after iframe is ready
          setTimeout(() => {
            videoPlayer.contentWindow.postMessage(JSON.stringify({
              event: 'command',
              func: 'seekTo',
              args: [startTime, true]
            }), '*');
          }, 500);
        }, { once: true });
      } else {
        alert('Invalid YouTube URL.');
      }
    } else if (file) {
      const reader = new FileReader();
      reader.onload = function(e) {
        videoPlayer.src = e.target.result;
        videoPlayer.load();
        videoPlayer.addEventListener('canplay', () => {
          videoPlayer.currentTime = startTime;
        }, { once: true });
      };
      reader.readAsDataURL(file);
    } else {
      alert('Please provide a YouTube URL or select a video file.');
    }
  });
  
  // Playback Control Functionality
  playButton.addEventListener('click', () => {
    audioPlayer.play();
    videoPlayer.play();
  });
  
  pauseButton.addEventListener('click', () => {
    audioPlayer.pause();
    videoPlayer.pause();
  });
  
  resumeButton.addEventListener('click', () => {
    audioPlayer.play();
    videoPlayer.play();
  });
  
  // Helper Function to Extract YouTube Video ID
  function extractYouTubeID(url) {
    const regex = /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w\-]{11})/;
    const match = url.match(regex);
    return match ? match[1] : null;
  }
  
  // Placeholder Function for YouTube Audio Loading
  function loadYouTubeAudio(url, player, startTime) {
    alert('Loading audio from YouTube is not supported in this version.');
    // Future implementation can use YouTube Data API and OAuth for advanced features
  }
});



i only want the completed updated files. in the audio section can you load the player when there is a song loaded in local files there and unload if there is not. and obey chromes content security policy.