Commit 2c62c397 by Rafa Castillo Passols

Versión medianamente funcional del proyecto. Los enlaces ya funcionan pero hay…

Versión medianamente funcional del proyecto. Los enlaces ya funcionan pero hay un bug. Falta convertir de video a imagen
parents d2d62b36 a138b91c
import 'dart:io'; import 'dart:io';
import 'package:ffmpeg_kit_flutter_new/return_code.dart';
import 'package:prueba_multimedia/modelo/conversor.dart'; import 'package:prueba_multimedia/modelo/conversor.dart';
import 'convertible.dart'; import 'convertible.dart';
...@@ -22,4 +23,9 @@ class Archivo extends Convertible { ...@@ -22,4 +23,9 @@ class Archivo extends Convertible {
{ {
metadatos = Conversor.getMetadatos(this); metadatos = Conversor.getMetadatos(this);
} }
@override
Future<ReturnCode?> convertir(String pathSalida) async {
return Conversor.convertir(this, pathSalida);
}
} }
import 'dart:io'; import 'dart:io';
import 'package:ffmpeg_kit_flutter_new/return_code.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:prueba_multimedia/modelo/archivo.dart'; import 'package:prueba_multimedia/modelo/archivo.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
...@@ -199,4 +200,9 @@ class InfoFormato extends Convertible { ...@@ -199,4 +200,9 @@ class InfoFormato extends Convertible {
@override @override
int get hashCode => Object.hash(formatoOriginal, formatoDestino, calidadSalida, _carpeta, seleccionado, subCarpeta); int get hashCode => Object.hash(formatoOriginal, formatoDestino, calidadSalida, _carpeta, seleccionado, subCarpeta);
@override
Future<ReturnCode?> convertir(String _) {
throw UnimplementedError("Esta función no debería de llamarse nunca");
}
} }
\ No newline at end of file
import 'elemento_seleccionable.dart'; import 'elemento_seleccionable.dart';
import 'formato.dart'; import 'formato.dart';
import 'package:ffmpeg_kit_flutter_new/return_code.dart';
/// Clase que representa genéricamente cualquier elemento que se pueda convertir abstract class Convertible extends ElementoSeleccionable{
class Convertible extends ElementoSeleccionable {
/// Formato original del elemento /// Formato original del elemento
final Formato _formatoOriginal; final Formato _formatoOriginal;
/// Formato del fichero de salida /// Formato del fichero de salida
...@@ -10,10 +10,11 @@ class Convertible extends ElementoSeleccionable { ...@@ -10,10 +10,11 @@ class Convertible extends ElementoSeleccionable {
/// Calidad del fichero de salida /// Calidad del fichero de salida
Calidad? calidadSalida; Calidad? calidadSalida;
/// Devuelve el formato original de este elemento
Formato get formatoOriginal => _formatoOriginal; Formato get formatoOriginal => _formatoOriginal;
Convertible({required super.id, required super.nombre, required super.icon, Convertible({required super.id, required super.nombre, required super.icon,
required Formato formatoOriginal}): required Formato formatoOriginal}):
_formatoOriginal = formatoOriginal; _formatoOriginal = formatoOriginal;
Future<ReturnCode?> convertir(String pathSalida);
} }
\ No newline at end of file
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:ffmpeg_kit_flutter_new/return_code.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'convertible.dart'; import 'package:path_provider/path_provider.dart';
import 'formato.dart'; import 'package:uuid/uuid.dart';
import 'package:prueba_multimedia/modelo/modelo.dart';
// TODO: QUE PASA SI NO RECONOCEMOS EL FORMATO? // TODO: QUE PASA SI NO RECONOCEMOS EL FORMATO?
class Enlace extends Convertible{ class Enlace extends Convertible {
final String _direccion; final String _url;
final RedSocial _redSocial; late final Future<File> _file;
List<String> metadatos = []; bool _descargado = false;
String? _nombreArchivo;
String get url => _url;
String? get nombreArchivo => _nombreArchivo;
bool get descargado => _descargado;
Enlace({required String super.id, required String direccion}): Enlace({required String super.id, required String direccion}):
_direccion = direccion, _url = direccion, super(
_redSocial = RedSocial.FACEBOOK, nombre: direccion.length < 33
super(nombre: direccion.split('/').last, ? direccion
// TODO: POR AHORA USAMOS (!) PERO HAY QUE TENERLO EN CUENTA : "${direccion.substring(0, 33)}...",
formatoOriginal: Formato.fromExtension('png')!, formatoOriginal: Formato.fromExtension('jpg')!,
icon: Icon(Icons.insert_drive_file_outlined)); icon: const Icon(Icons.link)
)
{
_file = _descargar(_url);
}
Future<File> _descargar(String direccion) async {
// Descargamos los datos
final dio = Dio();
final response = await dio.get(_url,
options: Options(
responseType: ResponseType.bytes
)
);
// Miramos el nombre del archivo en la respuesta o lo generamos aleatoriamente
String? nombre = response.headers.map['Content-Disposition']?.first;
if (nombre == null) {
nombre = const Uuid().v1().toString();
} else {
nombre = nombre.split('=').last;
// filename encoding, quitamos los "..."
if (nombre.startsWith('"')) {
nombre = nombre.substring(1, nombre.length-1);
}
// filename* encoding, habría que parsear en UTF8
// TODO: parsear en el caso de filename*
else {
nombre = nombre.substring(7);
}
}
_nombreArchivo = nombre;
// Escribimos los datos en archivo temporal
Directory temp = await getTemporaryDirectory();
final File file = await File("${temp.path}/$nombre.${formatoOriginal.name}").create();
final raf = file.openSync(mode: FileMode.write);
raf.writeFromSync(response.data);
await raf.close();
_descargado = true;
return file;
}
@override
Future<ReturnCode?> convertir(String pathSalida) async {
// Eliminamos el archivo temporal después de la conversión
Archivo archivo = Archivo(id: id, file: await _file);
archivo.formatoDestino = formatoDestino;
final result = Conversor.convertir(archivo, pathSalida);
result.then((_) async => (await _file).delete());
return result;
}
} }
enum RedSocial{ enum RedSocial{
......
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:prueba_multimedia/modelo/enlace.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
import 'carpeta.dart'; import 'carpeta.dart';
import 'elemento_seleccionable.dart'; import 'elemento_seleccionable.dart';
...@@ -64,6 +65,16 @@ class ListaSeleccionables extends ChangeNotifier { ...@@ -64,6 +65,16 @@ class ListaSeleccionables extends ChangeNotifier {
return false; return false;
} }
/// Agrega un enlace (archivo para descargar) a ala lista
bool addEnlace(String url) {
_seleccionables.add( Enlace(
id: const Uuid().v1(),
direccion: url
));
notifyListeners();
return true;
}
/// Reinserta un elemento anteriormente eliminado de la lista /// Reinserta un elemento anteriormente eliminado de la lista
void reinsertar(int index, ElementoSeleccionable element){ void reinsertar(int index, ElementoSeleccionable element){
if(index > _seleccionables.length){ if(index > _seleccionables.length){
......
...@@ -85,30 +85,77 @@ class ActionButton extends StatelessWidget { ...@@ -85,30 +85,77 @@ class ActionButton extends StatelessWidget {
} }
} }
// TODO: Implementar descarga de archivos /// Pide al usuario que introduzca un enlace y descarga el archivo
void enlaceAction() {} void enlaceAction() async {
String? url = await showDialog(context: context, builder: (context) {
final textEditingController = TextEditingController();
return SimpleDialog(
title: const Text("Introducir enlace"),
children: [
SimpleDialogOption(
child: Column(
children: [
SimpleDialogOption(
child: TextField(
controller: textEditingController,
),
),
const SizedBox(height: 16.0,),
SimpleDialogOption(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SimpleDialogOption(
onPressed: () {
Navigator.pop(context, textEditingController.text);
},
child: const Text("Aceptar"),
),
SimpleDialogOption(
onPressed: () { Navigator.pop(context); },
child: const Text("Cancelar"),
),
],
),
)
],
),
)
],
);
});
if (url != null) {
manager.addEnlace(url);
}
}
/// Coloca el resultado de la conversión en la carpeta de salida /// Coloca el resultado de la conversión en la carpeta de salida
void copiarAction() async { void copiarAction() async {
if(await _comprobacionesPreviasConversion(context)) { if(await _comprobacionesPreviasConversion(context)) {
// Averiguamos donde colocar los archivos de salida
String? directorioSalida = providerAjustes.carpetaSalida.isNotEmpty String? directorioSalida = providerAjustes.carpetaSalida.isNotEmpty
? providerAjustes.carpetaSalida ? providerAjustes.carpetaSalida
: await FilePicker.platform.getDirectoryPath(); : await FilePicker.platform.getDirectoryPath();
final resultados = <Future<ReturnCode?>>[];
if(directorioSalida != null){ if(directorioSalida != null){
_actualizadorProgreso(); _actualizadorProgreso();
while(manager.seleccionables.isNotEmpty){ while(manager.seleccionables.isNotEmpty){
ElementoSeleccionable sel = manager.seleccionables.first; ElementoSeleccionable sel = manager.seleccionables.first;
if(sel is Carpeta){ if(sel is Carpeta){
Directory d = await Directory("$directorioSalida/${sel.nombre}").create();
for (var archivo in sel.elementosSeleccionados) { for (var archivo in sel.elementosSeleccionados) {
Directory d = await Directory("$directorioSalida/${sel.nombre}").create(); resultados.add(archivo.convertir(d.path));
Conversor.convertir(archivo, d.path);
} }
} }
else if (sel is Archivo){ else if (sel is Convertible){
Conversor.convertir(sel, directorioSalida); resultados.add(sel.convertir(directorioSalida));
}
if (manager.seleccionables.length == 1) {
for (final result in resultados) {
await result;
}
} }
manager.borraSeleccionable(0); manager.borraSeleccionable(0);
...@@ -118,6 +165,7 @@ class ActionButton extends StatelessWidget { ...@@ -118,6 +165,7 @@ class ActionButton extends StatelessWidget {
} }
/// Igual que copiar pero guarda el resultado en un .zip /// Igual que copiar pero guarda el resultado en un .zip
/// TODO (RAFA): CREAR LOS ARCHIVOS TEMPORALES EN OTRO SITIO
void comprimirAction() async { void comprimirAction() async {
if(await _comprobacionesPreviasConversion(context)) { if(await _comprobacionesPreviasConversion(context)) {
// Averiguamos donde colocar los archivos de salida // Averiguamos donde colocar los archivos de salida
...@@ -143,7 +191,7 @@ class ActionButton extends StatelessWidget { ...@@ -143,7 +191,7 @@ class ActionButton extends StatelessWidget {
Directory d = await Directory("$directorioSalida/${sel.nombre}").create(); Directory d = await Directory("$directorioSalida/${sel.nombre}").create();
final resultsConversion = <Future<ReturnCode?>>[]; final resultsConversion = <Future<ReturnCode?>>[];
for (var archivo in sel.elementosSeleccionados) { for (var archivo in sel.elementosSeleccionados) {
resultsConversion.add(Conversor.convertir(archivo, d.path)); resultsConversion.add(archivo.convertir(d.path));
} }
// Esperamos a la conversión y añadimos a zip // Esperamos a la conversión y añadimos a zip
...@@ -151,14 +199,15 @@ class ActionButton extends StatelessWidget { ...@@ -151,14 +199,15 @@ class ActionButton extends StatelessWidget {
await result; await result;
} }
resultsZip.add( resultsZip.add(
zipFileEncoder.addDirectory(d)..then((_) => d.delete(recursive: true)) // Cuando se añada al zip eliminamos los archivos temporales
zipFileEncoder.addDirectory(d)..then((_) => d.delete(recursive: true))
); );
} }
// Conversion de archivos // Conversion de archivos y enlaces
else if (sel is Archivo){ else if (sel is Convertible){
// Esperamos a la conversión y añadimos a zip sel.convertir(directorioSalida);
await Conversor.convertir(sel, directorioSalida);
File tempFile = await File( File tempFile = await File(
// TODO: Esto da bug con los enlaces por el nombre
"$directorioSalida/${sel.nombre}.${sel.formatoDestino?.name}" "$directorioSalida/${sel.nombre}.${sel.formatoDestino?.name}"
).create(); ).create();
resultsZip.add( resultsZip.add(
...@@ -188,6 +237,7 @@ class ActionButton extends StatelessWidget { ...@@ -188,6 +237,7 @@ class ActionButton extends StatelessWidget {
// ------------------------ UTILIDADES ------------------------ // // ------------------------ UTILIDADES ------------------------ //
/// Actualiza la barra de progreso de la conversión de forma asíncrona /// Actualiza la barra de progreso de la conversión de forma asíncrona
void _actualizadorProgreso() async { void _actualizadorProgreso() async {
......
...@@ -43,6 +43,7 @@ dependencies: ...@@ -43,6 +43,7 @@ dependencies:
ffmpeg_kit_flutter_new: ^1.6.1 ffmpeg_kit_flutter_new: ^1.6.1
shared_preferences: ^2.5.3 shared_preferences: ^2.5.3
archive: ^4.0.7 archive: ^4.0.7
dio: ^5.8.0+1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment