mirror of
https://github.com/Readarr/Readarr
synced 2025-12-25 09:47:14 +01:00
New: Allow major version updates to be installed
(cherry picked from commit 0e95ba2021b23cc65bce0a0620dd48e355250dab)
This commit is contained in:
parent
cfccb4f9c3
commit
e7d7bc79f4
50 changed files with 545 additions and 346 deletions
|
|
@ -32,7 +32,7 @@ import LogsTableConnector from 'System/Events/LogsTableConnector';
|
|||
import Logs from 'System/Logs/Logs';
|
||||
import Status from 'System/Status/Status';
|
||||
import Tasks from 'System/Tasks/Tasks';
|
||||
import UpdatesConnector from 'System/Updates/UpdatesConnector';
|
||||
import Updates from 'System/Updates/Updates';
|
||||
import UnmappedFilesTableConnector from 'UnmappedFiles/UnmappedFilesTableConnector';
|
||||
import getPathWithUrlBase from 'Utilities/getPathWithUrlBase';
|
||||
import CutoffUnmetConnector from 'Wanted/CutoffUnmet/CutoffUnmetConnector';
|
||||
|
|
@ -247,7 +247,7 @@ function AppRoutes(props) {
|
|||
|
||||
<Route
|
||||
path="/system/updates"
|
||||
component={UpdatesConnector}
|
||||
component={Updates}
|
||||
/>
|
||||
|
||||
<Route
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import AuthorsAppState from './AuthorsAppState';
|
||||
import CommandAppState from './CommandAppState';
|
||||
import SettingsAppState from './SettingsAppState';
|
||||
import SystemAppState from './SystemAppState';
|
||||
import TagsAppState from './TagsAppState';
|
||||
|
||||
interface FilterBuilderPropOption {
|
||||
|
|
@ -35,10 +36,24 @@ export interface CustomFilter {
|
|||
filers: PropertyFilter[];
|
||||
}
|
||||
|
||||
export interface AppSectionState {
|
||||
isConnected: boolean;
|
||||
isReconnecting: boolean;
|
||||
version: string;
|
||||
prevVersion?: string;
|
||||
dimensions: {
|
||||
isSmallScreen: boolean;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface AppState {
|
||||
app: AppSectionState;
|
||||
authors: AuthorsAppState;
|
||||
commands: CommandAppState;
|
||||
settings: SettingsAppState;
|
||||
system: SystemAppState;
|
||||
tags: TagsAppState;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import AppSectionState, {
|
||||
AppSectionDeleteState,
|
||||
AppSectionItemState,
|
||||
AppSectionSaveState,
|
||||
} from 'App/State/AppSectionState';
|
||||
import DownloadClient from 'typings/DownloadClient';
|
||||
|
|
@ -7,13 +8,16 @@ import ImportList from 'typings/ImportList';
|
|||
import Indexer from 'typings/Indexer';
|
||||
import IndexerFlag from 'typings/IndexerFlag';
|
||||
import Notification from 'typings/Notification';
|
||||
import { UiSettings } from 'typings/UiSettings';
|
||||
import General from 'typings/Settings/General';
|
||||
import UiSettings from 'typings/Settings/UiSettings';
|
||||
|
||||
export interface DownloadClientAppState
|
||||
extends AppSectionState<DownloadClient>,
|
||||
AppSectionDeleteState,
|
||||
AppSectionSaveState {}
|
||||
|
||||
export type GeneralAppState = AppSectionItemState<General>;
|
||||
|
||||
export interface ImportListAppState
|
||||
extends AppSectionState<ImportList>,
|
||||
AppSectionDeleteState,
|
||||
|
|
@ -33,11 +37,12 @@ export type UiSettingsAppState = AppSectionState<UiSettings>;
|
|||
|
||||
interface SettingsAppState {
|
||||
downloadClients: DownloadClientAppState;
|
||||
general: GeneralAppState;
|
||||
importLists: ImportListAppState;
|
||||
indexerFlags: IndexerFlagSettingsAppState;
|
||||
indexers: IndexerAppState;
|
||||
notifications: NotificationAppState;
|
||||
uiSettings: UiSettingsAppState;
|
||||
ui: UiSettingsAppState;
|
||||
}
|
||||
|
||||
export default SettingsAppState;
|
||||
|
|
|
|||
13
frontend/src/App/State/SystemAppState.ts
Normal file
13
frontend/src/App/State/SystemAppState.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import SystemStatus from 'typings/SystemStatus';
|
||||
import Update from 'typings/Update';
|
||||
import AppSectionState, { AppSectionItemState } from './AppSectionState';
|
||||
|
||||
export type SystemStatusAppState = AppSectionItemState<SystemStatus>;
|
||||
export type UpdateAppState = AppSectionState<Update>;
|
||||
|
||||
interface SystemAppState {
|
||||
updates: UpdateAppState;
|
||||
status: SystemStatusAppState;
|
||||
}
|
||||
|
||||
export default SystemAppState;
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
|
||||
import styles from './UpdateChanges.css';
|
||||
|
||||
class UpdateChanges extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
title,
|
||||
changes
|
||||
} = this.props;
|
||||
|
||||
if (changes.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.title}>{title}</div>
|
||||
<ul>
|
||||
{
|
||||
changes.map((change, index) => {
|
||||
const checkChange = change.replace(/#\d{4,5}\b/g, (match, contents) => {
|
||||
return `[${match}](https://github.com/Readarr/Readarr/issues/${match.substring(1)})`;
|
||||
});
|
||||
|
||||
return (
|
||||
<li key={index}>
|
||||
<InlineMarkdown data={checkChange} />
|
||||
</li>
|
||||
);
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
UpdateChanges.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
changes: PropTypes.arrayOf(PropTypes.string)
|
||||
};
|
||||
|
||||
export default UpdateChanges;
|
||||
43
frontend/src/System/Updates/UpdateChanges.tsx
Normal file
43
frontend/src/System/Updates/UpdateChanges.tsx
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import React from 'react';
|
||||
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
|
||||
import styles from './UpdateChanges.css';
|
||||
|
||||
interface UpdateChangesProps {
|
||||
title: string;
|
||||
changes: string[];
|
||||
}
|
||||
|
||||
function UpdateChanges(props: UpdateChangesProps) {
|
||||
const { title, changes } = props;
|
||||
|
||||
if (changes.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const uniqueChanges = [...new Set(changes)];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.title}>{title}</div>
|
||||
<ul>
|
||||
{uniqueChanges.map((change, index) => {
|
||||
const checkChange = change.replace(
|
||||
/#\d{4,5}\b/g,
|
||||
(match) =>
|
||||
`[${match}](https://github.com/Readarr/Readarr/issues/${match.substring(
|
||||
1
|
||||
)})`
|
||||
);
|
||||
|
||||
return (
|
||||
<li key={index}>
|
||||
<InlineMarkdown data={checkChange} />
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default UpdateChanges;
|
||||
|
|
@ -1,252 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import Alert from 'Components/Alert';
|
||||
import Icon from 'Components/Icon';
|
||||
import Label from 'Components/Label';
|
||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import formatDate from 'Utilities/Date/formatDate';
|
||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import UpdateChanges from './UpdateChanges';
|
||||
import styles from './Updates.css';
|
||||
|
||||
class Updates extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
currentVersion,
|
||||
isFetching,
|
||||
isPopulated,
|
||||
updatesError,
|
||||
generalSettingsError,
|
||||
items,
|
||||
isInstallingUpdate,
|
||||
updateMechanism,
|
||||
isDocker,
|
||||
updateMechanismMessage,
|
||||
shortDateFormat,
|
||||
longDateFormat,
|
||||
timeFormat,
|
||||
onInstallLatestPress
|
||||
} = this.props;
|
||||
|
||||
const hasError = !!(updatesError || generalSettingsError);
|
||||
const hasUpdates = isPopulated && !hasError && items.length > 0;
|
||||
const noUpdates = isPopulated && !hasError && !items.length;
|
||||
const hasUpdateToInstall = hasUpdates && _.some(items, { installable: true, latest: true });
|
||||
const noUpdateToInstall = hasUpdates && !hasUpdateToInstall;
|
||||
|
||||
const externalUpdaterPrefix = 'Unable to update Readarr directly,';
|
||||
const externalUpdaterMessages = {
|
||||
external: 'Readarr is configured to use an external update mechanism',
|
||||
apt: 'use apt to install the update',
|
||||
docker: 'update the docker container to receive the update'
|
||||
};
|
||||
|
||||
return (
|
||||
<PageContent title={translate('Updates')}>
|
||||
<PageContentBody>
|
||||
{
|
||||
!isPopulated && !hasError &&
|
||||
<LoadingIndicator />
|
||||
}
|
||||
|
||||
{
|
||||
noUpdates &&
|
||||
<Alert kind={kinds.INFO}>
|
||||
{translate('NoUpdatesAreAvailable')}
|
||||
</Alert>
|
||||
}
|
||||
|
||||
{
|
||||
hasUpdateToInstall &&
|
||||
<div className={styles.messageContainer}>
|
||||
{
|
||||
(updateMechanism === 'builtIn' || updateMechanism === 'script') && !isDocker ?
|
||||
<SpinnerButton
|
||||
className={styles.updateAvailable}
|
||||
kind={kinds.PRIMARY}
|
||||
isSpinning={isInstallingUpdate}
|
||||
onPress={onInstallLatestPress}
|
||||
>
|
||||
Install Latest
|
||||
</SpinnerButton> :
|
||||
|
||||
<Fragment>
|
||||
<Icon
|
||||
name={icons.WARNING}
|
||||
kind={kinds.WARNING}
|
||||
size={30}
|
||||
/>
|
||||
|
||||
<div className={styles.message}>
|
||||
{externalUpdaterPrefix} <InlineMarkdown data={updateMechanismMessage || externalUpdaterMessages[updateMechanism] || externalUpdaterMessages.external} />
|
||||
</div>
|
||||
</Fragment>
|
||||
}
|
||||
|
||||
{
|
||||
isFetching &&
|
||||
<LoadingIndicator
|
||||
className={styles.loading}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
noUpdateToInstall &&
|
||||
<div className={styles.messageContainer}>
|
||||
<Icon
|
||||
className={styles.upToDateIcon}
|
||||
name={icons.CHECK_CIRCLE}
|
||||
size={30}
|
||||
/>
|
||||
|
||||
<div className={styles.message}>
|
||||
The latest version of Readarr is already installed
|
||||
</div>
|
||||
|
||||
{
|
||||
isFetching &&
|
||||
<LoadingIndicator
|
||||
className={styles.loading}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
hasUpdates &&
|
||||
<div>
|
||||
{
|
||||
items.map((update) => {
|
||||
const hasChanges = !!update.changes;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={update.version}
|
||||
className={styles.update}
|
||||
>
|
||||
<div className={styles.info}>
|
||||
<div className={styles.version}>{update.version}</div>
|
||||
<div className={styles.space}>—</div>
|
||||
<div
|
||||
className={styles.date}
|
||||
title={formatDateTime(update.releaseDate, longDateFormat, timeFormat)}
|
||||
>
|
||||
{formatDate(update.releaseDate, shortDateFormat)}
|
||||
</div>
|
||||
|
||||
{
|
||||
update.branch === 'master' ?
|
||||
null :
|
||||
<Label
|
||||
className={styles.label}
|
||||
>
|
||||
{update.branch}
|
||||
</Label>
|
||||
}
|
||||
|
||||
{
|
||||
update.version === currentVersion ?
|
||||
<Label
|
||||
className={styles.label}
|
||||
kind={kinds.SUCCESS}
|
||||
title={formatDateTime(update.installedOn, longDateFormat, timeFormat)}
|
||||
>
|
||||
Currently Installed
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
update.version !== currentVersion && update.installedOn ?
|
||||
<Label
|
||||
className={styles.label}
|
||||
kind={kinds.INVERSE}
|
||||
title={formatDateTime(update.installedOn, longDateFormat, timeFormat)}
|
||||
>
|
||||
Previously Installed
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
</div>
|
||||
|
||||
{
|
||||
!hasChanges &&
|
||||
<div>
|
||||
{translate('MaintenanceRelease')}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
hasChanges &&
|
||||
<div className={styles.changes}>
|
||||
<UpdateChanges
|
||||
title={translate('New')}
|
||||
changes={update.changes.new}
|
||||
/>
|
||||
|
||||
<UpdateChanges
|
||||
title={translate('Fixed')}
|
||||
changes={update.changes.fixed}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
!!updatesError &&
|
||||
<div>
|
||||
Failed to fetch updates
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
!!generalSettingsError &&
|
||||
<div>
|
||||
Failed to update settings
|
||||
</div>
|
||||
}
|
||||
</PageContentBody>
|
||||
</PageContent>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Updates.propTypes = {
|
||||
currentVersion: PropTypes.string.isRequired,
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
updatesError: PropTypes.object,
|
||||
generalSettingsError: PropTypes.object,
|
||||
items: PropTypes.array.isRequired,
|
||||
isInstallingUpdate: PropTypes.bool.isRequired,
|
||||
isDocker: PropTypes.bool.isRequired,
|
||||
updateMechanism: PropTypes.string,
|
||||
updateMechanismMessage: PropTypes.string,
|
||||
shortDateFormat: PropTypes.string.isRequired,
|
||||
longDateFormat: PropTypes.string.isRequired,
|
||||
timeFormat: PropTypes.string.isRequired,
|
||||
onInstallLatestPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default Updates;
|
||||
303
frontend/src/System/Updates/Updates.tsx
Normal file
303
frontend/src/System/Updates/Updates.tsx
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import AppState from 'App/State/AppState';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import Alert from 'Components/Alert';
|
||||
import Icon from 'Components/Icon';
|
||||
import Label from 'Components/Label';
|
||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
|
||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import { fetchGeneralSettings } from 'Store/Actions/settingsActions';
|
||||
import { fetchUpdates } from 'Store/Actions/systemActions';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { UpdateMechanism } from 'typings/Settings/General';
|
||||
import formatDate from 'Utilities/Date/formatDate';
|
||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import UpdateChanges from './UpdateChanges';
|
||||
import styles from './Updates.css';
|
||||
|
||||
const VERSION_REGEX = /\d+\.\d+\.\d+\.\d+/i;
|
||||
|
||||
function createUpdatesSelector() {
|
||||
return createSelector(
|
||||
(state: AppState) => state.system.updates,
|
||||
(state: AppState) => state.settings.general,
|
||||
(updates, generalSettings) => {
|
||||
const { error: updatesError, items } = updates;
|
||||
|
||||
const isFetching = updates.isFetching || generalSettings.isFetching;
|
||||
const isPopulated = updates.isPopulated && generalSettings.isPopulated;
|
||||
|
||||
return {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
updatesError,
|
||||
generalSettingsError: generalSettings.error,
|
||||
items,
|
||||
updateMechanism: generalSettings.item.updateMechanism,
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function Updates() {
|
||||
const currentVersion = useSelector((state: AppState) => state.app.version);
|
||||
const { packageUpdateMechanismMessage } = useSelector(
|
||||
createSystemStatusSelector()
|
||||
);
|
||||
const { shortDateFormat, longDateFormat, timeFormat } = useSelector(
|
||||
createUISettingsSelector()
|
||||
);
|
||||
const isInstallingUpdate = useSelector(
|
||||
createCommandExecutingSelector(commandNames.APPLICATION_UPDATE)
|
||||
);
|
||||
|
||||
const {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
updatesError,
|
||||
generalSettingsError,
|
||||
items,
|
||||
updateMechanism,
|
||||
} = useSelector(createUpdatesSelector());
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [isMajorUpdateModalOpen, setIsMajorUpdateModalOpen] = useState(false);
|
||||
const hasError = !!(updatesError || generalSettingsError);
|
||||
const hasUpdates = isPopulated && !hasError && items.length > 0;
|
||||
const noUpdates = isPopulated && !hasError && !items.length;
|
||||
|
||||
const externalUpdaterPrefix = translate('UpdateAppDirectlyLoadError');
|
||||
const externalUpdaterMessages: Partial<Record<UpdateMechanism, string>> = {
|
||||
external: translate('ExternalUpdater'),
|
||||
apt: translate('AptUpdater'),
|
||||
docker: translate('DockerUpdater'),
|
||||
};
|
||||
|
||||
const { isMajorUpdate, hasUpdateToInstall } = useMemo(() => {
|
||||
const majorVersion = parseInt(
|
||||
currentVersion.match(VERSION_REGEX)?.[0] ?? '0'
|
||||
);
|
||||
|
||||
const latestVersion = items[0]?.version;
|
||||
const latestMajorVersion = parseInt(
|
||||
latestVersion?.match(VERSION_REGEX)?.[0] ?? '0'
|
||||
);
|
||||
|
||||
return {
|
||||
isMajorUpdate: latestMajorVersion > majorVersion,
|
||||
hasUpdateToInstall: items.some(
|
||||
(update) => update.installable && update.latest
|
||||
),
|
||||
};
|
||||
}, [currentVersion, items]);
|
||||
|
||||
const noUpdateToInstall = hasUpdates && !hasUpdateToInstall;
|
||||
|
||||
const handleInstallLatestPress = useCallback(() => {
|
||||
if (isMajorUpdate) {
|
||||
setIsMajorUpdateModalOpen(true);
|
||||
} else {
|
||||
dispatch(executeCommand({ name: commandNames.APPLICATION_UPDATE }));
|
||||
}
|
||||
}, [isMajorUpdate, setIsMajorUpdateModalOpen, dispatch]);
|
||||
|
||||
const handleInstallLatestMajorVersionPress = useCallback(() => {
|
||||
setIsMajorUpdateModalOpen(false);
|
||||
|
||||
dispatch(
|
||||
executeCommand({
|
||||
name: commandNames.APPLICATION_UPDATE,
|
||||
installMajorUpdate: true,
|
||||
})
|
||||
);
|
||||
}, [setIsMajorUpdateModalOpen, dispatch]);
|
||||
|
||||
const handleCancelMajorVersionPress = useCallback(() => {
|
||||
setIsMajorUpdateModalOpen(false);
|
||||
}, [setIsMajorUpdateModalOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchUpdates());
|
||||
dispatch(fetchGeneralSettings());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<PageContent title={translate('Updates')}>
|
||||
<PageContentBody>
|
||||
{isPopulated || hasError ? null : <LoadingIndicator />}
|
||||
|
||||
{noUpdates ? (
|
||||
<Alert kind={kinds.INFO}>{translate('NoUpdatesAreAvailable')}</Alert>
|
||||
) : null}
|
||||
|
||||
{hasUpdateToInstall ? (
|
||||
<div className={styles.messageContainer}>
|
||||
{updateMechanism === 'builtIn' || updateMechanism === 'script' ? (
|
||||
<SpinnerButton
|
||||
kind={kinds.PRIMARY}
|
||||
isSpinning={isInstallingUpdate}
|
||||
onPress={handleInstallLatestPress}
|
||||
>
|
||||
{translate('InstallLatest')}
|
||||
</SpinnerButton>
|
||||
) : (
|
||||
<>
|
||||
<Icon name={icons.WARNING} kind={kinds.WARNING} size={30} />
|
||||
|
||||
<div className={styles.message}>
|
||||
{externalUpdaterPrefix}{' '}
|
||||
<InlineMarkdown
|
||||
data={
|
||||
packageUpdateMechanismMessage ||
|
||||
externalUpdaterMessages[updateMechanism] ||
|
||||
externalUpdaterMessages.external
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{isFetching ? (
|
||||
<LoadingIndicator className={styles.loading} size={20} />
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{noUpdateToInstall && (
|
||||
<div className={styles.messageContainer}>
|
||||
<Icon
|
||||
className={styles.upToDateIcon}
|
||||
name={icons.CHECK_CIRCLE}
|
||||
size={30}
|
||||
/>
|
||||
<div className={styles.message}>{translate('OnLatestVersion')}</div>
|
||||
|
||||
{isFetching && (
|
||||
<LoadingIndicator className={styles.loading} size={20} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hasUpdates && (
|
||||
<div>
|
||||
{items.map((update) => {
|
||||
return (
|
||||
<div key={update.version} className={styles.update}>
|
||||
<div className={styles.info}>
|
||||
<div className={styles.version}>{update.version}</div>
|
||||
<div className={styles.space}>—</div>
|
||||
<div
|
||||
className={styles.date}
|
||||
title={formatDateTime(
|
||||
update.releaseDate,
|
||||
longDateFormat,
|
||||
timeFormat
|
||||
)}
|
||||
>
|
||||
{formatDate(update.releaseDate, shortDateFormat)}
|
||||
</div>
|
||||
|
||||
{update.branch === 'master' ? null : (
|
||||
<Label className={styles.label}>{update.branch}</Label>
|
||||
)}
|
||||
|
||||
{update.version === currentVersion ? (
|
||||
<Label
|
||||
className={styles.label}
|
||||
kind={kinds.SUCCESS}
|
||||
title={formatDateTime(
|
||||
update.installedOn,
|
||||
longDateFormat,
|
||||
timeFormat
|
||||
)}
|
||||
>
|
||||
{translate('CurrentlyInstalled')}
|
||||
</Label>
|
||||
) : null}
|
||||
|
||||
{update.version !== currentVersion && update.installedOn ? (
|
||||
<Label
|
||||
className={styles.label}
|
||||
kind={kinds.INVERSE}
|
||||
title={formatDateTime(
|
||||
update.installedOn,
|
||||
longDateFormat,
|
||||
timeFormat
|
||||
)}
|
||||
>
|
||||
{translate('PreviouslyInstalled')}
|
||||
</Label>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{update.changes ? (
|
||||
<div>
|
||||
<UpdateChanges
|
||||
title={translate('New')}
|
||||
changes={update.changes.new}
|
||||
/>
|
||||
|
||||
<UpdateChanges
|
||||
title={translate('Fixed')}
|
||||
changes={update.changes.fixed}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div>{translate('MaintenanceRelease')}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{updatesError ? (
|
||||
<Alert kind={kinds.WARNING}>
|
||||
{translate('FailedToFetchUpdates')}
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
{generalSettingsError ? (
|
||||
<Alert kind={kinds.DANGER}>
|
||||
{translate('FailedToFetchSettings')}
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
<ConfirmModal
|
||||
isOpen={isMajorUpdateModalOpen}
|
||||
kind={kinds.WARNING}
|
||||
title={translate('InstallMajorVersionUpdate')}
|
||||
message={
|
||||
<div>
|
||||
<div>{translate('InstallMajorVersionUpdateMessage')}</div>
|
||||
<div>
|
||||
<InlineMarkdown
|
||||
data={translate('InstallMajorVersionUpdateMessageLink', {
|
||||
domain: 'readarr.com',
|
||||
url: 'https://readarr.com/#downloads',
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
confirmLabel={translate('Install')}
|
||||
onConfirm={handleInstallLatestMajorVersionPress}
|
||||
onCancel={handleCancelMajorVersionPress}
|
||||
/>
|
||||
</PageContentBody>
|
||||
</PageContent>
|
||||
);
|
||||
}
|
||||
|
||||
export default Updates;
|
||||
45
frontend/src/typings/Settings/General.ts
Normal file
45
frontend/src/typings/Settings/General.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
export type UpdateMechanism =
|
||||
| 'builtIn'
|
||||
| 'script'
|
||||
| 'external'
|
||||
| 'apt'
|
||||
| 'docker';
|
||||
|
||||
export default interface General {
|
||||
bindAddress: string;
|
||||
port: number;
|
||||
sslPort: number;
|
||||
enableSsl: boolean;
|
||||
launchBrowser: boolean;
|
||||
authenticationMethod: string;
|
||||
authenticationRequired: string;
|
||||
analyticsEnabled: boolean;
|
||||
username: string;
|
||||
password: string;
|
||||
passwordConfirmation: string;
|
||||
logLevel: string;
|
||||
consoleLogLevel: string;
|
||||
branch: string;
|
||||
apiKey: string;
|
||||
sslCertPath: string;
|
||||
sslCertPassword: string;
|
||||
urlBase: string;
|
||||
instanceName: string;
|
||||
applicationUrl: string;
|
||||
updateAutomatically: boolean;
|
||||
updateMechanism: UpdateMechanism;
|
||||
updateScriptPath: string;
|
||||
proxyEnabled: boolean;
|
||||
proxyType: string;
|
||||
proxyHostname: string;
|
||||
proxyPort: number;
|
||||
proxyUsername: string;
|
||||
proxyPassword: string;
|
||||
proxyBypassFilter: string;
|
||||
proxyBypassLocalAddresses: boolean;
|
||||
certificateValidation: string;
|
||||
backupFolder: string;
|
||||
backupInterval: number;
|
||||
backupRetention: number;
|
||||
id: number;
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
export interface UiSettings {
|
||||
export default interface UiSettings {
|
||||
theme: 'auto' | 'dark' | 'light';
|
||||
showRelativeDates: boolean;
|
||||
shortDateFormat: string;
|
||||
longDateFormat: string;
|
||||
32
frontend/src/typings/SystemStatus.ts
Normal file
32
frontend/src/typings/SystemStatus.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
interface SystemStatus {
|
||||
appData: string;
|
||||
appName: string;
|
||||
authentication: string;
|
||||
branch: string;
|
||||
buildTime: string;
|
||||
instanceName: string;
|
||||
isAdmin: boolean;
|
||||
isDebug: boolean;
|
||||
isDocker: boolean;
|
||||
isLinux: boolean;
|
||||
isNetCore: boolean;
|
||||
isOsx: boolean;
|
||||
isProduction: boolean;
|
||||
isUserInteractive: boolean;
|
||||
isWindows: boolean;
|
||||
migrationVersion: number;
|
||||
mode: string;
|
||||
osName: string;
|
||||
osVersion: string;
|
||||
packageUpdateMechanism: string;
|
||||
packageUpdateMechanismMessage: string;
|
||||
runtimeName: string;
|
||||
runtimeVersion: string;
|
||||
sqliteVersion: string;
|
||||
startTime: string;
|
||||
startupPath: string;
|
||||
urlBase: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
export default SystemStatus;
|
||||
20
frontend/src/typings/Update.ts
Normal file
20
frontend/src/typings/Update.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
export interface Changes {
|
||||
new: string[];
|
||||
fixed: string[];
|
||||
}
|
||||
|
||||
interface Update {
|
||||
version: string;
|
||||
branch: string;
|
||||
releaseDate: string;
|
||||
fileName: string;
|
||||
url: string;
|
||||
installed: boolean;
|
||||
installedOn: string;
|
||||
installable: boolean;
|
||||
latest: boolean;
|
||||
changes: Changes | null;
|
||||
hash: string;
|
||||
}
|
||||
|
||||
export default Update;
|
||||
|
|
@ -300,7 +300,7 @@
|
|||
"ChangeFileDate": "تغيير تاريخ الملف",
|
||||
"CertificateValidationHelpText": "تغيير مدى صرامة التحقق من صحة شهادة HTTPS",
|
||||
"CertificateValidation": "التحقق من صحة الشهادة",
|
||||
"CancelMessageText": "هل أنت متأكد أنك تريد إلغاء هذه المهمة المعلقة؟",
|
||||
"CancelPendingTask": "هل أنت متأكد أنك تريد إلغاء هذه المهمة المعلقة؟",
|
||||
"Cancel": "إلغاء",
|
||||
"CalendarWeekColumnHeaderHelpText": "يظهر فوق كل عمود عندما يكون الأسبوع هو العرض النشط",
|
||||
"Calendar": "التقويم",
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
"Calendar": "Календар",
|
||||
"CalendarWeekColumnHeaderHelpText": "Показва се над всяка колона, когато седмицата е активният изглед",
|
||||
"Cancel": "Отказ",
|
||||
"CancelMessageText": "Наистина ли искате да отмените тази чакаща задача?",
|
||||
"CancelPendingTask": "Наистина ли искате да отмените тази чакаща задача?",
|
||||
"CertificateValidation": "Валидиране на сертификат",
|
||||
"CertificateValidationHelpText": "Променете колко строго е валидирането на HTTPS сертифициране",
|
||||
"ChangeFileDate": "Промяна на датата на файла",
|
||||
|
|
|
|||
|
|
@ -445,7 +445,7 @@
|
|||
"ApplicationURL": "URL de l'aplicació",
|
||||
"ApplicationUrlHelpText": "URL extern de l'aplicació, inclòs http(s)://, port i URL base",
|
||||
"BackupFolderHelpText": "Els camins relatius estaran sota el directori AppData del Radarr",
|
||||
"CancelMessageText": "Esteu segur que voleu cancel·lar aquesta tasca pendent?",
|
||||
"CancelPendingTask": "Esteu segur que voleu cancel·lar aquesta tasca pendent?",
|
||||
"ChownGroupHelpTextWarning": "Això només funciona si l'usuari que executa Radarr és el propietari del fitxer. És millor assegurar-se que el client de descàrrega utilitza el mateix grup que Radarr.",
|
||||
"ConnectSettingsSummary": "Notificacions, connexions a servidors/reproductors multimèdia i scripts personalitzats",
|
||||
"DeleteEmptyFoldersHelpText": "Suprimeix les carpetes de pel·lícules buides durant l'exploració del disc i quan s'esborren els fitxers de pel·lícules",
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
"Calendar": "Kalendář",
|
||||
"CalendarWeekColumnHeaderHelpText": "Zobrazuje se nad každým sloupcem, když je aktivní zobrazení týden",
|
||||
"Cancel": "Zrušit",
|
||||
"CancelMessageText": "Opravdu chcete zrušit tento nevyřízený úkol?",
|
||||
"CancelPendingTask": "Opravdu chcete zrušit tento nevyřízený úkol?",
|
||||
"CertificateValidation": "Ověření certifikátu",
|
||||
"CertificateValidationHelpText": "Změňte přísnost ověřování certifikátů HTTPS. Neměňte, pokud nerozumíte rizikům.",
|
||||
"ChangeFileDate": "Změnit datum souboru",
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
"Calendar": "Kalender",
|
||||
"CalendarWeekColumnHeaderHelpText": "Vist over hver kolonne, når ugen er den aktive visning",
|
||||
"Cancel": "Afbryd",
|
||||
"CancelMessageText": "Er du sikker på, at du vil annullere denne afventende opgave?",
|
||||
"CancelPendingTask": "Er du sikker på, at du vil annullere denne afventende opgave?",
|
||||
"CertificateValidation": "Validering af certifikat",
|
||||
"CertificateValidationHelpText": "Skift, hvor streng HTTPS-certificering er",
|
||||
"ChangeFileDate": "Skift fildato",
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
"Calendar": "Kalender",
|
||||
"CalendarWeekColumnHeaderHelpText": "Wird in der Wochenansicht über jeder Spalte angezeigt",
|
||||
"Cancel": "Abbrechen",
|
||||
"CancelMessageText": "Diese laufende Aufgabe wirklich abbrechen?",
|
||||
"CancelPendingTask": "Diese laufende Aufgabe wirklich abbrechen?",
|
||||
"CertificateValidation": "Zertifikatsvalidierung",
|
||||
"CertificateValidationHelpText": "Ändern Sie, wie streng die Validierung der HTTPS-Zertifizierung ist. Ändern Sie nichts, es sei denn, Sie verstehen die Risiken.",
|
||||
"ChangeFileDate": "Ändern Sie das Dateidatum",
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
"Calendar": "Ημερολόγιο",
|
||||
"CalendarWeekColumnHeaderHelpText": "Εμφανίζεται πάνω από κάθε στήλη όταν η εβδομάδα είναι η ενεργή προβολή",
|
||||
"Cancel": "Ακύρωση",
|
||||
"CancelMessageText": "Είστε βέβαιοι ότι θέλετε να ακυρώσετε αυτήν την εργασία σε εκκρεμότητα;",
|
||||
"CancelPendingTask": "Είστε βέβαιοι ότι θέλετε να ακυρώσετε αυτήν την εργασία σε εκκρεμότητα;",
|
||||
"CertificateValidation": "Επικύρωση πιστοποιητικού",
|
||||
"CertificateValidationHelpText": "Αλλάξτε πόσο αυστηρή είναι η επικύρωση πιστοποίησης HTTPS.",
|
||||
"ChangeFileDate": "Αλλαγή ημερομηνίας αρχείου",
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
"ApplyTagsHelpTextHowToApplyIndexers": "How to apply tags to the selected indexers",
|
||||
"ApplyTagsHelpTextRemove": "Remove: Remove the entered tags",
|
||||
"ApplyTagsHelpTextReplace": "Replace: Replace the tags with the entered tags (enter no tags to clear all tags)",
|
||||
"AptUpdater": "Use apt to install the update",
|
||||
"AudioFileMetadata": "Write Metadata to Audio Files",
|
||||
"AuthBasic": "Basic (Browser Popup)",
|
||||
"AuthForm": "Forms (Login Page)",
|
||||
|
|
@ -137,7 +138,7 @@
|
|||
"CalibreUrlBase": "Calibre Url Base",
|
||||
"CalibreUsername": "Calibre Username",
|
||||
"Cancel": "Cancel",
|
||||
"CancelMessageText": "Are you sure you want to cancel this pending task?",
|
||||
"CancelPendingTask": "Are you sure you want to cancel this pending task?",
|
||||
"CatalogNumber": "Catalog Number",
|
||||
"CertificateValidation": "Certificate Validation",
|
||||
"CertificateValidationHelpText": "Change how strict HTTPS certification validation is. Do not change unless you understand the risks.",
|
||||
|
|
@ -197,6 +198,7 @@
|
|||
"CreateEmptyAuthorFolders": "Create empty author folders",
|
||||
"CreateEmptyAuthorFoldersHelpText": "Create missing author folders during disk scan",
|
||||
"CreateGroup": "Create group",
|
||||
"CurrentlyInstalled": "Currently Installed",
|
||||
"CustomFilter": "Custom Filter",
|
||||
"CustomFormat": "Custom Format",
|
||||
"CustomFormatScore": "Custom Format Score",
|
||||
|
|
@ -293,6 +295,7 @@
|
|||
"DoNotBlocklist": "Do not Blocklist",
|
||||
"DoNotBlocklistHint": "Remove without blocklisting",
|
||||
"Docker": "Docker",
|
||||
"DockerUpdater": "Update the docker container to receive the update",
|
||||
"DownloadClient": "Download Client",
|
||||
"DownloadClientCheckDownloadingToRoot": "Download client {0} places downloads in the root folder {1}. You should not download to a root folder.",
|
||||
"DownloadClientCheckNoneAvailableMessage": "No download client is available",
|
||||
|
|
@ -357,10 +360,13 @@
|
|||
"ExistingTagsScrubbed": "Existing tags scrubbed",
|
||||
"ExportCustomFormat": "Export Custom Format",
|
||||
"External": "External",
|
||||
"ExternalUpdater": "{appName} is configured to use an external update mechanism",
|
||||
"ExtraFileExtensionsHelpText": "Comma separated list of extra files to import (.nfo will be imported as .nfo-orig)",
|
||||
"ExtraFileExtensionsHelpTextsExamples": "Examples: '.sub, .nfo' or 'sub,nfo'",
|
||||
"FailedDownloadHandling": "Failed Download Handling",
|
||||
"FailedLoadingSearchResults": "Failed to load search results, please try again.",
|
||||
"FailedToFetchSettings": "Failed to fetch settings",
|
||||
"FailedToFetchUpdates": "Failed to fetch updates",
|
||||
"FailedToLoadQueue": "Failed to load Queue",
|
||||
"FileDateHelpText": "Change file date on import/rescan",
|
||||
"FileDetails": "File Details",
|
||||
|
|
@ -478,6 +484,11 @@
|
|||
"IndexerTagsHelpText": "Only use this indexer for authors with at least one matching tag. Leave blank to use with all authors.",
|
||||
"Indexers": "Indexers",
|
||||
"IndexersSettingsSummary": "Indexers and release restrictions",
|
||||
"Install": "Install",
|
||||
"InstallLatest": "Install Latest",
|
||||
"InstallMajorVersionUpdate": "Install Update",
|
||||
"InstallMajorVersionUpdateMessage": "This update will install a new major version and may not be compatible with your system. Are you sure you want to install this update?",
|
||||
"InstallMajorVersionUpdateMessageLink": "Please check [{domain}]({url}) for more information.",
|
||||
"InstanceName": "Instance Name",
|
||||
"InstanceNameHelpText": "Instance name in tab and for Syslog app name",
|
||||
"InteractiveSearchModalHeader": "Interactive Search",
|
||||
|
|
@ -674,6 +685,7 @@
|
|||
"OnHealthIssueHelpText": "On Health Issue",
|
||||
"OnImportFailure": "On Import Failure",
|
||||
"OnImportFailureHelpText": "On Import Failure",
|
||||
"OnLatestVersion": "The latest version of {appName} is already installed",
|
||||
"OnReleaseImport": "On Release Import",
|
||||
"OnReleaseImportHelpText": "On Release Import",
|
||||
"OnRename": "On Rename",
|
||||
|
|
@ -706,6 +718,7 @@
|
|||
"PosterSize": "Poster Size",
|
||||
"PreviewRename": "Preview Rename",
|
||||
"PreviewRetag": "Preview Retag",
|
||||
"PreviouslyInstalled": "Previously Installed",
|
||||
"Profiles": "Profiles",
|
||||
"ProfilesSettingsSummary": "Quality, Metadata, Delay, and Release profiles",
|
||||
"Progress": "Progress",
|
||||
|
|
@ -1051,6 +1064,7 @@
|
|||
"UnmonitoredHelpText": "Include unmonitored books in the iCal feed",
|
||||
"UnselectAll": "Unselect All",
|
||||
"UpdateAll": "Update all",
|
||||
"UpdateAppDirectlyLoadError": "Unable to update {appName} directly,",
|
||||
"UpdateAutomaticallyHelpText": "Automatically download and install updates. You will still be able to install from System: Updates",
|
||||
"UpdateAvailable": "New update is available",
|
||||
"UpdateCheckStartupNotWritableMessage": "Cannot install update because startup folder '{0}' is not writable by the user '{1}'.",
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
"Calendar": "Calendario",
|
||||
"CalendarWeekColumnHeaderHelpText": "Mostrado sobre cada columna cuando la vista activa es semana",
|
||||
"Cancel": "Cancelar",
|
||||
"CancelMessageText": "Seguro que quieres cancelar esta tarea pendiente?",
|
||||
"CancelPendingTask": "Seguro que quieres cancelar esta tarea pendiente?",
|
||||
"CertificateValidation": "Validacion de certificado",
|
||||
"CertificateValidationHelpText": "Cambia cómo de estricta es la validación de certificación de HTTPS. No cambiar a menos que entiendas los riesgos.",
|
||||
"ChangeFileDate": "Cambiar fecha de archivo",
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
"Calendar": "Kalenteri",
|
||||
"CalendarWeekColumnHeaderHelpText": "Näkyy jokaisen sarakkeen yläpuolella käytettäessä viikkonäkymää.",
|
||||
"Cancel": "Peruuta",
|
||||
"CancelMessageText": "Haluatko varmasti perua tämän odottavan tehtävän?",
|
||||
"CancelPendingTask": "Haluatko varmasti perua tämän odottavan tehtävän?",
|
||||
"CertificateValidation": "Varmenteen vahvistus",
|
||||
"CertificateValidationHelpText": "Määritä HTTPS-varmennevahvistuksen tiukkuus. Älä muta, jos et ymmärrä riskejä.",
|
||||
"ChangeFileDate": "Muuta tiedoston päiväys",
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
"Calendar": "Calendrier",
|
||||
"CalendarWeekColumnHeaderHelpText": "Affiché au dessus de chaque colonne quand \"Semaine\" est l'affichage actif",
|
||||
"Cancel": "Annuler",
|
||||
"CancelMessageText": "Êtes-vous sur de vouloir annuler cette tâche en attente ?",
|
||||
"CancelPendingTask": "Êtes-vous sur de vouloir annuler cette tâche en attente ?",
|
||||
"CertificateValidation": "Validation du certificat",
|
||||
"CertificateValidationHelpText": "Modifier le niveau de rigueur de la validation de la certification HTTPS. Ne pas modifier si vous ne maîtrisez pas les risques.",
|
||||
"ChangeFileDate": "Changer la date du fichier",
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
"BypassProxyForLocalAddresses": "עקיפת פרוקסי לכתובות מקומיות",
|
||||
"Calendar": "לוּחַ שָׁנָה",
|
||||
"CalendarWeekColumnHeaderHelpText": "מוצג מעל כל עמודה כאשר השבוע היא התצוגה הפעילה",
|
||||
"CancelMessageText": "האם אתה בטוח שברצונך לבטל משימה זו בהמתנה?",
|
||||
"CancelPendingTask": "האם אתה בטוח שברצונך לבטל משימה זו בהמתנה?",
|
||||
"CertificateValidation": "אימות תעודה",
|
||||
"CertificateValidationHelpText": "שנה את מידת אימות ההסמכה של HTTPS",
|
||||
"ChangeFileDate": "שנה את תאריך הקובץ",
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
"Calendar": "पंचांग",
|
||||
"CalendarWeekColumnHeaderHelpText": "प्रत्येक स्तंभ के ऊपर दिखाया गया जब सप्ताह सक्रिय दृश्य होता है",
|
||||
"Cancel": "रद्द करना",
|
||||
"CancelMessageText": "क्या आप वाकई इस लंबित कार्य को रद्द करना चाहते हैं?",
|
||||
"CancelPendingTask": "क्या आप वाकई इस लंबित कार्य को रद्द करना चाहते हैं?",
|
||||
"CertificateValidation": "प्रमाणपत्र सत्यापन",
|
||||
"CertificateValidationHelpText": "बदलें कि HTTPS प्रमाणन सत्यापन कितना सख्त है",
|
||||
"ChangeFileDate": "फ़ाइल दिनांक बदलें",
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@
|
|||
"AutoUnmonitorPreviouslyDownloadedBooksHelpText": "U Radarru se automatski isključuje nadzor za filmove koji su izbrisani sa diska",
|
||||
"BackupFolderHelpText": "Relativne putanje će biti unutar Radarrovog AppData direktorija",
|
||||
"BypassIfHighestQuality": "Zaobiđi ako je Najviši Kvalitet",
|
||||
"CancelMessageText": "Jeste li sigurni da želite otkazati ovaj zadatak na čekanju?",
|
||||
"CancelPendingTask": "Jeste li sigurni da želite otkazati ovaj zadatak na čekanju?",
|
||||
"ChmodFolderHelpTextWarning": "Ovo jedino radi ako je korisnik koji je pokrenuo Radarr vlasnik datoteke. Bolje je osigurati da klijent za preuzimanje postavi dozvolu ispravno.",
|
||||
"ChownGroupHelpTextWarning": "Ovo jedino radi ako je korisnik koji je pokrenuo Radarr vlasnik datoteke. Bolje je osigurati da klijent za preuzimanje koristi istu grupu kao Radarr.",
|
||||
"DeleteImportListMessageText": "Jeste li sigurni da želite obrisati oznaku formata {0}?",
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
"Calendar": "Naptár",
|
||||
"CalendarWeekColumnHeaderHelpText": "Minden oszlop felett jelenjen meg, hogy melyik hét az aktuális",
|
||||
"Cancel": "Mégse",
|
||||
"CancelMessageText": "Biztosan törlöd ezt a függőben lévő feladatot?",
|
||||
"CancelPendingTask": "Biztosan törlöd ezt a függőben lévő feladatot?",
|
||||
"CertificateValidation": "Tanúsítvány érvényesítése",
|
||||
"CertificateValidationHelpText": "Módosítsa a HTTPS-tanúsítvány-ellenőrzés szigorúságát. Ne változtasson, hacsak nem érti a kockázatokat.",
|
||||
"ChangeFileDate": "Fájl dátumának módosítása",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
"Calendar": "Dagatal",
|
||||
"CalendarWeekColumnHeaderHelpText": "Sýnt fyrir ofan hvern dálk þegar vikan er virka skjámyndin",
|
||||
"Cancel": "Hætta við",
|
||||
"CancelMessageText": "Ertu viss um að þú viljir hætta við þetta verkefni í bið?",
|
||||
"CancelPendingTask": "Ertu viss um að þú viljir hætta við þetta verkefni í bið?",
|
||||
"CertificateValidation": "Staðfesting skírteina",
|
||||
"CertificateValidationHelpText": "Breyttu hversu ströng HTTPS vottun er",
|
||||
"ChangeFileDate": "Breyttu dagsetningu skráar",
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
"Calendar": "Calendario",
|
||||
"CalendarWeekColumnHeaderHelpText": "Mostra sopra ogni colonna quando la settimana è la vista attiva",
|
||||
"Cancel": "Annulla",
|
||||
"CancelMessageText": "Sei sicuro di voler cancellare questa operazione in sospeso?",
|
||||
"CancelPendingTask": "Sei sicuro di voler cancellare questa operazione in sospeso?",
|
||||
"CertificateValidation": "Convalida del Certificato",
|
||||
"CertificateValidationHelpText": "Cambia quanto rigorosamente vengono validati i certificati HTTPS. Non cambiare senza conoscerne i rischi.",
|
||||
"ChangeFileDate": "Cambiare la Data del File",
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@
|
|||
"Calendar": "カレンダー",
|
||||
"CalendarWeekColumnHeaderHelpText": "週がアクティブビューの場合、各列の上に表示されます",
|
||||
"Cancel": "キャンセル",
|
||||
"CancelMessageText": "この保留中のタスクをキャンセルしてもよろしいですか?",
|
||||
"CancelPendingTask": "この保留中のタスクをキャンセルしてもよろしいですか?",
|
||||
"CertificateValidation": "証明書の検証",
|
||||
"CertificateValidationHelpText": "HTTPS認証検証の厳密さを変更する",
|
||||
"ChangeFileDate": "ファイルの日付を変更する",
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@
|
|||
"Calendar": "달력",
|
||||
"CalendarWeekColumnHeaderHelpText": "주가 활성보기 일 때 각 열 위에 표시됩니다.",
|
||||
"Cancel": "취소",
|
||||
"CancelMessageText": "이 보류중인 작업을 취소 하시겠습니까?",
|
||||
"CancelPendingTask": "이 보류중인 작업을 취소 하시겠습니까?",
|
||||
"CertificateValidation": "인증서 검증",
|
||||
"CertificateValidationHelpText": "HTTPS 인증 유효성 검사의 엄격한 방법 변경",
|
||||
"ChangeFileDate": "파일 날짜 변경",
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
"BindAddressHelpText": "Gyldig IPv4 -adresse, localhost eller \"*\" for alle grensesnitt",
|
||||
"BypassProxyForLocalAddresses": "Omgå proxy for lokale adresser",
|
||||
"Cancel": "Avbryt",
|
||||
"CancelMessageText": "Er du sikker på at du vil avbryte denne ventende oppgaven?",
|
||||
"CancelPendingTask": "Er du sikker på at du vil avbryte denne ventende oppgaven?",
|
||||
"CertificateValidation": "Sertifikatvalidering",
|
||||
"CertificateValidationHelpText": "Endre hvor streng HTTPS -sertifisering validering er. Ikke endre med mindre du forstår risikoene.",
|
||||
"ChangeHasNotBeenSavedYet": "Endringen er ikke lagret ennå",
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
"Calendar": "Kalender",
|
||||
"CalendarWeekColumnHeaderHelpText": "Wordt getoond boven elke kolom wanneer de actieve weergave Week is",
|
||||
"Cancel": "Annuleer",
|
||||
"CancelMessageText": "Bent u zeker dat u deze taak in afwachting wilt annuleren?",
|
||||
"CancelPendingTask": "Bent u zeker dat u deze taak in afwachting wilt annuleren?",
|
||||
"CertificateValidation": "Certificaat Validatie",
|
||||
"CertificateValidationHelpText": "Wijzig hoe strict HTTPS certificaat validatie is. Wijzig dit niet behalve als je de risico's begrijpt.",
|
||||
"ChangeFileDate": "Wijzig Bestandsdatum",
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@
|
|||
"Calendar": "Kalendarz",
|
||||
"CalendarWeekColumnHeaderHelpText": "Wyświetlany nad każdą kolumną, gdy tydzień jest aktywnym widokiem",
|
||||
"Cancel": "Anuluj",
|
||||
"CancelMessageText": "Czy na pewno chcesz anulować to oczekujące zadanie?",
|
||||
"CancelPendingTask": "Czy na pewno chcesz anulować to oczekujące zadanie?",
|
||||
"CertificateValidation": "Walidacja certyfikatu",
|
||||
"CertificateValidationHelpText": "Zmień ścisłość walidacji certyfikatu HTTPS. Nie zmieniaj, jeśli nie rozumiesz związanych z tym zagrożeń.",
|
||||
"ChangeFileDate": "Zmień datę pliku",
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@
|
|||
"Calendar": "Calendário",
|
||||
"CalendarWeekColumnHeaderHelpText": "Mostrar acima de cada coluna quando a semana é a vista ativa",
|
||||
"Cancel": "Cancelar",
|
||||
"CancelMessageText": "Tem a certeza que quer cancelar esta tarefa pendente?",
|
||||
"CancelPendingTask": "Tem a certeza que quer cancelar esta tarefa pendente?",
|
||||
"CertificateValidation": "Validação de certificado",
|
||||
"CertificateValidationHelpText": "Mudar nível de restrição da validação da certificação HTTPS",
|
||||
"ChangeFileDate": "Modificar data do ficheiro",
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@
|
|||
"Calendar": "Calendário",
|
||||
"CalendarWeekColumnHeaderHelpText": "Mostrar acima de cada coluna quando a semana está na exibição ativa",
|
||||
"Cancel": "Cancelar",
|
||||
"CancelMessageText": "Tem certeza que deseja cancelar esta tarefa pendente?",
|
||||
"CancelPendingTask": "Tem certeza que deseja cancelar esta tarefa pendente?",
|
||||
"CertificateValidation": "Validação de certificado",
|
||||
"CertificateValidationHelpText": "Altere a rigidez da validação da certificação HTTPS. Não mude a menos que você entenda os riscos.",
|
||||
"ChangeFileDate": "Alterar data do arquivo",
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@
|
|||
"Calendar": "Calendar",
|
||||
"CalendarWeekColumnHeaderHelpText": "Afișat deasupra fiecărei coloane când săptămâna este vizualizarea activă",
|
||||
"Cancel": "Anulează",
|
||||
"CancelMessageText": "Sigur doriți să anulați această sarcină în așteptare?",
|
||||
"CancelPendingTask": "Sigur doriți să anulați această sarcină în așteptare?",
|
||||
"CertificateValidation": "Validarea certificatului",
|
||||
"CertificateValidationHelpText": "Modificați cât de strictă este validarea certificării HTTPS. Nu schimbați dacă nu înțelegeți riscurile.",
|
||||
"ChangeHasNotBeenSavedYet": "Modificarea nu a fost încă salvată",
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
"Calendar": "Календарь",
|
||||
"CalendarWeekColumnHeaderHelpText": "Отображается над каждым столбцом, когда неделя активна",
|
||||
"Cancel": "Отменить",
|
||||
"CancelMessageText": "Вы уверены, что хотите убрать данную задачу из очереди?",
|
||||
"CancelPendingTask": "Вы уверены, что хотите убрать данную задачу из очереди?",
|
||||
"CertificateValidation": "Проверка сертификата",
|
||||
"CertificateValidationHelpText": "Измените строгую проверку сертификации HTTPS. Не меняйте, если вы не понимаете риски.",
|
||||
"ChangeFileDate": "Изменить дату файла",
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@
|
|||
"IsCutoffCutoff": "Hranica",
|
||||
"Label": "Štítok",
|
||||
"Remove": "Odstrániť",
|
||||
"CancelMessageText": "Naozaj chcete zrušiť túto prebiehajúcu úlohu?",
|
||||
"CancelPendingTask": "Naozaj chcete zrušiť túto prebiehajúcu úlohu?",
|
||||
"Publisher": "Vydavateľ",
|
||||
"Quality": "kvalita",
|
||||
"QualityProfile": "Profil kvality",
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
"Calendar": "Kalender",
|
||||
"CalendarWeekColumnHeaderHelpText": "Visas ovanför varje kolumn när veckan är den aktiva vyn",
|
||||
"Cancel": "Avbryt",
|
||||
"CancelMessageText": "Är du säker på att du vill avbryta den väntande uppgiften?",
|
||||
"CancelPendingTask": "Är du säker på att du vill avbryta den väntande uppgiften?",
|
||||
"CertificateValidation": "Validering av Certifikat",
|
||||
"CertificateValidationHelpText": "Ändra hur strikt valideringen av HTTPS certifikat är",
|
||||
"ChangeFileDate": "Ändra fildatum",
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@
|
|||
"Calendar": "ปฏิทิน",
|
||||
"CalendarWeekColumnHeaderHelpText": "แสดงอยู่เหนือแต่ละคอลัมน์เมื่อสัปดาห์เป็นมุมมองที่ใช้งานอยู่",
|
||||
"Cancel": "ยกเลิก",
|
||||
"CancelMessageText": "แน่ใจไหมว่าต้องการยกเลิกงานที่รอดำเนินการนี้",
|
||||
"CancelPendingTask": "แน่ใจไหมว่าต้องการยกเลิกงานที่รอดำเนินการนี้",
|
||||
"CertificateValidation": "การตรวจสอบใบรับรอง",
|
||||
"CertificateValidationHelpText": "เปลี่ยนวิธีการตรวจสอบการรับรอง HTTPS ที่เข้มงวด",
|
||||
"ChangeFileDate": "เปลี่ยนวันที่ของไฟล์",
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
"Calendar": "Takvim",
|
||||
"CalendarWeekColumnHeaderHelpText": "Aktif görünüm hafta olduğunda her bir sütunun üzerinde gösterilir",
|
||||
"Cancel": "Vazgeç",
|
||||
"CancelMessageText": "Bu bekleyen görevi iptal etmek istediğinizden emin misiniz?",
|
||||
"CancelPendingTask": "Bu bekleyen görevi iptal etmek istediğinizden emin misiniz?",
|
||||
"CertificateValidation": "Sertifika Doğrulama",
|
||||
"CertificateValidationHelpText": "HTTPS sertifika doğrulamasının sıkılığını değiştirin. Riskleri anlamadığınız sürece değişmeyin.",
|
||||
"ChangeFileDate": "Dosya Tarihini Değiştir",
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@
|
|||
"BindAddressHelpText": "Дійсна адреса IPv4 або '*' для всіх інтерфейсів",
|
||||
"Blocklist": "Чорний список",
|
||||
"BlocklistRelease": "Реліз із чорного списку",
|
||||
"CancelMessageText": "Ви впевнені, що хочете скасувати це незавершене завдання?",
|
||||
"CancelPendingTask": "Ви впевнені, що хочете скасувати це незавершене завдання?",
|
||||
"ChownGroupHelpText": "Назва групи або gid. Використовуйте gid для віддалених файлових систем.",
|
||||
"ChownGroupHelpTextWarning": "Це працює лише в тому випадку, якщо власником файлу є користувач, на якому працює Radarr. Краще переконатися, що клієнт для завантаження використовує ту саму групу, що й Radarr.",
|
||||
"DeleteReleaseProfile": "Видалити профіль випуску",
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@
|
|||
"Calendar": "Lịch",
|
||||
"CalendarWeekColumnHeaderHelpText": "Được hiển thị phía trên mỗi cột khi tuần là chế độ xem đang hoạt động",
|
||||
"Cancel": "Huỷ bỏ",
|
||||
"CancelMessageText": "Bạn có chắc chắn muốn hủy nhiệm vụ đang chờ xử lý này không?",
|
||||
"CancelPendingTask": "Bạn có chắc chắn muốn hủy nhiệm vụ đang chờ xử lý này không?",
|
||||
"CertificateValidation": "Xác thực chứng chỉ",
|
||||
"CertificateValidationHelpText": "Thay đổi cách xác thực chứng chỉ HTTPS nghiêm ngặt. Không được đổi nếu bạn biết rõ về",
|
||||
"ChangeFileDate": "Thay đổi ngày tệp",
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
"Calendar": "日历",
|
||||
"CalendarWeekColumnHeaderHelpText": "当使用周视图时显示上面的每一列",
|
||||
"Cancel": "取消",
|
||||
"CancelMessageText": "您确定要取消这个挂起的任务吗?",
|
||||
"CancelPendingTask": "您确定要取消这个挂起的任务吗?",
|
||||
"CertificateValidation": "验证证书",
|
||||
"CertificateValidationHelpText": "改变HTTPS证书验证的严格程度。不要更改除非您了解风险。",
|
||||
"ChangeFileDate": "修改文件日期",
|
||||
|
|
|
|||
|
|
@ -7,5 +7,7 @@ public class ApplicationUpdateCheckCommand : Command
|
|||
public override bool SendUpdatesToClient => true;
|
||||
|
||||
public override string CompletionMessage => null;
|
||||
|
||||
public bool InstallMajorUpdate { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ namespace NzbDrone.Core.Update.Commands
|
|||
{
|
||||
public class ApplicationUpdateCommand : Command
|
||||
{
|
||||
public bool InstallMajorUpdate { get; set; }
|
||||
public override bool SendUpdatesToClient => true;
|
||||
public override bool IsExclusive => true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
namespace NzbDrone.Core.Update
|
||||
{
|
||||
public class InstallUpdateService : IExecute<ApplicationUpdateCheckCommand>, IExecute<ApplicationUpdateCommand>, IHandle<ApplicationStartingEvent>
|
||||
public class InstallUpdateService : IExecute<ApplicationUpdateCommand>, IExecute<ApplicationUpdateCheckCommand>, IHandle<ApplicationStartingEvent>
|
||||
{
|
||||
private readonly ICheckUpdateService _checkUpdateService;
|
||||
private readonly Logger _logger;
|
||||
|
|
@ -232,7 +232,7 @@ private void EnsureAppDataSafety()
|
|||
}
|
||||
}
|
||||
|
||||
private UpdatePackage GetUpdatePackage(CommandTrigger updateTrigger)
|
||||
private UpdatePackage GetUpdatePackage(CommandTrigger updateTrigger, bool installMajorUpdate)
|
||||
{
|
||||
_logger.ProgressDebug("Checking for updates");
|
||||
|
||||
|
|
@ -244,7 +244,13 @@ private UpdatePackage GetUpdatePackage(CommandTrigger updateTrigger)
|
|||
return null;
|
||||
}
|
||||
|
||||
if (OsInfo.IsNotWindows && !_configFileProvider.UpdateAutomatically && updateTrigger != CommandTrigger.Manual)
|
||||
if (latestAvailable.Version.Major > BuildInfo.Version.Major && !installMajorUpdate)
|
||||
{
|
||||
_logger.ProgressInfo("Unable to install major update, please update update manually from System: Updates");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!_configFileProvider.UpdateAutomatically && updateTrigger != CommandTrigger.Manual)
|
||||
{
|
||||
_logger.ProgressDebug("Auto-update not enabled, not installing available update.");
|
||||
return null;
|
||||
|
|
@ -273,7 +279,7 @@ private UpdatePackage GetUpdatePackage(CommandTrigger updateTrigger)
|
|||
|
||||
public void Execute(ApplicationUpdateCheckCommand message)
|
||||
{
|
||||
if (GetUpdatePackage(message.Trigger) != null)
|
||||
if (GetUpdatePackage(message.Trigger, true) != null)
|
||||
{
|
||||
_commandQueueManager.Push(new ApplicationUpdateCommand(), trigger: message.Trigger);
|
||||
}
|
||||
|
|
@ -281,7 +287,7 @@ public void Execute(ApplicationUpdateCheckCommand message)
|
|||
|
||||
public void Execute(ApplicationUpdateCommand message)
|
||||
{
|
||||
var latestAvailable = GetUpdatePackage(message.Trigger);
|
||||
var latestAvailable = GetUpdatePackage(message.Trigger, message.InstallMajorUpdate);
|
||||
|
||||
if (latestAvailable != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Core.Update
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ public UpdatePackage GetLatestUpdate(string branch, Version currentVersion)
|
|||
.AddQueryParam("runtime", "netcore")
|
||||
.AddQueryParam("runtimeVer", _platformInfo.Version)
|
||||
.AddQueryParam("dbType", _mainDatabase.DatabaseType)
|
||||
.AddQueryParam("includeMajorVersion", true)
|
||||
.SetSegment("branch", branch);
|
||||
|
||||
if (_analyticsService.IsEnabled)
|
||||
|
|
|
|||
Loading…
Reference in a new issue