Compare commits

...

2 Commits

Author SHA1 Message Date
Lamp 85122ba2e9 Merge branch 'master' of gitea.moe:lamp/u2b.cx 2023-03-27 12:56:41 -07:00
Lamp 6e363f0787 Add direct id function 2023-03-27 12:53:52 -07:00
3 changed files with 73 additions and 14 deletions

View File

@ -11,6 +11,10 @@ The server will search YouTube for `<query>`, pick the first result, pick the be
Example: [`https://u2b.cx/nyan cat`](https://u2b.cx/nyan%20cat) will give you the raw mp4 file for [https://www.youtube.com/watch?v=QH2-TGUlwu4](https://www.youtube.com/watch?v=QH2-TGUlwu4), the first result on [https://www.youtube.com/results?search_query=nyan+cat](https://www.youtube.com/results?search_query=nyan+cat). Example: [`https://u2b.cx/nyan cat`](https://u2b.cx/nyan%20cat) will give you the raw mp4 file for [https://www.youtube.com/watch?v=QH2-TGUlwu4](https://www.youtube.com/watch?v=QH2-TGUlwu4), the first result on [https://www.youtube.com/results?search_query=nyan+cat](https://www.youtube.com/results?search_query=nyan+cat).
### GET `https://u2b.cx/id/<video id>`
Bypasses search to serve the video directly.
## Installation ## Installation

View File

@ -5,11 +5,10 @@ from threading import Event
from datetime import datetime, timedelta from datetime import datetime, timedelta
from os import environ from os import environ
import logging import logging
import re
def getBestMp4UrlFromInfo(info): def getBestMp4UrlFromInfo(info):
entries = info.get('entries') formats = info['entries'][0].get('formats') if info.get("entries") else info.get("formats")
if not entries: return None
formats = entries[0].get('formats')
if not formats: return None if not formats: return None
valid = list(filter(lambda x: x['ext'] == "mp4" and x['vcodec'] != 'none' and x['acodec'] != 'none', formats)) valid = list(filter(lambda x: x['ext'] == "mp4" and x['vcodec'] != 'none' and x['acodec'] != 'none', formats))
if not valid: return None if not valid: return None
@ -38,7 +37,10 @@ class Handler(BaseHTTPRequestHandler):
self.send_error(404) self.send_error(404)
return return
query = unquote(self.path[1:]) path = unquote(self.path)
match = re.match("\/(?:id\/|(?:https?:\/\/)?(?:(?:www\.)?youtube\.com\/(?:watch\?v=|shorts\/)|youtu\.be\/))([A-Za-z0-9_-]{11})", path)
query = match[1] if match else "ytsearch:" + path[1:]
dict = query_dicts.get(query) dict = query_dicts.get(query)
if not dict or 'expire' in dict and datetime.now() >= dict['expire']: if not dict or 'expire' in dict and datetime.now() >= dict['expire']:
@ -50,7 +52,7 @@ class Handler(BaseHTTPRequestHandler):
ips_running_ytdl.append(client_ip) ips_running_ytdl.append(client_ip)
query_dicts[query] = dict = {'event': Event()} query_dicts[query] = dict = {'event': Event()}
with YoutubeDL() as ydl: with YoutubeDL() as ydl:
info = ydl.extract_info("ytsearch:" + query, download=False) info = ydl.extract_info(query, download=False)
url = getBestMp4UrlFromInfo(info) url = getBestMp4UrlFromInfo(info)
dict['url'] = url dict['url'] = url
dict['expire'] = getExpireDatetime(url) dict['expire'] = getExpireDatetime(url)

View File

@ -10,21 +10,74 @@
img { img {
vertical-align: middle; vertical-align: middle;
} }
code {
white-space: nowrap;
color: brown;
}
</style> </style>
</head><body> </head><body>
<i><h2>Problem:</h2></i> <h2 id="Problem">Problem:</h2>
<img src="VRChat_2023-03-21_22-37-32.758_1920x1080.png" alt="A video player in VRChat" style="max-height: 50vh" /> <img src="VRChat_2023-03-21_22-37-32.758_1920x1080.png" alt="A video player in VRChat" style="max-height: 50vh" />
<p>No video! No PC to put video! No URLs memorized! What do???</p> <p>No video! No PC to put video! No URLs memorized! What do???</p>
<i><h2>SOLUTION!</h2></i> <h2 id="Solution">SOLUTION!</h2>
<ol> <ol>
<li>Type the following exactly into the URL bar: <code>https://u2b.cx/</code></li> <li>Type the following exactly into the URL bar: <code>https://u2b.cx/</code></li>
<li>TIP: Press the <img alt="copy" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADUAAAA0CAMAAAAdZIDnAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAADYUExURRwiKxwqMhsvNhwvNhwuNhwmLxwkLRo9QxhKUBdQVhZTWRZSWBdOVBhGTBsyORlCSRVVXBAdIw8XHQ8VGxNUWRdPVRs0Oxs3PhZUWhJFShlESxJJTxwkLBhJTxArMRVYXho6QBAqLxo6QRhLURApLxVZXxo7Qf///8rKy/39/VZYWfv7+2ZnaFBRU2RlZhRaYBo7QhhIThAxNhVXXRlARxRUWw8VHBZRWBswOBE2PRRYXxlCSBJKUBFITRNKURZTWxlFSxwpMRwtNRo8QxlGTBlFTBo4PxwjLAQa4UkAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADhSURBVEhL7dbXDoIwFIDhulFxD3Di3nvvPd7/jSxyIhjBHJF4I99VT5M/KU1ISr5gsdpw7A4oiNPFuD1eJNbnl6pAMBSOoEVjcTHieJiREox4ymQKRiyenjGdycKEJeToXTB5mLCEAq2KJZiwyhWzkv1ZVX2AjWdaVQ0W6oyu4IRUHTZlb6oGrJuv32ZWSoZWrbaeivppJYMdmXbVgUTtZ9Gu3jErJbNS0ld1e/S90R/AhDXk6NtmNIYJaTKd0crKzmHGWSzF1xfh2MVqjbXZ7vb3ihyOy9MZaXS5StHHCLkBefveLzCsA+EAAAAASUVORK5CYII=" width="24" height="24" /> button on the lower left 😉</li> <li>TIP: Press the copy <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADUAAAA0CAMAAAAdZIDnAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAADYUExURRwiKxwqMhsvNhwvNhwuNhwmLxwkLRo9QxhKUBdQVhZTWRZSWBdOVBhGTBsyORlCSRVVXBAdIw8XHQ8VGxNUWRdPVRs0Oxs3PhZUWhJFShlESxJJTxwkLBhJTxArMRVYXho6QBAqLxo6QRhLURApLxVZXxo7Qf///8rKy/39/VZYWfv7+2ZnaFBRU2RlZhRaYBo7QhhIThAxNhVXXRlARxRUWw8VHBZRWBswOBE2PRRYXxlCSBJKUBFITRNKURZTWxlFSxwpMRwtNRo8QxlGTBlFTBo4PxwjLAQa4UkAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADhSURBVEhL7dbXDoIwFIDhulFxD3Di3nvvPd7/jSxyIhjBHJF4I99VT5M/KU1ISr5gsdpw7A4oiNPFuD1eJNbnl6pAMBSOoEVjcTHieJiREox4ymQKRiyenjGdycKEJeToXTB5mLCEAq2KJZiwyhWzkv1ZVX2AjWdaVQ0W6oyu4IRUHTZlb6oGrJuv32ZWSoZWrbaeivppJYMdmXbVgUTtZ9Gu3jErJbNS0ld1e/S90R/AhDXk6NtmNIYJaTKd0crKzmHGWSzF1xfh2MVqjbXZ7vb3ihyOy9MZaXS5StHHCLkBefveLzCsA+EAAAAASUVORK5CYII=" width="24" height="24" /> button on the lower left 😉</li>
<li>Now after that just type what you wanna watch. For example, if you wanna play nyan cat, type in like so: <code>https://u2b.cx/nyan cat</code></li> <li>Now after that just type what you want to watch. Here are just a few examples:</li>
<li>The first result from YouTube will start playing. YAAY!</li> <ul>
<li>TIP: When you wanna watch again, press the <img alt="paste" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADUAAAA0CAMAAAAdZIDnAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAADeUExURRwiKxwqMhsvNxwvNhwtNRsyORhGTBdOVBZRVhZSWBZRVxhLURo+RBwkLBs2PBdPVRNUWg8ZHw8VGw8WHBAdIxVXXBlFSxwlLhhKUBJHTA8WGxZUWho5QBszOhJKTxo6QRVYXhArMBhJTxwkLRo7QRVZXxAqLxo7QhApL6Skpf///1ZXWVxdXvf39/Hx8bKzs7KysrGxsrCxsUlKTBVZXhs4PhVYXREwNhwjLBsvNhRUWxlBRxlBSBE2PRsxOBwoMBZTWhNMUhFITRJHThRYXxdMUhs3PhlFTBo9RBwuNtYqywoAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADwSURBVEhL7dbZboJQEIDhcalacatWRLHWvfUUF1AsLlXrUvX9X6gcnFhCghliYlLDf3OYSb6Qk3ABXFEgGCIWRgHwEInGHonFhUTSQql05ilLLfecFzkqSEXc0CrJZVO9yDhSq/CXvVZxolarNwCaLZyotQVTvXlV776y9c9VhzF8cuSqPhRFYV3GzAM3ttwUs8r2Toczd4XQJDdQ53x1v6rfHwxUVdU0ja6G+D1Z4e4vN3U5X9nzlb1r1AgnapbSxzhR+5QMgEk8hyOx6Yz/SM29XewrveBqKa6+19Q2290PRwCGPovsiYmH4wl5DuAXf0nZllJEIHwAAAAASUVORK5CYII=" width="24" height="24" /> button on the lower right so you don't have to type it all again 👌</li> <li><code>https://u2b.cx/nyan cat</code></li>
<li><code>https://u2b.cx/never gonna give you up</code></li>
<li><code>https://u2b.cx/arabic cat</code></li>
<li><code>https://u2b.cx/gangnam style</code></li>
<li><code>https://u2b.cx/miku</code></li>
<li><code>https://u2b.cx/despacito</code></li>
<li><code>https://u2b.cx/baby shark dance</code></li>
<li><code>https://u2b.cx/technology connections</code></li>
</ul>
<li>Just like with the Discord music bots, the first search result from YouTube will start playing.</li>
<li>TIP: Next time, press the paste <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADUAAAA0CAMAAAAdZIDnAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAADeUExURRwiKxwqMhsvNxwvNhwtNRsyORhGTBdOVBZRVhZSWBZRVxhLURo+RBwkLBs2PBdPVRNUWg8ZHw8VGw8WHBAdIxVXXBlFSxwlLhhKUBJHTA8WGxZUWho5QBszOhJKTxo6QRVYXhArMBhJTxwkLRo7QRVZXxAqLxo7QhApL6Skpf///1ZXWVxdXvf39/Hx8bKzs7KysrGxsrCxsUlKTBVZXhs4PhVYXREwNhwjLBsvNhRUWxlBRxlBSBE2PRsxOBwoMBZTWhNMUhFITRJHThRYXxdMUhs3PhlFTBo9RBwuNtYqywoAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADwSURBVEhL7dbZboJQEIDhcalacatWRLHWvfUUF1AsLlXrUvX9X6gcnFhCghliYlLDf3OYSb6Qk3ABXFEgGCIWRgHwEInGHonFhUTSQql05ilLLfecFzkqSEXc0CrJZVO9yDhSq/CXvVZxolarNwCaLZyotQVTvXlV776y9c9VhzF8cuSqPhRFYV3GzAM3ttwUs8r2Toczd4XQJDdQ53x1v6rfHwxUVdU0ja6G+D1Z4e4vN3U5X9nzlb1r1AgnapbSxzhR+5QMgEk8hyOx6Yz/SM29XewrveBqKa6+19Q2290PRwCGPovsiYmH4wl5DuAXf0nZllJEIHwAAAAASUVORK5CYII=" width="24" height="24" /> button on the lower right so you don't have to type the whole thing all over again 👍</li>
</ol> </ol>
<i><h3>Demo</h3></i> <h3 id="Demo">Demo</h3>
<video controls style="max-height: 80vh"><source src="com.vrchat.oculus.quest-20230321-223838.mp4" type="video/mp4"/></video> <video controls style="max-height: 80vh"><source src="com.vrchat.oculus.quest-20230321-223838.mp4" type="video/mp4"/></video>
<i><h3>Problems?</h3></i> <h3 id="Advanced_usage">Advanced Usage</h3>
<p>If you encounter any issues, please submit on <a href="https://gitea.moe/lamp/u2b.cx/issues">issue tracker</a>. You can use Discord or GitHub account.</p> <p>For PC users or world creators who want to make a particular video work on Quest, u2b.cx can also be used as an alternative to sites like <a href="https://nextnex.com" target="_blank">nextnex.com</a> and <a href="https://ytdlh.cf/" target="_blank">ytdlh.cf</a> by using the following syntax: <code>https://u2b.cx/id/&lt;youtube video id&gt;</code></p>
<p>Paste a YouTube URL here to have it converted for you: <input id="input" type="text" placeholder="https://www.youtube.com/watch?v=dQw4w9WgXcQ" style="width: 309px" /> <span id="output_container" style="display: none"><code><span id="output"></span></code> <button id="copy_btn">Copy</button></span></p>
<script>
var input = document.getElementById("input");
var output = document.getElementById("output");
var output_container = document.getElementById("output_container");
var copy_btn = document.getElementById("copy_btn");
input.oninput = function() {
var match = input.value.match(/[A-Za-z0-9_-]{11}/);
if (match) {
output.innerText = `https://u2b.cx/id/${match[0]}`;
output_container.style.display = '';
} else {
output.innerText = '';
output_container.style.display = "none";
}
};
copy_btn.onclick = function() {
navigator.clipboard.writeText(output.innerText);
copy_btn.disabled = true;
copy_btn.innerText = "Copied";
setTimeout(function(){
copy_btn.disabled = false;
copy_btn.innerText = "Copy";
}, 1000);
}
</script>
<p>For potential support of prefixing implementations, known YouTube URL formats will also be parsed for the ID. This means any one of the following will bypass the YouTube search and look up the video directly:</p>
<ul>
<li><code>https://u2b.cx/id/dQw4w9WgXcQ</code> (recommended)</li>
<li><code>https://u2b.cx/https://www.youtube.com/watch?v=dQw4w9WgXcQ</code></li>
<li><code>https://u2b.cx/https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdQw4w9WgXcQ</code></li>
<li><code>https://u2b.cx/http://youtube.com/watch?v=dQw4w9WgXcQ</code></li>
<li><code>https://u2b.cx/youtube.com/watch?v=dQw4w9WgXcQ</code></li>
<li><code>https://u2b.cx/https://youtu.be/dQw4w9WgXcQ</code></li>
<li><code>https://u2b.cx/https://youtube/shorts/dQw4w9WgXcQ</code></li>
</ul>
<p>Anything else will be sent to YouTube search, which will probably still get what you want.</p>
<h3 id="Problems">Problems?</h3>
<p>If you encounter any issues such as a service outage, please submit to the <a href="https://gitea.moe/lamp/u2b.cx/issues/new">issue tracker</a>. Thanks.</p>
</body></html> </body></html>