mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
Fix search to use SquidWTF HiFi API with round-robin base URLs, capitalize provider names in UI, and widen tracks modal to 90%
This commit is contained in:
@@ -40,6 +40,9 @@ public class AdminController : ControllerBase
|
|||||||
private readonly IWebHostEnvironment _environment;
|
private readonly IWebHostEnvironment _environment;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly string _envFilePath;
|
private readonly string _envFilePath;
|
||||||
|
private readonly List<string> _squidWtfApiUrls;
|
||||||
|
private static int _urlIndex = 0;
|
||||||
|
private static readonly object _urlIndexLock = new();
|
||||||
private const string CacheDirectory = "/app/cache/spotify";
|
private const string CacheDirectory = "/app/cache/spotify";
|
||||||
|
|
||||||
public AdminController(
|
public AdminController(
|
||||||
@@ -77,6 +80,9 @@ public class AdminController : ControllerBase
|
|||||||
_jellyfinHttpClient = httpClientFactory.CreateClient();
|
_jellyfinHttpClient = httpClientFactory.CreateClient();
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
|
|
||||||
|
// Decode SquidWTF base URLs
|
||||||
|
_squidWtfApiUrls = DecodeSquidWtfUrls();
|
||||||
|
|
||||||
// .env file path is always /app/.env in Docker (mounted from host)
|
// .env file path is always /app/.env in Docker (mounted from host)
|
||||||
// In development, it's in the parent directory of ContentRootPath
|
// In development, it's in the parent directory of ContentRootPath
|
||||||
_envFilePath = _environment.IsDevelopment()
|
_envFilePath = _environment.IsDevelopment()
|
||||||
@@ -86,6 +92,27 @@ public class AdminController : ControllerBase
|
|||||||
_logger.LogInformation("Admin controller initialized. .env path: {EnvFilePath}", _envFilePath);
|
_logger.LogInformation("Admin controller initialized. .env path: {EnvFilePath}", _envFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<string> DecodeSquidWtfUrls()
|
||||||
|
{
|
||||||
|
var encodedUrls = new[]
|
||||||
|
{
|
||||||
|
"aHR0cHM6Ly90cml0b24uc3F1aWQud3Rm", // triton
|
||||||
|
"aHR0cHM6Ly90aWRhbC1hcGkuYmluaW11bS5vcmc=", // binimum
|
||||||
|
"aHR0cHM6Ly90aWRhbC5raW5vcGx1cy5vbmxpbmU=", // kinoplus
|
||||||
|
"aHR0cHM6Ly9oaWZpLXR3by5zcG90aXNhdmVyLm5ldA==", // spoti-2
|
||||||
|
"aHR0cHM6Ly9oaWZpLW9uZS5zcG90aXNhdmVyLm5ldA==", // spoti-1
|
||||||
|
"aHR0cHM6Ly93b2xmLnFxZGwuc2l0ZQ==", // wolf
|
||||||
|
"aHR0cDovL2h1bmQucXFkbC5zaXRl", // hund
|
||||||
|
"aHR0cHM6Ly9rYXR6ZS5xcWRsLnNpdGU=", // katze
|
||||||
|
"aHR0cHM6Ly92b2dlbC5xcWRsLnNpdGU=", // vogel
|
||||||
|
"aHR0cHM6Ly9tYXVzLnFxZGwuc2l0ZQ==" // maus
|
||||||
|
};
|
||||||
|
|
||||||
|
return encodedUrls
|
||||||
|
.Select(encoded => System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(encoded)))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper method to safely check if a dynamic cache result has a value
|
/// Helper method to safely check if a dynamic cache result has a value
|
||||||
/// Handles the case where JsonElement cannot be compared to null directly
|
/// Handles the case where JsonElement cannot be compared to null directly
|
||||||
@@ -159,6 +186,27 @@ public class AdminController : ControllerBase
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a random SquidWTF base URL for searching (round-robin)
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("squidwtf-base-url")]
|
||||||
|
public IActionResult GetSquidWtfBaseUrl()
|
||||||
|
{
|
||||||
|
if (_squidWtfApiUrls.Count == 0)
|
||||||
|
{
|
||||||
|
return NotFound(new { error = "No SquidWTF base URLs configured" });
|
||||||
|
}
|
||||||
|
|
||||||
|
string baseUrl;
|
||||||
|
lock (_urlIndexLock)
|
||||||
|
{
|
||||||
|
baseUrl = _squidWtfApiUrls[_urlIndex];
|
||||||
|
_urlIndex = (_urlIndex + 1) % _squidWtfApiUrls.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(new { baseUrl });
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get list of configured playlists with their current data
|
/// Get list of configured playlists with their current data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -879,7 +879,7 @@
|
|||||||
|
|
||||||
<!-- Track List Modal -->
|
<!-- Track List Modal -->
|
||||||
<div class="modal" id="tracks-modal">
|
<div class="modal" id="tracks-modal">
|
||||||
<div class="modal-content" style="max-width: 700px;">
|
<div class="modal-content" style="max-width: 90%; width: 90%;">
|
||||||
<h3 id="tracks-modal-title">Playlist Tracks</h3>
|
<h3 id="tracks-modal-title">Playlist Tracks</h3>
|
||||||
<div class="tracks-list" id="tracks-list">
|
<div class="tracks-list" id="tracks-list">
|
||||||
<div class="loading">
|
<div class="loading">
|
||||||
@@ -1586,17 +1586,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchProvider(query, provider) {
|
async function searchProvider(query, provider) {
|
||||||
// Provider-specific search URLs
|
// Use SquidWTF HiFi API with round-robin base URLs for all searches
|
||||||
const searchUrls = {
|
// Get a random base URL from the backend
|
||||||
'Deezer': `https://www.deezer.com/search/${encodeURIComponent(query)}`,
|
try {
|
||||||
'Qobuz': `https://www.qobuz.com/us-en/search?q=${encodeURIComponent(query)}`,
|
const response = await fetch('/api/admin/squidwtf-base-url');
|
||||||
'SquidWTF': `https://triton.squid.wtf/search/?s=${encodeURIComponent(query)}`,
|
const data = await response.json();
|
||||||
'default': `https://www.google.com/search?q=${encodeURIComponent(query + ' music')}`
|
|
||||||
|
if (data.baseUrl) {
|
||||||
|
// Use the HiFi API search endpoint: /search/?s=query
|
||||||
|
const searchUrl = `${data.baseUrl}/search/?s=${encodeURIComponent(query)}`;
|
||||||
|
window.open(searchUrl, '_blank');
|
||||||
|
} else {
|
||||||
|
showToast('Failed to get search URL', 'error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showToast('Failed to get search URL', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function capitalizeProvider(provider) {
|
||||||
|
// Capitalize provider names for display
|
||||||
|
const providerMap = {
|
||||||
|
'squidwtf': 'SquidWTF',
|
||||||
|
'deezer': 'Deezer',
|
||||||
|
'qobuz': 'Qobuz'
|
||||||
};
|
};
|
||||||
|
return providerMap[provider?.toLowerCase()] || provider;
|
||||||
const url = searchUrls[provider] || searchUrls['default'];
|
|
||||||
window.open(url, '_blank');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clearCache() {
|
async function clearCache() {
|
||||||
@@ -1811,7 +1827,7 @@
|
|||||||
statusBadge += '<span class="status-badge" style="font-size:0.75rem;padding:2px 8px;margin-left:4px;background:var(--info);color:white;"><span class="status-dot" style="background:white;"></span>Manual</span>';
|
statusBadge += '<span class="status-badge" style="font-size:0.75rem;padding:2px 8px;margin-left:4px;background:var(--info);color:white;"><span class="status-dot" style="background:white;"></span>Manual</span>';
|
||||||
}
|
}
|
||||||
} else if (t.isLocal === false) {
|
} else if (t.isLocal === false) {
|
||||||
const provider = t.externalProvider || 'External';
|
const provider = capitalizeProvider(t.externalProvider) || 'External';
|
||||||
statusBadge = `<span class="status-badge warning" style="font-size:0.75rem;padding:2px 8px;margin-left:8px;"><span class="status-dot"></span>${escapeHtml(provider)}</span>`;
|
statusBadge = `<span class="status-badge warning" style="font-size:0.75rem;padding:2px 8px;margin-left:8px;"><span class="status-dot"></span>${escapeHtml(provider)}</span>`;
|
||||||
// Add manual mapping indicator for external tracks
|
// Add manual mapping indicator for external tracks
|
||||||
if (t.isManualMapping && t.manualMappingType === 'external') {
|
if (t.isManualMapping && t.manualMappingType === 'external') {
|
||||||
@@ -1864,8 +1880,8 @@
|
|||||||
<div class="track-meta">
|
<div class="track-meta">
|
||||||
${t.album ? escapeHtml(t.album) : ''}
|
${t.album ? escapeHtml(t.album) : ''}
|
||||||
${t.isrc ? '<br><small>ISRC: ' + t.isrc + '</small>' : ''}
|
${t.isrc ? '<br><small>ISRC: ' + t.isrc + '</small>' : ''}
|
||||||
${t.isLocal === false && t.searchQuery && t.externalProvider ? '<br><small style="color:var(--accent)"><a href="#" onclick="searchProvider(\'' + escapeJs(t.searchQuery) + '\', \'' + escapeJs(t.externalProvider) + '\'); return false;" style="color:var(--accent);text-decoration:underline;">🔍 Search: ' + escapeHtml(t.searchQuery) + '</a></small>' : ''}
|
${t.isLocal === false && t.searchQuery && t.externalProvider ? '<br><small style="color:var(--accent)"><a href="#" onclick="searchProvider(\'' + escapeJs(t.searchQuery) + '\', \'' + escapeJs(t.externalProvider) + '\'); return false;" style="color:var(--accent);text-decoration:underline;">🔍 Search</a></small>' : ''}
|
||||||
${t.isLocal === null && t.searchQuery ? '<br><small style="color:var(--text-secondary)"><a href="#" onclick="searchProvider(\'' + escapeJs(t.title + ' ' + (t.artists && t.artists.length > 0 ? t.artists[0] : '')) + '\', \'SquidWTF\'); return false;" style="color:var(--text-secondary);text-decoration:underline;">🔍 Search: ' + escapeHtml(t.title + ' ' + (t.artists && t.artists.length > 0 ? t.artists[0] : '')) + '</a></small>' : ''}
|
${t.isLocal === null && t.searchQuery ? '<br><small style="color:var(--text-secondary)"><a href="#" onclick="searchProvider(\'' + escapeJs(t.searchQuery) + '\', \'squidwtf\'); return false;" style="color:var(--text-secondary);text-decoration:underline;">🔍 Search</a></small>' : ''}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -2277,7 +2293,8 @@
|
|||||||
} else if (titleEl && mappingType === 'external') {
|
} else if (titleEl && mappingType === 'external') {
|
||||||
// For external mappings, update status badge to show provider
|
// For external mappings, update status badge to show provider
|
||||||
const currentTitle = titleEl.textContent.split(' - ')[0]; // Remove old status
|
const currentTitle = titleEl.textContent.split(' - ')[0]; // Remove old status
|
||||||
const newStatusBadge = `<span class="status-badge warning" style="font-size:0.75rem;padding:2px 8px;margin-left:8px;"><span class="status-dot"></span>${escapeHtml(requestBody.externalProvider)}</span>`;
|
const capitalizedProvider = capitalizeProvider(requestBody.externalProvider);
|
||||||
|
const newStatusBadge = `<span class="status-badge warning" style="font-size:0.75rem;padding:2px 8px;margin-left:8px;"><span class="status-dot"></span>${escapeHtml(capitalizedProvider)}</span>`;
|
||||||
titleEl.innerHTML = escapeHtml(currentTitle) + newStatusBadge;
|
titleEl.innerHTML = escapeHtml(currentTitle) + newStatusBadge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user