Stream refreshed media for playback and view full

This commit is contained in:
Fisto
2026-09-16 13:18:55 +02:00
parent d3f9d20190
commit 612b2504dc
9 changed files with 207 additions and 18 deletions
+5
View File
@@ -21,6 +21,8 @@ export interface ExtractedMedia {
kind: MediaKind;
url: string;
source: MediaSource;
/** Direct playable file URL when the embed carries one (gifv/Tenor mp4). */
playUrl: string | null;
provider: string | null;
contentType: string | null;
size: number | null;
@@ -73,6 +75,7 @@ function extractEmbed(embed: CoreEmbed): ExtractedMedia | null {
const provider = embed.provider ?? hostname(embed.url);
const common = {
source: "embed" as const,
playUrl: embed.videoUrl ?? null,
provider,
downloaded: false,
size: null,
@@ -123,6 +126,7 @@ function extractSticker(sticker: CoreSticker): ExtractedMedia | null {
return {
kind: ext === "gif" ? "gif" : "image",
source: "sticker",
playUrl: null,
url: `https://cdn.discordapp.com/stickers/${sticker.id}.${ext}`,
provider: "discord",
contentType: null,
@@ -145,6 +149,7 @@ function extractFrom(message: MediaMessage): ExtractedMedia[] {
items.push({
kind,
source: "attachment",
playUrl: null,
url: attachment.url,
provider: "discord",
contentType: attachment.contentType,
+31 -1
View File
@@ -122,6 +122,7 @@ export interface MediaRow {
author_name: string | null;
posted_at: string | null;
url: string;
play_url: string | null;
provider: string | null;
content_type: string | null;
size: number | null;
@@ -159,6 +160,7 @@ CREATE TABLE IF NOT EXISTS media (
author_name TEXT,
posted_at TEXT,
url TEXT NOT NULL UNIQUE,
play_url TEXT,
provider TEXT,
content_type TEXT,
size INTEGER,
@@ -215,6 +217,7 @@ export class ScrapeStore {
if (!names.has("download_error")) this.db.exec("ALTER TABLE media ADD COLUMN download_error TEXT");
if (!names.has("preview_error")) this.db.exec("ALTER TABLE media ADD COLUMN preview_error TEXT");
if (!names.has("preview_ext")) this.db.exec("ALTER TABLE media ADD COLUMN preview_ext TEXT");
if (!names.has("play_url")) this.db.exec("ALTER TABLE media ADD COLUMN play_url TEXT");
if (!names.has("aspect_ratio")) this.db.exec("ALTER TABLE media ADD COLUMN aspect_ratio REAL");
}
@@ -229,7 +232,7 @@ export class ScrapeStore {
"INSERT OR REPLACE INTO messages (id, author_id, author_name, timestamp, content) VALUES (?, ?, ?, ?, ?)"
);
const insertMedia = this.db.prepare(
"INSERT OR IGNORE INTO media (kind, source, author_id, author_name, posted_at, url, provider, content_type, size, width, height, aspect_ratio, duration_secs, downloaded, preview_url, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
"INSERT OR IGNORE INTO media (kind, source, author_id, author_name, posted_at, url, play_url, provider, content_type, size, width, height, aspect_ratio, duration_secs, downloaded, preview_url, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
);
const findMedia = this.db.prepare("SELECT id FROM media WHERE url = ?");
const linkSource = this.db.prepare(
@@ -256,6 +259,7 @@ export class ScrapeStore {
message.author.username,
message.timestamp,
item.url,
item.playUrl,
item.provider,
item.contentType,
item.size,
@@ -362,6 +366,32 @@ export class ScrapeStore {
this.db.prepare("UPDATE media SET preview_error = ? WHERE id = ?").run(error.slice(0, 200), id);
}
/** Refresh stored URLs after an expired CDN link is re-fetched. */
updateUrls(id: number, urls: { url?: string; playUrl?: string | null }): void {
const sets: string[] = [];
const values: Array<string | null> = [];
if (urls.url) {
sets.push("url = ?");
values.push(urls.url);
}
if (urls.playUrl !== undefined) {
sets.push("play_url = ?");
values.push(urls.playUrl);
}
if (!sets.length) return;
this.db.prepare(`UPDATE media SET ${sets.join(", ")} WHERE id = ?`).run(...values, id);
}
/** The message a media row came from, for URL refreshes. */
sourceMessageId(mediaId: number): string | null {
const row = this.db
.prepare("SELECT message_id FROM media_sources WHERE media_id = ? LIMIT 1")
.get(mediaId) as unknown as { message_id: string } | undefined;
return row?.message_id ?? null;
}
/** Fill in dimensions and duration after a download probe. */
updateMediaInfo(
id: number,
+1
View File
@@ -139,6 +139,7 @@ void (async () => {
assert.equal(youtube?.kind, "video");
assert.equal(youtube?.provider, "YouTube");
assert.equal(youtube?.downloaded, false);
assert.equal(youtube?.playUrl, "https://www.youtube.com/embed/x");
assert.equal(items.find(item => item.provider === "Tenor")?.kind, "gif");
assert.equal(items.find(item => item.url.endsWith("/stickers/55.gif"))?.kind, "gif");
assert.equal(items.some(item => item.url.includes("lottie")), false);