mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
quick media viewer (using new hmac read-file urls) (#451)
This commit is contained in:
parent
697f7c0758
commit
0f2f6c9cc0
7
src/plugins/media/media.less
Normal file
7
src/plugins/media/media.less
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.media-renderer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: var(--termpad);
|
||||||
|
}
|
49
src/plugins/media/media.tsx
Normal file
49
src/plugins/media/media.tsx
Normal 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 };
|
@ -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;
|
||||||
|
@ -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 {
|
||||||
|
@ -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))
|
||||||
|
@ -36,6 +36,7 @@ var BareMetaCmds = []BareMetaCmdDecl{
|
|||||||
{"mdview", "markdownview"},
|
{"mdview", "markdownview"},
|
||||||
{"csvview", "csvview"},
|
{"csvview", "csvview"},
|
||||||
{"pdfview", "pdfview"},
|
{"pdfview", "pdfview"},
|
||||||
|
{"mediaview", "mediaview"},
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -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"
|
||||||
|
Loading…
Reference in New Issue
Block a user