Fix Spotify playlist track filling
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -39,3 +39,6 @@ yarn-error.*
|
|||||||
# generated native folders
|
# generated native folders
|
||||||
/ios
|
/ios
|
||||||
/android
|
/android
|
||||||
|
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ export async function refreshSpotifyToken() {
|
|||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (data.access_token) {
|
if (data.access_token) {
|
||||||
console.log('[SpotifyToken] Spotify access token refreshed successfully!');
|
console.log('[SpotifyToken] Spotify access token refreshed successfully!');
|
||||||
|
console.log('[SpotifyToken] REFRESH_GRANTED_SCOPE:', data.scope ?? '(no scope field returned)');
|
||||||
await setSpotifyToken(data.access_token);
|
await setSpotifyToken(data.access_token);
|
||||||
if (data.refresh_token) {
|
if (data.refresh_token) {
|
||||||
await setSpotifyRefreshToken(data.refresh_token);
|
await setSpotifyRefreshToken(data.refresh_token);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { SafeAreaView } from 'react-native-safe-area-context';
|
|||||||
import { X, MapPin, ArrowRight, Navigation } from 'lucide-react-native';
|
import { X, MapPin, ArrowRight, Navigation } from 'lucide-react-native';
|
||||||
import { colors } from '../../utils/colors';
|
import { colors } from '../../utils/colors';
|
||||||
import { supabase } from '../../services/supabase';
|
import { supabase } from '../../services/supabase';
|
||||||
import { getSpotifyAccessToken, refreshSpotifyToken } from '../../auth/spotifyToken';
|
import { getSpotifyAccessToken, refreshSpotifyToken, clearSpotifyTokens } from '../../auth/spotifyToken';
|
||||||
import { OLLAMA_API_URL } from '../../services/ollama';
|
import { OLLAMA_API_URL } from '../../services/ollama';
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -45,7 +45,9 @@ export default function NewTripScreen({ navigation }) {
|
|||||||
setDuration(finalDuration);
|
setDuration(finalDuration);
|
||||||
|
|
||||||
let generatedPlaylistUrl = null;
|
let generatedPlaylistUrl = null;
|
||||||
let spotifyPremiumRequired = false;
|
let playlistCreationFailed = false;
|
||||||
|
let playlistFailureReason = '';
|
||||||
|
let playlistSuccessMessage = 'Viagem e playlist criadas com sucesso.';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log("GENERATING_PLAYLIST_FOR_TRIP:", tripName);
|
console.log("GENERATING_PLAYLIST_FOR_TRIP:", tripName);
|
||||||
@@ -79,13 +81,12 @@ export default function NewTripScreen({ navigation }) {
|
|||||||
console.log("SPOTIFY_ACCESS_TOKEN_EXISTS:", !!providerToken);
|
console.log("SPOTIFY_ACCESS_TOKEN_EXISTS:", !!providerToken);
|
||||||
|
|
||||||
if (providerToken) {
|
if (providerToken) {
|
||||||
// Proactively check if token is valid, or refresh it
|
// Validate token via GET /v1/me (free endpoint, no Premium required)
|
||||||
console.log("Validating Spotify token via GET /v1/me...");
|
let meRes = await fetch('https://api.spotify.com/v1/me', {
|
||||||
let testRes = await fetch('https://api.spotify.com/v1/me', {
|
|
||||||
headers: { Authorization: `Bearer ${providerToken}` }
|
headers: { Authorization: `Bearer ${providerToken}` }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (testRes.status === 401) {
|
if (meRes.status === 401) {
|
||||||
console.log("Spotify token is invalid/expired (401), attempting to refresh...");
|
console.log("Spotify token is invalid/expired (401), attempting to refresh...");
|
||||||
const newToken = await refreshSpotifyToken();
|
const newToken = await refreshSpotifyToken();
|
||||||
if (newToken) {
|
if (newToken) {
|
||||||
@@ -95,22 +96,22 @@ export default function NewTripScreen({ navigation }) {
|
|||||||
console.log("Failed to refresh Spotify token.");
|
console.log("Failed to refresh Spotify token.");
|
||||||
providerToken = null;
|
providerToken = null;
|
||||||
}
|
}
|
||||||
} else if (!testRes.ok) {
|
} else if (!meRes.ok) {
|
||||||
const testErr = await testRes.text();
|
const meErr = await meRes.text();
|
||||||
console.warn("Spotify validation request failed on GET /v1/me:", testRes.status, testErr);
|
console.warn("Spotify GET /v1/me failed:", meRes.status, meErr);
|
||||||
providerToken = null;
|
providerToken = null;
|
||||||
} else {
|
} else {
|
||||||
console.log("Spotify token is valid (GET /v1/me returned 200 OK).");
|
console.log("Spotify token valid (GET /v1/me returned 200 OK).");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!providerToken) {
|
if (!providerToken) {
|
||||||
console.log("Spotify token missing or expired, skipping playlist generation.");
|
console.log("Spotify token missing or expired, skipping playlist generation.");
|
||||||
if (!spotifyPremiumRequired) {
|
playlistCreationFailed = true;
|
||||||
Alert.alert('Spotify Desligado', 'O token do Spotify expirou ou está em falta. Por favor reconecte o Spotify no Perfil.');
|
playlistFailureReason = 'token';
|
||||||
}
|
Alert.alert('Spotify Desligado', 'O token do Spotify expirou ou está em falta. Por favor reconecte o Spotify no Perfil.');
|
||||||
} else {
|
} else {
|
||||||
// B. Fetch Spotify User ID
|
// B. Fetch Spotify User ID (reuse /v1/me — already validated above)
|
||||||
const spotifyUserRes = await fetch('https://api.spotify.com/v1/me', {
|
const spotifyUserRes = await fetch('https://api.spotify.com/v1/me', {
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${providerToken}`,
|
'Authorization': `Bearer ${providerToken}`,
|
||||||
@@ -118,21 +119,24 @@ export default function NewTripScreen({ navigation }) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
const spotifyUserData = await safeParseJson(spotifyUserRes, 'SpotifyUser');
|
const spotifyUserData = await safeParseJson(spotifyUserRes, 'SpotifyUser');
|
||||||
if (!spotifyUserData.id) throw new Error('Could not fetch Spotify User ID');
|
const spotifyUserId = spotifyUserData?.id ?? null;
|
||||||
const spotifyUserId = spotifyUserData.id;
|
const spotifyUserCountry = spotifyUserData?.country || 'PT';
|
||||||
|
console.log("SPOTIFY_USER_ID_EXISTS:", !!spotifyUserId);
|
||||||
|
if (!spotifyUserId) throw new Error('Could not fetch Spotify User ID from /v1/me');
|
||||||
|
|
||||||
// C. Call Ollama server
|
// C. Call Ollama server
|
||||||
|
const ollamaPrompt = `I am taking a roadtrip from ${origin} to ${destination}. The trip is called "${tripName}" and takes about ${duration}. Reply ONLY with a JSON array of up to 10 Spotify search queries (e.g. genres, moods, or themes) that fit this journey. Example: ["portuguese pop", "italian road trip", "european indie", "summer travel songs"]. No other text.`;
|
||||||
const ollamaRes = await fetch(`${OLLAMA_API_URL}/api/chat`, {
|
const ollamaRes = await fetch(`${OLLAMA_API_URL}/api/chat`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
model: "qwen3-coder:30b",
|
model: "qwen3-coder:30b",
|
||||||
messages: [{ "role": "user", "content": `I am taking a roadtrip from ${origin} to ${destination}. Reply ONLY with 3 Spotify genre seeds separated by commas (e.g., pop,rock,indie) that fit this journey. No other text.` }],
|
messages: [{ "role": "user", "content": ollamaPrompt }],
|
||||||
stream: false
|
stream: false
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let seed_genres = "pop,road-trip,happy"; // Fallback genres
|
let searchQueries: string[] = ["pop hits", "road trip songs", "top hits Portugal", "summer hits", "travel songs"]; // Fallback
|
||||||
try {
|
try {
|
||||||
const ollamaData = await safeParseJson(ollamaRes, 'Ollama');
|
const ollamaData = await safeParseJson(ollamaRes, 'Ollama');
|
||||||
let rawAiText = ollamaData?.message?.content || "";
|
let rawAiText = ollamaData?.message?.content || "";
|
||||||
@@ -140,39 +144,57 @@ export default function NewTripScreen({ navigation }) {
|
|||||||
// Clean AI text
|
// Clean AI text
|
||||||
rawAiText = rawAiText.replace(/```json/g, '').replace(/```/g, '').trim();
|
rawAiText = rawAiText.replace(/```json/g, '').replace(/```/g, '').trim();
|
||||||
|
|
||||||
if (rawAiText.length > 0 && !rawAiText.toLowerCase().startsWith("a ")) {
|
if (rawAiText.length > 0 && rawAiText.startsWith("[")) {
|
||||||
seed_genres = rawAiText.replace(/\s+/g, '').toLowerCase();
|
const parsed = JSON.parse(rawAiText);
|
||||||
// Spotify limits to 5 seed genres, let's keep it clean
|
if (Array.isArray(parsed) && parsed.length > 0) {
|
||||||
seed_genres = seed_genres.split(',').slice(0, 5).join(',');
|
const aiQueries = parsed.map(String).slice(0, 10);
|
||||||
|
searchQueries = [...aiQueries, ...searchQueries];
|
||||||
|
} else {
|
||||||
|
console.log("Ollama returned empty array, using fallbacks");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("AI returned plain text/error, using fallback genres:", rawAiText);
|
console.log("AI returned plain text/error, using fallback queries:", rawAiText);
|
||||||
}
|
}
|
||||||
} catch (aiError) {
|
} catch (aiError) {
|
||||||
console.log("AI parsing failed, using fallback genres.", aiError);
|
console.log("AI parsing failed, using fallback queries.", aiError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// D. Create empty playlist
|
// D. Create empty playlist
|
||||||
console.log("[SpotifyPlaylistDebug] playlist create request started");
|
const createPlaylistUrl = 'https://api.spotify.com/v1/me/playlists';
|
||||||
console.log("[SpotifyPlaylistDebug] userId exists:", Boolean(spotifyUserId));
|
const createPlaylistBody = JSON.stringify({
|
||||||
console.log("[SpotifyPlaylistDebug] access token exists:", Boolean(providerToken));
|
name: tripName,
|
||||||
|
description: `Roadtrip from ${origin} to ${destination}. Themes: ${searchQueries.join(', ')}`,
|
||||||
|
public: false
|
||||||
|
});
|
||||||
|
console.log("CREATE_PLAYLIST_URL:", createPlaylistUrl);
|
||||||
|
console.log("CREATE_PLAYLIST_BODY:", createPlaylistBody);
|
||||||
|
|
||||||
const createPlaylistRes = await fetch(`https://api.spotify.com/v1/users/${spotifyUserId}/playlists`, {
|
const createPlaylistRes = await fetch(createPlaylistUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${providerToken}`,
|
'Authorization': `Bearer ${providerToken}`,
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: createPlaylistBody
|
||||||
name: tripName,
|
|
||||||
description: `Roadtrip from ${origin} to ${destination}. Genres: ${seed_genres}`,
|
|
||||||
public: false
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("[SpotifyPlaylistDebug] create playlist HTTP status:", createPlaylistRes.status);
|
console.log("CREATE_PLAYLIST_HTTP_STATUS:", createPlaylistRes.status);
|
||||||
const createPlaylistResText = await createPlaylistRes.text();
|
const createPlaylistResText = await createPlaylistRes.text();
|
||||||
if (!createPlaylistRes.ok) {
|
if (!createPlaylistRes.ok) {
|
||||||
console.log("[SpotifyPlaylistDebug] create playlist response body if failed:", createPlaylistResText);
|
console.log("CREATE_PLAYLIST_RESPONSE_BODY_IF_FAILED:", createPlaylistResText.substring(0, 300));
|
||||||
|
if (createPlaylistRes.status === 403) {
|
||||||
|
// Stored refresh token predates playlist scopes — clear tokens so next login forces full re-auth
|
||||||
|
await clearSpotifyTokens();
|
||||||
|
console.warn("CREATE_PLAYLIST_403: Cleared stale Spotify tokens. User must reconnect.");
|
||||||
|
Alert.alert(
|
||||||
|
'Permissão Spotify Necessária',
|
||||||
|
'Reconnect Spotify to grant playlist permissions. Go to Profile and log in with Spotify again.'
|
||||||
|
);
|
||||||
|
playlistCreationFailed = true;
|
||||||
|
playlistFailureReason = 'scope';
|
||||||
|
// Skip further playlist work — trip will still be saved
|
||||||
|
throw new Error(`CREATE_PLAYLIST_SCOPE_ERROR: Tokens cleared, re-auth required.`);
|
||||||
|
}
|
||||||
throw new Error(`Spotify API returned status ${createPlaylistRes.status} for CreatePlaylist: ${createPlaylistResText.substring(0, 150)}`);
|
throw new Error(`Spotify API returned status ${createPlaylistRes.status} for CreatePlaylist: ${createPlaylistResText.substring(0, 150)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,63 +209,102 @@ export default function NewTripScreen({ navigation }) {
|
|||||||
const playlistId = playlistData.id;
|
const playlistId = playlistData.id;
|
||||||
generatedPlaylistUrl = playlistData.external_urls.spotify;
|
generatedPlaylistUrl = playlistData.external_urls.spotify;
|
||||||
|
|
||||||
// E. Fetch Spotify track recommendations via Search (does not require Premium)
|
// E. Fill playlist with tracks based on duration
|
||||||
|
console.log("TARGET_PLAYLIST_DURATION_MS:", tripDurationMs);
|
||||||
let accumulatedDurationMs = 0;
|
let accumulatedDurationMs = 0;
|
||||||
let trackUris: string[] = [];
|
let selectedTracks: { id: string; uri: string; duration_ms: number }[] = [];
|
||||||
let attempts = 0;
|
let searchRequestsCount = 0;
|
||||||
const genresList = seed_genres.split(',');
|
const MAX_SEARCH_REQUESTS = 40;
|
||||||
let genreIndex = 0;
|
const MAX_TRACKS = 400;
|
||||||
|
let queryIndex = 0;
|
||||||
|
let offset = 0;
|
||||||
|
let noMoreTracks = false;
|
||||||
|
|
||||||
while (accumulatedDurationMs < tripDurationMs && attempts < 10) {
|
while (
|
||||||
const currentGenre = genresList[genreIndex % genresList.length] || 'pop';
|
accumulatedDurationMs < tripDurationMs &&
|
||||||
const query = encodeURIComponent(`genre:${currentGenre}`);
|
searchRequestsCount < MAX_SEARCH_REQUESTS &&
|
||||||
const searchRes = await fetch(`https://api.spotify.com/v1/search?type=track&q=${query}&limit=50&offset=${attempts * 50}`, {
|
selectedTracks.length < MAX_TRACKS &&
|
||||||
|
!noMoreTracks
|
||||||
|
) {
|
||||||
|
const currentQuery = searchQueries[queryIndex % searchQueries.length];
|
||||||
|
const queryEncoded = encodeURIComponent(currentQuery);
|
||||||
|
const searchUrl = `https://api.spotify.com/v1/search?type=track&q=${queryEncoded}&limit=10&market=${spotifyUserCountry}&offset=${offset}`;
|
||||||
|
|
||||||
|
console.log("TRACK_SEARCH_QUERY:", currentQuery, "offset:", offset);
|
||||||
|
console.log("TRACK_SEARCH_URL:", searchUrl);
|
||||||
|
|
||||||
|
const searchRes = await fetch(searchUrl, {
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${providerToken}`,
|
'Authorization': `Bearer ${providerToken}`,
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log("TRACK_SEARCH_STATUS:", searchRes.status);
|
||||||
|
|
||||||
if (!searchRes.ok) {
|
if (!searchRes.ok) {
|
||||||
const errText = await searchRes.text();
|
const errText = await searchRes.text();
|
||||||
if (searchRes.status === 403 || errText.toLowerCase().includes('active premium subscription required')) {
|
console.warn("Spotify search failed:", searchRes.status, errText.substring(0, 150));
|
||||||
spotifyPremiumRequired = true;
|
console.log("TRACK_SEARCH_RESPONSE_BODY_IF_FAILED:", errText.substring(0, 300));
|
||||||
console.warn("Spotify search skipped due to expected Premium limitation:", searchRes.status, errText);
|
queryIndex++;
|
||||||
} else {
|
offset = 0;
|
||||||
console.warn("Search failed:", errText);
|
searchRequestsCount++;
|
||||||
Alert.alert('Erro Spotify', `Aviso ao adicionar músicas: ${errText.substring(0, 100)}`);
|
continue;
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchData = await safeParseJson(searchRes, 'SearchTracks');
|
const searchData = await safeParseJson(searchRes, 'SearchTracks');
|
||||||
const tracks = searchData?.tracks?.items;
|
const tracks = searchData?.tracks?.items || [];
|
||||||
|
console.log("TRACKS_RAW_FOUND_COUNT:", tracks.length);
|
||||||
|
|
||||||
if (!tracks || tracks.length === 0) {
|
if (tracks.length === 0) {
|
||||||
genreIndex++;
|
queryIndex++;
|
||||||
attempts++;
|
offset = 0;
|
||||||
|
if (queryIndex >= searchQueries.length * 3) {
|
||||||
|
noMoreTracks = true;
|
||||||
|
}
|
||||||
|
searchRequestsCount++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tracksAfterFilter = 0;
|
||||||
|
|
||||||
for (const track of tracks) {
|
for (const track of tracks) {
|
||||||
if (!trackUris.includes(track.uri)) {
|
if (selectedTracks.length >= MAX_TRACKS) break;
|
||||||
trackUris.push(track.uri);
|
if (accumulatedDurationMs >= tripDurationMs) break;
|
||||||
accumulatedDurationMs += track.duration_ms;
|
|
||||||
if (accumulatedDurationMs >= tripDurationMs) break;
|
if (track.id && track.uri && track.duration_ms && track.is_local !== true) {
|
||||||
|
if (track.is_playable === undefined || track.is_playable !== false) {
|
||||||
|
if (!selectedTracks.some(t => t.id === track.id)) {
|
||||||
|
selectedTracks.push({ id: track.id, uri: track.uri, duration_ms: track.duration_ms });
|
||||||
|
accumulatedDurationMs += track.duration_ms;
|
||||||
|
tracksAfterFilter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
attempts++;
|
|
||||||
genreIndex++;
|
console.log("TRACKS_AFTER_FILTER_COUNT:", tracksAfterFilter);
|
||||||
|
|
||||||
|
offset += 10;
|
||||||
|
if (offset >= 1000) {
|
||||||
|
queryIndex++;
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
searchRequestsCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackUris.length > 0) {
|
console.log("TRACKS_SELECTED_COUNT:", selectedTracks.length);
|
||||||
// F. Add tracks to playlist
|
console.log("SELECTED_TRACKS_TOTAL_DURATION_MS:", accumulatedDurationMs);
|
||||||
|
|
||||||
|
if (selectedTracks.length > 0) {
|
||||||
|
// F. Add tracks to playlist in chunks
|
||||||
|
const trackUris = selectedTracks.map(t => t.uri);
|
||||||
const chunkSize = 100;
|
const chunkSize = 100;
|
||||||
|
let tracksAddedSuccessfully = true;
|
||||||
for (let i = 0; i < trackUris.length; i += chunkSize) {
|
for (let i = 0; i < trackUris.length; i += chunkSize) {
|
||||||
const chunk = trackUris.slice(i, i + chunkSize);
|
const chunk = trackUris.slice(i, i + chunkSize);
|
||||||
|
|
||||||
console.log("[SpotifyPlaylistDebug] Add tracks request started for chunk:", i / chunkSize + 1);
|
const addTracksRes = await fetch(`https://api.spotify.com/v1/playlists/${playlistId}/items`, {
|
||||||
const addTracksRes = await fetch(`https://api.spotify.com/v1/playlists/${playlistId}/tracks`, {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${providerToken}`,
|
'Authorization': `Bearer ${providerToken}`,
|
||||||
@@ -252,32 +313,41 @@ export default function NewTripScreen({ navigation }) {
|
|||||||
body: JSON.stringify({ uris: chunk })
|
body: JSON.stringify({ uris: chunk })
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("[SpotifyPlaylistDebug] add tracks HTTP status:", addTracksRes.status);
|
console.log("ADD_TRACKS_HTTP_STATUS:", addTracksRes.status);
|
||||||
if (!addTracksRes.ok) {
|
if (!addTracksRes.ok) {
|
||||||
const addTracksErr = await addTracksRes.text();
|
const addTracksErr = await addTracksRes.text();
|
||||||
console.log("[SpotifyPlaylistDebug] add tracks response body if failed:", addTracksErr);
|
console.log("ADD_TRACKS_RESPONSE_BODY_IF_FAILED:", addTracksErr.substring(0, 300));
|
||||||
if (addTracksRes.status === 403 || addTracksErr.toLowerCase().includes('active premium subscription required')) {
|
tracksAddedSuccessfully = false;
|
||||||
spotifyPremiumRequired = true;
|
|
||||||
console.warn("Spotify add tracks skipped due to expected Premium limitation:", addTracksRes.status, addTracksErr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
throw new Error(`Spotify API returned status ${addTracksRes.status} while adding tracks: ${addTracksErr.substring(0, 150)}`);
|
throw new Error(`Spotify API returned status ${addTracksRes.status} while adding tracks: ${addTracksErr.substring(0, 150)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log("PLAYLIST_CREATE_SUCCESS:", generatedPlaylistUrl);
|
|
||||||
|
if (tracksAddedSuccessfully) {
|
||||||
|
console.log("PLAYLIST_CREATE_SUCCESS:", generatedPlaylistUrl);
|
||||||
|
playlistCreationFailed = false;
|
||||||
|
|
||||||
|
if (accumulatedDurationMs < tripDurationMs - 60000 && (selectedTracks.length >= MAX_TRACKS || searchRequestsCount >= MAX_SEARCH_REQUESTS || noMoreTracks)) {
|
||||||
|
const hours = Math.round((accumulatedDurationMs / 3600000) * 10) / 10;
|
||||||
|
if (hours >= 1) {
|
||||||
|
playlistSuccessMessage = `Viagem guardada! Playlist criada com ${hours} horas de música.`;
|
||||||
|
} else {
|
||||||
|
const minutes = Math.round(accumulatedDurationMs / 60000);
|
||||||
|
playlistSuccessMessage = `Viagem guardada! Playlist criada com ${minutes} minutos de música.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn("No tracks found for genres:", seed_genres);
|
console.warn("No tracks found for queries:", searchQueries);
|
||||||
|
playlistCreationFailed = true;
|
||||||
|
playlistFailureReason = 'notracks';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (playlistError: any) {
|
} catch (playlistError: any) {
|
||||||
const playlistErrorMessage = String(playlistError?.message || playlistError || '');
|
const playlistErrorMessage = String(playlistError?.message || playlistError || '');
|
||||||
if (playlistErrorMessage.includes('403') || playlistErrorMessage.toLowerCase().includes('active premium subscription required')) {
|
playlistCreationFailed = true;
|
||||||
spotifyPremiumRequired = true;
|
playlistFailureReason = 'error';
|
||||||
console.warn("Spotify playlist skipped due to expected Premium limitation:", playlistErrorMessage);
|
console.warn("Playlist generation failed:", playlistErrorMessage);
|
||||||
} else {
|
Alert.alert('Erro Playlist', `A viagem foi calculada, mas a playlist falhou: ${playlistErrorMessage.substring(0, 80) || 'Erro Desconhecido'}`);
|
||||||
console.warn("Expected failure generating playlist:", playlistErrorMessage);
|
|
||||||
Alert.alert('Erro Playlist', `A viagem foi calculada, mas a playlist falhou: ${playlistErrorMessage.substring(0, 50) || 'Erro Desconhecido'}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// G. Save to Supabase unconditionally if route is valid
|
// G. Save to Supabase unconditionally if route is valid
|
||||||
@@ -299,12 +369,17 @@ export default function NewTripScreen({ navigation }) {
|
|||||||
console.error("DB Insert error:", dbError);
|
console.error("DB Insert error:", dbError);
|
||||||
Alert.alert('Erro ao Guardar', 'Não foi possível guardar a viagem na base de dados: ' + dbError.message);
|
Alert.alert('Erro ao Guardar', 'Não foi possível guardar a viagem na base de dados: ' + dbError.message);
|
||||||
} else {
|
} else {
|
||||||
Alert.alert(
|
if (playlistCreationFailed) {
|
||||||
'Sucesso!',
|
if (playlistFailureReason === 'notracks') {
|
||||||
spotifyPremiumRequired
|
Alert.alert('Sucesso!', 'Playlist criada, mas não foram encontradas músicas para adicionar.');
|
||||||
? 'Viagem criada, mas a playlist do Spotify não pôde ser criada porque a app Spotify precisa de Premium.'
|
} else {
|
||||||
: 'Viagem calculada e guardada!'
|
Alert.alert('Sucesso!', 'Viagem guardada! A playlist do Spotify não pôde ser criada, mas a viagem foi guardada.');
|
||||||
);
|
}
|
||||||
|
} else if (generatedPlaylistUrl) {
|
||||||
|
Alert.alert('Sucesso!', playlistSuccessMessage);
|
||||||
|
} else {
|
||||||
|
Alert.alert('Sucesso!', 'Viagem calculada e guardada!');
|
||||||
|
}
|
||||||
navigation.goBack();
|
navigation.goBack();
|
||||||
}
|
}
|
||||||
} catch (dbEx) {
|
} catch (dbEx) {
|
||||||
|
|||||||
Reference in New Issue
Block a user