Add embedded sharing to Google Play Music from Chrome ext.

This commit is contained in:
Jonathan Cremin 2015-12-01 10:57:56 +00:00
parent 93ae893400
commit dbc7f4806d
9 changed files with 170 additions and 37 deletions

BIN
chrome/icon-blue-128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

BIN
chrome/icon-blue-16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
chrome/icon-blue-48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

View file

@ -1,19 +1,31 @@
{
"name" : "Match Audio",
"version" : "0.2.2",
"version" : "0.3.1",
"description" : "Match Audio makes sharing from music services better.",
"background" : {
"persistent": false,
"scripts": [ "background.js" ]
"scripts": [
"src/background.js"
]
},
"content_scripts": [
{
"matches": ["https://play.google.com/music/*"],
"js": ["src/lib/selector-listener.js", "src/google.js"]
}
],
"page_action" : {
"default_icon": {
"16": "icon-16.png",
"48": "icon-48.png",
"128": "icon-128.png"
}
},
"default_title": "Find matches for this music on Match Audio"
},
"permissions" : [ "declarativeContent", "https://match.audio/" ],
"permissions" : [
"declarativeContent",
"https://play.google.com/music"
],
"icons": {
"16": "icon-16.png",
"48": "icon-48.png",

View file

@ -1,45 +1,36 @@
"use strict";
const apiUrl = 'https://match.audio';
chrome.runtime.onInstalled.addListener(function() {
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
chrome.runtime.onInstalled.addListener(() => {
chrome.declarativeContent.onPageChanged.removeRules(undefined, () => {
chrome.declarativeContent.onPageChanged.addRules([
{
conditions: [
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostSuffix: 'beatsmusic.com', pathPrefix: "/album" },
pageUrl: { hostEquals: 'www.deezer.com', pathPrefix: '/album' },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostSuffix: 'beatsmusic.com', pathPrefix: "/track" },
pageUrl: { hostEquals: 'www.deezer.com', pathPrefix: '/track' },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostSuffix: 'deezer.com', pathPrefix: "/album" },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostSuffix: 'deezer.com', pathPrefix: "/track" },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostEquals: 'play.google.com', pathPrefix: "/music" },
pageUrl: { hostEquals: 'play.google.com', pathPrefix: '/music' },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostEquals: 'itunes.apple.com', pathPrefix: "/music" },
pageUrl: { hostEquals: 'itunes.apple.com', pathPrefix: '/music' },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostSuffix: 'rdio.com' },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostSuffix: 'rd.io' },
pageUrl: { hostSuffix: 'spotify.com', pathPrefix: '/album' },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostSuffix: 'spotify.com', pathPrefix: "/album" },
pageUrl: { hostSuffix: 'spotify.com', pathPrefix: '/track' },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostSuffix: 'spotify.com', pathPrefix: "/track" },
pageUrl: { hostEquals: 'music.microsoft.com', pathPrefix: '/track' },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostSuffix: 'music.xbox.com', pathPrefix: "/track" },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostSuffix: 'music.xbox.com', pathPrefix: "/album" },
pageUrl: { hostEquals: 'music.microsoft.com', pathPrefix: '/album' },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostSuffix: 'youtube.com' },
@ -53,17 +44,18 @@ chrome.runtime.onInstalled.addListener(function() {
});
chrome.pageAction.onClicked.addListener(function(tab) {
var params = "url=" + encodeURI(tab.url);
var xhr = new XMLHttpRequest();
xhr.open("POST", "https://match.audio/search", true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
var match = JSON.parse(xhr.response);
if (match.id) {
chrome.tabs.create({ url: "https://match.audio/" + match.service + "/" + match.type + "/" + match.id});
}
}
}
xhr.send(params);
chrome.pageAction.setIcon({tabId: tab.id, path: 'icon-blue-128.png'}, () => {
const headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
fetch(apiUrl + '/search', {method: 'POST', mode: 'cors', headers, body: 'url=' + encodeURI(tab.url)}).then((response) => {
response.json().then((match) => {
chrome.pageAction.setIcon({tabId: tab.id, path: 'icon-128.png'});
if (match.id) {
chrome.tabs.create({ url: apiUrl + '/' + match.service + '/' + match.type + '/' + match.id});
}
});
}).catch(() => {
chrome.pageAction.setIcon({tabId: tab.id, path: 'icon-128.png'});
});
});
});

51
chrome/src/google.js Normal file
View file

@ -0,0 +1,51 @@
const apiUrl = 'https://match.audio';
var button = document.createElement('button');
button.className = 'share-button';
button.setAttribute('aria-label', 'Share to Match Audio');
var buttonContent = document.createElement('div');
buttonContent.className = 'button-content';
button.appendChild(buttonContent);
var paperRipple = document.createElement('paper-ripple');
paperRipple.class = 'circle';
buttonContent.appendChild(paperRipple);
var background = document.createElement('div');
background.id = 'background';
background.className = 'style-scope paper-ripple';
paperRipple.appendChild(background);
var waves = document.createElement('div');
waves.id = 'waves';
waves.className = 'style-scope paper-ripple';
paperRipple.appendChild(waves);
var img = document.createElement('img');
img.src = 'https://match.audio/images/logo-128.png';
img.height = 48;
buttonContent.appendChild(img)
var buttonLabel = document.createElement('div');
buttonLabel.className = 'button-label';
buttonLabel.setAttribute('aria-hidden', true);
buttonLabel.innerText = 'Match Audio';
buttonContent.appendChild(buttonLabel);
// select the target node
document.addSelectorListener('.share-buttons', (event) => {
var input = event.target.parentElement.querySelector('paper-input input');
// Check that it's an album or track, and not a playlist
const match = input.value.match(/https:\/\/play\.google\.com\/music\/m\/([a-zA-Z0-9]+)/);
if (match) {
event.target.insertBefore(button, event.target.firstChild);
button.addEventListener('click', (event) => {
if (match[1].startsWith('T')) { // Track
window.open(apiUrl + '/google/track/' + match[1], '_blank');
} else if (match[1].startsWith('B')) { // Album
window.open(apiUrl + '/google/album/' + match[1], '_blank');
}
});
}
});

View file

@ -0,0 +1,77 @@
var events = {},
selectors = {},
styles = document.createElement('style'),
keyframes = document.createElement('style'),
head = document.getElementsByTagName('head')[0],
startNames = ['animationstart', 'oAnimationStart', 'MSAnimationStart', 'webkitAnimationStart'],
startEvent = function(event){
event.selector = (events[event.animationName] || {}).selector;
((this.selectorListeners || {})[event.animationName] || []).forEach(function(fn){
fn.call(this, event);
}, this);
},
prefix = (function() {
var duration = 'animation-duration: 0.001s;',
name = 'animation-name: SelectorListener !important;',
computed = window.getComputedStyle(document.documentElement, ''),
pre = (Array.prototype.slice.call(computed).join('').match(/moz|webkit|ms/)||(computed.OLink===''&&['o']))[0];
return {
css: '-' + pre + '-',
properties: '{' + duration + name + '-' + pre + '-' + duration + '-' + pre + '-' + name + '}',
keyframes: !!(window.CSSKeyframesRule || window[('WebKit|Moz|MS|O').match(new RegExp('(' + pre + ')', 'i'))[1] + 'CSSKeyframesRule'])
};
})();
styles.type = keyframes.type = 'text/css';
head.appendChild(styles);
head.appendChild(keyframes);
HTMLDocument.prototype.addSelectorListener = HTMLElement.prototype.addSelectorListener = function(selector, fn){
var key = selectors[selector],
listeners = this.selectorListeners = this.selectorListeners || {};
if (key) events[key].count++;
else {
key = selectors[selector] = 'SelectorListener-' + new Date().getTime();
var node = document.createTextNode('@' + (prefix.keyframes ? prefix.css : '') + 'keyframes ' + key + ' {'
+'from { outline-color: #fff; } to { outline-color: #000; }'
+ '}');
keyframes.appendChild(node);
styles.sheet.insertRule(selector + prefix.properties.replace(/SelectorListener/g, key), 0);
events[key] = { count: 1, selector: selector, keyframe: node, rule: styles.sheet.cssRules[0] };
}
if (listeners.count) listeners.count++;
else {
listeners.count = 1;
startNames.forEach(function(name){
this.addEventListener(name, startEvent, false);
}, this);
}
(listeners[key] = listeners[key] || []).push(fn);
};
HTMLDocument.prototype.removeSelectorListener = HTMLElement.prototype.removeSelectorListener = function(selector, fn){
var listeners = this.selectorListeners || {},
key = selectors[selector],
listener = listeners[key] || [],
index = listener.indexOf(fn);
if (index > -1){
var event = events[selectors[selector]];
event.count--;
if (!event.count){
styles.sheet.deleteRule(styles.sheet.cssRules.item(event.rule));
keyframes.removeChild(event.keyframe);
delete events[key];
delete selectors[selector];
}
listeners.count--;
listener.splice(index, 1);
if (!listeners.count) startNames.forEach(function(name){
this.removeEventListener(name, startEvent, false);
}, this);
}
};