quick media viewer (using new hmac read-file urls) (#451)

This commit is contained in:
Mike Sawka 2024-03-14 00:49:05 -07:00 committed by GitHub
parent 697f7c0758
commit 0f2f6c9cc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 124 additions and 1 deletions

View File

@ -0,0 +1,7 @@
.media-renderer {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding-top: var(--termpad);
}

View File

@ -0,0 +1,49 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import * as React from "react";
import * as mobx from "mobx";
import * as mobxReact from "mobx-react";
import { GlobalModel } from "@/models";
import "./media.less";
@mobxReact.observer
class SimpleMediaRenderer extends React.Component<
{ data: ExtBlob; context: RendererContext; opts: RendererOpts; savedHeight: number; lineState: LineStateType },
{}
> {
objUrl: string = null;
componentWillUnmount() {
if (this.objUrl != null) {
URL.revokeObjectURL(this.objUrl);
}
}
render() {
let dataBlob = this.props.data;
if (dataBlob == null || dataBlob.notFound) {
return (
<div className="media-renderer" style={{ fontSize: this.props.opts.termFontSize }}>
<div className="load-error-text">
ERROR: file {dataBlob && dataBlob.name ? JSON.stringify(dataBlob.name) : ""} not found
</div>
</div>
);
}
let videoUrl = GlobalModel.getBaseHostPort() + this.props.lineState["wave:fileurl"];
const opts = this.props.opts;
const maxHeight = opts.maxSize.height - 10;
const maxWidth = opts.maxSize.width - 10;
return (
<div className="media-renderer">
<video width="320" height="240" controls>
<source src={videoUrl} />
</video>
</div>
);
}
}
export { SimpleMediaRenderer };

View File

@ -32,7 +32,7 @@ class SimplePdfRenderer extends React.Component<
); );
} }
if (this.objUrl == null) { if (this.objUrl == null) {
const pdfBlob = new File([dataBlob], "test.pdf", { type: "application/pdf" }); const pdfBlob = new File([dataBlob], dataBlob.name ?? "file.pdf", { type: "application/pdf" });
this.objUrl = URL.createObjectURL(pdfBlob); this.objUrl = URL.createObjectURL(pdfBlob);
} }
const opts = this.props.opts; const opts = this.props.opts;

View File

@ -8,6 +8,7 @@ import { SimpleMustacheRenderer } from "./mustache/mustache";
import { CSVRenderer } from "./csv/csv"; import { CSVRenderer } from "./csv/csv";
import { OpenAIRenderer, OpenAIRendererModel } from "./openai/openai"; import { OpenAIRenderer, OpenAIRendererModel } from "./openai/openai";
import { SimplePdfRenderer } from "./pdf/pdf"; import { SimplePdfRenderer } from "./pdf/pdf";
import { SimpleMediaRenderer } from "./media/media";
import { isBlank } from "@/util/util"; import { isBlank } from "@/util/util";
import { sprintf } from "sprintf-js"; import { sprintf } from "sprintf-js";
@ -89,6 +90,16 @@ const PluginConfigs: RendererPluginType[] = [
mimeTypes: ["application/pdf"], mimeTypes: ["application/pdf"],
simpleComponent: SimplePdfRenderer, simpleComponent: SimplePdfRenderer,
}, },
{
name: "media",
rendererType: "simple",
heightType: "pixels",
dataType: "blob",
collapseType: "hide",
globalCss: null,
mimeTypes: ["video/*", "audio/*"],
simpleComponent: SimpleMediaRenderer,
},
]; ];
class PluginModelClass { class PluginModelClass {

View File

@ -37,6 +37,7 @@ import (
"github.com/wavetermdev/waveterm/wavesrv/pkg/comp" "github.com/wavetermdev/waveterm/wavesrv/pkg/comp"
"github.com/wavetermdev/waveterm/wavesrv/pkg/dbutil" "github.com/wavetermdev/waveterm/wavesrv/pkg/dbutil"
"github.com/wavetermdev/waveterm/wavesrv/pkg/pcloud" "github.com/wavetermdev/waveterm/wavesrv/pkg/pcloud"
"github.com/wavetermdev/waveterm/wavesrv/pkg/promptenc"
"github.com/wavetermdev/waveterm/wavesrv/pkg/releasechecker" "github.com/wavetermdev/waveterm/wavesrv/pkg/releasechecker"
"github.com/wavetermdev/waveterm/wavesrv/pkg/remote" "github.com/wavetermdev/waveterm/wavesrv/pkg/remote"
"github.com/wavetermdev/waveterm/wavesrv/pkg/remote/openai" "github.com/wavetermdev/waveterm/wavesrv/pkg/remote/openai"
@ -280,6 +281,7 @@ func init() {
registerCmdFn("mdview", MarkdownViewCommand) registerCmdFn("mdview", MarkdownViewCommand)
registerCmdFn("markdownview", MarkdownViewCommand) registerCmdFn("markdownview", MarkdownViewCommand)
registerCmdFn("pdfview", PdfViewCommand) registerCmdFn("pdfview", PdfViewCommand)
registerCmdFn("mediaview", MediaViewCommand)
registerCmdFn("csvview", CSVViewCommand) registerCmdFn("csvview", CSVViewCommand)
} }
@ -4916,6 +4918,58 @@ func PdfViewCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (scbu
return update, nil return update, nil
} }
func MakeReadFileUrl(screenId string, lineId string, filePath string) (string, error) {
qvals := make(url.Values)
qvals.Set("screenid", screenId)
qvals.Set("lineid", lineId)
qvals.Set("path", filePath)
qvals.Set("nonce", uuid.New().String())
hmacStr, err := promptenc.ComputeUrlHmac([]byte(scbase.WaveAuthKey), "/api/read-file", qvals)
if err != nil {
return "", fmt.Errorf("error computing hmac-url: %v", err)
}
qvals.Set("hmac", hmacStr)
return "/api/read-file?" + qvals.Encode(), nil
}
func MediaViewCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (scbus.UpdatePacket, error) {
if len(pk.Args) == 0 {
return nil, fmt.Errorf("%s requires an argument (file name)", GetCmdStr(pk))
}
// TODO more error checking on filename format?
if pk.Args[0] == "" {
return nil, fmt.Errorf("%s argument cannot be empty", GetCmdStr(pk))
}
fileName := pk.Args[0]
ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_RemoteConnected)
if err != nil {
return nil, err
}
outputStr := fmt.Sprintf("%s %q", GetCmdStr(pk), fileName)
cmd, err := makeStaticCmd(ctx, GetCmdStr(pk), ids, pk.GetRawStr(), []byte(outputStr))
if err != nil {
// TODO tricky error since the command was a success, but we can't show the output
return nil, err
}
// compute hmac read-file URL
readFileUrl, err := MakeReadFileUrl(ids.ScreenId, cmd.LineId, fileName)
if err != nil {
// TODO tricky error since the command was a success, but we can't show the output
return nil, fmt.Errorf("error making read-file url: %v", err)
}
// set the line state
lineState := make(map[string]any)
lineState[sstore.LineState_FileUrl] = readFileUrl
lineState[sstore.LineState_File] = fileName
update, err := addLineForCmd(ctx, "/"+GetCmdStr(pk), false, ids, cmd, "media", lineState)
if err != nil {
// TODO tricky error since the command was a success, but we can't show the output
return nil, err
}
update.AddUpdate(sstore.InteractiveUpdate(pk.Interactive))
return update, nil
}
func MarkdownViewCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (scbus.UpdatePacket, error) { func MarkdownViewCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (scbus.UpdatePacket, error) {
if len(pk.Args) == 0 { if len(pk.Args) == 0 {
return nil, fmt.Errorf("%s requires an argument (file name)", GetCmdStr(pk)) return nil, fmt.Errorf("%s requires an argument (file name)", GetCmdStr(pk))

View File

@ -36,6 +36,7 @@ var BareMetaCmds = []BareMetaCmdDecl{
{"mdview", "markdownview"}, {"mdview", "markdownview"},
{"csvview", "csvview"}, {"csvview", "csvview"},
{"pdfview", "pdfview"}, {"pdfview", "pdfview"},
{"mediaview", "mediaview"},
} }
const ( const (

View File

@ -63,6 +63,7 @@ const (
const ( const (
LineState_Source = "prompt:source" LineState_Source = "prompt:source"
LineState_File = "prompt:file" LineState_File = "prompt:file"
LineState_FileUrl = "wave:fileurl"
LineState_Min = "wave:min" LineState_Min = "wave:min"
LineState_Template = "template" LineState_Template = "template"
LineState_Mode = "mode" LineState_Mode = "mode"