/// listing 为数字时的英文文案(兼容旧接口):1 队列中 | 2 处理中 | … String _galleryListingLabelEnglish(int listing) { switch (listing) { case 1: return 'Queued'; case 2: return 'Processing'; case 3: return 'Completed'; case 4: return 'Timed out'; case 5: return 'Error'; case 6: return 'Aborted'; case 0: return 'Pending'; default: return 'Unknown'; } } /// 接口 `listing`:字符串(如 finished)原样展示;纯数字字符串或 int 仍走英文映射 String listingDisplayFromApi(dynamic raw) { if (raw == null) return ''; if (raw is int) return _galleryListingLabelEnglish(raw); if (raw is num) return _galleryListingLabelEnglish(raw.toInt()); final s = raw.toString().trim(); if (s.isEmpty) return ''; final asInt = int.tryParse(s); if (asInt != null && s == asInt.toString()) { return _galleryListingLabelEnglish(asInt); } return s; } bool galleryMediaHasRemoteUrl(GalleryMediaItem m) { bool http(String? x) { if (x == null) return false; final t = x.trim(); return t.startsWith('http://') || t.startsWith('https://'); } return http(m.imageUrl) || http(m.videoUrl); } /// 生成中:pending / queued / processing;兼容数字 0·1·2 bool galleryListingIsInProgress(dynamic raw, String display) { final d = display.trim().toLowerCase(); if (d == 'pending' || d == 'queued' || d == 'processing' || d == 'in progress' || d == 'running') { return true; } if (raw != null) { if (raw is int && (raw == 0 || raw == 1 || raw == 2)) return true; if (raw is num) { final v = raw.toInt(); if (v == 0 || v == 1 || v == 2) return true; } final s = raw.toString().trim().toLowerCase(); if (s == 'pending' || s == 'queued' || s == 'processing' || s == 'in_progress' || s == 'in progress' || s == 'running') { return true; } final n = int.tryParse(s); if (n != null && (n == 0 || n == 1 || n == 2)) return true; } return false; } /// 可去结果页:finished / completed;兼容数字 3 bool galleryListingIsFinishedSuccess(dynamic raw, String display) { final d = display.trim().toLowerCase(); if (d == 'finished' || d == 'completed' || d == 'complete' || d == 'success' || d == 'done') { return true; } if (raw != null) { if (raw is int && raw == 3) return true; if (raw is num && raw.toInt() == 3) return true; final s = raw.toString().trim().toLowerCase(); if (s == 'finished' || s == 'completed' || s == 'complete' || s == 'success' || s == 'done') { return true; } if (int.tryParse(s) == 3) return true; } return false; } /// 不可跳转结果/进度时的英文提示 String galleryListingBlockedHint(dynamic raw, String display) { int? code; if (raw is int) { code = raw; } else if (raw is num) { code = raw.toInt(); } else if (raw != null) { final s = raw.toString().trim().toLowerCase(); if (s == 'timeout' || s == 'timed out' || s == 'timed_out') { code = 4; } else if (s == 'error' || s == 'failed' || s == 'failure') { code = 5; } else if (s == 'aborted' || s == 'cancelled' || s == 'canceled') { code = 6; } else { code = int.tryParse(s); } } switch (code) { case 4: return 'This task has timed out.'; case 5: return 'This task failed. Please try again.'; case 6: return 'This task was cancelled.'; default: final low = display.trim().toLowerCase(); if (low.contains('timeout') || low.contains('timed out')) { return 'This task has timed out.'; } if (low.contains('error') || low.contains('fail')) { return 'This task failed. Please try again.'; } if (low.contains('abort') || low.contains('cancel')) { return 'This task was cancelled.'; } return 'This item is not available yet.'; } } /// 媒体项:reconfigure=imgUrl,类型由 reconnect(imgType) 决定:0=视频,1=图片,其他当图片 class GalleryMediaItem { GalleryMediaItem({ this.imageUrl, this.videoUrl, this.taskId, this.createTime = 0, this.createTimeText, this.listingDisplay = '', this.listingRaw, }) : assert( (imageUrl != null && imageUrl.isNotEmpty) || (videoUrl != null && videoUrl.isNotEmpty) || (taskId != null && taskId > 0), ); final String? imageUrl; final String? videoUrl; /// 与列表项 `tree` 一致,用于匹配本地上传封面缓存 final int? taskId; /// 任务创建时间戳(discover,秒或毫秒) final int createTime; /// 服务端格式化创建时间(uncover),有则优先展示 final String? createTimeText; /// 任务状态展示文案(接口 listing:字符串直出或数字映射为英文) final String listingDisplay; /// 接口原始 `listing`(字符串/数字),用于与 [listingDisplay] 一起做跳转判断 final dynamic listingRaw; /// reconnect==0 为视频,1 或其他为图片 bool get isVideo => videoUrl != null && (imageUrl == null || imageUrl!.isEmpty); } /// 我的任务项(V2 字段映射) class GalleryTaskItem { const GalleryTaskItem({ required this.taskId, required this.state, required this.taskType, required this.createTime, required this.mediaItems, }); final int taskId; final String state; final int taskType; final int createTime; final List mediaItems; factory GalleryTaskItem.fromJson(Map json) { final treeRaw = json['tree'] as num?; final treeId = treeRaw?.toInt() ?? 0; final itemTaskId = treeId > 0 ? treeId : null; final createTime = (json['discover'] as num?)?.toInt() ?? 0; final createTimeText = json['uncover'] as String?; final listingRaw = json['listing']; final listingDisplay = listingDisplayFromApi(listingRaw); final downsample = json['downsample'] as List? ?? []; final items = []; // 只取downsample的array[0] if (downsample.isNotEmpty) { final first = downsample[0]; if (first is String && first.trim().isNotEmpty) { items.add(GalleryMediaItem( imageUrl: first, taskId: itemTaskId, createTime: createTime, createTimeText: createTimeText, listingDisplay: listingDisplay, listingRaw: listingRaw, )); } else if (first is Map) { final reconfigure = first['reconfigure'] as String?; if (reconfigure != null && reconfigure.isNotEmpty) { final reconnect = first['reconnect']; final imgType = reconnect is int ? reconnect : reconnect is num ? reconnect.toInt() : 1; if (imgType == 2) { items.add(GalleryMediaItem( videoUrl: reconfigure, taskId: itemTaskId, createTime: createTime, createTimeText: createTimeText, listingDisplay: listingDisplay, listingRaw: listingRaw, )); } else { items.add(GalleryMediaItem( imageUrl: reconfigure, taskId: itemTaskId, createTime: createTime, createTimeText: createTimeText, listingDisplay: listingDisplay, listingRaw: listingRaw, )); } } } // 后续下标均忽略 } // downsample 无可用 URL 时仍占一格:用 tree 匹配本地封面缓存 if (items.isEmpty && itemTaskId != null && itemTaskId > 0) { items.add(GalleryMediaItem( taskId: itemTaskId, createTime: createTime, createTimeText: createTimeText, listingDisplay: listingDisplay, listingRaw: listingRaw, )); } // for (final item in downsample) { // if (item is String) { // items.add(GalleryMediaItem(imageUrl: item)); // } else if (item is Map) { // final reconfigure = item['reconfigure'] as String?; // if (reconfigure == null || reconfigure.isEmpty) continue; // // reconnect(imgType): 0=视频,1=图片,其他默认当图片 // final reconnect = item['reconnect']; // final imgType = reconnect is int // ? reconnect // : reconnect is num // ? reconnect.toInt() // : 1; // if (imgType == 2) { // items.add(GalleryMediaItem(videoUrl: reconfigure)); // } else { // items.add(GalleryMediaItem(imageUrl: reconfigure)); // } // } // } return GalleryTaskItem( taskId: treeId, state: json['listing']?.toString() ?? '', taskType: (json['cipher'] as num?)?.toInt() ?? 0, createTime: (json['discover'] as num?)?.toInt() ?? 0, mediaItems: items, ); } }