Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extension is no longer working after browser updates and manifest v3 (SOLVED). #47

Closed
XanSama opened this issue Dec 18, 2023 · 37 comments
Closed
Assignees
Labels

Comments

@XanSama
Copy link
Contributor

XanSama commented Dec 18, 2023

AudioPick_ManifestV3.zip

I don't feel like submitting a pull request but the attached is a rewritten version of the plugin that is confirmed working with the latest builds of Chrome and Edge (major version 120).

If the author wants they can update the repo/published plugin.
If not this can be extracted and loaded as an unpacked extension as long as developer mode is enabled in the browser.

@lrq3000
Copy link

lrq3000 commented Dec 19, 2023

Thank you for making this but please make a PR, not only this will help devs merge faster and with proper attribution, this will also help users ensure there is no rogue code as happend with other popular extensions.

@necropola
Copy link
Member

necropola commented Dec 20, 2023

Interesting!

I could create a manifest-v3 branch against which you could file a PR, i. .e

  • clone the repo
  • create a new branch based on the manifest-v3 branch
  • add your stuff and remove everything that is no longer needed
  • commit and push to your branch
  • create a Pull Request from your branch against the manifest-v3 branch

@necropola
Copy link
Member

necropola commented Dec 20, 2023

@XanSama I've created a manifest-v3 branch and made it the (temporary) default for the repository. Feel free to file a PR with your changes.

I could of course just update the branch using the zip file you have attached, but I would really like to give proper attribution.

Cheers

@necropola
Copy link
Member

necropola commented Dec 23, 2023

@XanSama Are you sure that the attachShadow hook actually works or rather helps to manipulate audio/video elements which we otherwise couldn't access?

I think the biggest challenge are sites which do not insert their audio/video elements into the DOM tree at all, e. g. Spotify which (according to Chrome Developer Tools / Heap Snaphot) has a Detached Video Element. Getting access to those would be really cool.

The resources folder of this repository also has an example (index.html + index.js) which creates and plays a hidden_audio element without inserting it into the DOM tree and hence is hiding it from AudioPick.

@XanSama
Copy link
Contributor Author

XanSama commented Dec 23, 2023

AudioPick-v0.3.6.zip

Good catch on the attachShadow; site I was testing with apparently doesn't always use them.
I've updated the resources example with a shadowRoot test and confirmed that this version is working with it.

Unfortunately this version required blanket host_permissions for <all_urls> as we need to be able to inject script into WORLD:MAIN to make the hooks work as intended.

I also added a hook for HTMLMediaElement.prototype.play and that resolves the issue with the hidden object in the test page.

Confirmed working on all tests, Spotify, new Reddit, and YouTube.

Still don't feel like making a PR so here's another attachment.
Don't worry about attribution.

@necropola
Copy link
Member

necropola commented Dec 23, 2023

@XanSama Nice!

I did not know about this Hooking Trick (inject a function into Element.prototype.method) before I saw it in your code for attaching the observer to ShadowRoots.

I really woudln't call myself a software developer, especially not with JavaScript which I kinda hate with a passion. Golang is much more up my alley.

Nonetheless (after reading your code) I already thought that hooking into a HTMLMediaElement method might be what we need for sites like Spotify. Though my idea was to somehow hook into the constructor itself and remember those deatched elements for when we want to switch their sinkId.

Your approach (hooking the play method) is simpler (more elegant) and avoids AudioPick holding onto detached elements which actually should be garbage collected, but it requires the play method to be triggered after changing the assigned sinkId.

By the way, I'm perfectly happy with you providing your code/changes as attachments to this issue. I'll soon update the manifest-v3 branch with a version based on your code. I'll just do some cosmetic changes and try to fix some errors, e. g. when the popup tries to send a message to a tab which does not have a listener yet.

I'm also currently working on a new version of the Rain-Fighters/AudioPick web site (using hugo) which I want to have proper documentation about how the new (manifest-v3) version of AudioPick works and where I got the code from.

As soon as the web site is ready and I've tested, understood and documented the v3 version of the extension well enough, I'll update the Chrome Web Store page, too. (Probably early 2024).

Which reminds me .... Merry Christmas and a Happy New Year to everyone and a BIG THANK YOU to @XanSama who made us this xmas present.

@necropola
Copy link
Member

@XanSama Can you give me your GitHub no-reply email address so I can add a proper Co-authored-by: ... entry to the commit message for importing your changes? You'll find it under Settings/Email/Keep my email address private and it should look like this

I basically need to know those leading digits ...

@XanSama
Copy link
Contributor Author

XanSama commented Dec 24, 2023

Address is: "109573827+XanSama"...
AudioPick-v0.3.7.zip

I'd initially not hooked the constructor because it doesn't seem to be reliably called for several ways that the objects can be created but your comment inspired me to take another look. I've updated it to hook Document.prototype.createElement in addition to the others as in theory that needs to be called to create an element outside of the DOM/shadowDOM. I've also adjusted the createElement() and play() hooks to create an eventListener per audio/video element that watches for the custom event "changeSinkId". This allows us to set the sinkId on all elements immediately rather than waiting for the next play(). Also added a dirty catch for that runtime error to suppress it; could probably actually handle the error but it doesn't seem to matter in my testing.

@necropola
Copy link
Member

necropola commented Dec 24, 2023

Hmm, does hooking Document.prototype.createElement also catch hidden_audio = new Audio(...)? I really like the idea of the custom event listener, but I also feel like the codes is getting a bit too complex (loads of special cases).

Brainstorming ....

  • There are 2 sources for the audio/video elements we are interested in:
    • (A) Those that are already in the DOM tree when our scripts are inserted
    • (B) Those that are dynamically created and either inserted into the DOM, Shadow DOM or not at all.
  • We can find type (A) elements by searching the DOM tree once. And we add the custom EventListener for each audio/video element we find
  • We can find type (B) elements by hooking a/the HTMLMediaElement constructor and add the custom EventListener on object creattion
  • Updating the sinkId is triggered for both (A) and (B) elements in the same way, i. e. by sending the changeSinkId event

Would that work?

Also, let me update the extension once on GitHub with your 0.3.6 version and my white-space/EOL fixes so that you can base your 0.3.7 version on that. OK?

@XanSama
Copy link
Contributor Author

XanSama commented Dec 24, 2023

Unfortunately not. The play() hook does address that though. Honestly we may be able to do without the createElement hook and just use the play() hook with the event listener for immediate sink changes.

@necropola
Copy link
Member

yeah, lets not make things too complicated. I'll push your 0.3.6 version in a moment. Please use the updated version on GitHub as a base for you next version (with the custom event listener added via the play() hook).

@necropola
Copy link
Member

necropola commented Dec 24, 2023

@XanSama pushed your 0.3.6 version + some white space (4 spaces -> 1 tab) and EOL (CRLF -> LF) cleanup. 😸

Also, I'm wondering, if we still need the ShadowRoot hook. Don't we catch elements from the Shadow DOM via our play hook (+ custom event listener), too?

@XanSama
Copy link
Contributor Author

XanSama commented Dec 24, 2023

AudioPick-manifest-v3_0.3.7.zip

Re-based and added the event trigger.
Left the createElement hook out for now. You can pull it from the last zip before this one if it ends up looking useful.

Also cleaned up the resources example a little.

Edited to correct attachment.

@necropola
Copy link
Member

@XanSama hmm, looks like you attached the wrong (same as before) archive.

@XanSama
Copy link
Contributor Author

XanSama commented Dec 24, 2023

Woops. Edited with correct version now.

@necropola
Copy link
Member

necropola commented Dec 24, 2023

@XanSama Imported and pushed 0.3.7. Looks good!

I really like the changeSinkId event method for immediate sink switching on Spotify and the likes ... but it's very late/early over here in Germany now and I need to get some sleep ... Merry Christmas!

P. S.: I'm still wondering whether we actually need to attach to ShadowRoots or even need to observe the DOM tree with the event trigger in place ... hmm ... Good Night!

@XanSama
Copy link
Contributor Author

XanSama commented Dec 24, 2023

Merry Christmas!
Hah. I thought I'd tested that and it hadn't worked but I've retested it like that and it seems like a Christmas miracle.

Tests OK on all examples, Reddit, and Spotify using only the play() hook.

It seems to test OK with the shadowRoot code disabled as well... theoretically if the site managed to load/create an element in the shadowDom that the user could directly interact with -before- our content script loaded it wouldn't catch that with the shadowRoot code disabled but I'm having a hard time imagining how one would even do that to test...

Tentatively attaching
main.zip
a stripped down main.js that only implements the hook/eventListener.

@necropola
Copy link
Member

necropola commented Dec 24, 2023

@XanSama Almost!

The issue is that clicking play⏯️ on a visible <audio/> (or <video/>) element' does not appear to trigger the HTMLMediaElement's play() method, hence it does not insert our changeSinkId listener.

To reproduce the issue use our example page and

  • set the domain or gbobal sink to something other than default
  • reload the page
  • click the Add new Visible and/or Add Visible in shadowRoot button
  • start the audio by clicking play⏯️ on the now visible element(s)
  • observe that it still uses the default audio device

I still think that the custom event listener is the way to go, i. e. the actual sinkId switching should always be handled by dispatching the changeSinkId event, but we use different methods to inject the listener.

(A) Visible/Attached HTMLMediaElements

These are either already in the DOM tree or are inserted later. The former we catch by traversing the tree once (-> setAllSinks() rename to prepareAllSinks()) and the latter we catch by using a Mutation Observer (we originally had one).

But instead of (or additionally to) changing the sinkId we insert our event listener for changeSinkId events. Note that elements found by the observer mighht already have the event listener added, if their play() method has been invoked while they were still detached. See (B).

The worker.js script then does not need to call setAllSinks() anymore but just dispatch the changeSinkId event. At least that's the idea.

(B) Detached HTMLMediaElements

Those are dynamically created at runtime and are usually not inserted into the DOM tree later. We assume that the only way to activate them while they are not visible is by calling their play() method, hence we insert our event listener by hooking into the prototype's play() method.

Note that dynamically created elements might already have the event listener, when their play() method is invoked for the first time after they have been attached to the DOM tree and have been found by the observer. See (A).

Also note that for this approach it does not matter whether an element was attached to a shadowRoot or not. Or am I overlooking something?

What do you think?

P. S.: I havent commited and pushed the new main.js due to the above mentioned issue.

P. P. S.: I assume that we still need the main.js script since hooking the prototype play() method does not work from the content.js script, right?

P.P.S.: Also, I'm still getting some errors ... (Context/URL does not matter)
image

P. P. P. S: And it looks liike it does not work on soundcloud. I've played around a bit with the developer console and it looks like soundcloud has DetachedAudioElements into which we apparently manage to inject our event listener via the prototype play() hook. But whenever main.js executes thisElement.setSinkId(top.activeSinkId) the activeSinkId is "default" and not the value that was chosen. Like we have some kind of race condition ...

@XanSama
Copy link
Contributor Author

XanSama commented Dec 25, 2023

Ahh, that was what I ran in to in my initial testing but missed it after I adjusted the play/pause buttons because I wasn't interacting directly with the media element.

Confirmed / reproduced / resolved.
Mutation observer and attachShadow hook are back in place to address the regression.

I've updated the code that sets the sinkId to set the sinkId and add a listener to an object on first pass, or leave it alone if it already has a listener. Objects generally all handle their own sinkId updates after the initial set.

I tweaked the injection (set it to all frames) and the content.js message handling (only register a handler in the top window).
The async error has also been resolved. I can't get it to pop any more errors but feel free to give it a kick.

SoundCloud appears to be using AudioContext objects for their streaming playback. I didn't know those existed until just now but I've implemented a hook of them as well in this version using the same logic/event handling as the existing functions.

AudioPick_v0.3.8.zip

Tests OK on all resource tests using direct media controls and javascript triggers. Tests OK on Spotify, Soundcloud, Youtube, and Reddit. Let me know if I missed anything!


Re:PPS - Yes, the function prototypes can't be hooked from the ISOLATED world content.js runs in, and main.js can't readily message with the rest of the extension from the MAIN world it runs in so both are required currently.

@necropola
Copy link
Member

necropola commented Dec 25, 2023

Yeah, looks a lot better now. Still some minor issues, but I'll push 0.3.8 after some further tests and some more white space/indention cleanup, i. e. you need to base your next version (I'm sure there will be one 😉) on the upcoming push to GitHub.

Meanwhile, thanks a LOT for your work. The fact that AudioPick now supports Spotify and Souncloud, i. e. sites which do not insert their Media Elements into the DOM tree, is fantastic. Not to mention all the changes that were needed to adjust the extension for Manifest V3. ⭐ ⭐ ⭐

@necropola
Copy link
Member

necropola commented Dec 25, 2023

@XanSama I've pushed 0.3.8 + some more white space cleanup and minor adjustments.

One thing that still does not work (reliably) is opening a SoundCloud link in a new tab, e. g.

https://soundcloud.com/arenanet/gw2-heart-of-thorns-tarir-the-forgotten-city?in=arenanet/sets/gw2-heart-of-thorns-ost&utm_source=clipboard&utm_medium=text&utm_campaign=social_sharing

which then automatically starts to play and apparently does not inject the listener. Interestingly it seems to work, when you have stored a domain or global device other than "default". A timing issue?

I was also wondering about this:

