import { useTranslation } from "next-i18next"; import { BsVolumeMuteFill, BsFillPlayFill, BsPauseFill, BsCpu, BsFillCpuFill } from "react-icons/bs"; import { MdOutlineSmartDisplay } from "react-icons/md"; import Block from "components/services/widget/block"; import Container from "components/services/widget/container"; import { formatProxyUrlWithSegments } from "utils/proxy/api-helpers"; import useWidgetAPI from "utils/proxy/use-widget-api"; function ticksToTime(ticks) { const milliseconds = ticks / 10000; const seconds = Math.floor((milliseconds / 1000) % 60); const minutes = Math.floor((milliseconds / (1000 * 60)) % 60); const hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24); return { hours, minutes, seconds }; } function ticksToString(ticks) { const { hours, minutes, seconds } = ticksToTime(ticks); const parts = []; if (hours > 0) { parts.push(hours); } parts.push(minutes); parts.push(seconds); return parts.map((part) => part.toString().padStart(2, "0")).join(":"); } function generateStreamTitle(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 { PlayState: { PositionTicks, IsPaused, IsMuted }, UserName, } = session; const RunTimeTicks = session.NowPlayingItem?.RunTimeTicks ?? session.NowPlayingItem?.CurrentProgram?.RunTimeTicks ?? 0; const { IsVideoDirect, VideoDecoderIsHardware, VideoEncoderIsHardware } = session?.TranscodingInfo || { IsVideoDirect: true, }; // if no transcodinginfo its videodirect const percent = Math.min(1, PositionTicks / RunTimeTicks) * 100; const streamTitle = generateStreamTitle(session, enableUser, showEpisodeNumber); return ( <>
{streamTitle} {enableUser && ` (${UserName})`}
{IsVideoDirect && } {!IsVideoDirect && (!VideoDecoderIsHardware || !VideoEncoderIsHardware) && } {!IsVideoDirect && VideoDecoderIsHardware && VideoEncoderIsHardware && ( )}
{IsPaused && ( { playCommand(session, "Unpause"); }} className="inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80" /> )} {!IsPaused && ( { playCommand(session, "Pause"); }} className="inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80" /> )}
{IsMuted && }
{ticksToString(PositionTicks)} / {ticksToString(RunTimeTicks)}
); } function SessionEntry({ playCommand, session, enableUser, showEpisodeNumber }) { const { PlayState: { PositionTicks, IsPaused, IsMuted }, UserName, } = session; const RunTimeTicks = session.NowPlayingItem?.RunTimeTicks ?? session.NowPlayingItem?.CurrentProgram?.RunTimeTicks ?? 0; const { IsVideoDirect, VideoDecoderIsHardware, VideoEncoderIsHardware } = session?.TranscodingInfo || { IsVideoDirect: true, }; // if no transcodinginfo its videodirect const streamTitle = generateStreamTitle(session, enableUser, showEpisodeNumber); const percent = Math.min(1, PositionTicks / RunTimeTicks) * 100; return (
{IsPaused && ( { playCommand(session, "Unpause"); }} className="inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80" /> )} {!IsPaused && ( { playCommand(session, "Pause"); }} className="inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80" /> )}
{streamTitle} {enableUser && ` (${UserName})`}
{IsMuted && }
{ticksToString(PositionTicks)}
{IsVideoDirect && } {!IsVideoDirect && (!VideoDecoderIsHardware || !VideoEncoderIsHardware) && } {!IsVideoDirect && VideoDecoderIsHardware && VideoEncoderIsHardware && }
); } function CountBlocks({ service, countData }) { const { t } = useTranslation(); // allows filtering // eslint-disable-next-line no-param-reassign if (service.widget?.type === "jellyfin") service.widget.type = "emby"; if (!countData) { return ( ); } return ( ); } export default function Component({ service }) { const { t } = useTranslation(); const { widget } = service; const { data: sessionsData, error: sessionsError, mutate: sessionMutate, } = useWidgetAPI(widget, "Sessions", { refreshInterval: 5000, }); const { data: countData, error: countError } = useWidgetAPI(widget, "Count", { refreshInterval: 60000, }); async function handlePlayCommand(session, command) { const url = formatProxyUrlWithSegments(widget, "PlayControl", { sessionId: session.Id, command, }); await fetch(url).then(() => { sessionMutate(); }); } if (sessionsError || countError) { return ; } const enableBlocks = service.widget?.enableBlocks; const enableNowPlaying = service.widget?.enableNowPlaying ?? true; 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) { return ( <> {enableBlocks && } {enableNowPlaying && (
-
-
)} ); } if (enableNowPlaying) { const playing = sessionsData .filter((session) => session?.NowPlayingItem) .sort((a, b) => { if (a.PlayState.PositionTicks > b.PlayState.PositionTicks) { return 1; } if (a.PlayState.PositionTicks < b.PlayState.PositionTicks) { return -1; } return 0; }); if (playing.length === 0) { return ( <> {enableBlocks && }
{t("emby.no_active")}
{expandOneStreamToTwoRows && (
-
)}
); } if (expandOneStreamToTwoRows && playing.length === 1) { const session = playing[0]; return ( <> {enableBlocks && }
handlePlayCommand(currentSession, command)} session={session} enableUser={enableUser} showEpisodeNumber={showEpisodeNumber} />
); } return ( <> {enableBlocks && }
{playing.map((session) => ( handlePlayCommand(currentSession, command)} session={session} enableUser={enableUser} showEpisodeNumber={showEpisodeNumber} /> ))}
); } if (enableBlocks) { return ; } }