video_utils.dart 8.89 KB
import 'dart:async';
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:one_key/utils/utils.dart';
import 'package:path_provider/path_provider.dart';
import 'package:video_compress/video_compress.dart';
import 'package:video_thumbnail/video_thumbnail.dart';

class VideoUtils {
// 1280*720
  static final int maxVideoPixel = 1280 * 720;
  static final int maxVideoSize = 50;

  static Future<String> downloadVideo(String videoUrl, String savePath) async {
    var dio = Dio();
    var response = await dio.get(
      videoUrl,
      options: Options(responseType: ResponseType.bytes),
    );

    final file = File(savePath);
    File tempFile = await file.writeAsBytes(response.data); // 将字节写入文件
    return tempFile.path;
  }

  /// 清除缓存
  static Future<void> clearTemporaryDirectory() async {
    Directory tempDir = await getTemporaryDirectory();
    if (tempDir == null) return;
    await _delete(tempDir);
  }

  static Future<void> _delete(FileSystemEntity file) async {
    if (file is Directory) {
      final List<FileSystemEntity> children = file.listSync();
      for (final FileSystemEntity child in children) {
        await _delete(child);
      }
    } else {
      await file.delete();
    }
  }

  static getDuration(double duration, {String dateFormat = 'mm:ss'}) {
    int value = duration ~/ 1000; // 毫秒转换为秒;
    int hour = (value) ~/ 3600;
    String hourStr = '${hour}';
    if (hour < 10) {
      //时
      hourStr = "0" + '${hour}';
    }
    int minute = (value % 3600) ~/ 60;
    String minuteStr = '${minute}';
    if (minute < 10) {
      //分
      minuteStr = "0" + '${minute}';
    }
    int second = value % 60;
    String secondStr = '${second}';
    if (second < 10) {
      //秒
      secondStr = "0" + '${second}';
    }
    switch (dateFormat) {
      case 'hh:mm':
        return hourStr + ":" + minuteStr;
        break;
      case 'mm:ss':
        return minuteStr + ":" + secondStr;
        break;
      case 'hh':
        return hourStr;
        break;
      default:
        return hourStr + ":" + minuteStr + ":" + secondStr;
        break;
    }
  }

  // 获取视频的大小(以 MB 为单位)
  static Future<double> getVideoFileSize(String videoPath) async {
    final File file = File(videoPath);
    return file.lengthSync() / 1024 / 1024;
  }

  // 获取视频的像素大小(宽度 * 高度)
  static Future<int> getVideoPixelSize(String videoPath) async {
    final info =
        await VideoCompress.getMediaInfo(videoPath); //视频信息:时长、像素(宽高)、大小、path

    final int width = info.width!;
    final int height = info.height!;

    return width * height;
  }

