Stream refreshed media for playback and view full
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user