Comenzando la documentación del código, arreglos menores

parent b213c38b
......@@ -4,9 +4,13 @@ import 'package:prueba_multimedia/modelo/conversor.dart';
import 'convertible.dart';
import 'formato.dart';
/// Clase que representa un archivo del dispositivo
class Archivo extends Convertible {
/// Referencia al archivo físico
final File file;
/// Metadatos del archivo
late final Future<List<Metadato>> metadatos;
/// Fotograma que convertir (solo conversión vídeo a imagen)
int? fotograma;
Archivo({required super.id, required this.file}):
......
......@@ -6,15 +6,24 @@ import 'convertible.dart';
import 'elemento_seleccionable.dart';
import 'formato.dart';
/// Clase que representa una carpeta cuyos elementos pueden ser convertidos
class Carpeta extends ElementoSeleccionable {
/// Referencia al directorio físico
final Directory _directory;
/// Flag para convertir recursivamente
bool _incluirSubcarpetas = false;
/// Lista de información sobre los formatos reconocidos en esta carpeta
final List<InfoFormato> _formatos = <InfoFormato>[];
/// Lista de archivos en la carpeta base
final _elementos = <Archivo>[];
/// Lista de archivos en las subcarpetas
final _elementosSubcarpetas = <Archivo>[];
/// Devuelve la referencia al directorio físico
Directory get directory => _directory;
/// Devuelve una lista inmodificable con los formatos reconocidos en esta carpeta
List<InfoFormato> get formatos => List.unmodifiable(_formatos);
/// Devuelve si el flag de las subcarpetas está activo o no
bool get incluyeSubcarpetas => _incluirSubcarpetas;
Carpeta({required super.id, required Directory directory, bool incluirSubcarpetas = false}):
......@@ -22,9 +31,10 @@ class Carpeta extends ElementoSeleccionable {
_incluirSubcarpetas = incluirSubcarpetas,
super(nombre: directory.path.split('/').last, icon: const Icon(Icons.folder_outlined))
{
// Buscamos en los archivos de la carpeta base y subcarpetas los diferentes formatos reconocibles
final fsEntities = directory.listSync(recursive: false, followLinks: false);
// Guardamos los archivos
// Guardamos los archivos de la carpeta base
for(var file in fsEntities.whereType<File>()) {
if(Formato.fromExtension(file.path.split('.').last) != null){
Archivo archivo = Archivo(id: Uuid().v1(), file: file);
......@@ -42,7 +52,6 @@ class Carpeta extends ElementoSeleccionable {
}
// Guardamos los archivos de las subcarpetas
// Ahora mismo solo consultamos un nivel de subcarpetas y no mas
for(var directory in fsEntities.whereType<Directory>()) {
final files = directory.listSync(recursive: true, followLinks: false)
.whereType<File>();
......@@ -61,7 +70,6 @@ class Carpeta extends ElementoSeleccionable {
}
}
}
}
/// Un getter que da todos los archivos según los filtros elegidos
......@@ -109,6 +117,7 @@ class Carpeta extends ElementoSeleccionable {
return seleccionado;
}
/// Devuelve los formatos actualmente seleccionados
List<InfoFormato> get formatosSeleccionados {
final seleccionados = <InfoFormato>[];
......@@ -123,6 +132,7 @@ class Carpeta extends ElementoSeleccionable {
return seleccionados;
}
/// Devuelve la información relativa a un cierto formato si la hay
InfoFormato? getInfoFormato({required Formato formato}){
for(InfoFormato i in _formatos){
if(i.formatoOriginal == formato){
......@@ -132,6 +142,7 @@ class Carpeta extends ElementoSeleccionable {
return null;
}
/// Fija el formato y calidad de destino de un formato original concreto
void setFormatoDestino(Formato original, Formato? destino, Calidad? calidad){
final formatosAlt = formatos.toList();
for(InfoFormato i in formatosAlt){
......@@ -143,22 +154,28 @@ class Carpeta extends ElementoSeleccionable {
}
/// Reacciona al cambio de inclusión de las subcarpetas
void pressIncluirSubcarpetas(){
_incluirSubcarpetas = !_incluirSubcarpetas;
}
/// Reacciona al cambio de inclusión de un formato concreto
void pressAltSeleccionado(int index){
_formatos[index].seleccionado = !_formatos[index].seleccionado;
}
}
/// Clase que engloba la información relevante a un formato dentro de una carpeta
class InfoFormato extends Convertible {
/// Carpeta que contiene esta información
final Carpeta _carpeta;
/// Flag que indica si este formato está incluido en la conversión
bool seleccionado;
/// Flag que indica si este formato solo se encuentra en subcarpetas
bool subCarpeta;
/// Devuelve la carpeta
Carpeta get carpeta => _carpeta;
InfoFormato({required Formato formato, required Carpeta carpeta,
......@@ -172,11 +189,13 @@ class InfoFormato extends Convertible {
formatoOriginal: formato
);
/// Operador de igualdad
bool operator ==(Object other) =>
other is InfoFormato &&
other.runtimeType == runtimeType &&
other.formatoOriginal == formatoOriginal;
/// Devuelve un hash sobre este objeto
@override
int get hashCode => Object.hash(formatoOriginal, formatoDestino, calidadSalida, _carpeta, seleccionado, subCarpeta);
......
import 'elemento_seleccionable.dart';
import 'formato.dart';
class Convertible extends ElementoSeleccionable{
/// Clase que representa genéricamente cualquier elemento que se pueda convertir
class Convertible extends ElementoSeleccionable {
/// Formato original del elemento
final Formato _formatoOriginal;
/// Formato del fichero de salida
Formato? formatoDestino;
/// Calidad del fichero de salida
Calidad? calidadSalida;
/// Devuelve el formato original de este elemento
Formato get formatoOriginal => _formatoOriginal;
Convertible({required super.id, required super.nombre, required super.icon,
required Formato formatoOriginal}):
_formatoOriginal = formatoOriginal;
void convertir(){
// TODO: <implement>
}
}
\ No newline at end of file
import 'package:flutter/material.dart';
abstract class ElementoSeleccionable{
/// Clase que representa cualquier elemento que se pueda seleccionar para la conversión
abstract class ElementoSeleccionable {
/// Identificador único del elemento
final String _id;
/// Nombre del elemento
final String _nombre;
/// Icono que representa al elemento
final Icon _icono;
/// Devuelve el identificador único del elemento
String get id => _id;
/// Devuelve el nombre del elemento
String get nombre => _nombre;
/// Devuelve el icono que representa al elemento
Icon get icono => _icono;
ElementoSeleccionable({required id,
......
......@@ -15,12 +15,6 @@ class Enlace extends Convertible{
// TODO: POR AHORA USAMOS (!) PERO HAY QUE TENERLO EN CUENTA
formatoOriginal: Formato.fromExtension('png')!,
icon: Icon(Icons.insert_drive_file_outlined));
@override
void convertir() {
// TODO: <implement> Descarga desde internet...
super.convertir();
}
}
enum RedSocial{
......
import 'package:flutter/material.dart';
// TODO: AÑADIR CALIDAD
/// Enumerado que representa a los diferentes formatos que puede leer y
/// a los que puede convertir la aplicación
enum Formato {
png(['png'], [], 'Portable Network Graphics', TipoMultimedia.imagen, Clasificacion.calidad, [],
'ClaseFormato gráfico basado en un algoritmo de compresión sin pérdida para bitmaps no sujeto a patentes. Fue desarrollado en buena parte para solventar las deficiencias del formato GIF y permite almacenar imágenes con una mayor profundidad de contraste y otros datos importantes.'
'Formato gráfico basado en un algoritmo de compresión sin pérdida para bitmaps no sujeto a patentes. Fue desarrollado en buena parte para solventar las deficiencias del formato GIF y permite almacenar imágenes con una mayor profundidad de contraste y otros datos importantes.'
),
jpg(['jpeg','jpg','jpe'], Calidad.values, 'Joint Photographic Experts Group', TipoMultimedia.imagen, Clasificacion.ligero, [],
......@@ -52,14 +53,22 @@ enum Formato {
'Un códec de vídeo libre que está siendo desarrollado por la Fundación Xiph.Org, como parte de su proyecto Ogg. Basado en el códec VP3 donado por On2 Technologies, Xiph.Org lo ha refinado y extendido dándole el mismo alcance futuro para mejoras en el codificador como el que posee el códec de audio Vorbis.'
);
/// Lista de extensiones asociadas al formato
final List<String> listExtensiones;
/// Lista de posibles calidades del formato
final List<Calidad> listCalidades;
/// Nombre completo del formato
final String nombre;
/// Tipo de fichero multimedia
final TipoMultimedia tipoMultimedia;
/// Clasificación del formato
final Clasificacion clasificacion;
/// Metadatos que puede contener el formato
final List<MetadatoInfo> metadatos;
/// Breve descripción del formato
final String descripcion;
/// Devuelve la extensión principal de este formato
String get extension => name;
......@@ -77,7 +86,7 @@ enum Formato {
return toRet;
}
/// Si la extensión no es valida devuelve nulo
/// Devuelve el formato de un archivo. Si la extensión no es válida, devuelve nulo
static Formato? fromExtension(String extension){
// COMPROBAR SI UN ARCHIVO OGG CONTIENE VÍDEO O SOLO AUDIO
// ffprobe -v error -select_streams v:0 -show_entries stream=codec_type -of csv=p=0 [ARCHIVO].ogg
......@@ -95,34 +104,45 @@ enum Formato {
}
}
/// Enumerado de los diferentes tipos de fichero multimedia que puede contener un formato
enum TipoMultimedia {
video("Vídeo", Icon(Icons.movie_creation_outlined)),
audio("Audio", Icon(Icons.music_note_outlined)),
imagen("Imagen", Icon(Icons.image_outlined));
/// Nombre mostrado del tipo de fichero multimedia
final String nombre;
/// Icono que representa al tipo de fichero
final Icon icono;
const TipoMultimedia(this.nombre, this.icono);
}
/// Enumerado de las diferentes clasificaciones que puede tener un formato
enum Clasificacion {
calidad("Calidad", Icons.high_quality_outlined),
ligero("Ligero", Icons.electric_bolt_outlined),
versatil("Versátil", Icons.auto_mode_outlined);
/// Nombre mostrado de la clasificación
final String nombre;
/// Icono que representa a la clasificación
final IconData icono;
const Clasificacion(this.nombre, this.icono);
}
/// Enumerado que representa las diferentes calidades a las que se puede convertir un archivo
enum Calidad {
baja("Baja"), media("Media"), alta("Alta"), muyAlta("Muy Alta");
/// Nombre mostrado de la calidad
final String texto;
const Calidad(this.texto);
}
/// Enumerado de los tipos de metadato soportados
enum MetadatoInfo {
title("Título"),
artist("Artista"),
......@@ -138,17 +158,19 @@ enum MetadatoInfo {
rating("Valoración", true),
encoder("Encoder", false);
final String _nombreMostrado;
final bool _esNumerico;
String get nombreMostrado => _nombreMostrado;
bool get numerico => _esNumerico;
/// Nombre mostrado del metadato
final String nombreMostrado;
/// Flag de si el metadato es únicamente numérico o no
final bool esNumerico;
const MetadatoInfo(this._nombreMostrado, [this._esNumerico = false]);
const MetadatoInfo(this.nombreMostrado, [this.esNumerico = false]);
}
/// Clase que representa un metadato que puede tener un archivo
class Metadato {
/// Tipo de metadato
final MetadatoInfo info;
/// Valor actual de este metadato
String valor;
Metadato({required this.info, required this.valor});
......
......@@ -6,25 +6,35 @@ import 'elemento_seleccionable.dart';
import 'archivo.dart';
import 'formato.dart';
/// Clase que contiene la lista de seleccionables elegidos por el usuario
class ListaSeleccionables extends ChangeNotifier {
/// Lista de seleccionables
final _seleccionables = <ElementoSeleccionable>[];
/// Flag que indica si actualmente se está convirtiendo
bool _convirtiendo = false;
/// Número del 0 al 100 que indica el progreso en la conversión
int _progress = 0;
/// Devuelve una copia inmutable de la lista de seleccionables
List<ElementoSeleccionable> get seleccionables => List.unmodifiable(_seleccionables);
/// Devuelve si se está convirtiendo
bool get convirtiendo => _convirtiendo;
/// Devuelve el progreso en la conversión
int get progress => _progress;
/// Borra un elemento de la lista
void borraSeleccionable(int indice) {
_seleccionables.removeAt(indice);
notifyListeners();
}
/// Actualiza un elemento de la lista
void actualizaSeleccionable(int indice, ElementoSeleccionable elemento){
_seleccionables[indice] = elemento;
notifyListeners();
}
/// Agrega un archivo a la lista
bool addArchivo(File file) {
if(Formato.fromExtension(file.path.split('.').last) != null){
_seleccionables.add(Archivo(id: const Uuid().v1(),
......@@ -35,6 +45,7 @@ class ListaSeleccionables extends ChangeNotifier {
return false;
}
/// Agrega una carpeta a la lista
bool addCarpeta(Directory directory, bool incluirSubcarpetasPorDefecto){
final newCarpeta = Carpeta(
id: const Uuid().v1(),
......@@ -53,6 +64,7 @@ class ListaSeleccionables extends ChangeNotifier {
return false;
}
/// Reinserta un elemento anteriormente eliminado de la lista
void reinsertar(int index, ElementoSeleccionable element){
if(index > _seleccionables.length){
_seleccionables.add(element);
......@@ -63,17 +75,20 @@ class ListaSeleccionables extends ChangeNotifier {
notifyListeners();
}
/// Inicia la conversión
void iniciarConversion() {
_convirtiendo = true;
notifyListeners();
}
/// Finaliza la conversión
void finalizarConversion() {
_convirtiendo = false;
_progress = 0;
notifyListeners();
}
/// Actualiza el progreso de conversión
void actualizarProgreso(int initialSize) {
_progress = ((1 - (_seleccionables.length / initialSize))*100).floor();
notifyListeners();
......
import 'formato.dart';
/// Enumerado que representa los diferentes perfiles a los cuales puede convertir el usuario
enum Perfil {
IMAGEN_ALTA_CALIDAD('Alta calidad', Formato.png, Formato.wav, Formato.mkv, Calidad.muyAlta),
IMAGEN_COMPARTIR('Para compartir', Formato.jpg, Formato.mp3, Formato.mp4, Calidad.baja);
altaCalidad('Alta calidad', Formato.png, Formato.wav, Formato.mkv, Calidad.muyAlta),
compartir('Para compartir', Formato.jpg, Formato.mp3, Formato.mp4, Calidad.baja),
whatsapp('Uso libre', Formato.webp, Formato.flac, Formato.mkv, Calidad.media);
/// Nombre mostrado del perfil
final String nombre;
/// Extensión de imagen
final Formato? extensionImagen;
final Formato? extensionMusica;
/// Extensión de audio
final Formato? extensionAudio;
/// Extensión de vídeo
final Formato? extensionVideo;
/// Calidad elegida
final Calidad calidad;
const Perfil(this.nombre, this.extensionImagen, this.extensionMusica,
const Perfil(this.nombre, this.extensionImagen, this.extensionAudio,
this.extensionVideo, this.calidad);
}
\ No newline at end of file
......@@ -3,34 +3,51 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// Clase que contiene los ajustes seleccionados por el usuario
class ProviderAjustes extends ChangeNotifier {
/// Índice del modo de conversión
int _modoConversion = 1;
/// Índice de la inclusión de subcarpetas
int _incluirSubcarpetas = 1;
/// Carpeta de salida. Si está vacía, se pregunta al convertir
String _carpetaSalida = "";
/// Flag que indica si se están cargando los valores anteriores
bool _cargando = true;
/// Clave del modo de conversión
static const modoConversionKey = 'modoConversion';
/// Clave de la inclusión de subcarpetas
static const incluirSubcarpetasKey = 'incluirSubcarpetas';
/// Clave de la carpeta de salida
static const carpetaSalidaKey = 'carpetaSalida';
/// Devuelve el modo de conversión
int get modoConversion => _modoConversion;
/// Devuelve si se incluyen subcarpetas por defecto
int get incluirSubcarpetas => _incluirSubcarpetas;
/// Devuelve la carpeta de salida actualmente seleccionada
String get carpetaSalida => _carpetaSalida;
/// Devuelve si se están cargando los valores anteriores
bool get cargando => _cargando;
/// Devuelve si se debe expandir el botón de Convertir
bool get expandConvert => _cargando || _modoConversion == 0;
ProviderAjustes() {
// Al crear el provider, se cargan inmediatamente los valores anteriores
_cargarValoresAnteriores();
}
/// Carga los valores anteriormente seleccionados si se puede
Future<void> _cargarValoresAnteriores() async {
final prefs = await SharedPreferences.getInstance();
_modoConversion = await prefs.getInt(modoConversionKey) ?? 0;
_modoConversion = await prefs.getInt(modoConversionKey) ?? 1;
_incluirSubcarpetas = await prefs.getInt(incluirSubcarpetasKey) ?? 1;
_carpetaSalida = await prefs.getString(carpetaSalidaKey) ?? '';
// Comprobamos si la carpeta existe
if(_carpetaSalida.isNotEmpty){
final directory = Directory(carpetaSalida);
if(!(await directory.exists())){
......@@ -42,34 +59,40 @@ class ProviderAjustes extends ChangeNotifier {
notifyListeners();
}
/// Fija un índice para el modo de conversión
void setModoConversion(int valor) {
_modoConversion = valor;
notifyListeners();
_actualizarModoConversion();
}
/// Fija un índice para la inclusión de subcarpetas
void setIncluirSubcarpetas(int valor) {
_incluirSubcarpetas = valor;
notifyListeners();
_actualizarIncluirSubcarpetasn();
}
/// Fija la carpeta de salida
void setCarpetaSalida(String valor) {
_carpetaSalida = valor;
notifyListeners();
_actualizarCarpetaSalida();
}
/// Guarda el valore actualmente seleccionado para el modo de conversión
Future<void> _actualizarModoConversion() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setInt(modoConversionKey, _modoConversion);
}
/// Guarda el valore actualmente seleccionado para la inclusión de subcarpetas
Future<void> _actualizarIncluirSubcarpetasn() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setInt(incluirSubcarpetasKey, _incluirSubcarpetas);
}
/// Guarda el valore actualmente seleccionado para la carpeta de salida
Future<void> _actualizarCarpetaSalida() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(carpetaSalidaKey, _carpetaSalida);
......
......@@ -337,7 +337,7 @@ class _PaginaConversionState extends State<PaginaConversion>
formatoPerfil = perfil.extensionVideo;
}
if(widget._formatoOriginal.tipoMultimedia == TipoMultimedia.audio){
formatoPerfil = perfil.extensionMusica;
formatoPerfil = perfil.extensionAudio;
}
if(formatoPerfil != null){
......@@ -500,10 +500,17 @@ class _PaginaConversionState extends State<PaginaConversion>
else{
widget._archivo!.formatoDestino = _formatoConvertido;
widget._archivo!.calidadSalida = _calidadActual;
resultado = widget._archivo!;
}
// TODO: ACTUALIZAR CALIDAD
if(widget._archivo!.formatoOriginal.tipoMultimedia == TipoMultimedia.video &&
_formatoConvertido?.tipoMultimedia == TipoMultimedia.imagen){
Archivo arch = widget._archivo! as Archivo;
arch.fotograma = 0;
resultado = arch;
}
else{
resultado = widget._archivo!;
}
}
widget._lista.actualizaSeleccionable(widget._indiceArchivo, resultado);
});
......
......@@ -132,7 +132,7 @@ class _PaginaMetadatosState extends State<PaginaMetadatos> {
border: OutlineInputBorder(),
),
keyboardType:
(metadatos[index].info.numerico)? const TextInputType.numberWithOptions(
(metadatos[index].info.esNumerico)? const TextInputType.numberWithOptions(
signed: true,
decimal: false
): null,
......
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