Channels! (work in progress)
This commit is contained in:
parent
ccd462b62a
commit
bee670ffa8
6 changed files with 175 additions and 9 deletions
74
index.js
74
index.js
|
@ -3,7 +3,8 @@ const express = require("express"),
|
||||||
fs = require("fs"),
|
fs = require("fs"),
|
||||||
ytdl = require("ytdl-core"),
|
ytdl = require("ytdl-core"),
|
||||||
bodyParser = require("body-parser"),
|
bodyParser = require("body-parser"),
|
||||||
youtube = require("scrape-youtube")
|
youtube = require("scrape-youtube"),
|
||||||
|
ytExt = require("youtube-ext")
|
||||||
|
|
||||||
const PORT = process.env.PORT || 8080
|
const PORT = process.env.PORT || 8080
|
||||||
|
|
||||||
|
@ -18,6 +19,7 @@ const searchCacheDur = (process.env.SEARCH_DUR || 24) * 3600000
|
||||||
|
|
||||||
const playerPath = path.join(resources, 'player.html')
|
const playerPath = path.join(resources, 'player.html')
|
||||||
const searchPath = path.join(resources, 'searchPage.html')
|
const searchPath = path.join(resources, 'searchPage.html')
|
||||||
|
const channelPath = path.join(resources, 'channelPage.html')
|
||||||
|
|
||||||
const cssHeader = `<link rel="stylesheet" href="/mainStyle.css">`
|
const cssHeader = `<link rel="stylesheet" href="/mainStyle.css">`
|
||||||
|
|
||||||
|
@ -93,7 +95,6 @@ app.get("/search", async (req, res) => {
|
||||||
var addedHTML = ""
|
var addedHTML = ""
|
||||||
|
|
||||||
var channels = results.channels
|
var channels = results.channels
|
||||||
|
|
||||||
if (channels.length > 0) {
|
if (channels.length > 0) {
|
||||||
|
|
||||||
addedHTML += "<h2><br>Channels:</h2>"
|
addedHTML += "<h2><br>Channels:</h2>"
|
||||||
|
@ -120,6 +121,7 @@ app.get("/search", async (req, res) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (videos.length > 0) {
|
||||||
addedHTML += "<h2><br>Videos:</h2>"
|
addedHTML += "<h2><br>Videos:</h2>"
|
||||||
|
|
||||||
for (let index = 0; index < videos.length; index++) {
|
for (let index = 0; index < videos.length; index++) {
|
||||||
|
@ -150,6 +152,7 @@ app.get("/search", async (req, res) => {
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
res.send(html.replace("{RESULTS}", addedHTML))
|
res.send(html.replace("{RESULTS}", addedHTML))
|
||||||
}
|
}
|
||||||
|
@ -178,6 +181,73 @@ app.get("/search", async (req, res) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
app.get("/channel", async (req, res) => {
|
||||||
|
var section = req.query.s || "videos"
|
||||||
|
var channel = req.query.q || "UChcrBJNJLZucy3TPyGyAY2g"
|
||||||
|
|
||||||
|
var html = fs.readFileSync(channelPath).toString()
|
||||||
|
|
||||||
|
var info = await ytExt.channelInfo(channel, { includeVideos: true })
|
||||||
|
|
||||||
|
var videos = info.videos
|
||||||
|
console.log(info)
|
||||||
|
var addedHTML = ""
|
||||||
|
if (section == "videos") {
|
||||||
|
for (let index = 0; index < videos.length; index++) {
|
||||||
|
const result = videos[index];
|
||||||
|
if (result.title != undefined) {
|
||||||
|
addedHTML += `
|
||||||
|
<div class="col-xxl-4 col-sm-6 resultContainer">
|
||||||
|
<div class="videoResult container-fluid row">
|
||||||
|
<div class="col-lg-6 thumbparent">
|
||||||
|
<a class="videoLink" href="/watch?v=${result.id}">
|
||||||
|
<img class="thumbnail" src="${result.thumbnails[0].url}">
|
||||||
|
<p style="display: block; text-align: left;">${result.duration.pretty}</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<a class="videoLink" href="/watch?v=${result.id}">
|
||||||
|
<p style="font-size: 1.25rem;">${result.title || "No Title Found"}</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
} else {
|
||||||
|
addedHTML = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} else if (section == "about") {
|
||||||
|
addedHTML += `
|
||||||
|
<div id="description">
|
||||||
|
<h2>Description: <br></h2>
|
||||||
|
<p>${info.description}</p>
|
||||||
|
<p>Subscribers: ${info.subscribers.pretty}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addedHTML == "") {
|
||||||
|
addedHTML = `<h2>Failed to load ${section}! Sorry... T^T<h2>`
|
||||||
|
}
|
||||||
|
|
||||||
|
html = html.replace("{RESULTS}", addedHTML)
|
||||||
|
|
||||||
|
html = html.replace("{CHANNEL_NAME}", info.name)
|
||||||
|
html = html.replace("{CHANNEL_DESC}", info.description)
|
||||||
|
|
||||||
|
for (let index = 0; index < 3; index++) {
|
||||||
|
html = html.replace("{CHANNEL_ID}", info.id)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
html = html.replace("{CHANNEL_PFP}", info.thumbnails[0].url)
|
||||||
|
html = html.replace("{SUB_COUNT}", info.subscribers.pretty)
|
||||||
|
|
||||||
|
res.send(html)
|
||||||
|
})
|
||||||
|
|
||||||
app.get("/video", async (req, res) => {
|
app.get("/video", async (req, res) => {
|
||||||
var id = req.query.q || req.query.v
|
var id = req.query.q || req.query.v
|
||||||
var range = req.headers.range
|
var range = req.headers.range
|
||||||
|
|
28
package-lock.json
generated
28
package-lock.json
generated
|
@ -12,9 +12,18 @@
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"scrape-youtube": "^2.4.0",
|
"scrape-youtube": "^2.4.0",
|
||||||
|
"youtube-ext": "^1.1.16",
|
||||||
"ytdl-core": "^4.11.5"
|
"ytdl-core": "^4.11.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@fastify/busboy": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/accepts": {
|
"node_modules/accepts": {
|
||||||
"version": "1.3.8",
|
"version": "1.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||||
|
@ -699,6 +708,17 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/undici": {
|
||||||
|
"version": "5.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici/-/undici-5.27.0.tgz",
|
||||||
|
"integrity": "sha512-l3ydWhlhOJzMVOYkymLykcRRXqbUaQriERtR70B9LzNkZ4bX52Fc8wbTDneMiwo8T+AemZXvXaTx+9o5ROxrXg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@fastify/busboy": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/unpipe": {
|
"node_modules/unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
|
@ -723,6 +743,14 @@
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/youtube-ext": {
|
||||||
|
"version": "1.1.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/youtube-ext/-/youtube-ext-1.1.16.tgz",
|
||||||
|
"integrity": "sha512-vyzHSwxlCAwqWUxZKJ/5g139BgnbmZFTy9I0nxDwqlbAh74dB1LjayCoB5BgLaaIkSMruEQwlf5bF+EeR235qA==",
|
||||||
|
"dependencies": {
|
||||||
|
"undici": "^5.26.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ytdl-core": {
|
"node_modules/ytdl-core": {
|
||||||
"version": "4.11.5",
|
"version": "4.11.5",
|
||||||
"resolved": "https://registry.npmjs.org/ytdl-core/-/ytdl-core-4.11.5.tgz",
|
"resolved": "https://registry.npmjs.org/ytdl-core/-/ytdl-core-4.11.5.tgz",
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"scrape-youtube": "^2.4.0",
|
"scrape-youtube": "^2.4.0",
|
||||||
|
"youtube-ext": "^1.1.16",
|
||||||
"ytdl-core": "^4.11.5"
|
"ytdl-core": "^4.11.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
51
resources/channelPage.html
Normal file
51
resources/channelPage.html
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
|
||||||
|
integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="./mainStyle.css">
|
||||||
|
|
||||||
|
<title>SimpleTube</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="titleBar" class="row container-fluid">
|
||||||
|
<div class="col-6">
|
||||||
|
<h1><a href="/">Simpletube</a></h1>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<form action="/search">
|
||||||
|
<input type="text" placeholder="Search" name="q">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="channelBar" class="row container-fluid">
|
||||||
|
<p>* Channels are still a work in progress.</p>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<img src="{CHANNEL_PFP}" class="pfp">
|
||||||
|
<p>Subscribers: {SUB_COUNT}</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<h1>{CHANNEL_NAME}</h1>
|
||||||
|
<p>{CHANNEL_DESC}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-6">
|
||||||
|
<a class="videoLink channelLink" href="?q={CHANNEL_ID}&s=video">Videos</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<a class="videoLink channelLink" href="?q={CHANNEL_ID}&s=about">About</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<main>
|
||||||
|
<div class="container-fluid row" style="margin: 0; padding: 0;">
|
||||||
|
{RESULTS}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -25,7 +25,7 @@
|
||||||
</div>
|
</div>
|
||||||
<main>
|
<main>
|
||||||
<h1>Welcome to SimpleTube</h1>
|
<h1>Welcome to SimpleTube</h1>
|
||||||
<p>SimpleTube is a Youtube client that aims to be free of Javascript, as small as possible, and lack annoyances.
|
<p>SimpleTube is a Youtube client that aims to be free and fast. (and have no ads!)
|
||||||
<br>
|
<br>
|
||||||
Currently SimpleTube does not have a trending tab or anything, so please use the search function at the top!
|
Currently SimpleTube does not have a trending tab or anything, so please use the search function at the top!
|
||||||
<br>
|
<br>
|
||||||
|
|
|
@ -124,7 +124,7 @@ p {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 720px) {
|
/* @media (max-width: 720px) {
|
||||||
.resultContainer {
|
.resultContainer {
|
||||||
max-width: 450px;
|
max-width: 450px;
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ p {
|
||||||
.resultContainer {
|
.resultContainer {
|
||||||
max-width: 550px;
|
max-width: 550px;
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
|
|
||||||
.resultContainer {
|
.resultContainer {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
@ -195,3 +195,19 @@ main {
|
||||||
hr {
|
hr {
|
||||||
margin: 20px auto;
|
margin: 20px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#channelBar {
|
||||||
|
background-color: rgb(15, 10, 25);
|
||||||
|
border-bottom: 2px gray solid;
|
||||||
|
padding: 20px;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channelLink {
|
||||||
|
color: white;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
border: 2px white solid;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 10px
|
||||||
|
}
|
Loading…
Reference in a new issue