🐛 Media Session widget jellyfin sessions + translations (#1353)

This commit is contained in:
Tagaishi
2023-09-03 22:24:27 +02:00
committed by GitHub
parent de349014d5
commit 624e2a3a2c
5 changed files with 87 additions and 112 deletions

View File

@@ -31,28 +31,5 @@
"request": "request...", "request": "request...",
"approved": "Request was approved!", "approved": "Request was approved!",
"declined": "Request was declined!" "declined": "Request was declined!"
},
"detail": {
"label": "Stats for nerds",
"id": "ID",
"device": "Device",
"video": {
"video":"Video",
"resolution": "Resolution",
"framerate": "Framerate",
"codec": "Video Codec"
},
"audio": {
"audio": "Audio",
"channels": "Audio Channels",
"codec": "Audio Codec"
},
"transcoding": {
"transcoding": "Transcoding",
"context": "Context",
"requested": "Hardware Encoding Requested",
"source": "Source Codec",
"target": "Target Codec"
}
} }
} }

View File

@@ -100,13 +100,15 @@ const handleServer = async (app: ConfigAppType): Promise<GenericMediaServer | un
const infoApi = await getSystemApi(api).getPublicSystemInfo(); const infoApi = await getSystemApi(api).getPublicSystemInfo();
await api.authenticateUserByName(username, password); await api.authenticateUserByName(username, password);
const sessionApi = await getSessionApi(api); const sessionApi = await getSessionApi(api);
const sessions = await sessionApi.getSessions(); const { data: sessions } = await sessionApi.getSessions();
return { return {
type: 'jellyfin', type: 'jellyfin',
appId: app.id, appId: app.id,
serverAddress: app.url, serverAddress: app.url,
version: infoApi.data.Version ?? undefined, version: infoApi.data.Version ?? undefined,
sessions: sessions.data.map( sessions: sessions
.filter((session) => session.NowPlayingItem)
.map(
(session): GenericSessionInfo => ({ (session): GenericSessionInfo => ({
id: session.Id ?? '?', id: session.Id ?? '?',
username: session.UserName ?? undefined, username: session.UserName ?? undefined,

View File

@@ -1,30 +1,29 @@
import { Card, Divider, Flex, Grid, Group, Text } from '@mantine/core'; import { Card, Divider, Flex, Grid, Group, Text } from '@mantine/core';
import { IconDeviceMobile, IconId } from '@tabler/icons-react'; import { IconDeviceMobile, IconId } from '@tabler/icons-react';
import { useTranslation } from 'react-i18next';
import { GenericSessionInfo } from '../../types/api/media-server/session-info'; import { GenericSessionInfo } from '../../types/api/media-server/session-info';
import { useTranslation } from 'react-i18next';
export const DetailCollapseable = ({ session }: { session: GenericSessionInfo }) => { export const DetailCollapseable = ({ session }: { session: GenericSessionInfo }) => {
let details: { title: string; metrics: { name: string; value: string | undefined }[] }[] = []; let details: { title: string; metrics: { name: string; value: string | undefined }[] }[] = [];
const { t } = useTranslation('modules/media-server-list');
if (session.currentlyPlaying) { if (session.currentlyPlaying) {
if (session.currentlyPlaying.metadata.video) { if (session.currentlyPlaying.metadata.video) {
details = [ details = [
...details, ...details,
{ {
title: t('detail.video.'), title: "Video",
metrics: [ metrics: [
{ {
name: t('detail.video.resolution'), name: "Resolution",
value: `${session.currentlyPlaying.metadata.video.width}x${session.currentlyPlaying.metadata.video.height}`, value: `${session.currentlyPlaying.metadata.video.width}x${session.currentlyPlaying.metadata.video.height}`,
}, },
{ {
name: t('detail.video.framerate'), name: "Framerate",
value: session.currentlyPlaying.metadata.video.videoFrameRate, value: session.currentlyPlaying.metadata.video.videoFrameRate,
}, },
{ {
name: t('detail.video.codec'), name: "Video Codec",
value: session.currentlyPlaying.metadata.video.videoCodec, value: session.currentlyPlaying.metadata.video.videoCodec,
}, },
{ {
@@ -41,14 +40,14 @@ export const DetailCollapseable = ({ session }: { session: GenericSessionInfo })
details = [ details = [
...details, ...details,
{ {
title: t('detail.audio.audio'), title: "Audio",
metrics: [ metrics: [
{ {
name: t('detail.audio.channels'), name: "Audio Channels",
value: `${session.currentlyPlaying.metadata.audio.audioChannels}`, value: `${session.currentlyPlaying.metadata.audio.audioChannels}`,
}, },
{ {
name: t('detail.audio.codec'), name: "Audio Codec",
value: session.currentlyPlaying.metadata.audio.audioCodec, value: session.currentlyPlaying.metadata.audio.audioCodec,
}, },
], ],
@@ -60,24 +59,24 @@ export const DetailCollapseable = ({ session }: { session: GenericSessionInfo })
details = [ details = [
...details, ...details,
{ {
title: t('detail.transcoding.transcoding'), title: "Transcoding",
metrics: [ metrics: [
{ {
name: t('detail.video.resolution'), name: "Resolution",
value: `${session.currentlyPlaying.metadata.transcoding.width}x${session.currentlyPlaying.metadata.transcoding.height}`, value: `${session.currentlyPlaying.metadata.transcoding.width}x${session.currentlyPlaying.metadata.transcoding.height}`,
}, },
{ {
name: t('detail.transcoding.context'), name: "Context",
value: session.currentlyPlaying.metadata.transcoding.context, value: session.currentlyPlaying.metadata.transcoding.context,
}, },
{ {
name: t('detail.transcoding.requested'), name: "Hardware Encoding Requested",
value: session.currentlyPlaying.metadata.transcoding.transcodeHwRequested value: session.currentlyPlaying.metadata.transcoding.transcodeHwRequested
? 'yes' ? 'yes'
: 'no', : 'no',
}, },
{ {
name: t('detail.transcoding.source'), name: "Source Codec",
value: value:
session.currentlyPlaying.metadata.transcoding.sourceAudioCodec || session.currentlyPlaying.metadata.transcoding.sourceAudioCodec ||
session.currentlyPlaying.metadata.transcoding.sourceVideoCodec session.currentlyPlaying.metadata.transcoding.sourceVideoCodec
@@ -85,7 +84,7 @@ export const DetailCollapseable = ({ session }: { session: GenericSessionInfo })
: undefined, : undefined,
}, },
{ {
name: t('detail.transcoding.target'), name: "Target Codec",
value: `${session.currentlyPlaying.metadata.transcoding.videoCodec} ${session.currentlyPlaying.metadata.transcoding.audioCodec}`, value: `${session.currentlyPlaying.metadata.transcoding.videoCodec} ${session.currentlyPlaying.metadata.transcoding.audioCodec}`,
}, },
], ],
@@ -99,19 +98,19 @@ export const DetailCollapseable = ({ session }: { session: GenericSessionInfo })
<Flex justify="space-between" mb="xs"> <Flex justify="space-between" mb="xs">
<Group> <Group>
<IconId size={16} /> <IconId size={16} />
<Text>{t('detail.id')}</Text> <Text>ID</Text>
</Group> </Group>
<Text>{session.id}</Text> <Text>{session.id}</Text>
</Flex> </Flex>
<Flex justify="space-between" mb="md"> <Flex justify="space-between" mb="md">
<Group> <Group>
<IconDeviceMobile size={16} /> <IconDeviceMobile size={16} />
<Text>{t('detail.device')}</Text> <Text>Device</Text>
</Group> </Group>
<Text>{session.sessionName}</Text> <Text>{session.sessionName}</Text>
</Flex> </Flex>
{details.length > 0 && ( {details.length > 0 && (
<Divider label={t('detail.label')} labelPosition="center" mt="lg" mb="sm" /> <Divider label={"Stats for nerds"} labelPosition="center" mt="lg" mb="sm" />
)} )}
<Grid> <Grid>
{details.map((detail, index) => ( {details.map((detail, index) => (

View File

@@ -13,12 +13,11 @@ import { IconAlertTriangle, IconMovie } from '@tabler/icons-react';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { AppAvatar } from '../../components/AppAvatar'; import { AppAvatar } from '../../components/AppAvatar';
import { useEditModeStore } from '../../components/Dashboard/Views/useEditModeStore';
import { useConfigContext } from '../../config/provider'; import { useConfigContext } from '../../config/provider';
import { useGetMediaServers } from './useGetMediaServers';
import { defineWidget } from '../helper'; import { defineWidget } from '../helper';
import { IWidget } from '../widgets'; import { IWidget } from '../widgets';
import { TableRow } from './TableRow'; import { TableRow } from './TableRow';
import { useGetMediaServers } from './useGetMediaServers';
const definition = defineWidget({ const definition = defineWidget({
id: 'media-server', id: 'media-server',
@@ -71,7 +70,7 @@ function MediaServerTile({ widget }: MediaServerWidgetProps) {
<Loader /> <Loader />
<Stack align="center" spacing={0}> <Stack align="center" spacing={0}>
<Text>{t('descriptor.name')}</Text> <Text>{t('descriptor.name')}</Text>
<Text color="dimmed">{t('descriptor.loading')}</Text> <Text color="dimmed">{t('loading')}</Text>
</Stack> </Stack>
</Stack> </Stack>
); );
@@ -79,7 +78,7 @@ function MediaServerTile({ widget }: MediaServerWidgetProps) {
return ( return (
<Stack h="100%"> <Stack h="100%">
<ScrollArea offsetScrollbars> <ScrollArea offsetScrollbars h="100%">
<Table highlightOnHover> <Table highlightOnHover>
<thead> <thead>
<tr> <tr>
@@ -99,7 +98,7 @@ function MediaServerTile({ widget }: MediaServerWidgetProps) {
</Table> </Table>
</ScrollArea> </ScrollArea>
<Group position="right" mt="auto"> <Group pos="absolute" bottom="15" right="15" mt="auto">
<Avatar.Group> <Avatar.Group>
{data?.servers.map((server) => { {data?.servers.map((server) => {
const app = config?.apps.find((x) => x.id === server.appId); const app = config?.apps.find((x) => x.id === server.appId);

View File

@@ -1,24 +1,21 @@
import { Flex, Stack, Text } from '@mantine/core'; import { Flex, Stack, Text } from '@mantine/core';
import { import {
Icon,
IconDeviceTv, IconDeviceTv,
IconHeadphones, IconHeadphones,
IconMovie, IconMovie,
IconQuestionMark, IconQuestionMark,
IconVideo, IconVideo,
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { useTranslation } from 'next-i18next';
import { GenericSessionInfo } from '../../types/api/media-server/session-info'; import { GenericSessionInfo } from '../../types/api/media-server/session-info';
export const NowPlayingDisplay = ({ session }: { session: GenericSessionInfo }) => { export const NowPlayingDisplay = ({ session }: { session: GenericSessionInfo }) => {
const { t } = useTranslation();
if (!session.currentlyPlaying) { if (!session.currentlyPlaying) {
return null; return null;
} }
const Icon = (): Icon => { const IconSelector = () => {
switch (session.currentlyPlaying?.type) { switch (session.currentlyPlaying?.type) {
case 'audio': case 'audio':
return IconHeadphones; return IconHeadphones;
@@ -33,11 +30,12 @@ export const NowPlayingDisplay = ({ session }: { session: GenericSessionInfo })
} }
}; };
const Test = Icon(); const Icon = IconSelector();
return ( return (
<Flex wrap="nowrap" gap="sm" align="center"> <Flex wrap="nowrap" gap="sm" align="center">
<Test size={16} /> <Icon size={16} />
<Stack spacing={0} w={200}> <Stack spacing={0}>
<Text lineClamp={1}>{session.currentlyPlaying.name}</Text> <Text lineClamp={1}>{session.currentlyPlaying.name}</Text>
{session.currentlyPlaying.albumName ? ( {session.currentlyPlaying.albumName ? (
@@ -46,7 +44,7 @@ export const NowPlayingDisplay = ({ session }: { session: GenericSessionInfo })
</Text> </Text>
) : ( ) : (
session.currentlyPlaying.seasonName && ( session.currentlyPlaying.seasonName && (
<Text lineClamp={2} color="dimmed" size="xs"> <Text lineClamp={1} color="dimmed" size="xs">
{session.currentlyPlaying.seasonName} - {session.currentlyPlaying.episodeName} {session.currentlyPlaying.seasonName} - {session.currentlyPlaying.episodeName}
</Text> </Text>
) )