diff --git a/lib/data/movies.dart b/lib/data/movies.dart index 148ec7b..f7af2c3 100644 --- a/lib/data/movies.dart +++ b/lib/data/movies.dart @@ -41,6 +41,26 @@ class Movie { }); factory Movie.fromJson(Map json) { + if (json.containsKey('backdropPath')) { + return Movie( + movieID: int.parse(json['movieID']), + movieTitle: json['movieTitle'] as String, + overview: json['overview'] as String, + releaseDate: DateTime.parse(json['releaseDate']), + state: int.parse(json['state']), + resolution: json['resolution'] as String, + backdropPath: json['backdropPath'] as String, + languages: json['languages'], + productionCountries: json['productionCountries'], + productionCompanies: json['productionCompanies'], + voteAverage: double.parse(json['voteAverage']), + voteCount: int.parse(json['voteCount']), + cast: json['cast'], + crew: json['crew'], + genre: json['genre'], + localPath: json['localpath'], + ); + } return Movie( movieID: int.parse(json['movieID']), movieTitle: json['movieTitle'] as String, @@ -48,16 +68,16 @@ class Movie { releaseDate: DateTime.parse(json['releaseDate']), state: int.parse(json['state']), resolution: json['resolution'] as String, - backdropPath: json['backdropPath'] as String, - languages: json['languages'], - productionCountries: json['productionCountries'], - productionCompanies: json['productionCompanies'], - voteAverage: double.parse(json['voteAverage']), - voteCount: int.parse(json['voteCount']), - cast: json['cast'], - crew: json['crew'], - genre: json['genre'], - localPath: json['localpath'], + backdropPath: '', + languages: '', + productionCountries: '', + productionCompanies: '', + voteAverage: 0, + voteCount: 0, + cast: '', + crew: '', + genre: '', + localPath: '', ); } } diff --git a/lib/data/tvshows.dart b/lib/data/tvshows.dart index 1d2364e..2a7339a 100644 --- a/lib/data/tvshows.dart +++ b/lib/data/tvshows.dart @@ -113,29 +113,56 @@ class TVShow { seriesState = 3; } + if (json.containsKey('overview')) { + return TVShow( + seriesID: int.parse(json['seriesID']), + seriesName: json['seriesName'], + firstAired: DateTime.parse(json['firstAired']), + seriesState: seriesState, + resolution: json['resolution'], + status: json['status'], + cliffhanger: int.parse(json['cliffhanger']), + download: json['download'], + overview: json['overview'], + backdropPath: json['backdropPath'], + seasonStatus: json['seasons'] + .map((json) => Season.fromJson(json)) + .toList(), + languages: json['languages'], + networks: json['networks'], + originCountries: json['originCountries'], + productionCompanies: json['productionCompanies'], + voteAverage: double.parse(json['voteAverage']), + voteCount: int.parse(json['voteCount']), + cast: json['cast'], + crew: json['crew'], + genre: json['genre'], + localPath: json['localPath'], + ); + } + return TVShow( seriesID: int.parse(json['seriesID']), seriesName: json['seriesName'], - overview: json['overview'], - backdropPath: json['backdropPath'], firstAired: DateTime.parse(json['firstAired']), seriesState: seriesState, resolution: json['resolution'], status: json['status'], cliffhanger: int.parse(json['cliffhanger']), - seasonStatus: - json['seasons'].map((json) => Season.fromJson(json)).toList(), - languages: json['languages'], - networks: json['networks'], - originCountries: json['originCountries'], - productionCompanies: json['productionCompanies'], - voteAverage: double.parse(json['voteAverage']), - voteCount: int.parse(json['voteCount']), - cast: json['cast'], - crew: json['crew'], - genre: json['genre'], download: json['download'], - localPath: json['localPath'], + overview: '', + backdropPath: '', + seasonStatus: [], + languages: '', + networks: '', + originCountries: '', + productionCompanies: '', + voteAverage: 0, + voteCount: 0, + cast: '', + crew: '', + genre: '', + localPath: '', ); } } diff --git a/lib/widgets/movies/details/detailsScreen.dart b/lib/widgets/movies/details/detailsScreen.dart index 6de98e7..96fe0f5 100644 --- a/lib/widgets/movies/details/detailsScreen.dart +++ b/lib/widgets/movies/details/detailsScreen.dart @@ -1,7 +1,10 @@ +import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:http/http.dart' as http; import 'package:transparent_image/transparent_image.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:multimedia/data/movies.dart'; @@ -20,7 +23,45 @@ class MovieDetailsScreen extends StatelessWidget { }); final Movie movie; - String firstAired = '?'; + late Movie movieDetails; + + Future loadMovieDetails(int movieID) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + + String? dbProtocol = prefs.getString('dbProtocol'); + String? dbHost = prefs.getString('dbHost'); + String? dbPath = prefs.getString('dbPath'); + + Uri url = Uri( + scheme: dbProtocol, + host: dbHost, + path: dbPath, + queryParameters: { + 'module': 'movie', + 'function': 'getDetails', + 'movieID': movieID.toString(), + }, + ); + + final response = await http.get(url); + bool? error; + String? errorMessage; + + Map movies = json.decode(response.body); + error = movies['error']; + errorMessage = movies['errmsg']; + var list = movies['data']; + + List _movies = + await list.map((json) => Movie.fromJson(json)).toList(); + + if (_movies.isNotEmpty) { + movieDetails = _movies[0]; + return _movies[0]; + } + + return _movies[0]; + } Widget generateLists(BuildContext context) { double width = MediaQuery.of(context).size.width; @@ -33,12 +74,12 @@ class MovieDetailsScreen extends StatelessWidget { Padding( padding: const EdgeInsets.only( left: 10.0, - right: 5.0, - top: 5.0, + right: 10.0, + top: 10.0, bottom: 5.0, ), child: MovieDetailsCastBox( - cast: movie.cast, + cast: movieDetails.cast, ), ), ], @@ -47,58 +88,58 @@ class MovieDetailsScreen extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.only( - left: 5.0, - right: 5.0, - top: 5.0, - bottom: 5.0, - ), - child: MovieDetailsCrewBox( - crew: movie.crew, - ), - ), - ], - ), - TableRow( - children: [ - Padding( - padding: const EdgeInsets.only( - left: 5.0, - right: 5.0, - top: 5.0, - bottom: 5.0, - ), - child: MovieDetailsGenreBox( - genre: movie.genre, - ), - ), - ], - ), - TableRow( - children: [ - Padding( - padding: const EdgeInsets.only( - left: 5.0, - right: 5.0, - top: 5.0, - bottom: 5.0, - ), - child: MovieDetailsCompaniesBox( - companies: movie.productionCompanies, - ), - ), - ], - ), - TableRow( - children: [ - Padding( - padding: const EdgeInsets.only( - left: 5.0, + left: 10.0, right: 10.0, top: 5.0, bottom: 5.0, ), + child: MovieDetailsCrewBox( + crew: movieDetails.crew, + ), + ), + ], + ), + TableRow( + children: [ + Padding( + padding: const EdgeInsets.only( + left: 10.0, + right: 10.0, + top: 5.0, + bottom: 5.0, + ), + child: MovieDetailsGenreBox( + genre: movieDetails.genre, + ), + ), + ], + ), + TableRow( + children: [ + Padding( + padding: const EdgeInsets.only( + left: 10.0, + right: 10.0, + top: 5.0, + bottom: 5.0, + ), + child: MovieDetailsCompaniesBox( + companies: movieDetails.productionCompanies, + ), + ), + ], + ), + TableRow( + children: [ + Padding( + padding: const EdgeInsets.only( + left: 10.0, + right: 10.0, + top: 5.0, + bottom: 10.0, + ), child: MovieDetailsCountriesBox( - countries: movie.productionCountries, + countries: movieDetails.productionCountries, ), ), ], @@ -116,33 +157,33 @@ class MovieDetailsScreen extends StatelessWidget { padding: const EdgeInsets.only( left: 10.0, right: 5.0, - top: 5.0, + top: 10.0, bottom: 5.0, ), child: MovieDetailsCastBox( - cast: movie.cast, + cast: movieDetails.cast, ), ), Padding( padding: const EdgeInsets.only( left: 5.0, right: 5.0, - top: 5.0, + top: 10.0, bottom: 5.0, ), child: MovieDetailsCrewBox( - crew: movie.crew, + crew: movieDetails.crew, ), ), Padding( padding: const EdgeInsets.only( left: 5.0, right: 5.0, - top: 5.0, + top: 10.0, bottom: 5.0, ), child: MovieDetailsGenreBox( - genre: movie.genre, + genre: movieDetails.genre, ), ), ], @@ -151,13 +192,13 @@ class MovieDetailsScreen extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.only( - left: 5.0, + left: 10.0, right: 5.0, top: 5.0, - bottom: 5.0, + bottom: 10.0, ), child: MovieDetailsCompaniesBox( - companies: movie.productionCompanies, + companies: movieDetails.productionCompanies, ), ), Padding( @@ -165,10 +206,10 @@ class MovieDetailsScreen extends StatelessWidget { left: 5.0, right: 10.0, top: 5.0, - bottom: 5.0, + bottom: 10.0, ), child: MovieDetailsCountriesBox( - countries: movie.productionCountries, + countries: movieDetails.productionCountries, ), ), const Text(''), @@ -190,7 +231,7 @@ class MovieDetailsScreen extends StatelessWidget { bottom: 5.0, ), child: MovieDetailsCastBox( - cast: movie.cast, + cast: movieDetails.cast, ), ), Padding( @@ -201,7 +242,7 @@ class MovieDetailsScreen extends StatelessWidget { bottom: 5.0, ), child: MovieDetailsCrewBox( - crew: movie.crew, + crew: movieDetails.crew, ), ), Padding( @@ -212,7 +253,7 @@ class MovieDetailsScreen extends StatelessWidget { bottom: 5.0, ), child: MovieDetailsGenreBox( - genre: movie.genre, + genre: movieDetails.genre, ), ), Padding( @@ -223,7 +264,7 @@ class MovieDetailsScreen extends StatelessWidget { bottom: 5.0, ), child: MovieDetailsCompaniesBox( - companies: movie.productionCompanies, + companies: movieDetails.productionCompanies, ), ), Padding( @@ -234,7 +275,7 @@ class MovieDetailsScreen extends StatelessWidget { bottom: 5.0, ), child: MovieDetailsCountriesBox( - countries: movie.productionCountries, + countries: movieDetails.productionCountries, ), ), ], @@ -249,9 +290,7 @@ class MovieDetailsScreen extends StatelessWidget { @override Widget build(BuildContext context) { - String url = 'http://image.tmdb.org/t/p/original${movie.backdropPath}'; String releaseDate = DateFormat('yyyy-MM-dd').format(movie.releaseDate); - return Scaffold( appBar: AppBar( title: Text("${movie.movieTitle} ($releaseDate)"), @@ -274,47 +313,64 @@ class MovieDetailsScreen extends StatelessWidget { ), ], ), - body: Stack(children: [ - Container( - decoration: BoxDecoration( - image: DecorationImage( - image: NetworkImage(url), - fit: BoxFit.cover, - ), - ), - ), - SingleChildScrollView( - child: Column( - children: [ - Padding( - padding: const EdgeInsets.only( - left: 10.0, - right: 10.0, - top: 10.0, - bottom: 5.0, - ), - child: MovieDetailsOverviewBox( - overview: movie.overview, + body: FutureBuilder( + future: loadMovieDetails(movie.movieID), + builder: (context, data) { + if (data.hasData) { + String url = + 'http://image.tmdb.org/t/p/original${movieDetails.backdropPath}'; + + return Stack(children: [ + Container( + decoration: BoxDecoration( + image: DecorationImage( + image: NetworkImage(url), + fit: BoxFit.cover, + ), ), ), - generateLists(context), - Padding( - padding: const EdgeInsets.only( - left: 10.0, - right: 10.0, - top: 5.0, - bottom: 5.0, - ), - child: MovieDetailsSettingsBox( - localPath: movie.localPath, - resolution: movie.resolution, - state: movie.state, + SingleChildScrollView( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only( + left: 10.0, + right: 10.0, + top: 10.0, + bottom: 5.0, + ), + child: MovieDetailsOverviewBox( + overview: movieDetails.overview, + ), + ), + generateLists(context), + Padding( + padding: const EdgeInsets.only( + left: 10.0, + right: 10.0, + top: 5.0, + bottom: 5.0, + ), + child: MovieDetailsSettingsBox( + localPath: movieDetails.localPath, + resolution: movieDetails.resolution, + state: movieDetails.state, + ), + ), + ], ), ), - ], - ), - ), - ]), + ]); + } else { + return const Center( + child: CircularProgressIndicator( + strokeWidth: 2, + value: 0.8, + ), + ); + } + }, + ), ); } } diff --git a/lib/widgets/tvshows/details/detailsScreen.dart b/lib/widgets/tvshows/details/detailsScreen.dart index d4c3de3..4a9f862 100644 --- a/lib/widgets/tvshows/details/detailsScreen.dart +++ b/lib/widgets/tvshows/details/detailsScreen.dart @@ -1,4 +1,10 @@ +import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:http/http.dart' as http; + +import 'package:shared_preferences/shared_preferences.dart'; + import 'package:multimedia/widgets/tvshows/details/companiesBox.dart'; import 'package:multimedia/widgets/tvshows/details/countriesBox.dart'; import 'package:multimedia/widgets/tvshows/details/crewBox.dart'; @@ -7,7 +13,7 @@ import 'package:multimedia/widgets/tvshows/details/genreBox.dart'; import 'package:multimedia/widgets/tvshows/details/overviewBox.dart'; import 'package:multimedia/widgets/tvshows/details/seasons.dart'; import 'package:multimedia/widgets/tvshows/details/settingsBox.dart'; -import 'package:transparent_image/transparent_image.dart'; +//import 'package:transparent_image/transparent_image.dart'; import 'package:multimedia/data/tvshows.dart'; @@ -18,7 +24,63 @@ class TVShowDetailsScreen extends StatelessWidget { }); final TVShow tvShow; - String firstAiredYear = '?'; + late int maxSeason; + late List episodeCount; + late TVShow tvShowDetails; + + Future loadTVShowDetails(int tvshowID) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + + String? dbProtocol = prefs.getString('dbProtocol'); + String? dbHost = prefs.getString('dbHost'); + String? dbPath = prefs.getString('dbPath'); + + Uri url = Uri( + scheme: dbProtocol, + host: dbHost, + path: dbPath, + queryParameters: { + 'module': 'tvshow', + 'function': 'getDetails', + 'tvshowID': tvshowID.toString(), + }, + ); + + print(url); + final response = await http.get(url); + bool? error; + String? errorMessage; + String? mSeason; + + Map tvshows = json.decode(response.body); + error = tvshows['error']; + errorMessage = tvshows['errmsg']; + mSeason = tvshows['maxSeason']; + + var tvshowList = tvshows['tvshows']; + var episodeList = tvshows['episodeCount']; + + if (mSeason != null) { + if (int.tryParse(mSeason!) != null) { + maxSeason = int.parse(mSeason); + } + } + + List _tvshows = + await tvshowList.map((json) => TVShow.fromJson(json)).toList(); + + List _episodecount = await episodeList + .map((json) => EpisodeCount.fromJson(json)) + .toList(); + + episodeCount = _episodecount; + + if (_tvshows.isNotEmpty) { + tvShowDetails = _tvshows[0]; + } + + return _tvshows[0]; + } Widget generateLists(BuildContext context) { double width = MediaQuery.of(context).size.width; @@ -31,12 +93,12 @@ class TVShowDetailsScreen extends StatelessWidget { Padding( padding: const EdgeInsets.only( left: 10.0, - right: 5.0, - top: 5.0, + right: 10.0, + top: 10.0, bottom: 5.0, ), child: TVShowDetailsCastBox( - cast: tvShow.cast, + cast: tvShowDetails.cast, ), ), ], @@ -45,58 +107,58 @@ class TVShowDetailsScreen extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.only( - left: 5.0, - right: 5.0, - top: 5.0, - bottom: 5.0, - ), - child: TVShowDetailsCrewBox( - crew: tvShow.crew, - ), - ), - ], - ), - TableRow( - children: [ - Padding( - padding: const EdgeInsets.only( - left: 5.0, - right: 5.0, - top: 5.0, - bottom: 5.0, - ), - child: TVShowDetailsGenreBox( - genre: tvShow.genre, - ), - ), - ], - ), - TableRow( - children: [ - Padding( - padding: const EdgeInsets.only( - left: 5.0, - right: 5.0, - top: 5.0, - bottom: 5.0, - ), - child: TVShowDetailsCompaniesBox( - companies: tvShow.productionCompanies, - ), - ), - ], - ), - TableRow( - children: [ - Padding( - padding: const EdgeInsets.only( - left: 5.0, + left: 10.0, right: 10.0, top: 5.0, bottom: 5.0, ), + child: TVShowDetailsCrewBox( + crew: tvShowDetails.crew, + ), + ), + ], + ), + TableRow( + children: [ + Padding( + padding: const EdgeInsets.only( + left: 10.0, + right: 10.0, + top: 5.0, + bottom: 5.0, + ), + child: TVShowDetailsGenreBox( + genre: tvShowDetails.genre, + ), + ), + ], + ), + TableRow( + children: [ + Padding( + padding: const EdgeInsets.only( + left: 10.0, + right: 10.0, + top: 5.0, + bottom: 5.0, + ), + child: TVShowDetailsCompaniesBox( + companies: tvShowDetails.productionCompanies, + ), + ), + ], + ), + TableRow( + children: [ + Padding( + padding: const EdgeInsets.only( + left: 10.0, + right: 10.0, + top: 5.0, + bottom: 10.0, + ), child: TVShowDetailsCountriesBox( - countries: tvShow.originCountries, + countries: tvShowDetails.originCountries, ), ), ], @@ -114,11 +176,11 @@ class TVShowDetailsScreen extends StatelessWidget { padding: const EdgeInsets.only( left: 10.0, right: 5.0, - top: 5.0, + top: 10.0, bottom: 5.0, ), child: TVShowDetailsCastBox( - cast: tvShow.cast, + cast: tvShowDetails.cast, ), ), Padding( @@ -129,33 +191,7 @@ class TVShowDetailsScreen extends StatelessWidget { bottom: 5.0, ), child: TVShowDetailsCrewBox( - crew: tvShow.crew, - ), - ), - Padding( - padding: const EdgeInsets.only( - left: 5.0, - right: 5.0, - top: 5.0, - bottom: 5.0, - ), - child: TVShowDetailsGenreBox( - genre: tvShow.genre, - ), - ), - ], - ), - TableRow( - children: [ - Padding( - padding: const EdgeInsets.only( - left: 5.0, - right: 5.0, - top: 5.0, - bottom: 5.0, - ), - child: TVShowDetailsCompaniesBox( - companies: tvShow.productionCompanies, + crew: tvShowDetails.crew, ), ), Padding( @@ -165,8 +201,34 @@ class TVShowDetailsScreen extends StatelessWidget { top: 5.0, bottom: 5.0, ), + child: TVShowDetailsGenreBox( + genre: tvShowDetails.genre, + ), + ), + ], + ), + TableRow( + children: [ + Padding( + padding: const EdgeInsets.only( + left: 10.0, + right: 5.0, + top: 5.0, + bottom: 10.0, + ), + child: TVShowDetailsCompaniesBox( + companies: tvShowDetails.productionCompanies, + ), + ), + Padding( + padding: const EdgeInsets.only( + left: 5.0, + right: 10.0, + top: 5.0, + bottom: 10.0, + ), child: TVShowDetailsCountriesBox( - countries: tvShow.originCountries, + countries: tvShowDetails.originCountries, ), ), const Text(''), @@ -188,7 +250,7 @@ class TVShowDetailsScreen extends StatelessWidget { bottom: 5.0, ), child: TVShowDetailsCastBox( - cast: tvShow.cast, + cast: tvShowDetails.cast, ), ), Padding( @@ -199,7 +261,7 @@ class TVShowDetailsScreen extends StatelessWidget { bottom: 5.0, ), child: TVShowDetailsCrewBox( - crew: tvShow.crew, + crew: tvShowDetails.crew, ), ), Padding( @@ -210,7 +272,7 @@ class TVShowDetailsScreen extends StatelessWidget { bottom: 5.0, ), child: TVShowDetailsGenreBox( - genre: tvShow.genre, + genre: tvShowDetails.genre, ), ), Padding( @@ -221,7 +283,7 @@ class TVShowDetailsScreen extends StatelessWidget { bottom: 5.0, ), child: TVShowDetailsCompaniesBox( - companies: tvShow.productionCompanies, + companies: tvShowDetails.productionCompanies, ), ), Padding( @@ -232,7 +294,7 @@ class TVShowDetailsScreen extends StatelessWidget { bottom: 5.0, ), child: TVShowDetailsCountriesBox( - countries: tvShow.originCountries, + countries: tvShowDetails.originCountries, ), ), ], @@ -247,9 +309,8 @@ class TVShowDetailsScreen extends StatelessWidget { @override Widget build(BuildContext context) { - String url = 'http://image.tmdb.org/t/p/original${tvShow.backdropPath}'; + String firstAiredYear = tvShow.firstAired.toString().substring(0, 4); - firstAiredYear = tvShow.firstAired.toString().substring(0, 4); return Scaffold( appBar: AppBar( title: Text("${tvShow.seriesName} ($firstAiredYear)"), @@ -270,60 +331,77 @@ class TVShowDetailsScreen extends StatelessWidget { Navigator.pop(context); }, ), - ], ), - body: Stack(children: [ - Container( - decoration: BoxDecoration( - image: DecorationImage( - image: NetworkImage(url), - fit: BoxFit.cover, - ), - ), - ), - SingleChildScrollView( - child: Column( - children: [ - Padding( - padding: const EdgeInsets.only( - left: 10.0, - right: 10.0, - top: 10.0, - bottom: 5.0, + ], + ), + body: FutureBuilder( + future: loadTVShowDetails(tvShow.seriesID), + builder: (context, data) { + if (data.hasData) { + String url = + 'http://image.tmdb.org/t/p/original${tvShowDetails.backdropPath}'; + return Stack(children: [ + Container( + decoration: BoxDecoration( + image: DecorationImage( + image: NetworkImage(url), + fit: BoxFit.cover, + ), + ), ), - child: TVShowDetailsOverviewBox( - overview: tvShow.overview, + SingleChildScrollView( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only( + left: 10.0, + right: 10.0, + top: 10.0, + bottom: 5.0, + ), + child: TVShowDetailsOverviewBox( + overview: tvShowDetails.overview, + ), + ), + generateLists(context), + Padding( + padding: const EdgeInsets.only( + left: 10.0, + right: 10.0, + top: 5.0, + bottom: 5.0, + ), + child: TVShowDetailsSettingsBox( + downloadLink: tvShowDetails.download, + localPath: tvShowDetails.localPath, + resolution: tvShowDetails.resolution, + cliffhanger: tvShowDetails.cliffhanger, + ), + ), + Padding( + padding: const EdgeInsets.only( + left: 10.0, + right: 10.0, + top: 5.0, + bottom: 5.0, + ), + child: TVShowDetailsSeasons( + seasonStatus: tvShowDetails.seasonStatus, + ), + ), + ], + ), ), - ), - generateLists(context), - Padding( - padding: const EdgeInsets.only( - left: 10.0, - right: 10.0, - top: 5.0, - bottom: 5.0, + ]); + ; + } else { + return const Center( + child: CircularProgressIndicator( + strokeWidth: 2, + value: 0.8, ), - child: TVShowDetailsSettingsBox( - downloadLink: tvShow.download, - localPath: tvShow.localPath, - resolution: tvShow.resolution, - cliffhanger: tvShow.cliffhanger, - ), - ), - Padding( - padding: const EdgeInsets.only( - left: 10.0, - right: 10.0, - top: 5.0, - bottom: 5.0, - ), - child: TVShowDetailsSeasons( - seasonStatus: tvShow.seasonStatus, - ), - ), - ], - ), - ), - ]), + ); + } + }), ); } } diff --git a/web/index.html b/web/index.html index fc5083b..8dfd7fb 100644 --- a/web/index.html +++ b/web/index.html @@ -38,6 +38,10 @@ + +