if (
(targetElement.constructor === AudioContext) &&
(window.activeSinkId === "default")
) {

  • Why is this even needed and
  • shouldn't it be testing for e.detail === "default"?

I mean, we are setting window.activeSinkId before dispatching the message, but the less dependencies from global variables the better, right?

Cheers

@XanSama
Copy link
Contributor Author

XanSama commented Dec 25, 2023

That check is needed because AudioContext objects don't support "default" as a sinkId. If you want an AudioContext to use the default sinkId you need to submit a blank value instead. The SoundCloud issue you describe may be related to that; I realize I didn't put the same logic in the code that sets the initial sinkId.

You're right that we should be using e.detail there; missed that one in my haste.

I've just done some quick testing and it seems like all objects will accept a blank sinkId and interpret it as default so I've removed the object type check and now just set the sinkId to "" if the selected device is "default" regardless. I've also added the logic to the code that sets the initial value (just below the lines you quoted).

main.zip

If that doesn't resolve the SoundCloud issue then we may need to take a look at the logic in content.js for injectSinkId() but I can't picture how it would cause issues currently.

@necropola
Copy link
Member

necropola commented Dec 26, 2023

@XanSama Thansk a lot!

I still (even with the new main.js) occasionally manage to get SoundCloud to not work, i. e. it appears that the listener did not get injected. Even not when stopping and starting audio again using the controls at the bottom. Ususally reloading the page helps. I wonder, how they trigger (auto)play without AudioPick being able to inject the listener, i. e. how they sometimes manage to set everything up (create all detached Audio elements and start the audio), before the extension is able to inject itself into any prototype methods.

The other thing I'm still not (fully) convinced of is that we actually need to handle shadowRoots. IMHO we either get those elements via injecting into prototype.play (while they are still detached) or via the Observer (when added to the DOM tree and becoming visible).

Anyway, I'm VERY HAPPY about the current state/functionality of the Manifest V3 Version. Hence I'm heading towards releasing it on GitHub and then on the Chrome Webstore, i. e.

  • Prettify the popup.html (including a new icon)
  • Implement/Update the new rain-fighters/AudioPick web site (using hugo)
  • Update the documentation on GitHub (README.md, CHANGELOG.md)
  • Check for / fix typos in comments
  • Adjust code style, e. g. placement of braces (if you don't mind)
  • Merge the manifest-v3 branch back into master/main
  • Release on GitHub
  • Update the information on the Chrome Webstore
  • Release on the Chrome Webstore

Thanks again for this 🎄WONDERFUL CHRISTMAS PRESENT 🎄

@necropola
Copy link
Member

necropola commented Dec 26, 2023

@XanSama I'm thinking about removing the option to set/store a global audio sink/device to reduce the number of unneeded/unwanted microphone access exceptions added by the extension.

Since we are now able to set/store per domain sinks/devices (which we could not before), we would loose very littlle functionality, but win a lot of user acceptance.

This also better matches the fact that we do not have a global options panel and that you can only open the popup for HTTPS tabs/sites.

What do you think?

@XanSama
Copy link
Contributor Author

XanSama commented Dec 27, 2023

I still (even with the new main.js) occasionally manage to get SoundCloud to not work, i. e. it appears that the listener did not get injected. Even not when stopping and starting audio again using the controls at the bottom. Ususally reloading the page helps. I wonder, how they trigger (auto)play without AudioPick being able to inject the listener, i. e. how they sometimes manage to set everything up (create all detached Audio elements and start the audio), before the extension is able to inject itself into any prototype methods.

I'm unable to replicate this on my end. If it is a race condition I wonder if it could be mitigated by adjusting the load order of the scripts in manifest.json so main.js loads before content.js? Might be worth a try.

The other thing I'm still not (fully) convinced of is that we actually need to handle shadowRoots. IMHO we either get those elements via injecting into prototype.play (while they are still detached) or via the Observer (when added to the DOM tree and becoming visible).

The issue with shadowRoots is that if they're created with the mode:closed option the observer won't catch them and our code in main.js wont be able to access them or their children after creation. If we don't find them with the observer or initial document parsing and they don't call .play() then we're SoL. By hooking attachShadow and attaching the observer to the shadowRoot's handle at the time of creation we avoid being locked out.

There's actually a case the current code doesn't address: if the page has created a closed shadowRoot -before- our code loads that shadowRoot and its children are invisible to us. We could address this using the chrome.dom.openOrClosedShadowRoot API from content.js for our queryShadowSelectorAll function, but then we'd need to move/copy some of the observer & sinkId/event code into content.js to get value from it.

If we wanted to do that the function would be something like this:

function queryShadowSelectorAll(selectors, e = document) {
	// Use chrome.dom API to find even pre-existing "closed" shadowRoots.
	const getShadowRoot = chrome.dom.openOrClosedShadowRoot;
	// Recursive function to locate all shadowRoots for the provided element.
	const getShadowRoots = function (el) {
		return Array.from(el.querySelectorAll("*"))
			.filter((ce) => Boolean(getShadowRoot(ce)))
				.flatMap((ce) => [
					getShadowRoot(ce),
					...getShadowRoots(getShadowRoot(ce))
				]);
	};
	var results = [];
	// Get every element from every shadowRoot.
	getShadowRoots(e).forEach(function (s) {
		s.querySelectorAll(":host *").forEach(
			function (el) {
				results.push(el);
			}
		);
	});
	// Filter results to our selectors.
	return results.filter(
		function (r) {
			return r.matches(selectors);
		}
	);
};

Adjust code style, e. g. placement of braces (if you don't mind)

I don't mind. Originally I had it formatted to please jslint but you should format it however you want to maintain it.

I'm thinking about removing the option to set/store a global audio sink/device to reduce the number of unneeded/unwanted microphone access exceptions added by the extension.
Since we are now able to set/store per domain sinks/devices (which we could not before), we would loose very littlle functionality, but win a lot of user acceptance.

That's fair. It does seem unlikely that someone would have a default audio device set for their workstation and want to use a different default for all browsing. I have no strong feelings one way or another on this change.

This also better matches the fact that we do not have a global options panel and that you can only open the popup for HTTPS tabs/sites.

I'm not sure I'm a fan of blanket disabling http coverage. I guess it's a layer of security against a targeted MITM from someone who knew the extension was in use but that seems... unlikely. Definitely makes testing less convenient if you're not running SSL on your dev server. I'll defer to you on this as the maintainer/publisher of the version in the Chrome store.

Regarding not having an options panel: I was just pondering if we should maybe make one to at least be able to see and remove stored domain preferences. Can't list the devices easily from there but displaying stored data should be doable.

I've also been wondering if it's maybe worthwhile to implement something similar to the sinkId change we're doing but for input devices. Not to actually change input devices used by the websites (though we could, I suppose) but to force them to use a non-existent sink or otherwise disable actual access to the microphone even though we granted the permission...

@necropola
Copy link
Member

necropola commented Dec 27, 2023

I'm unable to replicate this on my end. If it is a race condition I wonder if it could be mitigated by adjusting the load order of the scripts in manifest.json so main.js loads before content.js? Might be worth a try.

I'll give it a try, i. e. change the order in manifest.json.

The issue with shadowRoots is that if they're created with the mode:closed option the observer won't catch them and our code in main.js wont be able to access them or their children after creation. If we don't find them with the observer or initial document parsing and they don't call .play() then we're SoL. By hooking attachShadow and attaching the observer to the shadowRoot's handle at the time of creation we avoid being locked out.

I think I'll just leave the shadowRoot handling as it is at the moment and wait, until we bump into actual cases in the wild which require further actions.

I'm not sure I'm a fan of blanket disabling http coverage

I thought that .setSinkId() requires a secure context anyway (https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/setSinkId)? Does chrome allow this in on unencrytped pages, too?

I'm planning to provide our test page with some additional demo links to YouTube (Music), Spotify, SoundCloud, ... on the new (SSL encrypted) rain-fighters/AudioPick web site to provide a Reference of Test(ed) Scenarios.

Regarding not having an options panel: I was just pondering if we should maybe make one to at least be able to see and remove stored domain preferences. Can't list the devices easily from there but displaying stored data should be doable.

Assuming that we are currently storing Device Names (possibly edited by the user) and not Device Ids (automatically assigned by the system), I was already thinking about lettting the popup merge-match the stored entries with the actually found device names (grey/disabled row for an unmatched stored value) and have a separate column/marker to indicate which device is currently stored as the default for that domain.

I've also been wondering if it's maybe worthwhile to implement something similar to the sinkId change we're doing but for input devices. Not to actually change input devices used by the websites (though we could, I suppose) but to force them to use a non-existent sink or otherwise disable actual access to the microphone even though we granted the permission...

Hmm, I'lll give it a thought. For now I think we should focus on minimizing the number of sites we are creating microphone exceptions for, i. e. to only those for which the user actually picked or stored a non-default device . And set it back to "ask" when the user changes it back to system default (as we already do, I think).

To me it seems like we are setting more exceptions than needed ...

@XanSama
Copy link
Contributor Author

XanSama commented Dec 27, 2023

I thought that .setSinkId() requires a secure context anyway (https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/setSinkId)? Does chrome allow this in on unencrytped pages, too?

Chrome and Edge both seem to allow it on HTTP with the <all_urls> setting.

Assuming that we are currently storing Device Names (possibly edited by the user) and not Device Ids (automatically assigned by the system), I was already thinking about lettting the popup merge-match the stored entries with the actually found device names (grey/disabled row for an unmatched stored value) and have a separate column/marker to indicate which device is currently stored as the default for that domain.

That's an excellent idea.

Hmm, I'lll give it a thought. For now I think we should focus on minimizing the number of sites we are creating microphone exceptions for, i. e. to only those for which the user actually picked or stored a non-default device . And set it back to "ask" when the user changes it back to system default (as we already do, I think).

Agreed. Currently if we manually select default from the pop-up it does set it back to ask. I was originally hoping for a more robust logic than that but didn't manage to implement anything better.

Related to both points I think it would be logical for setting a domain back to the default device to clear the stored value for that domain.

2d / 2 colors?

I like this one best. Still looks good at 16x16 for icon sizing, too.

@necropola necropola added enhancement xmas miracle it rarely happens labels Dec 27, 2023
@necropola
Copy link
Member

necropola commented Dec 28, 2023

@XanSama As you can see, I've pushed a few changes ...

Swapping the order of injected scripts did not solve my issue with not intercepting SoundCloud early enough when a new tab starts to autoplay immediately. But since a reload usually fixes it, it's not my top priority at the moment.

I have removed the option to store a global preferred (new term to avoid confusion with default) device, but I think we are still requesting microphone access too often. Probably an issue with undefined != "" != "default" and/or not correctly checking whether anything has changed and the microphone permission is actually required.

Currently, after the extension is loaded and as soon as you open/reload any HTTP(S) site, this site get's an extension managed microphone permission.

The merge/matching idea for the popup's device list is still on my table. Maybe I'll also find the reason for those unneeded microphone permissions while I'm on it.

Cheers

@necropola necropola mentioned this issue Dec 28, 2023
@XanSama
Copy link
Contributor Author

XanSama commented Dec 28, 2023

Looking good :)

At a glance I can't see why we'd be requesting mic permissions when they're not needed but admittedly I'm wiped from Christmas and have picked up a case of COVID so my brain isn't performing at peak.

Also I think I'm going to step back and let you run with it from here. I don't even actually use the extension 😅 I just picked it up because I needed an example of how to override sink IDs for a private project. Figured making it work again would be a good crash course in the related API.

That is to say... Thank you for all of your work! AudioPick has been an invaluable learning resource for me.

Happy New Year and good luck going forward :)

@necropola
Copy link
Member

necropola commented Dec 28, 2023

It was a lot of fun working with you. Glad you got something out of it, too.
Maybe we meet again another time, maybe not ... You never know ...

Best wishes for the future! Thanks a LOT! Happy New Year and Get Better Soon!

@necropola
Copy link
Member

A small change that makes a big difference: 77167ab

@necropola
Copy link
Member

necropola commented Dec 29, 2023

Looks like we should prefix the names of global variables (and functions) we insert into MAIN

Example page: https://daily.dev/blog/create-chrome-extension-with-html-css-and-javascript

Resolved by commit fab7925

image

@necropola necropola pinned this issue Dec 29, 2023
@necropola
Copy link
Member

necropola commented Dec 29, 2023

Another issue/error may occur when the extension has just been (re-)loaded,
but the active tab has not been refreshed i .e the (new version of the) content script is not yet available to respond to messages from the popup.

This is more likely to happen during development (reload extension), but may also occur when updating the extension from the Chrome Webstore.

Resolved by commit 1db7a93

image

We need to catch the error here:

// Get the active device from the current tab.
const response = await chrome.tabs.sendMessage(activeTab.id, {
action: "getActiveDevice"
});
if (response) {
activeDevice = response;
}

@necropola
Copy link
Member

I've just pushed a major popup / UI Enhancement 1db7a93 ... getting close to release.

🧨 HAPPY NEW YEAR EVERYONE 🧨

@necropola
Copy link
Member

I've just released version 0.3.8 on GitHub and uploaded it to the Chrome WebStore for review. Let's see how long that takes ...

@necropola
Copy link
Member

Version 0.3.8 has been successfully reviewed by Google, but since I found a few issues, I've decided to not publish it on the Chrome WebStore. Instead I fixed those issues, released 0.3.9 on GitHub and uploaded the new version to the WebStore for review.

@necropola
Copy link
Member

Version 0.3.9 has been published on the Chrome WebStore.

Thanks again to @XanSama for rewriting the old Manifest V2 extension for Manifest V3 and making it work again. ❤️

Closing this issue ...

@necropola necropola unpinned this issue Jan 6, 2024
@necropola necropola pinned this issue Jan 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

3 participants