Compare commits

..

2 Commits

Author SHA1 Message Date
Lomanic 21629de594
Properly rewrite URL params for youtu.be links
Change occurs in converters.rewriteVideoDescription().
Previously the URL could end up with `?` in it twice, this ensures there
will be at most one.
2021-07-02 00:48:19 +12:00
Cadence Ember f62fce4fea
Highlight current chapter in video description 2021-07-02 00:41:50 +12:00
9 changed files with 94 additions and 5 deletions

View File

@ -1,4 +1,4 @@
import {q} from "/static/js/elemjs/elemjs.js" import {q} from "/static/js/elemjs/elemjs.js"
import {SubscribeButton} from "/static/js/subscribe.js" import {SubscribeButton} from "/static/js/modules/SubscribeButton.js"
new SubscribeButton(q("#subscribe")) new SubscribeButton(q("#subscribe"))

View File

@ -0,0 +1,53 @@
import {q, qa, ElemJS} from "./elemjs/elemjs.js"
class Chapter {
constructor(linkElement) {
this.link = new ElemJS(linkElement)
this.time = +linkElement.getAttribute("data-clickable-timestamp")
}
}
let chapters = [...document.querySelectorAll("[data-clickable-timestamp]")].map(linkElement => new Chapter(linkElement))
chapters.sort((a, b) => a.time - b.time)
function getCurrentChapter(time) {
const candidates = chapters.filter(chapter => chapter.time <= time)
if (candidates.length > 0) {
return candidates[candidates.length - 1]
} else {
return null
}
}
const video = q("#video")
const description = q("#description")
const regularBackground = "var(--regular-background)"
const highlightBackground = "var(--highlight-background)"
const paddingWidth = 4
let lastChapter = null
setInterval(() => {
const currentChapter = getCurrentChapter(video.currentTime)
if (currentChapter !== lastChapter) {
// Style link
if (lastChapter) {
lastChapter.link.removeClass("timestamp--active")
}
if (currentChapter) {
currentChapter.link.class("timestamp--active")
}
// Style background
if (currentChapter) {
const {offsetTop, offsetHeight} = currentChapter.link.element;
const offsetBottom = offsetTop + offsetHeight
let gradient = `linear-gradient(to bottom,`
+ ` ${regularBackground} ${offsetTop - paddingWidth}px, ${highlightBackground} ${offsetTop - paddingWidth}px,`
+ ` ${highlightBackground} ${offsetBottom + paddingWidth}px, ${regularBackground} ${offsetBottom + paddingWidth}px)`
console.log(gradient)
description.style.background = gradient
} else {
description.style.background = ""
}
}
lastChapter = currentChapter
}, 1000)

View File

@ -1,5 +1,5 @@
import {q, qa, ElemJS} from "/static/js/elemjs/elemjs.js" import {q, qa, ElemJS} from "/static/js/elemjs/elemjs.js"
import {SubscribeButton} from "/static/js/subscribe.js" import {SubscribeButton} from "/static/js/modules/SubscribeButton.js"
const video = q("#video") const video = q("#video")
const audio = q("#audio") const audio = q("#audio")

View File

@ -1,5 +1,5 @@
import {qa} from "/static/js/elemjs/elemjs.js" import {qa} from "/static/js/elemjs/elemjs.js"
import {MarkWatchedButton} from "/static/js/mark-watched.js" import {MarkWatchedButton} from "/static/js/modules/MarkWatchedButton.js"
for (const button of qa(".mark-watched__button")) { for (const button of qa(".mark-watched__button")) {
new MarkWatchedButton(button) new MarkWatchedButton(button)

View File

@ -9,6 +9,7 @@ block head
else else
title Error - CloudTube title Error - CloudTube
script(type="module" src=getStaticURL("html", "/static/js/player.js")) script(type="module" src=getStaticURL("html", "/static/js/player.js"))
script(type="module" src=getStaticURL("html", "/static/js/chapter-highlight.js"))
script const data = !{JSON.stringify({...video, continuous})} script const data = !{JSON.stringify({...video, continuous})}
block content block content
@ -73,7 +74,7 @@ block content
a(href=`https://www.youtube.com/watch?v=${video.videoId}#cloudtube`).border-look YouTube a(href=`https://www.youtube.com/watch?v=${video.videoId}#cloudtube`).border-look YouTube
a(href=`https://redirect.invidious.io/watch?v=${video.videoId}`).border-look Invidious a(href=`https://redirect.invidious.io/watch?v=${video.videoId}`).border-look Invidious
.description!= video.descriptionHtml .description#description!= video.descriptionHtml
//- Standard view //- Standard view
aside(style=continuous ? "display: none" : "")#standard-related-videos.related-videos aside(style=continuous ? "display: none" : "")#standard-related-videos.related-videos

View File

@ -93,13 +93,18 @@
flex-shrink: 0 flex-shrink: 0
.description .description
position: relative
font-size: 17px font-size: 17px
line-height: 1.4
word-break: break-word word-break: break-word
margin: 16px 4px 4px 4px margin: 16px 4px 4px 4px
background-color: c.$bg-accent-area background-color: c.$bg-accent-area
padding: 12px padding: 12px
border-radius: 4px border-radius: 4px
--regular-background: #{c.$bg-accent-area}
--highlight-background: #{c.$bg-darker}
.subscribe-form .subscribe-form
display: inline-block display: inline-block
@ -149,3 +154,26 @@
.actor .actor
margin: 4px 0px 10px margin: 4px 0px 10px
// Chapter highlights
.timestamp--active
margin: 4px 0px
display: inline-block
&::after
display: block
position: absolute
left: 0
right: 0
height: calc(1.4em + 4px)
transform: translateY(-1.4em) translateY(-4px)
padding-right: 6px
text-align: right
content: ""
line-height: 1.6
color: #fff
border: solid black
border-width: 2px 0px

View File

@ -66,8 +66,15 @@ function rewriteVideoDescription(descriptionHtml, id) {
// https://www.youtube.com/watch?v=fhum63fAwrI www.youtube.com/watch?v=<videoid> // https://www.youtube.com/watch?v=fhum63fAwrI www.youtube.com/watch?v=<videoid>
// https://www.youtube.com/watch?v=i-szWOrc3Mo www.youtube.com/<channelname> (unsupported by cloudtube currently) // https://www.youtube.com/watch?v=i-szWOrc3Mo www.youtube.com/<channelname> (unsupported by cloudtube currently)
// https://www.youtube.com/watch?v=LSG71wbKpbQ www.youtube.com/channel/<id> // https://www.youtube.com/watch?v=LSG71wbKpbQ www.youtube.com/channel/<id>
// https://www.youtube.com/watch?v=RiEkOKFOG3s youtu.be/<videoid> with params
descriptionHtml = descriptionHtml.replace(new RegExp(`<a href="https?://(?:www\\.)?youtu\\.be/(${constants.regex.video_id})([^"]*)">([^<]+)</a>`, "g"), `<a href="/watch?v=$1$2">$3</a>`) descriptionHtml = descriptionHtml.replace(new RegExp(`<a href="https?://(?:www\\.)?youtu\\.be/(${constants.regex.video_id})[?]?([^"]*)">([^<]+)</a>`, "g"), (_, id, params, innerText) => {
if (params) {
return `<a href="/watch?v=${id}&${params}">${innerText}</a>`
} else {
return `<a href="/watch?v=${id}">${innerText}</a>`
}
})
descriptionHtml = descriptionHtml.replace(new RegExp(`<a href="https?://(?:www\\.)?youtu(?:\\.be|be\\.com)/([^"]*)">([^<]+)<\/a>`, "g"), `<a href="/$1">$2</a>`) descriptionHtml = descriptionHtml.replace(new RegExp(`<a href="https?://(?:www\\.)?youtu(?:\\.be|be\\.com)/([^"]*)">([^<]+)<\/a>`, "g"), `<a href="/$1">$2</a>`)
descriptionHtml = descriptionHtml.replace(new RegExp(`(?:([0-9]*):)?([0-5]?[0-9]):([0-5][0-9])`, "g"), (_, hours, minutes, seconds) => { descriptionHtml = descriptionHtml.replace(new RegExp(`(?:([0-9]*):)?([0-5]?[0-9]):([0-5][0-9])`, "g"), (_, hours, minutes, seconds) => {
let timeURL, timeDisplay, timeSeconds let timeURL, timeDisplay, timeSeconds