date-fns#formatDistanceToNow JavaScript Examples

The following examples show how to use date-fns#formatDistanceToNow. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: scheduledtasks.js    From jellyfin-web-jmp with GNU General Public License v2.0 6 votes vote down vote up
function getTaskProgressHtml(task) {
        let html = '';
        if (task.State === 'Idle') {
            if (task.LastExecutionResult) {
                const endtime = Date.parse(task.LastExecutionResult.EndTimeUtc);
                const starttime = Date.parse(task.LastExecutionResult.StartTimeUtc);
                html += globalize.translate('LabelScheduledTaskLastRan', formatDistanceToNow(endtime, localeWithSuffix),
                    formatDistance(starttime, endtime, { locale: getLocale() }));
                if (task.LastExecutionResult.Status === 'Failed') {
                    html += " <span style='color:#FF0000;'>(" + globalize.translate('LabelFailed') + ')</span>';
                } else if (task.LastExecutionResult.Status === 'Cancelled') {
                    html += " <span style='color:#0026FF;'>(" + globalize.translate('LabelCancelled') + ')</span>';
                } else if (task.LastExecutionResult.Status === 'Aborted') {
                    html += " <span style='color:#FF0000;'>" + globalize.translate('LabelAbortedByServerShutdown') + '</span>';
                }
            }
        } else if (task.State === 'Running') {
            const progress = (task.CurrentProgressPercentage || 0).toFixed(1);
            html += '<div style="display:flex;align-items:center;">';
            html += '<div class="taskProgressOuter" title="' + progress + '%" style="flex-grow:1;">';
            html += '<div class="taskProgressInner" style="width:' + progress + '%;">';
            html += '</div>';
            html += '</div>';
            html += "<span style='color:#00a4dc;margin-left:5px;'>" + progress + '%</span>';
            html += '</div>';
        } else {
            html += "<span style='color:#FF0000;'>" + globalize.translate('LabelStopping') + '</span>';
        }
        return html;
    }
Example #2
Source File: scheduledtasks.js    From veso-web with GNU General Public License v2.0 6 votes vote down vote up
function getTaskProgressHtml(task) {
        let html = '';
        if (task.State === 'Idle') {
            if (task.LastExecutionResult) {
                const endtime = Date.parse(task.LastExecutionResult.EndTimeUtc);
                const starttime = Date.parse(task.LastExecutionResult.StartTimeUtc);
                html += globalize.translate('LabelScheduledTaskLastRan', formatDistanceToNow(endtime, localeWithSuffix),
                    formatDistance(starttime, endtime, { locale: getLocale() }));
                if (task.LastExecutionResult.Status === 'Failed') {
                    html += " <span style='color:#FF0000;'>(" + globalize.translate('LabelFailed') + ')</span>';
                } else if (task.LastExecutionResult.Status === 'Cancelled') {
                    html += " <span style='color:#0026FF;'>(" + globalize.translate('LabelCancelled') + ')</span>';
                } else if (task.LastExecutionResult.Status === 'Aborted') {
                    html += " <span style='color:#FF0000;'>" + globalize.translate('LabelAbortedByServerShutdown') + '</span>';
                }
            }
        } else if (task.State === 'Running') {
            const progress = (task.CurrentProgressPercentage || 0).toFixed(1);
            html += '<div style="display:flex;align-items:center;">';
            html += '<div class="taskProgressOuter" title="' + progress + '%" style="flex-grow:1;">';
            html += '<div class="taskProgressInner" style="width:' + progress + '%;">';
            html += '</div>';
            html += '</div>';
            html += "<span style='color:#d61e30;margin-left:5px;'>" + progress + '%</span>';
            html += '</div>';
        } else {
            html += "<span style='color:#FF0000;'>" + globalize.translate('LabelStopping') + '</span>';
        }
        return html;
    }
Example #3
Source File: Profile.jsx    From airdrop with MIT License 6 votes vote down vote up
function Profile({ user }) {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
    <div
      style={{ gridTemplateColumns: '60px 1fr 30px' }}
      className="grid p-3 gap-1 cursor-pointer"
      onClick={() => setIsOpen(true)}
    >
      <div className="w-12 h-12 bg-secondary rounded-full">
        <img src={user.photoURL} className="rounded-full" alt="Your Profile" />
      </div>
      <div
        className={`w-auto font-sans flex flex-col justify-between ${Styles.borderBorder} pb-2`}
      >
        <div className="text-white text-lg">
          <span>{user.isAnonymous && '~ '}</span>
          <span>{user?.displayName || 'User'}</span>
        </div>
        <div className="text-light text-xs">
          Last login{' '}
          {user.metadata.lastSignInTime &&
            formatDistanceToNow(new Date(user.metadata.lastSignInTime), {
              addSuffix: true,
            })}
        </div>
      </div>
      <div>
      <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 text-light" viewBox="0 0 20 20" fill="currentColor">
        <path d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z" />
        <path fillRule="evenodd" d="M2 6a2 2 0 012-2h4a1 1 0 010 2H4v10h10v-4a1 1 0 112 0v4a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" clipRule="evenodd" />
      </svg>
      </div>
    </div>
    <EditProfile isOpen={isOpen} setIsOpen={setIsOpen}/>
    </>
  );
}
Example #4
Source File: date-time.js    From tisn.app with GNU Affero General Public License v3.0 5 votes vote down vote up
distanceToNow = (dateTimeString) =>
  formatDistanceToNow(parseISO(dateTimeString), { locale: getLocale() })
