diff options
| -rw-r--r-- | content-script.js | 98 | ||||
| -rw-r--r-- | manifest.json | 50 | ||||
| -rw-r--r-- | popup.html | 12 | ||||
| -rw-r--r-- | popup.js | 28 |
4 files changed, 139 insertions, 49 deletions
diff --git a/content-script.js b/content-script.js index 1d14fa7..dbfd11a 100644 --- a/content-script.js +++ b/content-script.js @@ -13,21 +13,32 @@ var paster = document.createElement("button"); var adder = document.createElement("button"); var copier = document.createElement("button"); - var offset = 5; var copyMode = 0; + var videoId = location.search.split(/.+v=|&/)[1] || ""; const storedOffset = localStorage.getItem("offset"); - offset = storedOffset !== null ? parseInt(storedOffset) : 5; + const storedAutoSave = localStorage.getItem("autosave"); + var offset = storedOffset !== null ? parseInt(storedOffset) : 5; + var autosave = storedAutoSave !== null ? storedAutoSave : true; - browser.runtime.onMessage.addListener((message) => { + const browserAPI = typeof browser !== "undefined" ? browser : chrome; + + browserAPI.runtime.onMessage.addListener((message) => { if (message.type === "SET_OFFSET") { offset = message.offset; localStorage.setItem("offset", offset); } + if (message.type === "SET_AUTOSAVE") { + autosave = message.autosave; + localStorage.setItem("autosave", autosave); + } }); + let isLoaded = false; // Flag to track if timestamps are loaded + function closePane() { if (confirm("Close timestamp tool?")) { + saveTimestamps(); pane.remove(); cancelAnimationFrame(nowid); window.removeEventListener("beforeunload", warn); @@ -37,30 +48,27 @@ function updateStamp(stamp, time) { stamp.innerHTML = formatTime(time); stamp.dataset.time = time; - stamp.href = - "https://youtu.be/" + - location.search.split(/.+v=|&/)[1] + - "?t=" + - time; + stamp.href = "https://youtu.be/" + videoId + "?t=" + time; + saveTimestamps(); } function clickStamp(e) { if (e.target.dataset.time) { e.preventDefault(); - document.querySelector("video").currentTime = - e.target.dataset.time; + document.querySelector("video").currentTime = e.target.dataset.time; } else if (e.target.classList.contains("remove-timestamp")) { e.preventDefault(); var li = e.target.parentElement; li.remove(); + saveTimestamps(); } else if (e.target.dataset.increment) { e.preventDefault(); var li = e.target.parentElement; var a = li.children[3]; var time = - parseInt(a.dataset.time) + - parseInt(e.target.dataset.increment); + parseInt(a.dataset.time) + parseInt(e.target.dataset.increment); updateStamp(a, time); + saveTimestamps(); } } @@ -104,6 +112,9 @@ li.appendChild(a); li.appendChild(text); list.appendChild(li); + text.addEventListener("change", saveTimestamps); + text.addEventListener("blur", saveTimestamps); + return text; } @@ -122,6 +133,7 @@ text.value = note; } list.appendChild(nowli); + saveTimestamps(); } function formatTime(time) { @@ -138,13 +150,12 @@ function addStamp() { var time = Math.max( 0, - Math.floor( - document.querySelector("video").currentTime - offset, - ), + Math.floor(document.querySelector("video").currentTime - offset) ); var text = newLi(time); list.appendChild(nowli); text.focus(); + saveTimestamps(); } function resetCopier() { @@ -203,6 +214,54 @@ }); } + function saveTimestamps() { + if (!isLoaded || !videoId) return; + let timestamps = []; + for (let i = 0; i < list.children.length - 1; i++) { + const li = list.children[i]; + const a = li.querySelector("a"); + const note = li.querySelector("input").value; + + timestamps.push({ + time: parseInt(a.dataset.time), + note: note, + }); + } + + localStorage.setItem( + `yt-timestamps-${videoId}`, + JSON.stringify(timestamps) + ); + } + + function loadTimestamps() { + if (!videoId) return; + + const savedData = localStorage.getItem(`yt-timestamps-${videoId}`); + if (!savedData) { + isLoaded = true; + return; + } + + try { + const timestamps = JSON.parse(savedData); + while (list.children.length > 1) { + list.removeChild(list.firstChild); + } + timestamps.forEach((item) => { + const text = newLi(item.time); + text.value = item.note || ""; + }); + list.appendChild(nowli); + + isLoaded = true; // Mark as loaded after successful parsing + showToast("Timestamps loaded"); + } catch (e) { + console.error("Error loading timestamps:", e); + isLoaded = true; // Mark as loaded even if an error occurs + } + } + var toast = document.createElement("div"); toast.id = "ytls-toast"; document.body.appendChild(toast); @@ -240,6 +299,8 @@ adder.addEventListener("click", addStamp); copier.addEventListener("click", copyList); window.addEventListener("beforeunload", warn); + window.addEventListener("beforeunload", saveTimestamps); + pane.appendChild(exit); nowli.appendChild(nowa); nowli.appendChild(nowtext); @@ -252,5 +313,12 @@ pane.appendChild(buttons); document.body.appendChild(pane); box.focus(); + paster.disabled = true; + adder.disabled = true; + copier.disabled = true; + loadTimestamps(); + paster.disabled = false; + adder.disabled = false; + copier.disabled = false; } })(); diff --git a/manifest.json b/manifest.json index 77a8f55..eba16de 100644 --- a/manifest.json +++ b/manifest.json @@ -1,27 +1,27 @@ { - "manifest_version": 3, - "name": "QuickStamper", - "version": "1.0", - "description": "A tool for generating timestamps on YouTube videos or livestreams", - "browser_specific_settings": { - "gecko": { - "id": "ytquickstamper@pinapelz.com" - } - }, - "permissions": ["activeTab", "scripting", "clipboardWrite"], - "icons": { - "48": "icons/icon.png" - }, - "action": { - "default_popup": "popup.html", - "default_icon": { - "48": "icons/icon.png" - } - }, - "web_accessible_resources": [ - { - "resources": ["styles.css"], - "matches": ["<all_urls>"] - } - ] + "manifest_version": 3, + "name": "QuickStamper", + "version": "1.0", + "description": "A tool for generating timestamps on YouTube videos or livestreams", + "browser_specific_settings": { + "gecko": { + "id": "ytquickstamper@pinapelz.com" + } + }, + "permissions": ["activeTab", "scripting", "clipboardWrite", "storage"], + "icons": { + "48": "icons/icon.png" + }, + "action": { + "default_popup": "popup.html", + "default_icon": { + "48": "icons/icon.png" + } + }, + "web_accessible_resources": [ + { + "resources": ["styles.css"], + "matches": ["<all_urls>"] + } + ] } @@ -59,12 +59,16 @@ <button id="activate">Launch Timestamp Tool</button> <div> <label for="offset">Offset (sec):</label> - <input type="number" - id="offset" - value="5" + <input type="number" + id="offset" title="Offset each timestamp by this many seconds, ex. 3 = 3s before each timestamp, -3 = 3s after each timestamp" > + <label for="autosave">Autosave</label> + <input type="checkbox" + id="autosave" + title="Automatically save timestamps for each video"> + </input> </div> <script src="popup.js"></script> </body> -</html>
\ No newline at end of file +</html> @@ -1,7 +1,10 @@ +// Dynamically detect the browser API +const browserAPI = typeof browser !== 'undefined' ? browser : chrome; + document.getElementById('activate').addEventListener('click', () => { - browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => { + browserAPI.tabs.query({ active: true, currentWindow: true }, (tabs) => { if (tabs[0].url.includes('youtube.com/watch')) { - browser.scripting.executeScript({ + browserAPI.scripting.executeScript({ target: { tabId: tabs[0].id }, files: ['content-script.js'] }); @@ -11,10 +14,25 @@ document.getElementById('activate').addEventListener('click', () => { document.getElementById('offset').addEventListener('input', () => { const offset = parseInt(document.getElementById('offset').value, 10); - browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => { - browser.tabs.sendMessage(tabs[0].id, { type: 'SET_OFFSET', offset: offset }); + console.log(offset) + browserAPI.tabs.query({ active: true, currentWindow: true }, (tabs) => { + browserAPI.tabs.sendMessage(tabs[0].id, { type: 'SET_OFFSET', offset: offset }); localStorage.setItem('offset', offset); }); }); -localStorage.getItem('offset') !== null ? document.getElementById('offset').value = localStorage.getItem('offset') : document.getElementById('offset').value = 5;
\ No newline at end of file +document.getElementById('autosave').addEventListener('input', () => { + const autosaveStatus = document.getElementById('autosave').checked; + browserAPI.tabs.query({ active: true, currentWindow: true }, (tabs) => { + browserAPI.tabs.sendMessage(tabs[0].id, { type: 'SET_AUTOSAVE', autosave: autosaveStatus }); + localStorage.setItem('autosave', autosaveStatus); + }); +}); + +localStorage.getItem('offset') !== null + ? document.getElementById('offset').value = localStorage.getItem('offset') + : document.getElementById('offset').value = 5; + + localStorage.getItem('autosave') !== null + ? document.getElementById('autosave').checked = (localStorage.getItem('autosave') === 'true') + : document.getElementById('autosave').checked = true; |
