Skip to content
This repository has been archived by the owner on Feb 26, 2021. It is now read-only.

Commit

Permalink
Handle autoplay failure cases
Browse files Browse the repository at this point in the history
This checks if the tab was able to successfully start playing for Spotify and YouTube, giving an error message with instructions if it fails. It also detects the background Spotify tab not being able to play.
  • Loading branch information
ianb committed Oct 1, 2019
1 parent a32617f commit 3e24859
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 11 deletions.
6 changes: 6 additions & 0 deletions extension/background/intentRunner.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ this.intentRunner = (function() {
});
}

async failedAutoplay(tab) {
this.keepPopup();
await browser.tabs.update(tab.id, { active: true });
await browser.runtime.sendMessage({ type: "displayAutoplayFailure" });
}

showCard(cardData) {
this.keepPopup();
telemetry.add({ hasCard: true });
Expand Down
35 changes: 27 additions & 8 deletions extension/background/serviceList.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* globals content */
/* globals content, util */

this.services = {};

Expand Down Expand Up @@ -142,21 +142,24 @@ this.serviceList = (function() {
}

async activateOrOpen() {
return this.getTab(true);
return (await this.getTab(true)).tab;
}

async getTab(activate = false) {
const tabs = await this.getAllTabs();
if (!tabs.length) {
return this.context.createTab({
url: this.baseUrl,
active: activate,
});
return {
created: true,
tab: await this.context.createTab({
url: this.baseUrl,
active: activate,
}),
};
}
if (activate) {
await browser.tabs.update(tabs[0].id, { active: activate });
}
return tabs[0];
return { created: false, tab: tabs[0] };
}

async getAllTabs(extraQuery) {
Expand All @@ -168,7 +171,9 @@ this.serviceList = (function() {
}

async initTab(scripts) {
this.tab = await this.getTab();
const tabInfo = await this.getTab();
this.tab = tabInfo.tab;
this.tabCreated = tabInfo.created;
await content.lazyInject(this.tab.id, scripts);
}

Expand Down Expand Up @@ -197,6 +202,20 @@ this.serviceList = (function() {
}
return response;
}

async pollTabAudible(tabId, timeout) {
return util.trySeveralTimes({
func: async () => {
const tab = await browser.tabs.get(tabId);
if (tab.audible) {
return true;
}
return undefined;
},
returnOnTimeout: false,
timeout,
});
}
};

exports.detectServiceFromHistory = async function(services) {
Expand Down
Binary file added extension/popup/images/autoplay-instruction.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions extension/popup/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@
</div>
<div id="transcript"></div>
<div id="error-message"></div>
<div id="error-autoplay" style="display: none">
<img
src="images/autoplay-instruction.png"
style="width: 90%; height: auto"
alt="To enable autoplay, open the site preferences and select Allow Audio and Video"
/>
</div>
<div id="feedback">
<div id="feedback-prompt">
How was your last experience?
Expand Down
2 changes: 2 additions & 0 deletions extension/popup/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ this.popup = (function() {
if (message.message) {
ui.setErrorMessage(message.message);
}
} else if (message.type === "displayAutoplayFailure") {
ui.displayAutoplayFailure();
}
}

Expand Down
6 changes: 6 additions & 0 deletions extension/popup/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@ this.ui = (function() {
document.querySelector("#error-message").textContent = message;
};

exports.displayAutoplayFailure = function displayAutoplayFailure() {
document.querySelector("#error-message").textContent =
"Please enable autoplay on this site for a better experience";
document.querySelector("#error-autoplay").style.display = "";
};

exports.setIcon = function setIcon(state) {
browser.browserAction.setIcon({
16: `${state}-16.svg`,
Expand Down
2 changes: 1 addition & 1 deletion extension/services/spotify/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
this.player = (function() {
class Player extends helpers.Runner {
action_play() {
const button = document.querySelector("button[title='Play']");
const button = this.querySelector("button[title='Play']");
button.click();
}

Expand Down
16 changes: 16 additions & 0 deletions extension/services/spotify/spotify.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ this.services.spotify = (function() {
async playQuery(query) {
await this.initTab("/services/spotify/player.js");
await this.callTab("search", { query, thenPlay: true });
if (this.tabCreated) {
const isAudible = await this.pollTabAudible(this.tab.id, 2000);
if (!isAudible) {
const activeTabId = (await browser.tabs.query({ active: true }))[0]
.id;
await browser.tabs.update(this.tab.id, { active: true });
const nowAudible = await this.pollTabAudible(this.tab.id, 1000);
if (nowAudible) {
if (this.tab.id !== activeTabId) {
await browser.tabs.update(activeTabId, { active: true });
}
} else {
this.context.failedAutoplay(this.tab);
}
}
}
}

async pause() {
Expand Down
1 change: 1 addition & 0 deletions extension/services/youtube/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ this.player = (function() {
action_play() {
const button = this.querySelector("button.ytp-large-play-button");
button.click();
console.log("clicked", button);
}

action_pause() {
Expand Down
7 changes: 5 additions & 2 deletions extension/services/youtube/youtube.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ this.services.youtube = (function() {
url: searching.googleSearchUrl(`${query} youtube.com`, true),
active: true,
});
await content.lazyInject(this.tab.id, "/services/youtube/player.js");
await this.callTab("play");
this.tabCreated = true;
const isAudible = await this.pollTabAudible(this.tab.id, 2000);
if (!isAudible) {
this.context.failedAutoplay(this.tab);
}
}

async pause() {
Expand Down
40 changes: 40 additions & 0 deletions extension/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,46 @@ this.util = (function() {
return Promise.race([promise, sleeper]);
};

/** Try func() several times, eventually timing out after timeout milliseconds
* func() should return undefined when the results are indeterminate. Any other
* return value ends the attempts successfully.
*/
exports.trySeveralTimes = function({
func,
timeout,
interval,
returnOnTimeout,
}) {
timeout = timeout || 1000;
interval = interval || 100;
return new Promise((resolve, reject) => {
const intervalId = setInterval(async () => {
try {
const resp = await func();
if (resp !== undefined) {
clearTimeout(timeoutId);
clearInterval(intervalId);
resolve(resp);
}
} catch (e) {
clearTimeout(timeoutId);
clearInterval(intervalId);
reject(e);
}
}, interval);
const timeoutId = setTimeout(() => {
clearInterval(intervalId);
if (returnOnTimeout !== undefined) {
resolve(returnOnTimeout);
} else {
const exc = new Error("Timed out");
exc.name = "TimeoutError";
reject(exc);
}
}, timeout);
});
};

/** Creates a Promise with .resolve and .reject attributes, so you can pre-create it and then
* resolve it somewhere else (like after initialization has run) */
exports.makeNakedPromise = function() {
Expand Down

0 comments on commit 3e24859

Please sign in to comment.