mirror of
				https://git.sr.ht/~cadence/cloudtube
				synced 2025-10-27 19:59:08 +00:00 
			
		
		
		
	Add local fetch option
This commit is contained in:
		
							parent
							
								
									f78ee4ff0f
								
							
						
					
					
						commit
						9a890574d5
					
				| @ -37,9 +37,8 @@ module.exports = [ | ||||
| 								if (isNaN(provided)) data[key] = null | ||||
| 								else data[key] = +provided | ||||
| 							} else if (setting.type === "boolean") { | ||||
| 								if (provided === "true") data[key] = true | ||||
| 								else if (provided === "false") data[key] = false | ||||
| 								else data[key] = null | ||||
| 								if (provided === "1") data[key] = 1 | ||||
| 								else data[key] = 0 | ||||
| 							} else { | ||||
| 								throw new Error("Unsupported setting type: "+setting.type) | ||||
| 							} | ||||
|  | ||||
							
								
								
									
										91
									
								
								api/video.js
									
									
									
									
									
								
							
							
						
						
									
										91
									
								
								api/video.js
									
									
									
									
									
								
							| @ -35,41 +35,34 @@ function formatOrder(format) { | ||||
| 	return -total | ||||
| } | ||||
| 
 | ||||
| module.exports = [ | ||||
| 	{ | ||||
| 		route: "/watch", methods: ["GET"], code: async ({req, url}) => { | ||||
| 			const id = url.searchParams.get("v") | ||||
| 			const user = getUser(req) | ||||
| 			const settings = user.getSettingsOrDefaults() | ||||
| 			const instanceOrigin = settings.instance | ||||
| 			const outURL = `${instanceOrigin}/api/v1/videos/${id}` | ||||
| 			try { | ||||
| 				const video = await fetch(outURL).then(res => res.json()) | ||||
| 				if (!video) throw new Error("The instance returned null.") | ||||
| 				if (video.error) throw new InstanceError(video.error, video.identifier) | ||||
| 				// video data additional processing
 | ||||
| 				for (const format of video.formatStreams.concat(video.adaptiveFormats)) { | ||||
| 					if (!format.second__height && format.resolution) format.second__height = +format.resolution.slice(0, -1) | ||||
| 					if (!format.second__order) format.second__order = formatOrder(format) | ||||
| 				} | ||||
| 				for (const rec of video.recommendedVideos) { | ||||
| 					if (!rec.second__lengthText && rec.lengthSeconds > 0) { | ||||
| 						rec.second__lengthText = converters.lengthSecondsToLengthText(rec.lengthSeconds) | ||||
| 					} | ||||
| 				} | ||||
| 				const subscribed = user.isSubscribed(video.authorId) | ||||
| 				return render(200, "pug/video.pug", {video, subscribed, instanceOrigin}) | ||||
| 			} catch (e) { | ||||
| 				let message = pug.render("pre= error", {error: e.stack || e.toString()}) | ||||
| 				if (e instanceof fetch.FetchError) { | ||||
| 					const template = ` | ||||
| async function renderVideo(videoPromise, {user, id, instanceOrigin}) { | ||||
| 	try { | ||||
| 		const video = await videoPromise | ||||
| 		if (!video) throw new Error("The instance returned null.") | ||||
| 		if (video.error) throw new InstanceError(video.error, video.identifier) | ||||
| 		// video data additional processing
 | ||||
| 		for (const format of video.formatStreams.concat(video.adaptiveFormats)) { | ||||
| 			if (!format.second__height && format.resolution) format.second__height = +format.resolution.slice(0, -1) | ||||
| 			if (!format.second__order) format.second__order = formatOrder(format) | ||||
| 		} | ||||
| 		for (const rec of video.recommendedVideos) { | ||||
| 			if (!rec.second__lengthText && rec.lengthSeconds > 0) { | ||||
| 				rec.second__lengthText = converters.lengthSecondsToLengthText(rec.lengthSeconds) | ||||
| 			} | ||||
| 		} | ||||
| 		const subscribed = user.isSubscribed(video.authorId) | ||||
| 		return render(200, "pug/video.pug", {video, subscribed, instanceOrigin}) | ||||
| 	} catch (e) { | ||||
| 		let message = pug.render("pre= error", {error: e.stack || e.toString()}) | ||||
| 		if (e instanceof fetch.FetchError) { | ||||
| 			const template = ` | ||||
| p The selected instance, #[code= instanceOrigin], did not respond correctly. | ||||
| p Requested URL: #[a(href=url)= url] | ||||
| ` | ||||
| 					message = pug.render(template, {instanceOrigin, url: outURL}) | ||||
| 				} else if (e instanceof InstanceError) { | ||||
| 					if (e.identifier === "RATE_LIMITED_BY_YOUTUBE") { | ||||
| 						const template = ` | ||||
| 			message = pug.render(template, {instanceOrigin, url: outURL}) | ||||
| 		} else if (e instanceof InstanceError) { | ||||
| 			if (e.identifier === "RATE_LIMITED_BY_YOUTUBE") { | ||||
| 				const template = ` | ||||
| .blocked-explanation | ||||
| 	img(src="/static/images/instance-blocked.svg" width=552 height=96) | ||||
| 	.rows | ||||
| @ -96,18 +89,40 @@ p. | ||||
| p. | ||||
| 	This situation #[em will] be improved in the future! | ||||
| ` | ||||
| 						message = pug.render(template, {instanceOrigin}) | ||||
| 					} else { | ||||
| 						const template = ` | ||||
| 				message = pug.render(template, {instanceOrigin}) | ||||
| 			} else { | ||||
| 				const template = ` | ||||
| p #[strong= error.message] | ||||
| if error.identifier | ||||
| 	p #[code= error.identifier] | ||||
| p That error was generated by #[code= instanceOrigin]. | ||||
| ` | ||||
| 						message = pug.render(template, {instanceOrigin, error: e}) | ||||
| 					} | ||||
| 				message = pug.render(template, {instanceOrigin, error: e}) | ||||
| 			} | ||||
| 		} | ||||
| 		return render(500, "pug/video.pug", {video: {videoId: id}, error: true, message}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| module.exports = [ | ||||
| 	{ | ||||
| 		route: "/watch", methods: ["GET", "POST"], upload: true, code: async ({req, url, body}) => { | ||||
| 			const user = getUser(req) | ||||
| 			const settings = user.getSettingsOrDefaults() | ||||
| 			if (req.method === "GET") { | ||||
| 				const id = url.searchParams.get("v") | ||||
| 				if (!settings.local) { | ||||
| 					const instanceOrigin = settings.instance | ||||
| 					const outURL = `${instanceOrigin}/api/v1/videos/${id}` | ||||
| 					const videoPromise = fetch(outURL).then(res => res.json()) | ||||
| 					return renderVideo(videoPromise, {user, id, instanceOrigin}) | ||||
| 				} else { | ||||
| 					return render(200, "pug/local-video.pug", {id}) | ||||
| 				} | ||||
| 				return render(500, "pug/video.pug", {video: {videoId: id}, error: true, message}) | ||||
| 			} else { // req.method === "POST"
 | ||||
| 				const video = JSON.parse(new URLSearchParams(body.toString()).get("video")) | ||||
| 				const videoPromise = Promise.resolve(video) | ||||
| 				return renderVideo(videoPromise, {user}) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
							
								
								
									
										18
									
								
								html/static/js/local-video.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								html/static/js/local-video.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| import {q} from "./elemjs/elemjs.js" | ||||
| 
 | ||||
| const status = q("#status") | ||||
| const form = q("#form") | ||||
| const data = q("#video-data") | ||||
| 
 | ||||
| fetch(`http://localhost:3000/api/v1/videos/${id}`).then(res => res.json()).then(root => { | ||||
| 	if (root.error) throw new Error(root) | ||||
| 	data.value = JSON.stringify(root) | ||||
| 	form.submit() | ||||
| 	status.textContent = "Submitting..." | ||||
| }).catch(e => { | ||||
| 	if (e.message.includes("NetworkError")) { | ||||
| 		status.textContent = "Connection failed. Make sure you're running your own instance locally." | ||||
| 	} else { | ||||
| 		status.innerText = e.toString() | ||||
| 	} | ||||
| }) | ||||
							
								
								
									
										16
									
								
								pug/local-video.pug
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								pug/local-video.pug
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| extends includes/layout | ||||
| 
 | ||||
| include includes/video-list-item | ||||
| include includes/subscribe-button | ||||
| 
 | ||||
| block head | ||||
|   title Fetching... - CloudTube | ||||
|   script(type="module" src=getStaticURL("html", "/static/js/local-video.js")) | ||||
|   script const id = !{JSON.stringify(id)} | ||||
| 
 | ||||
| block content | ||||
|   main.video-error-page | ||||
|     h2 Fetching video | ||||
|     p#status Waiting... | ||||
|     form(method="post")#form.local-video-form | ||||
|       input(type="hidden" name="video")#video-data | ||||
| @ -20,7 +20,7 @@ mixin select(id, description, disabled, options) | ||||
|     label.description(for=id)= description | ||||
|     select(id=id name=id disabled=disabled).border-look | ||||
|       each option in options | ||||
|         option(value=option.value selected=(option.value === settings[id]))= option.text | ||||
|         option(value=option.value selected=(option.value == settings[id]))= option.text | ||||
| 
 | ||||
| block head | ||||
|   title Settings - CloudTube | ||||
| @ -38,8 +38,13 @@ block content | ||||
|         ]) | ||||
| 
 | ||||
|         +select("save_history", "Watch history", false, [ | ||||
|           {value: "", text: "Don't save"}, | ||||
|           {value: "yes", text: "Save"} | ||||
|           {value: "0", text: "Don't save"}, | ||||
|           {value: "1", text: "Save"} | ||||
|         ]) | ||||
| 
 | ||||
|         +select("local", "Fetch videos", false, [ | ||||
|           {value: "0", text: "Remote instance"}, | ||||
|           {value: "1", text: "Locally"} | ||||
|         ]) | ||||
| 
 | ||||
|       .save-settings | ||||
|  | ||||
| @ -7,6 +7,10 @@ const constants = { | ||||
| 		save_history: { | ||||
| 			type: "boolean", | ||||
| 			default: false | ||||
| 		}, | ||||
| 		local: { | ||||
| 			type: "boolean", | ||||
| 			default: false | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
|  | ||||
| @ -23,6 +23,11 @@ const deltas = [ | ||||
| 	function() { | ||||
| 		db.prepare("ALTER TABLE Channels ADD COLUMN refreshed INTEGER") | ||||
| 			.run() | ||||
| 	}, | ||||
| 	// 2: Settings +local
 | ||||
| 	function() { | ||||
| 		db.prepare("ALTER TABLE Settings ADD COLUMN local INTEGER DEFAULT 0") | ||||
| 			.run() | ||||
| 	} | ||||
| ] | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Cadence Ember
						Cadence Ember