mirror of
https://github.com/tubearchivist/browser-extension.git
synced 2025-04-21 10:40:11 +00:00
Compare commits
No commits in common. "master" and "v0.3.0" have entirely different histories.
13
README.md
13
README.md
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
<h1 align="center">Browser Extension for Tube Archivist</h1>
|
<h1 align="center">Browser Extension for Tube Archivist</h1>
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://addons.mozilla.org/addon/tubearchivist-companion/" target="_blank"><img src="https://tiles.tilefy.me/t/tubearchivist-firefox.png" alt="tubearchivist-firefox" title="TA Companion Firefox users" height="50" width="190"/></a>
|
<a href="https://www.tilefy.me" target="_blank"><img src="https://tiles.tilefy.me/t/tubearchivist-firefox.png" alt="tubearchivist-firefox" title="TA Companion Firefox users" height="50" width="190"/></a>
|
||||||
<a href="https://chrome.google.com/webstore/detail/tubearchivist-companion/jjnkmicfnfojkkgobdfeieblocadmcie" target="_blank"><img src="https://tiles.tilefy.me/t/tubearchivist-chrome.png" alt="tubearchivist-chrome" title="TA Companion Chrome users" height="50" width="190"/></a>
|
<a href="https://www.tilefy.me" target="_blank"><img src="https://tiles.tilefy.me/t/tubearchivist-chrome.png" alt="tubearchivist-chrome" title="TA Companion Chrome users" height="50" width="190"/></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Core Functionality
|
## Core Functionality
|
||||||
@ -45,14 +45,12 @@ After a new release here on GitHub, you'll get updates automatically in your bro
|
|||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
- **URL**: This is where your Tube Archivist instance is located. Can be a host name or an IP address. Add the port if needed at the end, e.g. `:8000`.
|
- **URL**: This is where your Tube Archivist instance is located. Can be a host name or an IP address. Add the port if needed at the end, e.g. `:8000`.
|
||||||
- **API key**: You can find your API key on the settings page (Settings -> Application -> Integrations section -> API token) of your Tube Archivist instance.
|
- **API key**: You can find your API key on the settings page of your Tube Archivist instance.
|
||||||
|
|
||||||
A green checkmark will appear next to the *Save* button if your connection is working.
|
A green checkmark will appear next to the *Save* button if your connection is working.
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
- **Continuous Cookie Sync**: Automatically and continuously update the cookie on change.
|
- **Sync YouTube cookies**: Send your cookies to TubeArchivist to use for yt-dlp requests.
|
||||||
- **Copy Now**: Copy the cookie now to TA.
|
|
||||||
- **Show Cookie**: Show the cookie on click, for copy paste.
|
|
||||||
- **Autostart**: Autostart and prioritize videos send from this extension.
|
- **Autostart**: Autostart and prioritize videos send from this extension.
|
||||||
|
|
||||||
## Test this extension
|
## Test this extension
|
||||||
@ -72,9 +70,6 @@ Symlink/copy the correct manifest file for your browser to the expected location
|
|||||||
- Open the folder containing the *manifest.json* file.
|
- Open the folder containing the *manifest.json* file.
|
||||||
- Click on *Service Worker* to open the dev tools at background.js.
|
- Click on *Service Worker* to open the dev tools at background.js.
|
||||||
|
|
||||||
Note:
|
|
||||||
- If you are running your TA dev setup outside of the container, you need to point the URL to the backend and _not_ the frontend. E.g. localhost:8000 and not localhost:3000.
|
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
- Verify that you are running the [latest version](https://github.com/tubearchivist/tubearchivist/releases/latest) of Tube Archivist as the API is under development and will change.
|
- Verify that you are running the [latest version](https://github.com/tubearchivist/tubearchivist/releases/latest) of Tube Archivist as the API is under development and will change.
|
||||||
- For testing this extension between releases, use the *unstable* builds of Tube Archivist, only for your testing environment.
|
- For testing this extension between releases, use the *unstable* builds of Tube Archivist, only for your testing environment.
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [[ $(basename "$(pwd)") != 'browser-extension' ]]; then
|
if [[ $(basename "$(pwd)") != 'tubearchivist_browserextension' ]]; then
|
||||||
echo 'not in browser-extension folder'
|
echo 'not in tubearchivist_browserextension folder'
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ async function sendData(path, payload, method) {
|
|||||||
let access = await getAccess();
|
let access = await getAccess();
|
||||||
const url = `${access.url}:${access.port}/${path}`;
|
const url = `${access.url}:${access.port}/${path}`;
|
||||||
console.log(`${method}: ${url}`);
|
console.log(`${method}: ${url}`);
|
||||||
if (!path.endsWith('cookie/')) console.log(`${method}: ${JSON.stringify(payload)}`);
|
console.log(`${method}: ${JSON.stringify(payload)}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const rawResponse = await fetch(url, {
|
const rawResponse = await fetch(url, {
|
||||||
@ -77,7 +77,7 @@ async function getAccess() {
|
|||||||
|
|
||||||
// check if cookie is valid
|
// check if cookie is valid
|
||||||
async function getCookieState() {
|
async function getCookieState() {
|
||||||
const path = 'api/appsettings/cookie/';
|
const path = 'api/cookie/';
|
||||||
let response = await sendGet(path);
|
let response = await sendGet(path);
|
||||||
console.log('cookie state: ' + JSON.stringify(response));
|
console.log('cookie state: ' + JSON.stringify(response));
|
||||||
|
|
||||||
@ -138,22 +138,54 @@ async function subscribe(url, subscribed) {
|
|||||||
async function videoExists(id) {
|
async function videoExists(id) {
|
||||||
const path = `api/video/${id}/`;
|
const path = `api/video/${id}/`;
|
||||||
let response = await sendGet(path);
|
let response = await sendGet(path);
|
||||||
if (response?.error) return false;
|
if (!response.data) return false;
|
||||||
let access = await getAccess();
|
let access = await getAccess();
|
||||||
return new URL(`video/${id}/`, `${access.url}:${access.port}/`).href;
|
return new URL(`video/${id}/`, `${access.url}:${access.port}/`).href;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getChannel(channelHandle) {
|
async function getChannelCache() {
|
||||||
const path = `api/channel/search/?q=${channelHandle}`;
|
let cache = await browserType.storage.local.get('cache');
|
||||||
try {
|
if (cache.cache) return cache;
|
||||||
return await sendGet(path);
|
return { cache: {} };
|
||||||
} catch {
|
}
|
||||||
return false;
|
|
||||||
|
async function setChannel(channelHandler, channelId) {
|
||||||
|
let cache = await getChannelCache();
|
||||||
|
cache.cache[channelHandler] = { id: channelId, timestamp: Date.now() };
|
||||||
|
browserType.storage.local.set(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getChannelId(channelHandle) {
|
||||||
|
let cache = await getChannelCache();
|
||||||
|
|
||||||
|
if (cache.cache[channelHandle]) {
|
||||||
|
return cache.cache[channelHandle]?.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let channel = await searchChannel(channelHandle);
|
||||||
|
if (channel) setChannel(channelHandle, channel.channel_id);
|
||||||
|
|
||||||
|
return channel.channel_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function searchChannel(channelHandle) {
|
||||||
|
const path = `api/channel/search/?q=${channelHandle}`;
|
||||||
|
let response = await sendGet(path);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getChannel(channelHandle) {
|
||||||
|
let channelId = await getChannelId(channelHandle);
|
||||||
|
if (!channelId) return;
|
||||||
|
|
||||||
|
const path = `api/channel/${channelId}/`;
|
||||||
|
let response = await sendGet(path);
|
||||||
|
|
||||||
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function cookieStr(cookieLines) {
|
async function cookieStr(cookieLines) {
|
||||||
const path = 'api/appsettings/cookie/';
|
const path = 'api/cookie/';
|
||||||
let payload = {
|
let payload = {
|
||||||
cookie: cookieLines.join('\n'),
|
cookie: cookieLines.join('\n'),
|
||||||
};
|
};
|
||||||
@ -163,12 +195,9 @@ async function cookieStr(cookieLines) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildCookieLine(cookie) {
|
function buildCookieLine(cookie) {
|
||||||
// 2nd argument controls subdomains, and must match leading dot in domain
|
|
||||||
let includeSubdomains = cookie.domain.startsWith('.') ? 'TRUE' : 'FALSE';
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
cookie.domain,
|
cookie.domain,
|
||||||
includeSubdomains,
|
'TRUE',
|
||||||
cookie.path,
|
cookie.path,
|
||||||
cookie.httpOnly.toString().toUpperCase(),
|
cookie.httpOnly.toString().toUpperCase(),
|
||||||
Math.trunc(cookie.expirationDate) || 0,
|
Math.trunc(cookie.expirationDate) || 0,
|
||||||
@ -177,8 +206,10 @@ function buildCookieLine(cookie) {
|
|||||||
].join('\t');
|
].join('\t');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getCookieLines() {
|
async function sendCookies() {
|
||||||
|
console.log('function sendCookies');
|
||||||
const acceptableDomains = ['.youtube.com', 'youtube.com', 'www.youtube.com'];
|
const acceptableDomains = ['.youtube.com', 'youtube.com', 'www.youtube.com'];
|
||||||
|
|
||||||
let cookieStores = await browserType.cookies.getAllCookieStores();
|
let cookieStores = await browserType.cookies.getAllCookieStores();
|
||||||
let cookieLines = [
|
let cookieLines = [
|
||||||
'# Netscape HTTP Cookie File',
|
'# Netscape HTTP Cookie File',
|
||||||
@ -198,46 +229,12 @@ async function getCookieLines() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cookieLines;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendCookies() {
|
|
||||||
console.log('function sendCookies');
|
|
||||||
let cookieLines = await getCookieLines();
|
|
||||||
let response = cookieStr(cookieLines);
|
let response = cookieStr(cookieLines);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
let listenerEnabled = false;
|
|
||||||
let isThrottled = false;
|
|
||||||
|
|
||||||
async function handleContinuousCookie(checked) {
|
|
||||||
if (checked === true) {
|
|
||||||
browserType.cookies.onChanged.addListener(onCookieChange);
|
|
||||||
listenerEnabled = true;
|
|
||||||
console.log('Cookie listener enabled');
|
|
||||||
} else {
|
|
||||||
browserType.cookies.onChanged.removeListener(onCookieChange);
|
|
||||||
listenerEnabled = false;
|
|
||||||
console.log('Cookie listener disabled');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onCookieChange(changeInfo) {
|
|
||||||
if (!isThrottled) {
|
|
||||||
isThrottled = true;
|
|
||||||
|
|
||||||
console.log('Cookie event detected:', changeInfo);
|
|
||||||
|
|
||||||
sendCookies();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
isThrottled = false;
|
|
||||||
}, 10000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
process and return message if needed
|
process and return message if needed
|
||||||
the following messages are supported:
|
the following messages are supported:
|
||||||
@ -245,8 +242,6 @@ type Message =
|
|||||||
| { type: 'verify' }
|
| { type: 'verify' }
|
||||||
| { type: 'cookieState' }
|
| { type: 'cookieState' }
|
||||||
| { type: 'sendCookie' }
|
| { type: 'sendCookie' }
|
||||||
| { type: 'getCookieLines' }
|
|
||||||
| { type: 'continuousSync', checked: boolean }
|
|
||||||
| { type: 'download', url: string }
|
| { type: 'download', url: string }
|
||||||
| { type: 'subscribe', url: string }
|
| { type: 'subscribe', url: string }
|
||||||
| { type: 'unsubscribe', url: string }
|
| { type: 'unsubscribe', url: string }
|
||||||
@ -270,12 +265,6 @@ function handleMessage(request, sender, sendResponse) {
|
|||||||
case 'sendCookie': {
|
case 'sendCookie': {
|
||||||
return await sendCookies();
|
return await sendCookies();
|
||||||
}
|
}
|
||||||
case 'getCookieLines': {
|
|
||||||
return await getCookieLines();
|
|
||||||
}
|
|
||||||
case 'continuousSync': {
|
|
||||||
return await handleContinuousCookie(request.checked);
|
|
||||||
}
|
|
||||||
case 'download': {
|
case 'download': {
|
||||||
return await download(request.url);
|
return await download(request.url);
|
||||||
}
|
}
|
||||||
@ -283,8 +272,8 @@ function handleMessage(request, sender, sendResponse) {
|
|||||||
return await subscribe(request.url, true);
|
return await subscribe(request.url, true);
|
||||||
}
|
}
|
||||||
case 'unsubscribe': {
|
case 'unsubscribe': {
|
||||||
let channel = await getChannel(request.url);
|
let channelId = await getChannelId(request.url);
|
||||||
return await subscribe(channel.channel_id, false);
|
return await subscribe(channelId, false);
|
||||||
}
|
}
|
||||||
case 'videoExists': {
|
case 'videoExists': {
|
||||||
return await videoExists(request.videoId);
|
return await videoExists(request.videoId);
|
||||||
@ -301,7 +290,7 @@ function handleMessage(request, sender, sendResponse) {
|
|||||||
})()
|
})()
|
||||||
.then(value => sendResponse({ success: true, value }))
|
.then(value => sendResponse({ success: true, value }))
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
console.log(e);
|
console.error(e);
|
||||||
let message = e?.message ?? e;
|
let message = e?.message ?? e;
|
||||||
if (message === 'Failed to fetch') {
|
if (message === 'Failed to fetch') {
|
||||||
// chrome's error message for failed `fetch` is not very user-friendly
|
// chrome's error message for failed `fetch` is not very user-friendly
|
||||||
@ -313,9 +302,3 @@ function handleMessage(request, sender, sendResponse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
browserType.runtime.onMessage.addListener(handleMessage);
|
browserType.runtime.onMessage.addListener(handleMessage);
|
||||||
|
|
||||||
browserType.runtime.onStartup.addListener(() => {
|
|
||||||
browserType.storage.local.get('continuousSync', data => {
|
|
||||||
handleContinuousCookie(data?.continuousSync?.checked || false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
<a href="#" id="ta-url" target="_blank">
|
<a href="#" id="ta-url" target="_blank">
|
||||||
<img src="/images/logo.png" alt="ta-logo">
|
<img src="/images/logo.png" alt="ta-logo">
|
||||||
</a>
|
</a>
|
||||||
<span>v0.4.1</span>
|
<span>v0.3.0</span>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<form class="login-form">
|
<form class="login-form">
|
||||||
@ -28,21 +28,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="error-out"></div>
|
<div id="error-out"></div>
|
||||||
<hr>
|
<hr>
|
||||||
<p>Cookies:</p>
|
<p>Options:</p>
|
||||||
<div class="options">
|
<div class="options">
|
||||||
<div>
|
<div>
|
||||||
<p>TA cookies: <span id="sendCookiesStatus" /></p>
|
<input type="checkbox" id="sendCookies" name="sendCookies">
|
||||||
|
<span>Sync YouTube cookies</span><span id="sendCookiesStatus"></span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="continuous-sync" name="continuous-sync">
|
|
||||||
<span>Continuous Cookie Sync</span>
|
|
||||||
</div>
|
|
||||||
<button id="sendCookies">Copy Now</button>
|
|
||||||
<button id="showCookies">Show Cookie</button><br>
|
|
||||||
<textarea id="cookieLinesResponse" readonly></textarea>
|
|
||||||
</div>
|
|
||||||
<p>Download:</p>
|
|
||||||
<div class="options">
|
|
||||||
<div>
|
<div>
|
||||||
<input type="checkbox" id="autostart" name="autostart">
|
<input type="checkbox" id="autostart" name="autostart">
|
||||||
<span>Autostart Downloads</span>
|
<span>Autostart Downloads</span>
|
||||||
@ -55,7 +46,7 @@
|
|||||||
<img src="/images/social/reddit.svg" alt="reddit-icon"></a>
|
<img src="/images/social/reddit.svg" alt="reddit-icon"></a>
|
||||||
<a href="https://www.tubearchivist.com/discord" target="_blank">
|
<a href="https://www.tubearchivist.com/discord" target="_blank">
|
||||||
<img src="/images/social/discord.svg" alt="discord-icon"></a>
|
<img src="/images/social/discord.svg" alt="discord-icon"></a>
|
||||||
<a href="https://github.com/tubearchivist/browser-extension/" target="_blank">
|
<a href="https://github.com/tubearchivist/tubearchivist" target="_blank">
|
||||||
<img src="/images/social/github.svg" alt="github-icon">
|
<img src="/images/social/github.svg" alt="github-icon">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "TubeArchivist Companion",
|
"name": "TubeArchivist Companion",
|
||||||
"description": "Interact with your selfhosted TA server.",
|
"description": "Interact with your selfhosted TA server.",
|
||||||
"version": "0.4.1",
|
"version": "0.3.0",
|
||||||
"icons": {
|
"icons": {
|
||||||
"48": "/images/icon.png",
|
"48": "/images/icon.png",
|
||||||
"128": "/images/icon128.png"
|
"128": "/images/icon128.png"
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"name": "TubeArchivist Companion",
|
"name": "TubeArchivist Companion",
|
||||||
"description": "Interact with your selfhosted TA server.",
|
"description": "Interact with your selfhosted TA server.",
|
||||||
"version": "0.4.1",
|
"version": "0.3.0",
|
||||||
"icons": {
|
"icons": {
|
||||||
"128": "/images/icon128.png"
|
"128": "/images/icon128.png"
|
||||||
},
|
},
|
||||||
|
@ -78,16 +78,6 @@ document.getElementById('sendCookies').addEventListener('click', function () {
|
|||||||
sendCookie();
|
sendCookie();
|
||||||
});
|
});
|
||||||
|
|
||||||
// show cookies
|
|
||||||
document.getElementById('showCookies').addEventListener('click', function () {
|
|
||||||
showCookies();
|
|
||||||
});
|
|
||||||
|
|
||||||
// continuous sync
|
|
||||||
document.getElementById('continuous-sync').addEventListener('click', function () {
|
|
||||||
toggleContinuousSync();
|
|
||||||
});
|
|
||||||
|
|
||||||
// autostart
|
// autostart
|
||||||
document.getElementById('autostart').addEventListener('click', function () {
|
document.getElementById('autostart').addEventListener('click', function () {
|
||||||
toggleAutostart();
|
toggleAutostart();
|
||||||
@ -113,8 +103,8 @@ function sendCookie() {
|
|||||||
|
|
||||||
function handleResponse(message) {
|
function handleResponse(message) {
|
||||||
console.log('handle cookie response: ' + JSON.stringify(message));
|
console.log('handle cookie response: ' + JSON.stringify(message));
|
||||||
let validattionMessage = `enabled, last verified ${message.validated_str}`;
|
let cookie_validated = message.cookie_validated;
|
||||||
document.getElementById('sendCookiesStatus').innerText = validattionMessage;
|
document.getElementById('sendCookiesStatus').innerText = 'validated: ' + cookie_validated;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleError(error) {
|
function handleError(error) {
|
||||||
@ -122,44 +112,20 @@ function sendCookie() {
|
|||||||
setError(error);
|
setError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
let sending = sendMessage({ type: 'sendCookie' });
|
let checked = document.getElementById('sendCookies').checked;
|
||||||
sending.then(handleResponse, handleError);
|
|
||||||
}
|
|
||||||
|
|
||||||
function showCookies() {
|
|
||||||
console.log('popup show cookies');
|
|
||||||
const textArea = document.getElementById('cookieLinesResponse');
|
|
||||||
|
|
||||||
function handleResponse(message) {
|
|
||||||
textArea.value = message.join('\n');
|
|
||||||
textArea.style.display = 'initial';
|
|
||||||
}
|
|
||||||
function handleError(error) {
|
|
||||||
console.log(`Error: ${error}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (textArea.value) {
|
|
||||||
textArea.value = '';
|
|
||||||
textArea.style.display = 'none';
|
|
||||||
document.getElementById('showCookies').textContent = 'Show Cookie';
|
|
||||||
} else {
|
|
||||||
let sending = sendMessage({ type: 'getCookieLines' });
|
|
||||||
sending.then(handleResponse, handleError);
|
|
||||||
document.getElementById('showCookies').textContent = 'Hide Cookie';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleContinuousSync() {
|
|
||||||
const checked = document.getElementById('continuous-sync').checked;
|
|
||||||
let toStore = {
|
let toStore = {
|
||||||
continuousSync: {
|
sendCookies: {
|
||||||
checked: checked,
|
checked: checked,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
browserType.storage.local.set(toStore, function () {
|
browserType.storage.local.set(toStore, function () {
|
||||||
console.log('stored option: ' + JSON.stringify(toStore));
|
console.log('stored option: ' + JSON.stringify(toStore));
|
||||||
});
|
});
|
||||||
sendMessage({ type: 'continuousSync', checked });
|
if (checked === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let sending = sendMessage({ type: 'sendCookie' });
|
||||||
|
sending.then(handleResponse, handleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleAutostart() {
|
function toggleAutostart() {
|
||||||
@ -175,7 +141,7 @@ function toggleAutostart() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// send ping message to TA backend
|
// send ping message to TA backend
|
||||||
async function pingBackend() {
|
function pingBackend() {
|
||||||
clearError();
|
clearError();
|
||||||
clearTempLocalStorage();
|
clearTempLocalStorage();
|
||||||
function handleResponse() {
|
function handleResponse() {
|
||||||
@ -204,14 +170,9 @@ function setCookieState() {
|
|||||||
clearError();
|
clearError();
|
||||||
function handleResponse(message) {
|
function handleResponse(message) {
|
||||||
console.log(message);
|
console.log(message);
|
||||||
if (!message.cookie_enabled) {
|
document.getElementById('sendCookies').checked = message.cookie_enabled;
|
||||||
document.getElementById('sendCookiesStatus').innerText = 'disabled';
|
if (message.validated_str) {
|
||||||
} else {
|
document.getElementById('sendCookiesStatus').innerText = message.validated_str;
|
||||||
let validattionMessage = 'enabled';
|
|
||||||
if (message.validated_str) {
|
|
||||||
validattionMessage += `, last verified ${message.validated_str}`;
|
|
||||||
}
|
|
||||||
document.getElementById('sendCookiesStatus').innerText = validattionMessage;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,7 +201,7 @@ function setStatusIcon(connected) {
|
|||||||
|
|
||||||
// fill in form
|
// fill in form
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
async function onGot(item) {
|
function onGot(item) {
|
||||||
if (!item.access) {
|
if (!item.access) {
|
||||||
console.log('no access details found');
|
console.log('no access details found');
|
||||||
if (item.popupFullUrl != null && fullUrlInput.value === '') {
|
if (item.popupFullUrl != null && fullUrlInput.value === '') {
|
||||||
@ -261,19 +222,18 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
document.getElementById('api-key').value = item.access.apiKey;
|
document.getElementById('api-key').value = item.access.apiKey;
|
||||||
pingBackend();
|
pingBackend();
|
||||||
addUrl(item.access);
|
addUrl(item.access);
|
||||||
setCookieState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setContinuousCookiesOptions(result) {
|
function setCookiesOptions(result) {
|
||||||
if (!result.continuousSync || result.continuousSync.checked === false) {
|
if (!result.sendCookies || result.sendCookies.checked === false) {
|
||||||
console.log('continuous cookie sync not set');
|
console.log('sync cookies not set');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('set options: ' + JSON.stringify(result));
|
console.log('set options: ' + JSON.stringify(result));
|
||||||
document.getElementById('continuous-sync').checked = true;
|
setCookieState();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setAutostartOption(result) {
|
function setAutostartOption(result) {
|
||||||
console.log(result);
|
console.log(result);
|
||||||
if (!result.autostart || result.autostart.checked === false) {
|
if (!result.autostart || result.autostart.checked === false) {
|
||||||
console.log('autostart not set');
|
console.log('autostart not set');
|
||||||
@ -287,8 +247,8 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
onGot(result);
|
onGot(result);
|
||||||
});
|
});
|
||||||
|
|
||||||
browserType.storage.local.get('continuousSync', function (result) {
|
browserType.storage.local.get('sendCookies', function (result) {
|
||||||
setContinuousCookiesOptions(result);
|
setCookiesOptions(result);
|
||||||
});
|
});
|
||||||
|
|
||||||
browserType.storage.local.get('autostart', function (result) {
|
browserType.storage.local.get('autostart', function (result) {
|
||||||
|
@ -106,13 +106,11 @@ function getBrowser() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getChannelContainers() {
|
function getChannelContainers() {
|
||||||
const elements = document.querySelectorAll(
|
const elements = document.querySelectorAll('.yt-flexible-actions-view-model-wiz, #owner');
|
||||||
'.page-header-view-model-wiz__page-header-flexible-actions, #owner'
|
|
||||||
);
|
|
||||||
const channelContainerNodes = [];
|
const channelContainerNodes = [];
|
||||||
|
|
||||||
elements.forEach(element => {
|
elements.forEach(element => {
|
||||||
if (isElementVisible(element) && window.location.pathname !== '/playlist') {
|
if (isElementVisible(element)) {
|
||||||
channelContainerNodes.push(element);
|
channelContainerNodes.push(element);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -196,38 +194,22 @@ function buildChannelButton(channelContainer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getChannelHandle(channelContainer) {
|
function getChannelHandle(channelContainer) {
|
||||||
function findeHandleString(container) {
|
let channelHandle;
|
||||||
let result = null;
|
const videoOwnerRenderer = channelContainer.querySelector('.ytd-video-owner-renderer');
|
||||||
|
|
||||||
function recursiveTraversal(element) {
|
if (!videoOwnerRenderer) {
|
||||||
for (let child of element.children) {
|
const channelHandleContainer = document.querySelector(
|
||||||
if (child.tagName === 'A' && child.hasAttribute('href')) {
|
'.yt-content-metadata-view-model-wiz__metadata-text'
|
||||||
const href = child.getAttribute('href');
|
);
|
||||||
const match = href.match(/\/@[^/]+/); // Match the path starting with "@"
|
channelHandle = channelHandleContainer ? channelHandleContainer.innerText : null;
|
||||||
if (match) {
|
} else {
|
||||||
// handle is in channel link
|
const href = videoOwnerRenderer.href;
|
||||||
result = match[0].substring(1);
|
if (href) {
|
||||||
return;
|
const urlObj = new URL(href);
|
||||||
}
|
channelHandle = urlObj.pathname.split('/')[1];
|
||||||
}
|
|
||||||
|
|
||||||
if (child.children.length === 0 && child.textContent.trim().startsWith('@')) {
|
|
||||||
// handle is in channel description text
|
|
||||||
result = child.textContent.trim();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
recursiveTraversal(child);
|
|
||||||
if (result) return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
recursiveTraversal(container);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let channelHandle = findeHandleString(channelContainer.parentElement);
|
|
||||||
|
|
||||||
return channelHandle;
|
return channelHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +247,6 @@ function buildChannelSubButton(channelHandle) {
|
|||||||
} else {
|
} else {
|
||||||
console.log('Unknown state');
|
console.log('Unknown state');
|
||||||
}
|
}
|
||||||
e.stopPropagation();
|
|
||||||
});
|
});
|
||||||
Object.assign(channelSubButton.style, {
|
Object.assign(channelSubButton.style, {
|
||||||
padding: '5px',
|
padding: '5px',
|
||||||
@ -327,7 +308,6 @@ function buildChannelDownloadButton() {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
console.log(`download: ${currentLocation}`);
|
console.log(`download: ${currentLocation}`);
|
||||||
sendDownload(channelDownloadButton);
|
sendDownload(channelDownloadButton);
|
||||||
e.stopPropagation();
|
|
||||||
});
|
});
|
||||||
Object.assign(channelDownloadButton.style, {
|
Object.assign(channelDownloadButton.style, {
|
||||||
filter: 'invert()',
|
filter: 'invert()',
|
||||||
@ -409,24 +389,6 @@ function buildVideoButton(titleContainer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getNearestLink(element) {
|
function getNearestLink(element) {
|
||||||
// Check siblings
|
|
||||||
let sibling = element;
|
|
||||||
while (sibling) {
|
|
||||||
sibling = sibling.previousElementSibling;
|
|
||||||
if (sibling && sibling.tagName === 'A' && sibling.getAttribute('href') !== '#') {
|
|
||||||
return sibling.getAttribute('href');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sibling = element;
|
|
||||||
while (sibling) {
|
|
||||||
sibling = sibling.nextElementSibling;
|
|
||||||
if (sibling && sibling.tagName === 'A' && sibling.getAttribute('href') !== '#') {
|
|
||||||
return sibling.getAttribute('href');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check parent elements
|
|
||||||
for (let i = 0; i < 5 && element && element !== document; i++) {
|
for (let i = 0; i < 5 && element && element !== document; i++) {
|
||||||
if (element.tagName === 'A' && element.getAttribute('href') !== '#') {
|
if (element.tagName === 'A' && element.getAttribute('href') !== '#') {
|
||||||
return element.getAttribute('href');
|
return element.getAttribute('href');
|
||||||
@ -491,14 +453,12 @@ function checkVideoExists(taButton) {
|
|||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
let videoId = taButton.dataset.id;
|
let aElem = taButton?.parentElement?.querySelector('a');
|
||||||
if (!videoId) {
|
let videoId = getVideoId(aElem);;
|
||||||
videoId = getVideoId(taButton);
|
if (aElem) {
|
||||||
if (videoId) {
|
taButton.setAttribute('data-id', videoId);
|
||||||
taButton.setAttribute('data-id', videoId);
|
taButton.setAttribute('data-type', 'video');
|
||||||
taButton.setAttribute('data-type', 'video');
|
taButton.title = `TA download video: ${taButton.parentElement.innerText} [${videoId}]`;
|
||||||
taButton.title = `TA download video: ${taButton.parentElement.innerText} [${videoId}]`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let message = { type: 'videoExists', videoId };
|
let message = { type: 'videoExists', videoId };
|
||||||
|
@ -18,20 +18,6 @@ hr {
|
|||||||
background-color: white;
|
background-color: white;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
button {
|
|
||||||
margin: 10px;
|
|
||||||
border-radius: 0;
|
|
||||||
padding: 5px 13px;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: #259485;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
button:hover {
|
|
||||||
background-color: #97d4c8;
|
|
||||||
transform: scale(1.05);
|
|
||||||
color: #00202f;
|
|
||||||
}
|
|
||||||
#download {
|
#download {
|
||||||
text-align: center
|
text-align: center
|
||||||
}
|
}
|
||||||
@ -42,7 +28,7 @@ button:hover {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.logo img {
|
.logo img {
|
||||||
width: 400px;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.logo span {
|
.logo span {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -62,6 +48,20 @@ button:hover {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
.submit button {
|
||||||
|
margin: 10px;
|
||||||
|
border-radius: 0;
|
||||||
|
padding: 5px 13px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #259485;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
.submit button:hover {
|
||||||
|
background-color: #97d4c8;
|
||||||
|
transform: scale(1.05);
|
||||||
|
color: #00202f;
|
||||||
|
}
|
||||||
.options {
|
.options {
|
||||||
display: block;
|
display: block;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
@ -81,8 +81,3 @@ button:hover {
|
|||||||
color: red;
|
color: red;
|
||||||
display: none; /* will be made visible when an error occurs */
|
display: none; /* will be made visible when an error occurs */
|
||||||
}
|
}
|
||||||
#cookieLinesResponse {
|
|
||||||
display: none;
|
|
||||||
width: 100%;
|
|
||||||
height: 50px;
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user