mirror of
https://git.sr.ht/~cadence/cloudtube
synced 2024-12-22 13:07:00 +00:00
Add database and subscribe button
This commit is contained in:
parent
2af05e43a9
commit
f24e1bb39c
3
.gitignore
vendored
3
.gitignore
vendored
@ -5,4 +5,5 @@
|
||||
.vscode
|
||||
|
||||
# Auto-generated files
|
||||
node_modules
|
||||
node_modules
|
||||
/db
|
@ -1,12 +1,16 @@
|
||||
const fetch = require("node-fetch")
|
||||
const {render} = require("pinski/plugins")
|
||||
const constants = require("./utils/constants")
|
||||
const {fetchChannel} = require("./utils/youtube")
|
||||
const {getUser} = require("./utils/getuser")
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
route: "/channel/([A-Za-z0-9-_]+)", methods: ["GET"], code: async ({fill}) => {
|
||||
route: `/channel/(${constants.regex.ucid})`, methods: ["GET"], code: async ({req, fill}) => {
|
||||
const id = fill[0]
|
||||
const data = await fetch(`http://localhost:3000/api/v1/channels/${id}`).then(res => res.json())
|
||||
return render(200, "pug/channel.pug", {data})
|
||||
const data = await fetchChannel(id)
|
||||
const user = getUser(req)
|
||||
const subscribed = user.isSubscribed(id)
|
||||
return render(200, "pug/channel.pug", {data, subscribed})
|
||||
}
|
||||
}
|
||||
]
|
||||
|
56
api/formapi.js
Normal file
56
api/formapi.js
Normal file
@ -0,0 +1,56 @@
|
||||
const {redirect} = require("pinski/plugins")
|
||||
const db = require("./utils/db")
|
||||
const constants = require("./utils/constants")
|
||||
const {getToken} = require("./utils/getuser")
|
||||
const validate = require("./utils/validate")
|
||||
const V = validate.V
|
||||
const {fetchChannel} = require("./utils/youtube")
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
route: `/formapi/(un|)subscribe/(${constants.regex.ucid})`, methods: ["POST"], upload: true, code: async ({req, fill, body}) => {
|
||||
const add = !fill[0]
|
||||
const ucid = fill[1]
|
||||
|
||||
return new V()
|
||||
.with(validate.presetLoad({body}))
|
||||
.with(validate.presetURLParamsBody())
|
||||
.last(async state => {
|
||||
const {params} = state
|
||||
const responseHeaders = {}
|
||||
const token = getToken(req, responseHeaders)
|
||||
|
||||
if (add) {
|
||||
await fetchChannel(ucid)
|
||||
db.prepare("INSERT OR IGNORE INTO Subscriptions (token, ucid) VALUES (?, ?)").run(token, ucid)
|
||||
} else {
|
||||
db.prepare("DELETE FROM Subscriptions WHERE token = ? AND ucid = ?").run(token, ucid)
|
||||
}
|
||||
|
||||
if (params.has("referrer")) {
|
||||
return {
|
||||
statusCode: 303,
|
||||
contentType: "application/json",
|
||||
headers: Object.assign(responseHeaders, {
|
||||
Location: params.get("referrer")
|
||||
}),
|
||||
content: {
|
||||
status: "ok"
|
||||
}
|
||||
}
|
||||
return redirect(params.get("referrer"), 303)
|
||||
} else {
|
||||
return {
|
||||
statusCode: 200,
|
||||
contentType: "application/json",
|
||||
headers: responseHeaders,
|
||||
content: {
|
||||
status: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.go()
|
||||
}
|
||||
}
|
||||
]
|
12
api/utils/constants.js
Normal file
12
api/utils/constants.js
Normal file
@ -0,0 +1,12 @@
|
||||
const constants = {
|
||||
caching: {
|
||||
csrf_time: 4*60*60*1000
|
||||
},
|
||||
|
||||
regex: {
|
||||
ucid: "[A-Za-z0-9-_]+",
|
||||
video_id: "[A-Za-z0-9-_]+"
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = constants
|
8
api/utils/db.js
Normal file
8
api/utils/db.js
Normal file
@ -0,0 +1,8 @@
|
||||
const sqlite = require("better-sqlite3")
|
||||
const pj = require("path").join
|
||||
const fs = require("fs")
|
||||
|
||||
const dir = pj(__dirname, "../../db")
|
||||
fs.mkdirSync(pj(dir, "backups"), {recursive: true})
|
||||
const db = new sqlite(pj(dir, "cloudtube.db"))
|
||||
module.exports = db
|
78
api/utils/getuser.js
Normal file
78
api/utils/getuser.js
Normal file
@ -0,0 +1,78 @@
|
||||
const crypto = require("crypto")
|
||||
const {parse: parseCookie} = require("cookie")
|
||||
|
||||
const constants = require("./constants")
|
||||
const db = require("./db")
|
||||
|
||||
function getToken(req, responseHeaders) {
|
||||
if (!req.headers.cookie) req.headers.cookie = ""
|
||||
const cookie = parseCookie(req.headers.cookie)
|
||||
const token = cookie.token
|
||||
if (token) return token
|
||||
if (responseHeaders) { // we should create a token
|
||||
const setCookie = responseHeaders["set-cookie"] || []
|
||||
const token = crypto.randomBytes(18).toString("base64").replace(/\W/g, "_")
|
||||
setCookie.push(`token=${token}; Path=/; Max-Age=2147483648; HttpOnly; SameSite=Lax`)
|
||||
responseHeaders["set-cookie"] = setCookie
|
||||
return token
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
class User {
|
||||
constructor(token) {
|
||||
this.token = token
|
||||
}
|
||||
|
||||
getSubscriptions() {
|
||||
if (this.token) {
|
||||
return db.prepare("SELECT ucid FROM Subscriptions WHERE token = ?").pluck().all(ucid)
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
isSubscribed(ucid) {
|
||||
if (this.token) {
|
||||
return !!db.prepare("SELECT * FROM Subscriptions WHERE token = ? AND ucid = ?").get([this.token, ucid])
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} responseHeaders supply this to create a token
|
||||
*/
|
||||
function getUser(req, responseHeaders) {
|
||||
const token = getToken(req, responseHeaders)
|
||||
return new User(token)
|
||||
}
|
||||
|
||||
function generateCSRF() {
|
||||
const token = crypto.randomBytes(16).toString("hex")
|
||||
const expires = Date.now() + constants.caching.csrf_time
|
||||
db.prepare("INSERT INTO CSRFTokens (token, expires) VALUES (?, ?)").run(token, expires)
|
||||
return token
|
||||
}
|
||||
|
||||
function checkCSRF(token) {
|
||||
const row = db.prepare("SELECT * FROM CSRFTokens WHERE token = ? AND expires > ?").get(token, Date.now())
|
||||
if (row) {
|
||||
db.prepare("DELETE FROM CSRFTokens WHERE token = ?").run(token)
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function cleanCSRF() {
|
||||
db.prepare("DELETE FROM CSRFTokens WHERE expires <= ?").run(Date.now())
|
||||
}
|
||||
cleanCSRF()
|
||||
setInterval(cleanCSRF, constants.caching.csrf_time).unref()
|
||||
|
||||
module.exports.getToken = getToken
|
||||
module.exports.generateCSRF = generateCSRF
|
||||
module.exports.checkCSRF = checkCSRF
|
||||
module.exports.getUser = getUser
|
55
api/utils/upgradedb.js
Normal file
55
api/utils/upgradedb.js
Normal file
@ -0,0 +1,55 @@
|
||||
const pj = require("path").join
|
||||
const db = require("./db")
|
||||
|
||||
const deltas = [
|
||||
// 0: from empty file, +DatabaseVersion, +Subscriptions
|
||||
function() {
|
||||
db.prepare("CREATE TABLE DatabaseVersion (version INTEGER NOT NULL, PRIMARY KEY (version))")
|
||||
.run()
|
||||
db.prepare("CREATE TABLE Subscriptions (token TEXT NOT NULL, ucid TEXT NOT NULL, PRIMARY KEY (token, ucid))")
|
||||
.run()
|
||||
db.prepare("CREATE TABLE Channels (ucid TEXT NOT NULL, name TEXT NOT NULL, icon_url TEXT, PRIMARY KEY (ucid))")
|
||||
.run()
|
||||
db.prepare("CREATE TABLE CSRFTokens (token TEXT NOT NULL, expires INTEGER NOT NULL, PRIMARY KEY (token))")
|
||||
.run()
|
||||
}
|
||||
]
|
||||
|
||||
async function createBackup(entry) {
|
||||
const filename = `db/backups/cloudtube.db.bak-v${entry-1}`
|
||||
process.stdout.write(`Backing up current to ${filename}... `)
|
||||
await db.backup(pj(__dirname, "../../", filename))
|
||||
process.stdout.write("done.\n")
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} entry
|
||||
* @param {boolean} log
|
||||
*/
|
||||
function runDelta(entry, log) {
|
||||
process.stdout.write(`Upgrading database to version ${entry}... `)
|
||||
deltas[entry]()
|
||||
db.prepare("DELETE FROM DatabaseVersion").run()
|
||||
db.prepare("INSERT INTO DatabaseVersion (version) VALUES (?)").run(entry)
|
||||
process.stdout.write("done.\n")
|
||||
}
|
||||
|
||||
module.exports = async function() {
|
||||
let currentVersion = -1
|
||||
const newVersion = deltas.length - 1
|
||||
|
||||
try {
|
||||
currentVersion = db.prepare("SELECT version FROM DatabaseVersion").pluck().get()
|
||||
} catch (e) {} // if the table doesn't exist yet then we don't care
|
||||
|
||||
if (currentVersion !== newVersion) {
|
||||
// go through the entire upgrade sequence
|
||||
for (let entry = currentVersion+1; entry <= newVersion; entry++) {
|
||||
// Back up current version
|
||||
if (entry > 0) await createBackup(entry)
|
||||
|
||||
// Run delta
|
||||
runDelta(entry)
|
||||
}
|
||||
}
|
||||
}
|
83
api/utils/validate.js
Normal file
83
api/utils/validate.js
Normal file
@ -0,0 +1,83 @@
|
||||
class V {
|
||||
constructor() {
|
||||
this.chain = []
|
||||
this.state = {}
|
||||
this.finished = false
|
||||
this.endValue = null
|
||||
}
|
||||
|
||||
with(preset) {
|
||||
this.check(...preset)
|
||||
return this
|
||||
}
|
||||
|
||||
check(conditionCallback, elseCallback) {
|
||||
this.chain.push(() => {
|
||||
if (!conditionCallback(this.state)) this._end(elseCallback(this.state))
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
last(callback) {
|
||||
this.chain.push(() => {
|
||||
this._end(callback(this.state))
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
go() {
|
||||
for (const s of this.chain) {
|
||||
s()
|
||||
if (this.finished) return this.endValue
|
||||
}
|
||||
return {
|
||||
statusCode: 500,
|
||||
contentType: "application/json",
|
||||
content: {
|
||||
error: "Reached end of V chain without response"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_end(value) {
|
||||
this.finished = true
|
||||
this.endValue = value
|
||||
}
|
||||
}
|
||||
|
||||
function presetLoad(additions) {
|
||||
return [
|
||||
state => {
|
||||
Object.assign(state, additions)
|
||||
return true
|
||||
},
|
||||
null
|
||||
]
|
||||
}
|
||||
|
||||
function presetURLParamsBody() {
|
||||
return [
|
||||
state => {
|
||||
try {
|
||||
state.params = new URLSearchParams(state.body.toString())
|
||||
return true
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return false
|
||||
}
|
||||
},
|
||||
() => {
|
||||
return {
|
||||
statusCode: 400,
|
||||
contentType: "application/json",
|
||||
content: {
|
||||
error: "Could not parse body as URLSearchParams"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
module.exports.V = V
|
||||
module.exports.presetLoad = presetLoad
|
||||
module.exports.presetURLParamsBody = presetURLParamsBody
|
15
api/utils/youtube.js
Normal file
15
api/utils/youtube.js
Normal file
@ -0,0 +1,15 @@
|
||||
const fetch = require("node-fetch")
|
||||
const db = require("./db")
|
||||
|
||||
async function fetchChannel(ucid) {
|
||||
// fetch
|
||||
const channel = await fetch(`http://localhost:3000/api/v1/channels/${ucid}`).then(res => res.json())
|
||||
// update database
|
||||
const bestIcon = channel.authorThumbnails.slice(-1)[0]
|
||||
const iconURL = bestIcon ? bestIcon.url : null
|
||||
db.prepare("REPLACE INTO Channels (ucid, name, icon_url) VALUES (?, ?, ?)").run([channel.authorId, channel.author, iconURL])
|
||||
// return
|
||||
return channel
|
||||
}
|
||||
|
||||
module.exports.fetchChannel = fetchChannel
|
19
api/video.js
Normal file
19
api/video.js
Normal file
@ -0,0 +1,19 @@
|
||||
const fetch = require("node-fetch")
|
||||
const {render} = require("pinski/plugins")
|
||||
const db = require("./utils/db")
|
||||
const {getToken} = require("./utils/getuser")
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
route: "/watch", methods: ["GET"], code: async ({req, url}) => {
|
||||
const id = url.searchParams.get("v")
|
||||
const video = await fetch(`http://localhost:3000/api/v1/videos/${id}`).then(res => res.json())
|
||||
let subscribed = false
|
||||
const token = getToken(req)
|
||||
if (token) {
|
||||
subscribed = !!db.prepare("SELECT * FROM Subscriptions WHERE token = ? AND ucid = ?").get([token, video.authorId])
|
||||
}
|
||||
return render(200, "pug/video.pug", {video, subscribed})
|
||||
}
|
||||
}
|
||||
]
|
@ -177,14 +177,14 @@ function fetchChannel(channelID, ignoreCache) {
|
||||
}
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
/*{
|
||||
route: "/watch", methods: ["GET"], code: async ({url}) => {
|
||||
const id = url.searchParams.get("v")
|
||||
const video = await videoCache.getAs(id, () => fetch(`http://localhost:3000/api/v1/videos/${id}`).then(res => res.json()))
|
||||
return render(200, "pug/video.pug", {video})
|
||||
}
|
||||
}
|
||||
/*
|
||||
},
|
||||
|
||||
{
|
||||
route: "/v/(.*)", methods: ["GET"], code: async ({fill}) => {
|
||||
let id;
|
||||
|
4
html/static/js/channel.js
Normal file
4
html/static/js/channel.js
Normal file
@ -0,0 +1,4 @@
|
||||
import {q} from "/static/js/elemjs/elemjs.js"
|
||||
import {SubscribeButton} from "/static/js/subscribe.js"
|
||||
|
||||
new SubscribeButton(q("#subscribe"))
|
@ -1,3 +1,5 @@
|
||||
document.body.classList.remove("show-focus")
|
||||
|
||||
document.addEventListener("mousedown", () => {
|
||||
document.body.classList.remove("show-focus")
|
||||
})
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {q, ElemJS} from "/static/js/elemjs/elemjs.js"
|
||||
import {SubscribeButton} from "/static/js/subscribe.js"
|
||||
|
||||
const video = q("#video")
|
||||
const audio = q("#audio")
|
||||
@ -67,6 +68,7 @@ class QualitySelect extends ElemJS {
|
||||
onInput() {
|
||||
const itag = this.element.value
|
||||
formatLoader.play(itag)
|
||||
video.focus()
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,3 +120,40 @@ for (let eventName of ["canplaythrough", "waiting", "stalled"]) {
|
||||
video.addEventListener(eventName, playbackIntervention)
|
||||
audio.addEventListener(eventName, playbackIntervention)
|
||||
}
|
||||
|
||||
function relativeSeek(seconds) {
|
||||
video.currentTime += seconds
|
||||
}
|
||||
|
||||
function togglePlaying() {
|
||||
if (video.paused) video.play()
|
||||
else video.pause()
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", event => {
|
||||
if (["INPUT", "SELECT", "BUTTON"].includes(event.target.tagName)) return
|
||||
let caught = true
|
||||
if (event.key === "j" || event.key === "n") {
|
||||
relativeSeek(-10)
|
||||
} else if (["k", "p", " ", "e"].includes(event.key)) {
|
||||
togglePlaying()
|
||||
} else if (event.key === "l" || event.key === "o") {
|
||||
relativeSeek(10)
|
||||
} else if (event.key === "ArrowLeft") {
|
||||
relativeSeek(-5)
|
||||
} else if (event.key === "ArrowRight") {
|
||||
relativeSeek(5)
|
||||
} else if (event.key === "ArrowUp" || event.key === "ArrowDown") {
|
||||
// no-op
|
||||
} else if (event.key >= "0" && event.key <= "9") {
|
||||
video.currentTime = video.duration * (+event.key) / 10
|
||||
} else if (event.key === "f") {
|
||||
if (document.fullscreen) document.exitFullscreen()
|
||||
else video.requestFullscreen()
|
||||
} else {
|
||||
caught = false
|
||||
}
|
||||
if (caught) event.preventDefault()
|
||||
})
|
||||
|
||||
new SubscribeButton(q("#subscribe"))
|
||||
|
28
html/static/js/subscribe.js
Normal file
28
html/static/js/subscribe.js
Normal file
@ -0,0 +1,28 @@
|
||||
import {ElemJS} from "/static/js/elemjs/elemjs.js"
|
||||
|
||||
class SubscribeButton extends ElemJS {
|
||||
constructor(element) {
|
||||
super(element)
|
||||
this.subscribed = this.element.getAttribute("data-subscribed") === "1"
|
||||
this.ucid = this.element.getAttribute("data-ucid")
|
||||
this.on("click", this.onClick.bind(this))
|
||||
this.render()
|
||||
}
|
||||
|
||||
onClick(event) {
|
||||
event.preventDefault()
|
||||
this.subscribed = !this.subscribed
|
||||
const path = this.subscribed ? "subscribe" : "unsubscribe"
|
||||
fetch(`/formapi/${path}/${this.ucid}`, {method: "POST"})
|
||||
this.render()
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.subscribed) this.text("Subscribe")
|
||||
else this.text("Unsubscribe")
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
SubscribeButton
|
||||
}
|
503
package-lock.json
generated
503
package-lock.json
generated
@ -29,6 +29,11 @@
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz",
|
||||
"integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w=="
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||
},
|
||||
"anymatch": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
|
||||
@ -38,6 +43,20 @@
|
||||
"picomatch": "^2.0.4"
|
||||
}
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
|
||||
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
|
||||
"requires": {
|
||||
"delegates": "^1.0.0",
|
||||
"readable-stream": "^2.0.6"
|
||||
}
|
||||
},
|
||||
"asap": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
|
||||
@ -56,11 +75,56 @@
|
||||
"@babel/types": "^7.9.6"
|
||||
}
|
||||
},
|
||||
"base64-js": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
|
||||
},
|
||||
"better-sqlite3": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-7.1.0.tgz",
|
||||
"integrity": "sha512-FV/snQ8F/kyqhdxsevzbojVtMowDWOfe1A5N3lYu1KJwoho2t7JgITmdlSc7DkOh3Zq65I+ZyeNWXQrkLEDFTg==",
|
||||
"requires": {
|
||||
"bindings": "^1.5.0",
|
||||
"prebuild-install": "^5.3.3",
|
||||
"tar": "4.4.10"
|
||||
}
|
||||
},
|
||||
"binary-extensions": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
|
||||
"integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ=="
|
||||
},
|
||||
"bindings": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
||||
"requires": {
|
||||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
},
|
||||
"bl": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz",
|
||||
"integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==",
|
||||
"requires": {
|
||||
"buffer": "^5.5.0",
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
@ -69,6 +133,15 @@
|
||||
"fill-range": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
|
||||
"integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
|
||||
"requires": {
|
||||
"base64-js": "^1.0.2",
|
||||
"ieee754": "^1.1.4"
|
||||
}
|
||||
},
|
||||
"character-parser": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz",
|
||||
@ -92,6 +165,21 @@
|
||||
"readdirp": "~3.4.0"
|
||||
}
|
||||
},
|
||||
"chownr": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
||||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
|
||||
},
|
||||
"constantinople": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz",
|
||||
@ -101,11 +189,62 @@
|
||||
"@babel/types": "^7.6.1"
|
||||
}
|
||||
},
|
||||
"cookie": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
|
||||
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||
},
|
||||
"decompress-response": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
|
||||
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
|
||||
"requires": {
|
||||
"mimic-response": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"deep-extend": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
|
||||
},
|
||||
"detect-libc": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
|
||||
},
|
||||
"doctypes": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz",
|
||||
"integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk="
|
||||
},
|
||||
"end-of-stream": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
||||
"requires": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"expand-template": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
||||
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
|
||||
},
|
||||
"file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
@ -114,12 +253,45 @@
|
||||
"to-regex-range": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
},
|
||||
"fs-minipass": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
|
||||
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
|
||||
"requires": {
|
||||
"minipass": "^2.6.0"
|
||||
}
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
|
||||
"integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
|
||||
"optional": true
|
||||
},
|
||||
"gauge": {
|
||||
"version": "2.7.4",
|
||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
|
||||
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
|
||||
"requires": {
|
||||
"aproba": "^1.0.3",
|
||||
"console-control-strings": "^1.0.0",
|
||||
"has-unicode": "^2.0.0",
|
||||
"object-assign": "^4.1.0",
|
||||
"signal-exit": "^3.0.0",
|
||||
"string-width": "^1.0.1",
|
||||
"strip-ansi": "^3.0.1",
|
||||
"wide-align": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"github-from-package": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
||||
"integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4="
|
||||
},
|
||||
"glob-parent": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
|
||||
@ -133,6 +305,26 @@
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
|
||||
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
|
||||
},
|
||||
"has-unicode": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
|
||||
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
|
||||
},
|
||||
"is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
@ -155,6 +347,14 @@
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-glob": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
|
||||
@ -181,6 +381,11 @@
|
||||
"has-symbols": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"js-stringify": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz",
|
||||
@ -205,21 +410,103 @@
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
|
||||
"integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA=="
|
||||
},
|
||||
"mimic-response": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
|
||||
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA=="
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
|
||||
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"minizlib": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
|
||||
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
|
||||
"requires": {
|
||||
"minipass": "^2.9.0"
|
||||
}
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"mkdirp-classic": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
|
||||
},
|
||||
"napi-build-utils": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
|
||||
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
|
||||
},
|
||||
"node-abi": {
|
||||
"version": "2.19.1",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.19.1.tgz",
|
||||
"integrity": "sha512-HbtmIuByq44yhAzK7b9j/FelKlHYISKQn0mtvcBrU5QBkhoCMp5bu8Hv5AI34DcKfOAcJBcOEMwLlwO62FFu9A==",
|
||||
"requires": {
|
||||
"semver": "^5.4.1"
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
|
||||
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
|
||||
},
|
||||
"noop-logger": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
|
||||
"integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI="
|
||||
},
|
||||
"normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
|
||||
},
|
||||
"npmlog": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
||||
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
||||
"requires": {
|
||||
"are-we-there-yet": "~1.1.2",
|
||||
"console-control-strings": "~1.1.0",
|
||||
"gauge": "~2.7.3",
|
||||
"set-blocking": "~2.0.0"
|
||||
}
|
||||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
@ -231,8 +518,8 @@
|
||||
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg=="
|
||||
},
|
||||
"pinski": {
|
||||
"version": "git+https://git.sr.ht/~cadence/nodejs-pinski#20548ccee95dfdd9c5eea91476a88f402d4aac08",
|
||||
"from": "git+https://git.sr.ht/~cadence/nodejs-pinski#20548ccee95dfdd9c5eea91476a88f402d4aac08",
|
||||
"version": "git+https://git.sr.ht/~cadence/nodejs-pinski#04cd72482574f689b78670b7f2b80209f5449a21",
|
||||
"from": "git+https://git.sr.ht/~cadence/nodejs-pinski#04cd72482574f689b78670b7f2b80209f5449a21",
|
||||
"requires": {
|
||||
"mime": "^2.4.6",
|
||||
"pug": "^3.0.0",
|
||||
@ -240,6 +527,33 @@
|
||||
"ws": "^7.3.1"
|
||||
}
|
||||
},
|
||||
"prebuild-install": {
|
||||
"version": "5.3.5",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.5.tgz",
|
||||
"integrity": "sha512-YmMO7dph9CYKi5IR/BzjOJlRzpxGGVo1EsLSUZ0mt/Mq0HWZIHOKHHcHdT69yG54C9m6i45GpItwRHpk0Py7Uw==",
|
||||
"requires": {
|
||||
"detect-libc": "^1.0.3",
|
||||
"expand-template": "^2.0.3",
|
||||
"github-from-package": "0.0.0",
|
||||
"minimist": "^1.2.3",
|
||||
"mkdirp": "^0.5.1",
|
||||
"napi-build-utils": "^1.0.1",
|
||||
"node-abi": "^2.7.0",
|
||||
"noop-logger": "^0.1.1",
|
||||
"npmlog": "^4.0.1",
|
||||
"pump": "^3.0.0",
|
||||
"rc": "^1.2.7",
|
||||
"simple-get": "^3.0.3",
|
||||
"tar-fs": "^2.0.0",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"which-pm-runs": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
},
|
||||
"promise": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
|
||||
@ -360,6 +674,40 @@
|
||||
"resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz",
|
||||
"integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ=="
|
||||
},
|
||||
"pump": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
|
||||
"requires": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"rc": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
||||
"requires": {
|
||||
"deep-extend": "^0.6.0",
|
||||
"ini": "~1.3.0",
|
||||
"minimist": "^1.2.0",
|
||||
"strip-json-comments": "~2.0.1"
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.7",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"readdirp": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
|
||||
@ -376,6 +724,11 @@
|
||||
"path-parse": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.26.10",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.26.10.tgz",
|
||||
@ -384,6 +737,116 @@
|
||||
"chokidar": ">=2.0.0 <4.0.0"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
|
||||
},
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
|
||||
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
|
||||
},
|
||||
"simple-concat": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
|
||||
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
|
||||
},
|
||||
"simple-get": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
|
||||
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
|
||||
"requires": {
|
||||
"decompress-response": "^4.2.0",
|
||||
"once": "^1.3.1",
|
||||
"simple-concat": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
"strip-ansi": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"strip-json-comments": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
|
||||
},
|
||||
"tar": {
|
||||
"version": "4.4.10",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz",
|
||||
"integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==",
|
||||
"requires": {
|
||||
"chownr": "^1.1.1",
|
||||
"fs-minipass": "^1.2.5",
|
||||
"minipass": "^2.3.5",
|
||||
"minizlib": "^1.2.1",
|
||||
"mkdirp": "^0.5.0",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.3"
|
||||
}
|
||||
},
|
||||
"tar-fs": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.0.tgz",
|
||||
"integrity": "sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg==",
|
||||
"requires": {
|
||||
"chownr": "^1.1.1",
|
||||
"mkdirp-classic": "^0.5.2",
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.3.tgz",
|
||||
"integrity": "sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA==",
|
||||
"requires": {
|
||||
"bl": "^4.0.1",
|
||||
"end-of-stream": "^1.4.1",
|
||||
"fs-constants": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
@ -402,11 +865,37 @@
|
||||
"resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz",
|
||||
"integrity": "sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ="
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
|
||||
"requires": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||
},
|
||||
"void-elements": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
|
||||
"integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk="
|
||||
},
|
||||
"which-pm-runs": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
|
||||
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
|
||||
},
|
||||
"wide-align": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
||||
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
|
||||
"requires": {
|
||||
"string-width": "^1.0.2 || 2"
|
||||
}
|
||||
},
|
||||
"with": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz",
|
||||
@ -418,10 +907,20 @@
|
||||
"babel-walk": "3.0.0-canary-5"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
|
||||
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA=="
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,9 @@
|
||||
"author": "",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^7.1.0",
|
||||
"cookie": "^0.4.1",
|
||||
"node-fetch": "^2.6.0",
|
||||
"pinski": "git+https://git.sr.ht/~cadence/nodejs-pinski#20548ccee95dfdd9c5eea91476a88f402d4aac08"
|
||||
"pinski": "git+https://git.sr.ht/~cadence/nodejs-pinski#04cd72482574f689b78670b7f2b80209f5449a21"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,11 @@
|
||||
extends includes/layout.pug
|
||||
extends includes/layout
|
||||
|
||||
include includes/video-list-item.pug
|
||||
include includes/video-list-item
|
||||
include includes/subscribe-button
|
||||
|
||||
block head
|
||||
title= `${data.author} - CloudTube`
|
||||
script(type="module" src=getStaticURL("html", "/static/js/channel.js"))
|
||||
|
||||
block content
|
||||
main.channel-page
|
||||
@ -18,8 +23,7 @@ block content
|
||||
.about
|
||||
.name= data.author
|
||||
.subscribers= data.second__subCountText || `${data.subCount} subscribers`
|
||||
form(method="post" action=`/formapi/subscribe/${data.authorId}`).subscribe
|
||||
button.subscribe-button.base-border-look Subscribe
|
||||
+subscribe_button(data.authorId, subscribed, `/channel/${data.authorId}`).subscribe-button.base-border-look
|
||||
.description!= data.descriptionHtml
|
||||
|
||||
.videos
|
||||
|
@ -1,5 +1,8 @@
|
||||
extends includes/layout.pug
|
||||
|
||||
block head
|
||||
title Home - CloudTube
|
||||
|
||||
block content
|
||||
main.home-page
|
||||
h1.top-header CloudTube
|
||||
|
6
pug/includes/subscribe-button.pug
Normal file
6
pug/includes/subscribe-button.pug
Normal file
@ -0,0 +1,6 @@
|
||||
mixin subscribe_button(ucid, isSubscribed, referrer)
|
||||
- const subscribePath = !subscribed ? "subscribe" : "unsubscribe"
|
||||
form(method="post" action=`/formapi/${subscribePath}/${ucid}`).subscribe-form
|
||||
if referrer
|
||||
input(type="hidden" name="referrer" value=referrer)
|
||||
button(data-subscribed=(+isSubscribed) data-ucid=ucid)&attributes(attributes)#subscribe= !subscribed ? "Subscribe" : "Unsubscribe"
|
@ -2,8 +2,11 @@ extends includes/layout.pug
|
||||
|
||||
include includes/video-list-item.pug
|
||||
|
||||
block head
|
||||
title= `${query} (search) - CloudTube`
|
||||
|
||||
block content
|
||||
main.search-page
|
||||
each result in results
|
||||
.search-result
|
||||
+video_list_item(result)
|
||||
+video_list_item(result)
|
||||
|
@ -1,9 +1,10 @@
|
||||
extends includes/layout.pug
|
||||
extends includes/layout
|
||||
|
||||
include includes/video-list-item.pug
|
||||
include includes/video-list-item
|
||||
include includes/subscribe-button
|
||||
|
||||
block head
|
||||
title= video.title
|
||||
title= `${video.title} - CloudTube`
|
||||
script(type="module" src=getStaticURL("html", "/static/js/player.js"))
|
||||
script const data = !{JSON.stringify(video)}
|
||||
|
||||
@ -36,21 +37,20 @@ block content
|
||||
#audio-loading-display
|
||||
|
||||
.video-button-container
|
||||
button.border-look#subscribe Subscribe
|
||||
button.border-look#theatre Theatre
|
||||
+subscribe_button(video.authorId, subscribed, `/watch?v=${video.videoId}`).border-look
|
||||
//- button.border-look#theatre Theatre
|
||||
select(autocomplete="off").border-look#quality-select
|
||||
each f in sortedFormatStreams
|
||||
option(value=f.itag)= `${f.qualityLabel} ${f.container}`
|
||||
each f in sortedVideoAdaptiveFormats
|
||||
option(value=f.itag)= `${f.qualityLabel} ${f.container} *`
|
||||
|
||||
.video-button-container
|
||||
a(href="/subscriptions").border-look
|
||||
img(src="/static/images/search.svg" width=17 height=17 alt="").button-icon
|
||||
| Search
|
||||
button.border-look#share Share
|
||||
a.border-look YouTube
|
||||
a.border-look Iv: Snopyta
|
||||
//-
|
||||
a(href="/subscriptions").border-look
|
||||
img(src="/static/images/search.svg" width=17 height=17 alt="").button-icon
|
||||
| Search
|
||||
//- button.border-look#share Share
|
||||
a(href=`https://www.youtube.com/watch?v=${video.videoId}`).border-look YouTube
|
||||
a(href=`https://invidio.us/watch?v=${video.videoId}`).border-look Invidious
|
||||
|
||||
.description!= video.descriptionHtml
|
||||
|
||||
|
@ -43,7 +43,7 @@
|
||||
color: c.$fg-main
|
||||
font-size: 18px
|
||||
|
||||
.subscribe
|
||||
.subscribe-form
|
||||
margin-top: 24px
|
||||
|
||||
.subscribe-button
|
||||
|
@ -64,6 +64,9 @@
|
||||
padding: 12px
|
||||
border-radius: 4px
|
||||
|
||||
.subscribe-form
|
||||
display: inline-block
|
||||
|
||||
.related-header
|
||||
margin: 4px 0px 12px 2px
|
||||
font-weight: normal
|
||||
|
32
server.js
32
server.js
@ -1,23 +1,27 @@
|
||||
const {Pinski} = require("pinski")
|
||||
const {setInstance} = require("pinski/plugins")
|
||||
|
||||
const server = new Pinski({
|
||||
port: 8080,
|
||||
relativeRoot: __dirname,
|
||||
filesDir: "html"
|
||||
})
|
||||
;(async () => {
|
||||
await require("./api/utils/upgradedb")()
|
||||
|
||||
setInstance(server)
|
||||
const server = new Pinski({
|
||||
port: 8080,
|
||||
relativeRoot: __dirname,
|
||||
filesDir: "html"
|
||||
})
|
||||
|
||||
server.addSassDir("sass", ["sass/includes"])
|
||||
server.addRoute("/static/css/main.css", "sass/main.sass", "sass")
|
||||
setInstance(server)
|
||||
|
||||
server.addPugDir("pug", ["pug/includes"])
|
||||
server.addRoute("/", "pug/home.pug", "pug")
|
||||
server.addSassDir("sass", ["sass/includes"])
|
||||
server.addRoute("/static/css/main.css", "sass/main.sass", "sass")
|
||||
|
||||
server.addStaticHashTableDir("html/static/js")
|
||||
server.addStaticHashTableDir("html/static/js/elemjs")
|
||||
server.addPugDir("pug", ["pug/includes"])
|
||||
server.addRoute("/", "pug/home.pug", "pug")
|
||||
|
||||
server.addAPIDir("api")
|
||||
server.addStaticHashTableDir("html/static/js")
|
||||
server.addStaticHashTableDir("html/static/js/elemjs")
|
||||
|
||||
server.startServer()
|
||||
server.addAPIDir("api")
|
||||
|
||||
server.startServer()
|
||||
})()
|
||||
|
Loading…
Reference in New Issue
Block a user