Enhancement: Show username in VOD cards.

This commit is contained in:
SergeantPanda 2026-03-28 16:46:06 -05:00
parent 30246a4d49
commit 38f5156ca6
3 changed files with 30 additions and 8 deletions

View file

@ -19,8 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Connection cards on the Stats page now show the **username** of the connected user in a new User column (between IP Address and Connected). The username is resolved from the user store using the `user_id` stored in Redis client metadata; unauthenticated connections display "Anonymous". (Closes #766, Closes #586)
- `ip_address` and `user_id` were not included in the client info returned by `get_detailed_channel_info()` despite being available in the Redis hash. Both fields are now extracted and returned.
- Connection cards on the Stats page now show the **username** of the connected user. For live channel connections a new User column appears between IP Address and Connected; for VOD connections the username is shown inline next to the IP address in the Client summary row. The username is resolved from the user store using the `user_id` stored in Redis client metadata. (Closes #766, Closes #586)
- `ip_address` and `user_id` were not included in the client info returned by `get_detailed_channel_info()` despite being available in the Redis hash. Both fields are now extracted and returned. `user_id` is now also included in the VOD stats response.
- Donate button added to the sidebar footer. A heart icon links to the project's Open Collective page, visible in both expanded and collapsed states. Hovering shows a "Support Dispatcharr" tooltip. The version string is also now clickable to copy it to the clipboard.
- User stream limits: administrators can now set a maximum number of concurrent streams per user account. When a user reaches their limit, the system can automatically terminate an existing stream to free a slot based on configurable rules. Limit enforcement applies to both live channels and VOD. (Closes #544)
- Each user account has a new **Stream Limit** field (0 = unlimited) configurable from the user edit form in Settings → Users.

View file

@ -949,6 +949,7 @@ class VODStatsView(View):
'm3u_profile': m3u_profile_info,
'client_id': client_id,
'client_ip': combined_data.get('client_ip', 'Unknown'),
'user_id': combined_data.get('user_id', '0'),
'user_agent': combined_data.get('client_user_agent', 'Unknown'),
'connected_at': combined_data.get('created_at'),
'last_activity': combined_data.get('last_activity'),

View file

@ -1,5 +1,5 @@
// Format duration for content length
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import logo from '../../images/logo.png';
import {
ActionIcon,
@ -38,6 +38,7 @@ import {
getMovieDisplayTitle,
getMovieSubtitle,
} from '../../utils/cards/VodConnectionCardUtils.js';
import useUsersStore from '../../store/users.jsx';
const ClientDetails = ({ connection, connectionStartTime }) => {
return (
@ -142,7 +143,10 @@ const ClientDetails = ({ connection, connectionStartTime }) => {
};
const ConnectionProgress = ({ connection, durationSecs }) => {
const { totalTime, currentTime, percentage } = calculateProgress(connection, durationSecs);
const { totalTime, currentTime, percentage } = calculateProgress(
connection,
durationSecs
);
return totalTime > 0 ? (
<Stack gap="xs" mt="sm">
<Group justify="space-between" align="center">
@ -172,6 +176,14 @@ const ConnectionProgress = ({ connection, durationSecs }) => {
const VodConnectionCard = ({ vodContent, stopVODClient }) => {
const { fullDateTimeFormat } = useDateTimeFormat();
const [isClientExpanded, setIsClientExpanded] = useState(false);
const users = useUsersStore((s) => s.users);
const usersMap = useMemo(() => {
const map = {};
users.forEach((u) => {
map[String(u.id)] = u.username;
});
return map;
}, [users]);
const [, setUpdateTrigger] = useState(0); // Force re-renders for progress updates
// Get metadata from the VOD content
@ -377,13 +389,12 @@ const VodConnectionCard = ({ vodContent, stopVODClient }) => {
</Group>
{/* Progress bar - show current position in content */}
{connection &&
metadata.duration_secs &&
{connection && metadata.duration_secs && (
<ConnectionProgress
connection={connection}
durationSecs={metadata.duration_secs}
/>
}
)}
{/* Client information section - collapsible like channel cards */}
{connection && (
@ -403,11 +414,21 @@ const VodConnectionCard = ({ vodContent, stopVODClient }) => {
>
<Group gap={8}>
<Text size="sm" fw={500} color="dimmed">
Client:
Client IP:
</Text>
<Text size="sm" ff={'monospace'}>
{connection.client_ip || 'Unknown IP'}
</Text>
{usersMap[String(connection.user_id)] && (
<>
<Text size="sm" c="dimmed">
User:
</Text>
<Text size="sm">
{usersMap[String(connection.user_id)]}
</Text>
</>
)}
</Group>
<Group gap={8}>