Example #5
Source File: locale.js    From treetracker-admin-client with GNU Affero General Public License v3.0 5 votes vote down vote up
timeAgoFormatDate = (date) => {
  let timeStr = formatDistanceToNow(date).replace(/about/, '');
  return timeStr;
}
Example #6
Source File: NotificationsPopover.js    From course-manager with MIT License 5 votes vote down vote up
function NotificationItem({ notification }) {
  const { avatar, title } = renderContent(notification);

  return (
    <ListItem
      button
      to="#"
      disableGutters
      component={RouterLink}
      sx={{
        py: 1.5,
        px: 2.5,
        mt: '1px',
        ...(notification.isUnRead && {
          bgcolor: 'action.selected'
        })
      }}
    >
      <ListItemAvatar>
        <Avatar sx={{ bgcolor: 'background.neutral' }}>{avatar}</Avatar>
      </ListItemAvatar>
      <ListItemText
        primary={title}
        secondary={
          <Typography
            variant="caption"
            sx={{
              mt: 0.5,
              display: 'flex',
              alignItems: 'center',
              color: 'text.disabled'
            }}
          >
            <Box component={Icon} icon={clockFill} sx={{ mr: 0.5, width: 16, height: 16 }} />
            {formatDistanceToNow(new Date(notification.createdAt))}
          </Typography>
        }
      />
    </ListItem>
  );
}
Example #7
Source File: formatTime.js    From course-manager with MIT License 5 votes vote down vote up
export function fToNow(date) {
  return formatDistanceToNow(new Date(date), {
    addSuffix: true
  });
}
Example #8
Source File: formatDate.js    From Mumble with Apache License 2.0 5 votes vote down vote up
distanceDate = (date) =>
  formatDistanceToNow(sqlDateToJsDate(date), { addSuffix: true })
Example #9
Source File: Item.jsx    From killed-by-microsoft with MIT License 5 votes vote down vote up
timePhrase() {
    const { dateClose } = this.props;
    const relativeDate = formatDistanceToNow(parseISO(dateClose), new Date());
    if (!this.isPast()) {
      return <span>{`${soonToDieIdiom()} in ${relativeDate}, `}</span>;
    }
    return <span>{`Killed ${relativeDate} ago, `}</span>;
  }
Example #10
Source File: formatTime.js    From Django-REST-Framework-React-BoilerPlate with MIT License 5 votes vote down vote up
export function fToNow(date) {
  return formatDistanceToNow(new Date(date), {
    addSuffix: true,
  });
}
Example #11
Source File: devices.js    From jellyfin-web-jmp with GNU General Public License v2.0 5 votes vote down vote up
function load(page, devices) {
        let html = '';
        html += devices.map(function (device) {
            let deviceHtml = '';
            deviceHtml += "<div data-id='" + device.Id + "' class='card backdropCard'>";
            deviceHtml += '<div class="cardBox visualCardBox">';
            deviceHtml += '<div class="cardScalable">';
            deviceHtml += '<div class="cardPadder cardPadder-backdrop"></div>';
            deviceHtml += `<a is="emby-linkbutton" href="${canEdit ? '#!/device.html?id=' + device.Id : '#'}" class="cardContent cardImageContainer ${cardBuilder.getDefaultBackgroundClass()}">`;
            const iconUrl = imageHelper.getDeviceIcon(device);

            if (iconUrl) {
                deviceHtml += '<div class="cardImage" style="background-image:url(\'' + iconUrl + "');background-size: auto 64%;background-position:center center;\">";
                deviceHtml += '</div>';
            } else {
                deviceHtml += '<span class="cardImageIcon material-icons tablet_android" aria-hidden="true"></span>';
            }

            deviceHtml += '</a>';
            deviceHtml += '</div>';
            deviceHtml += '<div class="cardFooter">';

            if (canEdit || canDelete(device.Id)) {
                deviceHtml += '<div style="text-align:right; float:right;padding-top:5px;">';
                deviceHtml += '<button type="button" is="paper-icon-button-light" data-id="' + device.Id + '" title="' + globalize.translate('Menu') + '" class="btnDeviceMenu"><span class="material-icons more_vert" aria-hidden="true"></span></button>';
                deviceHtml += '</div>';
            }

            deviceHtml += "<div class='cardText'>";
            deviceHtml += escapeHtml(device.Name);
            deviceHtml += '</div>';
            deviceHtml += "<div class='cardText cardText-secondary'>";
            deviceHtml += escapeHtml(device.AppName + ' ' + device.AppVersion);
            deviceHtml += '</div>';
            deviceHtml += "<div class='cardText cardText-secondary'>";

            if (device.LastUserName) {
                deviceHtml += escapeHtml(device.LastUserName);
                deviceHtml += ', ' + formatDistanceToNow(Date.parse(device.DateLastActivity), localeWithSuffix);
            }

            deviceHtml += '&nbsp;';
            deviceHtml += '</div>';
            deviceHtml += '</div>';
            deviceHtml += '</div>';
            deviceHtml += '</div>';
            return deviceHtml;
        }).join('');
        page.querySelector('.devicesList').innerHTML = html;
    }
