mirror of
https://github.com/tubearchivist/browser-extension.git
synced 2025-04-20 18:20:12 +00:00
Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
017b789fe2 | |||
f9bef73540 | |||
b07acde00e | |||
60160b293b | |||
0d24520017 | |||
5f53ae52d6 | |||
c867da85f5 | |||
c8382d1d7b | |||
2703555723 | |||
68bf0fa5d7 | |||
53c5e7f077 | |||
0218d782ce | |||
c17b0172d9 | |||
c18ccd6e78 | |||
cb4082362b | |||
b28efc7960 | |||
492db3ed3c | |||
|
ea12e6bc08 | ||
bcb8f545e8 | |||
b36ac337cf | |||
|
f5152a4717 | ||
af61f2a4ab | |||
|
967a52881b | ||
d3f01b372a |
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://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://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-chrome.png" alt="tubearchivist-chrome" title="TA Companion Chrome 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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Core Functionality
|
## Core Functionality
|
||||||
@ -45,12 +45,14 @@ 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 of your Tube Archivist instance.
|
- **API key**: You can find your API key on the settings page (Settings -> Application -> Integrations section -> API token) 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
|
||||||
- **Sync YouTube cookies**: Send your cookies to TubeArchivist to use for yt-dlp requests.
|
- **Continuous Cookie Sync**: Automatically and continuously update the cookie on change.
|
||||||
|
- **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
|
||||||
@ -70,6 +72,9 @@ 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)") != 'tubearchivist_browserextension' ]]; then
|
if [[ $(basename "$(pwd)") != 'browser-extension' ]]; then
|
||||||
echo 'not in tubearchivist_browserextension folder'
|
echo 'not in browser-extension 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}`);
|
||||||
console.log(`${method}: ${JSON.stringify(payload)}`);
|
if (!path.endsWith('cookie/')) 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/cookie/';
|
const path = 'api/appsettings/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,54 +138,22 @@ 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.data) return false;
|
if (response?.error) 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 getChannelCache() {
|
|
||||||
let cache = await browserType.storage.local.get('cache');
|
|
||||||
if (cache.cache) return cache;
|
|
||||||
return { cache: {} };
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
async function getChannel(channelHandle) {
|
||||||
let channelId = await getChannelId(channelHandle);
|
const path = `api/channel/search/?q=${channelHandle}`;
|
||||||
if (!channelId) return;
|
try {
|
||||||
|
return await sendGet(path);
|
||||||
const path = `api/channel/${channelId}/`;
|
} catch {
|
||||||
let response = await sendGet(path);
|
return false;
|
||||||
|
}
|
||||||
return response.data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function cookieStr(cookieLines) {
|
async function cookieStr(cookieLines) {
|
||||||
const path = 'api/cookie/';
|
const path = 'api/appsettings/cookie/';
|
||||||
let payload = {
|
let payload = {
|
||||||
cookie: cookieLines.join('\n'),
|
cookie: cookieLines.join('\n'),
|
||||||
};
|
};
|
||||||
@ -195,9 +163,12 @@ 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,
|
||||||
'TRUE',
|
includeSubdomains,
|
||||||
cookie.path,
|
cookie.path,
|
||||||
cookie.httpOnly.toString().toUpperCase(),
|
cookie.httpOnly.toString().toUpperCase(),
|
||||||
Math.trunc(cookie.expirationDate) || 0,
|
Math.trunc(cookie.expirationDate) || 0,
|
||||||
@ -206,10 +177,8 @@ function buildCookieLine(cookie) {
|
|||||||
].join('\t');
|
].join('\t');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendCookies() {
|
async function getCookieLines() {
|
||||||
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',
|
||||||
@ -229,12 +198,46 @@ async function sendCookies() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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:
|
||||||
@ -242,6 +245,8 @@ 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 }
|
||||||
@ -265,6 +270,12 @@ 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);
|
||||||
}
|
}
|
||||||
@ -272,8 +283,8 @@ function handleMessage(request, sender, sendResponse) {
|
|||||||
return await subscribe(request.url, true);
|
return await subscribe(request.url, true);
|
||||||
}
|
}
|
||||||
case 'unsubscribe': {
|
case 'unsubscribe': {
|
||||||
let channelId = await getChannelId(request.url);
|
let channel = await getChannel(request.url);
|
||||||
return await subscribe(channelId, false);
|
return await subscribe(channel.channel_id, false);
|
||||||
}
|
}
|
||||||
case 'videoExists': {
|
case 'videoExists': {
|
||||||
return await videoExists(request.videoId);
|
return await videoExists(request.videoId);
|
||||||
@ -290,7 +301,7 @@ function handleMessage(request, sender, sendResponse) {
|
|||||||
})()
|
})()
|
||||||
.then(value => sendResponse({ success: true, value }))
|
.then(value => sendResponse({ success: true, value }))
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
console.error(e);
|
console.log(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
|
||||||
@ -302,3 +313,9 @@ 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.3.1</span>
|
<span>v0.4.1</span>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<form class="login-form">
|
<form class="login-form">
|
||||||
@ -28,12 +28,21 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="error-out"></div>
|
<div id="error-out"></div>
|
||||||
<hr>
|
<hr>
|
||||||
<p>Options:</p>
|
<p>Cookies:</p>
|
||||||
<div class="options">
|
<div class="options">
|
||||||
<div>
|
<div>
|
||||||
<input type="checkbox" id="sendCookies" name="sendCookies">
|
<p>TA cookies: <span id="sendCookiesStatus" /></p>
|
||||||
<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>
|
||||||
@ -46,7 +55,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/tubearchivist" target="_blank">
|
<a href="https://github.com/tubearchivist/browser-extension/" 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.3.1",
|
"version": "0.4.1",
|
||||||
"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.3.1",
|
"version": "0.4.1",
|
||||||
"icons": {
|
"icons": {
|
||||||
"128": "/images/icon128.png"
|
"128": "/images/icon128.png"
|
||||||
},
|
},
|
||||||
|
@ -78,6 +78,16 @@ 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();
|
||||||
@ -103,8 +113,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 cookie_validated = message.cookie_validated;
|
let validattionMessage = `enabled, last verified ${message.validated_str}`;
|
||||||
document.getElementById('sendCookiesStatus').innerText = 'validated: ' + cookie_validated;
|
document.getElementById('sendCookiesStatus').innerText = validattionMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleError(error) {
|
function handleError(error) {
|
||||||
@ -112,20 +122,44 @@ function sendCookie() {
|
|||||||
setError(error);
|
setError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
let checked = document.getElementById('sendCookies').checked;
|
let sending = sendMessage({ type: 'sendCookie' });
|
||||||
|
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 = {
|
||||||
sendCookies: {
|
continuousSync: {
|
||||||
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));
|
||||||
});
|
});
|
||||||
if (checked === false) {
|
sendMessage({ type: 'continuousSync', checked });
|
||||||
return;
|
|
||||||
}
|
|
||||||
let sending = sendMessage({ type: 'sendCookie' });
|
|
||||||
sending.then(handleResponse, handleError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleAutostart() {
|
function toggleAutostart() {
|
||||||
@ -141,7 +175,7 @@ function toggleAutostart() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// send ping message to TA backend
|
// send ping message to TA backend
|
||||||
function pingBackend() {
|
async function pingBackend() {
|
||||||
clearError();
|
clearError();
|
||||||
clearTempLocalStorage();
|
clearTempLocalStorage();
|
||||||
function handleResponse() {
|
function handleResponse() {
|
||||||
@ -170,9 +204,14 @@ function setCookieState() {
|
|||||||
clearError();
|
clearError();
|
||||||
function handleResponse(message) {
|
function handleResponse(message) {
|
||||||
console.log(message);
|
console.log(message);
|
||||||
document.getElementById('sendCookies').checked = message.cookie_enabled;
|
if (!message.cookie_enabled) {
|
||||||
if (message.validated_str) {
|
document.getElementById('sendCookiesStatus').innerText = 'disabled';
|
||||||
document.getElementById('sendCookiesStatus').innerText = message.validated_str;
|
} else {
|
||||||
|
let validattionMessage = 'enabled';
|
||||||
|
if (message.validated_str) {
|
||||||
|
validattionMessage += `, last verified ${message.validated_str}`;
|
||||||
|
}
|
||||||
|
document.getElementById('sendCookiesStatus').innerText = validattionMessage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +240,7 @@ function setStatusIcon(connected) {
|
|||||||
|
|
||||||
// fill in form
|
// fill in form
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
function onGot(item) {
|
async 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 === '') {
|
||||||
@ -222,18 +261,19 @@ 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);
|
||||||
}
|
|
||||||
|
|
||||||
function setCookiesOptions(result) {
|
|
||||||
if (!result.sendCookies || result.sendCookies.checked === false) {
|
|
||||||
console.log('sync cookies not set');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log('set options: ' + JSON.stringify(result));
|
|
||||||
setCookieState();
|
setCookieState();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setAutostartOption(result) {
|
async function setContinuousCookiesOptions(result) {
|
||||||
|
if (!result.continuousSync || result.continuousSync.checked === false) {
|
||||||
|
console.log('continuous cookie sync not set');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log('set options: ' + JSON.stringify(result));
|
||||||
|
document.getElementById('continuous-sync').checked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async 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');
|
||||||
@ -247,8 +287,8 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
onGot(result);
|
onGot(result);
|
||||||
});
|
});
|
||||||
|
|
||||||
browserType.storage.local.get('sendCookies', function (result) {
|
browserType.storage.local.get('continuousSync', function (result) {
|
||||||
setCookiesOptions(result);
|
setContinuousCookiesOptions(result);
|
||||||
});
|
});
|
||||||
|
|
||||||
browserType.storage.local.get('autostart', function (result) {
|
browserType.storage.local.get('autostart', function (result) {
|
||||||
|
@ -106,11 +106,13 @@ function getBrowser() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getChannelContainers() {
|
function getChannelContainers() {
|
||||||
const elements = document.querySelectorAll('.yt-flexible-actions-view-model-wiz, #owner');
|
const elements = document.querySelectorAll(
|
||||||
|
'.page-header-view-model-wiz__page-header-flexible-actions, #owner'
|
||||||
|
);
|
||||||
const channelContainerNodes = [];
|
const channelContainerNodes = [];
|
||||||
|
|
||||||
elements.forEach(element => {
|
elements.forEach(element => {
|
||||||
if (isElementVisible(element)) {
|
if (isElementVisible(element) && window.location.pathname !== '/playlist') {
|
||||||
channelContainerNodes.push(element);
|
channelContainerNodes.push(element);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -194,22 +196,38 @@ function buildChannelButton(channelContainer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getChannelHandle(channelContainer) {
|
function getChannelHandle(channelContainer) {
|
||||||
let channelHandle;
|
function findeHandleString(container) {
|
||||||
const videoOwnerRenderer = channelContainer.querySelector('.ytd-video-owner-renderer');
|
let result = null;
|
||||||
|
|
||||||
if (!videoOwnerRenderer) {
|
function recursiveTraversal(element) {
|
||||||
const channelHandleContainer = document.querySelector(
|
for (let child of element.children) {
|
||||||
'.yt-content-metadata-view-model-wiz__metadata-text'
|
if (child.tagName === 'A' && child.hasAttribute('href')) {
|
||||||
);
|
const href = child.getAttribute('href');
|
||||||
channelHandle = channelHandleContainer ? channelHandleContainer.innerText : null;
|
const match = href.match(/\/@[^/]+/); // Match the path starting with "@"
|
||||||
} else {
|
if (match) {
|
||||||
const href = videoOwnerRenderer.href;
|
// handle is in channel link
|
||||||
if (href) {
|
result = match[0].substring(1);
|
||||||
const urlObj = new URL(href);
|
return;
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,6 +265,7 @@ 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',
|
||||||
@ -308,6 +327,7 @@ 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()',
|
||||||
|
@ -18,6 +18,20 @@ 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
|
||||||
}
|
}
|
||||||
@ -28,7 +42,7 @@ hr {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.logo img {
|
.logo img {
|
||||||
width: 100%;
|
width: 400px;
|
||||||
}
|
}
|
||||||
.logo span {
|
.logo span {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -48,20 +62,6 @@ hr {
|
|||||||
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,3 +81,8 @@ hr {
|
|||||||
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