mirror of https://github.com/lidarr/Lidarr
New: Show downloading status for artist progress bar
(cherry picked from commit ac806a2933bf2dc0c96d471ec143fca8e1f5282f)
This commit is contained in:
parent
f1dede240d
commit
af12fad185
|
@ -1,4 +1,10 @@
|
|||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { SelectProvider } from 'App/SelectContext';
|
||||
import NoArtist from 'Artist/NoArtist';
|
||||
|
@ -22,6 +28,7 @@ import {
|
|||
setArtistView,
|
||||
} from 'Store/Actions/artistIndexActions';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import { fetchQueueDetails } from 'Store/Actions/queueActions';
|
||||
import scrollPositions from 'Store/scrollPositions';
|
||||
import createArtistClientSideCollectionItemsSelector from 'Store/Selectors/createArtistClientSideCollectionItemsSelector';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
|
@ -48,7 +55,7 @@ import ArtistIndexTable from './Table/ArtistIndexTable';
|
|||
import ArtistIndexTableOptions from './Table/ArtistIndexTableOptions';
|
||||
import styles from './ArtistIndex.css';
|
||||
|
||||
function getViewComponent(view) {
|
||||
function getViewComponent(view: string) {
|
||||
if (view === 'posters') {
|
||||
return ArtistIndexPosters;
|
||||
}
|
||||
|
@ -94,6 +101,10 @@ const ArtistIndex = withScrollPosition((props: ArtistIndexProps) => {
|
|||
const [jumpToCharacter, setJumpToCharacter] = useState<string | null>(null);
|
||||
const [isSelectMode, setIsSelectMode] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchQueueDetails({ all: true }));
|
||||
}, [dispatch]);
|
||||
|
||||
const onRssSyncPress = useCallback(() => {
|
||||
dispatch(
|
||||
executeCommand({
|
||||
|
|
|
@ -50,6 +50,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
.downloading {
|
||||
composes: legendItemColor;
|
||||
|
||||
background-color: var(--purple);
|
||||
}
|
||||
|
||||
.statistics {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Please do not change this file!
|
||||
interface CssExports {
|
||||
'continuing': string;
|
||||
'downloading': string;
|
||||
'ended': string;
|
||||
'footer': string;
|
||||
'legendItem': string;
|
||||
|
|
|
@ -113,6 +113,16 @@ export default function ArtistIndexFooter() {
|
|||
/>
|
||||
<div>{translate('MissingTracksArtistNotMonitored')}</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.legendItem}>
|
||||
<div
|
||||
className={classNames(
|
||||
styles.downloading,
|
||||
enableColorImpairedMode && 'colorImpaired'
|
||||
)}
|
||||
/>
|
||||
<div>{translate('ArtistIndexFooterDownloading')}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.statistics}>
|
||||
|
|
|
@ -183,13 +183,15 @@ function ArtistIndexBanner(props: ArtistIndexBannerProps) {
|
|||
</div>
|
||||
|
||||
<ArtistIndexProgressBar
|
||||
artistId={artistId}
|
||||
monitored={monitored}
|
||||
status={status}
|
||||
trackCount={trackCount}
|
||||
trackFileCount={trackFileCount}
|
||||
totalTrackCount={totalTrackCount}
|
||||
posterWidth={bannerWidth}
|
||||
width={bannerWidth}
|
||||
detailedProgressBar={detailedProgressBar}
|
||||
isStandalone={false}
|
||||
/>
|
||||
|
||||
{showTitle ? (
|
||||
|
|
|
@ -160,13 +160,15 @@ function ArtistIndexOverview(props: ArtistIndexOverviewProps) {
|
|||
</div>
|
||||
|
||||
<ArtistIndexProgressBar
|
||||
artistId={artistId}
|
||||
monitored={monitored}
|
||||
status={status}
|
||||
trackCount={trackCount}
|
||||
trackFileCount={trackFileCount}
|
||||
totalTrackCount={totalTrackCount}
|
||||
posterWidth={posterWidth}
|
||||
width={posterWidth}
|
||||
detailedProgressBar={overviewOptions.detailedProgressBar}
|
||||
isStandalone={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -183,13 +183,15 @@ function ArtistIndexPoster(props: ArtistIndexPosterProps) {
|
|||
</div>
|
||||
|
||||
<ArtistIndexProgressBar
|
||||
artistId={artistId}
|
||||
monitored={monitored}
|
||||
status={status}
|
||||
trackCount={trackCount}
|
||||
trackFileCount={trackFileCount}
|
||||
totalTrackCount={totalTrackCount}
|
||||
posterWidth={posterWidth}
|
||||
width={posterWidth}
|
||||
detailedProgressBar={detailedProgressBar}
|
||||
isStandalone={false}
|
||||
/>
|
||||
|
||||
{showTitle ? (
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import createArtistQueueItemsDetailsSelector, {
|
||||
ArtistQueueDetails,
|
||||
} from 'Artist/Index/createArtistQueueDetailsSelector';
|
||||
import ProgressBar from 'Components/ProgressBar';
|
||||
import { sizes } from 'Helpers/Props';
|
||||
import getProgressBarKind from 'Utilities/Artist/getProgressBarKind';
|
||||
|
@ -6,35 +10,51 @@ import translate from 'Utilities/String/translate';
|
|||
import styles from './ArtistIndexProgressBar.css';
|
||||
|
||||
interface ArtistIndexProgressBarProps {
|
||||
artistId: number;
|
||||
monitored: boolean;
|
||||
status: string;
|
||||
trackCount: number;
|
||||
trackFileCount: number;
|
||||
totalTrackCount: number;
|
||||
posterWidth: number;
|
||||
width: number;
|
||||
detailedProgressBar: boolean;
|
||||
isStandalone: boolean;
|
||||
}
|
||||
|
||||
function ArtistIndexProgressBar(props: ArtistIndexProgressBarProps) {
|
||||
const {
|
||||
artistId,
|
||||
monitored,
|
||||
status,
|
||||
trackCount,
|
||||
trackFileCount,
|
||||
totalTrackCount,
|
||||
posterWidth,
|
||||
width,
|
||||
detailedProgressBar,
|
||||
isStandalone,
|
||||
} = props;
|
||||
|
||||
const queueDetails: ArtistQueueDetails = useSelector(
|
||||
createArtistQueueItemsDetailsSelector(artistId)
|
||||
);
|
||||
|
||||
const newDownloads = queueDetails.count - queueDetails.tracksWithFiles;
|
||||
const progress = trackCount ? (trackFileCount / trackCount) * 100 : 100;
|
||||
const text = `${trackFileCount} / ${trackCount}`;
|
||||
const text = newDownloads
|
||||
? `${trackFileCount} + ${newDownloads} / ${trackCount}`
|
||||
: `${trackFileCount} / ${trackCount}`;
|
||||
|
||||
return (
|
||||
<ProgressBar
|
||||
className={styles.progressBar}
|
||||
containerClassName={styles.progress}
|
||||
containerClassName={isStandalone ? undefined : styles.progress}
|
||||
progress={progress}
|
||||
kind={getProgressBarKind(status, monitored, progress)}
|
||||
kind={getProgressBarKind(
|
||||
status,
|
||||
monitored,
|
||||
progress,
|
||||
queueDetails.count > 0
|
||||
)}
|
||||
size={detailedProgressBar ? sizes.MEDIUM : sizes.SMALL}
|
||||
showText={detailedProgressBar}
|
||||
text={text}
|
||||
|
@ -42,8 +62,9 @@ function ArtistIndexProgressBar(props: ArtistIndexProgressBarProps) {
|
|||
trackFileCount,
|
||||
trackCount,
|
||||
totalTrackCount,
|
||||
downloadingCount: queueDetails.count,
|
||||
})}
|
||||
width={posterWidth}
|
||||
width={width}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@ import ArtistNameLink from 'Artist/ArtistNameLink';
|
|||
import DeleteArtistModal from 'Artist/Delete/DeleteArtistModal';
|
||||
import EditArtistModalConnector from 'Artist/Edit/EditArtistModalConnector';
|
||||
import createArtistIndexItemSelector from 'Artist/Index/createArtistIndexItemSelector';
|
||||
import ArtistIndexProgressBar from 'Artist/Index/ProgressBar/ArtistIndexProgressBar';
|
||||
import ArtistStatusCell from 'Artist/Index/Table/ArtistStatusCell';
|
||||
import { ARTIST_SEARCH, REFRESH_ARTIST } from 'Commands/commandNames';
|
||||
import HeartRating from 'Components/HeartRating';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import Link from 'Components/Link/Link';
|
||||
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
||||
import ProgressBar from 'Components/ProgressBar';
|
||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
|
||||
import VirtualTableSelectCell from 'Components/Table/Cells/VirtualTableSelectCell';
|
||||
|
@ -23,7 +23,6 @@ import Column from 'Components/Table/Column';
|
|||
import TagListConnector from 'Components/TagListConnector';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import getProgressBarKind from 'Utilities/Artist/getProgressBarKind';
|
||||
import formatBytes from 'Utilities/Number/formatBytes';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AlbumsCell from './AlbumsCell';
|
||||
|
@ -301,23 +300,18 @@ function ArtistIndexRow(props: ArtistIndexRowProps) {
|
|||
}
|
||||
|
||||
if (name === 'trackProgress') {
|
||||
const progress = trackCount
|
||||
? (trackFileCount / trackCount) * 100
|
||||
: 100;
|
||||
|
||||
return (
|
||||
<VirtualTableRowCell key={name} className={styles[name]}>
|
||||
<ProgressBar
|
||||
progress={progress}
|
||||
kind={getProgressBarKind(status, monitored, progress)}
|
||||
showText={true}
|
||||
text={`${trackFileCount} / ${trackCount}`}
|
||||
title={translate('ArtistProgressBarText', {
|
||||
trackFileCount,
|
||||
trackCount,
|
||||
totalTrackCount,
|
||||
})}
|
||||
<ArtistIndexProgressBar
|
||||
artistId={artistId}
|
||||
monitored={monitored}
|
||||
status={status}
|
||||
trackCount={trackCount}
|
||||
trackFileCount={trackFileCount}
|
||||
totalTrackCount={totalTrackCount}
|
||||
width={125}
|
||||
detailedProgressBar={true}
|
||||
isStandalone={true}
|
||||
/>
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import { createSelector } from 'reselect';
|
||||
import AppState from 'App/State/AppState';
|
||||
|
||||
export interface ArtistQueueDetails {
|
||||
count: number;
|
||||
tracksWithFiles: number;
|
||||
}
|
||||
|
||||
function createArtistQueueDetailsSelector(artistId: number) {
|
||||
return createSelector(
|
||||
(state: AppState) => state.queue.details.items,
|
||||
(queueItems) => {
|
||||
return queueItems.reduce(
|
||||
(acc: ArtistQueueDetails, item) => {
|
||||
if (item.artistId !== artistId) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
acc.count += item.trackFileCount ?? 0;
|
||||
|
||||
if (item.trackHasFileCount) {
|
||||
acc.tracksWithFiles += item.trackHasFileCount;
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
count: 0,
|
||||
tracksWithFiles: 0,
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createArtistQueueDetailsSelector;
|
|
@ -1,6 +1,15 @@
|
|||
import { kinds } from 'Helpers/Props';
|
||||
|
||||
function getProgressBarKind(status, monitored, progress) {
|
||||
function getProgressBarKind(
|
||||
status: string,
|
||||
monitored: boolean,
|
||||
progress: number,
|
||||
isDownloading: boolean
|
||||
) {
|
||||
if (isDownloading) {
|
||||
return kinds.PURPLE;
|
||||
}
|
||||
|
||||
if (progress === 100) {
|
||||
return status === 'ended' ? kinds.SUCCESS : kinds.PRIMARY;
|
||||
}
|
|
@ -25,6 +25,8 @@ interface Queue extends ModelBase {
|
|||
protocol: string;
|
||||
downloadClient: string;
|
||||
outputPath: string;
|
||||
trackFileCount: number;
|
||||
trackHasFileCount: number;
|
||||
artistId?: number;
|
||||
albumId?: number;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,8 @@ namespace Lidarr.Api.V1.Queue
|
|||
public bool DownloadClientHasPostImportCategory { get; set; }
|
||||
public string Indexer { get; set; }
|
||||
public string OutputPath { get; set; }
|
||||
public int TrackFileCount { get; set; }
|
||||
public int TrackHasFileCount { get; set; }
|
||||
public bool DownloadForced { get; set; }
|
||||
}
|
||||
|
||||
|
@ -53,6 +55,8 @@ namespace Lidarr.Api.V1.Queue
|
|||
var customFormats = model.RemoteAlbum?.CustomFormats;
|
||||
var customFormatScore = model.Artist?.QualityProfile?.Value?.CalculateCustomFormatScore(customFormats) ?? 0;
|
||||
|
||||
var albumRelease = model.Album?.AlbumReleases?.Value?.SingleOrDefault(x => x.Monitored);
|
||||
|
||||
return new QueueResource
|
||||
{
|
||||
Id = model.Id,
|
||||
|
@ -80,6 +84,8 @@ namespace Lidarr.Api.V1.Queue
|
|||
DownloadClientHasPostImportCategory = model.DownloadClientHasPostImportCategory,
|
||||
Indexer = model.Indexer,
|
||||
OutputPath = model.OutputPath,
|
||||
TrackFileCount = albumRelease?.Tracks?.Value?.Count ?? 0,
|
||||
TrackHasFileCount = albumRelease?.Tracks?.Value?.Count(x => x.HasFile) ?? 0,
|
||||
DownloadForced = model.DownloadForced
|
||||
};
|
||||
}
|
||||
|
|
|
@ -111,12 +111,13 @@
|
|||
"ArtistClickToChangeAlbum": "Click to change album",
|
||||
"ArtistEditor": "Artist Editor",
|
||||
"ArtistFolderFormat": "Artist Folder Format",
|
||||
"ArtistIndexFooterDownloading": "Downloading",
|
||||
"ArtistIsMonitored": "Artist is monitored",
|
||||
"ArtistIsUnmonitored": "Artist is unmonitored",
|
||||
"ArtistMonitoring": "Artist Monitoring",
|
||||
"ArtistName": "Artist Name",
|
||||
"ArtistNameHelpText": "The name of the artist/album to exclude (can be anything meaningful)",
|
||||
"ArtistProgressBarText": "{trackFileCount} / {trackCount} (Total: {totalTrackCount})",
|
||||
"ArtistProgressBarText": "{trackFileCount} / {trackCount} (Total: {totalTrackCount}, Downloading: {downloadingCount})",
|
||||
"ArtistType": "Artist Type",
|
||||
"Artists": "Artists",
|
||||
"ArtistsEditRootFolderHelpText": "Moving artists to the same root folder can be used to rename artist folders to match updated name or naming format",
|
||||
|
|
Loading…
Reference in New Issue