initial version.

Cette révision appartient à :
Abdulmohsen B. A. A.
2025-03-09 00:43:41 +03:00
révision eaf96d5e3f
17 fichiers modifiés avec 457 ajouts et 0 suppressions
+2
Voir le fichier
@@ -0,0 +1,2 @@
.idea
src/src.zip
+5
Voir le fichier
@@ -0,0 +1,5 @@
# CHANGELOG
## 0.0.1 - 2025-03-08
- Initial release version
+19
Voir le fichier
@@ -0,0 +1,19 @@
Copyright (c) 2025 ArabCoders
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+16
Voir le fichier
@@ -0,0 +1,16 @@
# A YTPTube extension
This extension can be used in `Chrome/Chromium browsers` and `Firefox` to add URLs to [YTPTube](https://github.com/arabcoders/ytptube) instance.
## Screenshots
![options](./assets/options-menu.png) ![popup](./assets/popup-action.png)
## Installation from store
- Install from [Firefox]()
- Install from [Chrome]()
## Usage
Configure the extension to point to your YTPTube instance in addon preferences.
BIN
Voir le fichier
Fichier binaire non affiché.

Après

Largeur:  |  Hauteur:  |  Taille: 24 KiB

BIN
Voir le fichier
Fichier binaire non affiché.

Après

Largeur:  |  Hauteur:  |  Taille: 3.8 KiB

+135
Voir le fichier
@@ -0,0 +1,135 @@
// noinspection JSUnresolvedReference
const str_keys = ["instance_url", "preset", "template", "folder", "username", "password"]
const bool_keys = ["showContextMenu"]
if (typeof chrome === 'undefined') {
let chrome = browser
}
const notify = message => chrome.notifications.create({
"type": "basic",
"iconUrl": chrome.runtime.getURL("icons/icon-128.png"),
"title": 'YTPTube Extension',
"message": message,
});
const onMenuCreated = () => {
if (chrome.runtime.lastError) {
console.log(`error creating menu item. ${chrome.runtime.lastError}`);
}
syncContextMenu().then(_ => '').catch(console.error);
}
const shouldShowContextMenu = async () => {
let item = await chrome.storage.sync.get("showContextMenu");
return 'showContextMenu' in item ? item.showContextMenu : true;
};
const syncContextMenu = async () => {
let showContextMenu = await shouldShowContextMenu();
chrome.contextMenus.update("send-to-ytptube", {visible: showContextMenu});
}
chrome.contextMenus.create({
id: "send-to-ytptube",
title: "Send to YTPTube",
contexts: ["link"]
}, onMenuCreated);
const getCurrentUrl = async () => (await chrome.tabs.query({currentWindow: true, active: true}))[0].url
const getOption = async key => {
let item = await chrome.storage.sync.get(key);
if (str_keys.includes(key)) {
return item[key] ?? '';
}
if (bool_keys.includes(key)) {
return item[key] ?? false;
}
}
const sendRequest = async user_url => {
const instanceUrl = await getOption("instance_url");
if (!instanceUrl) {
notify('YTPTube instance url not configured.');
return;
}
let headers = {};
let data = {
url: user_url,
}
const auth_username = await getOption("username");
const auth_password = await getOption("password");
if (auth_username && auth_password) {
headers['Authorization'] = 'Basic ' + btoa(`${auth_username}:${auth_password}`);
}
const preset = await getOption("preset");
if (preset) {
data['preset'] = preset;
}
const template = await getOption("template");
if (template) {
data['template'] = template;
}
const folder = await getOption("folder");
if (folder) {
data['folder'] = folder;
}
console.debug(`Sending to '${instanceUrl}'.`, data, headers.length > 0 ? 'with auth header' : 'without auth header');
const url = new URL(instanceUrl);
url.pathname = '/api/history';
const req = await fetch(url, {
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
if (200 === req.status) {
notify('Request sent successfully.');
return
}
notify(`Failed to send request. '${req.status}: ${req.statusText}'.`);
};
chrome.contextMenus.onClicked.addListener(async (info, _) => {
if (info.menuItemId !== "send-to-ytptube") {
return;
}
if (!info.linkUrl) {
notify('No link url found');
return;
}
await sendRequest(info.linkUrl);
});
chrome.runtime.onMessage.addListener(async (message) => {
if (message.command !== "send-to-ytptube") {
return;
}
let url = message.url || await getCurrentUrl();
if (!url) {
await notify('No url found');
return;
}
await sendRequest(url);
});
+11
Voir le fichier
Diff de fichier supprimé car une ou plusieurs lignes sont trop longues
BIN
Voir le fichier
Fichier binaire non affiché.

Après

Largeur:  |  Hauteur:  |  Taille: 22 KiB

BIN
Voir le fichier
Fichier binaire non affiché.

Après

Largeur:  |  Hauteur:  |  Taille: 1.4 KiB

BIN
Voir le fichier
Fichier binaire non affiché.

Après

Largeur:  |  Hauteur:  |  Taille: 3.2 KiB

BIN
Voir le fichier
Fichier binaire non affiché.

Après

Largeur:  |  Hauteur:  |  Taille: 5.5 KiB

+45
Voir le fichier
@@ -0,0 +1,45 @@
{
"manifest_version": 3,
"background": {
"service_worker": "background.js",
"scripts": [
"background.js"
]
},
"name": "YTPTube Extension",
"version": "0.0.2",
"description": "Add URLs to YTPTube instance",
"permissions": [
"activeTab",
"contextMenus",
"storage",
"notifications"
],
"homepage_url": "https://github.com/arabcoders/ytptube-extension",
"icons": {
"16": "icons/icon-16.png",
"32": "icons/icon-32.png",
"48": "icons/icon-48.png",
"128": "icons/icon-128.png"
},
"options_ui": {
"page": "options.html",
"open_in_tab": false
},
"action": {
"default_title": "Add URL to YTPTube",
"default_popup": "popup.html",
"default_icon": {
"16": "icons/icon-16.png",
"32": "icons/icon-32.png",
"48": "icons/icon-48.png",
"128": "icons/icon-128.png"
}
},
"browser_specific_settings": {
"gecko": {
"id": "ytptube@arabcoders.org",
"strict_min_version": "113.0"
}
}
}
+79
Voir le fichier
@@ -0,0 +1,79 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Options menu for YTPTube Extension</title>
<link rel="stylesheet" href="css/pure.css"/>
<style>
@media (prefers-color-scheme: dark) {
body {
color: white;
background-color: #19191c;
}
input {
background-color: #19191c;
color: white;
}
fieldset > legend {
color: white !important;
}
}
</style>
</head>
<body>
<form id="ytptube_options" class="pure-form pure-form-stacked">
<fieldset style="padding:1em">
<legend>Settings</legend>
<div class="pure-control-group">
<label for="instance_url">YTPTube URL</label>
<input type="text" class="pure-input-1" id="instance_url" placeholder="https://ytptube.example.org">
<span class="pure-form-message">URL to your YTPTube instance</span><br>
</div>
<div class="pure-control-group">
<label for="preset">Preset</label>
<input type="text" class="pure-input-1" id="preset" placeholder="Leave empty to use default preset.">
<span class="pure-form-message">Preset to use for the URL</span><br>
</div>
<div class="pure-control-group">
<label for="template">Output Template</label>
<input type="text" class="pure-input-1" id="template"
placeholder="Leave empty to use default output template.">
<span class="pure-form-message">Filename template to use for the output file.</span><br>
</div>
<div class="pure-control-group">
<label for="folder">Download Folder</label>
<input type="text" class="pure-input-1" id="folder" placeholder="Leave empty to use default download path.">
<span class="pure-form-message">Where to save downloaded files.</span><br>
</div>
<div class="pure-control-group">
<label for="username">Auth Username</label>
<input type="text" class="pure-input-1" id="username"
placeholder="Leave empty if you have not configured a authentication.">
<span class="pure-form-message">Username to use for authentication.</span><br>
</div>
<div class="pure-control-group">
<label for="password">Auth Password</label>
<input type="password" class="pure-input-1" id="password"
placeholder="Leave empty if you have not configured a authentication.">
<span class="pure-form-message">Password to use for authentication.</span><br>
</div>
<label for="showContextMenu">
<input type="checkbox" id="showContextMenu"/> Show context menu in supported sites
</label><br>
<button class="pure-button pure-input-1 pure-button-primary" type="submit">Save</button>
</fieldset>
</form>
<script src="options.js"></script>
</body>
</html>
+43
Voir le fichier
@@ -0,0 +1,43 @@
// noinspection JSUnresolvedReference
const str_keys = ["instance_url", "preset", "template", "folder", "username", "password"]
const $ = s => document.querySelector(s)
if (typeof chrome === 'undefined') {
let chrome = browser
}
document.addEventListener("DOMContentLoaded", () => {
function onError(error) {
console.log(`Error: ${error}`);
}
str_keys.forEach(k => {
chrome.storage.sync.get(k).then(r => document.querySelector(`#${k}`).value = r[k] || "", onError);
});
chrome.storage.sync.get("showContextMenu").then(r => document.querySelector("#showContextMenu").checked = r.showContextMenu || false);
});
document.getElementById("ytptube_options").addEventListener("submit", (e) => {
e.preventDefault();
let showContextMenu = document.querySelector("#showContextMenu").checked;
let data = {}
str_keys.forEach(key => data[key] = document.querySelector(`#${key}`).value)
data["showContextMenu"] = showContextMenu
chrome.storage.sync.set(data);
chrome.contextMenus.update("send-to-ytptube", {visible: showContextMenu});
chrome.notifications.create({
"type": "basic",
"iconUrl": chrome.runtime.getURL("icons/icon-128.png"),
"title": 'YTPTube Extension',
"message": "Options saved."
});
setTimeout(() => window.close(), 1000);
});
+57
Voir le fichier
@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>YTPTube Extension</title>
<link rel="stylesheet" href="css/pure.css"/>
<style>
#ytptube_popup {
min-width: 250px;
min-height: 120px;
margin: 1em;
}
@media (prefers-color-scheme: dark) {
body {
color: white;
background-color: #19191c;
}
input {
background-color: #19191c;
color: white;
}
fieldset > legend {
color: white !important;
}
}
.button-secondary {
background: rgb(66, 184, 221);
}
</style>
</head>
<body>
<form id="ytptube_popup" class="pure-form pure-form-stacked">
<div class="pure-control-group">
<label for="user_url">URL:</label>
<input id="user_url" class="pure-input-1" type="text" placeholder="https://..">
<span class="pure-form-message">The URL to send to YTPTube instance</span><br>
</div>
<div class="pure-control-group">
<button class="pure-button pure-input-1 pure-button-primary" type="submit">Send to YTPTube</button>
</div>
<br>
<div class="pure-control-group">
<button class="pure-button pure-input-1 button-secondary" type="button" id="go-to-options">
Open options page
</button>
</div>
</form>
<script src="popup.js"></script>
</body>
</html>
+45
Voir le fichier
@@ -0,0 +1,45 @@
// noinspection JSUnresolvedReference
const $ = s => document.querySelector(s)
if (typeof chrome === 'undefined') {
let chrome = browser
}
const notify = message => chrome.notifications.create({
"type": "basic",
"iconUrl": chrome.runtime.getURL("icons/icon-128.png"),
"title": 'YTPTube Extension',
"message": message
});
$("#ytptube_popup").addEventListener("submit", async (e) => {
e.preventDefault()
const url = $('#user_url').value
if (!url) {
notify('URL is required.')
return
}
await chrome.runtime.sendMessage({command: 'send-to-ytptube', url: url})
})
const getCurrentUrl = async () => (await chrome.tabs.query({currentWindow: true, active: true}))[0].url
addEventListener('DOMContentLoaded', async _ => {
let url = await getCurrentUrl()
if (url && !url.startsWith('http')) {
url = ""
}
$('#user_url').value = url || ''
})
$('#go-to-options').addEventListener('click', function () {
if (chrome.runtime.openOptionsPage) {
chrome.runtime.openOptionsPage();
} else {
window.open(chrome.runtime.getURL('options.html'));
}
});