outsourced lots of api calls to api folder

centered error message when failed loading video feed
display server url on settings page
This commit is contained in:
lukas-heiligenbrunner 2022-10-15 20:28:31 +02:00
parent cb42db80af
commit 9ef317f0ba
17 changed files with 147 additions and 159 deletions

View File

@ -1,5 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import '../log/log.dart';
import '../types/actor.dart'; import '../types/actor.dart';
import 'api.dart'; import 'api.dart';
@ -10,3 +11,26 @@ Future<List<Actor>> loadAllActors() async {
final actors = d.map((e) => Actor.fromJson(e)).toList(growable: false); final actors = d.map((e) => Actor.fromJson(e)).toList(growable: false);
return actors; return actors;
} }
Future<List<Actor>> loadActorsOfVideo(int movieId) async {
final data =
await API.query("actor", "getActorsOfVideo", {'MovieId': movieId});
if (data == 'null') {
return [];
}
final d = jsonDecode(data);
List<Actor> dta = (d as List).map((e) => Actor.fromJson(e)).toList();
return dta;
}
Future<void> addActorToVideo(int actorId, int movieId) async {
final data = await API.query(
"actor", "addActorToVideo", {'ActorId': actorId, 'MovieId': movieId});
final d = jsonDecode(data);
if (d["result"] != "success") {
Log.w("couldn't add actor to video");
}
}

23
lib/api/tag_api.dart Normal file
View File

@ -0,0 +1,23 @@
import 'dart:convert';
import '../log/log.dart';
import '../types/tag.dart';
import 'api.dart';
Future<List<Tag>> loadAllTags() async {
final data = await API.query("tags", "getAllTags", {});
final d = (jsonDecode(data) ?? []) as List<dynamic>;
final tags = d.map((e) => Tag.fromJson(e)).toList(growable: false);
return tags;
}
Future<void> addTagToVideo(int tagId, int movieId) async {
final data =
await API.query("tags", "addTag", {'TagId': tagId, 'MovieId': movieId});
final d = jsonDecode(data);
if (d["result"] != "success") {
Log.w("couldn't add actor to video");
}
}

View File

@ -1,5 +1,11 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import '../log/log.dart';
import '../types/actor.dart';
import '../types/tag.dart';
import '../types/video.dart';
import '../types/video_data.dart'; import '../types/video_data.dart';
import 'api.dart'; import 'api.dart';
@ -10,3 +16,55 @@ Future<VideoData> loadVideoData(int videoId) async {
final video = VideoData.fromJson(d); final video = VideoData.fromJson(d);
return video; return video;
} }
Future<List<VideoT>> loadVideo(Tag? tag, int filterIdx) async {
final data = await API
.query("video", "getMovies", {'Tag': tag?.tagId ?? 1, 'Sort': filterIdx});
final d = jsonDecode(data);
List<VideoT> dta =
(d['Videos'] as List).map((e) => VideoT.fromJson(e)).toList();
return dta;
}
Future<List<VideoT>> loadShuffledVideos(int nr) async {
final data = await API.query("video", "getRandomMovies",
{'Number': nr, 'Seed': Random().nextInt(0x7fffffff)});
final d = jsonDecode(data);
List<VideoT> dta =
(d['Videos'] as List).map((e) => VideoT.fromJson(e)).toList();
return dta;
}
Future<List<VideoT>> loadVideoByActor(Actor actor) async {
final data =
await API.query("actor", "getActorInfo", {'ActorId': actor.actorId});
final d = jsonDecode(data);
List<VideoT> dta =
(d['Videos'] as List).map((e) => VideoT.fromJson(e)).toList();
return dta;
}
Future<bool> addLike(int movieId) async {
final data = await API.query("video", "addLike", {'MovieId': movieId});
final d = jsonDecode(data);
if (d["result"] != 'success') {
Log.w(d);
}
return d["result"] == 'success';
}
Future<Uint8List> fetchThumbnail(int movieId) async {
final base64str =
await API.query("video", "readThumbnail", {'Movieid': movieId});
return base64Decode(base64str.substring(23));
}

