-
-
Notifications
You must be signed in to change notification settings - Fork 296
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
✨ Add Tdarr integration and widget #1882
Changes from 12 commits
fcbaad1
de39757
38acef5
a27d786
64d982d
a3a5e90
89e8022
66e28b4
e9afdac
5eddda5
050f8fa
45b1c76
e4d34b4
7195bb1
8a0b4a8
2f304e5
7850471
2c7b92f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
{ | ||
"descriptor": { | ||
"name": "Tdarr Queue", | ||
"description": "Displays the queue of an external Tdarr instance.", | ||
"settings": { | ||
"title": "Tdarr Queue Settings", | ||
"appId": { | ||
"label": "Select an app" | ||
}, | ||
"defaultView": { | ||
"label": "Default view", | ||
"data": { | ||
"workers": "Workers", | ||
"queue": "Queue", | ||
"statistics": "Statistics" | ||
} | ||
}, | ||
"showHealthCheck": { | ||
"label": "Show Health Check indicator" | ||
}, | ||
"showHealthChecksInQueue": { | ||
"label": "Show Health Checks in queue" | ||
}, | ||
"queuePageSize": { | ||
"label": "Queue: Items per page" | ||
}, | ||
"showAppIcon": { | ||
"label": "Show app icon in the bottom right corner" | ||
} | ||
} | ||
}, | ||
"noAppSelected": "Please select an app in the widget settings", | ||
"views": { | ||
"workers": { | ||
"table": { | ||
"header": { | ||
"name": "File", | ||
"eta": "ETA", | ||
"progress": "Progress" | ||
}, | ||
"empty": "Empty", | ||
"tooltip": { | ||
"transcode": "Transcode", | ||
"healthCheck": "Health Check" | ||
} | ||
} | ||
}, | ||
"queue": { | ||
"table": { | ||
"header": { | ||
"name": "File", | ||
"size": "Size" | ||
}, | ||
"footer": { | ||
"currentIndex": "{{start}}-{{end}} of {{total}}" | ||
}, | ||
"empty": "Empty", | ||
"tooltip": { | ||
"transcode": "Transcode", | ||
"healthCheck": "Health Check" | ||
} | ||
} | ||
}, | ||
"statistics": { | ||
"empty": "Empty", | ||
"box": { | ||
"transcodes": "Transcodes: {{value}}", | ||
"healthChecks": "Health Checks: {{value}}", | ||
"files": "Files: {{value}}", | ||
"spaceSaved": "Saved: {{value}}" | ||
}, | ||
"pies": { | ||
"transcodes": "Transcodes", | ||
"healthChecks": "Health Checks", | ||
"videoCodecs": "Codecs", | ||
"videoContainers": "Containers", | ||
"videoResolutions": "Resolutions" | ||
} | ||
} | ||
}, | ||
"error": { | ||
"title": "Error", | ||
"message": "An error occurred while fetching data from Tdarr." | ||
}, | ||
"tabs": { | ||
"workers": "Workers", | ||
"queue": "Queue", | ||
"statistics": "Statistics" | ||
}, | ||
"healthCheckStatus": { | ||
"title": "Health Check", | ||
"queued": "Queued", | ||
"healthy": "Healthy", | ||
"unhealthy": "Unhealthy" | ||
} | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea behind the app selector is nice, but I'm not sure it's the place to do it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should not be too opinionated about peoples setups, especially seeing as Homarr is an application aimed at users with a certain degree of technical prowess. There might be edge cases or situations in which two Tdarr apps might be viable. But I will change it so the first app is preselected by default. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's why I still mentioned the first point first. Even if people should not do that, in the case they do, it'll be an option in the future. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { Group, Select, Text } from '@mantine/core'; | ||
import { ComponentProps, forwardRef } from 'react'; | ||
import { AppAvatar } from '~/components/AppAvatar'; | ||
import { useConfigContext } from '~/config/provider'; | ||
import { IntegrationType } from '~/types/app'; | ||
|
||
export type AppSelectorProps = { | ||
value: string; | ||
onChange: (value: string) => void; | ||
integrations: IntegrationType[]; | ||
selectProps?: Omit<ComponentProps<typeof Select>, 'value' | 'data' | 'onChange'>; | ||
}; | ||
|
||
export function AppSelector(props: AppSelectorProps) { | ||
const { value, integrations, onChange } = props; | ||
const { config } = useConfigContext(); | ||
|
||
const apps = | ||
config?.apps.filter( | ||
(app) => app.integration.type && integrations.includes(app.integration.type) | ||
) ?? []; | ||
const selectedApp = apps.find((app) => app.id === value); | ||
|
||
return ( | ||
<Select | ||
value={value} | ||
data={apps.map((app) => ({ | ||
value: app.id, | ||
label: app.name, | ||
}))} | ||
onChange={onChange} | ||
icon={selectedApp ? <AppAvatar iconUrl={selectedApp?.appearance.iconUrl} /> : undefined} | ||
itemComponent={forwardRef(({ value, label, ...rest }, ref) => { | ||
const app = apps.find((app) => app.id === value); | ||
|
||
if (!app) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<Group ref={ref} {...rest}> | ||
<AppAvatar iconUrl={app.appearance.iconUrl} /> | ||
<Text size="xs">{label}</Text> | ||
</Group> | ||
); | ||
})} | ||
nothingFound="No apps found" | ||
/> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ import { ContextModalProps } from '@mantine/modals'; | |
import { IconAlertTriangle, IconPlaylistX, IconPlus } from '@tabler/icons-react'; | ||
import { Trans, useTranslation } from 'next-i18next'; | ||
import { FC, useState } from 'react'; | ||
import { AppSelector } from '~/components/Dashboard/Tiles/Widgets/Inputs/AppSelector'; | ||
import { useConfigContext } from '~/config/provider'; | ||
import { useConfigStore } from '~/config/store'; | ||
import { mapObject } from '~/tools/client/objects'; | ||
|
@@ -85,7 +86,7 @@ export const WidgetsEditModal = ({ | |
|
||
return ( | ||
<Stack> | ||
{items.map(([key, _], index) => { | ||
{items.map(([key], index) => { | ||
const option = (currentWidgetDefinition as any).options[key] as IWidgetOptionValue; | ||
const value = moduleProperties[key] ?? option.defaultValue; | ||
|
||
|
@@ -385,6 +386,28 @@ const WidgetOptionTypeSwitch: FC<{ | |
</Flex> | ||
</Stack> | ||
); | ||
|
||
case 'app-select': | ||
return ( | ||
<Stack spacing={0}> | ||
<Group align="center" spacing="sm"> | ||
<Text size="0.875rem" weight="500"> | ||
{t(`descriptor.settings.${key}.label`)} | ||
</Text> | ||
{info && <InfoCard message={t(`descriptor.settings.${key}.info`)} link={link} />} | ||
</Group> | ||
<AppSelector | ||
value={value} | ||
onChange={(v) => handleChange(key, v ?? option.defaultValue)} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rename to event There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The parameter is the new value, not an event. I'll rename it to |
||
integrations={option.integrations} | ||
selectProps={{ | ||
withinPortal: true, | ||
...option.inputProps, | ||
}} | ||
/> | ||
</Stack> | ||
); | ||
|
||
/* eslint-enable no-case-declarations */ | ||
default: | ||
return null; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to be modifying this file but it's not that important.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That one slipped through the cracks while I was reverting the formatting