Example #12
Source File: jobs.js    From remotebear with GNU Affero General Public License v3.0 5 votes vote down vote up
export function getJobs({ query, locationId, departmentId, companyId }) {
  return allJobs
    .filter((job) => {
      const company = allCompaniesById[job.companyId];
      const disabled = job?.status === "disabled";
      let satisfiesQuery = true;
      if (query) {
        satisfiesQuery =
          searchInString(job.title, query) ||
          searchInString(job.location, query) ||
          searchInString(company.name, query);
      }
      let satisfiesLocationId = true;
      if (!job.normalizedLocation.length) {
        satisfiesLocationId = false;
      } else if (locationId) {
        satisfiesLocationId =
          job.normalizedLocation.includes(locationIds.global) ||
          job.normalizedLocation.includes(locationId);
      }
      let satisfiesDepartmentId = true;
      if (departmentId) {
        satisfiesDepartmentId =
          departmentId === departmentIds.engineering
            ? job.normalizedDepartment === departmentId
            : !job.normalizedDepartment;
      }
      let satisfiesCompanyId = true;
      if (companyId) {
        satisfiesCompanyId = job.companyId === companyId;
      }
      return (
        satisfiesQuery &&
        satisfiesLocationId &&
        satisfiesDepartmentId &&
        satisfiesCompanyId &&
        !disabled
      );
    })
    .map((job) => {
      // Add a new "formattedCreatedAt" field holding a human readable date
      let formattedCreatedAt;
      if (isToday(job.createdAt)) {
        formattedCreatedAt = "Today";
      } else if (isYesterday(job.createdAt)) {
        formattedCreatedAt = "Yesterday";
      } else {
        formattedCreatedAt = formatDistanceToNow(job.createdAt, {
          addSuffix: true,
        });
      }
      // Capitalize
      formattedCreatedAt = `${formattedCreatedAt
        .charAt(0)
        .toUpperCase()}${formattedCreatedAt.slice(1)}`;

      // Denormalize the company infos into "company" to avoid loading them
      // on the client side
      const iconName = companyFaviconPathsByCompanyIds[job.companyId];
      const company = {
        id: allCompaniesById[job.companyId].id,
        name: allCompaniesById[job.companyId].name,
        iconUrl: `/company-favicons/${iconName}`,
      };
      return { ...job, company, formattedCreatedAt };
    });
}
Example #13
Source File: DrawLastUpdated.js    From covid19japan with MIT License 5 votes vote down vote up
getLocalizedRelativeTime = (lastUpdated, language) => {
  const locale = LOCALES[language];
  return formatDistanceToNow(lastUpdated, {
    locale,
    addSuffix: true,
  });
}
Example #14
Source File: devices.js    From veso-web with GNU General Public License v2.0 5 votes vote down vote up
function load(page, devices) {
        let html = '';
        html += devices.map(function (device) {
            let deviceHtml = '';
            deviceHtml += "<div data-id='" + device.Id + "' class='card backdropCard'>";
            deviceHtml += '<div class="cardBox visualCardBox">';
            deviceHtml += '<div class="cardScalable">';
            deviceHtml += '<div class="cardPadder cardPadder-backdrop"></div>';
            deviceHtml += `<a is="emby-linkbutton" href="${canEdit ? '#/device.html?id=' + device.Id : '#'}" class="cardContent cardImageContainer ${cardBuilder.getDefaultBackgroundClass()}">`;
            const iconUrl = imageHelper.getDeviceIcon(device);

            if (iconUrl) {
                deviceHtml += '<div class="cardImage" style="background-image:url(\'' + iconUrl + "');background-size: auto 64%;background-position:center center;\">";
                deviceHtml += '</div>';
            } else {
                deviceHtml += '<span class="cardImageIcon material-icons tablet_android" aria-hidden="true"></span>';
            }

            deviceHtml += '</a>';
            deviceHtml += '</div>';
            deviceHtml += '<div class="cardFooter">';

            if (canEdit || canDelete(device.Id)) {
                deviceHtml += '<div style="text-align:right; float:right;padding-top:5px;">';
                deviceHtml += '<button type="button" is="paper-icon-button-light" data-id="' + device.Id + '" title="' + globalize.translate('Menu') + '" class="btnDeviceMenu"><span class="material-icons more_vert" aria-hidden="true"></span></button>';
                deviceHtml += '</div>';
            }

            deviceHtml += "<div class='cardText'>";
            deviceHtml += escapeHtml(device.Name);
            deviceHtml += '</div>';
            deviceHtml += "<div class='cardText cardText-secondary'>";
            deviceHtml += escapeHtml(device.AppName + ' ' + device.AppVersion);
            deviceHtml += '</div>';
            deviceHtml += "<div class='cardText cardText-secondary'>";

            if (device.LastUserName) {
                deviceHtml += escapeHtml(device.LastUserName);
                deviceHtml += ', ' + formatDistanceToNow(Date.parse(device.DateLastActivity), localeWithSuffix);
            }

            deviceHtml += '&nbsp;';
            deviceHtml += '</div>';
            deviceHtml += '</div>';
            deviceHtml += '</div>';
            deviceHtml += '</div>';
            return deviceHtml;
        }).join('');
        page.querySelector('.devicesList').innerHTML = html;
    }
