Compare commits
1 Commits
master
...
statistics
Author | SHA1 | Date |
---|---|---|
|
bef55a6994 | 5 years ago |
12 changed files with 323 additions and 33 deletions
@ -0,0 +1,251 @@ |
||||
import React, { useEffect, useState } from 'react'; |
||||
import { WindowState } from "./Windows" |
||||
import { MusicStore, whichStore } from '../../lib/MusicStore'; |
||||
import { songGetters, SongGetters } from '../../lib/songGetters'; |
||||
import { Typography, Box, CircularProgress, Paper } from '@material-ui/core'; |
||||
import EqualizerIcon from '@material-ui/icons/Equalizer'; |
||||
import * as serverApi from '../../api'; |
||||
|
||||
var _ = require('lodash'); |
||||
|
||||
export interface SongStatistics { |
||||
numberOf: number, |
||||
noAlbum: number, |
||||
noArtist: number, |
||||
perStore: Record<MusicStore, number>, |
||||
} |
||||
|
||||
export interface AlbumStatistics { |
||||
numberOf: number, |
||||
noSongs: number, |
||||
noArtist: number, |
||||
perStore: Record<MusicStore, number>, |
||||
} |
||||
|
||||
export interface ArtistStatistics { |
||||
numberOf: number, |
||||
noSongs: number, |
||||
noAlbums: number, |
||||
perStore: Record<MusicStore, number>, |
||||
} |
||||
|
||||
export interface TagStatistics { |
||||
numberOf: number, |
||||
noItems: number, |
||||
} |
||||
|
||||
|
||||
export interface Statistics { |
||||
songStats: SongStatistics, |
||||
artistStats: ArtistStatistics, |
||||
albumStats: AlbumStatistics, |
||||
tagStats: TagStatistics, |
||||
} |
||||
export function newStatistics(): Statistics { |
||||
return { |
||||
songStats: { |
||||
numberOf: 0, |
||||
noAlbum: 0, |
||||
noArtist: 0, |
||||
perStore: { |
||||
[MusicStore.GooglePlayMusic]: 0 |
||||
} |
||||
}, |
||||
artistStats: { |
||||
numberOf: 0, |
||||
noAlbums: 0, |
||||
noSongs: 0, |
||||
perStore: { |
||||
[MusicStore.GooglePlayMusic]: 0 |
||||
} |
||||
}, |
||||
albumStats: { |
||||
numberOf: 0, |
||||
noArtist: 0, |
||||
noSongs: 0, |
||||
perStore: { |
||||
[MusicStore.GooglePlayMusic]: 0 |
||||
} |
||||
}, |
||||
tagStats: { |
||||
numberOf: 0, |
||||
noItems: 0, |
||||
} |
||||
}; |
||||
} |
||||
|
||||
export interface StatisticsWindowState extends WindowState { |
||||
stats: Statistics | null, |
||||
retrievingStats: boolean, |
||||
} |
||||
|
||||
export enum StatisticsWindowStateActions { |
||||
Reset = "Reset", |
||||
AddSongs = "AddSongs", |
||||
SetRetrieving = "SetRetrieving", |
||||
} |
||||
|
||||
export function StatisticsWindowReducer(state: StatisticsWindowState, action: any) { |
||||
switch (action.type) { |
||||
case StatisticsWindowStateActions.Reset: |
||||
return { |
||||
...state, |
||||
stats: null, |
||||
retrievingStats: true, |
||||
}; |
||||
case StatisticsWindowStateActions.AddSongs: |
||||
return { |
||||
...state, |
||||
stats: addSongs(state.stats, action.songs, songGetters) |
||||
}; |
||||
case StatisticsWindowStateActions.SetRetrieving: |
||||
return { |
||||
...state, |
||||
retrievingStats: action.value, |
||||
}; |
||||
default: |
||||
throw new Error("Unimplemented SongWindow state update.") |
||||
} |
||||
} |
||||
|
||||
export function addSongs(stats: Statistics | null, songs: any[], songGetters: SongGetters) { |
||||
var r: Statistics = stats ? _.cloneDeep(stats) : newStatistics(); |
||||
|
||||
songs.forEach((song: any) => { |
||||
r.songStats.numberOf++; |
||||
if (songGetters.getAlbumIds(song) === []) { r.songStats.noAlbum++; } |
||||
if (songGetters.getArtistIds(song) === []) { r.songStats.noArtist++; } |
||||
songGetters.getStoreLinks(song).forEach((link: string) => { |
||||
const which = whichStore(link); |
||||
if (which) { |
||||
r.songStats.perStore[which]++; |
||||
} |
||||
}) |
||||
}) |
||||
|
||||
return r; |
||||
} |
||||
|
||||
export async function gatherStats(dispatch: (action: any) => void) { |
||||
// Gather 50 songs at a time
|
||||
for (let i = 0; ; i += 50) { |
||||
var q: serverApi.QueryRequest = { |
||||
query: {}, |
||||
offsetsLimits: { |
||||
songOffset: i, |
||||
songLimit: 50, |
||||
}, |
||||
ordering: { |
||||
orderBy: { |
||||
type: serverApi.OrderByType.Name, |
||||
}, |
||||
ascending: true, |
||||
}, |
||||
}; |
||||
|
||||
const requestOpts = { |
||||
method: 'POST', |
||||
headers: { 'Content-Type': 'application/json' }, |
||||
body: JSON.stringify(q), |
||||
}; |
||||
const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts) |
||||
let json: any = await response.json(); |
||||
const songs = json.songs; |
||||
|
||||
if (!songs.length || songs.length === 0) { |
||||
// No more songs.
|
||||
break; |
||||
} |
||||
dispatch({ |
||||
type: StatisticsWindowStateActions.AddSongs, |
||||
songs: songs, |
||||
}) |
||||
} |
||||
} |
||||
|
||||
export function StatisticsDisplay(props: { stats: Statistics }) { |
||||
return <Box display="flex" alignItems="center"> |
||||
<Box m={2}> |
||||
<Paper> |
||||
<Box p={2}> |
||||
<Box mb={1}><Typography variant="h4">Songs</Typography></Box> |
||||
<Typography>Total: {props.stats.songStats.numberOf}</Typography> |
||||
<Typography>Without album: {props.stats.songStats.noAlbum}</Typography> |
||||
<Typography>Without artist: {props.stats.songStats.noArtist}</Typography> |
||||
<Typography>Linked to Google Play Music: {props.stats.songStats.perStore[MusicStore.GooglePlayMusic]}</Typography> |
||||
</Box> |
||||
</Paper> |
||||
</Box> |
||||
<Box m={2}> |
||||
<Paper> |
||||
<Box p={2}> |
||||
<Box mb={1}><Typography variant="h4">Artists</Typography></Box> |
||||
<Typography>Total: {props.stats.artistStats.numberOf}</Typography> |
||||
<Typography>Without album: {props.stats.artistStats.noAlbums}</Typography> |
||||
<Typography>Without songs: {props.stats.artistStats.noSongs}</Typography> |
||||
<Typography>Linked to Google Play Music: {props.stats.artistStats.perStore[MusicStore.GooglePlayMusic]}</Typography> |
||||
</Box> |
||||
</Paper> |
||||
</Box> |
||||
<Box m={2}> |
||||
<Paper> |
||||
<Box p={2}> |
||||
<Box mb={1}><Typography variant="h4">Albums</Typography></Box> |
||||
<Typography>Total: {props.stats.albumStats.numberOf}</Typography> |
||||
<Typography>Without album: {props.stats.albumStats.noArtist}</Typography> |
||||
<Typography>Without songs: {props.stats.albumStats.noSongs}</Typography> |
||||
<Typography>Linked to Google Play Music: {props.stats.albumStats.perStore[MusicStore.GooglePlayMusic]}</Typography> |
||||
</Box> |
||||
</Paper> |
||||
</Box> |
||||
<Box m={2}> |
||||
<Paper> |
||||
<Box p={2}> |
||||
<Box mb={1}><Typography variant="h4">Tags</Typography></Box> |
||||
<Typography>Total: {props.stats.tagStats.numberOf}</Typography> |
||||
<Typography>Unused: {props.stats.tagStats.noItems}</Typography> |
||||
</Box> |
||||
</Paper> |
||||
</Box> |
||||
</Box > |
||||
} |
||||
|
||||
export interface IProps { |
||||
state: StatisticsWindowState, |
||||
dispatch: (action: any) => void, |
||||
mainDispatch: (action: any) => void, |
||||
} |
||||
|
||||
export default function StatisticsWindow(props: IProps) { |
||||
useEffect(() => { |
||||
if (!props.state.retrievingStats) return; |
||||
|
||||
props.dispatch({ |
||||
type: StatisticsWindowStateActions.Reset |
||||
}) |
||||
gatherStats(props.dispatch).then(() => { |
||||
props.dispatch({ |
||||
type: StatisticsWindowStateActions.SetRetrieving, |
||||
value: false, |
||||
}) |
||||
}) |
||||
}, [props.state.retrievingStats]) |
||||
|
||||
return <Box width="100%" justifyContent="center" display="flex" flexWrap="wrap"> |
||||
<Box |
||||
m={1} |
||||
mt={4} |
||||
width="80%" |
||||
> |
||||
<EqualizerIcon style={{ fontSize: 80 }} /> |
||||
</Box> |
||||
<Box |
||||
m={1} |
||||
mt={4} |
||||
width="80%" |
||||
> |
||||
<Box visibility={props.state.retrievingStats ? "visible" : "hidden"}><CircularProgress /></Box> |
||||
{props.state.stats && <StatisticsDisplay stats={props.state.stats} />} |
||||
</Box> |
||||
</Box> |
||||
} |
@ -0,0 +1,9 @@ |
||||
export enum MusicStore { |
||||
GooglePlayMusic = "GPM", |
||||
} |
||||
export function whichStore(url: string) { |
||||
if(url.includes('play.google.com')) { |
||||
return MusicStore.GooglePlayMusic; |
||||
} |
||||
return undefined; |
||||
} |
Loading…
Reference in new issue