yt search wip
This commit is contained in:
parent
1339ce27ad
commit
703411a135
7 changed files with 100 additions and 79 deletions
39
app/cyp.js
39
app/cyp.js
|
@ -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; }
|
||||||
|
|
|
@ -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 & 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>
|
||||||
|
|
|
@ -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) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
48
index.js
48
index.js
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue