1
0
mirror of https://git.sr.ht/~cadence/cloudtube synced 2024-11-26 09:27:30 +00:00

Add subscriptions page

This commit is contained in:
Cadence Ember 2020-08-31 03:14:05 +12:00
parent f24e1bb39c
commit 59a7489545
No known key found for this signature in database
GPG Key ID: 128B99B1B74A6412
11 changed files with 159 additions and 6 deletions

30
api/subscriptions.js Normal file
View File

@ -0,0 +1,30 @@
const {render} = require("pinski/plugins")
const db = require("./utils/db")
const {fetchChannelLatest} = require("./utils/youtube")
const {getUser} = require("./utils/getuser")
module.exports = [
{
route: `/subscriptions`, methods: ["GET"], code: async ({req}) => {
const user = getUser(req)
let hasSubscriptions = false
let videos = []
let channels = []
if (user.token) {
const subscriptions = user.getSubscriptions()
const channelPrepared = db.prepare("SELECT * FROM Channels WHERE ucid = ?")
channels = subscriptions.map(id => channelPrepared.get(id)).sort((a, b) => {
if (a.name < b.name) return -1
else if (b.name > a.name) return 1
else return 0
})
if (subscriptions.length) {
hasSubscriptions = true
const all = await Promise.all(subscriptions.map(id => fetchChannelLatest(id)))
videos = all.flat(1).sort((a, b) => b.published - a.published).slice(0, 60)
}
}
return render(200, "pug/subscriptions.pug", {hasSubscriptions, videos, channels})
}
}
]

View File

@ -1,6 +1,5 @@
const crypto = require("crypto") const crypto = require("crypto")
const {parse: parseCookie} = require("cookie") const {parse: parseCookie} = require("cookie")
const constants = require("./constants") const constants = require("./constants")
const db = require("./db") const db = require("./db")
@ -26,7 +25,7 @@ class User {
getSubscriptions() { getSubscriptions() {
if (this.token) { if (this.token) {
return db.prepare("SELECT ucid FROM Subscriptions WHERE token = ?").pluck().all(ucid) return db.prepare("SELECT ucid FROM Subscriptions WHERE token = ?").pluck().all(this.token)
} else { } else {
return [] return []
} }

View File

@ -12,4 +12,14 @@ async function fetchChannel(ucid) {
return channel return channel
} }
function fetchChannelLatest(ucid) {
return fetch(`http://localhost:3000/api/v1/channels/${ucid}/latest`).then(res => res.json()).then(root => {
root.forEach(video => {
video.descriptionHtml = video.descriptionHtml.replace(/<a /g, '<a tabindex="-1" ')
})
return root
})
}
module.exports.fetchChannel = fetchChannel module.exports.fetchChannel = fetchChannel
module.exports.fetchChannelLatest = fetchChannelLatest

View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="30"
height="24.999874"
viewBox="0 0 7.9374998 6.6145502"
version="1.1"
id="svg27"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="subscriptions.svg">
<defs
id="defs21" />
<sodipodi:namedview
id="base"
pagecolor="#36393f"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="22.627417"
inkscape:cx="17.632604"
inkscape:cy="10.153317"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1440"
inkscape:window-height="877"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-bbox="true"
inkscape:snap-bbox-edge-midpoints="false"
inkscape:snap-text-baseline="false"
inkscape:snap-center="false"
inkscape:snap-global="false"
inkscape:pagecheckerboard="false">
<inkscape:grid
type="xygrid"
id="grid828"
originx="32.014582"
originy="-164.3063" />
</sodipodi:namedview>
<metadata
id="metadata24">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(32.014583,-126.07915)">
<path
id="rect15"
style="opacity:1;fill:#c4c4c4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.05833316;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
d="m -29.641547,126.07915 h 3.191429 c 0.288608,0 0.520954,0.23235 0.520954,0.52096 H -30.1625 c 0,-0.28861 0.232345,-0.52096 0.520953,-0.52096 z m -0.90981,0.79375 h 5.011047 c 0.37089,0 0.669473,0.29859 0.669476,0.66948 l 10e-7,0.12427 -6.35,-0.0163 v -0.10799 c 0,-0.37089 0.298586,-0.66948 0.669476,-0.66948 z m -0.404893,1.0583 c -0.586322,0 -1.058333,0.33169 -1.058333,0.91801 v 2.86801 c 0,0.58632 0.472011,0.97648 1.058333,0.97648 h 5.820834 c 0.586321,0 1.058333,-0.39016 1.058333,-0.97648 v -2.86801 c 0,-0.58632 -0.472012,-0.91801 -1.058333,-0.91801 z m 4.241825,2.16468 c 0.167331,0.0992 0.189069,0.27627 0.01203,0.3666 l -2.09776,0.9851 c -0.131847,0.0763 -0.304206,-0.002 -0.302769,-0.19054 v -1.88797 c 0,-0.20313 0.17976,-0.33275 0.340294,-0.24486 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ssccscssccsccsssssssssccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -10,6 +10,8 @@ html
body.show-focus body.show-focus
nav.main-nav nav.main-nav
a(href="/").link.home CloudTube a(href="/").link.home CloudTube
a(href="/subscriptions" title="Subscriptions").link.subscriptions-link
img(src=getStaticURL("html", "/static/images/subscriptions.svg") width=30 height=25 alt="Subscriptions.").subscriptions-icon
form(method="get" action="/search").search-form form(method="get" action="/search").search-form
input(type="text" placeholder="Search" name="q" autocomplete="off" value=query).search input(type="text" placeholder="Search" name="q" autocomplete="off" value=query).search

View File

@ -1,14 +1,16 @@
mixin video_list_item(video) mixin video_list_item(video)
- let link = `/watch?v=${video.videoId}` - let link = `/watch?v=${video.videoId}`
a(href=link).thumbnail a(href=link tabindex="-1").thumbnail
img(src=`https://i.ytimg.com/vi/${video.videoId}/mqdefault.jpg` width=320 height=180 alt="").image img(src=`https://i.ytimg.com/vi/${video.videoId}/mqdefault.jpg` width=320 height=180 alt="").image
span.duration= video.second__lengthText span.duration= video.second__lengthText
.info .info
div.title: a(href=link).title-link= video.title div.title: a(href=link).title-link= video.title
div.author-line div.author-line
a(href=`/channel/${video.authorId}`).author= video.author a(href=`/channel/${video.authorId}`).author= video.author
= ` • ` - const views = video.viewCountText || video.second__viewCountText
span.views= video.viewCountText || video.second__viewCountText if views
= ` • `
span.views= views
if video.publishedText if video.publishedText
= ` • ` = ` • `
span.published= video.publishedText span.published= video.publishedText

12
pug/subscriptions.pug Normal file
View File

@ -0,0 +1,12 @@
extends includes/layout.pug
include includes/video-list-item.pug
block head
title Subscriptions - CloudTube
block content
main.subscriptions-page
each video in videos
.subscriptions-video
+video_list_item(video)

View File

@ -0,0 +1,9 @@
@use "video-list-item.sass" as *
.subscriptions-page
padding: 40px 20px 20px
max-width: 900px
margin: 0 auto
.subscriptions-video
@include subscriptions-video

View File

@ -59,6 +59,7 @@
grid-template-columns: 240px 1fr grid-template-columns: 240px 1fr
margin-bottom: 20px margin-bottom: 20px
overflow: hidden overflow: hidden
max-height: 150px
.image .image
width: 240px width: 240px
@ -94,3 +95,6 @@
@mixin channel-video @mixin channel-video
@include large-item @include large-item
@mixin subscriptions-video
@include large-item

View File

@ -5,10 +5,11 @@
@use "includes/search-page.sass" @use "includes/search-page.sass"
@use "includes/home-page.sass" @use "includes/home-page.sass"
@use "includes/channel-page.sass" @use "includes/channel-page.sass"
@use "includes/subscriptions-page.sass"
@font-face @font-face
font-family: "Bariol" font-family: "Bariol"
src: url(/static/fonts/bariol.woff) src: url(/static/fonts/bariol.woff?statichash=1)
@mixin button-base @mixin button-base
appearance: none appearance: none
@ -74,6 +75,8 @@
text-decoration: none text-decoration: none
margin: 1px 8px 1px 0px margin: 1px 8px 1px 0px
font-size: 20px font-size: 20px
display: flex
align-items: center
&.home &.home
font-weight: bold font-weight: bold
@ -98,3 +101,7 @@
&:hover, &:focus &:hover, &:focus
border: 1px solid c.$edge-grey border: 1px solid c.$edge-grey
margin: 0px margin: 0px
.subscriptions-link:hover, .subscriptions-link:focus
.subscriptions-icon
filter: brightness(2)

View File

@ -20,6 +20,8 @@ const {setInstance} = require("pinski/plugins")
server.addStaticHashTableDir("html/static/js") server.addStaticHashTableDir("html/static/js")
server.addStaticHashTableDir("html/static/js/elemjs") server.addStaticHashTableDir("html/static/js/elemjs")
server.addStaticHashTableDir("html/static/images")
server.addStaticHashTableDir("html/static/fonts")
server.addAPIDir("api") server.addAPIDir("api")