This repository has been archived by the owner on Sep 11, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 829
/
Media.ts
178 lines (160 loc) · 6.86 KB
/
Media.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/*
* Copyright 2021 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { MatrixClient, parseErrorResponse, ResizeMethod } from "matrix-js-sdk/src/matrix";
import { MediaEventContent } from "matrix-js-sdk/src/types";
import { Optional } from "matrix-events-sdk";
import { MatrixClientPeg } from "../MatrixClientPeg";
import { IPreparedMedia, prepEventContentAsMedia } from "./models/IMediaEventContent";
import { UserFriendlyError } from "../languageHandler";
// Populate this class with the details of your customisations when copying it.
// Implementation note: The Media class must complete the contract as shown here, though
// the constructor can be whatever is relevant to your implementation. The mediaForX
// functions below create an instance of the Media class and are used throughout the
// project.
/**
* A media object is a representation of a "source media" and an optional
* "thumbnail media", derived from event contents or external sources.
*/
export class Media {
private client: MatrixClient;
// Per above, this constructor signature can be whatever is helpful for you.
public constructor(
private prepared: IPreparedMedia,
client?: MatrixClient,
) {
this.client = client ?? MatrixClientPeg.safeGet();
if (!this.client) {
throw new Error("No possible MatrixClient for media resolution. Please provide one or log in.");
}
}
/**
* True if the media appears to be encrypted. Actual file contents may vary.
*/
public get isEncrypted(): boolean {
return !!this.prepared.file;
}
/**
* The MXC URI of the source media.
*/
public get srcMxc(): string {
return this.prepared.mxc;
}
/**
* The MXC URI of the thumbnail media, if a thumbnail is recorded. Null/undefined
* otherwise.
*/
public get thumbnailMxc(): Optional<string> {
return this.prepared.thumbnail?.mxc;
}
/**
* Whether or not a thumbnail is recorded for this media.
*/
public get hasThumbnail(): boolean {
return !!this.thumbnailMxc;
}
/**
* The HTTP URL for the source media.
*/
public get srcHttp(): string | null {
// eslint-disable-next-line no-restricted-properties
return this.client.mxcUrlToHttp(this.srcMxc, undefined, undefined, undefined, false, true) || null;
}
/**
* The HTTP URL for the thumbnail media (without any specified width, height, etc). Null/undefined
* if no thumbnail media recorded.
*/
public get thumbnailHttp(): string | null {
if (!this.hasThumbnail) return null;
// eslint-disable-next-line no-restricted-properties
return this.client.mxcUrlToHttp(this.thumbnailMxc!, undefined, undefined, undefined, false, true);
}
/**
* Gets the HTTP URL for the thumbnail media with the requested characteristics, if a thumbnail
* is recorded for this media. Returns null/undefined otherwise.
* @param {number} width The desired width of the thumbnail.
* @param {number} height The desired height of the thumbnail.
* @param {"scale"|"crop"} mode The desired thumbnailing mode. Defaults to scale.
* @returns {string} The HTTP URL which points to the thumbnail.
*/
public getThumbnailHttp(width: number, height: number, mode: ResizeMethod = "scale"): string | null {
if (!this.hasThumbnail) return null;
// scale using the device pixel ratio to keep images clear
width = Math.floor(width * window.devicePixelRatio);
height = Math.floor(height * window.devicePixelRatio);
// eslint-disable-next-line no-restricted-properties
return this.client.mxcUrlToHttp(this.thumbnailMxc!, width, height, mode, false, true);
}
/**
* Gets the HTTP URL for a thumbnail of the source media with the requested characteristics.
* @param {number} width The desired width of the thumbnail.
* @param {number} height The desired height of the thumbnail.
* @param {"scale"|"crop"} mode The desired thumbnailing mode. Defaults to scale.
* @returns {string} The HTTP URL which points to the thumbnail.
*/
public getThumbnailOfSourceHttp(width: number, height: number, mode: ResizeMethod = "scale"): string | null {
// scale using the device pixel ratio to keep images clear
width = Math.floor(width * window.devicePixelRatio);
height = Math.floor(height * window.devicePixelRatio);
// eslint-disable-next-line no-restricted-properties
return this.client.mxcUrlToHttp(this.srcMxc, width, height, mode, false, true);
}
/**
* Creates a square thumbnail of the media. If the media has a thumbnail recorded, that MXC will
* be used, otherwise the source media will be used.
* @param {number} dim The desired width and height.
* @returns {string} An HTTP URL for the thumbnail.
*/
public getSquareThumbnailHttp(dim: number): string | null {
dim = Math.floor(dim * window.devicePixelRatio); // scale using the device pixel ratio to keep images clear
if (this.hasThumbnail) {
return this.getThumbnailHttp(dim, dim, "crop");
}
return this.getThumbnailOfSourceHttp(dim, dim, "crop");
}
/**
* Downloads the source media.
* @returns {Promise<Response>} Resolves to the server's response for chaining.
*/
public async downloadSource(): Promise<Response> {
const src = this.srcHttp;
if (!src) {
throw new UserFriendlyError("error|download_media");
}
const res = await fetch(src);
if (!res.ok) {
throw parseErrorResponse(res, await res.text());
}
return res;
}
}
/**
* Creates a media object from event content.
* @param {MediaEventContent} content The event content.
* @param {MatrixClient} client? Optional client to use.
* @returns {Media} The media object.
*/
export function mediaFromContent(content: Partial<MediaEventContent>, client?: MatrixClient): Media {
return new Media(prepEventContentAsMedia(content), client);
}
/**
* Creates a media object from an MXC URI.
* @param {string} mxc The MXC URI.
* @param {MatrixClient} client? Optional client to use.
* @returns {Media} The media object.
*/
export function mediaFromMxc(mxc?: string, client?: MatrixClient): Media {
return mediaFromContent({ url: mxc }, client);
}