mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2025-09-17 17:14:18 +02:00
Fixed: Backend/Frontend Cleanup
This commit is contained in:
@@ -163,7 +163,7 @@ function HistoryDetails(props) {
|
||||
);
|
||||
}
|
||||
|
||||
if (eventType === 'episodeFileDeleted') {
|
||||
if (eventType === 'movieFileDeleted') {
|
||||
const {
|
||||
reason
|
||||
} = data;
|
||||
@@ -199,7 +199,7 @@ function HistoryDetails(props) {
|
||||
);
|
||||
}
|
||||
|
||||
if (eventType === 'episodeFileRenamed') {
|
||||
if (eventType === 'movieFileRenamed') {
|
||||
const {
|
||||
sourcePath,
|
||||
sourceRelativePath,
|
||||
|
@@ -18,11 +18,11 @@ function getHeaderTitle(eventType) {
|
||||
case 'downloadFailed':
|
||||
return 'Download Failed';
|
||||
case 'downloadFolderImported':
|
||||
return 'Episode Imported';
|
||||
case 'episodeFileDeleted':
|
||||
return 'Episode File Deleted';
|
||||
case 'episodeFileRenamed':
|
||||
return 'Episode File Renamed';
|
||||
return 'Movie Imported';
|
||||
case 'movieFileDeleted':
|
||||
return 'Movie File Deleted';
|
||||
case 'movieFileRenamed':
|
||||
return 'Movie File Renamed';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
|
@@ -71,6 +71,7 @@ class QueueRow extends Component {
|
||||
quality,
|
||||
protocol,
|
||||
indexer,
|
||||
outputPath,
|
||||
downloadClient,
|
||||
estimatedCompletionTime,
|
||||
timeleft,
|
||||
@@ -195,6 +196,14 @@ class QueueRow extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'outputPath') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{outputPath}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'estimatedCompletionTime') {
|
||||
return (
|
||||
<TimeleftCell
|
||||
@@ -297,6 +306,7 @@ QueueRow.propTypes = {
|
||||
quality: PropTypes.object.isRequired,
|
||||
protocol: PropTypes.string.isRequired,
|
||||
indexer: PropTypes.string,
|
||||
outputPath: PropTypes.string,
|
||||
downloadClient: PropTypes.string,
|
||||
estimatedCompletionTime: PropTypes.string,
|
||||
timeleft: PropTypes.string,
|
||||
|
@@ -12,8 +12,12 @@ function createMapStateToProps() {
|
||||
(state) => state.queue.options.includeUnknownMovieItems,
|
||||
(app, status, includeUnknownMovieItems) => {
|
||||
const {
|
||||
errors,
|
||||
warnings,
|
||||
unknownErrors,
|
||||
unknownWarnings,
|
||||
count,
|
||||
unknownCount
|
||||
totalCount
|
||||
} = status.item;
|
||||
|
||||
return {
|
||||
@@ -21,7 +25,9 @@ function createMapStateToProps() {
|
||||
isReconnecting: app.isReconnecting,
|
||||
isPopulated: status.isPopulated,
|
||||
...status.item,
|
||||
count: includeUnknownMovieItems ? count : count - unknownCount
|
||||
count: includeUnknownMovieItems ? totalCount : count,
|
||||
errors: includeUnknownMovieItems ? errors || unknownErrors : errors,
|
||||
warnings: includeUnknownMovieItems ? warnings || unknownWarnings : warnings
|
||||
};
|
||||
}
|
||||
);
|
||||
|
@@ -13,10 +13,10 @@ function createMapStateToProps() {
|
||||
createMovieFileSelector(),
|
||||
createQueueItemSelector(),
|
||||
createUISettingsSelector(),
|
||||
(calendarOptions, series, episodeFile, queueItem, uiSettings) => {
|
||||
(calendarOptions, movie, movieFile, queueItem, uiSettings) => {
|
||||
return {
|
||||
series,
|
||||
episodeFile,
|
||||
movie,
|
||||
movieFile,
|
||||
queueItem,
|
||||
...calendarOptions,
|
||||
timeFormat: uiSettings.timeFormat,
|
||||
|
@@ -6,11 +6,11 @@ import CalendarEventGroup from './CalendarEventGroup';
|
||||
|
||||
function createIsDownloadingSelector() {
|
||||
return createSelector(
|
||||
(state, { episodeIds }) => episodeIds,
|
||||
(state, { movieIds }) => movieIds,
|
||||
(state) => state.queue.details,
|
||||
(episodeIds, details) => {
|
||||
(movieIds, details) => {
|
||||
return details.items.some((item) => {
|
||||
return episodeIds.includes(item.episode.id);
|
||||
return item.movie && movieIds.includes(item.movie.id);
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -22,9 +22,9 @@ function createMapStateToProps() {
|
||||
createMovieSelector(),
|
||||
createIsDownloadingSelector(),
|
||||
createUISettingsSelector(),
|
||||
(calendarOptions, series, isDownloading, uiSettings) => {
|
||||
(calendarOptions, movie, isDownloading, uiSettings) => {
|
||||
return {
|
||||
series,
|
||||
movie,
|
||||
isDownloading,
|
||||
...calendarOptions,
|
||||
timeFormat: uiSettings.timeFormat,
|
||||
|
@@ -83,6 +83,7 @@ class CalendarHeader extends Component {
|
||||
end,
|
||||
longDateFormat,
|
||||
isSmallScreen,
|
||||
collapseViewButtons,
|
||||
onTodayPress,
|
||||
onPreviousPress,
|
||||
onNextPress
|
||||
@@ -145,7 +146,7 @@ class CalendarHeader extends Component {
|
||||
}
|
||||
|
||||
{
|
||||
isSmallScreen ?
|
||||
collapseViewButtons ?
|
||||
<Menu
|
||||
className={styles.viewMenu}
|
||||
alignMenu={align.RIGHT}
|
||||
@@ -158,6 +159,18 @@ class CalendarHeader extends Component {
|
||||
</MenuButton>
|
||||
|
||||
<MenuContent>
|
||||
{
|
||||
isSmallScreen ?
|
||||
null :
|
||||
<ViewMenuItem
|
||||
name={calendarViews.MONTH}
|
||||
selectedView={view}
|
||||
onPress={this.onViewChange}
|
||||
>
|
||||
Month
|
||||
</ViewMenuItem>
|
||||
}
|
||||
|
||||
<ViewMenuItem
|
||||
name={calendarViews.WEEK}
|
||||
selectedView={view}
|
||||
@@ -243,6 +256,7 @@ CalendarHeader.propTypes = {
|
||||
end: PropTypes.string.isRequired,
|
||||
view: PropTypes.oneOf(calendarViews.all).isRequired,
|
||||
isSmallScreen: PropTypes.bool.isRequired,
|
||||
collapseViewButtons: PropTypes.bool.isRequired,
|
||||
longDateFormat: PropTypes.string.isRequired,
|
||||
onViewChange: PropTypes.func.isRequired,
|
||||
onTodayPress: PropTypes.func.isRequired,
|
||||
|
@@ -23,6 +23,7 @@ function createMapStateToProps() {
|
||||
]);
|
||||
|
||||
result.isSmallScreen = dimensions.isSmallScreen;
|
||||
result.collapseViewButtons = dimensions.isLargeScreen;
|
||||
result.longDateFormat = uiSettings.longDateFormat;
|
||||
|
||||
return result;
|
||||
|
@@ -78,8 +78,8 @@
|
||||
color: $disabledColor;
|
||||
}
|
||||
|
||||
.addNewSeriesSuggestion {
|
||||
padding: 0 3px;
|
||||
.addNewMovieSuggestion {
|
||||
padding: 5px 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
@@ -76,7 +76,7 @@ class MovieSearchInput extends Component {
|
||||
renderSuggestion(item, { query }) {
|
||||
if (item.type === ADD_NEW_TYPE) {
|
||||
return (
|
||||
<div className={styles.addNewSeriesSuggestion}>
|
||||
<div className={styles.addNewMovieSuggestion}>
|
||||
Search for {query}
|
||||
</div>
|
||||
);
|
||||
|
@@ -1,10 +1,40 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createDeepEqualSelector from 'Store/Selectors/createDeepEqualSelector';
|
||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||
import MovieIndexFooter from './MovieIndexFooter';
|
||||
|
||||
function createUnoptimizedSelector() {
|
||||
return createSelector(
|
||||
createClientSideCollectionSelector('movies', 'movieIndex'),
|
||||
(movies) => {
|
||||
return movies.items.map((s) => {
|
||||
const {
|
||||
monitored,
|
||||
status,
|
||||
statistics
|
||||
} = s;
|
||||
|
||||
return {
|
||||
monitored,
|
||||
status,
|
||||
statistics
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function createMoviesSelector() {
|
||||
return createDeepEqualSelector(
|
||||
createUnoptimizedSelector(),
|
||||
(movies) => movies
|
||||
);
|
||||
}
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.movies.items,
|
||||
createMoviesSelector(),
|
||||
(movies) => {
|
||||
return {
|
||||
movies
|
||||
|
@@ -32,7 +32,7 @@ function BackupSettings(props) {
|
||||
<FormLabel>Folder</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
type={inputTypes.PATH}
|
||||
name="backupFolder"
|
||||
helpText="Relative paths will be under Radarr's AppData directory"
|
||||
onChange={onInputChange}
|
||||
|
@@ -83,10 +83,6 @@ class NamingModal extends Component {
|
||||
value,
|
||||
isOpen,
|
||||
advancedSettings,
|
||||
season,
|
||||
episode,
|
||||
daily,
|
||||
anime,
|
||||
additional,
|
||||
onInputChange,
|
||||
onModalClose
|
||||
@@ -112,58 +108,21 @@ class NamingModal extends Component {
|
||||
|
||||
const fileNameTokens = [
|
||||
{
|
||||
token: '{Series Title} - S{season:00}E{episode:00} - {Episode Title} {Quality Full}',
|
||||
example: 'Series Title (2010) - S01E01 - Episode Title HDTV-720p Proper'
|
||||
},
|
||||
{
|
||||
token: '{Series Title} - {season:0}x{episode:00} - {Episode Title} {Quality Full}',
|
||||
example: 'Series Title (2010) - 1x01 - Episode Title HDTV-720p Proper'
|
||||
},
|
||||
{
|
||||
token: '{Series.Title}.S{season:00}E{episode:00}.{EpisodeClean.Title}.{Quality.Full}',
|
||||
example: 'Series.Title.(2010).S01E01.Episode.Title.HDTV-720p'
|
||||
token: '{Movie Title} - {Quality Full}',
|
||||
example: 'Movie Title (2010) - HDTV-720p Proper'
|
||||
}
|
||||
];
|
||||
|
||||
const seriesTokens = [
|
||||
{ token: '{Series Title}', example: 'Series Title!' },
|
||||
{ token: '{Series CleanTitle}', example: 'Series Title' },
|
||||
{ token: '{Series CleanTitleYear}', example: 'Series Title 2010' },
|
||||
{ token: '{Series TitleThe}', example: 'Series Title, The' },
|
||||
{ token: '{Series TitleTheYear}', example: 'Series Title, The (2010)' },
|
||||
{ token: '{Series TitleYear}', example: 'Series Title (2010)' }
|
||||
const movieTokens = [
|
||||
{ token: '{Movie Title}', example: 'Movie Title!' },
|
||||
{ token: '{Movie CleanTitle}', example: 'Movie Title' },
|
||||
{ token: '{Movie TitleThe}', example: 'Movie Title, The' }
|
||||
|
||||
];
|
||||
|
||||
const seriesIdTokens = [
|
||||
const movieIdTokens = [
|
||||
{ token: '{ImdbId}', example: 'tt12345' },
|
||||
{ token: '{TvdbId}', example: '12345' },
|
||||
{ token: '{TvMazeId}', example: '54321' }
|
||||
];
|
||||
|
||||
const seasonTokens = [
|
||||
{ token: '{season:0}', example: '1' },
|
||||
{ token: '{season:00}', example: '01' }
|
||||
];
|
||||
|
||||
const episodeTokens = [
|
||||
{ token: '{episode:0}', example: '1' },
|
||||
{ token: '{episode:00}', example: '01' }
|
||||
];
|
||||
|
||||
const airDateTokens = [
|
||||
{ token: '{Air-Date}', example: '2016-03-20' },
|
||||
{ token: '{Air Date}', example: '2016 03 20' }
|
||||
];
|
||||
|
||||
const absoluteTokens = [
|
||||
{ token: '{absolute:0}', example: '1' },
|
||||
{ token: '{absolute:00}', example: '01' },
|
||||
{ token: '{absolute:000}', example: '001' }
|
||||
];
|
||||
|
||||
const episodeTitleTokens = [
|
||||
{ token: '{Episode Title}', example: 'Episode Title' },
|
||||
{ token: '{Episode CleanTitle}', example: 'Episode Title' }
|
||||
{ token: '{TmdbId}', example: '123456' }
|
||||
];
|
||||
|
||||
const qualityTokens = [
|
||||
@@ -175,8 +134,14 @@ class NamingModal extends Component {
|
||||
{ token: '{MediaInfo Simple}', example: 'x264 DTS' },
|
||||
{ token: '{MediaInfo Full}', example: 'x264 DTS [EN+DE]' },
|
||||
{ token: '{MediaInfo VideoCodec}', example: 'x264' },
|
||||
{ token: '{MediaInfo AudioFormat}', example: 'DTS' },
|
||||
{ token: '{MediaInfo AudioChannels}', example: '5.1' }
|
||||
{ token: '{MediaInfo AudioCodec}', example: 'DTS' },
|
||||
{ token: '{MediaInfo AudioChannels}', example: '5.1' },
|
||||
{ token: '{MediaInfo AudioLanguages}', example: '[EN+DE]' },
|
||||
{ token: '{MediaInfo SubtitleLanguages}', example: '[DE]' },
|
||||
|
||||
{ token: '{MediaInfo VideoCodec}', example: 'x264' },
|
||||
{ token: '{MediaInfo VideoBitDepth}', example: '10' },
|
||||
{ token: '{MediaInfo VideoDynamicRange}', example: 'HDR' }
|
||||
];
|
||||
|
||||
const releaseGroupTokens = [
|
||||
@@ -184,8 +149,8 @@ class NamingModal extends Component {
|
||||
];
|
||||
|
||||
const originalTokens = [
|
||||
{ token: '{Original Title}', example: 'Series.Title.S01E01.HDTV.x264-EVOLVE' },
|
||||
{ token: '{Original Filename}', example: 'series.title.s01e01.hdtv.x264-EVOLVE' }
|
||||
{ token: '{Original Title}', example: 'Movie.Title.HDTV.x264-EVOLVE' },
|
||||
{ token: '{Original Filename}', example: 'Movie.title.hdtv.x264-EVOLVE' }
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -244,10 +209,10 @@ class NamingModal extends Component {
|
||||
</FieldSet>
|
||||
}
|
||||
|
||||
<FieldSet legend="Series">
|
||||
<FieldSet legend="Movie">
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
seriesTokens.map(({ token, example }) => {
|
||||
movieTokens.map(({ token, example }) => {
|
||||
return (
|
||||
<NamingOption
|
||||
key={token}
|
||||
@@ -266,10 +231,10 @@ class NamingModal extends Component {
|
||||
</div>
|
||||
</FieldSet>
|
||||
|
||||
<FieldSet legend="Series ID">
|
||||
<FieldSet legend="Movie ID">
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
seriesIdTokens.map(({ token, example }) => {
|
||||
movieIdTokens.map(({ token, example }) => {
|
||||
return (
|
||||
<NamingOption
|
||||
key={token}
|
||||
@@ -288,133 +253,9 @@ class NamingModal extends Component {
|
||||
</div>
|
||||
</FieldSet>
|
||||
|
||||
{
|
||||
season &&
|
||||
<FieldSet legend="Season">
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
seasonTokens.map(({ token, example }) => {
|
||||
return (
|
||||
<NamingOption
|
||||
key={token}
|
||||
name={name}
|
||||
value={value}
|
||||
token={token}
|
||||
example={example}
|
||||
tokenSeparator={tokenSeparator}
|
||||
tokenCase={tokenCase}
|
||||
onPress={this.onOptionPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</FieldSet>
|
||||
}
|
||||
|
||||
{
|
||||
episode &&
|
||||
<div>
|
||||
<FieldSet legend="Episode">
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
episodeTokens.map(({ token, example }) => {
|
||||
return (
|
||||
<NamingOption
|
||||
key={token}
|
||||
name={name}
|
||||
value={value}
|
||||
token={token}
|
||||
example={example}
|
||||
tokenSeparator={tokenSeparator}
|
||||
tokenCase={tokenCase}
|
||||
onPress={this.onOptionPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</FieldSet>
|
||||
|
||||
{
|
||||
daily &&
|
||||
<FieldSet legend="Air-Date">
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
airDateTokens.map(({ token, example }) => {
|
||||
return (
|
||||
<NamingOption
|
||||
key={token}
|
||||
name={name}
|
||||
value={value}
|
||||
token={token}
|
||||
example={example}
|
||||
tokenSeparator={tokenSeparator}
|
||||
tokenCase={tokenCase}
|
||||
onPress={this.onOptionPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</FieldSet>
|
||||
}
|
||||
|
||||
{
|
||||
anime &&
|
||||
<FieldSet legend="Absolute Episode Number">
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
absoluteTokens.map(({ token, example }) => {
|
||||
return (
|
||||
<NamingOption
|
||||
key={token}
|
||||
name={name}
|
||||
value={value}
|
||||
token={token}
|
||||
example={example}
|
||||
tokenSeparator={tokenSeparator}
|
||||
tokenCase={tokenCase}
|
||||
onPress={this.onOptionPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</FieldSet>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
additional &&
|
||||
<div>
|
||||
<FieldSet legend="Episode Title">
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
episodeTitleTokens.map(({ token, example }) => {
|
||||
return (
|
||||
<NamingOption
|
||||
key={token}
|
||||
name={name}
|
||||
value={value}
|
||||
token={token}
|
||||
example={example}
|
||||
tokenSeparator={tokenSeparator}
|
||||
tokenCase={tokenCase}
|
||||
onPress={this.onOptionPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</FieldSet>
|
||||
|
||||
<FieldSet legend="Quality">
|
||||
<div className={styles.groups}>
|
||||
{
|
||||
@@ -529,20 +370,12 @@ NamingModal.propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
advancedSettings: PropTypes.bool.isRequired,
|
||||
season: PropTypes.bool.isRequired,
|
||||
episode: PropTypes.bool.isRequired,
|
||||
daily: PropTypes.bool.isRequired,
|
||||
anime: PropTypes.bool.isRequired,
|
||||
additional: PropTypes.bool.isRequired,
|
||||
onInputChange: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
NamingModal.defaultProps = {
|
||||
season: false,
|
||||
episode: false,
|
||||
daily: false,
|
||||
anime: false,
|
||||
additional: false
|
||||
};
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
.option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: stretch;
|
||||
flex-wrap: wrap;
|
||||
margin: 3px;
|
||||
border: 1px solid $borderColor;
|
||||
@@ -17,7 +17,7 @@
|
||||
}
|
||||
|
||||
.small {
|
||||
width: 420px;
|
||||
width: 480px;
|
||||
}
|
||||
|
||||
.large {
|
||||
@@ -32,6 +32,9 @@
|
||||
}
|
||||
|
||||
.example {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
flex: 0 0 50%;
|
||||
padding: 6px 16px;
|
||||
background-color: #ddd;
|
||||
|
@@ -84,7 +84,7 @@ class Notification extends Component {
|
||||
{
|
||||
supportsOnDownload && onDownload &&
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
On Download
|
||||
On Import
|
||||
</Label>
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,11 @@ class MoreInfo extends Component {
|
||||
<Link to="https://radarr.video/">radarr.video</Link>
|
||||
</DescriptionListItemDescription>
|
||||
|
||||
<DescriptionListItemTitle>Discord</DescriptionListItemTitle>
|
||||
<DescriptionListItemDescription>
|
||||
<Link to="https://discord.gg/AD3UP37">discord.gg/AD3UP37</Link>
|
||||
</DescriptionListItemDescription>
|
||||
|
||||
<DescriptionListItemTitle>Wiki</DescriptionListItemTitle>
|
||||
<DescriptionListItemDescription>
|
||||
<Link to="https://github.com/Radarr/Radarr/wiki">github.com/Radarr/Radarr/wiki</Link>
|
||||
|
Reference in New Issue
Block a user