Example #15
Source File: dashboard.js    From jellyfin-web-jmp with GNU General Public License v2.0 4 votes vote down vote up
window.DashboardPage = {
        startInterval: function (apiClient) {
            apiClient.sendMessage('SessionsStart', '0,1500');
            apiClient.sendMessage('ScheduledTasksInfoStart', '0,1000');
        },
        stopInterval: function (apiClient) {
            apiClient.sendMessage('SessionsStop');
            apiClient.sendMessage('ScheduledTasksInfoStop');
        },
        getSessionNowPlayingStreamInfo: function (session) {
            let html = '';
            let showTranscodingInfo = false;
            const displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session);

            if (displayPlayMethod === 'DirectPlay') {
                html += globalize.translate('DirectPlaying');
            } else if (displayPlayMethod === 'Remux') {
                html += globalize.translate('Remuxing');
            } else if (displayPlayMethod === 'DirectStream') {
                html += globalize.translate('DirectStreaming');
            } else if (displayPlayMethod === 'Transcode') {
                if (session.TranscodingInfo && session.TranscodingInfo.Framerate) {
                    html += `${globalize.translate('Framerate')}: ${session.TranscodingInfo.Framerate}fps`;
                }

                showTranscodingInfo = true;
            }

            if (showTranscodingInfo) {
                const line = [];

                if (session.TranscodingInfo) {
                    if (session.TranscodingInfo.Bitrate) {
                        if (session.TranscodingInfo.Bitrate > 1e6) {
                            line.push((session.TranscodingInfo.Bitrate / 1e6).toFixed(1) + ' Mbps');
                        } else {
                            line.push(Math.floor(session.TranscodingInfo.Bitrate / 1e3) + ' Kbps');
                        }
                    }

                    if (session.TranscodingInfo.Container) {
                        line.push(session.TranscodingInfo.Container.toUpperCase());
                    }

                    if (session.TranscodingInfo.VideoCodec) {
                        line.push(session.TranscodingInfo.VideoCodec.toUpperCase());
                    }

                    if (session.TranscodingInfo.AudioCodec && session.TranscodingInfo.AudioCodec != session.TranscodingInfo.Container) {
                        line.push(session.TranscodingInfo.AudioCodec.toUpperCase());
                    }
                }

                if (line.length) {
                    html += '<br/><br/>' + line.join(' ');
                }
            }

            return html;
        },
        getSessionNowPlayingTime: function (session) {
            const nowPlayingItem = session.NowPlayingItem;
            let html = '';

            if (nowPlayingItem) {
                if (session.PlayState.PositionTicks) {
                    html += datetime.getDisplayRunningTime(session.PlayState.PositionTicks);
                } else {
                    html += '0:00';
                }

                html += ' / ';

                if (nowPlayingItem && nowPlayingItem.RunTimeTicks) {
                    html += datetime.getDisplayRunningTime(nowPlayingItem.RunTimeTicks);
                } else {
                    html += '0:00';
                }
            }

            return html;
        },
        getAppSecondaryText: function (session) {
            return session.Client + ' ' + session.ApplicationVersion;
        },
        getNowPlayingName: function (session) {
            let imgUrl = '';
            const nowPlayingItem = session.NowPlayingItem;
            // FIXME: It seems that, sometimes, server sends date in the future, so date-fns displays messages like 'in less than a minute'. We should fix
            // how dates are returned by the server when the session is active and show something like 'Active now', instead of past/future sentences
            if (!nowPlayingItem) {
                return {
                    html: globalize.translate('LastSeen', formatDistanceToNow(Date.parse(session.LastActivityDate), localeWithSuffix)),
                    image: imgUrl
                };
            }

            let topText = escapeHtml(itemHelper.getDisplayName(nowPlayingItem));
            let bottomText = '';

            if (nowPlayingItem.Artists && nowPlayingItem.Artists.length) {
                bottomText = topText;
                topText = escapeHtml(nowPlayingItem.Artists[0]);
            } else {
                if (nowPlayingItem.SeriesName || nowPlayingItem.Album) {
                    bottomText = topText;
                    topText = escapeHtml(nowPlayingItem.SeriesName || nowPlayingItem.Album);
                } else if (nowPlayingItem.ProductionYear) {
                    bottomText = nowPlayingItem.ProductionYear;
                }
            }

            if (nowPlayingItem.ImageTags && nowPlayingItem.ImageTags.Logo) {
                imgUrl = ApiClient.getScaledImageUrl(nowPlayingItem.Id, {
                    tag: nowPlayingItem.ImageTags.Logo,
                    maxHeight: 24,
                    maxWidth: 130,
                    type: 'Logo'
                });
            } else if (nowPlayingItem.ParentLogoImageTag) {
                imgUrl = ApiClient.getScaledImageUrl(nowPlayingItem.ParentLogoItemId, {
                    tag: nowPlayingItem.ParentLogoImageTag,
                    maxHeight: 24,
                    maxWidth: 130,
                    type: 'Logo'
                });
            }

            if (imgUrl) {
                topText = '<img src="' + imgUrl + '" style="max-height:24px;max-width:130px;" />';
            }

            return {
                html: bottomText ? topText + '<br/>' + bottomText : topText,
                image: imgUrl
            };
        },
        getUsersHtml: function (session) {
            const html = [];

            if (session.UserId) {
                html.push(escapeHtml(session.UserName));
            }

            for (let i = 0, length = session.AdditionalUsers.length; i < length; i++) {
                html.push(escapeHtml(session.AdditionalUsers[i].UserName));
            }

            return html.join(', ');
        },
        getUserImage: function (session) {
            if (session.UserId && session.UserPrimaryImageTag) {
                return ApiClient.getUserImageUrl(session.UserId, {
                    tag: session.UserPrimaryImageTag,
                    type: 'Primary'
                });
            }

            return null;
        },
        updateSession: function (row, session) {
            row.classList.remove('deadSession');
            const nowPlayingItem = session.NowPlayingItem;

            if (nowPlayingItem) {
                row.classList.add('playingSession');
                row.querySelector('.btnSessionInfo').classList.remove('hide');
            } else {
                row.classList.remove('playingSession');
                row.querySelector('.btnSessionInfo').classList.add('hide');
            }

            if (session.ServerId && session.SupportedCommands.indexOf('DisplayMessage') !== -1) {
                row.querySelector('.btnSessionSendMessage').classList.remove('hide');
            } else {
                row.querySelector('.btnSessionSendMessage').classList.add('hide');
            }

            const btnSessionPlayPause = row.querySelector('.btnSessionPlayPause');

            if (session.ServerId && nowPlayingItem && session.SupportsRemoteControl) {
                btnSessionPlayPause.classList.remove('hide');
                row.querySelector('.btnSessionStop').classList.remove('hide');
            } else {
                btnSessionPlayPause.classList.add('hide');
                row.querySelector('.btnSessionStop').classList.add('hide');
            }

            const btnSessionPlayPauseIcon = btnSessionPlayPause.querySelector('.material-icons');
            btnSessionPlayPauseIcon.classList.remove('play_arrow', 'pause');
            btnSessionPlayPauseIcon.classList.add(session.PlayState && session.PlayState.IsPaused ? 'play_arrow' : 'pause');

            row.querySelector('.sessionNowPlayingTime').innerText = DashboardPage.getSessionNowPlayingTime(session);
            row.querySelector('.sessionUserName').innerHTML = DashboardPage.getUsersHtml(session);
            row.querySelector('.sessionAppSecondaryText').innerText = DashboardPage.getAppSecondaryText(session);
            const nowPlayingName = DashboardPage.getNowPlayingName(session);
            const nowPlayingInfoElem = row.querySelector('.sessionNowPlayingInfo');

            if (!(nowPlayingName.image && nowPlayingName.image == nowPlayingInfoElem.getAttribute('data-imgsrc'))) {
                nowPlayingInfoElem.innerHTML = nowPlayingName.html;
                nowPlayingInfoElem.setAttribute('data-imgsrc', nowPlayingName.image || '');
            }

            const playbackProgressElem = row.querySelector('.playbackProgress');
            const transcodingProgress = row.querySelector('.transcodingProgress');

            let percent = 100 * session?.PlayState?.PositionTicks / nowPlayingItem?.RunTimeTicks;
            playbackProgressElem.outerHTML = indicators.getProgressHtml(percent || 0, {
                containerClass: 'playbackProgress'
            });

            percent = session?.TranscodingInfo?.CompletionPercentage?.toFixed(1);
            transcodingProgress.outerHTML = indicators.getProgressHtml(percent || 0, {
                containerClass: 'transcodingProgress'
            });

            const imgUrl = DashboardPage.getNowPlayingImageUrl(nowPlayingItem) || '';
            const imgElem = row.querySelector('.sessionNowPlayingContent');

            if (imgUrl != imgElem.getAttribute('data-src')) {
                imgElem.style.backgroundImage = imgUrl ? "url('" + imgUrl + "')" : '';
                imgElem.setAttribute('data-src', imgUrl);

                if (imgUrl) {
                    imgElem.classList.add('sessionNowPlayingContent-withbackground');
                    row.querySelector('.sessionNowPlayingInnerContent').classList.add('darkenContent');
                } else {
                    imgElem.classList.remove('sessionNowPlayingContent-withbackground');
                    row.querySelector('.sessionNowPlayingInnerContent').classList.remove('darkenContent');
                }
            }
        },
        getClientImage: function (connection) {
            const iconUrl = imageHelper.getDeviceIcon(connection);
            return "<img src='" + iconUrl + "' />";
        },
        getNowPlayingImageUrl: function (item) {
            /* Screen width is multiplied by 0.2, as the there is currently no way to get the width of
            elements that aren't created yet. */
            if (item && item.BackdropImageTags && item.BackdropImageTags.length) {
                return ApiClient.getScaledImageUrl(item.Id, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Backdrop',
                    tag: item.BackdropImageTags[0]
                });
            }

            if (item && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length) {
                return ApiClient.getScaledImageUrl(item.ParentBackdropItemId, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Backdrop',
                    tag: item.ParentBackdropImageTags[0]
                });
            }

            if (item && item.BackdropImageTag) {
                return ApiClient.getScaledImageUrl(item.BackdropItemId, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Backdrop',
                    tag: item.BackdropImageTag
                });
            }

            const imageTags = (item || {}).ImageTags || {};

            if (item && imageTags.Thumb) {
                return ApiClient.getScaledImageUrl(item.Id, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Thumb',
                    tag: imageTags.Thumb
                });
            }

            if (item && item.ParentThumbImageTag) {
                return ApiClient.getScaledImageUrl(item.ParentThumbItemId, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Thumb',
                    tag: item.ParentThumbImageTag
                });
            }

            if (item && item.ThumbImageTag) {
                return ApiClient.getScaledImageUrl(item.ThumbItemId, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Thumb',
                    tag: item.ThumbImageTag
                });
            }

            if (item && imageTags.Primary) {
                return ApiClient.getScaledImageUrl(item.Id, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Primary',
                    tag: imageTags.Primary
                });
            }

            if (item && item.PrimaryImageTag) {
                return ApiClient.getScaledImageUrl(item.PrimaryImageItemId, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Primary',
                    tag: item.PrimaryImageTag
                });
            }

            if (item && item.AlbumPrimaryImageTag) {
                return ApiClient.getScaledImageUrl(item.AlbumId, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Primary',
                    tag: item.AlbumPrimaryImageTag
                });
            }

            return null;
        },
        systemUpdateTaskKey: 'SystemUpdateTask',
        stopTask: function (btn, id) {
            const page = dom.parentWithClass(btn, 'page');
            ApiClient.stopScheduledTask(id).then(function () {
                pollForInfo(page, ApiClient);
            });
        },
        restart: function (btn) {
            confirm({
                title: globalize.translate('Restart'),
                text: globalize.translate('MessageConfirmRestart'),
                confirmText: globalize.translate('Restart'),
                primary: 'delete'
            }).then(function () {
                const page = dom.parentWithClass(btn, 'page');
                page.querySelector('#btnRestartServer').disabled = true;
                page.querySelector('#btnShutdown').disabled = true;
                ApiClient.restartServer();
            });
        },
        shutdown: function (btn) {
            confirm({
                title: globalize.translate('ButtonShutdown'),
                text: globalize.translate('MessageConfirmShutdown'),
                confirmText: globalize.translate('ButtonShutdown'),
                primary: 'delete'
            }).then(function () {
                const page = dom.parentWithClass(btn, 'page');
                page.querySelector('#btnRestartServer').disabled = true;
                page.querySelector('#btnShutdown').disabled = true;
                ApiClient.shutdownServer();
            });
        }
    };
