Skip to content

Commit

Permalink
extension ready
Browse files Browse the repository at this point in the history
  • Loading branch information
vikash committed Dec 27, 2022
1 parent 089ebe7 commit 3a0da48
Show file tree
Hide file tree
Showing 12 changed files with 285 additions and 0 deletions.
Binary file added assets/bookmark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/delete.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/ext-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/play.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/save.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
chrome.tabs.onUpdated.addListener((tabId, tab) => {
if (tab.url && tab.url.includes("youtube.com/watch")) {
const queryParameters = tab.url.split("?")[1];
const urlParameters = new URLSearchParams(queryParameters);

chrome.tabs.sendMessage(tabId, {
type: "NEW",
videoId: urlParameters.get("v"),
});
}
});
72 changes: 72 additions & 0 deletions contentScript.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
(() => {
let youtubeLeftControls, youtubePlayer;
let currentVideo = "";
let currentVideoBookmarks = [];

const fetchBookmarks = () => {
return new Promise((resolve) => {
chrome.storage.sync.get([currentVideo], (obj) => {
resolve(obj[currentVideo] ? JSON.parse(obj[currentVideo]) : []);
});
});
};

const addNewBookmarkEventHandler = async () => {
const currentTime = youtubePlayer.currentTime;
const newBookmark = {
time: currentTime,
desc: "Bookmark at " + getTime(currentTime),
};

currentVideoBookmarks = await fetchBookmarks();

chrome.storage.sync.set({
[currentVideo]: JSON.stringify([...currentVideoBookmarks, newBookmark].sort((a, b) => a.time - b.time))
});
};

const newVideoLoaded = async () => {
const bookmarkBtnExists = document.getElementsByClassName("bookmark-btn")[0];

currentVideoBookmarks = await fetchBookmarks();

if (!bookmarkBtnExists) {
const bookmarkBtn = document.createElement("img");

bookmarkBtn.src = chrome.runtime.getURL("assets/bookmark.png");
bookmarkBtn.className = "ytp-button " + "bookmark-btn";
bookmarkBtn.title = "Click to bookmark current timestamp";

youtubeLeftControls = document.getElementsByClassName("ytp-left-controls")[0];
youtubePlayer = document.getElementsByClassName('video-stream')[0];

youtubeLeftControls.appendChild(bookmarkBtn);
bookmarkBtn.addEventListener("click", addNewBookmarkEventHandler);
}
};

chrome.runtime.onMessage.addListener((obj, sender, response) => {
const { type, value, videoId } = obj;

if (type === "NEW") {
currentVideo = videoId;
newVideoLoaded();
} else if (type === "PLAY") {
youtubePlayer.currentTime = value;
} else if ( type === "DELETE") {
currentVideoBookmarks = currentVideoBookmarks.filter((b) => b.time != value);
chrome.storage.sync.set({ [currentVideo]: JSON.stringify(currentVideoBookmarks) });

response(currentVideoBookmarks);
}
});

newVideoLoaded();
})();

const getTime = t => {
var date = new Date(0);
date.setSeconds(t);

return date.toISOString().substr(11, 8);
};
37 changes: 37 additions & 0 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "youtube Bookmarks",
"version": "1.0.0",
"description": "Saving timestamps in YouTube videos",
"permissions": ["storage", "tabs"],
"host_permissions": ["https://*.youtube.com/*"],
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["https://*.youtube.com/*"],
"js": ["contentScript.js"]
}
],
"web_accessible_resources": [
{
"resources": [
"assets/bookmark.png",
"assets/play.png",
"assets/delete.png",
"assets/save.png"
],
"matches": ["https://*.youtube.com/*"]
}
],
"action": {
"default_icon": {
"16": "assets/ext-icon.png",
"24": "assets/ext-icon.png",
"32": "assets/ext-icon.png"
},
"default_title": "My YT Bookmarks",
"default_popup": "popup.html"
},
"manifest_version": 3
}
53 changes: 53 additions & 0 deletions popup.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
.container {
width: 280px;
color: #314d3e;
}

.title {
font-size: 14px;
font-weight: bold;
padding: 8px;
}

.textbox {
width: 100%;
font-size: 12px;
margin: 0;
padding: 0px 2px;
}

.textbox:focus {
outline: 0;
border-color: #66afe9;
}

.bookmarks {
margin: 5px 5px;
padding: 3px;
}

.bookmark {
display: flex;
border-bottom-color: #00254d;
border-bottom-width: 1px;
border-bottom-style: solid;
border-radius: 0.8rem;
padding: 3px;
padding-bottom: 7px;
margin-bottom: 7px;
}

.bookmark-title {
padding-left: 2px;
}

.bookmark-controls img {
margin: 0 4px;
width: 18px;
height: 18px;
cursor: pointer;
}
.bookmark-controls {
flex: auto;
text-align: right;
}
10 changes: 10 additions & 0 deletions popup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<link href="popup.css" rel="stylesheet" type="text/css" />
<script type="module" src="popup.js"></script>

<div class="container">
<div>
<div class="title">Your bookmarks for this video</div>

<div class="bookmarks" id="bookmarks"></div>
</div>
</div>
93 changes: 93 additions & 0 deletions popup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { getActiveTabURL } from "./utils.js";

const addNewBookmark = (bookmarks, bookmark) => {
const bookmarkTitleElement = document.createElement("div");
const controlsElement = document.createElement("div");
const newBookmarkElement = document.createElement("div");

bookmarkTitleElement.textContent = bookmark.desc;
bookmarkTitleElement.className = "bookmark-title";
controlsElement.className = "bookmark-controls";

setBookmarkAttributes("play", onPlay, controlsElement);
setBookmarkAttributes("delete", onDelete, controlsElement);

newBookmarkElement.id = "bookmark-" + bookmark.time;
newBookmarkElement.className = "bookmark";
newBookmarkElement.setAttribute("timestamp", bookmark.time);

newBookmarkElement.appendChild(bookmarkTitleElement);
newBookmarkElement.appendChild(controlsElement);
bookmarks.appendChild(newBookmarkElement);
};

const viewBookmarks = (currentBookmarks=[]) => {
const bookmarksElement = document.getElementById("bookmarks");
bookmarksElement.innerHTML = "";

if (currentBookmarks.length > 0) {
for (let i = 0; i < currentBookmarks.length; i++) {
const bookmark = currentBookmarks[i];
addNewBookmark(bookmarksElement, bookmark);
}
} else {
bookmarksElement.innerHTML = '<i class="row">No bookmarks to show</i>';
}

return;
};

const onPlay = async e => {
const bookmarkTime = e.target.parentNode.parentNode.getAttribute("timestamp");
const activeTab = await getActiveTabURL();

chrome.tabs.sendMessage(activeTab.id, {
type: "PLAY",
value: bookmarkTime,
});
};

const onDelete = async e => {
const activeTab = await getActiveTabURL();
const bookmarkTime = e.target.parentNode.parentNode.getAttribute("timestamp");
const bookmarkElementToDelete = document.getElementById(
"bookmark-" + bookmarkTime
);

bookmarkElementToDelete.parentNode.removeChild(bookmarkElementToDelete);

chrome.tabs.sendMessage(activeTab.id, {
type: "DELETE",
value: bookmarkTime,
}, viewBookmarks);
};

const setBookmarkAttributes = (src, eventListener, controlParentElement) => {
const controlElement = document.createElement("img");

controlElement.src = "assets/" + src + ".png";
controlElement.title = src;
controlElement.addEventListener("click", eventListener);
controlParentElement.appendChild(controlElement);
};

document.addEventListener("DOMContentLoaded", async () => {
const activeTab = await getActiveTabURL();
const queryParameters = activeTab.url.split("?")[1];
const urlParameters = new URLSearchParams(queryParameters);

const currentVideo = urlParameters.get("v");

if (activeTab.url.includes("youtube.com/watch") && currentVideo) {
chrome.storage.sync.get([currentVideo], (data) => {
const currentVideoBookmarks = data[currentVideo] ? JSON.parse(data[currentVideo]) : [];

viewBookmarks(currentVideoBookmarks);
});
} else {
const container = document.getElementsByClassName("container")[0];

container.innerHTML = '<div class="title">This is not a youtube video page.</div>';
}
});

9 changes: 9 additions & 0 deletions utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export async function getActiveTabURL() {
const tabs = await chrome.tabs.query({
currentWindow: true,
active: true
});

return tabs[0];
}

0 comments on commit 3a0da48

Please sign in to comment.