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