base creada
This commit is contained in:
690
lib/providers/videos_provider.dart
Normal file
690
lib/providers/videos_provider.dart
Normal file
@@ -0,0 +1,690 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:pluto_grid/pluto_grid.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:nethive_neo/helpers/globals.dart';
|
||||
import 'package:nethive_neo/models/media/media_models.dart';
|
||||
|
||||
class VideosProvider extends ChangeNotifier {
|
||||
// ========== ORGANIZATION CONSTANT ==========
|
||||
static const int organizationId = 17;
|
||||
|
||||
// ========== STATE MANAGEMENT ==========
|
||||
PlutoGridStateManager? stateManager;
|
||||
List<PlutoRow> videosRows = [];
|
||||
|
||||
// ========== DATA LISTS ==========
|
||||
List<MediaFileModel> mediaFiles = [];
|
||||
List<MediaCategoryModel> categories = [];
|
||||
List<MediaWithPosterModel> mediaWithPosters = [];
|
||||
|
||||
// ========== CONTROLLERS ==========
|
||||
final busquedaVideoController = TextEditingController();
|
||||
final tituloController = TextEditingController();
|
||||
final descripcionController = TextEditingController();
|
||||
|
||||
// ========== VIDEO/IMAGE UPLOAD STATE ==========
|
||||
String? videoName;
|
||||
String? videoUrl;
|
||||
String? videoStoragePath;
|
||||
String videoFileExtension = '';
|
||||
Uint8List? webVideoBytes;
|
||||
|
||||
String? posterName;
|
||||
String? posterUrl;
|
||||
String? posterStoragePath;
|
||||
String posterFileExtension = '';
|
||||
Uint8List? webPosterBytes;
|
||||
|
||||
// ========== LOADING STATE ==========
|
||||
bool isLoading = false;
|
||||
String? errorMessage;
|
||||
|
||||
// ========== CONSTRUCTOR ==========
|
||||
VideosProvider() {
|
||||
loadMediaFiles();
|
||||
loadCategories();
|
||||
}
|
||||
|
||||
// ========== LOAD METHODS ==========
|
||||
|
||||
/// Load all media files with organization filter
|
||||
Future<void> loadMediaFiles() async {
|
||||
try {
|
||||
isLoading = true;
|
||||
errorMessage = null;
|
||||
notifyListeners();
|
||||
|
||||
final response = await supabaseML
|
||||
.from('media_files')
|
||||
.select()
|
||||
.eq('organization_fk', organizationId)
|
||||
.order('created_at_timestamp', ascending: false);
|
||||
|
||||
mediaFiles = (response as List<dynamic>)
|
||||
.map((item) => MediaFileModel.fromMap(item))
|
||||
.toList();
|
||||
|
||||
await _buildPlutoRows();
|
||||
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
errorMessage = 'Error cargando videos: $e';
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
print('Error en loadMediaFiles: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Load media files with posters using view
|
||||
Future<void> loadMediaWithPosters() async {
|
||||
try {
|
||||
isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
final response = await supabaseML
|
||||
.from('vw_media_files_with_posters')
|
||||
.select()
|
||||
.eq('organization_fk', organizationId)
|
||||
.order('media_created_at', ascending: false);
|
||||
|
||||
mediaWithPosters = (response as List<dynamic>)
|
||||
.map((item) => MediaWithPosterModel.fromMap(item))
|
||||
.toList();
|
||||
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
errorMessage = 'Error cargando videos con posters: $e';
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
print('Error en loadMediaWithPosters: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Load all categories
|
||||
Future<void> loadCategories() async {
|
||||
try {
|
||||
final response = await supabaseML
|
||||
.from('media_categories')
|
||||
.select()
|
||||
.order('category_name');
|
||||
|
||||
categories = (response as List<dynamic>)
|
||||
.map((item) => MediaCategoryModel.fromMap(item))
|
||||
.toList();
|
||||
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
print('Error en loadCategories: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Build PlutoGrid rows from media files
|
||||
Future<void> _buildPlutoRows() async {
|
||||
videosRows.clear();
|
||||
|
||||
for (var media in mediaFiles) {
|
||||
videosRows.add(
|
||||
PlutoRow(
|
||||
cells: {
|
||||
'id': PlutoCell(value: media.mediaFileId),
|
||||
'thumbnail':
|
||||
PlutoCell(value: media.fileUrl), // Para mostrar thumbnail
|
||||
'title': PlutoCell(value: media.title ?? media.fileName),
|
||||
'description': PlutoCell(value: media.fileDescription ?? ''),
|
||||
'category':
|
||||
PlutoCell(value: _getCategoryName(media.mediaCategoryFk)),
|
||||
'reproducciones': PlutoCell(value: media.reproducciones),
|
||||
'duration': PlutoCell(value: media.seconds ?? 0),
|
||||
'size': PlutoCell(value: _formatFileSize(media.fileSizeBytes)),
|
||||
'created_at': PlutoCell(value: media.createdAt),
|
||||
'actions': PlutoCell(value: media.mediaFileId),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get category name by ID
|
||||
String _getCategoryName(int? categoryId) {
|
||||
if (categoryId == null) return 'Sin categoría';
|
||||
try {
|
||||
return categories
|
||||
.firstWhere((cat) => cat.mediaCategoriesId == categoryId)
|
||||
.categoryName;
|
||||
} catch (e) {
|
||||
return 'Sin categoría';
|
||||
}
|
||||
}
|
||||
|
||||
/// Format file size to human readable
|
||||
String _formatFileSize(int? bytes) {
|
||||
if (bytes == null) return '-';
|
||||
if (bytes < 1024) return '$bytes B';
|
||||
if (bytes < 1048576) return '${(bytes / 1024).toStringAsFixed(1)} KB';
|
||||
if (bytes < 1073741824) return '${(bytes / 1048576).toStringAsFixed(1)} MB';
|
||||
return '${(bytes / 1073741824).toStringAsFixed(1)} GB';
|
||||
}
|
||||
|
||||
// ========== VIDEO UPLOAD ==========
|
||||
|
||||
/// Select video file from device
|
||||
Future<bool> selectVideo() async {
|
||||
try {
|
||||
final ImagePicker picker = ImagePicker();
|
||||
final XFile? pickedVideo = await picker.pickVideo(
|
||||
source: ImageSource.gallery,
|
||||
);
|
||||
|
||||
if (pickedVideo == null) return false;
|
||||
|
||||
videoName = pickedVideo.name;
|
||||
videoFileExtension = p.extension(pickedVideo.name);
|
||||
webVideoBytes = await pickedVideo.readAsBytes();
|
||||
|
||||
// Remove extension from name for title
|
||||
final nameWithoutExt = videoName!.replaceAll(videoFileExtension, '');
|
||||
tituloController.text = nameWithoutExt;
|
||||
|
||||
notifyListeners();
|
||||
return true;
|
||||
} catch (e) {
|
||||
errorMessage = 'Error seleccionando video: $e';
|
||||
notifyListeners();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Select poster/thumbnail image
|
||||
Future<bool> selectPoster() async {
|
||||
try {
|
||||
final ImagePicker picker = ImagePicker();
|
||||
final XFile? pickedImage = await picker.pickImage(
|
||||
source: ImageSource.gallery,
|
||||
);
|
||||
|
||||
if (pickedImage == null) return false;
|
||||
|
||||
posterName = pickedImage.name;
|
||||
posterFileExtension = p.extension(pickedImage.name);
|
||||
webPosterBytes = await pickedImage.readAsBytes();
|
||||
|
||||
notifyListeners();
|
||||
return true;
|
||||
} catch (e) {
|
||||
errorMessage = 'Error seleccionando poster: $e';
|
||||
notifyListeners();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Upload video to Supabase Storage and create record
|
||||
Future<bool> uploadVideo({
|
||||
required String title,
|
||||
String? description,
|
||||
int? categoryId,
|
||||
int? durationSeconds,
|
||||
}) async {
|
||||
if (webVideoBytes == null || videoName == null) {
|
||||
errorMessage = 'No hay video seleccionado';
|
||||
notifyListeners();
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
// 1. Upload video to storage
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch;
|
||||
final fileName = '${timestamp}_$videoName';
|
||||
videoStoragePath = 'videos/$fileName';
|
||||
|
||||
await supabaseML.storage.from('energymedia').uploadBinary(
|
||||
videoStoragePath!,
|
||||
webVideoBytes!,
|
||||
fileOptions: const FileOptions(
|
||||
cacheControl: '3600',
|
||||
upsert: false,
|
||||
),
|
||||
);
|
||||
|
||||
// 2. Get public URL
|
||||
videoUrl = supabaseML.storage
|
||||
.from('energymedia')
|
||||
.getPublicUrl(videoStoragePath!);
|
||||
|
||||
// 3. Upload poster if exists
|
||||
int? posterFileId;
|
||||
if (webPosterBytes != null && posterName != null) {
|
||||
posterFileId = await _uploadPoster();
|
||||
}
|
||||
|
||||
// 4. Create media_files record
|
||||
final metadataJson = {
|
||||
'uploaded_at': DateTime.now().toIso8601String(),
|
||||
'reproducciones': 0,
|
||||
'original_file_name': videoName,
|
||||
'duration_seconds': durationSeconds,
|
||||
};
|
||||
|
||||
final response = await supabaseML.from('media_files').insert({
|
||||
'file_name': fileName,
|
||||
'title': title,
|
||||
'file_description': description,
|
||||
'file_type': 'video',
|
||||
'mime_type': _getMimeType(videoFileExtension),
|
||||
'file_extension': videoFileExtension,
|
||||
'file_size_bytes': webVideoBytes!.length,
|
||||
'file_url': videoUrl,
|
||||
'storage_path': videoStoragePath,
|
||||
'organization_fk': organizationId,
|
||||
'media_category_fk': categoryId,
|
||||
'metadata_json': metadataJson,
|
||||
'seconds': durationSeconds,
|
||||
'is_public_file': true,
|
||||
'uploaded_by_user_id': currentUser?.id,
|
||||
}).select();
|
||||
|
||||
// 5. Create poster relationship if exists
|
||||
if (posterFileId != null && response.isNotEmpty) {
|
||||
final mediaFileId = response[0]['media_file_id'];
|
||||
await supabaseML.from('media_posters').insert({
|
||||
'media_file_id': mediaFileId,
|
||||
'poster_file_id': posterFileId,
|
||||
});
|
||||
}
|
||||
|
||||
// Clean up
|
||||
_clearUploadState();
|
||||
|
||||
// Reload data
|
||||
await loadMediaFiles();
|
||||
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
return true;
|
||||
} catch (e) {
|
||||
errorMessage = 'Error subiendo video: $e';
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
print('Error en uploadVideo: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Upload poster image (internal helper)
|
||||
Future<int?> _uploadPoster() async {
|
||||
if (webPosterBytes == null || posterName == null) return null;
|
||||
|
||||
try {
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch;
|
||||
final fileName = '${timestamp}_$posterName';
|
||||
posterStoragePath = 'imagenes/$fileName';
|
||||
|
||||
await supabaseML.storage.from('energymedia').uploadBinary(
|
||||
posterStoragePath!,
|
||||
webPosterBytes!,
|
||||
fileOptions: const FileOptions(
|
||||
cacheControl: '3600',
|
||||
upsert: false,
|
||||
),
|
||||
);
|
||||
|
||||
posterUrl = supabaseML.storage
|
||||
.from('energymedia')
|
||||
.getPublicUrl(posterStoragePath!);
|
||||
|
||||
// Create media_files record for poster
|
||||
final response = await supabaseML.from('media_files').insert({
|
||||
'file_name': fileName,
|
||||
'title': 'Poster',
|
||||
'file_type': 'image',
|
||||
'mime_type': _getMimeType(posterFileExtension),
|
||||
'file_extension': posterFileExtension,
|
||||
'file_size_bytes': webPosterBytes!.length,
|
||||
'file_url': posterUrl,
|
||||
'storage_path': posterStoragePath,
|
||||
'organization_fk': organizationId,
|
||||
'is_public_file': true,
|
||||
'uploaded_by_user_id': currentUser?.id,
|
||||
}).select();
|
||||
|
||||
return response[0]['media_file_id'] as int;
|
||||
} catch (e) {
|
||||
print('Error en _uploadPoster: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get MIME type from file extension
|
||||
String _getMimeType(String extension) {
|
||||
final ext = extension.toLowerCase().replaceAll('.', '');
|
||||
switch (ext) {
|
||||
case 'mp4':
|
||||
return 'video/mp4';
|
||||
case 'webm':
|
||||
return 'video/webm';
|
||||
case 'mov':
|
||||
return 'video/quicktime';
|
||||
case 'avi':
|
||||
return 'video/x-msvideo';
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
return 'image/jpeg';
|
||||
case 'png':
|
||||
return 'image/png';
|
||||
case 'gif':
|
||||
return 'image/gif';
|
||||
default:
|
||||
return 'application/octet-stream';
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear upload state
|
||||
void _clearUploadState() {
|
||||
videoName = null;
|
||||
videoUrl = null;
|
||||
videoStoragePath = null;
|
||||
videoFileExtension = '';
|
||||
webVideoBytes = null;
|
||||
posterName = null;
|
||||
posterUrl = null;
|
||||
posterStoragePath = null;
|
||||
posterFileExtension = '';
|
||||
webPosterBytes = null;
|
||||
tituloController.clear();
|
||||
descripcionController.clear();
|
||||
}
|
||||
|
||||
// ========== UPDATE METHODS ==========
|
||||
|
||||
/// Update video title
|
||||
Future<bool> updateVideoTitle(int mediaFileId, String title) async {
|
||||
try {
|
||||
await supabaseML
|
||||
.from('media_files')
|
||||
.update({'title': title})
|
||||
.eq('media_file_id', mediaFileId)
|
||||
.eq('organization_fk', organizationId);
|
||||
|
||||
await loadMediaFiles();
|
||||
return true;
|
||||
} catch (e) {
|
||||
errorMessage = 'Error actualizando título: $e';
|
||||
notifyListeners();
|
||||
print('Error en updateVideoTitle: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Update video description
|
||||
Future<bool> updateVideoDescription(
|
||||
int mediaFileId, String description) async {
|
||||
try {
|
||||
await supabaseML
|
||||
.from('media_files')
|
||||
.update({'file_description': description})
|
||||
.eq('media_file_id', mediaFileId)
|
||||
.eq('organization_fk', organizationId);
|
||||
|
||||
await loadMediaFiles();
|
||||
return true;
|
||||
} catch (e) {
|
||||
errorMessage = 'Error actualizando descripción: $e';
|
||||
notifyListeners();
|
||||
print('Error en updateVideoDescription: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Update video category
|
||||
Future<bool> updateVideoCategory(int mediaFileId, int? categoryId) async {
|
||||
try {
|
||||
await supabaseML
|
||||
.from('media_files')
|
||||
.update({'media_category_fk': categoryId})
|
||||
.eq('media_file_id', mediaFileId)
|
||||
.eq('organization_fk', organizationId);
|
||||
|
||||
await loadMediaFiles();
|
||||
return true;
|
||||
} catch (e) {
|
||||
errorMessage = 'Error actualizando categoría: $e';
|
||||
notifyListeners();
|
||||
print('Error en updateVideoCategory: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Update video metadata
|
||||
Future<bool> updateVideoMetadata(
|
||||
int mediaFileId,
|
||||
Map<String, dynamic> metadata,
|
||||
) async {
|
||||
try {
|
||||
await supabaseML
|
||||
.from('media_files')
|
||||
.update({'metadata_json': metadata})
|
||||
.eq('media_file_id', mediaFileId)
|
||||
.eq('organization_fk', organizationId);
|
||||
|
||||
await loadMediaFiles();
|
||||
return true;
|
||||
} catch (e) {
|
||||
errorMessage = 'Error actualizando metadata: $e';
|
||||
notifyListeners();
|
||||
print('Error en updateVideoMetadata: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ========== DELETE METHODS ==========
|
||||
|
||||
/// Delete video and its storage files
|
||||
Future<bool> deleteVideo(int mediaFileId) async {
|
||||
try {
|
||||
isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
// Get video info
|
||||
final response = await supabaseML
|
||||
.from('media_files')
|
||||
.select()
|
||||
.eq('media_file_id', mediaFileId)
|
||||
.eq('organization_fk', organizationId)
|
||||
.single();
|
||||
|
||||
final storagePath = response['storage_path'] as String?;
|
||||
|
||||
// Delete from storage if path exists
|
||||
if (storagePath != null) {
|
||||
await supabaseML.storage.from('energymedia').remove([storagePath]);
|
||||
}
|
||||
|
||||
// Delete associated posters
|
||||
final posters = await supabaseML
|
||||
.from('media_posters')
|
||||
.select('poster_file_id')
|
||||
.eq('media_file_id', mediaFileId);
|
||||
|
||||
for (var poster in posters) {
|
||||
await _deletePosterFile(poster['poster_file_id']);
|
||||
}
|
||||
|
||||
// Delete database record (cascade will delete posters relationship)
|
||||
await supabaseML
|
||||
.from('media_files')
|
||||
.delete()
|
||||
.eq('media_file_id', mediaFileId)
|
||||
.eq('organization_fk', organizationId);
|
||||
|
||||
await loadMediaFiles();
|
||||
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
return true;
|
||||
} catch (e) {
|
||||
errorMessage = 'Error eliminando video: $e';
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
print('Error en deleteVideo: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete poster file (internal helper)
|
||||
Future<void> _deletePosterFile(int posterFileId) async {
|
||||
try {
|
||||
final response = await supabaseML
|
||||
.from('media_files')
|
||||
.select('storage_path')
|
||||
.eq('media_file_id', posterFileId)
|
||||
.single();
|
||||
|
||||
final storagePath = response['storage_path'] as String?;
|
||||
|
||||
if (storagePath != null) {
|
||||
await supabaseML.storage.from('energymedia').remove([storagePath]);
|
||||
}
|
||||
|
||||
await supabaseML
|
||||
.from('media_files')
|
||||
.delete()
|
||||
.eq('media_file_id', posterFileId);
|
||||
} catch (e) {
|
||||
print('Error en _deletePosterFile: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// ========== ANALYTICS METHODS ==========
|
||||
|
||||
/// Increment view count
|
||||
Future<bool> incrementReproduccion(int mediaFileId) async {
|
||||
try {
|
||||
// Get current metadata
|
||||
final response = await supabaseML
|
||||
.from('media_files')
|
||||
.select('metadata_json')
|
||||
.eq('media_file_id', mediaFileId)
|
||||
.eq('organization_fk', organizationId)
|
||||
.single();
|
||||
|
||||
final metadata = response['metadata_json'] as Map<String, dynamic>? ?? {};
|
||||
final currentCount = metadata['reproducciones'] ?? 0;
|
||||
|
||||
metadata['reproducciones'] = currentCount + 1;
|
||||
metadata['last_viewed_at'] = DateTime.now().toIso8601String();
|
||||
|
||||
await updateVideoMetadata(mediaFileId, metadata);
|
||||
return true;
|
||||
} catch (e) {
|
||||
print('Error en incrementReproduccion: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get dashboard statistics
|
||||
Future<Map<String, dynamic>> getDashboardStats() async {
|
||||
try {
|
||||
// Total videos
|
||||
final totalVideos = mediaFiles.length;
|
||||
|
||||
// Total reproducciones
|
||||
int totalReproducciones = 0;
|
||||
for (var media in mediaFiles) {
|
||||
totalReproducciones += media.reproducciones;
|
||||
}
|
||||
|
||||
// Most viewed video
|
||||
MediaFileModel? mostViewed;
|
||||
if (mediaFiles.isNotEmpty) {
|
||||
mostViewed = mediaFiles.reduce((curr, next) =>
|
||||
curr.reproducciones > next.reproducciones ? curr : next);
|
||||
}
|
||||
|
||||
// Videos by category
|
||||
Map<String, int> videosByCategory = {};
|
||||
for (var media in mediaFiles) {
|
||||
final categoryName = _getCategoryName(media.mediaCategoryFk);
|
||||
videosByCategory[categoryName] =
|
||||
(videosByCategory[categoryName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
// Most viewed category
|
||||
String? mostViewedCategory;
|
||||
if (videosByCategory.isNotEmpty) {
|
||||
mostViewedCategory = videosByCategory.entries
|
||||
.reduce((a, b) => a.value > b.value ? a : b)
|
||||
.key;
|
||||
}
|
||||
|
||||
return {
|
||||
'total_videos': totalVideos,
|
||||
'total_reproducciones': totalReproducciones,
|
||||
'most_viewed_video': mostViewed?.toMap(),
|
||||
'videos_by_category': videosByCategory,
|
||||
'most_viewed_category': mostViewedCategory,
|
||||
'total_categories': categories.length,
|
||||
};
|
||||
} catch (e) {
|
||||
print('Error en getDashboardStats: $e');
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// ========== SEARCH & FILTER ==========
|
||||
|
||||
/// Search videos by title or description
|
||||
void searchVideos(String query) {
|
||||
if (query.isEmpty) {
|
||||
_buildPlutoRows();
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
|
||||
videosRows.clear();
|
||||
final filteredMedia = mediaFiles.where((media) {
|
||||
final title = (media.title ?? media.fileName).toLowerCase();
|
||||
final description = (media.fileDescription ?? '').toLowerCase();
|
||||
final searchQuery = query.toLowerCase();
|
||||
return title.contains(searchQuery) || description.contains(searchQuery);
|
||||
}).toList();
|
||||
|
||||
for (var media in filteredMedia) {
|
||||
videosRows.add(
|
||||
PlutoRow(
|
||||
cells: {
|
||||
'id': PlutoCell(value: media.mediaFileId),
|
||||
'thumbnail': PlutoCell(value: media.fileUrl),
|
||||
'title': PlutoCell(value: media.title ?? media.fileName),
|
||||
'description': PlutoCell(value: media.fileDescription ?? ''),
|
||||
'category':
|
||||
PlutoCell(value: _getCategoryName(media.mediaCategoryFk)),
|
||||
'reproducciones': PlutoCell(value: media.reproducciones),
|
||||
'duration': PlutoCell(value: media.seconds ?? 0),
|
||||
'size': PlutoCell(value: _formatFileSize(media.fileSizeBytes)),
|
||||
'created_at': PlutoCell(value: media.createdAt),
|
||||
'actions': PlutoCell(value: media.mediaFileId),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// ========== CLEANUP ==========
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
busquedaVideoController.dispose();
|
||||
tituloController.dispose();
|
||||
descripcionController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user