mirror of
https://github.com/riwiwa/muzi.git
synced 2026-03-04 00:51:59 -08:00
add top albums and top tracks widgets to profile
This commit is contained in:
137
db/entities.go
137
db/entities.go
@@ -510,6 +510,19 @@ type TopArtist struct {
|
||||
ListenCount int
|
||||
}
|
||||
|
||||
type TopAlbum struct {
|
||||
AlbumName string
|
||||
Artist string
|
||||
CoverUrl string
|
||||
ListenCount int
|
||||
}
|
||||
|
||||
type TopTrack struct {
|
||||
SongName string
|
||||
Artist string
|
||||
ListenCount int
|
||||
}
|
||||
|
||||
func GetTopArtists(userId int, limit int, startDate, endDate *time.Time) ([]TopArtist, error) {
|
||||
var err error
|
||||
var rows pgx.Rows
|
||||
@@ -579,6 +592,130 @@ func GetTopArtists(userId int, limit int, startDate, endDate *time.Time) ([]TopA
|
||||
return topArtists, nil
|
||||
}
|
||||
|
||||
func GetTopAlbums(userId int, limit int, startDate, endDate *time.Time) ([]TopAlbum, error) {
|
||||
var err error
|
||||
var rows pgx.Rows
|
||||
|
||||
if startDate == nil && endDate == nil {
|
||||
rows, err = Pool.Query(context.Background(),
|
||||
`SELECT h.album_name, h.artist, COALESCE(a.cover_url, ''), COUNT(*) as listen_count
|
||||
FROM history h
|
||||
LEFT JOIN albums a ON a.user_id = h.user_id AND a.title = h.album_name AND a.artist_id IN (SELECT id FROM artists WHERE user_id = h.user_id AND name = h.artist)
|
||||
WHERE h.user_id = $1 AND h.album_name IS NOT NULL AND h.album_name != ''
|
||||
GROUP BY h.album_name, h.artist, a.cover_url
|
||||
ORDER BY listen_count DESC
|
||||
LIMIT $2`,
|
||||
userId, limit)
|
||||
} else if startDate != nil && endDate == nil {
|
||||
rows, err = Pool.Query(context.Background(),
|
||||
`SELECT h.album_name, h.artist, COALESCE(a.cover_url, ''), COUNT(*) as listen_count
|
||||
FROM history h
|
||||
LEFT JOIN albums a ON a.user_id = h.user_id AND a.title = h.album_name AND a.artist_id IN (SELECT id FROM artists WHERE user_id = h.user_id AND name = h.artist)
|
||||
WHERE h.user_id = $1 AND h.timestamp >= $2 AND h.album_name IS NOT NULL AND h.album_name != ''
|
||||
GROUP BY h.album_name, h.artist, a.cover_url
|
||||
ORDER BY listen_count DESC
|
||||
LIMIT $3`,
|
||||
userId, startDate, limit)
|
||||
} else if startDate == nil && endDate != nil {
|
||||
rows, err = Pool.Query(context.Background(),
|
||||
`SELECT h.album_name, h.artist, COALESCE(a.cover_url, ''), COUNT(*) as listen_count
|
||||
FROM history h
|
||||
LEFT JOIN albums a ON a.user_id = h.user_id AND a.title = h.album_name AND a.artist_id IN (SELECT id FROM artists WHERE user_id = h.user_id AND name = h.artist)
|
||||
WHERE h.user_id = $1 AND h.timestamp <= $2 AND h.album_name IS NOT NULL AND h.album_name != ''
|
||||
GROUP BY h.album_name, h.artist, a.cover_url
|
||||
ORDER BY listen_count DESC
|
||||
LIMIT $3`,
|
||||
userId, endDate, limit)
|
||||
} else {
|
||||
rows, err = Pool.Query(context.Background(),
|
||||
`SELECT h.album_name, h.artist, COALESCE(a.cover_url, ''), COUNT(*) as listen_count
|
||||
FROM history h
|
||||
LEFT JOIN albums a ON a.user_id = h.user_id AND a.title = h.album_name AND a.artist_id IN (SELECT id FROM artists WHERE user_id = h.user_id AND name = h.artist)
|
||||
WHERE h.user_id = $1 AND h.timestamp >= $2 AND h.timestamp <= $3 AND h.album_name IS NOT NULL AND h.album_name != ''
|
||||
GROUP BY h.album_name, h.artist, a.cover_url
|
||||
ORDER BY listen_count DESC
|
||||
LIMIT $4`,
|
||||
userId, startDate, endDate, limit)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var topAlbums []TopAlbum
|
||||
for rows.Next() {
|
||||
var albumName, artist, coverUrl string
|
||||
var count int
|
||||
err := rows.Scan(&albumName, &artist, &coverUrl, &count)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
topAlbums = append(topAlbums, TopAlbum{AlbumName: albumName, Artist: artist, CoverUrl: coverUrl, ListenCount: count})
|
||||
}
|
||||
return topAlbums, nil
|
||||
}
|
||||
|
||||
func GetTopTracks(userId int, limit int, startDate, endDate *time.Time) ([]TopTrack, error) {
|
||||
var err error
|
||||
var rows pgx.Rows
|
||||
|
||||
if startDate == nil && endDate == nil {
|
||||
rows, err = Pool.Query(context.Background(),
|
||||
`SELECT song_name, artist, COUNT(*) as listen_count
|
||||
FROM history
|
||||
WHERE user_id = $1
|
||||
GROUP BY song_name, artist
|
||||
ORDER BY listen_count DESC
|
||||
LIMIT $2`,
|
||||
userId, limit)
|
||||
} else if startDate != nil && endDate == nil {
|
||||
rows, err = Pool.Query(context.Background(),
|
||||
`SELECT song_name, artist, COUNT(*) as listen_count
|
||||
FROM history
|
||||
WHERE user_id = $1 AND timestamp >= $2
|
||||
GROUP BY song_name, artist
|
||||
ORDER BY listen_count DESC
|
||||
LIMIT $3`,
|
||||
userId, startDate, limit)
|
||||
} else if startDate == nil && endDate != nil {
|
||||
rows, err = Pool.Query(context.Background(),
|
||||
`SELECT song_name, artist, COUNT(*) as listen_count
|
||||
FROM history
|
||||
WHERE user_id = $1 AND timestamp <= $2
|
||||
GROUP BY song_name, artist
|
||||
ORDER BY listen_count DESC
|
||||
LIMIT $3`,
|
||||
userId, endDate, limit)
|
||||
} else {
|
||||
rows, err = Pool.Query(context.Background(),
|
||||
`SELECT song_name, artist, COUNT(*) as listen_count
|
||||
FROM history
|
||||
WHERE user_id = $1 AND timestamp >= $2 AND timestamp <= $3
|
||||
GROUP BY song_name, artist
|
||||
ORDER BY listen_count DESC
|
||||
LIMIT $4`,
|
||||
userId, startDate, endDate, limit)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var topTracks []TopTrack
|
||||
for rows.Next() {
|
||||
var songName, artist string
|
||||
var count int
|
||||
err := rows.Scan(&songName, &artist, &count)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
topTracks = append(topTracks, TopTrack{SongName: songName, Artist: artist, ListenCount: count})
|
||||
}
|
||||
return topTracks, nil
|
||||
}
|
||||
|
||||
func GetSongStats(userId, songId int) (int, error) {
|
||||
var count int
|
||||
err := Pool.QueryRow(context.Background(),
|
||||
|
||||
@@ -32,18 +32,89 @@ function updateLimitOptions() {
|
||||
|
||||
for (let option of limitSelect.options) {
|
||||
const value = parseInt(option.value);
|
||||
if (value > maxLimit) {
|
||||
if (value > maxLimit || (view === 'grid' && value === 7)) {
|
||||
option.style.display = 'none';
|
||||
} else {
|
||||
option.style.display = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (parseInt(limitSelect.value) > maxLimit) {
|
||||
if (parseInt(limitSelect.value) > maxLimit || (view === 'grid' && parseInt(limitSelect.value) === 7)) {
|
||||
limitSelect.value = maxLimit;
|
||||
}
|
||||
}
|
||||
|
||||
function updateTopAlbums() {
|
||||
const period = document.getElementById('album-period-select').value;
|
||||
const limit = document.getElementById('album-limit-select').value;
|
||||
const view = document.getElementById('album-view-select').value;
|
||||
|
||||
const customDates = document.getElementById('album-custom-dates');
|
||||
if (period === 'custom') {
|
||||
customDates.style.display = 'inline-block';
|
||||
} else {
|
||||
customDates.style.display = 'none';
|
||||
}
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
params.set('album_period', period);
|
||||
params.set('album_limit', limit);
|
||||
params.set('album_view', view);
|
||||
|
||||
if (period === 'custom') {
|
||||
const startDate = document.getElementById('album-start-date').value;
|
||||
const endDate = document.getElementById('album-end-date').value;
|
||||
if (startDate) params.set('album_start', startDate);
|
||||
if (endDate) params.set('album_end', endDate);
|
||||
}
|
||||
|
||||
window.location.search = params.toString();
|
||||
}
|
||||
|
||||
function updateTopAlbumsLimitOptions() {
|
||||
const view = document.getElementById('album-view-select').value;
|
||||
const limitSelect = document.getElementById('album-limit-select');
|
||||
const maxLimit = view === 'grid' ? 8 : 30;
|
||||
|
||||
for (let option of limitSelect.options) {
|
||||
const value = parseInt(option.value);
|
||||
if (value > maxLimit || (view === 'grid' && value === 7)) {
|
||||
option.style.display = 'none';
|
||||
} else {
|
||||
option.style.display = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (parseInt(limitSelect.value) > maxLimit || (view === 'grid' && parseInt(limitSelect.value) === 7)) {
|
||||
limitSelect.value = maxLimit;
|
||||
}
|
||||
}
|
||||
|
||||
function updateTopTracks() {
|
||||
const period = document.getElementById('track-period-select').value;
|
||||
const limit = document.getElementById('track-limit-select').value;
|
||||
|
||||
const customDates = document.getElementById('track-custom-dates');
|
||||
if (period === 'custom') {
|
||||
customDates.style.display = 'inline-block';
|
||||
} else {
|
||||
customDates.style.display = 'none';
|
||||
}
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
params.set('track_period', period);
|
||||
params.set('track_limit', limit);
|
||||
|
||||
if (period === 'custom') {
|
||||
const startDate = document.getElementById('track-start-date').value;
|
||||
const endDate = document.getElementById('track-end-date').value;
|
||||
if (startDate) params.set('track_start', startDate);
|
||||
if (endDate) params.set('track_end', endDate);
|
||||
}
|
||||
|
||||
window.location.search = params.toString();
|
||||
}
|
||||
|
||||
function syncGridHeights() {}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
@@ -57,4 +128,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
}
|
||||
|
||||
updateLimitOptions();
|
||||
|
||||
updateTopAlbumsLimitOptions();
|
||||
});
|
||||
|
||||
@@ -770,6 +770,22 @@ a.button:hover {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.top-albums {
|
||||
width: 50%;
|
||||
margin: 15px 0;
|
||||
padding: 10px;
|
||||
background: #1a1a1a;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.top-tracks {
|
||||
width: 50%;
|
||||
margin: 15px 0;
|
||||
padding: 10px;
|
||||
background: #1a1a1a;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.top-artists-controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -777,13 +793,34 @@ a.button:hover {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.top-albums-controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.top-tracks-controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#top-artists-display {
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
#top-albums-display {
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
#top-tracks-display {
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
.top-artists-controls h3 {
|
||||
margin: 0;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.controls-row {
|
||||
|
||||
@@ -157,6 +157,204 @@
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="top-albums">
|
||||
<div class="top-albums-controls">
|
||||
<h3>Top Albums</h3>
|
||||
<div class="controls-row">
|
||||
<label>
|
||||
Period:
|
||||
<select id="album-period-select" onchange="updateTopAlbums()">
|
||||
<option value="all_time" {{if eq .TopAlbumsPeriod "all_time"}}selected{{end}}>All Time</option>
|
||||
<option value="week" {{if eq .TopAlbumsPeriod "week"}}selected{{end}}>Last 7 Days</option>
|
||||
<option value="month" {{if eq .TopAlbumsPeriod "month"}}selected{{end}}>Last 30 Days</option>
|
||||
<option value="year" {{if eq .TopAlbumsPeriod "year"}}selected{{end}}>Last Year</option>
|
||||
<option value="custom" {{if eq .TopAlbumsPeriod "custom"}}selected{{end}}>Custom</option>
|
||||
</select>
|
||||
</label>
|
||||
<div id="album-custom-dates" style="display: {{if eq .TopAlbumsPeriod "custom"}}inline-block{{else}}none{{end}};">
|
||||
<input type="date" id="album-start-date" onchange="updateTopAlbums()">
|
||||
<input type="date" id="album-end-date" onchange="updateTopAlbums()">
|
||||
</div>
|
||||
<label>
|
||||
Count:
|
||||
<select id="album-limit-select" onchange="updateTopAlbums()">
|
||||
<option value="5" {{if eq .TopAlbumsLimit 5}}selected{{end}}>5</option>
|
||||
<option value="6" {{if eq .TopAlbumsLimit 6}}selected{{end}}>6</option>
|
||||
<option value="7" {{if eq .TopAlbumsLimit 7}}selected{{end}}>7</option>
|
||||
<option value="8" {{if eq .TopAlbumsLimit 8}}selected{{end}}>8</option>
|
||||
<option value="9" {{if eq .TopAlbumsLimit 9}}selected{{end}}>9</option>
|
||||
<option value="10" {{if eq .TopAlbumsLimit 10}}selected{{end}}>10</option>
|
||||
<option value="15" {{if eq .TopAlbumsLimit 15}}selected{{end}}>15</option>
|
||||
<option value="20" {{if eq .TopAlbumsLimit 20}}selected{{end}}>20</option>
|
||||
<option value="25" {{if eq .TopAlbumsLimit 25}}selected{{end}}>25</option>
|
||||
<option value="30" {{if eq .TopAlbumsLimit 30}}selected{{end}}>30</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
View:
|
||||
<select id="album-view-select" onchange="updateTopAlbumsLimitOptions(); updateTopAlbums()">
|
||||
<option value="grid" {{if eq .TopAlbumsView "grid"}}selected{{end}}>Grid</option>
|
||||
<option value="list" {{if eq .TopAlbumsView "list"}}selected{{end}}>List</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{{if .TopAlbums}}
|
||||
<div id="top-albums-display" class="top-albums-{{.TopAlbumsView}}">
|
||||
{{$view := .TopAlbumsView}}
|
||||
{{$albums := .TopAlbums}}
|
||||
{{$len := len $albums}}
|
||||
{{if eq $view "grid"}}
|
||||
<div class="artist-grid {{if mod $len 2}}artist-grid-odd{{end}}">
|
||||
{{if mod $len 2}}
|
||||
<div class="artist-cell-first">
|
||||
<a href="/profile/{{$.Username}}/album/{{urlquery (index $albums 0).Artist}}/{{urlquery (index $albums 0).AlbumName}}" class="grid-items-cover-image">
|
||||
<div class="grid-items-cover-image-image">
|
||||
{{if (index $albums 0).CoverUrl}}
|
||||
<img src="{{(index $albums 0).CoverUrl}}" alt="{{(index $albums 0).AlbumName}}">
|
||||
{{else}}
|
||||
<div class="artist-placeholder"></div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="grid-items-item-details">
|
||||
<p class="grid-items-item-main-text">{{(index $albums 0).AlbumName}}</p>
|
||||
<p class="grid-items-item-aux-text">{{(index $albums 0).Artist}}</p>
|
||||
<p class="grid-items-item-aux-text">{{formatInt (index $albums 0).ListenCount}} plays</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="artist-right-col">
|
||||
<div class="artist-row">
|
||||
{{range $i, $a := sliceAlbum $albums 1 (add 1 (div (sub $len 1) 2))}}
|
||||
<div class="artist-cell">
|
||||
<a href="/profile/{{$.Username}}/album/{{urlquery $a.Artist}}/{{urlquery $a.AlbumName}}" class="grid-items-cover-image">
|
||||
<div class="grid-items-cover-image-image">
|
||||
{{if $a.CoverUrl}}<img src="{{$a.CoverUrl}}" alt="{{$a.AlbumName}}">{{else}}<div class="artist-placeholder"></div>{{end}}
|
||||
</div>
|
||||
<div class="grid-items-item-details">
|
||||
<p class="grid-items-item-main-text">{{$a.AlbumName}}</p>
|
||||
<p class="grid-items-item-aux-text">{{$a.Artist}}</p>
|
||||
<p class="grid-items-item-aux-text">{{formatInt $a.ListenCount}} plays</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="artist-row">
|
||||
{{range $i, $a := sliceAlbum $albums (add 1 (div (sub $len 1) 2)) $len}}
|
||||
<div class="artist-cell">
|
||||
<a href="/profile/{{$.Username}}/album/{{urlquery $a.Artist}}/{{urlquery $a.AlbumName}}" class="grid-items-cover-image">
|
||||
<div class="grid-items-cover-image-image">
|
||||
{{if $a.CoverUrl}}<img src="{{$a.CoverUrl}}" alt="{{$a.AlbumName}}">{{else}}<div class="artist-placeholder"></div>{{end}}
|
||||
</div>
|
||||
<div class="grid-items-item-details">
|
||||
<p class="grid-items-item-main-text">{{$a.AlbumName}}</p>
|
||||
<p class="grid-items-item-aux-text">{{$a.Artist}}</p>
|
||||
<p class="grid-items-item-aux-text">{{formatInt $a.ListenCount}} plays</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="artist-row">
|
||||
{{range $i, $a := sliceAlbum $albums 0 (div $len 2)}}
|
||||
<div class="artist-cell">
|
||||
<a href="/profile/{{$.Username}}/album/{{urlquery $a.Artist}}/{{urlquery $a.AlbumName}}" class="grid-items-cover-image">
|
||||
<div class="grid-items-cover-image-image">
|
||||
{{if $a.CoverUrl}}<img src="{{$a.CoverUrl}}" alt="{{$a.AlbumName}}">{{else}}<div class="artist-placeholder"></div>{{end}}
|
||||
</div>
|
||||
<div class="grid-items-item-details">
|
||||
<p class="grid-items-item-main-text">{{$a.AlbumName}}</p>
|
||||
<p class="grid-items-item-aux-text">{{$a.Artist}}</p>
|
||||
<p class="grid-items-item-aux-text">{{formatInt $a.ListenCount}} plays</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="artist-row">
|
||||
{{range $i, $a := sliceAlbum $albums (div $len 2) $len}}
|
||||
<div class="artist-cell">
|
||||
<a href="/profile/{{$.Username}}/album/{{urlquery $a.Artist}}/{{urlquery $a.AlbumName}}" class="grid-items-cover-image">
|
||||
<div class="grid-items-cover-image-image">
|
||||
{{if $a.CoverUrl}}<img src="{{$a.CoverUrl}}" alt="{{$a.AlbumName}}">{{else}}<div class="artist-placeholder"></div>{{end}}
|
||||
</div>
|
||||
<div class="grid-items-item-details">
|
||||
<p class="grid-items-item-main-text">{{$a.AlbumName}}</p>
|
||||
<p class="grid-items-item-aux-text">{{$a.Artist}}</p>
|
||||
<p class="grid-items-item-aux-text">{{formatInt $a.ListenCount}} plays</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="artist-list">
|
||||
{{range $a := $albums}}
|
||||
<a href="/profile/{{$.Username}}/album/{{urlquery $a.Artist}}/{{urlquery $a.AlbumName}}" class="artist-row">
|
||||
{{if $a.CoverUrl}}<img src="{{$a.CoverUrl}}" alt="{{$a.AlbumName}}">{{else}}<div class="artist-placeholder-row"></div>{{end}}
|
||||
<span class="artist-name">{{$a.AlbumName}} - {{$a.Artist}}</span>
|
||||
<span class="artist-count">{{formatInt $a.ListenCount}} plays</span>
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="top-tracks">
|
||||
<div class="top-tracks-controls">
|
||||
<h3>Top Tracks</h3>
|
||||
<div class="controls-row">
|
||||
<label>
|
||||
Period:
|
||||
<select id="track-period-select" onchange="updateTopTracks()">
|
||||
<option value="all_time" {{if eq .TopTracksPeriod "all_time"}}selected{{end}}>All Time</option>
|
||||
<option value="week" {{if eq .TopTracksPeriod "week"}}selected{{end}}>Last 7 Days</option>
|
||||
<option value="month" {{if eq .TopTracksPeriod "month"}}selected{{end}}>Last 30 Days</option>
|
||||
<option value="year" {{if eq .TopTracksPeriod "year"}}selected{{end}}>Last Year</option>
|
||||
<option value="custom" {{if eq .TopTracksPeriod "custom"}}selected{{end}}>Custom</option>
|
||||
</select>
|
||||
</label>
|
||||
<div id="track-custom-dates" style="display: {{if eq .TopTracksPeriod "custom"}}inline-block{{else}}none{{end}};">
|
||||
<input type="date" id="track-start-date" onchange="updateTopTracks()">
|
||||
<input type="date" id="track-end-date" onchange="updateTopTracks()">
|
||||
</div>
|
||||
<label>
|
||||
Count:
|
||||
<select id="track-limit-select" onchange="updateTopTracks()">
|
||||
<option value="5" {{if eq .TopTracksLimit 5}}selected{{end}}>5</option>
|
||||
<option value="6" {{if eq .TopTracksLimit 6}}selected{{end}}>6</option>
|
||||
<option value="7" {{if eq .TopTracksLimit 7}}selected{{end}}>7</option>
|
||||
<option value="8" {{if eq .TopTracksLimit 8}}selected{{end}}>8</option>
|
||||
<option value="9" {{if eq .TopTracksLimit 9}}selected{{end}}>9</option>
|
||||
<option value="10" {{if eq .TopTracksLimit 10}}selected{{end}}>10</option>
|
||||
<option value="15" {{if eq .TopTracksLimit 15}}selected{{end}}>15</option>
|
||||
<option value="20" {{if eq .TopTracksLimit 20}}selected{{end}}>20</option>
|
||||
<option value="25" {{if eq .TopTracksLimit 25}}selected{{end}}>25</option>
|
||||
<option value="30" {{if eq .TopTracksLimit 30}}selected{{end}}>30</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{{if .TopTracks}}
|
||||
<div id="top-tracks-display">
|
||||
{{$tracks := .TopTracks}}
|
||||
<div class="artist-list">
|
||||
{{range $t := $tracks}}
|
||||
<a href="/profile/{{$.Username}}/song/{{urlquery $t.Artist}}/{{urlquery $t.SongName}}" class="artist-row">
|
||||
<span class="artist-name">{{$t.SongName}} - {{$t.Artist}}</span>
|
||||
<span class="artist-count">{{formatInt $t.ListenCount}} plays</span>
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="history">
|
||||
<h3>Listening History</h3>
|
||||
<table>
|
||||
|
||||
130
web/profile.go
130
web/profile.go
@@ -38,6 +38,13 @@ type ProfileData struct {
|
||||
TopArtistsPeriod string
|
||||
TopArtistsLimit int
|
||||
TopArtistsView string
|
||||
TopAlbums []db.TopAlbum
|
||||
TopAlbumsPeriod string
|
||||
TopAlbumsLimit int
|
||||
TopAlbumsView string
|
||||
TopTracks []db.TopTrack
|
||||
TopTracksPeriod string
|
||||
TopTracksLimit int
|
||||
}
|
||||
|
||||
// Render a page of the profile in the URL
|
||||
@@ -155,6 +162,129 @@ func profilePageHandler() http.HandlerFunc {
|
||||
profileData.TopArtists = topArtists
|
||||
}
|
||||
|
||||
albumPeriod := r.URL.Query().Get("album_period")
|
||||
if albumPeriod == "" {
|
||||
albumPeriod = "all_time"
|
||||
}
|
||||
|
||||
var albumStartDate, albumEndDate *time.Time
|
||||
albumNow := time.Now()
|
||||
switch albumPeriod {
|
||||
case "week":
|
||||
start := albumNow.AddDate(0, 0, -7)
|
||||
albumStartDate = &start
|
||||
case "month":
|
||||
start := albumNow.AddDate(0, -1, 0)
|
||||
albumStartDate = &start
|
||||
case "year":
|
||||
start := albumNow.AddDate(-1, 0, 0)
|
||||
albumStartDate = &start
|
||||
case "custom":
|
||||
albumStartStr := r.URL.Query().Get("album_start")
|
||||
albumEndStr := r.URL.Query().Get("album_end")
|
||||
if albumStartStr != "" {
|
||||
if t, err := time.Parse("2006-01-02", albumStartStr); err == nil {
|
||||
albumStartDate = &t
|
||||
}
|
||||
}
|
||||
if albumEndStr != "" {
|
||||
if t, err := time.Parse("2006-01-02", albumEndStr); err == nil {
|
||||
t = t.AddDate(0, 0, 1)
|
||||
albumEndDate = &t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
albumLimitStr := r.URL.Query().Get("album_limit")
|
||||
albumLimit := 10
|
||||
if albumLimitStr != "" {
|
||||
albumLimit, err = strconv.Atoi(albumLimitStr)
|
||||
if err != nil || albumLimit < 5 {
|
||||
albumLimit = 10
|
||||
}
|
||||
if albumLimit > 30 {
|
||||
albumLimit = 30
|
||||
}
|
||||
}
|
||||
|
||||
albumView := r.URL.Query().Get("album_view")
|
||||
if albumView == "" {
|
||||
albumView = "grid"
|
||||
}
|
||||
albumMaxLimit := 30
|
||||
if albumView == "grid" {
|
||||
albumMaxLimit = 8
|
||||
}
|
||||
if albumLimit > albumMaxLimit {
|
||||
albumLimit = albumMaxLimit
|
||||
}
|
||||
|
||||
profileData.TopAlbumsPeriod = albumPeriod
|
||||
profileData.TopAlbumsLimit = albumLimit
|
||||
profileData.TopAlbumsView = albumView
|
||||
|
||||
topAlbums, err := db.GetTopAlbums(userId, albumLimit, albumStartDate, albumEndDate)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Cannot get top albums: %v\n", err)
|
||||
} else {
|
||||
profileData.TopAlbums = topAlbums
|
||||
}
|
||||
|
||||
trackPeriod := r.URL.Query().Get("track_period")
|
||||
if trackPeriod == "" {
|
||||
trackPeriod = "all_time"
|
||||
}
|
||||
|
||||
var trackStartDate, trackEndDate *time.Time
|
||||
trackNow := time.Now()
|
||||
switch trackPeriod {
|
||||
case "week":
|
||||
start := trackNow.AddDate(0, 0, -7)
|
||||
trackStartDate = &start
|
||||
case "month":
|
||||
start := trackNow.AddDate(0, -1, 0)
|
||||
trackStartDate = &start
|
||||
case "year":
|
||||
start := trackNow.AddDate(-1, 0, 0)
|
||||
trackStartDate = &start
|
||||
case "custom":
|
||||
trackStartStr := r.URL.Query().Get("track_start")
|
||||
trackEndStr := r.URL.Query().Get("track_end")
|
||||
if trackStartStr != "" {
|
||||
if t, err := time.Parse("2006-01-02", trackStartStr); err == nil {
|
||||
trackStartDate = &t
|
||||
}
|
||||
}
|
||||
if trackEndStr != "" {
|
||||
if t, err := time.Parse("2006-01-02", trackEndStr); err == nil {
|
||||
t = t.AddDate(0, 0, 1)
|
||||
trackEndDate = &t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trackLimitStr := r.URL.Query().Get("track_limit")
|
||||
trackLimit := 10
|
||||
if trackLimitStr != "" {
|
||||
trackLimit, err = strconv.Atoi(trackLimitStr)
|
||||
if err != nil || trackLimit < 5 {
|
||||
trackLimit = 10
|
||||
}
|
||||
if trackLimit > 30 {
|
||||
trackLimit = 30
|
||||
}
|
||||
}
|
||||
|
||||
profileData.TopTracksPeriod = trackPeriod
|
||||
profileData.TopTracksLimit = trackLimit
|
||||
|
||||
topTracks, err := db.GetTopTracks(userId, trackLimit, trackStartDate, trackEndDate)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Cannot get top tracks: %v\n", err)
|
||||
} else {
|
||||
profileData.TopTracks = topTracks
|
||||
}
|
||||
|
||||
if pageInt == 1 {
|
||||
if np, ok := scrobble.GetNowPlaying(userId); ok {
|
||||
profileData.NowPlayingArtist = np.Artist
|
||||
|
||||
20
web/utils.go
20
web/utils.go
@@ -43,6 +43,26 @@ func slice(a []db.TopArtist, start int, end int) []db.TopArtist {
|
||||
return a[start:end]
|
||||
}
|
||||
|
||||
func sliceAlbum(a []db.TopAlbum, start int, end int) []db.TopAlbum {
|
||||
if start >= len(a) {
|
||||
return []db.TopAlbum{}
|
||||
}
|
||||
if end > len(a) {
|
||||
end = len(a)
|
||||
}
|
||||
return a[start:end]
|
||||
}
|
||||
|
||||
func sliceTrack(a []db.TopTrack, start int, end int) []db.TopTrack {
|
||||
if start >= len(a) {
|
||||
return []db.TopTrack{}
|
||||
}
|
||||
if end > len(a) {
|
||||
end = len(a)
|
||||
}
|
||||
return a[start:end]
|
||||
}
|
||||
|
||||
func gridReorder(artists []db.TopArtist) []db.TopArtist {
|
||||
if len(artists) < 2 {
|
||||
return artists
|
||||
|
||||
@@ -36,6 +36,8 @@ func init() {
|
||||
"div": div,
|
||||
"mod": mod,
|
||||
"slice": slice,
|
||||
"sliceAlbum": sliceAlbum,
|
||||
"sliceTrack": sliceTrack,
|
||||
"gridReorder": gridReorder,
|
||||
"formatInt": formatInt,
|
||||
"formatTimestamp": formatTimestamp,
|
||||
|
||||
Reference in New Issue
Block a user