A Chrome extension that scrobbles NTS Radio tracks to teal.fm
1// Popup script
2
3document.addEventListener('DOMContentLoaded', async () => {
4 const loginForm = document.getElementById('login-form');
5 const loggedInSection = document.getElementById('logged-in');
6 const loginBtn = document.getElementById('login-btn');
7 const logoutBtn = document.getElementById('logout-btn');
8 const pdsUrlInput = document.getElementById('pds-url');
9 const identifierInput = document.getElementById('identifier');
10 const passwordInput = document.getElementById('password');
11 const errorMsg = document.getElementById('error-msg');
12 const autoScrobbleCheckbox = document.getElementById('auto-scrobble');
13
14 // Check auth status
15 const response = await chrome.runtime.sendMessage({ type: 'GET_AUTH_STATUS' });
16
17 if (response.authenticated) {
18 showLoggedIn();
19 } else {
20 showLogin();
21 }
22
23 // Load settings
24 const settings = await chrome.storage.local.get(['autoScrobble', 'pdsUrl']);
25 if (settings.autoScrobble !== undefined) {
26 autoScrobbleCheckbox.checked = settings.autoScrobble;
27 }
28 if (settings.pdsUrl) {
29 pdsUrlInput.value = settings.pdsUrl;
30 }
31
32 // Get current track and recent scrobbles
33 updateCurrentTrack();
34 updateRecentScrobbles();
35
36 // Listen for track updates
37 chrome.runtime.onMessage.addListener((message) => {
38 if (message.type === 'TRACK_UPDATE') {
39 updateCurrentTrack();
40 } else if (message.type === 'SCROBBLE_SUCCESS') {
41 updateRecentScrobbles();
42 }
43 });
44
45 // Login handler
46 loginBtn.addEventListener('click', async () => {
47 const identifier = identifierInput.value.trim();
48 const password = passwordInput.value;
49 let pdsUrl = pdsUrlInput.value.trim();
50
51 if (!pdsUrl) {
52 pdsUrl = 'https://bsky.social';
53 }
54
55 if (!identifier || !password) {
56 errorMsg.textContent = 'Please enter both handle/email and password';
57 return;
58 }
59
60 loginBtn.disabled = true;
61 loginBtn.textContent = 'Logging in...';
62 errorMsg.textContent = '';
63
64 // Save PDS URL
65 await chrome.storage.local.set({ pdsUrl });
66
67 const result = await chrome.runtime.sendMessage({
68 type: 'LOGIN',
69 data: { identifier, password, pdsUrl }
70 });
71
72 if (result.success) {
73 showLoggedIn();
74 passwordInput.value = '';
75 } else {
76 errorMsg.textContent = result.error || 'Login failed';
77 }
78
79 loginBtn.disabled = false;
80 loginBtn.textContent = 'Login';
81 });
82
83 // Logout handler
84 logoutBtn.addEventListener('click', async () => {
85 await chrome.runtime.sendMessage({ type: 'LOGOUT' });
86 showLogin();
87 });
88
89 // Auto-scrobble setting
90 autoScrobbleCheckbox.addEventListener('change', async (e) => {
91 await chrome.storage.local.set({ autoScrobble: e.target.checked });
92 });
93
94 function showLogin() {
95 loginForm.style.display = 'block';
96 loggedInSection.style.display = 'none';
97 }
98
99 function showLoggedIn() {
100 loginForm.style.display = 'none';
101 loggedInSection.style.display = 'block';
102 }
103
104 async function updateCurrentTrack() {
105 const response = await chrome.runtime.sendMessage({ type: 'GET_CURRENT_TRACK' });
106 const currentTrackDiv = document.getElementById('current-track');
107
108 if (response && response.track) {
109 const track = response.track;
110 currentTrackDiv.innerHTML = `
111 <div class="track-info">
112 <div class="track-title">${track.track}</div>
113 <div class="track-artist">${track.artist}</div>
114 </div>
115 `;
116 } else {
117 currentTrackDiv.innerHTML = '<p class="info">No track detected</p>';
118 }
119 }
120
121 async function updateRecentScrobbles() {
122 const response = await chrome.runtime.sendMessage({ type: 'GET_RECENT_TRACKS' });
123 const recentTracksList = document.getElementById('recent-tracks-list');
124
125 if (response && response.tracks && response.tracks.length > 0) {
126 // Show only the latest 3 scrobbles
127 const latestTracks = response.tracks.slice(0, 3);
128 recentTracksList.innerHTML = latestTracks.map(track => `
129 <div class="recent-track">
130 <span class="recent-track-title">${track.track}</span>
131 <span class="recent-track-artist">${track.artist}</span>
132 </div>
133 `).join('');
134 } else {
135 recentTracksList.innerHTML = '<p class="info small">No recent scrobbles</p>';
136 }
137 }
138});