-
Notifications
You must be signed in to change notification settings - Fork 2
/
background.js
175 lines (141 loc) · 4.14 KB
/
background.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
// All of the tracked tabs.
let tabs = new Map();
// The last name that was downloaded.
let lastName;
// The default name format.
let nameFormat = '{title}';
// Try to get the stored format, if one exists.
chrome.storage.local.get('format', (result) => {
if (result.format) {
nameFormat = result.format;
}
});
// And also listen to further changes.
chrome.storage.onChanged.addListener((changes) => {
if (changes.format) {
nameFormat = changes.format.newValue;
// Default the format if it's empty.
if (nameFormat === '') {
nameFormat = '{title}';
}
// If there is a song currently playing, redownload it.
if (lastName) {
download(lastName, true);
}
}
});
// Gets the url to a blob containing the given string.
function stringToUrl(s) {
return URL.createObjectURL(new Blob([s], {type: "text/plain"}));
}
// Given a name, if it's different than the last name, download it.
function download(name, redownload) {
if (name !== lastName || redownload) {
lastName = name;
// Interpolate the format.
let formatted = nameFormat.replace(/\{title\}/g, name);
// Download the name.
chrome.downloads.download({url: stringToUrl(formatted), filename: 'currentsong.txt', conflictAction: 'overwrite'}, (downloadId) => {
// Erase the download record, to not spam the user's downloads list.
chrome.downloads.erase({id: downloadId});
});
}
}
// Get the first tracked tab that has a known name and is audible, and download its name.
function update() {
for (let tab of tabs.values()) {
if (tab.name !== '' && tab.audible) {
download(tab.name);
return;
}
}
download('');
}
// Map from urls to the file that handles them.
function getScript(url) {
if (url.startsWith('https://www.youtube.com/watch')) {
return 'youtube.js';
} else if (url.startsWith('https://nightbot.tv/song_requests')) {
return 'nightbot.js';
} else if (url.startsWith('https://music.youtube.com/')) {
return 'youtubemusic.js';
}
}
// Convenience wrapper.
function isUrlSupported(url) {
return !!getScript(url);
}
// Add a tracked tab.
function add(id, url, audible) {
let script = getScript(url);
if (script && !tabs.has(id)) {
tabs.set(id, {name: '', audible: audible || false});
chrome.tabs.executeScript(id, {file: script, runAt: "document_end"});
update();
}
}
// Remove a tracked tab.
function remove(id) {
let tab = tabs.get(id);
if (tab) {
tabs.delete(id);
update();
}
}
// Set the name of a tracked tab.
function setName(id, name) {
let tab = tabs.get(id);
if (tab) {
tab.name = name;
update();
}
}
// Set the audible state of a tracked tab.
function setAudible(id, audible) {
let tab = tabs.get(id);
if (tab) {
tab.audible = audible;
update();
}
}
// Listen to name changes from the scripts running on the sites.
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
setName(sender.tab.id, request.name);
});
// Listen to tab updates.
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.url !== undefined) {
// If a tracked tab changed the url to something that is not tracked, stop tracking it.
if (tabs.has(tabId)) {
if (!isUrlSupported(changeInfo.url)) {
remove(tabId);
}
} else {
add(tabId, tab.url);
}
} else if (changeInfo.audible !== undefined) {
setAudible(tabId, changeInfo.audible);
} else if (changeInfo.status === 'loading') {
remove(tabId);
}
});
// Listen to tabs being removed.
chrome.tabs.onRemoved.addListener((tabId, removeInfo) => {
remove(tabId);
});
// Listen to history state changes.
// This is needed for sites that don't reload the tab when the user clicks on a video.
// Content scripts defined in the manifest are only loaded on tab loads, and thus do not work in this case.
chrome.webNavigation.onHistoryStateUpdated.addListener((details) => {
add(details.tabId, details.url);
});
// Go over all of the open tabs and add whatever's relevant.
function initialize() {
chrome.tabs.query({}, (tabs) => {
for (let tab of tabs) {
add(tab.id, tab.url, tab.audible);
}
});
}
// Start.
initialize();