diff --git a/lib/api/actor_api.dart b/lib/api/actor_api.dart new file mode 100644 index 0000000..659918b --- /dev/null +++ b/lib/api/actor_api.dart @@ -0,0 +1,12 @@ +import 'dart:convert'; + +import '../types/actor.dart'; +import 'api.dart'; + +Future> loadAllActors() async { + final data = await API.query("actor", "getAllActors", {}); + + final d = (jsonDecode(data) ?? []) as List; + final actors = d.map((e) => Actor.fromJson(e)).toList(growable: false); + return actors; +} diff --git a/lib/api/video_api.dart b/lib/api/video_api.dart new file mode 100644 index 0000000..6197061 --- /dev/null +++ b/lib/api/video_api.dart @@ -0,0 +1,12 @@ +import 'dart:convert'; + +import '../types/video_data.dart'; +import 'api.dart'; + +Future loadVideoData(int videoId) async { + final data = await API.query("video", "loadVideo", {'MovieId': videoId}); + + final d = jsonDecode(data); + final video = VideoData.fromJson(d); + return video; +} diff --git a/lib/dialog/add_actor_dialog.dart b/lib/dialog/add_actor_dialog.dart new file mode 100644 index 0000000..2df3907 --- /dev/null +++ b/lib/dialog/add_actor_dialog.dart @@ -0,0 +1,67 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; + +import '../api/actor_api.dart'; +import '../api/api.dart'; +import '../log/log.dart'; +import '../screen_loading.dart'; +import '../types/actor.dart'; + +class AddActorDialog extends StatefulWidget { + const AddActorDialog({Key? key, required this.movieId}) : super(key: key); + final int movieId; + + @override + State createState() => _AddActorDialogState(); +} + +class _AddActorDialogState extends State { + late Future> actors = loadAllActors(); + + Future addActorToVideo(int actorId) async { + final data = await API.query("actor", "addActorToVideo", + {'ActorId': actorId, 'MovieId': widget.movieId}); + + final d = jsonDecode(data); + if (d["result"] != "success") { + Log.w("couldn't add actor to video"); + } + } + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + scrollable: true, + title: Text("Add Actor"), + content: FutureBuilder( + future: actors, + builder: (context, snapshot) { + if (snapshot.hasError) { + return Text("Error"); + } else if (snapshot.hasData) { + final data = snapshot.data! as List; + return Column( + mainAxisSize: MainAxisSize.min, + children: data + .map((e) => ListTile( + title: Text(e.name), + onTap: () async { + await addActorToVideo(e.actorId); + Navigator.pop(context, e); + }, + )) + .toList(), + ); + } else { + return ScreenLoading(); + } + }, + )); + } +} diff --git a/lib/dialog/add_tag_dialog.dart b/lib/dialog/add_tag_dialog.dart new file mode 100644 index 0000000..754268a --- /dev/null +++ b/lib/dialog/add_tag_dialog.dart @@ -0,0 +1,76 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; + +import '../api/api.dart'; +import '../log/log.dart'; +import '../screen_loading.dart'; +import '../types/tag.dart'; + +class AddTagDialog extends StatefulWidget { + const AddTagDialog({Key? key, required this.movieId}) : super(key: key); + final int movieId; + + @override + State createState() => _AddTagDialogState(); +} + +class _AddTagDialogState extends State { + late Future> tags = loadAllTags(); + + Future> loadAllTags() async { + final data = await API.query("tags", "getAllTags", {}); + + final d = (jsonDecode(data) ?? []) as List; + final tags = d.map((e) => Tag.fromJson(e)).toList(growable: false); + return tags; + } + + Future addTagToVideo(int tagId) async { + final data = await API + .query("tags", "addTag", {'TagId': tagId, 'MovieId': widget.movieId}); + + final d = jsonDecode(data); + if (d["result"] != "success") { + Log.w("couldn't add actor to video"); + } + } + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + scrollable: true, + title: Text("Add Tag"), + content: FutureBuilder( + future: tags, + builder: (context, snapshot) { + if (snapshot.hasError) { + return Text("Error"); + } else if (snapshot.hasData) { + final data = snapshot.data! as List; + return Column( + mainAxisSize: MainAxisSize.min, + children: data + .map( + (e) => ListTile( + title: Text(e.tagName), + onTap: () async { + await addTagToVideo(e.tagId); + Navigator.pop(context, e); + }, + ), + ) + .toList(), + ); + } else { + return ScreenLoading(); + } + }, + )); + } +} diff --git a/lib/navigation/actor_screen.dart b/lib/navigation/actor_screen.dart index 2a2c9ac..f1aadd2 100644 --- a/lib/navigation/actor_screen.dart +++ b/lib/navigation/actor_screen.dart @@ -1,11 +1,9 @@ -import 'dart:convert'; - import 'package:flutter/material.dart'; import 'package:openmediacentermobile/types/actor.dart'; import 'package:openmediacentermobile/preview/actor_tile.dart'; import '../DrawerPage.dart'; -import '../api/api.dart'; +import '../api/actor_api.dart'; import '../screen_loading.dart'; class ActorScreen extends StatefulWidget { @@ -18,18 +16,10 @@ class ActorScreen extends StatefulWidget { class _ActorScreenState extends State { late Future> _categories; - Future> loadVideoData() async { - final data = await API.query("actor", "getAllActors", {}); - - final d = (jsonDecode(data) ?? []) as List; - final actors = d.map((e) => Actor.fromJson(e)).toList(growable: false); - return actors; - } - @override void initState() { super.initState(); - _categories = loadVideoData(); + _categories = loadAllActors(); } @override diff --git a/lib/navigation/settings_screen.dart b/lib/navigation/settings_screen.dart index 731aa6c..b9a9000 100644 --- a/lib/navigation/settings_screen.dart +++ b/lib/navigation/settings_screen.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:openmediacentermobile/utils/FileFormatter.dart'; import '../DrawerPage.dart'; import '../api/token.dart'; @@ -41,7 +42,8 @@ class _SettingsScreenState extends State { })); }, child: const Text("Delete cache!")), - Text("db size: ${dbsize / 1024} kb"), + Text( + "DB size: ${dbsize.readableFileSize()} / ${dbsize.readableFileSize(base1024: false)}"), ElevatedButton( onPressed: () { loginCtx.onLoggin(false); diff --git a/lib/preview/preview_tile.dart b/lib/preview/preview_tile.dart index 74b660b..1f3b007 100644 --- a/lib/preview/preview_tile.dart +++ b/lib/preview/preview_tile.dart @@ -32,7 +32,6 @@ class _PreviewTileState extends State { super.initState(); _preview = loadData(); - Log.d("initial load of tile"); } @override @@ -43,7 +42,6 @@ class _PreviewTileState extends State { setState(() { _preview = loadData(); }); - Log.i("load of tile due to change"); } } diff --git a/lib/utils/FileFormatter.dart b/lib/utils/FileFormatter.dart new file mode 100644 index 0000000..d9e81db --- /dev/null +++ b/lib/utils/FileFormatter.dart @@ -0,0 +1,17 @@ +import 'dart:math'; + +double _log10(num x) => log(x) / ln10; + +extension FileFormatter on num { + String readableFileSize({bool base1024 = true}) { + if (this <= 0) return "0"; + final base = base1024 ? 1024 : 1000; + final units = base1024 + ? ["Bi", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"] + : ["B", "KB", "MB", "GB", "TB", "PB", "EB"]; + final digitGroups = (_log10(this) / _log10(base)).floor(); + return (this / pow(base, digitGroups)).toStringAsFixed(2) + + " " + + units[digitGroups]; + } +} diff --git a/lib/video_screen/info_view.dart b/lib/video_screen/info_view.dart index 4d71734..20c8333 100644 --- a/lib/video_screen/info_view.dart +++ b/lib/video_screen/info_view.dart @@ -1,18 +1,21 @@ import 'dart:convert'; import 'package:flutter/material.dart'; -import 'package:openmediacentermobile/screen_loading.dart'; -import 'package:openmediacentermobile/preview/tag_tile.dart'; -import 'package:openmediacentermobile/types/video_data.dart'; -import 'package:openmediacentermobile/preview/actor_tile.dart'; +import '../api/video_api.dart'; +import '../dialog/add_actor_dialog.dart'; +import '../dialog/add_tag_dialog.dart'; +import '../navigation/video_feed.dart'; +import '../screen_loading.dart'; +import '../types/video_data.dart'; +import '../preview/actor_tile.dart'; import '../api/api.dart'; import '../log/log.dart'; import '../types/actor.dart'; class InfoView extends StatefulWidget { - const InfoView({Key? key, required this.vdata}) : super(key: key); - final VideoData vdata; + const InfoView({Key? key, required this.videoId}) : super(key: key); + final int videoId; @override State createState() => _InfoViewState(); @@ -20,18 +23,25 @@ class InfoView extends StatefulWidget { class _InfoViewState extends State { late Future> _data; + late Future vdata; @override void initState() { + super.initState(); setState(() { _data = loadData(); + vdata = loadVideoData(widget.videoId); }); - super.initState(); + } + + @override + void didUpdateWidget(InfoView oldWidget) { + super.didUpdateWidget(oldWidget); } Future> loadData() async { final data = await API - .query("actor", "getActorsOfVideo", {'MovieId': widget.vdata.movieId}); + .query("actor", "getActorsOfVideo", {'MovieId': widget.videoId}); if (data == 'null') { return []; } @@ -45,42 +55,106 @@ class _InfoViewState extends State { @override Widget build(BuildContext context) { return FutureBuilder( - future: _data, - builder: (context, AsyncSnapshot> snapshot) { + future: Future.wait([_data, vdata]), + builder: (context, AsyncSnapshot> snapshot) { if (snapshot.hasError) { return Text("Error"); } else if (snapshot.hasData) { - final actors = snapshot.data; + final actors = snapshot.data![0] as List; + final videoData = snapshot.data![1] as VideoData; return Padding( padding: EdgeInsets.only(left: 10, right: 10, top: 60), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Likes: ${widget.vdata.likes}"), - IconButton( - onPressed: () async { - final data = await API.query("video", "addLike", - {'MovieId': widget.vdata.movieId}); - final d = jsonDecode(data); - if (d["result"] != 'success') { - Log.w(d); - } - // bit hacky but it works - widget.vdata.likes += 1; - }, - icon: Icon(Icons.thumb_up)), - Text("Quality: ${widget.vdata.quality}"), - Text("Length: ${widget.vdata.length}sec"), - Text("Actors:"), - actors?.isEmpty ?? true - ? Text("no actors available") - : Row( - children: _renderActors(snapshot.data!), - ), - Text("Tags:"), Row( - children: widget.vdata.tags - .map((e) => TagTile(tag: e)) + children: [ + IconButton( + onPressed: () async { + final data = await API.query("video", "addLike", + {'MovieId': videoData.movieId}); + final d = jsonDecode(data); + if (d["result"] != 'success') { + Log.w(d); + } + setState(() { + vdata = loadVideoData(widget.videoId); + }); + }, + icon: Icon(Icons.thumb_up)), + TextButton( + onPressed: () async { + await showDialog( + context: context, + builder: (context) => AddActorDialog( + movieId: videoData.movieId, + ), + ); + Log.d("finished dialog"); + setState(() { + _data = loadData(); + }); + }, + child: Text("Add Actor"), + ), + TextButton( + onPressed: () async { + await showDialog( + context: context, + builder: (context) => AddTagDialog( + movieId: videoData.movieId, + ), + ); + Log.d("finished dialog"); + setState(() { + vdata = loadVideoData(widget.videoId); + }); + }, + child: Text("Add Tag"), + ) + ], + ), + Text( + "General info:", + style: TextStyle(fontWeight: FontWeight.bold), + ), + Text("Likes: ${videoData.likes}"), + Text("Quality: ${videoData.quality}"), + Text("Length: ${videoData.length}sec"), + Text("Actors:", + style: TextStyle(fontWeight: FontWeight.bold)), + actors.isEmpty + ? Text("no actors available") + : SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: _renderActors(actors), + ), + ), + Text("Tags:", + style: TextStyle(fontWeight: FontWeight.bold)), + SizedBox( + height: 5, + ), + Wrap( + spacing: 4, + runSpacing: 4, + children: videoData.tags + .map( + (e) => ActionChip( + backgroundColor: + Theme.of(context).secondaryHeaderColor, + label: Text(e.tagName), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => VideoFeed(tag: e), + ), + ); + }, + ), + ) .toList(growable: false), ) ])); diff --git a/lib/video_screen/videoscreen.dart b/lib/video_screen/videoscreen.dart index 8136c0b..1576399 100644 --- a/lib/video_screen/videoscreen.dart +++ b/lib/video_screen/videoscreen.dart @@ -1,9 +1,8 @@ import 'dart:async'; -import 'dart:convert'; import 'package:flutter/material.dart'; -import '../api/api.dart'; import '../api/token.dart'; +import '../api/video_api.dart'; import '../platform.dart'; import '../screen_loading.dart'; import '../types/video.dart'; @@ -32,15 +31,6 @@ class _VideoScreenState extends State { String url = ""; - Future loadVideoData() async { - final data = - await API.query("video", "loadVideo", {'MovieId': widget.metaData.id}); - - final d = jsonDecode(data); - final video = VideoData.fromJson(d); - return video; - } - void initPlayer() async { final videodata = await _videoData; @@ -57,7 +47,7 @@ class _VideoScreenState extends State { @override void initState() { super.initState(); - _videoData = loadVideoData(); + _videoData = loadVideoData(widget.metaData.id); initPlayer(); _setAppBarTimer(); } @@ -117,7 +107,7 @@ class _VideoScreenState extends State { } } }, - behavior: HitTestBehavior.opaque, + // behavior: HitTestBehavior.opaque, child: Stack(children: [ PageView( scrollDirection: Axis.vertical, @@ -132,7 +122,7 @@ class _VideoScreenState extends State { url: url, )), InfoView( - vdata: snapshot.data!, + videoId: widget.metaData.id, ) ]), if (_appBarVisible)