Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes Issue 294: Add Details feature for playing video #2712

Draft
wants to merge 15 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
.env
68 changes: 66 additions & 2 deletions background.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ chrome.windows.onFocusChanged.addListener(function (wId) {
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
//console.log(message);
//console.log(sender);
console.log('Message received in background:', message);

switch (message.action || message.name || message) {
case 'play':
Expand Down Expand Up @@ -319,8 +320,71 @@ chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
} catch (error) { console.error(error); }
} else { console.error('Permission is not granted.'); }
})
break
}
break;

case 'store-video-id':
//console.log('Storing video ID:', message.videoId); // Debugging log
if (message.videoId) {
chrome.storage.local.set({ videoId: message.videoId }, function() {
//console.log('Video ID stored:', message.videoId); // Debugging log
sendResponse({ status: 'success' });
});
} else {
sendResponse({ status: 'error', message: 'No video ID provided' });
}
return true; // Indicates that sendResponse will be called asynchronously

case 'check-switch-state':
chrome.storage.local.get('switchState', function(result) {
//console.log('Switch state:', result.switchState);
sendResponse( {isSwitchOn: result.switchState});
});
return true;

case 'fetch-new-data':
chrome.storage.local.get('videoId', async function(result) {
const videoId = result.videoId;
const apiKey = "AIzaSyA0r8em0ndGCnx6vZu1Tv6T0iyLW4nB1jI"; // Replace with your YouTube Data API key
try {
const videoInfo = await getVideoInfo(apiKey, videoId);
const channelId = videoInfo.snippet.channelId;
const channelInfo = await getChannelInfo(apiKey, channelId);

chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
//console.log("Sending message to content.js");
chrome.tabs.sendMessage(tabs[0].id, {
action: 'append-channel-info',
channelName: channelInfo.channelName,
uploadTime: new Date(videoInfo.snippet.publishedAt).toLocaleString(),
videoCount: channelInfo.videoCount,
customUrl: channelInfo.customUrl
});
});
} catch (error) {
console.error(error);
}
});
return true;

}
});
async function getVideoInfo(apiKey, videoId) {
const response = await fetch(`https://www.googleapis.com/youtube/v3/videos?part=snippet&id=${videoId}&key=${apiKey}`);
const data = await response.json();
return data.items[0];
}

async function getChannelInfo(apiKey, channelId) {
const response = await fetch(`https://www.googleapis.com/youtube/v3/channels?part=snippet,statistics&id=${channelId}&key=${apiKey}`);
const data = await response.json();
const channel = data.items[0];

const channelName = channel.snippet.title;
const uploadTime = new Date(channel.snippet.publishedAt).toLocaleString();
const videoCount = channel.statistics.videoCount;
const customUrl = channel.snippet.customUrl;

return { channelName, uploadTime, videoCount, customUrl };
}
/*-----# UNINSTALL URL-----------------------------------*/
chrome.runtime.setUninstallURL('https://improvedtube.com/uninstalled');
88 changes: 88 additions & 0 deletions js&css/extension/content.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Function to get the video ID from the URL
function getVideoIdFromUrl() {
const url = window.location.href;
const urlObj = new URL(url);
return urlObj.searchParams.get("v");
}

// Send the video ID to the background script
function sendVideoIdToBackground() {
const videoId = getVideoIdFromUrl();
if (videoId) {
//console.log('Sending video ID to background:', videoId);
chrome.runtime.sendMessage({ action: 'store-video-id', videoId: videoId }, function(response) {
//console.log('Response from background:', response);
});
}
}

// Check if the switch is on and fetch new data. Otherwise, remove the details block
function checkSwitchStateAndFetchData() {
chrome.runtime.sendMessage({ action: 'check-switch-state' }, function(response) {
if (response.isSwitchOn) {
chrome.runtime.sendMessage({ action: 'fetch-new-data' });
} else {
// Remove existing video details if the switch is off
const targetElement = document.querySelector('.channel-info')
if (targetElement) {
targetElement.remove();
}
}
});
}

document.addEventListener('DOMContentLoaded', function() {

// After DOM is loaded, check the switch state and fetch data even if the video ID is the same
checkSwitchStateAndFetchData();
sendVideoIdToBackground()

let previousVideoId = getVideoIdFromUrl();

// Listen for changes in the video URL and send the updated video ID
const observer = new MutationObserver(() => {
const currentVideoId = getVideoIdFromUrl();
if (currentVideoId !== previousVideoId) {
previousVideoId = currentVideoId;

sendVideoIdToBackground();

checkSwitchStateAndFetchData();
}
});
observer.observe(document.body, { childList: true, subtree: true });

// Listen for visibility changes to handle page navigation
document.addEventListener('visibilitychange', function() {
if (document.visibilityState === 'visible') {
checkSwitchStateAndFetchData();
}
});

// Listen for messages from the background.js script
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
if (message.action === 'append-channel-info') {
const {channelName, uploadTime, videoCount, customUrl} = message;

// Forward the message to the web-accessible/www.youtube.com/channel.js script
window.postMessage(
{
type: 'CHANNEL_INFO',
channelName,
uploadTime,
videoCount,
customUrl,
switchState: true
},
);
} else if (message.action === 'remove-channel-info') {
// Forward the message to the web-accessible/www.youtube.com/channel.js script
window.postMessage(
{
type: 'CHANNEL_INFO',
switchState: false
},
);
}
});
})
40 changes: 40 additions & 0 deletions js&css/extension/www.youtube.com/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ html {overflow-x: hidden !important}
6.0 Channel
6.1 "Play all" button
6.2 Featured content
6.3 Details
7.0 Shortcuts
8.0 Settings
8.1 ImprovedTube icon on YouTube
Expand Down Expand Up @@ -312,6 +313,45 @@ html[it-channel-hide-featured-content=true] #secondary ytd-browse-secondary-cont
padding: 0;
}

/*------------------------------------------------------------------------------
6.3 Details
------------------------------------------------------------------------------*/
.channel-info {
display: flex;
flex-direction: row;
justify-content: space-between;
gap: 2px;
align-items: center;
margin-right: 2px;
}
.channel-info .channel-name {
font-weight: bold;
display: block;
margin-bottom: 5px;
}
.channel-info .upload-time, .channel-info .video-count {
color: #555;
display: block;
margin-bottom: 5px;
width: 80px;
font-size: 13px;
font-weight: bold;
}
.channel-info .all-videos-link,
.channel-info .view-data-link {
font-family: 'Roboto', Arial, sans-serif;
font-size: 13px;
font-weight: bold;
border: none;
border-radius: 20px;
cursor: pointer;
padding: 8px 8px;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.channel-info .all-videos-link:hover,
.channel-info .view-data-link:hover {
text-decoration: underline;
}

/*------------------------------------------------------------------------------
7.0 SHORTCUTS
Expand Down
1 change: 1 addition & 0 deletions js&css/web-accessible/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ ImprovedTube.init = function () {
this.myColors();
this.YouTubeExperiments();
this.channelCompactTheme();
this.channelDetails();

if (ImprovedTube.elements.player && ImprovedTube.elements.player.setPlaybackRate) {
ImprovedTube.videoPageUpdate();
Expand Down
111 changes: 111 additions & 0 deletions js&css/web-accessible/www.youtube.com/channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,114 @@ ImprovedTube.channelCompactTheme = function () {
compact.styles = []
}
}

/*------------------------------------------------------------------------------
4.6.4 Details
------------------------------------------------------------------------------*/
ImprovedTube.channelDetails = function () {
function createChannelInfo(channelName, uploadTime, videoCount, customUrl) {
//console.log('Creating channel info:', channelName, uploadTime, videoCount, customUrl); // Debugging log
const container = document.createElement('div');
container.className = 'channel-info';

const channelInfoContainer = document.createElement('div');

if (uploadTime) {
const uploadTimeElement = document.createElement('span');
uploadTimeElement.className = 'upload-time';
uploadTimeElement.textContent = `${uploadTime}`;
channelInfoContainer.appendChild(uploadTimeElement);
container.appendChild(channelInfoContainer);
}
if (videoCount) {
const videoCountElement = document.createElement('span');
videoCountElement.className = 'video-count';
videoCountElement.textContent = ` (${videoCount} videos)`;
channelInfoContainer.appendChild(videoCountElement);
container.appendChild(channelInfoContainer);
}

// All Videos Link
const allVideosLink = document.createElement('a');
allVideosLink.className = 'all-videos-link';
allVideosLink.href = `https://www.youtube.com/${customUrl}/videos`;
allVideosLink.title = 'All Videos';

const svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svgElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
svgElement.setAttribute('viewBox', '0 0 24 24');
svgElement.setAttribute('width', '24px');
svgElement.setAttribute('height', '24px');
svgElement.setAttribute('fill', 'gray');
container.appendChild(allVideosLink);

const pathElement = document.createElementNS('http://www.w3.org/2000/svg', 'path');
pathElement.setAttribute('d', 'M4 4h6v6H4V4zm0 10h6v6H4v-6zm10-10h6v6h-6V4zm0 10h6v6h-6v-6z');
svgElement.appendChild(pathElement);

allVideosLink.appendChild(svgElement);
container.appendChild(allVideosLink);

// View Data Link
const viewDataLink = document.createElement('a');
viewDataLink.className = 'view-data-link';
viewDataLink.href = 'https://ytlarge.com/youtube/video-data-viewer/';
// Tooltip text using `title`
viewDataLink.title = 'View detailed video data';

const svgElement2 = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svgElement2.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
svgElement2.setAttribute('viewBox', '0 0 24 24');
svgElement2.setAttribute('width', '24px');
svgElement2.setAttribute('height', '24px');
svgElement2.setAttribute('fill', 'gray');
container.appendChild(viewDataLink);

const pathElement2 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
pathElement2.setAttribute('d', 'M11 7h2V5h-2v2zm1 14c-5.52 0-10-4.48-10-10S6.48 1 12 1s10 4.48 10 10-4.48 10-10 10zm-1-5h2v-6h-2v6z');
svgElement2.appendChild(pathElement2);

viewDataLink.appendChild(svgElement2);
container.appendChild(viewDataLink);

// CSS for this Detail area is added in extension/styles.css
return container;
}

document.addEventListener('DOMContentLoaded', function() {
// Listen for messages from the content.js script
window.addEventListener('message', (event) => {
// Only process messages with the expected type
if (event.source === window && event.data.type === 'CHANNEL_INFO') {
const { channelName, uploadTime, videoCount, customUrl, switchState } = event.data;

const observer = new MutationObserver((mutationsList, observer) => {
const targetElement = document.querySelector('ytd-video-owner-renderer.style-scope.ytd-watch-metadata');

if (targetElement) {
// Stop observing once the target is found
observer.disconnect();

if(switchState) {
const channelInfo = createChannelInfo(channelName, uploadTime, videoCount, customUrl);

// Removing existing channel info if present
const existingChannelInfo = targetElement.querySelector('.channel-info');
if (existingChannelInfo) {
existingChannelInfo.remove();
}
targetElement.appendChild(channelInfo);
} else {
targetElement.remove();
}
} else {
console.log('Target element not found');
}
});

// Start observing the body for child node changes
observer.observe(document.body, { childList: true, subtree: true });
}
});
});
}
3 changes: 2 additions & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@
"js&css/extension/www.youtube.com/general/general.js",
"js&css/extension/www.youtube.com/appearance/sidebar/sidebar.js",
"js&css/extension/www.youtube.com/appearance/comments/comments.js",
"js&css/extension/init.js"
"js&css/extension/init.js",
"js&css/extension/content.js"
],
"matches": [
"https://www.youtube.com/*"
Expand Down
Loading