import 'cloud_media_link.dart'; import '../services/cloud_link_extractor.dart'; class Post { const Post({ required this.id, required this.providerId, required this.providerName, required this.previewUrl, required this.sampleUrl, required this.fileUrl, required this.tags, required this.rating, required this.width, required this.height, this.source, required this.createdAt, required this.fileType, required this.score, this.tagGroups = const {}, }); final String id; final String providerId; final String providerName; final String previewUrl; final String sampleUrl; final String fileUrl; final List tags; final String rating; final int width; final int height; final String? source; final DateTime createdAt; final String fileType; final int score; final Map> tagGroups; String get cacheKey => '$providerId:$id'; List get cloudLinks { final list = tagGroups['cloud_links']; if (list != null && list.isNotEmpty) { return list .map((e) => CloudMediaLink.tryDecode(e)) .whereType() .toList(growable: false); } if (source != null && source!.isNotEmpty) { return CloudLinkExtractor.extractLinks(source: source); } return const []; } String? get commentary { final list = tagGroups['description']; if (list != null && list.isNotEmpty) { return list.first; } return null; } List get cleanTags { return tags.where((tag) { final t = tag.trim(); if (t.isEmpty) return false; if (t == 'cloud_mirror') return false; if (t.startsWith('{') && t.endsWith('}')) return false; if (t.contains('"url"') || t.contains('"service"')) return false; if (t.startsWith('http://') || t.startsWith('https://')) return false; if (t.contains('mega.nz') || t.contains('drive.google.com') || t.contains('dropbox.com') || t.contains('pixeldrain.com') || t.contains('catbox.moe') || t.contains('mediafire.com')) { return false; } return true; }).toList(growable: false); } Post copyWith({ String? id, String? providerId, String? providerName, String? previewUrl, String? sampleUrl, String? fileUrl, List? tags, String? rating, int? width, int? height, String? source, DateTime? createdAt, String? fileType, int? score, Map>? tagGroups, }) { return Post( id: id ?? this.id, providerId: providerId ?? this.providerId, providerName: providerName ?? this.providerName, previewUrl: previewUrl ?? this.previewUrl, sampleUrl: sampleUrl ?? this.sampleUrl, fileUrl: fileUrl ?? this.fileUrl, tags: tags ?? this.tags, rating: rating ?? this.rating, width: width ?? this.width, height: height ?? this.height, source: source ?? this.source, createdAt: createdAt ?? this.createdAt, fileType: fileType ?? this.fileType, score: score ?? this.score, tagGroups: tagGroups ?? this.tagGroups, ); } Map toJson() => { 'id': id, 'providerId': providerId, 'providerName': providerName, 'previewUrl': previewUrl, 'sampleUrl': sampleUrl, 'fileUrl': fileUrl, 'tags': tags, 'rating': rating, 'width': width, 'height': height, 'source': source, 'createdAt': createdAt.toIso8601String(), 'fileType': fileType, 'score': score, 'tagGroups': tagGroups, }; factory Post.fromJson(Map json) => Post( id: json['id'].toString(), providerId: json['providerId'] as String, providerName: json['providerName'] as String, previewUrl: (json['previewUrl'] as String?) ?? '', sampleUrl: (json['sampleUrl'] as String?) ?? '', fileUrl: (json['fileUrl'] as String?) ?? '', tags: List.from((json['tags'] as List?) ?? const []), rating: (json['rating'] as String?) ?? 'unknown', width: (json['width'] as num?)?.toInt() ?? 0, height: (json['height'] as num?)?.toInt() ?? 0, source: json['source'] as String?, createdAt: DateTime.tryParse((json['createdAt'] as String?) ?? '') ?? DateTime.fromMillisecondsSinceEpoch(0), fileType: (json['fileType'] as String?) ?? 'unknown', score: (json['score'] as num?)?.toInt() ?? 0, tagGroups: (json['tagGroups'] as Map?)?.map( (key, value) => MapEntry( key.toString(), List.from((value as List?) ?? const []), ), ) ?? const {}, ); } class PostNavigationContext { const PostNavigationContext({ required this.currentPost, required this.posts, }); final Post currentPost; final List posts; }