2022-09-25 16:15:47 -07:00
import { useTranslation } from "next-i18next" ;
import { BsVolumeMuteFill , BsFillPlayFill , BsPauseFill , BsCpu , BsFillCpuFill } from "react-icons/bs" ;
import { MdOutlineSmartDisplay } from "react-icons/md" ;
2023-03-12 21:57:13 +05:30
import Block from "components/services/widget/block" ;
2022-10-22 23:00:51 -07:00
import Container from "components/services/widget/container" ;
2022-11-07 09:24:15 -08:00
import { formatProxyUrlWithSegments } from "utils/proxy/api-helpers" ;
import useWidgetAPI from "utils/proxy/use-widget-api" ;
2022-09-25 16:15:47 -07:00
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 SingleSessionEntry ( { playCommand , session } ) {
const {
2023-08-27 13:55:47 -07:00
NowPlayingItem : { Name , SeriesName } ,
2022-09-25 16:15:47 -07:00
PlayState : { PositionTicks , IsPaused , IsMuted } ,
} = session ;
2023-08-27 13:55:47 -07:00
const RunTimeTicks = session . NowPlayingItem ? . RunTimeTicks ? ? session . NowPlayingItem ? . CurrentProgram ? . RunTimeTicks ? ? 0 ;
2023-10-07 20:58:05 -07:00
const { IsVideoDirect , VideoDecoderIsHardware , VideoEncoderIsHardware } = session ? . TranscodingInfo || { IsVideoDirect : true } ; // if no transcodinginfo its videodirect
2022-09-25 16:15:47 -07:00
2023-08-27 13:55:47 -07:00
const percent = Math . min ( 1 , PositionTicks / RunTimeTicks ) * 100 ;
2022-09-25 16:15:47 -07:00
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 = "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" >
{ Name }
{ SeriesName && ` - ${ SeriesName } ` }
< / div >
< / div >
< div className = "self-center text-xs flex justify-end mr-1.5 pl-1" >
{ IsVideoDirect && < MdOutlineSmartDisplay className = "opacity-50" / > }
{ ! IsVideoDirect && ( ! VideoDecoderIsHardware || ! VideoEncoderIsHardware ) && < BsCpu className = "opacity-50" / > }
{ ! IsVideoDirect && VideoDecoderIsHardware && VideoEncoderIsHardware && (
< BsFillCpuFill className = "opacity-50" / >
) }
< / div >
< / div >
< 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 = "absolute h-5 rounded-md bg-theme-200 dark:bg-theme-900/40 z-0"
style = { {
width : ` ${ percent } % ` ,
} }
/ >
< div className = "text-xs z-10 self-center ml-1" >
{ IsPaused && (
< BsFillPlayFill
onClick = { ( ) => {
playCommand ( session , "Unpause" ) ;
} }
className = "inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80"
/ >
) }
{ ! IsPaused && (
< BsPauseFill
onClick = { ( ) => {
playCommand ( session , "Pause" ) ;
} }
className = "inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80"
/ >
) }
< / div >
< div className = "grow " / >
< div className = "self-center text-xs flex justify-end mr-1 z-10" > { IsMuted && < BsVolumeMuteFill / > } < / div >
< div className = "self-center text-xs flex justify-end mr-2 z-10" >
{ ticksToString ( PositionTicks ) }
< span className = "mx-0.5 text-[8px]" > / < / span >
{ ticksToString ( RunTimeTicks ) }
< / div >
< / div >
< / >
) ;
}
function SessionEntry ( { playCommand , session } ) {
const {
2023-08-27 13:55:47 -07:00
NowPlayingItem : { Name , SeriesName } ,
2022-09-25 16:15:47 -07:00
PlayState : { PositionTicks , IsPaused , IsMuted } ,
} = session ;
2023-08-27 13:55:47 -07:00
const RunTimeTicks = session . NowPlayingItem ? . RunTimeTicks ? ? session . NowPlayingItem ? . CurrentProgram ? . RunTimeTicks ? ? 0 ;
2023-10-07 20:58:05 -07:00
const { IsVideoDirect , VideoDecoderIsHardware , VideoEncoderIsHardware } = session ? . TranscodingInfo || { IsVideoDirect : true } ; // if no transcodinginfo its videodirect
2022-09-25 16:15:47 -07:00
2023-08-27 13:55:47 -07:00
const percent = Math . min ( 1 , PositionTicks / RunTimeTicks ) * 100 ;
2022-09-25 16:15:47 -07:00
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 = "absolute h-5 rounded-md bg-theme-200 dark:bg-theme-900/40 z-0"
style = { {
width : ` ${ percent } % ` ,
} }
/ >
< div className = "text-xs z-10 self-center ml-1" >
{ IsPaused && (
< BsFillPlayFill
onClick = { ( ) => {
playCommand ( session , "Unpause" ) ;
} }
className = "inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80"
/ >
) }
{ ! IsPaused && (
< BsPauseFill
onClick = { ( ) => {
playCommand ( session , "Pause" ) ;
} }
className = "inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80"
/ >
) }
< / div >
< 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" >
{ Name }
{ SeriesName && ` - ${ SeriesName } ` }
< / div >
< / div >
< div className = "self-center text-xs flex justify-end mr-1 z-10" > { IsMuted && < BsVolumeMuteFill / > } < / div >
< div className = "self-center text-xs flex justify-end mr-1 z-10" > { ticksToString ( PositionTicks ) } < / div >
< div className = "self-center items-center text-xs flex justify-end mr-1.5 pl-1 z-10" >
{ IsVideoDirect && < MdOutlineSmartDisplay className = "opacity-50" / > }
{ ! IsVideoDirect && ( ! VideoDecoderIsHardware || ! VideoEncoderIsHardware ) && < BsCpu className = "opacity-50" / > }
{ ! IsVideoDirect && VideoDecoderIsHardware && VideoEncoderIsHardware && < BsFillCpuFill className = "opacity-50" / > }
< / div >
< / div >
) ;
}
2023-03-12 16:50:28 -07:00
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 (
< Container service = { service } >
< Block label = "emby.movies" / >
< Block label = "emby.series" / >
< Block label = "emby.episodes" / >
< Block label = "emby.songs" / >
< / Container >
)
}
return (
< Container service = { service } >
< Block label = "emby.movies" value = { t ( "common.number" , { value : countData . MovieCount } ) } / >
< Block label = "emby.series" value = { t ( "common.number" , { value : countData . SeriesCount } ) } / >
< Block label = "emby.episodes" value = { t ( "common.number" , { value : countData . EpisodeCount } ) } / >
< Block label = "emby.songs" value = { t ( "common.number" , { value : countData . SongCount } ) } / >
< / Container >
)
}
2022-09-25 16:15:47 -07:00
export default function Component ( { service } ) {
const { t } = useTranslation ( ) ;
2022-09-27 22:59:14 +03:00
const { widget } = service ;
2022-09-25 16:15:47 -07:00
const {
data : sessionsData ,
error : sessionsError ,
mutate : sessionMutate ,
2022-11-07 09:24:15 -08:00
} = useWidgetAPI ( widget , "Sessions" , {
2022-09-25 16:15:47 -07:00
refreshInterval : 5000 ,
} ) ;
2023-03-12 21:57:13 +05:30
const {
data : countData ,
error : countError ,
} = useWidgetAPI ( widget , "Count" , {
refreshInterval : 60000 , } ) ;
2022-09-25 16:15:47 -07:00
async function handlePlayCommand ( session , command ) {
2022-09-27 22:59:14 +03:00
const url = formatProxyUrlWithSegments ( widget , "PlayControl" , {
2022-09-25 16:15:47 -07:00
sessionId : session . Id ,
2022-09-26 12:04:37 +03:00
command ,
2022-09-25 16:15:47 -07:00
} ) ;
await fetch ( url ) . then ( ( ) => {
2023-03-12 22:11:15 +05:30
sessionMutate ( ) ;
2022-09-25 16:15:47 -07:00
} ) ;
}
2023-03-12 21:57:13 +05:30
if ( sessionsError || countError ) {
2023-04-30 19:09:37 -04:00
return < Container service = { service } error = { sessionsError ? ? countError } / > ;
2023-03-12 21:57:13 +05:30
}
2023-03-12 16:50:28 -07:00
const enableBlocks = service . widget ? . enableBlocks
const enableNowPlaying = service . widget ? . enableNowPlaying ? ? true
if ( ! sessionsData || ! countData ) {
2023-03-12 21:57:13 +05:30
return (
< >
2023-03-12 16:50:28 -07:00
{ enableBlocks && < CountBlocks service = { service } countData = { null } / > }
{ enableNowPlaying && < div className = "flex flex-col pb-1" >
2023-03-12 21:57:13 +05:30
< 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 >
< / 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" >
< span className = "absolute left-2 text-xs mt-[2px]" > - < / span >
< / div >
2023-03-12 16:50:28 -07:00
< / div > }
2023-03-12 21:57:13 +05:30
< / >
) ;
2022-09-25 16:15:47 -07:00
}
2023-03-12 16:50:28 -07:00
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 && < CountBlocks service = { service } countData = { countData } / > }
< 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" >
< span className = "absolute left-2 text-xs mt-[2px]" > { t ( "emby.no_active" ) } < / span >
< / 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" >
< span className = "absolute left-2 text-xs mt-[2px]" > - < / span >
< / div >
2022-09-25 16:15:47 -07:00
< / div >
2023-03-12 16:50:28 -07:00
< / >
) ;
}
if ( playing . length === 1 ) {
const session = playing [ 0 ] ;
return (
< >
{ enableBlocks && < CountBlocks service = { service } countData = { countData } / > }
< div className = "flex flex-col pb-1 mx-1" >
< SingleSessionEntry
playCommand = { ( currentSession , command ) => handlePlayCommand ( currentSession , command ) }
session = { session }
/ >
2022-09-25 16:15:47 -07:00
< / div >
2023-03-12 16:50:28 -07:00
< / >
) ;
}
2023-04-04 15:00:09 -07:00
if ( playing . length > 0 )
2022-09-25 16:15:47 -07:00
return (
2023-03-12 21:57:13 +05:30
< >
2023-03-12 16:50:28 -07:00
{ enableBlocks && < CountBlocks service = { service } countData = { countData } / > }
2022-09-25 16:15:47 -07:00
< div className = "flex flex-col pb-1 mx-1" >
2023-03-12 16:50:28 -07:00
{ playing . map ( ( session ) => (
< SessionEntry
key = { session . Id }
playCommand = { ( currentSession , command ) => handlePlayCommand ( currentSession , command ) }
session = { session }
/ >
) ) }
2022-09-25 16:15:47 -07:00
< / div >
2023-03-12 21:57:13 +05:30
< / >
2022-09-25 16:15:47 -07:00
) ;
}
2023-03-12 16:50:28 -07:00
if ( enableBlocks ) {
2022-09-25 16:15:47 -07:00
return (
2023-03-12 16:50:28 -07:00
< CountBlocks service = { service } countData = { countData } / >
)
2022-09-25 16:15:47 -07:00
}
}