mirror of
				https://git.sr.ht/~cadence/cloudtube
				synced 2025-10-27 19:59:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			101 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			101 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| function timeToPastText(timestamp) {
 | |
| 	const difference = Date.now() - timestamp
 | |
| 	return [
 | |
| 		["year", 365 * 24 * 60 * 60 * 1000],
 | |
| 		["month", 30 * 24 * 60 * 60 * 1000],
 | |
| 		["week", 7 * 24 * 60 * 60 * 1000],
 | |
| 		["day", 24 * 60 * 60 * 1000],
 | |
| 		["hour", 60 * 60 * 1000],
 | |
| 		["minute", 60 * 1000],
 | |
| 		["second", 1 * 1000]
 | |
| 	].reduce((acc, /** @type {[string, number]} */ [unitName, unitValue]) => {
 | |
| 		if (acc) return acc
 | |
| 		if (difference > unitValue) {
 | |
| 			const number = Math.floor(difference / unitValue)
 | |
| 			const pluralUnit = unitName + (number == 1 ? "" : "s")
 | |
| 			return `${number} ${pluralUnit} ago`
 | |
| 		}
 | |
| 	}, null) || "just now"
 | |
| }
 | |
| 
 | |
| function lengthSecondsToLengthText(seconds) {
 | |
| 	let parts = [Math.floor(seconds/3600), Math.floor(seconds/60)%60, seconds%60]
 | |
| 	if (parts[0] === 0) parts = parts.slice(1)
 | |
| 	return parts.map((x, i) => i === 0 ? x : (x+"").padStart(2, "0")).join(":")
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Second and Invidious don't return quite the same data. This
 | |
|  * function normalises them so that all the useful properties are
 | |
|  * available no matter the kind of instance. The video is modified
 | |
|  * in-place.
 | |
|  *
 | |
|  * Changes:
 | |
|  * - second__lengthText is added, may be [hh:]mm:ss or "LIVE"
 | |
|  * - publishedText may be changed to "Live now"
 | |
|  */
 | |
| function normaliseVideoInfo(video) {
 | |
| 	if (!video.second__lengthText && video.lengthSeconds > 0) {
 | |
| 		video.second__lengthText = lengthSecondsToLengthText(video.lengthSeconds)
 | |
| 	}
 | |
| 	if (!video.second__lengthText && video.lengthSeconds === 0) {
 | |
| 		video.second__lengthText = "LIVE"
 | |
| 		video.liveNow = true
 | |
| 	}
 | |
| 	if (video.publishedText === "0 seconds ago") {
 | |
| 		video.publishedText = "Live now"
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * YT supports a time parameter t in these possible formats:
 | |
|  * - [digits] -> seconds
 | |
|  * - ([digits]:)?[digits]:[digits] -> hours?, minutes, seconds
 | |
|  * - ([digits]h)?([digits]m)?([digits]s)? -> hours?, minutes?, seconds
 | |
|  *
 | |
|  * Try our best to get something suitable out. Fail by returning empty
 | |
|  * string, meaning nothing suitable was recognised.
 | |
|  */
 | |
| function tToMediaFragment(t) {
 | |
| 	let resolved = ""
 | |
| 
 | |
| 	if (!t || t.length > 10) { // don't parse missing values, don't parse too long strings
 | |
| 		// skip processing
 | |
| 	} else if (t.match(/^[0-9.,]+$/)) {
 | |
| 		resolved = t
 | |
| 	} else if (t.includes(":")) {
 | |
| 		resolved = t.split(":").map(s => s.padStart(2, "0")).join(":") // need to pad each to length 2 for npt
 | |
| 	} else if (t.match(/[hms]/)) {
 | |
| 		let buffer = ""
 | |
| 		let sum = 0
 | |
| 		const multipliers = new Map([
 | |
| 			["h", 60*60],
 | |
| 			["m", 60],
 | |
| 			["s", 1]
 | |
| 		])
 | |
| 		for (const char of t) {
 | |
| 			console.log(char)
 | |
| 			if (char.match(/[0-9]/)) {
 | |
| 				buffer += char
 | |
| 			} else if (char.match(/[hms]/)) {
 | |
| 				sum += +buffer * multipliers.get(char)
 | |
| 				buffer = ""
 | |
| 			} else {
 | |
| 				buffer = ""
 | |
| 			}
 | |
| 		}
 | |
| 		resolved = String(sum)
 | |
| 	}
 | |
| 
 | |
| 	if (resolved) {
 | |
| 		return "#t=npt:" + resolved
 | |
| 	} else {
 | |
| 		return ""
 | |
| 	}
 | |
| }
 | |
| 
 | |
| module.exports.timeToPastText = timeToPastText
 | |
| module.exports.lengthSecondsToLengthText = lengthSecondsToLengthText
 | |
| module.exports.normaliseVideoInfo = normaliseVideoInfo
 | |
| module.exports.tToMediaFragment = tToMediaFragment
 | 