View File

@ -12,6 +12,7 @@ class AppScrollBehavior extends MaterialScrollBehavior {
Set<PointerDeviceKind> get dragDevices => { Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch, PointerDeviceKind.touch,
PointerDeviceKind.mouse, PointerDeviceKind.mouse,
PointerDeviceKind.trackpad
}; };
} }

View File

@ -25,12 +25,8 @@ class Db {
} }
_db = await openDatabase( _db = await openDatabase(
// Set the path to the database. Note: Using the `join` function from the
// `path` package is best practice to ensure the path is correctly
// constructed for each platform.
dbpath, dbpath,
onCreate: (db, version) { onCreate: (db, version) {
// Run the CREATE TABLE statement on the database.
return db.execute( return db.execute(
'CREATE TABLE previews(id INTEGER PRIMARY KEY, thumbnail BLOB)', 'CREATE TABLE previews(id INTEGER PRIMARY KEY, thumbnail BLOB)',
); );

View File

@ -1,10 +1,6 @@
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../api/actor_api.dart'; import '../api/actor_api.dart';
import '../api/api.dart';
import '../log/log.dart';
import '../screen_loading.dart'; import '../screen_loading.dart';
import '../types/actor.dart'; import '../types/actor.dart';
@ -19,16 +15,6 @@ class AddActorDialog extends StatefulWidget {
class _AddActorDialogState extends State<AddActorDialog> { class _AddActorDialogState extends State<AddActorDialog> {
late Future<List<Actor>> actors = loadAllActors(); late Future<List<Actor>> actors = loadAllActors();
Future<void> 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 @override
void initState() { void initState() {
super.initState(); super.initState();
@ -52,7 +38,7 @@ class _AddActorDialogState extends State<AddActorDialog> {
.map((e) => ListTile( .map((e) => ListTile(
title: Text(e.name), title: Text(e.name),
onTap: () async { onTap: () async {
await addActorToVideo(e.actorId); await addActorToVideo(e.actorId, widget.movieId);
Navigator.pop(context, e); Navigator.pop(context, e);
}, },
)) ))

View File

@ -1,9 +1,6 @@
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../api/api.dart'; import '../api/tag_api.dart';
import '../log/log.dart';
import '../screen_loading.dart'; import '../screen_loading.dart';
import '../types/tag.dart'; import '../types/tag.dart';
@ -18,24 +15,6 @@ class AddTagDialog extends StatefulWidget {
class _AddTagDialogState extends State<AddTagDialog> { class _AddTagDialogState extends State<AddTagDialog> {
late Future<List<Tag>> tags = loadAllTags(); late Future<List<Tag>> tags = loadAllTags();
Future<List<Tag>> loadAllTags() async {
final data = await API.query("tags", "getAllTags", {});
final d = (jsonDecode(data) ?? []) as List<dynamic>;
final tags = d.map((e) => Tag.fromJson(e)).toList(growable: false);
return tags;
}
Future<void> 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 @override
void initState() { void initState() {
super.initState(); super.initState();
@ -60,7 +39,7 @@ class _AddTagDialogState extends State<AddTagDialog> {
(e) => ListTile( (e) => ListTile(
title: Text(e.tagName), title: Text(e.tagName),
onTap: () async { onTap: () async {
await addTagToVideo(e.tagId); await addTagToVideo(e.tagId, widget.movieId);
Navigator.pop(context, e); Navigator.pop(context, e);
}, },
), ),

View File

@ -1,11 +1,9 @@
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:openmediacentermobile/preview/tag_tile.dart'; import '../api/tag_api.dart';
import '../preview/tag_tile.dart';
import '../drawer/my_drawer.dart'; import '../drawer/my_drawer.dart';
import '../screen_loading.dart'; import '../screen_loading.dart';
import '../api/api.dart';
import '../types/tag.dart'; import '../types/tag.dart';
class CategorieScreen extends StatefulWidget { class CategorieScreen extends StatefulWidget {
@ -18,18 +16,10 @@ class CategorieScreen extends StatefulWidget {
class _CategorieScreenState extends State<CategorieScreen> { class _CategorieScreenState extends State<CategorieScreen> {
late Future<List<Tag>> _categories; late Future<List<Tag>> _categories;
Future<List<Tag>> loadVideoData() async {
final data = await API.query("tags", "getAllTags", {});
final d = (jsonDecode(data) ?? []) as List<dynamic>;
final tags = d.map((e) => Tag.fromJson(e)).toList(growable: false);
return tags;
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_categories = loadVideoData(); _categories = loadAllTags();
} }
@override @override

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:openmediacentermobile/utils/file_formatter.dart'; import '../utils/file_formatter.dart';
import '../api/token.dart'; import '../api/token.dart';
import '../db/database.dart'; import '../db/database.dart';
@ -15,6 +15,7 @@ class SettingsScreen extends StatefulWidget {
class _SettingsScreenState extends State<SettingsScreen> { class _SettingsScreenState extends State<SettingsScreen> {
int dbsize = 0; int dbsize = 0;
String serverUrl = "";
@override @override
void initState() { void initState() {
@ -22,6 +23,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
Db().getDbSize().then((v) => setState(() { Db().getDbSize().then((v) => setState(() {
dbsize = v; dbsize = v;
})); }));
Token.getInstance().getToken().then((value) => setState(() {
serverUrl = value?.domain ?? "unknown";
}));
} }
@override @override
@ -34,6 +39,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
), ),
body: Column( body: Column(
children: [ children: [
Text("Current server: $serverUrl"),
ElevatedButton( ElevatedButton(
onPressed: () async { onPressed: () async {
await Db().clear(); await Db().clear();

View File

@ -1,12 +1,9 @@
import 'dart:convert';
import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../api/video_api.dart';
import '../drawer/my_drawer.dart'; import '../drawer/my_drawer.dart';
import '../preview/preview_grid.dart'; import '../preview/preview_grid.dart';
import '../api/api.dart';
import '../utils/platform.dart'; import '../utils/platform.dart';
import '../types/video.dart';
class ShuffleScreen extends StatefulWidget { class ShuffleScreen extends StatefulWidget {
const ShuffleScreen({Key? key}) : super(key: key); const ShuffleScreen({Key? key}) : super(key: key);
@ -16,18 +13,6 @@ class ShuffleScreen extends StatefulWidget {
} }
class _ShuffleScreenState extends State<ShuffleScreen> { class _ShuffleScreenState extends State<ShuffleScreen> {
Future<List<VideoT>> loadData(int nr) async {
final data = await API.query("video", "getRandomMovies",
{'Number': nr, 'Seed': Random().nextInt(0x7fffffff)});
final d = jsonDecode(data);
List<VideoT> dta =
(d['Videos'] as List).map((e) => VideoT.fromJson(e)).toList();
return dta;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width; double width = MediaQuery.of(context).size.width;
@ -38,7 +23,8 @@ class _ShuffleScreenState extends State<ShuffleScreen> {
), ),
body: PreviewGrid( body: PreviewGrid(
videoLoader: () { videoLoader: () {
return loadData((isTV() ? width ~/ 200 : width ~/ 275) * 2); return loadShuffledVideos(
(isTV() ? width ~/ 200 : width ~/ 275) * 2);
}, },
footerBuilder: (state) => Column( footerBuilder: (state) => Column(
children: [ children: [

View File

@ -1,12 +1,9 @@
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../api/video_api.dart';
import '../api/api.dart';
import '../drawer/my_drawer.dart'; import '../drawer/my_drawer.dart';
import '../preview/preview_grid.dart'; import '../preview/preview_grid.dart';
import '../types/tag.dart'; import '../types/tag.dart';
import '../types/video.dart';
enum FilterTypes { DATE, LIKES, RANDOM, NAMES, LENGTH } enum FilterTypes { DATE, LIKES, RANDOM, NAMES, LENGTH }
@ -24,18 +21,6 @@ class VideoFeedState extends State<VideoFeed> {
FilterTypes filterSelection = FilterTypes.DATE; FilterTypes filterSelection = FilterTypes.DATE;
Key _refreshKey = UniqueKey(); Key _refreshKey = UniqueKey();
Future<List<VideoT>> loadData() async {
final data = await API.query("video", "getMovies",
{'Tag': widget.tag?.tagId ?? 1, 'Sort': filterSelection.index});
final d = jsonDecode(data);
List<VideoT> dta =
(d['Videos'] as List).map((e) => VideoT.fromJson(e)).toList();
return dta;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -80,7 +65,7 @@ class VideoFeedState extends State<VideoFeed> {
), ),
body: PreviewGrid( body: PreviewGrid(
key: _refreshKey, key: _refreshKey,
videoLoader: () => loadData(), videoLoader: () => loadVideo(widget.tag, filterSelection.index),
), ),
drawer: widget.tag == null ? MyDrawer() : null); drawer: widget.tag == null ? MyDrawer() : null);
} }

View File

@ -1,11 +1,8 @@
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../api/api.dart'; import '../api/video_api.dart';
import 'preview_grid.dart'; import 'preview_grid.dart';
import '../types/actor.dart'; import '../types/actor.dart';
import '../types/video.dart';
class ActorFeed extends StatefulWidget { class ActorFeed extends StatefulWidget {
const ActorFeed({Key? key, required this.actor}) : super(key: key); const ActorFeed({Key? key, required this.actor}) : super(key: key);
@ -16,22 +13,10 @@ class ActorFeed extends StatefulWidget {
} }
class _ActorFeedState extends State<ActorFeed> { class _ActorFeedState extends State<ActorFeed> {
Future<List<VideoT>> loadData() async {
final data = await API
.query("actor", "getActorInfo", {'ActorId': widget.actor.actorId});
final d = jsonDecode(data);
List<VideoT> dta =
(d['Videos'] as List).map((e) => VideoT.fromJson(e)).toList();
return dta;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return PreviewGrid( return PreviewGrid(
videoLoader: () => loadData(), videoLoader: () => loadVideoByActor(widget.actor),
); );
} }
} }

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:openmediacentermobile/preview/actor_feed.dart'; import '../preview/actor_feed.dart';
import '../utils/platform.dart'; import '../utils/platform.dart';
import '../types/actor.dart'; import '../types/actor.dart';

View File

@ -56,15 +56,15 @@ class _PreviewGridState extends State<PreviewGrid> {
builder: builder:
(BuildContext context, AsyncSnapshot<List<VideoT>> snapshot) { (BuildContext context, AsyncSnapshot<List<VideoT>> snapshot) {
if (snapshot.hasError) { if (snapshot.hasError) {
return Column( return Center(
children: [ child: Column(
Text("Error"), mainAxisAlignment: MainAxisAlignment.center,
TextButton( children: [
onPressed: () { Text("Error"),
loadData(); TextButton(
}, onPressed: () => loadData(), child: Text("Reload page"))
child: Text("Reload page")) ],
], ),
); );
} else if (snapshot.hasData) { } else if (snapshot.hasData) {
return _mainGrid(snapshot.data!, width); return _mainGrid(snapshot.data!, width);

View File

@ -1,9 +1,8 @@
import 'dart:convert';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
import '../api/api.dart'; import '../api/video_api.dart';
import '../db/database.dart'; import '../db/database.dart';
import '../log/log.dart'; import '../log/log.dart';
import '../utils/platform.dart'; import '../utils/platform.dart';
@ -51,23 +50,17 @@ class _PreviewTileState extends State<PreviewTile> {
); );
} }
Future<Uint8List> _fetchThumbnail(int id) async {
final base64str =
await API.query("video", "readThumbnail", {'Movieid': id});
return base64Decode(base64str.substring(23));
}
Future<Image> loadData() async { Future<Image> loadData() async {
Uint8List data; Uint8List data;
final id = widget.dta.id; final id = widget.dta.id;
if (kIsWeb) { if (kIsWeb) {
data = await _fetchThumbnail(id); data = await fetchThumbnail(id);
} else { } else {
final List<Map<String, dynamic>> prev = final List<Map<String, dynamic>> prev =
await Db().db().query('previews', where: "id=$id"); await Db().db().query('previews', where: "id=$id");
if (prev.isEmpty) { if (prev.isEmpty) {
data = await _fetchThumbnail(id); data = await fetchThumbnail(id);
insert(id, data); insert(id, data);
Log.d("Adding $id to db"); Log.d("Adding $id to db");
} else { } else {

View File

@ -1,6 +1,5 @@
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../api/actor_api.dart';
import '../api/video_api.dart'; import '../api/video_api.dart';
import '../dialog/add_actor_dialog.dart'; import '../dialog/add_actor_dialog.dart';
import '../dialog/add_tag_dialog.dart'; import '../dialog/add_tag_dialog.dart';
@ -8,9 +7,6 @@ import '../navigation/video_feed.dart';
import '../screen_loading.dart'; import '../screen_loading.dart';
import '../types/video_data.dart'; import '../types/video_data.dart';
import '../preview/actor_tile.dart'; import '../preview/actor_tile.dart';
import '../api/api.dart';
import '../log/log.dart';
import '../types/actor.dart'; import '../types/actor.dart';
class InfoView extends StatefulWidget { class InfoView extends StatefulWidget {
@ -29,7 +25,7 @@ class _InfoViewState extends State<InfoView> {
void initState() { void initState() {
super.initState(); super.initState();
setState(() { setState(() {
_data = loadData(); _data = loadActorsOfVideo(widget.videoId);
vdata = loadVideoData(widget.videoId); vdata = loadVideoData(widget.videoId);
}); });
} }
@ -39,19 +35,6 @@ class _InfoViewState extends State<InfoView> {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
} }
Future<List<Actor>> loadData() async {
final data = await API
.query("actor", "getActorsOfVideo", {'MovieId': widget.videoId});
if (data == 'null') {
return [];
}
final d = jsonDecode(data);
List<Actor> dta = (d as List).map((e) => Actor.fromJson(e)).toList();
return dta;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FutureBuilder( return FutureBuilder(
@ -71,15 +54,10 @@ class _InfoViewState extends State<InfoView> {
children: [ children: [
IconButton( IconButton(
onPressed: () async { onPressed: () async {
final data = await API.query("video", "addLike", if (await addLike(videoData.movieId))
{'MovieId': videoData.movieId}); setState(() {
final d = jsonDecode(data); vdata = loadVideoData(widget.videoId);
if (d["result"] != 'success') { });
Log.w(d);
}
setState(() {
vdata = loadVideoData(widget.videoId);
});
}, },
icon: Icon(Icons.thumb_up)), icon: Icon(Icons.thumb_up)),
TextButton( TextButton(
@ -90,9 +68,8 @@ class _InfoViewState extends State<InfoView> {
movieId: videoData.movieId, movieId: videoData.movieId,
), ),
); );
Log.d("finished dialog");
setState(() { setState(() {
_data = loadData(); _data = loadActorsOfVideo(widget.videoId);
}); });
}, },
child: Text("Add Actor"), child: Text("Add Actor"),
@ -105,7 +82,6 @@ class _InfoViewState extends State<InfoView> {
movieId: videoData.movieId, movieId: videoData.movieId,
), ),
); );
Log.d("finished dialog");
setState(() { setState(() {
vdata = loadVideoData(widget.videoId); vdata = loadVideoData(widget.videoId);
}); });

View File

@ -92,7 +92,7 @@ class _VideoScreenState extends State<VideoScreen> {
child: GestureDetector( child: GestureDetector(
onPanDown: (details) async { onPanDown: (details) async {
if (_appBarVisible) { if (_appBarVisible) {
await Future.delayed(Duration(milliseconds: 100)); await Future.delayed(Duration(milliseconds: 300));
setState(() { setState(() {
_appBarVisible = false; _appBarVisible = false;
}); });