Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 20 additions & 17 deletions lib/video_thumbnail_web.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import 'dart:async';
import 'dart:html';
import 'dart:js_interop';
import 'dart:math' as math;

import 'package:cross_file/cross_file.dart';
import 'package:flutter/services.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:get_thumbnail_video/src/image_format.dart';
import 'package:get_thumbnail_video/src/video_thumbnail_platform.dart';
import 'package:web/web.dart';

// An error code value to error name Map.
// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code
Expand Down Expand Up @@ -61,7 +62,7 @@ class VideoThumbnailWeb extends VideoThumbnailPlatform {
quality: quality,
);

return XFile(Url.createObjectUrlFromBlob(blob), mimeType: blob.type);
return XFile(URL.createObjectURL(blob), mimeType: blob.type);
}

@override
Expand All @@ -83,10 +84,10 @@ class VideoThumbnailWeb extends VideoThumbnailPlatform {
timeMs: timeMs,
quality: quality,
);
final path = Url.createObjectUrlFromBlob(blob);
final path = URL.createObjectURL(blob);
final file = XFile(path, mimeType: blob.type);
final bytes = await file.readAsBytes();
Url.revokeObjectUrl(path);
URL.revokeObjectURL(path);

return bytes;
}
Expand All @@ -102,21 +103,21 @@ class VideoThumbnailWeb extends VideoThumbnailPlatform {
}) async {
final completer = Completer<Blob>();

final video = document.createElement('video') as VideoElement;
final video = document.createElement('video') as HTMLVideoElement;
final timeSec = math.max(timeMs / 1000, 0);
final fetchVideo = headers != null && headers.isNotEmpty;

video.onLoadedMetadata.listen((event) {
video.currentTime = timeSec;

if (fetchVideo) {
Url.revokeObjectUrl(video.src);
URL.revokeObjectURL(video.src);
}
});

video.onSeeked.listen((Event e) async {
if (!completer.isCompleted) {
final canvas = document.createElement('canvas') as CanvasElement;
final canvas = document.createElement('canvas') as HTMLCanvasElement;
final ctx = canvas.getContext('2d')! as CanvasRenderingContext2D;

if (maxWidth == 0 && maxHeight == 0) {
Expand All @@ -142,16 +143,18 @@ class VideoThumbnailWeb extends VideoThumbnailPlatform {
canvas
..width = maxWidth
..height = maxHeight;
ctx.drawImageScaled(video, 0, 0, maxWidth, maxHeight);
ctx.drawImage(video, 0, 0, maxWidth.toDouble(), maxHeight.toDouble());
}

try {
final blob = canvas.toBlob(
void callback(Blob? blob) {
completer.complete(blob);
}
canvas.toBlob(
callback.toJS,
_imageFormatToCanvasFormat(imageFormat),
quality / 100,
(quality / 100).toJS,
);

completer.complete(blob);
} catch (e, s) {
completer.completeError(
PlatformException(
Expand Down Expand Up @@ -189,7 +192,7 @@ class VideoThumbnailWeb extends VideoThumbnailPlatform {
headers: headers,
);

video.src = Url.createObjectUrlFromBlob(blob);
video.src = URL.createObjectURL(blob);
} catch (e, s) {
completer.completeError(e, s);
}
Expand All @@ -205,18 +208,18 @@ class VideoThumbnailWeb extends VideoThumbnailPlatform {
/// Fetching video by [headers].
///
/// To avoid reading the video's bytes into memory, set the
/// [HttpRequest.responseType] to 'blob'. This allows the blob to be stored in
/// [XMLHttpRequest.responseType] to 'blob'. This allows the blob to be stored in
/// the browser's disk or memory cache.
Future<Blob> _fetchVideoByHeaders({
required String videoSrc,
required Map<String, String> headers,
}) async {
final completer = Completer<Blob>();

final xhr = HttpRequest()
..open('GET', videoSrc, async: true)
final xhr = XMLHttpRequest()
..open('GET', videoSrc, true)
..responseType = 'blob';
headers.forEach(xhr.setRequestHeader);
headers.forEach((key, value) => xhr.setRequestHeader(key, value));

xhr.onLoad.first.then((ProgressEvent value) {
completer.complete(xhr.response as Blob);
Expand Down