yt tuning
This commit is contained in:
parent
0e339fc88d
commit
e0ab191d72
6 changed files with 85 additions and 74 deletions
43
app/app.css
43
app/app.css
|
@ -38,6 +38,7 @@ button {
|
|||
flex-direction: row;
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
white-space: nowrap;
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
border: none;
|
||||
|
@ -576,8 +577,6 @@ nav ul li.active {
|
|||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
#yt header {
|
||||
display: flex;
|
||||
|
@ -632,36 +631,32 @@ nav ul li.active {
|
|||
#yt li:nth-child(odd) {
|
||||
background-color: var(--bg-alt);
|
||||
}
|
||||
#yt .go {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
justify-content: center;
|
||||
#yt header {
|
||||
border-bottom: 1px solid var(--fg);
|
||||
}
|
||||
#yt .go:disabled {
|
||||
position: relative;
|
||||
#yt header button + button {
|
||||
margin-left: 16px;
|
||||
}
|
||||
#yt .go:disabled::before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
border-top: 3px solid var(--primary);
|
||||
border-radius: 50%;
|
||||
animation: rotate linear 3s infinite;
|
||||
#yt .clear {
|
||||
margin-left: auto;
|
||||
}
|
||||
#yt p {
|
||||
margin: 16px 8px;
|
||||
#yt pre {
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
@keyframes rotate {
|
||||
#yt.pending header {
|
||||
background-image: linear-gradient(var(--primary), var(--primary));
|
||||
background-repeat: no-repeat;
|
||||
background-size: 25% 4px;
|
||||
animation: bar ease-in-out 3s alternate infinite;
|
||||
}
|
||||
@keyframes bar {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
background-position: 0 100%;
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
background-position: 100% 100%;
|
||||
}
|
||||
}
|
||||
#settings {
|
||||
|
|
|
@ -35,6 +35,7 @@ button {
|
|||
|
||||
.flex-row;
|
||||
display: inline-flex;
|
||||
white-space: nowrap;
|
||||
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
|
|
|
@ -1,39 +1,33 @@
|
|||
#yt {
|
||||
.component;
|
||||
.component;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
header {
|
||||
border-bottom: 1px solid var(--fg);
|
||||
|
||||
.go {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
justify-content: center;
|
||||
|
||||
&:disabled {
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
border-top: 3px solid var(--primary);
|
||||
border-radius: 50%;
|
||||
animation: rotate linear 3s infinite;
|
||||
}
|
||||
button + button {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 16px 8px;
|
||||
.clear {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
pre {
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
&.pending header {
|
||||
background-image: linear-gradient(var(--primary), var(--primary));
|
||||
background-repeat: no-repeat;
|
||||
background-size: 25% 4px;
|
||||
animation: bar ease-in-out 3s alternate infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
@keyframes bar {
|
||||
0% { background-position: 0 100%; }
|
||||
100% { background-position: 100% 100%; }
|
||||
}
|
||||
|
|
|
@ -57,8 +57,12 @@
|
|||
<ul></ul>
|
||||
</section>
|
||||
<section id="yt">
|
||||
<button class="go" data-icon="download">Go!</button>
|
||||
<p></p>
|
||||
<header>
|
||||
<button class="download" data-icon="download">Download</button>
|
||||
<button class="search-download" data-icon="magnify">Search & Download</button>
|
||||
<button class="clear" data-icon="close">Clear</button>
|
||||
</header>
|
||||
<pre></pre>
|
||||
</section>
|
||||
<section id="settings">
|
||||
<dl>
|
||||
|
|
45
app/js/yt.js
45
app/js/yt.js
|
@ -8,41 +8,58 @@ let node;
|
|||
const decoder = new TextDecoder("utf-8");
|
||||
|
||||
function decodeChunk(byteArray) {
|
||||
return decoder.decode(byteArray);
|
||||
// \r => \n
|
||||
return decoder.decode(byteArray).replace(/\u000d/g, "\n");
|
||||
}
|
||||
|
||||
async function onClick(e) {
|
||||
let url = prompt("Please enter a YouTube URL:");
|
||||
if (!url) { return; }
|
||||
async function post(q) {
|
||||
let pre = node.querySelector("pre");
|
||||
html.clear(pre);
|
||||
|
||||
let button = e.target;
|
||||
button.disabled = true;
|
||||
|
||||
let p = node.querySelector("p");
|
||||
p.textContent = "";
|
||||
node.classList.add("pending");
|
||||
|
||||
let body = new URLSearchParams();
|
||||
body.set("url", url);
|
||||
body.set("q", q);
|
||||
let response = await fetch("/youtube", {method:"POST", body});
|
||||
|
||||
let reader = response.body.getReader();
|
||||
while (true) {
|
||||
let { done, value } = await reader.read();
|
||||
if (done) { break; }
|
||||
p.textContent += decodeChunk(value);
|
||||
pre.textContent += decodeChunk(value);
|
||||
pre.scrollTop = pre.scrollHeight;
|
||||
}
|
||||
reader.releaseLock();
|
||||
|
||||
button.disabled = false;
|
||||
node.classList.remove("pending");
|
||||
|
||||
if (response.status == 200) {
|
||||
mpd.command(`update ${mpd.escape(conf.ytPath)}`);
|
||||
}
|
||||
}
|
||||
|
||||
function download() {
|
||||
let url = prompt("Please enter a YouTube URL:");
|
||||
if (!url) { return; }
|
||||
|
||||
post(url);
|
||||
}
|
||||
|
||||
function search() {
|
||||
let q = prompt("Please enter a search string:");
|
||||
if (!q) { return; }
|
||||
post(`ytsearch:${q}`);
|
||||
}
|
||||
|
||||
function clear() {
|
||||
html.clear(node.querySelector("pre"));
|
||||
}
|
||||
|
||||
export async function activate() {}
|
||||
|
||||
export function init(n) {
|
||||
node = n;
|
||||
|
||||
let button = node.querySelector(".go").addEventListener("click", onClick);
|
||||
node.querySelector(".download").addEventListener("click", e => download());
|
||||
node.querySelector(".search-download").addEventListener("click", e => search());
|
||||
node.querySelector(".clear").addEventListener("click", e => clear());
|
||||
}
|
||||
|
|
12
index.js
12
index.js
|
@ -5,16 +5,16 @@ const port = Number(process.argv[2]) || 8080;
|
|||
const cmd = "youtube-dl";
|
||||
//const cmd = "./test.sh";
|
||||
|
||||
function downloadYoutube(url, response) {
|
||||
function downloadYoutube(q, response) {
|
||||
response.setHeader("Content-Type", "text/plain"); // necessary for firefox to read by chunks
|
||||
// response.setHeader("Content-Type", "text/plain; charset=utf-8");
|
||||
|
||||
// FIXME create directory
|
||||
console.log("YouTube downloading", url);
|
||||
console.log("YouTube downloading", q);
|
||||
let args = [
|
||||
"-f", "bestaudio",
|
||||
"-o", `${__dirname}/_youtube/%(title)s-%(id)s.%(ext)s`,
|
||||
url
|
||||
q
|
||||
]
|
||||
let child = require("child_process").spawn(cmd, args);
|
||||
|
||||
|
@ -39,9 +39,9 @@ function handleYoutube(request, response) {
|
|||
request.setEncoding("utf8");
|
||||
request.on("data", chunk => str += chunk);
|
||||
request.on("end", () => {
|
||||
let url = require("querystring").parse(str)["url"];
|
||||
if (url) {
|
||||
downloadYoutube(url, response);
|
||||
let q = require("querystring").parse(str)["q"];
|
||||
if (q) {
|
||||
downloadYoutube(q, response);
|
||||
} else {
|
||||
response.writeHead(404);
|
||||
response.end();
|
||||
|
|
Loading…
Reference in a new issue