import 'dart:convert'; import 'dart:typed_data'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:file_picker/_internal/file_picker_web.dart'; import 'package:file_picker/file_picker.dart'; import 'package:image_picker/image_picker.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:pluto_grid/pluto_grid.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:path/path.dart' as p; import 'package:cbluna_crm_lu/helpers/globals.dart'; import 'package:cbluna_crm_lu/models/content_manager/ad_by_genre.dart'; import 'package:cbluna_crm_lu/pages/widgets/toasts/msj_toast.dart'; import '../../lib/models/content_manager/all_ads_one_table_model.dart'; import '../../lib/pages/widgets/video_player_caro.dart'; class VideosProvider extends ChangeNotifier { List listStateManager = []; PlutoGridStateManager? stateManager; final controllerBusqueda = TextEditingController(); String parametroBusqueda = ""; TextEditingController modeloController = TextEditingController(); String? videoCoverFile; String? videoName; String? videoUrl; String? videoPath; String fileExtension = ''; String resolucion = "1"; String area = "7"; Uint8List? webVideo; String? categoryImgFileName; Uint8List? categoryImageToUpload; String? categoryName; bool noImageToUpload = true; //------- late final TextEditingController filasController; //------- List> listPagos = []; final controllerCount = TextEditingController(); int countI = 0; int countF = 19; final controllerBusquedaFiltro = TextEditingController(); bool filtroAvanzado = false; bool filtroSimple = false; /////////////////////////////////////////// var tableTop1Gourp = AutoSizeGroup(); var tableTopGroup = AutoSizeGroup(); var tableContentGroup = AutoSizeGroup(); final busquedaVideoController = TextEditingController(); /////////////////////////////////////////// late MsjToast toastMessage; /// /// List adsByGenre = []; List> rows = []; List rows_ = []; List allVideoRows = []; List listaQrsSeleccionados = []; //----------------------------------------------Paginador variables int seccionActual = 1; int totalSecciones = 1; int totalFilas = 0; int from = 0; int hasta = 20; //----------------------------------------------Calendario Programacion FilePickerResult? docProveedor; //----------------------------------------------Video data String? videoCoverFileNameNoModif; List videoCategories = []; List videoCategoriesId = []; List selectedVideoCategories = []; List selectedVideoCategoriesId = []; List videoCategoriesImages = []; String? posterPath; String tituloVideo = ''; String descripcionVideo = ''; String videoOutlink = ''; String videoPatner = ''; Uint8List? videoPoster; int videoPoints = 0; Duration duracionVideo = Duration.zero; int duratinVideoSeconds = 0; List videoPriority = []; String? videoPosterPath; //----------------------------------------------VideoYables bool showFullVideoTable = true; List videos = []; String selectePriority = "baja"; int warningCountTablaVideos = 0; bool showWarningsOnTable = false; int? selectedVideoId; //---------------------------------------------- VideosProvider() { getVideoPrioritiesList(); getCategories(); refreshVideoTablet(); } updateState(AdsByGenre expandir) { expandir.isExpanded = !expandir.isExpanded; notifyListeners(); } getVideoData(Duration? duracion, int? duracionSegundos) async { duracionVideo = duracion ?? Duration.zero; duratinVideoSeconds = duracionSegundos ?? 0; } setVideoTableFullOrCategories(int index) { switch (index) { case 0: showFullVideoTable = true; getAllAdsOnOneTable(); break; case 1: showFullVideoTable = false; getAdsByCategories(); break; default: showFullVideoTable = false; getAdsByCategories(); } notifyListeners(); return; } getAdsByCategories() async { try { final query = supabaseLU.rpc('lu_buscar_videos_por_genero', params: { 'busqueda': busquedaVideoController.text, }); final res = await query.select(); if (res == null) { return; } adsByGenre = (res as List) .map((ad) => AdsByGenre.fromJson(jsonEncode(ad))) .toList(); rows_ = []; rows_.clear(); rows.clear(); for (var genre in adsByGenre) { adsByGenre[adsByGenre.indexOf(genre)].videoCount = genre.videos.length; for (var video in genre.videos) { rows_.add(PlutoRow(cells: { 'video_id': PlutoCell(value: video.videoId.toString()), 'posterPath': PlutoCell( value: video.posterFileName != null ? "${supabase.storage.from('lectores_urb/imagenes/videos/posters').getPublicUrl(video.posterFileName)}?${DateTime.now().millisecondsSinceEpoch}" : ''), 'status': PlutoCell(value: video.status ?? 'neutra'), 'priority': PlutoCell(value: video.priority), 'title': PlutoCell(value: video.title), 'urlAd': PlutoCell(value: video.urlAd ?? ''), 'overview': PlutoCell(value: video.overview), 'points': PlutoCell(value: 1), 'expirationDate': PlutoCell(value: video.expirationDate ?? ''), 'created_at': PlutoCell(value: video.createdAt ?? ''), 'durationVideo': PlutoCell(value: video.duration ?? ''), 'video_url': PlutoCell( value: video.videoUrl != null ? "${supabase.storage.from('lectores_urb/videos/cm_videos').getPublicUrl(video.videoFileName.toString())}?${DateTime.now().millisecondsSinceEpoch}" : ''), 'editar': PlutoCell(value: video.videoId), 'eliminar': PlutoCell(value: video.videoId), 'video_file_name': PlutoCell(value: video.videoFileName.toString()), 'poster_file_name': PlutoCell(value: video.posterFileName.toString()), 'patner': PlutoCell(value: video.partner ?? ''), 'categories': PlutoCell(value: video.categories ?? ''), 'video_status': PlutoCell(value: video.videoStatus), })); } rows.add(rows_); rows_ = []; } showWarningsOnTable = false; notifyListeners(); return; } catch (e) { print('error en getAdsByCategories(): ${e.toString()}'); } notifyListeners(); return; } getAdsByCategoriesWarnings() async { try { final query = supabaseLU.rpc('lu_buscar_videos_por_genero', params: { 'busqueda': busquedaVideoController.text, }); final res = await query.select(); if (res == null) { return; } adsByGenre = (res as List) .map((ad) => AdsByGenre.fromJson(jsonEncode(ad))) .toList(); rows_ = []; rows_.clear(); rows.clear(); for (var genre in adsByGenre) { genre.videoCount = 0; for (var video in genre.videos!) { if (video.categories[0] == [null] || video.categories[0].isEmpty || video.categories[0] == [""] || video.categories[0] == ["null"] || video.urlAd == null || video.urlAd == "" || video.urlAd == "null" || video.overview == "" || video.overview == "null" || video.partner == "" || video.partner == null) { rows_.add(PlutoRow(cells: { 'video_id': PlutoCell(value: video.videoId.toString()), 'posterPath': PlutoCell( value: video.posterFileName != null ? "${supabase.storage.from('lectores_urb/imagenes/videos/posters').getPublicUrl(video.posterFileName)}?${DateTime.now().millisecondsSinceEpoch}" : 'https://placehold.co/150x150'), 'status': PlutoCell(value: video.status ?? 'neutra'), 'priority': PlutoCell(value: video.priority ?? '4'), 'title': PlutoCell(value: video.title), 'urlAd': PlutoCell(value: video.urlAd ?? ''), 'overview': PlutoCell(value: video.overview), 'points': PlutoCell(value: video.points ?? ''), 'expirationDate': PlutoCell(value: video.expirationDate ?? ''), 'created_at': PlutoCell(value: video.createdAt ?? ''), 'durationVideo': PlutoCell(value: video.duration ?? ''), 'video_url': PlutoCell( value: video.videoUrl != null ? "${supabase.storage.from('lectores_urb/videos/cm_videos').getPublicUrl(video.videoFileName.toString())}?${DateTime.now().millisecondsSinceEpoch}" : ''), 'editar': PlutoCell(value: video.videoId), 'eliminar': PlutoCell(value: video.videoId), 'video_file_name': PlutoCell(value: video.videoFileName.toString()), 'poster_file_name': PlutoCell(value: video.posterFileName.toString()), 'patner': PlutoCell(value: video.partner ?? ''), 'categories': PlutoCell(value: video.categories ?? ''), 'video_status': PlutoCell(value: video.videoStatus), })); genre.videoCount += 1; } } rows.add(rows_); rows_ = []; } showWarningsOnTable = true; notifyListeners(); return; } catch (e) { print('error en getAdsCategoriesWarning: ${e.toString()}'); } } getAllAdsOnOneTable([bool warningMode = false]) async { try { final String query = busquedaVideoController.text.trim(); final res = await supabaseLU.rpc('lu_buscar_videos', params: { 'busqueda': query.isEmpty ? null : query, 'warningmode': false, }); if (res == null) { print("--X---Error: respuesta nula al llamar RPC."); return; } try { videos = (res as List) .map((ad) => AllAdsOneTableModel.fromJson(jsonEncode(ad))) .toList(); } catch (e) { print("Error al parsear lista de videos: ${e.toString()}"); } allVideoRows.clear(); // El warning count total lo devuelve cada fila, tomamos el primero warningCountTablaVideos = videos.isNotEmpty ? videos.first.warningCount : 0; for (AllAdsOneTableModel video in videos) { allVideoRows.add(PlutoRow(cells: { 'video_id': PlutoCell(value: video.id), 'poster_path': PlutoCell( value: video.posterFileName != null ? "${supabase.storage.from('lectores_urb/imagenes/videos/posters').getPublicUrl(video.posterFileName)}?${DateTime.now().millisecondsSinceEpoch}" : 'https://placehold.co/150x150'), 'priority': PlutoCell(value: video.priority), 'title': PlutoCell(value: video.title), 'url_ad': PlutoCell(value: video.urlAd ?? ''), 'overview': PlutoCell(value: video.overview), 'points': PlutoCell(value: video.points), 'expiration_date': PlutoCell(value: video.expirationDate), 'created_at': PlutoCell(value: video.createdAt), 'duration_video': PlutoCell(value: video.durationVideo), 'video_url': PlutoCell( value: video.video.isNotEmpty ? "${video.video}?${DateTime.now().millisecondsSinceEpoch}" : '', ), 'editar': PlutoCell(value: video.id), 'eliminar': PlutoCell(value: video.id), 'video_file_name': PlutoCell(value: video.videoFileName ?? ''), 'poster_file_name': PlutoCell(value: video.posterFileName ?? ''), 'partner': PlutoCell(value: video.partner ?? ''), 'categories': PlutoCell(value: video.categories), 'video_status': PlutoCell(value: video.videoStatus), 'qr_codes': PlutoCell( value: video.qrCodes.expand((entry) { try { final details = entry['details']; // Verifica si 'details' está presente y tiene la clave 'qrs' if (details != null && details is Map && details['qrs'] is List) { return (details['qrs'] as List) .map((qrsEntry) => qrsEntry['qrs_concat']) .whereType(); } } catch (e) { // Si ocurre un error al intentar acceder a los datos, lo manejamos de forma silenciosa print('Error al acceder a QR details: $e'); } return []; // Devuelve una lista vacía en caso de error o datos inválidos }).join('\n'), // o ', ' si prefieres en una sola línea ), 'qr_codes_list': PlutoCell( value: video.qrCodes.expand((entry) { try { final details = entry['details']; if (details != null && details is Map && details['qrs'] is List) { return (details['qrs'] as List) .map((qrsEntry) => qrsEntry['qrs_concat']) .whereType(); } } catch (e) { print('Error al acceder a QR details (lista): $e'); } return []; }).toList(), // ← clave aquí ), })); } showWarningsOnTable = false; notifyListeners(); } catch (e) { print('Error en getAllAdsOnOneTable: ${e.toString()}'); notifyListeners(); } } getAllAdsOnOneTableWarnings() async { try { final res = await supabaseLU.from('videos_view').select(); if (res == null) { print("--X---Error: ${res.error}"); return; } videos = (res as List) .map((ad) => AllAdsOneTableModel.fromJson(jsonEncode(ad))) .toList(); allVideoRows.clear(); for (AllAdsOneTableModel video in videos) { if (video.categories[0] == [null] || video.categories[0] == null || video.categories[0] == [] || video.categories[0] == [""] || video.categories[0] == ["null"] || video.urlAd == "" || video.urlAd == null || video.urlAd == "null" || video.overview == "" || video.overview == "null" || video.partner == "" || video.partner == null) { allVideoRows.add(PlutoRow(cells: { 'id': PlutoCell(value: video.id), 'posterPath': PlutoCell( value: video.posterFileName != null ? "${supabase.storage.from('lectores_urb/imagenes/videos/posters').getPublicUrl(video.posterFileName)}?${DateTime.now().millisecondsSinceEpoch}" : 'https://placehold.co/150x150'), 'priority': PlutoCell(value: video.priority ?? '4'), 'title': PlutoCell(value: video.title ?? ''), 'urlAd': PlutoCell(value: video.urlAd.toString() ?? ''), 'overview': PlutoCell(value: video.overview ?? ''), 'points': PlutoCell(value: video.points ?? ''), 'expirationDate': PlutoCell(value: video.expirationDate ?? ''), 'created_at': PlutoCell(value: video.createdAt ?? ''), 'durationVideo': PlutoCell(value: video.durationVideo ?? ''), 'video_url': PlutoCell( value: "${supabase.storage.from('lectores_urb/videos/cm_videos').getPublicUrl(video.videoFileName.toString())}?${DateTime.now().millisecondsSinceEpoch}"), 'editar': PlutoCell(value: video.id), 'eliminar': PlutoCell(value: video.id), 'video_file_name': PlutoCell(value: video.videoFileName ?? ''), 'poster_file_name': PlutoCell(value: video.posterFileName ?? ''), 'patner': PlutoCell(value: video.partner ?? ''), 'categories': PlutoCell(value: video.categories ?? ''), 'video_status': PlutoCell(value: video.videoStatus), })); } } showWarningsOnTable = true; notifyListeners(); return; } catch (e) { print('error en getAllAdsOnOneTableWarnings: ${e.toString()}'); } notifyListeners(); return; } Future> getCategories() async { try { final res = await supabaseLU .from('video_genero') .select('name, id, poster_image_file') .eq('visible', true) .order('name', ascending: true); videoCategories = (res as List) .map((category) => category['name'] as String) .toList(); videoCategoriesId = (res as List) .map((category) => category['id'] as int) .toList(); videoCategoriesImages = (res as List) .map((category) => category['poster_image_file'] as String) .toList(); return videoCategories; } catch (e) { print("ERROR en getVideosVinculados: " + e.toString()); notifyListeners(); return []; } } //usando el id del video cambiar su estado "video_status" a true o false cambiarEstadoVideo(int idVideo, bool estado) async { await supabaseLU .from('videos') .update({'video_status': estado}) .eq('id', idVideo) .select(); } /* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */ Widget? getPosterImage(dynamic image, {double height = 180, BoxFit boxFit = BoxFit.cover}) { if (image == null) { return Image.asset('assets/images/placeholder_no_image.jpg'); } else if (image is Uint8List) { return Image.memory( image, height: height, width: double.infinity, fit: boxFit, ); } else if (image is String) { return Image.network( image, height: height, width: double.infinity, filterQuality: FilterQuality.high, fit: boxFit, ); } else { return Image.asset('assets/images/placeholder_no_image.jpg'); } } Widget? getCategoryImage(dynamic image, {double height = 180, BoxFit boxFit = BoxFit.cover}) { if (image == null) { noImageToUpload = true; return Image.asset('assets/images/placeholder_upload_image.png'); } else if (image is Uint8List) { noImageToUpload = false; return Image.memory( image, height: height, width: double.infinity, fit: boxFit, ); } else if (image is String) { noImageToUpload = false; return Image.network( "${supabase.storage.from('lectores_urb/imagenes/videos/categories').getPublicUrl(image)}?${DateTime.now().millisecondsSinceEpoch}", height: height, width: double.infinity, filterQuality: FilterQuality.high, fit: boxFit, ); } else { noImageToUpload = true; return Image.asset('assets/images/placeholder_upload_image.png'); } } //get video widget VideoScreenNew; Widget? getVideoWidget(dynamic image, {double height = 180, width = 500, BoxFit boxFit = BoxFit.contain}) { if (videoPath == null) { return Image.asset('assets/images/placeholder_no_video.png', width: width, height: height, fit: boxFit); } else { return VideoScreenNew( videoUrl: videoPath, ); } } /////////////CATEGORY/////////////////////// Future selectCategoryImage() async { categoryImgFileName = null; categoryImageToUpload = null; FilePickerResult? picker = await FilePickerWeb.platform .pickFiles(type: FileType.custom, allowedExtensions: ['jpg', 'png']); //get and load pdf if (picker != null) { var now = DateTime.now(); var formatter = DateFormat('yyyyMMddHHmmss'); var timestamp = formatter.format(now); categoryImgFileName = 'category-$timestamp-${picker.files.single.name}'; categoryImageToUpload = picker.files.single.bytes; noImageToUpload = false; } else { categoryImgFileName = null; categoryImageToUpload = null; } notifyListeners(); return; } uploadCategoryImage() async { if (categoryImageToUpload != null && categoryImgFileName != null) { await supabase.storage .from('lectores_urb/imagenes/videos/categories') .uploadBinary( categoryImgFileName!, categoryImageToUpload!, fileOptions: const FileOptions( cacheControl: '3600', upsert: false, ), ); return; } return null; } Future registrarNuevaCategoria() async { await uploadCategoryImage(); final res = await supabaseLU.from('video_genero').insert( { 'name': categoryName, 'poster_image_file': categoryImgFileName, }, ).select(); if (res == null) { print("Error al registrar categoria"); print(res.error!.message); return false; } return true; } /////////////ACTUALIZAR CATEGORIA/////////////////////// Future updateCategory(int idCategory, String? name) async { if (categoryImageToUpload == null) { final res = await supabaseLU .from('video_genero') .update( { 'name': name.toString(), }, ) .eq('id', idCategory) .select(); if (res == null) { print("Error al registrar updateCategory1"); print(res.error!.message); return false; } return true; } //--------------------------------------------------------------- final getFileName = await supabaseLU .from('video_genero') .select('poster_image_file') .eq('id', idCategory) .single(); if (getFileName['poster_image_file'] != null && categoryImageToUpload != null) { await supabase.storage .from('lectores_urb/imagenes/videos/categories') .updateBinary( getFileName['poster_image_file'], categoryImageToUpload!, fileOptions: const FileOptions( cacheControl: '3600', upsert: false, ), ); } else { print("NO EXISTE SUBIR"); await supabase.storage .from('lectores_urb/imagenes/videos/categories') .uploadBinary( categoryImgFileName!, categoryImageToUpload!, fileOptions: const FileOptions( cacheControl: '3600', upsert: false, ), ); await supabaseLU .from('video_genero') .update( { 'poster_image_file': categoryImgFileName, }, ) .eq('id', idCategory) .select(); } //--------------------------------------------------------------- return true; } /////////////BORRAR CATEGORIA/////////////////////// Future deleteCategory(int idCategory, String? filename) async { await supabaseLU .from('video_in_genero') .delete() .eq('genero_id', idCategory) .select(); await supabase.storage .from('lectores_urb') .remove(["imagenes/videos/categories/$filename"]); await supabaseLU .from('video_genero') .delete() .eq('id', idCategory) .select(); categoryImgFileName = null; resetAllvideoData(); getCategories(); refreshVideoTablet(); return true; } //---------------ATUALIZAR VIDEOS------------------ Future updateVideoData( int idVideo, String title, String description, String partner, String outlink, int points, List selectedVideoCategoriesId, { String? videoImage, String? posterImage, String? posterFileName, List? qrList, }) async { /* print("updateVideoData"); print("idVideo: $idVideo"); print("title: $title"); print("description: $description"); print("partner: $partner"); print("outlink: $outlink"); print("points: $points"); print("selectedVideoCategoriesId: $selectedVideoCategoriesId"); print("posterFileName: $posterFileName"); print("posterImage: $posterImage"); print("videoImage: $videoImage"); print("qrList: $qrList"); */ if (qrList != null) { final existingQrs = await getQrsByVideoId(idVideo); // Eliminar los QRs que no están en qrList for (String qr in existingQrs) { if (!qrList.contains(qr)) { await supabaseLU .from('video_in_qr') .delete() .eq('id_qr_fk', qr) .eq('id_video_fk', idVideo) .select(); } } // Agregar nuevos QRs for (String qr in qrList) { if (!existingQrs.contains(qr)) { await supabaseLU.from('video_in_qr').insert({ 'id_qr_fk': qr, 'id_video_fk': idVideo, }).select(); } } } final priorityrecibe = await getPriorityId(selectePriority); final res = await supabaseLU .from('videos') .update( { 'title': title.toString(), 'overview': description.toString(), 'url_ad': outlink.toString(), 'partner': partner.toString(), 'points': 1, 'priority': priorityrecibe, 'video_status': true, }, ) .eq('id', idVideo) .select(); if (res == null) { print("Error al registrar updateVideoTitulo"); print(res.error!.message); resetAllvideoData(); return false; } if (webVideo != null) { final getFileName = await supabaseLU .from('videos') .select('video_file_name') .eq('id', idVideo) .single(); print("getFileName: $getFileName"); if (getFileName['video_file_name'] != null) { await supabase.storage .from('lectores_urb/videos/cm_videos') .updateBinary( getFileName['video_file_name'], webVideo!, fileOptions: const FileOptions( cacheControl: '3600', upsert: false, ), ); webVideo = null; } } if (videoPoster != null) { await supabase.storage .from('lectores_urb/imagenes/videos/posters') .updateBinary(videoCoverFileNameNoModif.toString(), videoPoster!); } await supabaseLU .from('video_in_genero') .delete() .eq('video_id', idVideo) .select(); await registrarCategoriasDelVideoUsandoId( idVideo, selectedVideoCategoriesId); resetAllvideoData(); refreshVideoTablet(); return true; } /////////////VIDEOS/////////////////////// getVideoPrioritiesList() async { final res = await supabaseLU.from('video_priority').select("name"); videoPriority = (res as List) .map((priority) => priority['name'] as String) .toList(); return videoPriority; } Future uploadPosterImage() async { if (videoPoster != null && videoCoverFile != null) { await supabase.storage .from('lectores_urb/imagenes/videos/posters') .uploadBinary( '$videoName-$videoCoverFile', videoPoster!, fileOptions: const FileOptions( cacheControl: '3600', upsert: false, ), ); final res = await supabase.storage .from('lectores_urb/imagenes/videos/posters') .getPublicUrl('$videoName-$videoCoverFile'); posterPath = res; return videoCoverFile; } return null; } Future selectPosterImage() async { videoCoverFile = null; videoPoster = null; FilePickerResult? picker = await FilePickerWeb.platform .pickFiles(type: FileType.custom, allowedExtensions: ['jpg', 'png']); //get and load pdf if (picker != null) { var now = DateTime.now(); var formatter = DateFormat('yyyyMMddHHmmss'); var timestamp = formatter.format(now); videoCoverFile = 'poster-$timestamp-${picker.files.single.name}'; videoPoster = picker.files.single.bytes; } else { videoCoverFile = null; videoPoster = null; } notifyListeners(); return; } Future selectVideo() async { videoName = ""; final ImagePicker picker = ImagePicker(); final XFile? pickedVideo = await picker.pickVideo( source: ImageSource.gallery, ); if (pickedVideo == null) return false; fileExtension = p.extension(pickedVideo.name); videoPath = pickedVideo.path; videoName = pickedVideo.name; videoName = videoName!.replaceAll(fileExtension, ""); webVideo = await pickedVideo.readAsBytes(); return true; } Future uploadVideo(List categoryID, {List? qrList}) async { try { if (webVideo != null && videoName != null) { final files = await supabase.storage .from('lectores_urb') .list(path: 'videos/cm_videos'); // Verificar si el archivo ya existe final fileExists = files.any((file) => file.name == '$videoName$fileExtension'); if (fileExists) { toastMessage = MsjToast( message: 'video already exists', color: Color.fromARGB(255, 236, 187, 50), ); return false; } if (videoCoverFile == null) { toastMessage = MsjToast( message: 'No poster selected, upload an image to continue', color: Color.fromARGB(255, 236, 187, 50), ); return false; } //check if videoCoverFile ya existe en storage final filesPoster = await supabase.storage .from('lectores_urb') .list(path: 'imagenes/videos/posters'); // Verificar si el archivo ya existe final fileExistsPoster = filesPoster .any((file) => file.name == '$videoName-$videoCoverFile'); if (fileExistsPoster) { toastMessage = MsjToast( message: 'poster file name already exists, (change the name)', color: Color.fromARGB(255, 236, 187, 50), ); return false; } final storageResponse = await supabase.storage .from('lectores_urb/videos/cm_videos') .uploadBinary( '$videoName$fileExtension', webVideo!, fileOptions: const FileOptions( cacheControl: '3600', upsert: false, ), ); if (storageResponse == null) return false; dynamic res = supabase.storage .from('lectores_urb/videos/cm_videos') .getPublicUrl('$videoName$fileExtension'); videoUrl = res; if (await registrarVideo2(categoryID, qrList: qrList)) { return true; } else { return false; } } return false; } catch (e) { print("Error en uploadVideo: $e"); return false; } } //get priority id using name Future getPriorityId(String priorityName) async { final res = await supabaseLU .from('video_priority') .select("id") .eq("name", priorityName) .limit(1); if (res == null) { print("Error al obtener id de prioridad"); print(res.error!.message); return 0; } return res[0]['id']; } Future registrarCategoriasDelVideoUsandoId( int idVideo, List listaIdsCategorias) async { try { await supabaseLU.rpc('lu_añadir_generos_al_video', params: { 'lista_id_generos': listaIdsCategorias, 'videoid': idVideo }).select(); return true; } catch (e) { print("Error al registrar Categorias del video"); print(e.toString()); return false; } } Future obtenerIdVideoUsandoNombreArchivoStorage( String videoPath) async { //obtiene el id del video recien registrado usando su archivo cargado final res = await supabaseLU .from('videos') .select("id") .eq("video_file_name", videoPath) .limit(1); if (res == null) { print("Error al al obtener id del video"); print(res.error!.message); return false; } await registrarCategoriasDelVideoUsandoId( res[0]['id'], selectedVideoCategoriesId); return true; } Future deleteVideo2( int idVideo, String videoFileName, String posterFileName) async { try { // Llamada al RPC para eliminar el video y obtener los nombres de archivos final res = await supabaseLU .rpc('lu_borrar_video', params: {'id_video': idVideo}); // Validar la respuesta if (res == null || res.isEmpty) { print("El video no pudo ser eliminado o no existe."); refreshVideoTablet(); return false; } await supabaseLU.from('videos').delete().eq('id', idVideo).select(); await eliminarVideo(videoFileName, posterFileName); refreshVideoTablet(); return true; } catch (e) { print("Error al eliminar el video: ${e.toString()}"); refreshVideoTablet(); return false; } } Future eliminarVideo( String videoFileName, String posterFileName) async { await supabase.storage .from('lectores_urb') .remove(['videos/cm_videos/$videoFileName']); await supabase.storage .from('lectores_urb') .remove(['imagenes/videos/posters/$posterFileName']); return true; } Future>> getGroupedQRCodesByCustomer() async { try { // Llamada al RPC en Supabase final res = await supabaseLU.rpc('get_qr_codes_grouped_by_customer'); // Validar el resultado if (res == null || res.isEmpty) { print("No se encontraron datos agrupados."); return []; } // Convertir los resultados en una lista de Map return List>.from(res).map((group) { return { 'row_number': group['row_number'], 'customer_fk': group['customer_fk'], 'customer_name': group['customer_name'], 'details': group['details'], // JSONB con los detalles }; }).toList(); } catch (e) { print("Error en getGroupedQRCodesByCustomer: ${e.toString()}"); return []; // Retornar una lista vacía en caso de error } } Future> getQrsByVideoId(int videoId) async { try { final response = await supabaseLU .from('video_in_qr') .select('id_qr_fk') .eq('id_video_fk', videoId); if (response == null) { print('Error al obtener getQrsByCouponId()'); return []; } return List.from( (response as List).map((item) => item['id_qr_fk']), ); } catch (e) { print("Error en getQrsByVideoId: ${e.toString()}"); return []; // Retornar una lista vacía en caso de error } } void resetAllvideoData() { videoPoster = null; videoCoverFile = null; videoPath = null; videoName = null; selectedVideoCategoriesId = []; selectedVideoCategories = []; categoryImgFileName = null; categoryImageToUpload = null; noImageToUpload = true; selectePriority = "baja"; videoOutlink = ""; videoPoints = 0; descripcionVideo = ""; videoPatner = ""; videoUrl = null; webVideo = null; posterPath = null; videoPosterPath = null; tituloVideo = ""; return; } void clearPosterImage() { videoPoster = null; videoCoverFile = null; videoPosterPath = null; notifyListeners(); return; } void clearControllers() { modeloController.clear(); notifyListeners(); } void updatestateManager() { print(videoName); notifyListeners(); } void clearVideo() { videoPath = null; webVideo = null; notifyListeners(); } void refreshVideoTablet() { print(showWarningsOnTable); if (showWarningsOnTable && warningCountTablaVideos > 0) { showFullVideoTable == true ? getAllAdsOnOneTableWarnings() : getAdsByCategoriesWarnings(); } else { showFullVideoTable == true ? getAllAdsOnOneTable() : getAdsByCategories(); } } Future registrarVideo2(List categoryIds, {List? qrList}) async { await uploadPosterImage(); final priorityrecibe = await getPriorityId(selectePriority); final res = await supabaseLU.rpc( 'lu_registrar_videos_master', params: { 'p_title': tituloVideo, 'p_overview': descripcionVideo, 'p_video_url': videoUrl, 'p_url_ad': videoOutlink, 'p_poster_path': posterPath ?? "https://u-supabase.virtalus.cbluna-dev.com/storage/v1/object/public/assets/placeholder_no_image.jpg", 'p_poster_file_name': '$videoName-$videoCoverFile', 'p_video_file_name': '$videoName$fileExtension', 'p_duration_video': duratinVideoSeconds, 'p_partner': videoPatner, 'p_priority': priorityrecibe, 'p_qr_list': qrList, 'p_category_ids': categoryIds }, ).select(); if (res == null) { print("Error al registrar video"); print(res.error!.message); return false; } if (await obtenerIdVideoUsandoNombreArchivoStorage( '$videoName$fileExtension')) { videoName = ""; descripcionVideo = ""; refreshVideoTablet(); return true; } else { videoName = ""; descripcionVideo = ""; refreshVideoTablet(); return false; } } }