Update server.js

pull/1/head
accius 6 days ago
parent 34b6e459ab
commit b4e0277fde

@ -119,7 +119,7 @@ app.get('/api/hamqsl/conditions', async (req, res) => {
app.get('/api/dxcluster/spots', async (req, res) => { app.get('/api/dxcluster/spots', async (req, res) => {
console.log('[DX Cluster] ========== Fetching spots =========='); console.log('[DX Cluster] ========== Fetching spots ==========');
// Source 1: HamQTH CSV (most reliable based on logs) // Source 1: HamQTH (uses ^ delimiter!)
try { try {
console.log('[DX Cluster] Trying HamQTH...'); console.log('[DX Cluster] Trying HamQTH...');
const controller = new AbortController(); const controller = new AbortController();
@ -135,55 +135,46 @@ app.get('/api/dxcluster/spots', async (req, res) => {
if (response.ok) { if (response.ok) {
const text = await response.text(); const text = await response.text();
console.log('[DX Cluster] HamQTH response length:', text.length); console.log('[DX Cluster] HamQTH response length:', text.length);
console.log('[DX Cluster] HamQTH first 300 chars:', text.substring(0, 300));
const lines = text.trim().split('\n').filter(line => line.trim() && !line.startsWith('#')); const lines = text.trim().split('\n').filter(line => line.trim() && !line.startsWith('#'));
console.log('[DX Cluster] HamQTH lines count:', lines.length); console.log('[DX Cluster] HamQTH lines count:', lines.length);
if (lines.length > 0) { if (lines.length > 0) {
// Log first line to understand format
console.log('[DX Cluster] HamQTH first line:', lines[0]);
const spots = []; const spots = [];
for (const line of lines.slice(0, 20)) { for (const line of lines.slice(0, 25)) {
const parts = line.split(','); // HamQTH format uses ^ delimiter:
// HamQTH format appears to be: spotter,freq,dx_call,comment,time,date // spotter^freq^dx_call^comment^time date^^^continent^band^country^id
// or: timestamp,freq,dx_call,spotter,time,comment // Example: F5PAC^7022.0^KP5/NP3VI^Up 2^0610 2026-01-30^^^NA^40M^Desecheo Island^43
// Let's handle both possibilities const parts = line.split('^');
if (parts.length >= 3) { if (parts.length >= 5) {
let spot; const spotter = parts[0] || '';
const freqKhz = parts[1] || '';
// Check if first field looks like a callsign (has letters) const dxCall = parts[2] || '';
if (/[A-Z]/.test(parts[0])) { const comment = parts[3] || '';
// Format: spotter,freq,dx_call,... const timeDate = parts[4] || ''; // "0610 2026-01-30"
spot = { const band = parts[9] || '';
freq: parts[1] ? String(parseFloat(parts[1]) / 1000) : '0',
call: parts[2] || 'UNKNOWN',
comment: parts[3] || '',
time: parts[4] ? parts[4].substring(0, 5) + 'z' : '',
spotter: parts[0] || ''
};
} else {
// Format: timestamp,freq,dx_call,spotter,time,comment
spot = {
freq: parts[1] ? String(parseFloat(parts[1]) / 1000) : '0',
call: parts[2] || 'UNKNOWN',
comment: parts[5] || '',
time: parts[4] ? parts[4].substring(0, 5) + 'z' : '',
spotter: parts[3] || ''
};
}
// Clean up frequency - ensure 3 decimal places // Parse frequency (already in kHz, convert to MHz)
if (spot.freq && spot.freq !== '0') { const freqNum = parseFloat(freqKhz);
const freqNum = parseFloat(spot.freq); if (!isNaN(freqNum) && freqNum > 0 && dxCall) {
if (!isNaN(freqNum) && freqNum > 0) { // Convert kHz to MHz for display (7022.0 -> 7.022)
spot.freq = freqNum.toFixed(3); const freqMhz = (freqNum / 1000).toFixed(3);
if (spot.call && spot.call !== 'UNKNOWN') {
spots.push(spot); // Extract time from "0610 2026-01-30" -> "06:10z"
} let time = '';
if (timeDate && timeDate.length >= 4) {
const timeStr = timeDate.substring(0, 4);
time = timeStr.substring(0, 2) + ':' + timeStr.substring(2, 4) + 'z';
} }
spots.push({
freq: freqMhz,
call: dxCall,
comment: comment + (band ? ' ' + band : ''),
time: time,
spotter: spotter
});
} }
} }
} }
@ -200,140 +191,91 @@ app.get('/api/dxcluster/spots', async (req, res) => {
console.error('[DX Cluster] HamQTH error:', error.name, error.message); console.error('[DX Cluster] HamQTH error:', error.name, error.message);
} }
// Source 2: DXHeat // Source 2: DX Summit
try { try {
console.log('[DX Cluster] Trying DXHeat...'); console.log('[DX Cluster] Trying DX Summit...');
const controller = new AbortController(); const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 8000); const timeout = setTimeout(() => controller.abort(), 8000);
const response = await fetch('https://dxheat.com/dxc/data.php', { const response = await fetch('https://www.dxsummit.fi/api/v1/spots?limit=25', {
headers: { headers: {
'User-Agent': 'OpenHamClock/3.3', 'User-Agent': 'OpenHamClock/3.3 (Amateur Radio Dashboard)',
'Accept': 'application/json' 'Accept': 'application/json'
}, },
signal: controller.signal signal: controller.signal
}); });
clearTimeout(timeout); clearTimeout(timeout);
console.log('[DX Cluster] DXHeat status:', response.status); console.log('[DX Cluster] DX Summit status:', response.status);
if (response.ok) { if (response.ok) {
const text = await response.text(); const text = await response.text();
console.log('[DX Cluster] DXHeat response length:', text.length); console.log('[DX Cluster] DX Summit response preview:', text.substring(0, 300));
console.log('[DX Cluster] DXHeat first 300 chars:', text.substring(0, 300));
try { try {
const data = JSON.parse(text); const data = JSON.parse(text);
const spots = data.spots || data; console.log('[DX Cluster] DX Summit data type:', typeof data, Array.isArray(data) ? 'array len=' + data.length : 'object');
if (Array.isArray(spots) && spots.length > 0) { if (Array.isArray(data) && data.length > 0) {
const mapped = spots.slice(0, 20).map(spot => ({ console.log('[DX Cluster] DX Summit first item:', JSON.stringify(data[0]));
freq: spot.f || spot.frequency || '0.000', const spots = data.slice(0, 20).map(spot => ({
call: spot.c || spot.dx || spot.callsign || 'UNKNOWN', freq: spot.frequency ? String(spot.frequency) : '0.000',
comment: spot.i || spot.info || '', call: spot.dx_call || spot.dxcall || spot.callsign || 'UNKNOWN',
time: spot.t ? String(spot.t).substring(11, 16) + 'z' : '', comment: spot.info || spot.comment || '',
spotter: spot.s || spot.spotter || '' time: spot.time ? String(spot.time).substring(0, 5) + 'z' : '',
spotter: spot.spotter || spot.de || ''
})); }));
console.log('[DX Cluster] DXHeat SUCCESS:', mapped.length, 'spots'); console.log('[DX Cluster] DX Summit SUCCESS:', spots.length, 'spots');
return res.json(mapped); return res.json(spots);
} }
} catch (parseErr) { } catch (parseErr) {
console.log('[DX Cluster] DXHeat parse error:', parseErr.message); console.log('[DX Cluster] DX Summit parse error:', parseErr.message);
} }
} }
} catch (error) { } catch (error) {
console.error('[DX Cluster] DXHeat error:', error.name, error.message); console.error('[DX Cluster] DX Summit error:', error.name, error.message);
} }
// Source 3: DXWatch (need to understand the actual format) // Source 3: DXHeat (backup)
try { try {
console.log('[DX Cluster] Trying DXWatch...'); console.log('[DX Cluster] Trying DXHeat...');
const controller = new AbortController(); const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 8000); const timeout = setTimeout(() => controller.abort(), 8000);
const response = await fetch('https://dxwatch.com/dxsd1/s.php?s=0&r=25', { const response = await fetch('https://dxheat.com/dxc/data.php', {
headers: { headers: {
'User-Agent': 'OpenHamClock/3.3', 'User-Agent': 'OpenHamClock/3.3',
'Accept': '*/*' 'Accept': 'application/json'
}, },
signal: controller.signal signal: controller.signal
}); });
clearTimeout(timeout); clearTimeout(timeout);
console.log('[DX Cluster] DXWatch status:', response.status); console.log('[DX Cluster] DXHeat status:', response.status);
if (response.ok) { if (response.ok) {
const text = await response.text(); const text = await response.text();
console.log('[DX Cluster] DXWatch response length:', text.length); console.log('[DX Cluster] DXHeat response preview:', text.substring(0, 300));
console.log('[DX Cluster] DXWatch full response:', text.substring(0, 500));
// DXWatch might return JSONP or different format
// Try to extract JSON from potential JSONP wrapper
let jsonText = text;
const jsonpMatch = text.match(/\((\[.*\])\)/s);
if (jsonpMatch) {
jsonText = jsonpMatch[1];
console.log('[DX Cluster] DXWatch extracted JSONP');
}
try { try {
const data = JSON.parse(jsonText); const data = JSON.parse(text);
console.log('[DX Cluster] DXWatch parsed, is array:', Array.isArray(data), 'length:', data?.length); const spots = data.spots || data;
if (Array.isArray(data) && data.length > 0) { if (Array.isArray(spots) && spots.length > 0) {
console.log('[DX Cluster] DXWatch first item:', JSON.stringify(data[0])); const mapped = spots.slice(0, 20).map(spot => ({
const spots = data.slice(0, 20).map(spot => ({ freq: spot.f || spot.frequency || '0.000',
freq: spot.fr ? (parseFloat(spot.fr) / 1000).toFixed(3) : (spot.f || '0.000'), call: spot.c || spot.dx || spot.callsign || 'UNKNOWN',
call: spot.dx || spot.c || 'UNKNOWN', comment: spot.i || spot.info || '',
comment: spot.cm || spot.i || '', time: spot.t ? String(spot.t).substring(11, 16) + 'z' : '',
time: spot.t || '', spotter: spot.s || spot.spotter || ''
spotter: spot.sp || spot.s || ''
})); }));
console.log('[DX Cluster] DXWatch SUCCESS:', spots.length, 'spots'); console.log('[DX Cluster] DXHeat SUCCESS:', mapped.length, 'spots');
return res.json(spots); return res.json(mapped);
} }
} catch (parseErr) { } catch (parseErr) {
console.log('[DX Cluster] DXWatch parse error:', parseErr.message); console.log('[DX Cluster] DXHeat parse error:', parseErr.message);
console.log('[DX Cluster] DXWatch raw text type:', typeof text, 'starts with:', text.substring(0, 50));
}
}
} catch (error) {
console.error('[DX Cluster] DXWatch error:', error.name, error.message);
}
// Source 4: DX Summit
try {
console.log('[DX Cluster] Trying DX Summit...');
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 8000);
const response = await fetch('https://www.dxsummit.fi/api/v1/spots?limit=25', {
headers: {
'User-Agent': 'OpenHamClock/3.3 (Amateur Radio Dashboard)',
'Accept': 'application/json'
},
signal: controller.signal
});
clearTimeout(timeout);
console.log('[DX Cluster] DX Summit status:', response.status);
if (response.ok) {
const data = await response.json();
console.log('[DX Cluster] DX Summit data type:', typeof data, Array.isArray(data) ? 'array len=' + data.length : 'object');
if (Array.isArray(data) && data.length > 0) {
console.log('[DX Cluster] DX Summit first item:', JSON.stringify(data[0]));
const spots = data.slice(0, 20).map(spot => ({
freq: spot.frequency ? String(spot.frequency) : '0.000',
call: spot.dx_call || spot.dxcall || spot.callsign || 'UNKNOWN',
comment: spot.info || spot.comment || '',
time: spot.time ? String(spot.time).substring(0, 5) + 'z' : '',
spotter: spot.spotter || spot.de || ''
}));
console.log('[DX Cluster] DX Summit SUCCESS:', spots.length, 'spots');
return res.json(spots);
} }
} }
} catch (error) { } catch (error) {
console.error('[DX Cluster] DX Summit error:', error.name, error.message); console.error('[DX Cluster] DXHeat error:', error.name, error.message);
} }
console.log('[DX Cluster] ========== ALL SOURCES FAILED =========='); console.log('[DX Cluster] ========== ALL SOURCES FAILED ==========');

Loading…
Cancel
Save

Powered by TurnKey Linux.