Skip to content

Commit

Permalink
Use play stats endpoint instead of doing calculation clientside (#791)
Browse files Browse the repository at this point in the history
  • Loading branch information
robbevp authored Jun 30, 2022
1 parent 2b7ce50 commit d5dcebe
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 92 deletions.
5 changes: 2 additions & 3 deletions src/components/PercentagePlayedCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { gsap } from "gsap";
export default {
name: "PercentagePlayedCard",
props: {
plays: {
playStats: {
type: Array,
required: true,
},
Expand All @@ -63,8 +63,7 @@ export default {
},
computed: {
playedTracksInPeriod() {
const trackIds = this.plays.map((p) => p.track_id);
return [...new Set(trackIds)];
return this.playStats.map((p) => p.track_id);
},
playedTracksLength() {
return this.playedTracksInPeriod.reduce((acc, cur) => {
Expand Down
8 changes: 3 additions & 5 deletions src/components/PlayCountCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ import { gsap } from "gsap";
export default {
name: "PlayCountCard",
props: {
plays: {
playStats: {
type: Array,
required: true,
},
Expand All @@ -92,12 +92,10 @@ export default {
computed: {
...mapState("tracks", ["tracks"]),
playCount() {
return this.plays.length;
return this.playStats.reduce((acc, cur) => acc + cur.count, 0);
},
playTime() {
return this.plays.reduce((acc, cur) => {
return acc + (this.tracks[cur.track_id]?.length || 0);
}, 0);
return this.playStats.reduce((acc, cur) => acc + cur.total_length, 0);
},
},
watch: {
Expand Down
6 changes: 3 additions & 3 deletions src/components/TopAlbumsList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import TopList from "@/components/TopList";
export default {
name: "TopAlbumsList",
props: {
plays: {
playStats: {
type: Array,
required: true,
},
Expand All @@ -54,8 +54,8 @@ export default {
topAlbums() {
return Object.entries(
this.useTrackLength
? calcPlayTimeForAlbums(this.plays, this.tracks, this.useAverage)
: calcPlayCountForAlbums(this.plays, this.tracks, this.useAverage)
? calcPlayTimeForAlbums(this.playStats, this.tracks, this.useAverage)
: calcPlayCountForAlbums(this.playStats, this.tracks, this.useAverage)
)
.sort((a1, a2) => a2[1] - a1[1])
.slice(0, 10);
Expand Down
12 changes: 7 additions & 5 deletions src/components/TopArtistsList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@

<script>
import { mapState } from "vuex";
import { calcPlayCountForArtists, calcPlayTimeForArtists } from "@/reducers";
import { calcPlayStatsForArtists } from "@/reducers";
import TopList from "@/components/TopList";
export default {
name: "TopArtistsList",
props: {
plays: {
playStats: {
type: Array,
required: true,
},
Expand All @@ -34,9 +34,11 @@ export default {
...mapState("tracks", ["tracks"]),
topTracks() {
return Object.entries(
this.useTrackLength
? calcPlayTimeForArtists(this.plays, this.tracks)
: calcPlayCountForArtists(this.plays, this.tracks)
calcPlayStatsForArtists(
this.playStats,
this.tracks,
this.useTrackLength
)
)
.sort((t1, t2) => t2[1] - t1[1])
.slice(0, 10);
Expand Down
18 changes: 8 additions & 10 deletions src/components/TopTracksList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@

<script>
import { mapState } from "vuex";
import { calcPlayCountForTracks, calcPlayTimeForTracks } from "@/reducers";
import TopList from "@/components/TopList";
export default {
name: "TopTracksList",
props: {
plays: {
playStats: {
type: Array,
required: true,
},
Expand All @@ -31,18 +30,17 @@ export default {
},
computed: {
...mapState("tracks", ["tracks"]),
useProp() {
return this.useTrackLength ? "total_length" : "count";
},
topTracks() {
return Object.entries(
this.useTrackLength
? calcPlayTimeForTracks(this.plays, this.tracks)
: calcPlayCountForTracks(this.plays)
)
.sort((t1, t2) => t2[1] - t1[1])
return [...this.playStats]
.sort((t1, t2) => t2[this.useProp] - t1[this.useProp])
.slice(0, 10);
},
listData() {
return [...this.topTracks].map((tt) => {
const track = this.tracks[tt[0]];
const track = this.tracks[tt.track_id];
const trackArtists = track?.track_artists
.filter((a) => !a.hidden)
.map((ta) => ta)
Expand All @@ -51,7 +49,7 @@ export default {
.join(" / ");
const label = `${trackArtists} - ${track?.title}`;
return {
count: tt[1],
count: tt[this.useProp],
label,
};
});
Expand Down
76 changes: 18 additions & 58 deletions src/reducers.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,15 @@
export function calcPlayCountForTracks(plays) {
export function calcPlayCountForAlbums(playStats, tracks, useAverage = false) {
const acc = {};
for (const play of plays) {
if (!(play.track_id in acc)) {
acc[play.track_id] = 1;
} else {
acc[play.track_id]++;
}
}
return acc;
}

export function calcPlayTimeForTracks(plays, tracks) {
const acc = calcPlayCountForTracks(plays);
for (const track_id in acc) {
acc[track_id] = acc[track_id] * tracks[track_id]?.length || 0;
}
return acc;
}

export function calcPlayCountForAlbums(plays, tracks, useAverage = false) {
const playCounts = calcPlayCountForTracks(plays);
const acc = {};
for (const track_id in playCounts) {
const album_id = tracks[track_id]?.album_id;
for (const stat of playStats) {
const album_id = tracks[stat.track_id]?.album_id;
if (!album_id) {
continue;
}

if (!(album_id in acc)) {
acc[album_id] = playCounts[track_id];
acc[album_id] = stat.count;
} else {
acc[album_id] += playCounts[track_id];
acc[album_id] += stat.count;
}
}

Expand All @@ -44,19 +23,18 @@ export function calcPlayCountForAlbums(plays, tracks, useAverage = false) {
return acc;
}

export function calcPlayTimeForAlbums(plays, tracks, useAverage = false) {
const playCounts = calcPlayCountForTracks(plays);
export function calcPlayTimeForAlbums(playStats, tracks, useAverage = false) {
const acc = {};
for (const track_id in playCounts) {
const track = tracks[track_id];
if (!track) {
for (const stat of playStats) {
const album_id = tracks[stat.track_id]?.album_id;
if (!album_id) {
continue;
}

if (!(track.album_id in acc)) {
acc[track.album_id] = playCounts[track_id] * track.length;
if (!(album_id in acc)) {
acc[album_id] = stat.total_length;
} else {
acc[track.album_id] += playCounts[track_id] * track.length;
acc[album_id] += stat.total_length;
}
}

Expand All @@ -73,36 +51,18 @@ export function calcPlayTimeForAlbums(plays, tracks, useAverage = false) {
return acc;
}

export function calcPlayCountForArtists(plays, tracks) {
const playCounts = calcPlayCountForTracks(plays);
const acc = {};
for (const track_id in playCounts) {
const uniqueIds = (tracks[track_id]?.track_artists || [])
.map((ta) => ta.artist_id)
.filter((id, index, list) => list.indexOf(id) == index);
for (const ta of uniqueIds) {
if (!(ta in acc)) {
acc[ta] = playCounts[track_id];
} else {
acc[ta] += playCounts[track_id];
}
}
}
return acc;
}

export function calcPlayTimeForArtists(plays, tracks) {
const playCounts = calcPlayCountForTracks(plays);
export function calcPlayStatsForArtists(playStats, tracks, useTrackLength) {
const prop = useTrackLength ? "total_length" : "count";
const acc = {};
for (const track_id in playCounts) {
const uniqueIds = (tracks[track_id]?.track_artists || [])
for (const stat of playStats) {
const uniqueIds = (tracks[stat.track_id]?.track_artists || [])
.map((ta) => ta.artist_id)
.filter((id, index, list) => list.indexOf(id) == index);
for (const ta of uniqueIds) {
if (!(ta in acc)) {
acc[ta] = playCounts[track_id] * tracks[track_id].length;
acc[ta] = stat[prop];
} else {
acc[ta] += playCounts[track_id] * tracks[track_id].length;
acc[ta] += stat[prop];
}
}
}
Expand Down
42 changes: 34 additions & 8 deletions src/views/Stats.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,19 @@
</VRow>
<div class="stats">
<PlayCountCard
:plays="filteredPlays"
:playStats="playStats"
title=""
class="stats__play-count"
/>
<TopTracksList
class="stats__top-tracks"
:plays="filteredPlays"
:playStats="playStats"
:use-track-length="useTrackLength"
:title="$t('stats.topTracks')"
/>
<PercentagePlayedCard
class="stats__percentage-played"
:plays="filteredPlays"
:playStats="playStats"
:use-track-length="useTrackLength"
:tracks="filteredTracks"
:title="
Expand All @@ -39,13 +39,13 @@
/>
<TopAlbumsList
class="stats__top-albums"
:plays="filteredPlays"
:playStats="playStats"
:use-track-length="useTrackLength"
:title="$t('stats.topAlbums')"
/>
<TopArtistsList
class="stats__top-artists"
:plays="filteredPlays"
:playStats="playStats"
:use-track-length="useTrackLength"
:title="$t('stats.topArtists')"
/>
Expand All @@ -69,6 +69,8 @@ import TopArtistsList from "@/components/TopArtistsList";
import TopTracksList from "@/components/TopTracksList";
import TopAlbumsList from "@/components/TopAlbumsList";
import { filterPlaysByPeriod, filterPlaysByTracks } from "@/filters";
import $api from "../api";
import { PlaysScope } from "@accentor/api-client-js";
export default {
name: "Stats",
Expand All @@ -91,6 +93,7 @@ export default {
end: null,
},
useTrackLength: false,
playStats: [],
};
},
props: {
Expand All @@ -99,13 +102,11 @@ export default {
default: null,
},
},
created() {
this.reloadPlays();
},
computed: {
...mapGetters({
plays: "plays/plays",
}),
...mapState("artists", ["artists"]),
artistName() {
return this.artist_id && this.artists[this.artist_id]?.name;
Expand Down Expand Up @@ -138,13 +139,38 @@ export default {
}
return this.$store.getters["tracks/tracks"];
},
playStatsScope() {
const scope = new PlaysScope();
if (this.period.start && this.period.end) {
scope.playedAfter(this.period.start);
scope.playedBefore(this.period.end);
}
if (this.artist_id) {
scope.artist(this.artist_id);
}
return scope;
},
},
methods: {
async reloadPlays() {
await this.$store.dispatch("plays/index");
},
async loadPlayStats() {
const gen = $api.plays.stats(this.$store.state.auth, this.playStatsScope);
let done = false;
let results = [];
while (!done) {
let value = [];
({ value, done } = await gen.next());
results.push(...value);
}
this.playStats = results;
},
},
watch: {
playStatsScope() {
this.loadPlayStats();
},
useTrackLength() {
if (this.useTrackLength.toString() !== this.$route.query.useTrackLength) {
this.$router.replace({
Expand Down

0 comments on commit d5dcebe

Please sign in to comment.