forked from web-scrobbler/web-scrobbler
-
Notifications
You must be signed in to change notification settings - Fork 0
/
amazon.js
237 lines (207 loc) · 6.13 KB
/
amazon.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/*
* Chrome-Last.fm-Scrobbler amazon.com "new interface" Connector
*
* Jacob Tolar --- http://sheckel.net --- jacob[at]sheckel[dot]net
*
* Derived from Pandora module by Jordan Perr
*/
/********* Configuration: ***********/
// changes to the DOM in this container will trigger an update.
watchedContainer = "div.nowPlayingDetail";
// changes to the DOM in this container are due to play/pause/forward/back
playerMasterControl = "div.mp3Player-MasterControl";
/**
* Function that returns title and artist of current song.
* Make best effort to get the full, untruncated values from the
* main cloud player pane. But, the user may have navigated away
* from that - if so, fall back to the (possibly truncated) values
* in the bottom now playing pane.
*/
function titleAndArtist() {
var mainContentTableRow = $("tr.selectable").has("a.nowPlaying");
if (mainContentTableRow.size () > 0) {
return {
title: mainContentTableRow.children("td.titleCell").attr("title"),
artist: mainContentTableRow.children("td.artistCell").attr("title")
}
} else {
var currentSongDetails = $(".currentSongDetails .title");
return {
title: currentSongDetails.text(),
// substring(3) because format is: 'by Artist'
artist: currentSongDetails.next().text().substring(3)
}
}
}
/**
* Takes raw string, checks that it is in the form "xx:xx", and
* calculates and returns the number of seconds, otherwise returns
* -1 indicating failure to parse.
*/
function parseTime (maybeTime) {
if (typeof(maybeTime) == "string" && maybeTime.indexOf (":") > 0) {
var timeArr = maybeTime.split(":");
return parseInt(timeArr[0])*60 + parseInt(timeArr[1]);
} else {
return -1;
}
}
function currentTimeAndTrackDuration () {
var songStatusTimer = $(".currentSongStatus .timer");
return {
currentTime: parseTime(songStatusTimer.find("#currentTime").html()),
trackDuration: parseTime(songStatusTimer.children().filter(":last").html())
}
}
function isPaused() {
return $("div.mp3MasterPlayGroup").hasClass("paused");
}
function isPlaying() {
return $("div.mp3MasterPlayGroup").hasClass("playing");
}
/********* Connector: ***********/
var track = function(title, artist) {
return title + " " + artist;
}
var songTrack = function (song) {
return track (song.title, song.artist);
}
/**
* Encapsulate some "private" state and functions,
* present a "public" API.
*/
var module = function() {
var resetState = function (track) {
return {
lastTrack : track,
lock : false,
scrobbled : false
}
}
var initState = function() {
return resetState ("");
}
var state = initState();
var parseNewState = function() {
var tAndA = titleAndArtist ();
var timeAndDuration = currentTimeAndTrackDuration ();
return {
title : tAndA.title,
artist : tAndA.artist,
currentTime : timeAndDuration.currentTime,
duration : timeAndDuration.trackDuration,
track : track (tAndA.title, tAndA.artist)
}
}
var maybeScrobbled = function (scrobbledSong) {
if (state.lastTrack == songTrack (scrobbledSong)) {
state.scrobbled = true;
}
}
var update = function(newState) {
console.log("submitting a now playing request. artist: "+newState.artist+", title: "+newState.title+", current time: "+newState.currentTime+", duration: "+newState.duration);
chrome.extension.sendRequest({type: 'validate', artist: newState.artist, track: newState.title}, function(response) {
if (response != false) {
chrome.extension.sendRequest({type: 'nowPlaying', artist: newState.artist, track: newState.title, currentTime:newState.currentTime, duration: newState.duration});
} else { // on failure send nowPlaying 'unknown song'
chrome.extension.sendRequest({type: 'nowPlaying', duration: newState.duration});
}
});
state = resetState (newState.track);
}
var isReadyToUpdate = function(newState) {
return (isPlaying() &&
newState.currentTime >= 0 &&
newState.duration > 0 &&
newState.track != "" &&
newState.track != state.lastTrack);
}
var maybeUpdate = function() {
var newState = parseNewState();
if (isReadyToUpdate(newState)) {
update(newState);
} else {
setTimeout(maybeUpdate, 1000);
}
}
/**
* Pause requests are ignored for a track that has already been
* scrobbled. But a "reset" request forces a reset.
*/
var cancelAndReset = function(force) {
if (force || !(state.scrobbled)) {
clearTimeout();
state = initState();
chrome.extension.sendRequest({type: "reset"});
}
}
var updateIfNotLocked = function() {
if (!state.lock) {
state.lock = true;
setTimeout(maybeUpdate, 2000);
}
}
/**
* Here is the "public" API.
*/
return {
updateNowPlaying : function() {
updateIfNotLocked();
},
pause : function() {
cancelAndReset(false);
},
resume : function() {
this.updateNowPlaying();
},
reset : function() {
cancelAndReset(true);
},
/**
* Handle confirmation of scrobble from main scrobbler.js
*/
scrobbled : function (song) {
maybeScrobbled (song);
}
}
}();
/**
* Listen for requests from scrobbler.js
*/
chrome.extension.onRequest.addListener(
function(request, sender, sendResponse) {
switch(request.type) {
case 'submitOK':
// translate song from scrobbler.js to amazon.js - TODO Clean
// this up re: "title" versus "track" confusion
var amazonSong = {artist: request.song.artist, title: request.song.track};
module.scrobbled (amazonSong);
break;
}
}
);
/**
* Run at startup
*/
$(function(){
console.log("Amazon module starting up");
$(watchedContainer).live('DOMSubtreeModified', function(e) {
//console.log("Live watcher called");
if ($(watchedContainer).length > 0) {
module.updateNowPlaying();
return;
}
});
$(playerMasterControl).click( function(e) {
if (isPaused()) {
module.pause();
} else if (isPlaying()) {
module.resume();
}
return;
});
$(window).unload(function() {
module.reset();
return true;
});
});