Skip to content

Commit

Permalink
Feat/mpv profiles (#108)
Browse files Browse the repository at this point in the history
* feat: Add options menu for extension

* feat: Add options.html to manifest and storage permissions

* fix: delete option without id error

* feat: Update native client ff2mpv.py

* feat: Update ff2mpv.js to allow multiple context menus

* refactor: Remove logs

* refactor: Add new line at end of file

* feat: Update ruby client to support options

* refactor: Reduce unnecessary function calls

* refactor: Trim lines and prevent sending empty arguments

* fix: Prevent access to dead objects

* refactor: Remove uuid wrapper function

* refactor: Improve css handling for light and dark theme

* feat: Add warning message on top of options page

* refactor: Expand control flow on save/update

* refactor: Prevent unnecesary operations when deleting a new profile
  • Loading branch information
DanSM-5 authored Nov 19, 2023
1 parent 96897a8 commit 7f0eb17
Show file tree
Hide file tree
Showing 7 changed files with 426 additions and 23 deletions.
2 changes: 2 additions & 0 deletions ff2mpv
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ require "json"
len = $stdin.read(4).unpack1("L")
data = JSON.parse($stdin.read(len))
url = data["url"]
options = data["options"] || []

args = %w[--no-terminal]
args.push(*options)

# HACK(ww): On macOS, graphical applications inherit their path from `launchd`
# rather than the default path list in `/etc/paths`. `launchd` doesn't include
Expand Down
101 changes: 80 additions & 21 deletions ff2mpv.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,99 @@
const contexts = ["link", "image", "video", "audio", "selection", "frame"];

function onError(error) {
console.log(`${error}`);
console.log(`${error}`);
}

function ff2mpv(url) {
browser.tabs.executeScript({
code: "video = document.getElementsByTagName('video');video[0].pause();"
});
browser.runtime.sendNativeMessage("ff2mpv", { url: url }).catch(onError);
function ff2mpv(url, options = []) {
browser.tabs.executeScript({
code: "video = document.getElementsByTagName('video');video[0].pause();"
});
browser.runtime.sendNativeMessage("ff2mpv", { url, options }).catch(onError);
}

async function getOS() {
return browser.runtime.getPlatformInfo().then((i) => i.os);
}

getOS().then((os) => {
async function getProfiles() {
return (await browser.storage.sync.get('profiles'))['profiles'] || [];
};

async function getOptions(id) {
const profiles = await getProfiles();
const profile = profiles.find(pf => pf.id === id);

// If profile, remove empty lines
return profile
? profile.content.filter(line => !!line)
: [];
}

async function submenuClicked(info) {
switch (info.parentMenuItemId) {
case "ff2mpv":
/* These should be mutually exclusive, but,
if they aren't, this is a reasonable priority.
*/
url = info.linkUrl || info.srcUrl || info.selectionText || info.frameUrl;
if (url) {
const options = await getOptions(info.menuItemId);
ff2mpv(url, options);
}
break;
}
}

function createProfile(profile) {
browser.contextMenus.create({
parentId: "ff2mpv",
id: profile.id,
title: profile.name,
contexts,
onclick: submenuClicked,
})
}

function deleteProfile(menuItemId) {
browser.contextMenus.remove(menuItemId);
}

function updateProfile(profile) {
browser.contextMenus.update(profile.id, {
title: profile.name,
});
}

getOS().then(async (os) => {
var title = os == "win" ? "Play in MP&V" : "Play in MPV (&W)";

browser.contextMenus.create({
id: "ff2mpv",
title: title,
contexts: ["link", "image", "video", "audio", "selection", "frame"]
id: "ff2mpv",
title: "Profiles",
contexts,
});

browser.contextMenus.onClicked.addListener((info, tab) => {
switch (info.menuItemId) {
case "ff2mpv":
/* These should be mutually exclusive, but,
if they aren't, this is a reasonable priority.
*/
url = info.linkUrl || info.srcUrl || info.selectionText || info.frameUrl;
if (url) ff2mpv(url);
break;
}
// Default entry
browser.contextMenus.create({
parentId: "ff2mpv",
title,
contexts,
onclick: submenuClicked,
});

const profiles = await getProfiles();

profiles.forEach(profile => {
browser.contextMenus.create({
parentId: "ff2mpv",
id: profile.id,
title: profile.name,
contexts,
onclick: submenuClicked,
})
});

browser.browserAction.onClicked.addListener((tab) => {
ff2mpv(tab.url);
ff2mpv(tab.url);
});
});
3 changes: 2 additions & 1 deletion ff2mpv.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
def main():
message = get_message()
url = message.get("url")
options = message.get("options") or []

args = ["mpv", "--no-terminal", "--", url]
args = ["mpv", "--no-terminal", *options, "--", url]

kwargs = {}
# https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging#Closing_the_native_app
Expand Down
7 changes: 6 additions & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
"name": "ff2mpv",
"version": "4.0.0",

"options_ui": {
"page": "options/options.html",
"open_in_tab": false
},

"icons": {
"16": "icons/icon_16x16.png",
"32": "icons/icon_32x32.png",
Expand Down Expand Up @@ -40,5 +45,5 @@
]
},

"permissions": ["nativeMessaging", "contextMenus", "activeTab"]
"permissions": ["nativeMessaging", "contextMenus", "activeTab", "storage"]
}
173 changes: 173 additions & 0 deletions options/options.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
:root {
--light-theme-profile-odd: hsl(0, 0%, 95%);
--light-theme-profile-border: hsl(0, 0%, 0%);
--light-theme-color: hsl(0, 0%, 0%);
--light-theme-background: hsl(0, 0%, 100%);
--light-theme-background-input: hsl(0, 0%, 100%);
--light-theme-normal-button: hsl(300, 67%, 42%);
--light-theme-delete-button: hsl(0, 100%, 50%);
--light-theme-button-color: hsl(0, 0%, 100%);
--light-theme-normal-button-hover: hsl(300, 67%, 22%);
--light-theme-delete-button-hover: hsl(0, 100%, 30%);
--light-theme-warning-background: hsl(62, 90%, 85%);
--light-theme-warning-text: hsl(49, 26%, 20%);
--light-theme-warning-url: hsl(240, 100%, 40%);
--light-theme-warning-url-visited: hsl(271, 68%, 32%);

--dark-theme-profile-odd: hsl(255, 6%, 8%);
--dark-theme-profile-border: hsl(0, 0%, 56%);
--dark-theme-color: hsl(0, 0%, 89%);
--dark-theme-background: hsl(255, 6%, 13%);
--dark-theme-background-input: hsl(255, 6%, 28%);
--dark-theme-normal-button: hsl(300, 67%, 22%);
--dark-theme-delete-button: hsl(0, 100%, 30%);
--dark-theme-button-color: hsl(0, 0%, 89%);
--dark-theme-normal-button-hover: hsl(300, 67%, 42%);
--dark-theme-delete-button-hover: hsl(0, 100%, 50%);
--dark-theme-warning-background: hsl(49, 26%, 20%);
--dark-theme-warning-text: hsl(45, 97%, 70%);
--dark-theme-warning-url: hsl(93, 65%, 50%);
--dark-theme-warning-url-visited: hsl(93, 45%, 30%);

/* Use light theme as default */
--profile-odd: var(--light-theme-profile-odd);
--profile-border: var(--light-theme-profile-border);
--color: var(--light-theme-color);
--background: var(--light-theme-background);
--backgraund-input: var(--light-theme-background-input);
--normal-button: var(--light-theme-normal-button);
--delete-button: var(--light-theme-delete-button);
--button-color: var(--light-theme-button-color);
--normal-button-hover: var(--light-theme-normal-button-hover);
--delete-button-hover: var(--light-theme-delete-button-hover);
--warning-background: var(--light-theme-warning-background);
--warning-text: var(--light-theme-warning-text);
--warning-url: var(--light-theme-warning-url);
--warning-url-visited: var(--light-theme-warning-url-visited);
}

body {
min-width: 600px;
min-height: 300px;
display: flex;
flex-direction: column;
align-items: center;
color: var(--color);
background-color: var(--background);
box-sizing: border-box;
}

.warning {
display: flex;
font-size: 16px;
padding: 20px;
border: 2px solid var(--warning-text);
border-radius: 10px;
margin: 20px 0;
color: var(--warning-text);
background-color: var(--warning-background);
}

.warning a {
color: var(--warning-url);
}

.warning a:visited {
color: var(--warning-url-visited);
}

#profiles-wrapper {
width: 100%;
}

.profile:nth-child(2n + 1) {
background-color: var(--profile-odd);
}

.profile {
display: flex;
flex-direction: column;
gap: 10px;
box-sizing: border-box;
border: 2px solid var(--profile-border);
border-radius: 10px;
padding: 10px;
}

.profile textarea, .profile input {
color: var(--color);
background-color: var(--background-input);
}

.profile textarea:focus-visible, .profile input:focus-visible {
outline: dotted;
outline-color: var(--profile-border);
}

.profile textarea {
height: 120px;
}

.profile + .profile {
margin-top: 10px;
}

.new-profile {
border-color: var(--normal-button);
}

.button {
border-radius: 5px;
color: var(--button-color);
background-color: var(--normal-button);
}

.button:focus-visible {
outline: dotted;
outline-color: var(--profile-border);
}

.button:hover {
background-color: var(--normal-button-hover);
}

.delete-button {
background-color: var(--delete-button);
}

.delete-button:hover {
background-color: var(--delete-button-hover);
}

.buttons-wrapper {
display: flex;
justify-content: flex-end;
gap: 10px;
}

#add {
margin-top: 20px;
width: 80%;
height: 2rem;
text-align: center;
}

@media (prefers-color-scheme: dark) {
:root {
/* If browser settings use dark theme, override styles */
--profile-odd: var(--dark-theme-profile-odd);
--profile-border: var(--dark-theme-profile-border);
--color: var(--dark-theme-color);
--background: var(--dark-theme-background);
--backgraund-input: var(--dark-theme-background-input);
--normal-button: var(--dark-theme-normal-button);
--delete-button: var(--dark-theme-delete-button);
--button-color: var(--dark-theme-button-color);
--normal-button-hover: var(--dark-theme-normal-button-hover);
--delete-button-hover: var(--dark-theme-delete-button-hover);
--warning-background: var(--dark-theme-warning-background);
--warning-text: var(--dark-theme-warning-text);
--warning-url: var(--dark-theme-warning-url);
--warning-url-visited: var(--dark-theme-warning-url-visited);
}
}
21 changes: 21 additions & 0 deletions options/options.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<title>ff2mpv options</title>
<link rel="stylesheet" href="options.css">
</head>
<body>
<div class="warning">
<p>
<strong>Warning</strong>:&nbsp;Only add profiles that you trust! Arbitrary MPV flags should be considered arbitrary code, and should be reviewed carefully. <a href="https://mpv.io/manual">See Manual.</a>
</p>
</div>
<div id="profiles-wrapper">
</div>
<div id="status"></div>
<button id="add" class="button">Add</button>

<script src="../browser-polyfill.js"></script>
<script src="options.js"></script>
</body>
</html>
Loading

0 comments on commit 7f0eb17

Please sign in to comment.