media_item.dart 2.38 KB
/// 媒体项类型
enum MediaType {
  image,
  video,
}

/// 媒体项数据模型
///
/// 用于 [previewMedia] 指令统一描述图片或视频。
class MediaItem {
  final String url;
  final MediaType type;

  /// 视频封面图(仅视频有效,可选)
  final String? poster;

  const MediaItem({
    required this.url,
    required this.type,
    this.poster,
  });

  /// 根据 URL 后缀粗略推断媒体类型
  ///
  /// 支持的视频后缀:mp4 / mov / m4v / 3gp / mkv / webm / avi / flv / ts
  /// 其它默认按图片处理
  factory MediaItem.fromUrl(String url) {
    return MediaItem(url: url, type: _detectType(url));
  }

  /// 从 previewMedia 指令的 source 对象创建:
  /// {
  ///   "url": String,           // 必填,远程或本地 URL
  ///   "type": "image|video",  // 可选,未提供时按 URL 后缀推断
  ///   "poster": String,        // 可选,视频封面图
  /// }
  factory MediaItem.fromSource(Map source) {
    final dynamic rawUrl = source['url'];
    final String url = rawUrl is String ? rawUrl : '';

    MediaType type;
    final dynamic rawType = source['type'];
    if (rawType is String) {
      final String t = rawType.trim().toLowerCase();
      if (t == 'video') {
        type = MediaType.video;
      } else if (t == 'image') {
        type = MediaType.image;
      } else {
        type = _detectType(url);
      }
    } else {
      type = _detectType(url);
    }

    final dynamic rawPoster = source['poster'];
    final String? poster =
        (rawPoster is String && rawPoster.isNotEmpty) ? rawPoster : null;

    return MediaItem(url: url, type: type, poster: poster);
  }

  static MediaType _detectType(String url) {
    if (url.isEmpty) {
      return MediaType.image;
    }
    // 去除 query / fragment
    String path = url;
    final int qIdx = path.indexOf('?');
    if (qIdx >= 0) {
      path = path.substring(0, qIdx);
    }
    final int hIdx = path.indexOf('#');
    if (hIdx >= 0) {
      path = path.substring(0, hIdx);
    }
    final int dotIdx = path.lastIndexOf('.');
    if (dotIdx < 0) {
      return MediaType.image;
    }
    final String ext = path.substring(dotIdx + 1).toLowerCase();
    const Set<String> videoExt = {
      'mp4',
      'mov',
      'm4v',
      '3gp',
      'mkv',
      'webm',
      'avi',
      'flv',
      'ts',
    };
    return videoExt.contains(ext) ? MediaType.video : MediaType.image;
  }
}