diff --git a/api/channels.js b/api/channels.js index b340bbd..0681d44 100644 --- a/api/channels.js +++ b/api/channels.js @@ -14,6 +14,9 @@ module.exports = [ const subscribed = user.isSubscribed(id) const instanceOrigin = settings.instance // normalise info, apply watched status + if (!data.second__subCountText && data.subCount) { + data.second__subCountText = converters.subscriberCountToText(data.subCount) + } const watchedVideos = user.getWatchedVideos() if (data.latestVideos) { data.latestVideos.forEach(video => { diff --git a/api/video.js b/api/video.js index 315de3a..0ac4ba7 100644 --- a/api/video.js +++ b/api/video.js @@ -47,11 +47,9 @@ async function renderVideo(videoPromise, {user, id, instanceOrigin}, locals = {} if (!format.second__height && format.resolution) format.second__height = +format.resolution.slice(0, -1) if (!format.second__order) format.second__order = formatOrder(format) } - // process length text + // process length text and view count for (const rec of video.recommendedVideos) { - if (!rec.second__lengthText && rec.lengthSeconds > 0) { - rec.second__lengthText = converters.lengthSecondsToLengthText(rec.lengthSeconds) - } + converters.normaliseVideoInfo(rec) } // get subscription data const subscribed = user.isSubscribed(video.authorId) @@ -63,6 +61,10 @@ async function renderVideo(videoPromise, {user, id, instanceOrigin}, locals = {} rec.watched = watchedVideos.includes(rec.videoId) } } + // normalise view count + if (!video.second__viewCountText && video.viewCount) { + video.second__viewCountText = converters.viewCountToText(video.viewCount) + } return render(200, "pug/video.pug", Object.assign(locals, {video, subscribed, instanceOrigin})) } catch (e) { // show an appropriate error message diff --git a/pug/channel.pug b/pug/channel.pug index 0ab4c34..ac75e4a 100644 --- a/pug/channel.pug +++ b/pug/channel.pug @@ -22,7 +22,7 @@ block content img(src=thumbnail.url width=thumbnail.width height=thumbnail.height alt="").thumbnail-image .about .name= data.author - .subscribers= data.second__subCountText || `${data.subCount} subscribers` + .subscribers= data.second__subCountText +subscribe_button(data.authorId, subscribed, `/channel/${data.authorId}`).subscribe-button.base-border-look .description!= data.descriptionHtml diff --git a/utils/converters.js b/utils/converters.js index 09c0435..95e6093 100644 --- a/utils/converters.js +++ b/utils/converters.js @@ -33,6 +33,7 @@ function lengthSecondsToLengthText(seconds) { * Changes: * - second__lengthText is added, may be [hh:]mm:ss or "LIVE" * - publishedText may be changed to "Live now" + * - second__viewCountText is added */ function normaliseVideoInfo(video) { if (!video.second__lengthText && video.lengthSeconds > 0) { @@ -45,6 +46,9 @@ function normaliseVideoInfo(video) { if (video.publishedText === "0 seconds ago") { video.publishedText = "Live now" } + if (!video.second__viewCountText) { + video.second__viewCountText = viewCountToText(video.viewCount) + } } /** @@ -94,7 +98,34 @@ function tToMediaFragment(t) { } } +function viewCountToText(viewCount) { + return viewCount.toLocaleString("en-US") + " views" +} + +/** + * YT does not give the exact count sometimes but a rounded value, + * e.g. for the subscriber count. + * + * This function returns the text version of the rounded count. + */ +function preroundedCountToText(count) { + for (const scale of [[1e9, "B"], [1e6, "M"], [1e3, "K"]]) { + if (count >= scale[0]) { + // YouTube returns 3 significant figures. At least it does for channels. + const rounded = (count/scale[0]).toPrecision(3) + return `${rounded}${scale[1]}` + } + } + return String(count) +} + +function subscriberCountToText(count) { + return preroundedCountToText(count) + " subscribers" +} + module.exports.timeToPastText = timeToPastText module.exports.lengthSecondsToLengthText = lengthSecondsToLengthText module.exports.normaliseVideoInfo = normaliseVideoInfo module.exports.tToMediaFragment = tToMediaFragment +module.exports.viewCountToText = viewCountToText +module.exports.subscriberCountToText = subscriberCountToText