Example #16
Source File: dashboard.js    From veso-web with GNU General Public License v2.0 4 votes vote down vote up
window.DashboardPage = {
        startInterval: function (apiClient) {
            apiClient.sendMessage('SessionsStart', '0,1500');
            apiClient.sendMessage('ScheduledTasksInfoStart', '0,1000');
        },
        stopInterval: function (apiClient) {
            apiClient.sendMessage('SessionsStop');
            apiClient.sendMessage('ScheduledTasksInfoStop');
        },
        getSessionNowPlayingStreamInfo: function (session) {
            let html = '';
            let showTranscodingInfo = false;
            const displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session);

            if (displayPlayMethod === 'DirectPlay') {
                html += globalize.translate('DirectPlaying');
            } else if (displayPlayMethod === 'Remux') {
                html += globalize.translate('Remuxing');
            } else if (displayPlayMethod === 'DirectStream') {
                html += globalize.translate('DirectStreaming');
            } else if (displayPlayMethod === 'Transcode') {
                if (session.TranscodingInfo && session.TranscodingInfo.Framerate) {
                    html += `${globalize.translate('Framerate')}: ${session.TranscodingInfo.Framerate}fps`;
                }

                showTranscodingInfo = true;
            }

            if (showTranscodingInfo) {
                const line = [];

                if (session.TranscodingInfo) {
                    if (session.TranscodingInfo.Bitrate) {
                        if (session.TranscodingInfo.Bitrate > 1e6) {
                            line.push((session.TranscodingInfo.Bitrate / 1e6).toFixed(1) + ' Mbps');
                        } else {
                            line.push(Math.floor(session.TranscodingInfo.Bitrate / 1e3) + ' Kbps');
                        }
                    }

                    if (session.TranscodingInfo.Container) {
                        line.push(session.TranscodingInfo.Container.toUpperCase());
                    }

                    if (session.TranscodingInfo.VideoCodec) {
                        line.push(session.TranscodingInfo.VideoCodec.toUpperCase());
                    }

                    if (session.TranscodingInfo.AudioCodec && session.TranscodingInfo.AudioCodec != session.TranscodingInfo.Container) {
                        line.push(session.TranscodingInfo.AudioCodec.toUpperCase());
                    }
                }

                if (line.length) {
                    html += '<br/><br/>' + line.join(' ');
                }
            }

            return html;
        },
        getSessionNowPlayingTime: function (session) {
            const nowPlayingItem = session.NowPlayingItem;
            let html = '';

            if (nowPlayingItem) {
                if (session.PlayState.PositionTicks) {
                    html += datetime.getDisplayRunningTime(session.PlayState.PositionTicks);
                } else {
                    html += '0:00';
                }

                html += ' / ';

                if (nowPlayingItem && nowPlayingItem.RunTimeTicks) {
                    html += datetime.getDisplayRunningTime(nowPlayingItem.RunTimeTicks);
                } else {
                    html += '0:00';
                }
            }

            return html;
        },
        getAppSecondaryText: function (session) {
            return session.Client + ' ' + session.ApplicationVersion;
        },
        getNowPlayingName: function (session) {
            let imgUrl = '';
            const nowPlayingItem = session.NowPlayingItem;
            // FIXME: It seems that, sometimes, server sends date in the future, so date-fns displays messages like 'in less than a minute'. We should fix
            // how dates are returned by the server when the session is active and show something like 'Active now', instead of past/future sentences
            if (!nowPlayingItem) {
                return {
                    html: globalize.translate('LastSeen', formatDistanceToNow(Date.parse(session.LastActivityDate), localeWithSuffix)),
                    image: imgUrl
                };
            }

            let topText = escapeHtml(itemHelper.getDisplayName(nowPlayingItem));
            let bottomText = '';

            if (nowPlayingItem.Artists && nowPlayingItem.Artists.length) {
                bottomText = topText;
                topText = escapeHtml(nowPlayingItem.Artists[0]);
            } else {
                if (nowPlayingItem.SeriesName || nowPlayingItem.Album) {
                    bottomText = topText;
                    topText = escapeHtml(nowPlayingItem.SeriesName || nowPlayingItem.Album);
                } else if (nowPlayingItem.ProductionYear) {
                    bottomText = nowPlayingItem.ProductionYear;
                }
            }

            if (nowPlayingItem.ImageTags && nowPlayingItem.ImageTags.Logo) {
                imgUrl = ApiClient.getScaledImageUrl(nowPlayingItem.Id, {
                    tag: nowPlayingItem.ImageTags.Logo,
                    maxHeight: 24,
                    maxWidth: 130,
                    type: 'Logo'
                });
            } else if (nowPlayingItem.ParentLogoImageTag) {
                imgUrl = ApiClient.getScaledImageUrl(nowPlayingItem.ParentLogoItemId, {
                    tag: nowPlayingItem.ParentLogoImageTag,
                    maxHeight: 24,
                    maxWidth: 130,
                    type: 'Logo'
                });
            }

            if (imgUrl) {
                topText = '<img src="' + imgUrl + '" style="max-height:24px;max-width:130px;" />';
            }

            return {
                html: bottomText ? topText + '<br/>' + bottomText : topText,
                image: imgUrl
            };
        },
        getUsersHtml: function (session) {
            const html = [];

            if (session.UserId) {
                html.push(escapeHtml(session.UserName));
            }

            for (let i = 0, length = session.AdditionalUsers.length; i < length; i++) {
                html.push(escapeHtml(session.AdditionalUsers[i].UserName));
            }

            return html.join(', ');
        },
        getUserImage: function (session) {
            if (session.UserId && session.UserPrimaryImageTag) {
                return ApiClient.getUserImageUrl(session.UserId, {
                    tag: session.UserPrimaryImageTag,
                    type: 'Primary'
                });
            }

            return null;
        },
        updateSession: function (row, session) {
            row.classList.remove('deadSession');
            const nowPlayingItem = session.NowPlayingItem;

            if (nowPlayingItem) {
                row.classList.add('playingSession');
                row.querySelector('.btnSessionInfo').classList.remove('hide');
            } else {
                row.classList.remove('playingSession');
                row.querySelector('.btnSessionInfo').classList.add('hide');
            }

            if (session.ServerId && session.SupportedCommands.indexOf('DisplayMessage') !== -1) {
                row.querySelector('.btnSessionSendMessage').classList.remove('hide');
            } else {
                row.querySelector('.btnSessionSendMessage').classList.add('hide');
            }

            const btnSessionPlayPause = row.querySelector('.btnSessionPlayPause');

            if (session.ServerId && nowPlayingItem && session.SupportsRemoteControl) {
                btnSessionPlayPause.classList.remove('hide');
                row.querySelector('.btnSessionStop').classList.remove('hide');
            } else {
                btnSessionPlayPause.classList.add('hide');
                row.querySelector('.btnSessionStop').classList.add('hide');
            }

            const btnSessionPlayPauseIcon = btnSessionPlayPause.querySelector('.material-icons');
            btnSessionPlayPauseIcon.classList.remove('play_arrow', 'pause');
            btnSessionPlayPauseIcon.classList.add(session.PlayState && session.PlayState.IsPaused ? 'play_arrow' : 'pause');

            row.querySelector('.sessionNowPlayingTime').innerText = DashboardPage.getSessionNowPlayingTime(session);
            row.querySelector('.sessionUserName').innerHTML = DashboardPage.getUsersHtml(session);
            row.querySelector('.sessionAppSecondaryText').innerText = DashboardPage.getAppSecondaryText(session);
            const nowPlayingName = DashboardPage.getNowPlayingName(session);
            const nowPlayingInfoElem = row.querySelector('.sessionNowPlayingInfo');

            if (!(nowPlayingName.image && nowPlayingName.image == nowPlayingInfoElem.getAttribute('data-imgsrc'))) {
                nowPlayingInfoElem.innerHTML = nowPlayingName.html;
                nowPlayingInfoElem.setAttribute('data-imgsrc', nowPlayingName.image || '');
            }

            const playbackProgressElem = row.querySelector('.playbackProgress');
            const transcodingProgress = row.querySelector('.transcodingProgress');

            let percent = 100 * session?.PlayState?.PositionTicks / nowPlayingItem?.RunTimeTicks;
            playbackProgressElem.outerHTML = indicators.getProgressHtml(percent || 0, {
                containerClass: 'playbackProgress'
            });

            percent = session?.TranscodingInfo?.CompletionPercentage?.toFixed(1);
            transcodingProgress.outerHTML = indicators.getProgressHtml(percent || 0, {
                containerClass: 'transcodingProgress'
            });

            const imgUrl = DashboardPage.getNowPlayingImageUrl(nowPlayingItem) || '';
            const imgElem = row.querySelector('.sessionNowPlayingContent');

            if (imgUrl != imgElem.getAttribute('data-src')) {
                imgElem.style.backgroundImage = imgUrl ? "url('" + imgUrl + "')" : '';
                imgElem.setAttribute('data-src', imgUrl);

                if (imgUrl) {
                    imgElem.classList.add('sessionNowPlayingContent-withbackground');
                    row.querySelector('.sessionNowPlayingInnerContent').classList.add('darkenContent');
                } else {
                    imgElem.classList.remove('sessionNowPlayingContent-withbackground');
                    row.querySelector('.sessionNowPlayingInnerContent').classList.remove('darkenContent');
                }
            }
        },
        getClientImage: function (connection) {
            const iconUrl = imageHelper.getDeviceIcon(connection);
            return "<img src='" + iconUrl + "' />";
        },
        getNowPlayingImageUrl: function (item) {
            /* Screen width is multiplied by 0.2, as the there is currently no way to get the width of
            elements that aren't created yet. */
            if (item && item.BackdropImageTags && item.BackdropImageTags.length) {
                return ApiClient.getScaledImageUrl(item.Id, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Backdrop',
                    tag: item.BackdropImageTags[0]
                });
            }

            if (item && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length) {
                return ApiClient.getScaledImageUrl(item.ParentBackdropItemId, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Backdrop',
                    tag: item.ParentBackdropImageTags[0]
                });
            }

            if (item && item.BackdropImageTag) {
                return ApiClient.getScaledImageUrl(item.BackdropItemId, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Backdrop',
                    tag: item.BackdropImageTag
                });
            }

            const imageTags = (item || {}).ImageTags || {};

            if (item && imageTags.Thumb) {
                return ApiClient.getScaledImageUrl(item.Id, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Thumb',
                    tag: imageTags.Thumb
                });
            }

            if (item && item.ParentThumbImageTag) {
                return ApiClient.getScaledImageUrl(item.ParentThumbItemId, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Thumb',
                    tag: item.ParentThumbImageTag
                });
            }

            if (item && item.ThumbImageTag) {
                return ApiClient.getScaledImageUrl(item.ThumbItemId, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Thumb',
                    tag: item.ThumbImageTag
                });
            }

            if (item && imageTags.Primary) {
                return ApiClient.getScaledImageUrl(item.Id, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Primary',
                    tag: imageTags.Primary
                });
            }

            if (item && item.PrimaryImageTag) {
                return ApiClient.getScaledImageUrl(item.PrimaryImageItemId, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Primary',
                    tag: item.PrimaryImageTag
                });
            }

            if (item && item.AlbumPrimaryImageTag) {
                return ApiClient.getScaledImageUrl(item.AlbumId, {
                    maxWidth: Math.round(dom.getScreenWidth() * 0.20),
                    type: 'Primary',
                    tag: item.AlbumPrimaryImageTag
                });
            }

            return null;
        },
        systemUpdateTaskKey: 'SystemUpdateTask',
        stopTask: function (btn, id) {
            const page = dom.parentWithClass(btn, 'page');
            ApiClient.stopScheduledTask(id).then(function () {
                pollForInfo(page, ApiClient);
            });
        },
        restart: function (btn) {
            confirm({
                title: globalize.translate('Restart'),
                text: globalize.translate('MessageConfirmRestart'),
                confirmText: globalize.translate('Restart'),
                primary: 'delete'
            }).then(function () {
                const page = dom.parentWithClass(btn, 'page');
                page.querySelector('#btnRestartServer').disabled = true;
                page.querySelector('#btnShutdown').disabled = true;
                ApiClient.restartServer();
            });
        },
        shutdown: function (btn) {
            confirm({
                title: globalize.translate('ButtonShutdown'),
                text: globalize.translate('MessageConfirmShutdown'),
                confirmText: globalize.translate('ButtonShutdown'),
                primary: 'delete'
            }).then(function () {
                const page = dom.parentWithClass(btn, 'page');
                page.querySelector('#btnRestartServer').disabled = true;
                page.querySelector('#btnShutdown').disabled = true;
                ApiClient.shutdownServer();
            });
        }
    };