Add showEpisodeNumber and expandOneStreamToTwoRows configuration options to Tautulli and Emby/Jellyfin widgets
This commit is contained in:
parent
f4fc30cd9f
commit
c56dbeeebb
@ -17,4 +17,6 @@ widget:
|
|||||||
enableBlocks: true # optional, defaults to false
|
enableBlocks: true # optional, defaults to false
|
||||||
enableNowPlaying: true # optional, defaults to true
|
enableNowPlaying: true # optional, defaults to true
|
||||||
enableUser: true # optional, defaults to false
|
enableUser: true # optional, defaults to false
|
||||||
|
showEpisodeNumber: true # optional, defaults to false
|
||||||
|
expandOneStreamToTwoRows: false # optional, defaults to true
|
||||||
```
|
```
|
||||||
|
|||||||
@ -16,4 +16,7 @@ widget:
|
|||||||
key: apikeyapikeyapikeyapikeyapikey
|
key: apikeyapikeyapikeyapikeyapikey
|
||||||
enableBlocks: true # optional, defaults to false
|
enableBlocks: true # optional, defaults to false
|
||||||
enableNowPlaying: true # optional, defaults to true
|
enableNowPlaying: true # optional, defaults to true
|
||||||
|
enableUser: true # optional, defaults to false
|
||||||
|
showEpisodeNumber: true # optional, defaults to false
|
||||||
|
expandOneStreamToTwoRows: false # optional, defaults to true
|
||||||
```
|
```
|
||||||
|
|||||||
@ -15,4 +15,6 @@ widget:
|
|||||||
url: http://tautulli.host.or.ip
|
url: http://tautulli.host.or.ip
|
||||||
key: apikeyapikeyapikeyapikeyapikey
|
key: apikeyapikeyapikeyapikeyapikey
|
||||||
enableUser: true # optional, defaults to false
|
enableUser: true # optional, defaults to false
|
||||||
|
showEpisodeNumber: true # optional, defaults to false
|
||||||
|
expandOneStreamToTwoRows: false # optional, defaults to true
|
||||||
```
|
```
|
||||||
|
|||||||
@ -395,6 +395,8 @@ export function cleanServiceGroups(groups) {
|
|||||||
|
|
||||||
// emby, jellyfin, tautulli
|
// emby, jellyfin, tautulli
|
||||||
enableUser,
|
enableUser,
|
||||||
|
expandOneStreamToTwoRows,
|
||||||
|
showEpisodeNumber,
|
||||||
|
|
||||||
// glances, pihole
|
// glances, pihole
|
||||||
version,
|
version,
|
||||||
@ -520,11 +522,10 @@ export function cleanServiceGroups(groups) {
|
|||||||
if (["emby", "jellyfin"].includes(type)) {
|
if (["emby", "jellyfin"].includes(type)) {
|
||||||
if (enableBlocks !== undefined) cleanedService.widget.enableBlocks = JSON.parse(enableBlocks);
|
if (enableBlocks !== undefined) cleanedService.widget.enableBlocks = JSON.parse(enableBlocks);
|
||||||
if (enableNowPlaying !== undefined) cleanedService.widget.enableNowPlaying = JSON.parse(enableNowPlaying);
|
if (enableNowPlaying !== undefined) cleanedService.widget.enableNowPlaying = JSON.parse(enableNowPlaying);
|
||||||
if (enableUser !== undefined) {
|
|
||||||
cleanedService.widget.enableUser = !!JSON.parse(enableUser);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (["tautulli"].includes(type)) {
|
if (["emby", "jellyfin", "tautulli"].includes(type)) {
|
||||||
|
if (expandOneStreamToTwoRows !== undefined) cleanedService.widget.expandOneStreamToTwoRows = !!JSON.parse(expandOneStreamToTwoRows);
|
||||||
|
if (showEpisodeNumber !== undefined) cleanedService.widget.showEpisodeNumber = !!JSON.parse(showEpisodeNumber);
|
||||||
if (enableUser !== undefined) {
|
if (enableUser !== undefined) {
|
||||||
cleanedService.widget.enableUser = !!JSON.parse(enableUser);
|
cleanedService.widget.enableUser = !!JSON.parse(enableUser);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,9 +27,20 @@ function ticksToString(ticks) {
|
|||||||
return parts.map((part) => part.toString().padStart(2, "0")).join(":");
|
return parts.map((part) => part.toString().padStart(2, "0")).join(":");
|
||||||
}
|
}
|
||||||
|
|
||||||
function SingleSessionEntry({ playCommand, session, enableUser }) {
|
function generateSeriesTitle(session, showEpisodeNumber) {
|
||||||
|
const {
|
||||||
|
NowPlayingItem: { Name, SeriesName, Type, ParentIndexNumber, IndexNumber },
|
||||||
|
} = session;
|
||||||
|
|
||||||
|
if (Type === "Episode" && showEpisodeNumber) {
|
||||||
|
return `${SeriesName} - S${ParentIndexNumber.toString().padStart(2, "0")}E${IndexNumber.toString().padStart(2, "0")} - ${Name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${Name}${SeriesName ? ` - ${SeriesName}` : ""}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SingleSessionEntry({ playCommand, session, enableUser, showEpisodeNumber }) {
|
||||||
const {
|
const {
|
||||||
NowPlayingItem: { Name, SeriesName },
|
|
||||||
PlayState: { PositionTicks, IsPaused, IsMuted },
|
PlayState: { PositionTicks, IsPaused, IsMuted },
|
||||||
UserName,
|
UserName,
|
||||||
} = session;
|
} = session;
|
||||||
@ -43,13 +54,13 @@ function SingleSessionEntry({ playCommand, session, enableUser }) {
|
|||||||
|
|
||||||
const percent = Math.min(1, PositionTicks / RunTimeTicks) * 100;
|
const percent = Math.min(1, PositionTicks / RunTimeTicks) * 100;
|
||||||
|
|
||||||
|
const seriesTitle = generateSeriesTitle(session, enableUser, showEpisodeNumber);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="text-theme-700 dark:text-theme-200 relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1 flex">
|
<div className="text-theme-700 dark:text-theme-200 relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1 flex">
|
||||||
<div className="grow text-xs z-10 self-center ml-2 relative w-full h-4 mr-2">
|
<div className="grow text-xs z-10 self-center ml-2 relative w-full h-4 mr-2">
|
||||||
<div className="absolute w-full whitespace-nowrap text-ellipsis overflow-hidden">
|
<div className="absolute w-full whitespace-nowrap text-ellipsis overflow-hidden">
|
||||||
{Name}
|
{seriesTitle}
|
||||||
{SeriesName && ` - ${SeriesName}`}
|
|
||||||
{enableUser && ` (${UserName})`}
|
{enableUser && ` (${UserName})`}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -99,9 +110,8 @@ function SingleSessionEntry({ playCommand, session, enableUser }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SessionEntry({ playCommand, session, enableUser }) {
|
function SessionEntry({ playCommand, session, enableUser, showEpisodeNumber }) {
|
||||||
const {
|
const {
|
||||||
NowPlayingItem: { Name, SeriesName },
|
|
||||||
PlayState: { PositionTicks, IsPaused, IsMuted },
|
PlayState: { PositionTicks, IsPaused, IsMuted },
|
||||||
UserName,
|
UserName,
|
||||||
} = session;
|
} = session;
|
||||||
@ -113,6 +123,8 @@ function SessionEntry({ playCommand, session, enableUser }) {
|
|||||||
IsVideoDirect: true,
|
IsVideoDirect: true,
|
||||||
}; // if no transcodinginfo its videodirect
|
}; // if no transcodinginfo its videodirect
|
||||||
|
|
||||||
|
const seriesTitle = generateSeriesTitle(session, enableUser, showEpisodeNumber);
|
||||||
|
|
||||||
const percent = Math.min(1, PositionTicks / RunTimeTicks) * 100;
|
const percent = Math.min(1, PositionTicks / RunTimeTicks) * 100;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -143,8 +155,7 @@ function SessionEntry({ playCommand, session, enableUser }) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="grow text-xs z-10 self-center relative w-full h-4">
|
<div className="grow text-xs z-10 self-center relative w-full h-4">
|
||||||
<div className="absolute w-full whitespace-nowrap text-ellipsis overflow-hidden">
|
<div className="absolute w-full whitespace-nowrap text-ellipsis overflow-hidden">
|
||||||
{Name}
|
{seriesTitle}
|
||||||
{SeriesName && ` - ${SeriesName}`}
|
|
||||||
{enableUser && ` (${UserName})`}
|
{enableUser && ` (${UserName})`}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -219,7 +230,9 @@ export default function Component({ service }) {
|
|||||||
|
|
||||||
const enableBlocks = service.widget?.enableBlocks;
|
const enableBlocks = service.widget?.enableBlocks;
|
||||||
const enableNowPlaying = service.widget?.enableNowPlaying ?? true;
|
const enableNowPlaying = service.widget?.enableNowPlaying ?? true;
|
||||||
const enableUser = !!service.widget?.enableUser;
|
const enableUser = !!service.widget?.enableUser; // default is true
|
||||||
|
const expandOneStreamToTwoRows = service.widget?.expandOneStreamToTwoRows !== false; // default is true
|
||||||
|
const showEpisodeNumber = !!service.widget?.showEpisodeNumber; // default is false
|
||||||
|
|
||||||
if (!sessionsData || !countData) {
|
if (!sessionsData || !countData) {
|
||||||
return (
|
return (
|
||||||
@ -260,15 +273,17 @@ export default function Component({ service }) {
|
|||||||
<div className="text-theme-700 dark:text-theme-200 text-xs relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1">
|
<div className="text-theme-700 dark:text-theme-200 text-xs relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1">
|
||||||
<span className="absolute left-2 text-xs mt-[2px]">{t("emby.no_active")}</span>
|
<span className="absolute left-2 text-xs mt-[2px]">{t("emby.no_active")}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-theme-700 dark:text-theme-200 text-xs relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1">
|
{expandOneStreamToTwoRows && (
|
||||||
|
<div className="text-theme-700 dark:text-theme-200 text-xs relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1">
|
||||||
<span className="absolute left-2 text-xs mt-[2px]">-</span>
|
<span className="absolute left-2 text-xs mt-[2px]">-</span>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playing.length === 1) {
|
if (expandOneStreamToTwoRows && playing.length === 1) {
|
||||||
const session = playing[0];
|
const session = playing[0];
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -278,28 +293,29 @@ export default function Component({ service }) {
|
|||||||
playCommand={(currentSession, command) => handlePlayCommand(currentSession, command)}
|
playCommand={(currentSession, command) => handlePlayCommand(currentSession, command)}
|
||||||
session={session}
|
session={session}
|
||||||
enableUser={enableUser}
|
enableUser={enableUser}
|
||||||
|
showEpisodeNumber={showEpisodeNumber}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playing.length > 0)
|
return (
|
||||||
return (
|
<>
|
||||||
<>
|
{enableBlocks && <CountBlocks service={service} countData={countData} />}
|
||||||
{enableBlocks && <CountBlocks service={service} countData={countData} />}
|
<div className="flex flex-col pb-1 mx-1">
|
||||||
<div className="flex flex-col pb-1 mx-1">
|
{playing.map((session) => (
|
||||||
{playing.map((session) => (
|
<SessionEntry
|
||||||
<SessionEntry
|
key={session.Id}
|
||||||
key={session.Id}
|
playCommand={(currentSession, command) => handlePlayCommand(currentSession, command)}
|
||||||
playCommand={(currentSession, command) => handlePlayCommand(currentSession, command)}
|
session={session}
|
||||||
session={session}
|
enableUser={enableUser}
|
||||||
enableUser={enableUser}
|
showEpisodeNumber={showEpisodeNumber}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enableBlocks) {
|
if (enableBlocks) {
|
||||||
|
|||||||
@ -25,16 +25,34 @@ function millisecondsToString(milliseconds) {
|
|||||||
return parts.map((part) => part.toString().padStart(2, "0")).join(":");
|
return parts.map((part) => part.toString().padStart(2, "0")).join(":");
|
||||||
}
|
}
|
||||||
|
|
||||||
function SingleSessionEntry({ session, enableUser }) {
|
function generateStreamTitle(session, showEpisodeNumber) {
|
||||||
const { full_title, duration, view_offset, progress_percent, state, video_decision, audio_decision, username } =
|
const { media_type, parent_media_index, media_index, title, grandparent_title, full_title } = session;
|
||||||
session;
|
if (media_type === "episode" && showEpisodeNumber) {
|
||||||
|
return `${grandparent_title}: S${parent_media_index.toString().padStart(2, "0")} · E${media_index.toString().padStart(2, "0")} - ${title}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return full_title;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SingleSessionEntry({ session, enableUser, showEpisodeNumber }) {
|
||||||
|
const {
|
||||||
|
duration,
|
||||||
|
view_offset,
|
||||||
|
progress_percent,
|
||||||
|
state,
|
||||||
|
video_decision,
|
||||||
|
audio_decision,
|
||||||
|
username
|
||||||
|
} = session;
|
||||||
|
|
||||||
|
const stream_title = generateStreamTitle(session, showEpisodeNumber)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="text-theme-700 dark:text-theme-200 relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1 flex">
|
<div className="text-theme-700 dark:text-theme-200 relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1 flex">
|
||||||
<div className="text-xs z-10 self-center ml-2 relative w-full h-4 grow mr-2">
|
<div className="text-xs z-10 self-center ml-2 relative w-full h-4 grow mr-2">
|
||||||
<div className="absolute w-full whitespace-nowrap text-ellipsis overflow-hidden">
|
<div className="absolute w-full whitespace-nowrap text-ellipsis overflow-hidden">
|
||||||
{full_title}
|
{stream_title}
|
||||||
{enableUser && ` (${username})`}
|
{enableUser && ` (${username})`}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -78,8 +96,17 @@ function SingleSessionEntry({ session, enableUser }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SessionEntry({ session, enableUser }) {
|
function SessionEntry({ session, enableUser, showEpisodeNumber }) {
|
||||||
const { full_title, view_offset, progress_percent, state, video_decision, audio_decision, username } = session;
|
const {
|
||||||
|
view_offset,
|
||||||
|
progress_percent,
|
||||||
|
state,
|
||||||
|
video_decision,
|
||||||
|
audio_decision,
|
||||||
|
username
|
||||||
|
} = session;
|
||||||
|
|
||||||
|
const stream_title = generateStreamTitle(session, showEpisodeNumber)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="text-theme-700 dark:text-theme-200 relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1 flex">
|
<div className="text-theme-700 dark:text-theme-200 relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1 flex">
|
||||||
@ -99,7 +126,7 @@ function SessionEntry({ session, enableUser }) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="text-xs z-10 self-center ml-2 relative w-full h-4 grow mr-2">
|
<div className="text-xs z-10 self-center ml-2 relative w-full h-4 grow mr-2">
|
||||||
<div className="absolute w-full whitespace-nowrap text-ellipsis overflow-hidden">
|
<div className="absolute w-full whitespace-nowrap text-ellipsis overflow-hidden">
|
||||||
{full_title}
|
{stream_title}
|
||||||
{enableUser && ` (${username})`}
|
{enableUser && ` (${username})`}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -156,26 +183,31 @@ export default function Component({ service }) {
|
|||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const expandOneStreamToTwoRows = service.widget?.expandOneStreamToTwoRows !== false; // default is true
|
||||||
|
|
||||||
if (playing.length === 0) {
|
if (playing.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col pb-1 mx-1">
|
<div className="flex flex-col pb-1 mx-1">
|
||||||
<div className="text-theme-700 dark:text-theme-200 text-xs relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1">
|
<div className="text-theme-700 dark:text-theme-200 text-xs relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1">
|
||||||
<span className="absolute left-2 text-xs mt-[2px]">{t("tautulli.no_active")}</span>
|
<span className="absolute left-2 text-xs mt-[2px]">{t("tautulli.no_active")}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-theme-700 dark:text-theme-200 text-xs relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1">
|
{expandOneStreamToTwoRows && (
|
||||||
<span className="absolute left-2 text-xs mt-[2px]">-</span>
|
<div className="text-theme-700 dark:text-theme-200 text-xs relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1">
|
||||||
</div>
|
<span className="absolute left-2 text-xs mt-[2px]">-</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const enableUser = !!service.widget?.enableUser;
|
const enableUser = !!service.widget?.enableUser; // default is false
|
||||||
|
const showEpisodeNumber = !!service.widget?.showEpisodeNumber; // default is false
|
||||||
|
|
||||||
if (playing.length === 1) {
|
if (expandOneStreamToTwoRows && playing.length === 1) {
|
||||||
const session = playing[0];
|
const session = playing[0];
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col pb-1 mx-1">
|
<div className="flex flex-col pb-1 mx-1">
|
||||||
<SingleSessionEntry session={session} enableUser={enableUser} />
|
<SingleSessionEntry session={session} enableUser={enableUser} showEpisodeNumber={showEpisodeNumber} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -183,7 +215,7 @@ export default function Component({ service }) {
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col pb-1 mx-1">
|
<div className="flex flex-col pb-1 mx-1">
|
||||||
{playing.map((session) => (
|
{playing.map((session) => (
|
||||||
<SessionEntry key={session.Id} session={session} enableUser={enableUser} />
|
<SessionEntry key={session.Id} session={session} enableUser={enableUser} showEpisodeNumber={showEpisodeNumber} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user