From 68a9be5e86ae8fb02a8258c9138ddc6d5a1534a0 Mon Sep 17 00:00:00 2001 From: Deluan Date: Mon, 12 Oct 2020 21:21:28 -0400 Subject: [PATCH] Add Artist (discography) size, and show sizes in Download caption --- ...20201012210022_add_artist_playlist_size.go | 41 +++++++++++++++++++ model/artist.go | 1 + persistence/artist_repository.go | 2 +- ui/src/album/AlbumActions.js | 8 +++- ui/src/common/ContextMenus.js | 17 ++++---- ui/src/common/SizeField.js | 2 +- ui/src/common/SongContextMenu.js | 15 ++++--- 7 files changed, 70 insertions(+), 16 deletions(-) create mode 100644 db/migration/20201012210022_add_artist_playlist_size.go diff --git a/db/migration/20201012210022_add_artist_playlist_size.go b/db/migration/20201012210022_add_artist_playlist_size.go new file mode 100644 index 000000000..5dd67609d --- /dev/null +++ b/db/migration/20201012210022_add_artist_playlist_size.go @@ -0,0 +1,41 @@ +package migration + +import ( + "database/sql" + + "github.com/pressly/goose" +) + +func init() { + goose.AddMigration(Up20201012210022, Down20201012210022) +} + +func Up20201012210022(tx *sql.Tx) error { + _, err := tx.Exec(` +alter table artist + add size integer default 0 not null; +create index if not exists artist_size + on artist(size); + +alter table playlist + add size integer default 0 not null; +create index if not exists playlist_size + on playlist(size); + +update playlist set size = ifnull(( + select sum(size) + from media_file f + left join playlist_tracks pt on f.id = pt.media_file_id + where pt.playlist_id = playlist.id +), 0);`) + + if err != nil { + return err + } + notice(tx, "A full rescan will be performed to calculate artists (discographies) and playlists sizes.") + return forceFullRescan(tx) +} + +func Down20201012210022(tx *sql.Tx) error { + return nil +} diff --git a/model/artist.go b/model/artist.go index 4bbcce1a1..c99232280 100644 --- a/model/artist.go +++ b/model/artist.go @@ -10,6 +10,7 @@ type Artist struct { FullText string `json:"fullText"` SortArtistName string `json:"sortArtistName"` OrderArtistName string `json:"orderArtistName"` + Size int64 `json:"size"` } type Artists []Artist diff --git a/persistence/artist_repository.go b/persistence/artist_repository.go index bd28b89e2..cd68a191b 100644 --- a/persistence/artist_repository.go +++ b/persistence/artist_repository.go @@ -133,7 +133,7 @@ func (r *artistRepository) refresh(ids ...string) error { var artists []refreshArtist sel := Select("f.album_artist_id as id", "f.album_artist as name", "count(*) as album_count", "a.id as current_id", "f.sort_album_artist_name as sort_artist_name", "f.order_album_artist_name as order_artist_name", - "sum(f.song_count) as song_count"). + "sum(f.song_count) as song_count", "sum(f.size) as size"). From("album f"). LeftJoin("artist a on f.album_artist_id = a.id"). Where(Eq{"f.album_artist_id": ids}). diff --git a/ui/src/album/AlbumActions.js b/ui/src/album/AlbumActions.js index b9547da90..62a8f5359 100644 --- a/ui/src/album/AlbumActions.js +++ b/ui/src/album/AlbumActions.js @@ -13,10 +13,13 @@ import CloudDownloadOutlinedIcon from '@material-ui/icons/CloudDownloadOutlined' import { RiPlayListAddFill, RiPlayList2Fill } from 'react-icons/ri' import { playNext, addTracks, playTracks, shuffleTracks } from '../audioplayer' import subsonic from '../subsonic' +import { formatBytes } from '../common/SizeField' +import { useMediaQuery } from '@material-ui/core' const AlbumActions = ({ className, ids, data, record, ...rest }) => { const dispatch = useDispatch() const translate = useTranslate() + const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md')) const handlePlay = React.useCallback(() => { dispatch(playTracks(data, ids)) @@ -66,7 +69,10 @@ const AlbumActions = ({ className, ids, data, record, ...rest }) => { diff --git a/ui/src/common/ContextMenus.js b/ui/src/common/ContextMenus.js index d0a31d062..9ee7bdbca 100644 --- a/ui/src/common/ContextMenus.js +++ b/ui/src/common/ContextMenus.js @@ -11,6 +11,7 @@ import { playNext, addTracks, playTracks, shuffleTracks } from '../audioplayer' import { openAddToPlaylist } from '../dialogs/dialogState' import subsonic from '../subsonic' import StarButton from './StarButton' +import { formatBytes } from './SizeField' const useStyles = makeStyles({ noWrap: { @@ -40,32 +41,34 @@ const ContextMenu = ({ const options = { play: { needData: true, - label: 'resources.album.actions.playAll', + label: translate('resources.album.actions.playAll'), action: (data, ids) => dispatch(playTracks(data, ids)), }, playNext: { needData: true, - label: 'resources.album.actions.playNext', + label: translate('resources.album.actions.playNext'), action: (data, ids) => dispatch(playNext(data, ids)), }, addToQueue: { needData: true, - label: 'resources.album.actions.addToQueue', + label: translate('resources.album.actions.addToQueue'), action: (data, ids) => dispatch(addTracks(data, ids)), }, shuffle: { needData: true, - label: 'resources.album.actions.shuffle', + label: translate('resources.album.actions.shuffle'), action: (data, ids) => dispatch(shuffleTracks(data, ids)), }, addToPlaylist: { needData: true, - label: 'resources.album.actions.addToPlaylist', + label: translate('resources.album.actions.addToPlaylist'), action: (data, ids) => dispatch(openAddToPlaylist({ selectedIds: ids })), }, download: { needData: false, - label: 'resources.album.actions.download', + label: `${translate('resources.album.actions.download')} (${formatBytes( + record.size + )})`, action: () => subsonic.download(record.id), }, } @@ -140,7 +143,7 @@ const ContextMenu = ({ > {Object.keys(options).map((key) => ( - {translate(options[key].label)} + {options[key].label} ))} diff --git a/ui/src/common/SizeField.js b/ui/src/common/SizeField.js index c288a5ff3..e1159b196 100644 --- a/ui/src/common/SizeField.js +++ b/ui/src/common/SizeField.js @@ -5,7 +5,7 @@ const SizeField = ({ record = {}, source }) => { return {formatBytes(record[source])} } -function formatBytes(bytes, decimals = 2) { +export const formatBytes = (bytes, decimals = 2) => { if (bytes === 0) return '0 Bytes' const k = 1024 diff --git a/ui/src/common/SongContextMenu.js b/ui/src/common/SongContextMenu.js index 35b18894e..5df5f6d86 100644 --- a/ui/src/common/SongContextMenu.js +++ b/ui/src/common/SongContextMenu.js @@ -9,6 +9,7 @@ import { playNext, addTracks, setTrack } from '../audioplayer' import { openAddToPlaylist } from '../dialogs/dialogState' import subsonic from '../subsonic' import StarButton from './StarButton' +import { formatBytes } from './SizeField' const useStyles = makeStyles({ noWrap: { @@ -32,19 +33,19 @@ const SongContextMenu = ({ const [anchorEl, setAnchorEl] = useState(null) const options = { playNow: { - label: 'resources.song.actions.playNow', + label: translate('resources.song.actions.playNow'), action: (record) => dispatch(setTrack(record)), }, playNext: { - label: 'resources.song.actions.playNext', + label: translate('resources.song.actions.playNext'), action: (record) => dispatch(playNext({ [record.id]: record })), }, addToQueue: { - label: 'resources.song.actions.addToQueue', + label: translate('resources.song.actions.addToQueue'), action: (record) => dispatch(addTracks({ [record.id]: record })), }, addToPlaylist: { - label: 'resources.song.actions.addToPlaylist', + label: translate('resources.song.actions.addToPlaylist'), action: (record) => dispatch( openAddToPlaylist({ @@ -54,7 +55,9 @@ const SongContextMenu = ({ ), }, download: { - label: 'resources.song.actions.download', + label: `${translate('resources.song.actions.download')} (${formatBytes( + record.size + )})`, action: (record) => subsonic.download(record.id), }, } @@ -95,7 +98,7 @@ const SongContextMenu = ({ > {Object.keys(options).map((key) => ( - {translate(options[key].label)} + {options[key].label} ))}