  // 获取视频的像素大小(宽度 * 高度)
  // static Future<double> getVideoPixelSize(String videoPath) async {
  //   final VideoPlayerController _controller = VideoPlayerController.file(File(videoPath));
  //   await _controller.initialize();
  //   final double width = _controller.value.size.width;
  //   final double height = _controller.value.size.height;
  //   await _controller.dispose();
  //   return width * height;
  // }
  // 获取视频的封面图
//  static Future<String> getVideoThumbnail(String videoPath) async {
//    final thumbnailPath = await VideoThumbnail.thumbnailFile(
//      video: videoPath,
//      thumbnailPath: (await getTemporaryDirectory()).path,
//      imageFormat: ImageFormat.JPEG,
//      maxHeight: 128,  // 设置最大高度
//      quality: 75, // 设置质量
//    );
//    return thumbnailPath;
//  }
  // 获取视频的封面图
  static Future<String> getThumbnailFile(String url,
      {bool isNetUrl = true}) async {
    // final fileName = await VideoThumbnail.thumbnailFile(
    //   video: url,
    //   thumbnailPath: (await getTemporaryDirectory()).path,
    //   // imageFormat: ImageFormat.WEBP,
    //   // maxHeight:
    //   //     64, // specify the height of the thumbnail, let the width auto-scaled to keep the source aspect ratio
    //   quality: 100,
    // );

    String thumbnailPath = '';

    if (Platform.isAndroid || Platform.isIOS) {
      final thumbnail = (await VideoThumbnail.thumbnailFile(
        video: url,
        imageFormat: ImageFormat.PNG,
        quality: 100,
      ))!;

      // 获取应用文件夹路径
      String name = DateTime.now().millisecondsSinceEpoch.toString();
      final directory = await getApplicationDocumentsDirectory();
      thumbnailPath = '${directory.path}/${name}.png';

      // 将封面图保存到应用文件夹
      final File file = File(thumbnailPath);
      await file.writeAsBytes(await File(thumbnail).readAsBytes());

      // print('Thumbnail saved to $thumbnailPath');
    } else if (Platform.isOhos) {
      // final PathProviderPlatform provider = PathProviderPlatform.instance;

      // thumbnailPath = await provider.getExternalStoragePath();
      // Directory? downloadsDir = await getTemporaryDirectory();
      String thumbnailPathVideo = url;
      String fileName = '';
      String thumbnailPathSave = (await getTemporaryDirectory()).path;
      print(thumbnailPathSave);
      if (isNetUrl) {
        // 鸿蒙端网络地址需要自己提前下载

        // getTemporaryDirectory().then((d) => thumbnailPath = d.path);

        fileName = Utils.extractFileNameFromUrl(url);
        print("File Name: $fileName"); // 输出:File Name: file.txt
//鸿蒙,flutter侧下载视频到缓存,然后使用video_thumbnail 插件从鸿蒙设备本地缓存读取文件并获取第一帧图片
        thumbnailPathVideo = await VideoUtils.downloadVideo(
            url, thumbnailPathSave + '/' + fileName);
      }

      try {
        // 封面图片路径
        String name = DateTime.now().millisecondsSinceEpoch.toString();
        thumbnailPath = '${thumbnailPathSave}/${name}.png';

        String? imagePath = await VideoThumbnail.thumbnailFile(
          video: thumbnailPathVideo,
          thumbnailPath: thumbnailPath,
          // imageFormat: ImageFormat.WEBP,
          // maxHeight:
          //     64, // specify the height of the thumbnail, let the width auto-scaled to keep the source aspect ratio
          quality: 100,
        );

        thumbnailPath = imagePath!;

        ///没有直接使用插件返回的图片地址    /cachepng    图片地址有问题,url没有后缀   .mp4   鸿蒙端是根据字符串中 .  位置分割来获取名称的,   找不到.   状态返回-1  ,所以就出现了路径错误
        // if (isNetUrl && !fileName.contains('.')) {
        //   // 获取应用文件夹路径
        //   String name = DateTime.now().millisecondsSinceEpoch.toString();
        //   thumbnailPath = '${thumbnailPath}/${name}.png';

        //   // 将封面图保存到应用文件夹
        //   final File file = File(thumbnailPath);
        //   File imagePathNew =
        //       await file.writeAsBytes(await File(imagePath!).readAsBytes());

        //   print(imagePathNew.path);

        //   thumbnailPath = imagePathNew.path;
        // }
      } catch (e) {
        print(e);

        return '';
      }
    }

    return thumbnailPath;
  }

  Future<void> copyFile(String oldPath, String newPath) async {
    // 获取应用的临时目录
    final tempDir = await getTemporaryDirectory();
    final newTempPath = '${tempDir.path}/$newPath';

    // 创建新文件的目录
    final newDir =
        Directory(newTempPath.substring(0, newTempPath.lastIndexOf('/')));
    if (!newDir.existsSync()) {
      newDir.createSync(recursive: true);
    }

    // 复制文件到新路径
    final file = File(oldPath);
    await file.copy(newTempPath);

    print('文件已复制到: $newTempPath');
  }

  // Future<String> getTempDirectoryPathWithTimestamp() async {
  //   // 获取临时目录
  //   final tempDir = await getTemporaryDirectory();
  //   // 获取当前时间戳
  //   final timestamp = DateTime.now().millisecondsSinceEpoch;
  //   // 拼接路径和时间戳
  //   final tempPathWithTimestamp = join(tempDir.path, '$timestamp');

  //   return tempPathWithTimestamp;
  // }

  // 是否压缩 (压缩前的校验)
  // 视频像素大小 小于 720p || 视频大小 小于 50mb --> 压缩
  static Future<bool> shouldCompress(String videoPath) async {
    int p = await getVideoPixelSize(videoPath);
    double s = await getVideoFileSize(videoPath); //size
    if (p > maxVideoPixel || s >= maxVideoSize) {
      return true;
    } else {
      return false;
    }
  }

  // 压缩视频的方法
  static Future<File?> compress(String videoPath) async {
    // 订阅压缩进度
    // _subscription = VideoCompress.compressProgress$.subscribe((progress) {
    //   _progress = progress;
    //   // 回调进度给外部
    //   if (onProgress != null) {
    //     onProgress!(progress);
    //   }
    // });

    MediaInfo info = (await VideoCompress.compressVideo(
      videoPath,
      quality: VideoQuality.Res1280x720Quality,
      deleteOrigin: false,
    ))!;
    return info.file;
  }
}