commit eaf96d5e3fb1cc3eb21c8e405b92cda7dc7c57aa Author: Abdulmohsen B. A. A. Date: Sun Mar 9 00:43:41 2025 +0300 initial version. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..429ae95 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +src/src.zip \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..159aff4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# CHANGELOG + +## 0.0.1 - 2025-03-08 + +- Initial release version diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c8f664a --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b6bf822 --- /dev/null +++ b/README.md @@ -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. diff --git a/assets/options-menu.png b/assets/options-menu.png new file mode 100644 index 0000000..386a572 Binary files /dev/null and b/assets/options-menu.png differ diff --git a/assets/popup-action.png b/assets/popup-action.png new file mode 100644 index 0000000..3328049 Binary files /dev/null and b/assets/popup-action.png differ diff --git a/src/background.js b/src/background.js new file mode 100644 index 0000000..4e92ff3 --- /dev/null +++ b/src/background.js @@ -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); +}); + + + diff --git a/src/css/pure.css b/src/css/pure.css new file mode 100644 index 0000000..acdc431 --- /dev/null +++ b/src/css/pure.css @@ -0,0 +1,11 @@ +/*! +Pure v3.0.0 +Copyright 2013 Yahoo! +Licensed under the BSD License. +https://github.com/pure-css/pure/blob/master/LICENSE +*/ +/*! +normalize.css v | MIT License | https://necolas.github.io/normalize.css/ +Copyright (c) Nicolas Gallagher and Jonathan Neal +*/ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}html{font-family:sans-serif}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}.pure-g{display:flex;flex-flow:row wrap;align-content:flex-start}.pure-u{display:inline-block;vertical-align:top}.pure-u-1,.pure-u-1-1,.pure-u-1-12,.pure-u-1-2,.pure-u-1-24,.pure-u-1-3,.pure-u-1-4,.pure-u-1-5,.pure-u-1-6,.pure-u-1-8,.pure-u-10-24,.pure-u-11-12,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-2-24,.pure-u-2-3,.pure-u-2-5,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24,.pure-u-3-24,.pure-u-3-4,.pure-u-3-5,.pure-u-3-8,.pure-u-4-24,.pure-u-4-5,.pure-u-5-12,.pure-u-5-24,.pure-u-5-5,.pure-u-5-6,.pure-u-5-8,.pure-u-6-24,.pure-u-7-12,.pure-u-7-24,.pure-u-7-8,.pure-u-8-24,.pure-u-9-24{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%}.pure-u-1-12,.pure-u-2-24{width:8.3333%}.pure-u-1-8,.pure-u-3-24{width:12.5%}.pure-u-1-6,.pure-u-4-24{width:16.6667%}.pure-u-1-5{width:20%}.pure-u-5-24{width:20.8333%}.pure-u-1-4,.pure-u-6-24{width:25%}.pure-u-7-24{width:29.1667%}.pure-u-1-3,.pure-u-8-24{width:33.3333%}.pure-u-3-8,.pure-u-9-24{width:37.5%}.pure-u-2-5{width:40%}.pure-u-10-24,.pure-u-5-12{width:41.6667%}.pure-u-11-24{width:45.8333%}.pure-u-1-2,.pure-u-12-24{width:50%}.pure-u-13-24{width:54.1667%}.pure-u-14-24,.pure-u-7-12{width:58.3333%}.pure-u-3-5{width:60%}.pure-u-15-24,.pure-u-5-8{width:62.5%}.pure-u-16-24,.pure-u-2-3{width:66.6667%}.pure-u-17-24{width:70.8333%}.pure-u-18-24,.pure-u-3-4{width:75%}.pure-u-19-24{width:79.1667%}.pure-u-4-5{width:80%}.pure-u-20-24,.pure-u-5-6{width:83.3333%}.pure-u-21-24,.pure-u-7-8{width:87.5%}.pure-u-11-12,.pure-u-22-24{width:91.6667%}.pure-u-23-24{width:95.8333%}.pure-u-1,.pure-u-1-1,.pure-u-24-24,.pure-u-5-5{width:100%}.pure-button{display:inline-block;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;user-select:none;box-sizing:border-box}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-group{letter-spacing:-.31em;text-rendering:optimizespeed}.opera-only :-o-prefocus,.pure-button-group{word-spacing:-0.43em}.pure-button-group .pure-button{letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-button{font-family:inherit;font-size:100%;padding:.5em 1em;color:rgba(0,0,0,.8);border:none transparent;background-color:#e6e6e6;text-decoration:none;border-radius:2px}.pure-button-hover,.pure-button:focus,.pure-button:hover{background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))}.pure-button:focus{outline:0}.pure-button-active,.pure-button:active{box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;border-color:#000}.pure-button-disabled,.pure-button-disabled:active,.pure-button-disabled:focus,.pure-button-disabled:hover,.pure-button[disabled]{border:none;background-image:none;opacity:.4;cursor:not-allowed;box-shadow:none;pointer-events:none}.pure-button-hidden{display:none}.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{background-color:#0078e7;color:#fff}.pure-button-group .pure-button{margin:0;border-radius:0;border-right:1px solid rgba(0,0,0,.2)}.pure-button-group .pure-button:first-child{border-top-left-radius:2px;border-bottom-left-radius:2px}.pure-button-group .pure-button:last-child{border-top-right-radius:2px;border-bottom-right-radius:2px;border-right:none}.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form select,.pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;box-sizing:border-box}.pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;box-sizing:border-box}.pure-form input[type=color]{padding:.2em .5em}.pure-form input[type=color]:focus,.pure-form input[type=date]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=email]:focus,.pure-form input[type=month]:focus,.pure-form input[type=number]:focus,.pure-form input[type=password]:focus,.pure-form input[type=search]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=text]:focus,.pure-form input[type=time]:focus,.pure-form input[type=url]:focus,.pure-form input[type=week]:focus,.pure-form select:focus,.pure-form textarea:focus{outline:0;border-color:#129fea}.pure-form input:not([type]):focus{outline:0;border-color:#129fea}.pure-form input[type=checkbox]:focus,.pure-form input[type=file]:focus,.pure-form input[type=radio]:focus{outline:thin solid #129FEA;outline:1px auto #129FEA}.pure-form .pure-checkbox,.pure-form .pure-radio{margin:.5em 0;display:block}.pure-form input[type=color][disabled],.pure-form input[type=date][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=email][disabled],.pure-form input[type=month][disabled],.pure-form input[type=number][disabled],.pure-form input[type=password][disabled],.pure-form input[type=search][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=text][disabled],.pure-form input[type=time][disabled],.pure-form input[type=url][disabled],.pure-form input[type=week][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input:not([type])[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}.pure-form input:focus:invalid,.pure-form select:focus:invalid,.pure-form textarea:focus:invalid{color:#b94a48;border-color:#e9322d}.pure-form input[type=checkbox]:focus:invalid:focus,.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus{outline-color:#e9322d}.pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}.pure-form select[multiple]{height:auto}.pure-form label{margin:.5em 0 .2em}.pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}.pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}.pure-form-stacked input[type=color],.pure-form-stacked input[type=date],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=email],.pure-form-stacked input[type=file],.pure-form-stacked input[type=month],.pure-form-stacked input[type=number],.pure-form-stacked input[type=password],.pure-form-stacked input[type=search],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=text],.pure-form-stacked input[type=time],.pure-form-stacked input[type=url],.pure-form-stacked input[type=week],.pure-form-stacked label,.pure-form-stacked select,.pure-form-stacked textarea{display:block;margin:.25em 0}.pure-form-stacked input:not([type]){display:block;margin:.25em 0}.pure-form-aligned input,.pure-form-aligned select,.pure-form-aligned textarea,.pure-form-message-inline{display:inline-block;vertical-align:middle}.pure-form-aligned textarea{vertical-align:top}.pure-form-aligned .pure-control-group{margin-bottom:.5em}.pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}.pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}.pure-form .pure-input-rounded,.pure-form input.pure-input-rounded{border-radius:2em;padding:.5em 1em}.pure-form .pure-group fieldset{margin-bottom:10px}.pure-form .pure-group input,.pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}.pure-form .pure-group input:focus,.pure-form .pure-group textarea:focus{z-index:3}.pure-form .pure-group input:first-child,.pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}.pure-form .pure-group input:first-child:last-child,.pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}.pure-form .pure-group input:last-child,.pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}.pure-form .pure-group button{margin:.35em 0}.pure-form .pure-input-1{width:100%}.pure-form .pure-input-3-4{width:75%}.pure-form .pure-input-2-3{width:66%}.pure-form .pure-input-1-2{width:50%}.pure-form .pure-input-1-3{width:33%}.pure-form .pure-input-1-4{width:25%}.pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}.pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width :480px){.pure-form button[type=submit]{margin:.7em 0 0}.pure-form input:not([type]),.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form label{margin-bottom:.3em;display:block}.pure-group input:not([type]),.pure-group input[type=color],.pure-group input[type=date],.pure-group input[type=datetime-local],.pure-group input[type=datetime],.pure-group input[type=email],.pure-group input[type=month],.pure-group input[type=number],.pure-group input[type=password],.pure-group input[type=search],.pure-group input[type=tel],.pure-group input[type=text],.pure-group input[type=time],.pure-group input[type=url],.pure-group input[type=week]{margin-bottom:0}.pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.pure-form-aligned .pure-controls{margin:1.5em 0 0 0}.pure-form-message,.pure-form-message-inline{display:block;font-size:.75em;padding:.2em 0 .8em}}.pure-menu{box-sizing:border-box}.pure-menu-fixed{position:fixed;left:0;top:0;z-index:3}.pure-menu-item,.pure-menu-list{position:relative}.pure-menu-list{list-style:none;margin:0;padding:0}.pure-menu-item{padding:0;margin:0;height:100%}.pure-menu-heading,.pure-menu-link{display:block;text-decoration:none;white-space:nowrap}.pure-menu-horizontal{width:100%;white-space:nowrap}.pure-menu-horizontal .pure-menu-list{display:inline-block}.pure-menu-horizontal .pure-menu-heading,.pure-menu-horizontal .pure-menu-item,.pure-menu-horizontal .pure-menu-separator{display:inline-block;vertical-align:middle}.pure-menu-item .pure-menu-item{display:block}.pure-menu-children{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;z-index:3}.pure-menu-horizontal .pure-menu-children{left:0;top:auto;width:inherit}.pure-menu-active>.pure-menu-children,.pure-menu-allow-hover:hover>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;padding:.5em 0}.pure-menu-horizontal .pure-menu-children .pure-menu-separator,.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-horizontal .pure-menu-children .pure-menu-separator{display:block;width:auto}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-heading,.pure-menu-link{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent;cursor:default}.pure-menu-active>.pure-menu-link,.pure-menu-link:focus,.pure-menu-link:hover{background-color:#eee}.pure-menu-selected>.pure-menu-link,.pure-menu-selected>.pure-menu-link:visited{color:#000}.pure-table{border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:.5em 1em}.pure-table thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td{background-color:#f2f2f2}.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child>td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child>td{border-bottom-width:0} \ No newline at end of file diff --git a/src/icons/icon-128.png b/src/icons/icon-128.png new file mode 100644 index 0000000..843d041 Binary files /dev/null and b/src/icons/icon-128.png differ diff --git a/src/icons/icon-16.png b/src/icons/icon-16.png new file mode 100644 index 0000000..ef04618 Binary files /dev/null and b/src/icons/icon-16.png differ diff --git a/src/icons/icon-32.png b/src/icons/icon-32.png new file mode 100644 index 0000000..cb5aba0 Binary files /dev/null and b/src/icons/icon-32.png differ diff --git a/src/icons/icon-48.png b/src/icons/icon-48.png new file mode 100644 index 0000000..be2a5ad Binary files /dev/null and b/src/icons/icon-48.png differ diff --git a/src/manifest.json b/src/manifest.json new file mode 100644 index 0000000..3d80d41 --- /dev/null +++ b/src/manifest.json @@ -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" + } + } +} diff --git a/src/options.html b/src/options.html new file mode 100644 index 0000000..9a197d4 --- /dev/null +++ b/src/options.html @@ -0,0 +1,79 @@ + + + + + + Options menu for YTPTube Extension + + + + + +
+
+ Settings + +
+ + + URL to your YTPTube instance
+
+ +
+ + + Preset to use for the URL
+
+ +
+ + + Filename template to use for the output file.
+
+ +
+ + + Where to save downloaded files.
+
+ +
+ + + Username to use for authentication.
+
+ +
+ + + Password to use for authentication.
+
+ +
+ + +
+
+ + + + diff --git a/src/options.js b/src/options.js new file mode 100644 index 0000000..104f015 --- /dev/null +++ b/src/options.js @@ -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); +}); \ No newline at end of file diff --git a/src/popup.html b/src/popup.html new file mode 100644 index 0000000..02b6429 --- /dev/null +++ b/src/popup.html @@ -0,0 +1,57 @@ + + + + + YTPTube Extension + + + + + +
+
+ + + The URL to send to YTPTube instance
+
+
+ +
+
+
+ +
+
+ + + + + diff --git a/src/popup.js b/src/popup.js new file mode 100644 index 0000000..0bf1bd7 --- /dev/null +++ b/src/popup.js @@ -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')); + } +}); \ No newline at end of file