add video versioning
This commit is contained in:
parent
eb790eb8fc
commit
e007c0ebd3
|
|
@ -26,17 +26,30 @@ app.get('/watch', async ({ query: { v }, set, redirect, error }) => {
|
||||||
return cached
|
return cached
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!v.match(/[\w\-_]{11}/)) return error(404)
|
const idMatch = v.match(/^([\w\-_]{11})(?:-(\d+))?$/)
|
||||||
|
if (!idMatch) return error(404)
|
||||||
|
|
||||||
const json = await db.selectFrom('videos')
|
const baseId = idMatch[1];
|
||||||
|
const escapeRegex = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
const allowedVersionPattern = new RegExp(`^${escapeRegex(baseId)}(?:-\\d+)?$`);
|
||||||
|
|
||||||
|
const videoVersions = (await db.selectFrom('videos')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('id', '=', v)
|
.where('id', 'like', `${baseId}%`)
|
||||||
.executeTakeFirst()
|
.execute())
|
||||||
|
.filter(video => allowedVersionPattern.test(video.id))
|
||||||
|
.sort((a, b) => {
|
||||||
|
const aVersion = Number(a.id.slice(baseId.length + 1) || 1);
|
||||||
|
const bVersion = Number(b.id.slice(baseId.length + 1) || 1);
|
||||||
|
return aVersion - bVersion;
|
||||||
|
});
|
||||||
|
|
||||||
|
const json = videoVersions.find(video => video.id === v) || videoVersions[0];
|
||||||
|
|
||||||
if (!json) {
|
if (!json) {
|
||||||
const html = await m(eta.render('./watch', {
|
const html = await m(eta.render('./watch', {
|
||||||
isMissing: true,
|
isMissing: true,
|
||||||
id: v,
|
id: baseId,
|
||||||
title: 'Video Not Found | PreserveTube',
|
title: 'Video Not Found | PreserveTube',
|
||||||
manualAnalytics: true
|
manualAnalytics: true
|
||||||
}))
|
}))
|
||||||
|
|
@ -45,13 +58,13 @@ app.get('/watch', async ({ query: { v }, set, redirect, error }) => {
|
||||||
set.headers['content-type'] = 'text/html; charset=utf-8'
|
set.headers['content-type'] = 'text/html; charset=utf-8'
|
||||||
return error(404, html)
|
return error(404, html)
|
||||||
}
|
}
|
||||||
if (json.disabled) return redirect(`/transparency/${v}`)
|
if (json.disabled) return redirect(`/transparency/${json.id}`)
|
||||||
|
|
||||||
let transparency: any[] = []
|
let transparency: any[] = []
|
||||||
if (json.hasBeenReported) {
|
if (json.hasBeenReported) {
|
||||||
transparency = await db.selectFrom('reports')
|
transparency = await db.selectFrom('reports')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where('target', '=', v)
|
.where('target', '=', json.id)
|
||||||
.execute()
|
.execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,6 +98,11 @@ app.get('/watch', async ({ query: { v }, set, redirect, error }) => {
|
||||||
|
|
||||||
const html = await m(eta.render('./watch', {
|
const html = await m(eta.render('./watch', {
|
||||||
transparency,
|
transparency,
|
||||||
|
versions: videoVersions.map(video => ({
|
||||||
|
id: video.id,
|
||||||
|
archived: video.archived
|
||||||
|
})),
|
||||||
|
baseId,
|
||||||
...json,
|
...json,
|
||||||
description: DOMPurify.sanitize(json.description),
|
description: DOMPurify.sanitize(json.description),
|
||||||
title: `${json.title} | PreserveTube`,
|
title: `${json.title} | PreserveTube`,
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,28 @@
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<div class="report">
|
<div class="report">
|
||||||
|
<% if (it.versions && it.versions.length > 1) { %>
|
||||||
|
<div class="version-picker">
|
||||||
|
<label for="version-select">Version:</label>
|
||||||
|
<select
|
||||||
|
id="version-select"
|
||||||
|
onchange="window.location.href = '/watch?v=' + this.value"
|
||||||
|
>
|
||||||
|
<% it.versions.forEach(function(version, index){ %>
|
||||||
|
<option value="<%= version.id %>" <%= version.id === it.id ? 'selected' : '' %>>
|
||||||
|
v<%= version.id.slice(it.baseId.length + 1) || '1' %> (archived <%= version.archived %>)
|
||||||
|
</option>
|
||||||
|
<% }) %>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<% } else { %>
|
||||||
|
<div></div>
|
||||||
|
<% } %>
|
||||||
|
<div class="report-links">
|
||||||
<a href="abuse">[report abuse]</a>
|
<a href="abuse">[report abuse]</a>
|
||||||
<div class="space"></div>
|
|
||||||
<a href="about">[faq]</a>
|
<a href="about">[faq]</a>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<% if (it.transparency.length != 0) { %>
|
<% if (it.transparency.length != 0) { %>
|
||||||
<div class="reports">
|
<div class="reports">
|
||||||
|
|
@ -76,13 +94,16 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.report {
|
.report {
|
||||||
text-align: right;
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.space {
|
.report-links {
|
||||||
display: inline-block;
|
display: flex;
|
||||||
width: 0.5%;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
video {
|
video {
|
||||||
|
|
@ -154,6 +175,17 @@
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.version-picker {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-picker select {
|
||||||
|
font: inherit;
|
||||||
|
padding: 0.2rem 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
.video-wrapper {
|
.video-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue