160 lines
7.2 KiB
JavaScript
160 lines
7.2 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.recalculateTopPlayed = exports.updateTopPlayedOnHistoryChange = void 0;
|
|
const functions = require("firebase-functions");
|
|
const admin = require("firebase-admin");
|
|
// Initialize Firebase Admin
|
|
admin.initializeApp();
|
|
// Database reference
|
|
const db = admin.database();
|
|
/**
|
|
* Cloud Function that triggers when a song is added to history
|
|
* This function aggregates all history items to create/update the topPlayed collection
|
|
* based on unique combinations of artist and title (not path)
|
|
*/
|
|
exports.updateTopPlayedOnHistoryChange = functions.database
|
|
.ref('/controllers/{controllerName}/history/{historyId}')
|
|
.onCreate(async (snapshot, context) => {
|
|
const { controllerName } = context.params;
|
|
console.log(`TopPlayed update triggered for controller: ${controllerName}`);
|
|
try {
|
|
// Get the controller reference
|
|
const controllerRef = db.ref(`/controllers/${controllerName}`);
|
|
// Get all history items for this controller
|
|
const historySnapshot = await controllerRef.child('history').once('value');
|
|
const historyData = historySnapshot.val();
|
|
if (!historyData) {
|
|
console.log('No history data found, skipping TopPlayed update');
|
|
return;
|
|
}
|
|
// Aggregate history items by artist + title combination
|
|
const aggregation = {};
|
|
Object.values(historyData).forEach((song) => {
|
|
const historySong = song;
|
|
if (historySong && historySong.artist && historySong.title) {
|
|
// Create a unique key based on artist and title (case-insensitive)
|
|
// Replace invalid Firebase key characters with underscores
|
|
const sanitizedArtist = String(historySong.artist || '').toLowerCase().trim().replace(/[.#$/[\]]/g, '_');
|
|
const sanitizedTitle = String(historySong.title || '').toLowerCase().trim().replace(/[.#$/[\]]/g, '_');
|
|
const key = `${sanitizedArtist}_${sanitizedTitle}`;
|
|
if (aggregation[key]) {
|
|
// Increment count for existing song
|
|
aggregation[key].count += historySong.count || 1;
|
|
}
|
|
else {
|
|
// Create new entry
|
|
aggregation[key] = {
|
|
artist: historySong.artist,
|
|
title: historySong.title,
|
|
count: historySong.count || 1
|
|
};
|
|
}
|
|
}
|
|
});
|
|
// Convert aggregation to array, sort by count (descending), and take top 100
|
|
const sortedSongs = Object.entries(aggregation)
|
|
.map(([key, songData]) => ({
|
|
key,
|
|
artist: songData.artist,
|
|
title: songData.title,
|
|
count: songData.count
|
|
}))
|
|
.sort((a, b) => b.count - a.count) // Sort by count descending
|
|
.slice(0, 100); // Take only top 100
|
|
// Convert back to object format for Firebase
|
|
const topPlayedData = {};
|
|
sortedSongs.forEach((song) => {
|
|
topPlayedData[song.key] = {
|
|
artist: song.artist,
|
|
title: song.title,
|
|
count: song.count
|
|
};
|
|
});
|
|
// Update the topPlayed collection
|
|
await controllerRef.child('topPlayed').set(topPlayedData);
|
|
console.log(`Successfully updated TopPlayed for controller ${controllerName} with ${Object.keys(topPlayedData).length} unique songs`);
|
|
}
|
|
catch (error) {
|
|
console.error('Error updating TopPlayed:', error);
|
|
throw error;
|
|
}
|
|
});
|
|
/**
|
|
* Alternative function that can be called manually to recalculate TopPlayed
|
|
* This is useful for initial setup or data migration
|
|
*/
|
|
exports.recalculateTopPlayed = functions.https.onCall(async (data, context) => {
|
|
const { controllerName } = data;
|
|
if (!controllerName) {
|
|
throw new functions.https.HttpsError('invalid-argument', 'controllerName is required');
|
|
}
|
|
console.log(`Manual TopPlayed recalculation requested for controller: ${controllerName}`);
|
|
try {
|
|
// Get the controller reference
|
|
const controllerRef = db.ref(`/controllers/${controllerName}`);
|
|
// Get all history items for this controller
|
|
const historySnapshot = await controllerRef.child('history').once('value');
|
|
const historyData = historySnapshot.val();
|
|
if (!historyData) {
|
|
console.log('No history data found, returning empty TopPlayed');
|
|
await controllerRef.child('topPlayed').set({});
|
|
return { success: true, message: 'No history data found, TopPlayed cleared' };
|
|
}
|
|
// Aggregate history items by artist + title combination
|
|
const aggregation = {};
|
|
Object.values(historyData).forEach((song) => {
|
|
const historySong = song;
|
|
if (historySong && historySong.artist && historySong.title) {
|
|
// Create a unique key based on artist and title (case-insensitive)
|
|
// Replace invalid Firebase key characters with underscores
|
|
const sanitizedArtist = String(historySong.artist || '').toLowerCase().trim().replace(/[.#$/[\]]/g, '_');
|
|
const sanitizedTitle = String(historySong.title || '').toLowerCase().trim().replace(/[.#$/[\]]/g, '_');
|
|
const key = `${sanitizedArtist}_${sanitizedTitle}`;
|
|
if (aggregation[key]) {
|
|
// Increment count for existing song
|
|
aggregation[key].count += historySong.count || 1;
|
|
}
|
|
else {
|
|
// Create new entry
|
|
aggregation[key] = {
|
|
artist: historySong.artist,
|
|
title: historySong.title,
|
|
count: historySong.count || 1
|
|
};
|
|
}
|
|
}
|
|
});
|
|
// Convert aggregation to array, sort by count (descending), and take top 100
|
|
const sortedSongs = Object.entries(aggregation)
|
|
.map(([key, songData]) => ({
|
|
key,
|
|
artist: songData.artist,
|
|
title: songData.title,
|
|
count: songData.count
|
|
}))
|
|
.sort((a, b) => b.count - a.count) // Sort by count descending
|
|
.slice(0, 100); // Take only top 100
|
|
// Convert back to object format for Firebase
|
|
const topPlayedData = {};
|
|
sortedSongs.forEach((song) => {
|
|
topPlayedData[song.key] = {
|
|
artist: song.artist,
|
|
title: song.title,
|
|
count: song.count
|
|
};
|
|
});
|
|
// Update the topPlayed collection
|
|
await controllerRef.child('topPlayed').set(topPlayedData);
|
|
console.log(`Successfully recalculated TopPlayed for controller ${controllerName} with ${Object.keys(topPlayedData).length} unique songs`);
|
|
return {
|
|
success: true,
|
|
message: `TopPlayed recalculated successfully`,
|
|
songCount: Object.keys(topPlayedData).length
|
|
};
|
|
}
|
|
catch (error) {
|
|
console.error('Error recalculating TopPlayed:', error);
|
|
throw new functions.https.HttpsError('internal', 'Failed to recalculate TopPlayed');
|
|
}
|
|
});
|
|
//# sourceMappingURL=index.js.map
|