FMCL
This commit is contained in:
parent
762488d823
commit
084bf957f5
4 changed files with 124 additions and 0 deletions
12
index.html
12
index.html
|
|
@ -3908,6 +3908,18 @@
|
||||||
<span class="slider"></span>
|
<span class="slider"></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Analytics</span>
|
||||||
|
<span class="description"
|
||||||
|
>Send anonymous usage data to help improve the app</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<label class="toggle-switch">
|
||||||
|
<input type="checkbox" id="analytics-toggle" checked />
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<span class="label">Reset Local Data</span>
|
<span class="label">Reset Local Data</span>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,22 @@
|
||||||
// js/analytics.js - Plausible Analytics custom event tracking
|
// js/analytics.js - Plausible Analytics custom event tracking
|
||||||
|
|
||||||
|
import { analyticsSettings } from './storage.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if analytics is enabled
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function isAnalyticsEnabled() {
|
||||||
|
return analyticsSettings.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Track a custom event with Plausible
|
* Track a custom event with Plausible
|
||||||
* @param {string} eventName - The name of the event
|
* @param {string} eventName - The name of the event
|
||||||
* @param {object} [props] - Optional event properties
|
* @param {object} [props] - Optional event properties
|
||||||
*/
|
*/
|
||||||
export function trackEvent(eventName, props = {}) {
|
export function trackEvent(eventName, props = {}) {
|
||||||
|
if (!isAnalyticsEnabled()) return;
|
||||||
if (window.plausible) {
|
if (window.plausible) {
|
||||||
try {
|
try {
|
||||||
window.plausible(eventName, { props });
|
window.plausible(eventName, { props });
|
||||||
|
|
@ -26,26 +37,40 @@ export function trackPageView(path) {
|
||||||
// Playback Events
|
// Playback Events
|
||||||
export function trackPlayTrack(track) {
|
export function trackPlayTrack(track) {
|
||||||
trackEvent('Play Track', {
|
trackEvent('Play Track', {
|
||||||
|
track_id: track?.id || 'unknown',
|
||||||
track_title: track?.title || 'Unknown',
|
track_title: track?.title || 'Unknown',
|
||||||
|
artist_id: track?.artist?.id || track?.artists?.[0]?.id || 'unknown',
|
||||||
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
||||||
|
album_id: track?.album?.id || 'unknown',
|
||||||
album: track?.album?.title || 'Unknown',
|
album: track?.album?.title || 'Unknown',
|
||||||
duration: track?.duration || 0,
|
duration: track?.duration || 0,
|
||||||
quality: track?.audioQuality || track?.quality || 'Unknown',
|
quality: track?.audioQuality || track?.quality || 'Unknown',
|
||||||
is_local: track?.isLocal || false,
|
is_local: track?.isLocal || false,
|
||||||
|
is_explicit: track?.explicit || false,
|
||||||
|
track_number: track?.trackNumber || 0,
|
||||||
|
year: track?.album?.releaseYear || track?.album?.releaseDate || 'unknown',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function trackPauseTrack(track) {
|
export function trackPauseTrack(track) {
|
||||||
trackEvent('Pause Track', {
|
trackEvent('Pause Track', {
|
||||||
|
track_id: track?.id || 'unknown',
|
||||||
track_title: track?.title || 'Unknown',
|
track_title: track?.title || 'Unknown',
|
||||||
|
artist_id: track?.artist?.id || track?.artists?.[0]?.id || 'unknown',
|
||||||
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
||||||
|
album_id: track?.album?.id || 'unknown',
|
||||||
|
album: track?.album?.title || 'Unknown',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function trackSkipTrack(track, direction) {
|
export function trackSkipTrack(track, direction) {
|
||||||
trackEvent('Skip Track', {
|
trackEvent('Skip Track', {
|
||||||
|
track_id: track?.id || 'unknown',
|
||||||
track_title: track?.title || 'Unknown',
|
track_title: track?.title || 'Unknown',
|
||||||
|
artist_id: track?.artist?.id || track?.artists?.[0]?.id || 'unknown',
|
||||||
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
||||||
|
album_id: track?.album?.id || 'unknown',
|
||||||
|
album: track?.album?.title || 'Unknown',
|
||||||
direction: direction,
|
direction: direction,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -58,6 +83,19 @@ export function trackToggleRepeat(mode) {
|
||||||
trackEvent('Toggle Repeat', { mode });
|
trackEvent('Toggle Repeat', { mode });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function trackTrackComplete(track, completionPercent) {
|
||||||
|
trackEvent('Track Complete', {
|
||||||
|
track_id: track?.id || 'unknown',
|
||||||
|
track_title: track?.title || 'Unknown',
|
||||||
|
artist_id: track?.artist?.id || track?.artists?.[0]?.id || 'unknown',
|
||||||
|
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
||||||
|
album_id: track?.album?.id || 'unknown',
|
||||||
|
album: track?.album?.title || 'Unknown',
|
||||||
|
duration: track?.duration || 0,
|
||||||
|
completion_percent: completionPercent || 100,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function trackSetVolume(level) {
|
export function trackSetVolume(level) {
|
||||||
// Only track volume changes at coarse intervals to avoid spam
|
// Only track volume changes at coarse intervals to avoid spam
|
||||||
const roundedLevel = Math.round(level * 10) / 10;
|
const roundedLevel = Math.round(level * 10) / 10;
|
||||||
|
|
@ -80,6 +118,16 @@ export function trackSeek(position, duration) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track listening progress milestones (10%, 25%, 50%, 75%, 90%, 100%)
|
||||||
|
export function trackListeningProgress(track, percent) {
|
||||||
|
trackEvent('Listening Progress', {
|
||||||
|
track_id: track?.id || 'unknown',
|
||||||
|
track_title: track?.title || 'Unknown',
|
||||||
|
artist_id: track?.artist?.id || track?.artists?.[0]?.id || 'unknown',
|
||||||
|
percent: percent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Search Events
|
// Search Events
|
||||||
export function trackSearch(query, resultsCount) {
|
export function trackSearch(query, resultsCount) {
|
||||||
trackEvent('Search', {
|
trackEvent('Search', {
|
||||||
|
|
@ -108,15 +156,22 @@ export function trackSidebarNavigation(item) {
|
||||||
// Library Events
|
// Library Events
|
||||||
export function trackLikeTrack(track) {
|
export function trackLikeTrack(track) {
|
||||||
trackEvent('Like Track', {
|
trackEvent('Like Track', {
|
||||||
|
track_id: track?.id || 'unknown',
|
||||||
track_title: track?.title || 'Unknown',
|
track_title: track?.title || 'Unknown',
|
||||||
|
artist_id: track?.artist?.id || track?.artists?.[0]?.id || 'unknown',
|
||||||
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
||||||
|
album_id: track?.album?.id || 'unknown',
|
||||||
|
album: track?.album?.title || 'Unknown',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function trackUnlikeTrack(track) {
|
export function trackUnlikeTrack(track) {
|
||||||
trackEvent('Unlike Track', {
|
trackEvent('Unlike Track', {
|
||||||
|
track_id: track?.id || 'unknown',
|
||||||
track_title: track?.title || 'Unknown',
|
track_title: track?.title || 'Unknown',
|
||||||
|
artist_id: track?.artist?.id || track?.artists?.[0]?.id || 'unknown',
|
||||||
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
||||||
|
album_id: track?.album?.id || 'unknown',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,23 +259,29 @@ export function trackDeleteFolder(folderName) {
|
||||||
// Playback Actions
|
// Playback Actions
|
||||||
export function trackPlayAlbum(album, shuffle) {
|
export function trackPlayAlbum(album, shuffle) {
|
||||||
trackEvent('Play Album', {
|
trackEvent('Play Album', {
|
||||||
|
album_id: album?.id || 'unknown',
|
||||||
album_title: album?.title || 'Unknown',
|
album_title: album?.title || 'Unknown',
|
||||||
|
artist_id: album?.artist?.id || 'unknown',
|
||||||
artist: album?.artist?.name || 'Unknown',
|
artist: album?.artist?.name || 'Unknown',
|
||||||
shuffle: shuffle || false,
|
shuffle: shuffle || false,
|
||||||
track_count: album?.numberOfTracks || album?.tracks?.length || 0,
|
track_count: album?.numberOfTracks || album?.tracks?.length || 0,
|
||||||
|
year: album?.releaseYear || album?.releaseDate || 'unknown',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function trackPlayPlaylist(playlist, shuffle) {
|
export function trackPlayPlaylist(playlist, shuffle) {
|
||||||
trackEvent('Play Playlist', {
|
trackEvent('Play Playlist', {
|
||||||
|
playlist_id: playlist?.id || 'unknown',
|
||||||
playlist_name: playlist?.title || playlist?.name || 'Unknown',
|
playlist_name: playlist?.title || playlist?.name || 'Unknown',
|
||||||
shuffle: shuffle || false,
|
shuffle: shuffle || false,
|
||||||
track_count: playlist?.tracks?.length || 0,
|
track_count: playlist?.tracks?.length || 0,
|
||||||
|
is_public: playlist?.isPublic || false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function trackPlayArtistRadio(artist) {
|
export function trackPlayArtistRadio(artist) {
|
||||||
trackEvent('Play Artist Radio', {
|
trackEvent('Play Artist Radio', {
|
||||||
|
artist_id: artist?.id || 'unknown',
|
||||||
artist_name: artist?.name || 'Unknown',
|
artist_name: artist?.name || 'Unknown',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -232,15 +293,20 @@ export function trackShuffleLikedTracks(count) {
|
||||||
// Download Events
|
// Download Events
|
||||||
export function trackDownloadTrack(track, quality) {
|
export function trackDownloadTrack(track, quality) {
|
||||||
trackEvent('Download Track', {
|
trackEvent('Download Track', {
|
||||||
|
track_id: track?.id || 'unknown',
|
||||||
track_title: track?.title || 'Unknown',
|
track_title: track?.title || 'Unknown',
|
||||||
|
artist_id: track?.artist?.id || track?.artists?.[0]?.id || 'unknown',
|
||||||
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
||||||
|
album_id: track?.album?.id || 'unknown',
|
||||||
quality: quality || 'Unknown',
|
quality: quality || 'Unknown',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function trackDownloadAlbum(album, quality) {
|
export function trackDownloadAlbum(album, quality) {
|
||||||
trackEvent('Download Album', {
|
trackEvent('Download Album', {
|
||||||
|
album_id: album?.id || 'unknown',
|
||||||
album_title: album?.title || 'Unknown',
|
album_title: album?.title || 'Unknown',
|
||||||
|
artist_id: album?.artist?.id || 'unknown',
|
||||||
artist: album?.artist?.name || 'Unknown',
|
artist: album?.artist?.name || 'Unknown',
|
||||||
track_count: album?.numberOfTracks || album?.tracks?.length || 0,
|
track_count: album?.numberOfTracks || album?.tracks?.length || 0,
|
||||||
quality: quality || 'Unknown',
|
quality: quality || 'Unknown',
|
||||||
|
|
@ -249,6 +315,7 @@ export function trackDownloadAlbum(album, quality) {
|
||||||
|
|
||||||
export function trackDownloadPlaylist(playlist, quality) {
|
export function trackDownloadPlaylist(playlist, quality) {
|
||||||
trackEvent('Download Playlist', {
|
trackEvent('Download Playlist', {
|
||||||
|
playlist_id: playlist?.id || 'unknown',
|
||||||
playlist_name: playlist?.title || playlist?.name || 'Unknown',
|
playlist_name: playlist?.title || playlist?.name || 'Unknown',
|
||||||
track_count: playlist?.tracks?.length || 0,
|
track_count: playlist?.tracks?.length || 0,
|
||||||
quality: quality || 'Unknown',
|
quality: quality || 'Unknown',
|
||||||
|
|
@ -264,6 +331,7 @@ export function trackDownloadLikedTracks(count, quality) {
|
||||||
|
|
||||||
export function trackDownloadDiscography(artist, selection) {
|
export function trackDownloadDiscography(artist, selection) {
|
||||||
trackEvent('Download Discography', {
|
trackEvent('Download Discography', {
|
||||||
|
artist_id: artist?.id || 'unknown',
|
||||||
artist_name: artist?.name || 'Unknown',
|
artist_name: artist?.name || 'Unknown',
|
||||||
include_albums: selection?.includeAlbums || false,
|
include_albums: selection?.includeAlbums || false,
|
||||||
include_eps: selection?.includeEPs || false,
|
include_eps: selection?.includeEPs || false,
|
||||||
|
|
@ -274,16 +342,22 @@ export function trackDownloadDiscography(artist, selection) {
|
||||||
// Queue Management
|
// Queue Management
|
||||||
export function trackAddToQueue(track, position) {
|
export function trackAddToQueue(track, position) {
|
||||||
trackEvent('Add to Queue', {
|
trackEvent('Add to Queue', {
|
||||||
|
track_id: track?.id || 'unknown',
|
||||||
track_title: track?.title || 'Unknown',
|
track_title: track?.title || 'Unknown',
|
||||||
|
artist_id: track?.artist?.id || track?.artists?.[0]?.id || 'unknown',
|
||||||
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
||||||
|
album_id: track?.album?.id || 'unknown',
|
||||||
position: position || 'end',
|
position: position || 'end',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function trackPlayNext(track) {
|
export function trackPlayNext(track) {
|
||||||
trackEvent('Play Next', {
|
trackEvent('Play Next', {
|
||||||
|
track_id: track?.id || 'unknown',
|
||||||
track_title: track?.title || 'Unknown',
|
track_title: track?.title || 'Unknown',
|
||||||
|
artist_id: track?.artist?.id || track?.artists?.[0]?.id || 'unknown',
|
||||||
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
||||||
|
album_id: track?.album?.id || 'unknown',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -306,37 +380,46 @@ export function trackContextMenuAction(action, itemType, item) {
|
||||||
|
|
||||||
export function trackBlockTrack(track) {
|
export function trackBlockTrack(track) {
|
||||||
trackEvent('Block Track', {
|
trackEvent('Block Track', {
|
||||||
|
track_id: track?.id || 'unknown',
|
||||||
track_title: track?.title || 'Unknown',
|
track_title: track?.title || 'Unknown',
|
||||||
|
artist_id: track?.artist?.id || track?.artists?.[0]?.id || 'unknown',
|
||||||
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
artist: track?.artist?.name || track?.artists?.[0]?.name || 'Unknown',
|
||||||
|
album_id: track?.album?.id || 'unknown',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function trackUnblockTrack(track) {
|
export function trackUnblockTrack(track) {
|
||||||
trackEvent('Unblock Track', {
|
trackEvent('Unblock Track', {
|
||||||
|
track_id: track?.id || 'unknown',
|
||||||
track_title: track?.title || 'Unknown',
|
track_title: track?.title || 'Unknown',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function trackBlockAlbum(album) {
|
export function trackBlockAlbum(album) {
|
||||||
trackEvent('Block Album', {
|
trackEvent('Block Album', {
|
||||||
|
album_id: album?.id || 'unknown',
|
||||||
album_title: album?.title || 'Unknown',
|
album_title: album?.title || 'Unknown',
|
||||||
|
artist_id: album?.artist?.id || 'unknown',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function trackUnblockAlbum(album) {
|
export function trackUnblockAlbum(album) {
|
||||||
trackEvent('Unblock Album', {
|
trackEvent('Unblock Album', {
|
||||||
|
album_id: album?.id || 'unknown',
|
||||||
album_title: album?.title || 'Unknown',
|
album_title: album?.title || 'Unknown',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function trackBlockArtist(artist) {
|
export function trackBlockArtist(artist) {
|
||||||
trackEvent('Block Artist', {
|
trackEvent('Block Artist', {
|
||||||
|
artist_id: artist?.id || 'unknown',
|
||||||
artist_name: artist?.name || 'Unknown',
|
artist_name: artist?.name || 'Unknown',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function trackUnblockArtist(artist) {
|
export function trackUnblockArtist(artist) {
|
||||||
trackEvent('Unblock Artist', {
|
trackEvent('Unblock Artist', {
|
||||||
|
artist_id: artist?.id || 'unknown',
|
||||||
artist_name: artist?.name || 'Unknown',
|
artist_name: artist?.name || 'Unknown',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -634,6 +717,8 @@ export function trackSessionEnd(duration) {
|
||||||
|
|
||||||
// Initialize analytics on page load
|
// Initialize analytics on page load
|
||||||
export function initAnalytics() {
|
export function initAnalytics() {
|
||||||
|
if (!isAnalyticsEnabled()) return;
|
||||||
|
|
||||||
// Track initial page view
|
// Track initial page view
|
||||||
trackPageView(window.location.pathname);
|
trackPageView(window.location.pathname);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import {
|
||||||
pwaUpdateSettings,
|
pwaUpdateSettings,
|
||||||
contentBlockingSettings,
|
contentBlockingSettings,
|
||||||
musicProviderSettings,
|
musicProviderSettings,
|
||||||
|
analyticsSettings,
|
||||||
} from './storage.js';
|
} from './storage.js';
|
||||||
import { audioContextManager, EQ_PRESETS } from './audio-context.js';
|
import { audioContextManager, EQ_PRESETS } from './audio-context.js';
|
||||||
import { getButterchurnPresets } from './visualizers/butterchurn.js';
|
import { getButterchurnPresets } from './visualizers/butterchurn.js';
|
||||||
|
|
@ -2363,6 +2364,15 @@ export function initializeSettings(scrobbler, player, api, ui) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Analytics Toggle
|
||||||
|
const analyticsToggle = document.getElementById('analytics-toggle');
|
||||||
|
if (analyticsToggle) {
|
||||||
|
analyticsToggle.checked = analyticsSettings.isEnabled();
|
||||||
|
analyticsToggle.addEventListener('change', (e) => {
|
||||||
|
analyticsSettings.setEnabled(e.target.checked);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Reset Local Data Button
|
// Reset Local Data Button
|
||||||
const resetLocalDataBtn = document.getElementById('reset-local-data-btn');
|
const resetLocalDataBtn = document.getElementById('reset-local-data-btn');
|
||||||
if (resetLocalDataBtn) {
|
if (resetLocalDataBtn) {
|
||||||
|
|
|
||||||
|
|
@ -1481,6 +1481,23 @@ export const homePageSettings = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const analyticsSettings = {
|
||||||
|
ENABLED_KEY: 'analytics-enabled',
|
||||||
|
|
||||||
|
isEnabled() {
|
||||||
|
try {
|
||||||
|
const val = localStorage.getItem(this.ENABLED_KEY);
|
||||||
|
return val === null ? true : val === 'true';
|
||||||
|
} catch {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setEnabled(enabled) {
|
||||||
|
localStorage.setItem(this.ENABLED_KEY, enabled ? 'true' : 'false');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const sidebarSectionSettings = {
|
export const sidebarSectionSettings = {
|
||||||
SHOW_HOME_KEY: 'sidebar-show-home',
|
SHOW_HOME_KEY: 'sidebar-show-home',
|
||||||
SHOW_LIBRARY_KEY: 'sidebar-show-library',
|
SHOW_LIBRARY_KEY: 'sidebar-show-library',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue