Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No implementation found for method file on channel plugins.justsoft.xyz/video_thumbnail #133

Open
wlingf opened this issue Jan 14, 2023 · 19 comments

Comments

@wlingf
Copy link

wlingf commented Jan 14, 2023

image

@Alex-web0
Copy link

Same problem here

@sdkysfzai
Copy link

having same problem.

@Alex-web0
Copy link

Alex-web0 commented Mar 4, 2023

I found a solution thanks to chatGBT

class VideoCache {
  late final Directory cacheDir;

  /// Whenever this class is doing a download operation, this will be called
  final Function(int bytesRecieved, int bytesLeft)? onReceiveBytes;

  /// Whenever a download operation is completed, this will be called!
  final VoidCallback? downloadCompleted;

  final VoidCallback? onDownloadOperationCanceled;

  final VoidCallback? onDownloadStart;

  CancelToken cancelToken = CancelToken();
  bool _disposed = false;

  VideoCache({
    this.onReceiveBytes,
    this.downloadCompleted,
    this.onDownloadOperationCanceled,
    this.onDownloadStart,
  });

  /// Must be called ONCE, before using any method!
  init() async {
    cacheDir = await getTemporaryDirectory();
    if (kDebugMode) {
      print("INITIALIZED INSTANCE OF VIDEO CACHE");
    }
  }

  Future<File?> getVideoThumbnail(String outPath, String url) async {
    final arguments = [
      '-i',
      url,
      '-ss',
      '00:00:01.000',
      '-vframes',
      '1',
      '-q:v',
      '50',
      outPath
    ];

    final flutterFFmpeg = FlutterFFmpeg();
    final execution = await flutterFFmpeg.executeWithArguments(arguments);
    if (execution == 0) {
      return File(outPath);
    } else {
      print('Error extracting video thumbnail');
      return null;
    }
  }

  Future<ImageProvider> getCachedVideoThumbnail(String url) async {
    final filename = url.split('/').last;
    final usableFileName = _generateUniqueKeyName(filename);
    const determinedExtension = 'jpg';

    final file = File('${cacheDir.path}/$usableFileName.$determinedExtension');
    print(file.path);

    if (file.existsSync()) {
      return FileImage(file);
    } else {
      try {
        print('Thumbnail not cached, trying to download');
        final thumbnailFile = await getVideoThumbnail(file.path, url);

        return (thumbnailFile != null
                ? FileImage(thumbnailFile)
                : const AssetImage('assets/images/default-fallback-media.png'))
            as ImageProvider;
      } catch (e) {
        print('Error downloading video thumbnail: $e');
        return const AssetImage('assets/images/default-fallback-media.png');
      }
    }
  }

  void clearCache() async {
    try {
      await cacheDir.delete(recursive: true);
      await cacheDir.create();
    } catch (e) {
      print('Error clearing video cache: $e');
    }
  }

  String _determineVideoExtension(String name) {
    if (name.contains('.avi')) {
      return 'avi';
    }
    return 'mp4';
  }

  /// generates a key, from ANY string.
  String _generateUniqueKeyName(String input) {
    var bytes = utf8.encode(input); // Convert the input string to bytes
    var digest = sha256.convert(bytes); // Generate the SHA-256 hash
    var base64Url =
        base64UrlEncode(digest.bytes); // Encode the hash as Base64 URL
    return base64Url.substring(
        0, 32); // Return the first 32 characters of the encoded string
    // Encode the key using URL-safe Base64
  }

  /// fetch the video
  Future<File?> fetchVideoFromCacheIfAny(String url) async {
    File file = _generateFileWithCorrectNamingFromUrl(url);
    print(file.path);

    if (file.existsSync()) {
      return file;
    } else {
      try {
        onDownloadStart?.call();
        final response = await Dio().get(url,
            options: Options(responseType: ResponseType.bytes),
            cancelToken: cancelToken,
            onReceiveProgress: onReceiveBytes);

        final downloadedFile = File(file.path);
        await downloadedFile.writeAsBytes(response.data as List<int>);
        downloadCompleted?.call();
        return file;
      } catch (e) {
        if (e is CancelToken) {}
        if (!_disposed) {
          onDownloadOperationCanceled?.call();
        }
        cancelToken = CancelToken();
        print('Error downloading video: $e');
        return null;
      }
    }
  }

  File _generateFileWithCorrectNamingFromUrl(String url) {
    final filename = url.split('/').last;
    final usableFileName = _generateUniqueKeyName(filename);
    final determinedExtension = _determineVideoExtension(filename);

    final file = File('${cacheDir.path}/$usableFileName.$determinedExtension');
    return file;
  }

  bool doesUrlFileExistInCache(String url) {
    File file = _generateFileWithCorrectNamingFromUrl(url);
    return file.existsSync();
  }

  cancelDownloadOperation() {
    cancelToken.cancel();
  }

  dispose() {
    print("DISPOSED OF: VIDEO CACHE INSTANCE! & CANCELED ON GOING OPERATIONS");
    _disposed = true;
    cancelToken.cancel();
  }
}

@sdkysfzai
Copy link

The solution for me was to run in release mode in Android, debug mode did not work. While in iOS debug works fine.

@saveKenny
Copy link

Facing the same issue v0.5.3

@lucasuracosta
Copy link

Same issue here on v0.5.3

@misha-muraly
Copy link

facing the same issue in v0.5.3

1 similar comment
@serikqaliev
Copy link

facing the same issue in v0.5.3

@bjmcallister
Copy link

Just adding to the list here.

Same here v0.5.3

@AbdullahGaber
Copy link

same here v0.5.3

@berkaykurkcu
Copy link

same here. Any workarounds?

@AbdullahGaber
Copy link

same here. Any workarounds?

worked with me only in https links

@codesculpture
Copy link

codesculpture commented Aug 3, 2023

Hey Guys, The problem is when u call thumbnailFile requires the thumbnailPath argument. You may ignore it since it was marked as optional. But in native side the code was designed in a way that it expecting the thumbnailPath argument and there is no null check or fallback. So thats why u may get MissingException Error.. I got that and fixed by providing thumbNailPath as getTemporaryDirectory() (you can give other dirs as well)

@Piscen
Copy link

Piscen commented Aug 29, 2023

Hey Guys, The problem is when u call thumbnailFile requires the thumbnailPath argument. You may ignore it since it was marked as optional. But in native side the code was designed in a way that it expecting the thumbnailPath argument and there is no null check or fallback. So thats why u may get MissingException Error.. I got that and fixed by providing thumbNailPath as getTemporaryDirectory() (you can give other dirs as well)

Even if this parameter “thumbnailPath” is added, an error will still be reported

@iOS-Kel
Copy link

iOS-Kel commented Sep 4, 2023

same here v0.5.3

@indeedshaw
Copy link

any solution?

@WongChanKit
Copy link

android\app\src\main\AndroidManifest.xml, add this in application

this:

android:usesCleartextTraffic="true"

@saramand9
Copy link

android\app\src\main\AndroidManifest.xml,将其添加到应用程序中

这:

android:usesCleartextTraffic="true"

this works for me

@BuyMyBeard
Copy link

BuyMyBeard commented Jun 10, 2024

android\app\src\main\AndroidManifest.xml, add this in application

this:

android:usesCleartextTraffic="true"

Worked for me too. Except that this line is very dangerous. It makes your app use http instead of https. This means that it lowers the security significantly and allows Man in the Middle Attacks, spying, and other security concerns.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests