yt search wip

This commit is contained in:
Ondrej Zara 2020-03-15 23:14:55 +01:00
parent 1339ce27ad
commit 703411a135
No known key found for this signature in database
GPG key ID: B0A5751E616840C5
7 changed files with 100 additions and 79 deletions

View file

@ -782,12 +782,7 @@ class Selection {
} }
} }
class HasApp extends HTMLElement { class Component extends HTMLElement {
get _app() { return this.closest("cyp-app"); }
get _mpd() { return this._app.mpd; }
}
class Component extends HasApp {
constructor(options = {}) { constructor(options = {}) {
super(); super();
if (options.selection) { this.selection = new Selection(this, options.selection); } if (options.selection) { this.selection = new Selection(this, options.selection); }
@ -802,6 +797,9 @@ class Component extends HasApp {
}); });
} }
get _app() { return this.closest("cyp-app"); }
get _mpd() { return this._app.mpd; }
_onAppLoad() {} _onAppLoad() {}
_onComponentChange(_component, _isThis) {} _onComponentChange(_component, _isThis) {}
} }
@ -1056,7 +1054,7 @@ class Player extends Component {
customElements.define("cyp-player", Player); customElements.define("cyp-player", Player);
class Item extends HasApp { class Item extends HTMLElement {
constructor() { constructor() {
super(); super();
this.addEventListener("click", _ => this.onClick()); this.addEventListener("click", _ => this.onClick());
@ -1376,12 +1374,31 @@ function decodeChunk(byteArray) {
} }
class YT extends Component { class YT extends Component {
_onAppLoad() { connectedCallback() {
this.querySelector(".download").addEventListener("click", _ => this._download()); super.connectedCallback();
this.querySelector(".search-download").addEventListener("click", _ => this._search());
this.querySelector(".clear").addEventListener("click", _ => this._clear()); const form = node("form", {}, "", this);
const input = node("input", {type:"text"}, "", form);
button({icon:"magnify"}, "", form);
form.addEventListener("submit", e => {
e.preventDefault();
const query = input.value.trim();
if (!query.length) { return; }
this._doSearch(query, form);
});
} }
async _doSearch(query, form) {
let response = await fetch(`/youtube?q=${encodeURIComponent(query)}`);
let data = await response.json();
clear(this);
this.appendChild(form);
console.log(data);
}
_download() { _download() {
let url = prompt("Please enter a YouTube URL:"); let url = prompt("Please enter a YouTube URL:");
if (!url) { return; } if (!url) { return; }

View file

@ -42,14 +42,7 @@
<cyp-queue></cyp-queue> <cyp-queue></cyp-queue>
<cyp-playlists></cyp-playlists> <cyp-playlists></cyp-playlists>
<cyp-library></cyp-library> <cyp-library></cyp-library>
<cyp-yt> <cyp-yt></cyp-yt>
<header>
<button class="download" data-icon="download">Download</button>
<button class="search-download" data-icon="magnify">Search &amp; Download</button>
<button class="clear" data-icon="close">Clear</button>
</header>
<pre></pre>
</cyp-yt>
<cyp-settings> <cyp-settings>
<dl> <dl>
<dt>Theme</dt> <dt>Theme</dt>

View file

@ -1,11 +1,7 @@
import Selection from "./selection.js"; import Selection from "./selection.js";
export class HasApp extends HTMLElement {
get _app() { return this.closest("cyp-app"); }
get _mpd() { return this._app.mpd; }
}
export default class Component extends HasApp { export default class Component extends HTMLElement {
constructor(options = {}) { constructor(options = {}) {
super(); super();
if (options.selection) { this.selection = new Selection(this, options.selection); } if (options.selection) { this.selection = new Selection(this, options.selection); }
@ -20,6 +16,9 @@ export default class Component extends HasApp {
}); });
} }
get _app() { return this.closest("cyp-app"); }
get _mpd() { return this._app.mpd; }
_onAppLoad() {} _onAppLoad() {}
_onComponentChange(_component, _isThis) {} _onComponentChange(_component, _isThis) {}
} }

View file

@ -12,12 +12,31 @@ function decodeChunk(byteArray) {
} }
class YT extends Component { class YT extends Component {
_onAppLoad() { connectedCallback() {
this.querySelector(".download").addEventListener("click", _ => this._download()); super.connectedCallback();
this.querySelector(".search-download").addEventListener("click", _ => this._search());
this.querySelector(".clear").addEventListener("click", _ => this._clear()); const form = html.node("form", {}, "", this);
const input = html.node("input", {type:"text"}, "", form);
html.button({icon:"magnify"}, "", form);
form.addEventListener("submit", e => {
e.preventDefault();
const query = input.value.trim();
if (!query.length) { return; }
this._doSearch(query, form);
});
} }
async _doSearch(query, form) {
let response = await fetch(`/youtube?q=${encodeURIComponent(query)}`);
let data = await response.json();
html.clear(this);
this.appendChild(form);
console.log(data);
}
_download() { _download() {
let url = prompt("Please enter a YouTube URL:"); let url = prompt("Please enter a YouTube URL:");
if (!url) { return; } if (!url) { return; }

View file

@ -1,7 +1,7 @@
import * as html from "./html.js"; import * as html from "./html.js";
import { HasApp } from "./component.js";
export default class Item extends HasApp {
export default class Item extends HTMLElement {
constructor() { constructor() {
super(); super();
this.addEventListener("click", _ => this.onClick()); this.addEventListener("click", _ => this.onClick());

View file

@ -1,43 +0,0 @@
import * as html from "./html.js";
import * as conf from "./conf.js";
const OPEN = "open";
const collator = new Intl.Collator(conf.locale, {usage:"search", sensitivity:"base"});
export default class Search extends EventTarget {
constructor(parent) {
super();
this._node = html.node("label", {className:"search"});
this._input = html.node("input", {type:"text"}, "", this._node);
html.icon("magnify", this._node);
this._node.addEventListener("click", e => {
if (e.target == this._input) { return; }
if (this._node.classList.contains(OPEN)) {
this.reset();
this.dispatchEvent(new Event("input"));
} else {
this._node.classList.add(OPEN);
}
});
this._input.addEventListener("input", e => {
this.dispatchEvent(new Event("input"));
});
}
getNode() { return this._node; }
match(str) {
let q = this._input.value.trim();
if (!q) { return true; }
let len = q.length;
return str.split(" ").some(str => collator.compare(q, str.substring(0, len)) == 0);
}
reset() {
this._input.value = "";
this._node.classList.remove(OPEN);
}
}

View file

@ -6,6 +6,27 @@ let tickets = [];
const cmd = "youtube-dl"; const cmd = "youtube-dl";
function escape(arg) {
return `'${arg.replace(/'/g, `'\\''`)}'`;
}
function searchYoutube(q, response) {
response.setHeader("Content-Type", "text/plain"); // necessary for firefox to read by chunks
console.log("YouTube searching", q);
q = escape(`ytsearch10:${q}`);
const command = `${cmd} -j ${q} | jq "{id,title}" | jq -s .`;
require("child_process").exec(command, {}, (error, stdout, stderr) => {
if (error) {
console.log("error", error);
response.writeHead(500);
response.end(error.message);
} else {
response.end(stdout);
}
});
}
function downloadYoutube(q, 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
@ -34,12 +55,22 @@ function downloadYoutube(q, response) {
}); });
} }
function handleYoutube(request, response) { function handleYoutubeSearch(url, response) {
let q = url.searchParams.get("q");
if (q) {
searchYoutube(q, response);
} else {
response.writeHead(404);
response.end();
}
}
function handleYoutubeDownload(request, response) {
let str = ""; let str = "";
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 q = require("querystring").parse(str)["q"]; let q = require("querystring").parse(str)["id"];
if (q) { if (q) {
downloadYoutube(q, response); downloadYoutube(q, response);
} else { } else {
@ -62,11 +93,16 @@ function handleTicket(request, response) {
} }
function onRequest(request, response) { function onRequest(request, response) {
switch (true) { const url = new URL(request.url, "http://localhost");
case request.method == "POST" && request.url == "/youtube":
return handleYoutube(request, response);
case request.method == "POST" && request.url == "/ticket": switch (true) {
case request.method == "GET" && url.pathname == "/youtube":
return handleYoutubeSearch(url, response);
case request.method == "POST" && url.pathname == "/youtube":
return handleYoutubeDownload(request, response);
case request.method == "POST" && url.pathname == "/ticket":
return handleTicket(request, response); return handleTicket(request, response);
default: default: