Compare commits

..

2 Commits

Author SHA1 Message Date
eff1ea0812 add load error in featurecontext 2022-10-20 09:28:14 +02:00
6f6fbb3897 add page for tv shows
only showing up if enabled in backend
2022-10-15 23:15:21 +02:00
25 changed files with 775 additions and 761 deletions

View File

@ -1,30 +1,10 @@
# This file tracks properties of this Flutter project. # This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc. # Used by Flutter tool to assess capabilities and perform upgrades etc.
# #
# This file should be version controlled. # This file should be version controlled and should not be manually edited.
version: version:
revision: f92f44110e87bad5ff168335c36da6f6053036e6 revision: 37de8a2a9ad217ec9d731f8af0bd7c83e8f4980c
channel: stable channel: master
project_type: app project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: f92f44110e87bad5ff168335c36da6f6053036e6
base_revision: f92f44110e87bad5ff168335c36da6f6053036e6
- platform: web
create_revision: f92f44110e87bad5ff168335c36da6f6053036e6
base_revision: f92f44110e87bad5ff168335c36da6f6053036e6
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

View File

@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'token.dart'; import 'token.dart';
class TokenException implements Exception { class TokenException implements Exception {
@ -12,7 +11,7 @@ class TokenException implements Exception {
class API { class API {
static Future<String> query( static Future<String> query(
String apinode, String action, Object payload) async { String apinode, String action, Object payload) async {
final t = await getToken(); final t = await Token.getInstance().getToken();
if (t != null) { if (t != null) {
final resp = await http.post( final resp = await http.post(
Uri.parse(t.domain + '/api/$apinode/$action'), Uri.parse(t.domain + '/api/$apinode/$action'),

View File

@ -1,19 +1,66 @@
import 'dart:async'; import 'dart:async';
import 'package:openmediacentermobile/db/settings_db.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import '../log/log.dart';
class TokenT { class TokenT {
String token; String token;
String domain; String domain;
String videoPath;
String tvPath;
TokenT(this.token, this.domain); TokenT(this.token, this.domain, this.videoPath, this.tvPath);
}
class Token {
static final Token _token = Token._();
final _storage = const FlutterSecureStorage();
String _tokenval = "";
String _domain = "";
String _vPath = "";
String _tvPath = "";
static Token getInstance() {
return _token;
} }
Future<TokenT?> getToken() async { Future<TokenT?> getToken() async {
final settings = await SettingsDB.getInstance().getSettings(); if (_tokenval == "" || _domain == "" || _vPath == "") {
if (settings.token == "" || settings.domain == "") { Log.d("reading token store");
return null; WidgetsFlutterBinding.ensureInitialized();
final token = await _storage.read(key: 'jwt');
final domain = await _storage.read(key: 'domain');
// todo paths should be loaded on each app start
final vPath = await _storage.read(key: 'videoPath');
final tvPath = await _storage.read(key: 'tvPath');
// check if value is defined in phone store
if (token != null && domain != null && vPath != null) {
_tokenval = token;
_domain = domain;
return TokenT(token, domain, vPath, tvPath ?? "");
} else { } else {
return TokenT(settings.token, settings.domain); Log.d("no token defined");
return null;
}
} else {
return TokenT(_tokenval, _domain, _vPath, _tvPath);
} }
} }
void setToken(String token, String domain, String videoPath, String tvPath) {
_tokenval = token;
_domain = domain;
_vPath = videoPath;
_tvPath = tvPath;
_storage.write(key: 'jwt', value: token);
_storage.write(key: 'domain', value: domain);
_storage.write(key: 'videoPath', value: videoPath);
_storage.write(key: 'tvPath', value: tvPath);
}
Token._();
}

View File

@ -3,6 +3,7 @@ import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:openmediacentermobile/log/log.dart'; import 'package:openmediacentermobile/log/log.dart';
import 'package:openmediacentermobile/login/login_screen.dart'; import 'package:openmediacentermobile/login/login_screen.dart';
import 'package:openmediacentermobile/utils/feature_context.dart';
import 'drawer/drawer_page.dart'; import 'drawer/drawer_page.dart';
import 'login/login_context.dart'; import 'login/login_context.dart';
@ -21,20 +22,21 @@ class App extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var loginCtx = LoginContext.of(context); final loginCtx = LoginContext.of(context);
Log.d("We are logged in: ${loginCtx.loggedIn}"); Log.d("We are logged in: ${loginCtx.loggedIn}");
if (!loginCtx.loggedIn) { if (!loginCtx.loggedIn) {
return const MaterialApp(home: LoginScreen()); return const MaterialApp(home: LoginScreen());
} else { } else {
return MaterialApp( return FeatureContainer(
theme: ThemeData( child: MaterialApp(
appBarTheme: AppBarTheme(backgroundColor: Color(0xff0d0d0d))),
scrollBehavior: AppScrollBehavior(), scrollBehavior: AppScrollBehavior(),
home: DrawerPage( home: DrawerPage(
title: 'OpenMediaCenter', title: 'OpenMediaCenter',
)); ),
),
);
} }
} }
} }

View File

@ -1,47 +1,39 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:path/path.dart'; import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart' as nativeffi; import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart' as webffi;
import '../log/log.dart'; import '../log/log.dart';
class Db { class Db {
late Database _db; late Database _db;
Future<void> init() async { void init() async {
if (kIsWeb) {
Log.i("Database on web is not supported");
return;
}
String dbpath = 'previews.db'; String dbpath = 'previews.db';
if (defaultTargetPlatform == TargetPlatform.android || if (defaultTargetPlatform == TargetPlatform.android ||
defaultTargetPlatform == TargetPlatform.iOS) { defaultTargetPlatform == TargetPlatform.iOS) {
dbpath = join(await getDatabasesPath(), dbpath); dbpath = join(await getDatabasesPath(), dbpath);
} else if(kIsWeb) {
databaseFactory = webffi.databaseFactoryFfiWeb;
} else { } else {
// Initialize FFI // Initialize FFI
nativeffi.sqfliteFfiInit(); sqfliteFfiInit();
// Change the default factory // Change the default factory
databaseFactory = nativeffi.databaseFactoryFfi; databaseFactory = databaseFactoryFfi;
} }
_db = await openDatabase( _db = await openDatabase(
dbpath, dbpath,
onCreate: (db, version) { onCreate: (db, version) {
final batch = db.batch(); return db.execute(
batch.execute( 'CREATE TABLE previews(id INTEGER PRIMARY KEY, thumbnail BLOB)',
'CREATE TABLE previews(id INTEGER PRIMARY KEY, thumbnail BLOB);',
); );
batch.execute(
'CREATE TABLE settings(domain TEXT, token TEXT, videopath TEXT, tilewidth INTEGER);',
);
batch.insert("settings",
{"domain": "", "token": "", "videopath": "", "tilewidth": 0});
return batch.commit();
}, },
// Set the version. This executes the onCreate function and provides a // Set the version. This executes the onCreate function and provides a
// path to perform database upgrades and downgrades. // path to perform database upgrades and downgrades.
version: 2, version: 1,
); );
} }
@ -54,16 +46,10 @@ class Db {
/// get db size in bytes /// get db size in bytes
Future<int> getDbSize() async { Future<int> getDbSize() async {
final batch = _db.batch(); final int cnt = (await Db().db().rawQuery("pragma page_count;"))[0]
batch.rawQuery("pragma page_count;"); ["page_count"] as int;
batch.rawQuery("pragma page_size;");
final result = (await batch.commit(noResult: false));
print(result);
final int cnt =
((result[0] as List<Map<String, dynamic>>)[0]["page_count"] as int);
final int pagesize = final int pagesize =
(result[1] as List<Map<String, dynamic>>)[0]["page_size"] as int; (await Db().db().rawQuery("pragma page_size;"))[0]["page_size"] as int;
return cnt * pagesize; return cnt * pagesize;
} }

View File

@ -1,51 +0,0 @@
import 'database.dart';
class SettingsT {
String domain;
String token;
String videopath;
int tilewidth;
SettingsT(this.domain, this.token, this.videopath, this.tilewidth);
}
class SettingsDB {
static final SettingsDB _instance = SettingsDB._();
SettingsT _settings = SettingsT("", "", "", 0);
bool _initialized = false;
Future<SettingsT> getSettings() async {
if (!_initialized) {
final result = (await Db().db().query("settings",
where: "1",
columns: ["domain", "token", "videopath", "tilewidth"]))
.first;
_settings = SettingsT(
result["domain"] as String,
result["token"] as String,
result["videopath"] as String,
result["tilewidth"] as int);
}
return _settings;
}
Future<void> setSettings(SettingsT settings) async {
await Db().db().update(
"settings",
{
"domain": settings.domain,
"token": settings.token,
"videopath": settings.videopath,
"tilewidth": settings.tilewidth
},
where: "1");
}
static SettingsDB getInstance() {
return _instance;
}
SettingsDB._();
}

View File

@ -32,8 +32,6 @@ class _AddActorDialogState extends State<AddActorDialog> {
return Text("Error"); return Text("Error");
} else if (snapshot.hasData) { } else if (snapshot.hasData) {
final data = snapshot.data! as List<Actor>; final data = snapshot.data! as List<Actor>;
data.sort((a, b) =>
a.name.toLowerCase().compareTo(b.name.toLowerCase()));
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: data children: data

View File

@ -32,10 +32,6 @@ class _AddTagDialogState extends State<AddTagDialog> {
return Text("Error"); return Text("Error");
} else if (snapshot.hasData) { } else if (snapshot.hasData) {
final data = snapshot.data! as List<Tag>; final data = snapshot.data! as List<Tag>;
data.sort(
(a, b) =>
a.tagName.toLowerCase().compareTo(b.tagName.toLowerCase()),
);
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: data children: data

View File

@ -3,6 +3,7 @@ import 'package:openmediacentermobile/navigation/settings_screen.dart';
import '../navigation/actor_screen.dart'; import '../navigation/actor_screen.dart';
import '../navigation/categorie_screen.dart'; import '../navigation/categorie_screen.dart';
import '../navigation/shuffle_screen.dart'; import '../navigation/shuffle_screen.dart';
import '../navigation/tv_show_screen.dart';
import '../navigation/video_feed.dart'; import '../navigation/video_feed.dart';
import 'drawer_context.dart'; import 'drawer_context.dart';
@ -15,7 +16,7 @@ class DrawerPage extends StatefulWidget {
_DrawerPageState createState() => _DrawerPageState(); _DrawerPageState createState() => _DrawerPageState();
} }
enum Section { HOME, SHUFFLE, SETTING, CATEGORIE, ACTOR } enum Section { HOME, SHUFFLE, SETTING, CATEGORIE, ACTOR, TVSHOW }
class _DrawerPageState extends State<DrawerPage> { class _DrawerPageState extends State<DrawerPage> {
Section _sec = Section.HOME; Section _sec = Section.HOME;
@ -50,6 +51,11 @@ class _DrawerPageState extends State<DrawerPage> {
body = ActorScreen(); body = ActorScreen();
title = "Actors"; title = "Actors";
break; break;
case Section.TVSHOW:
body = TVShowScreen();
title = "TV Shows";
break;
} }
return DrawerContext( return DrawerContext(

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:openmediacentermobile/utils/feature_context.dart';
import 'drawer_context.dart'; import 'drawer_context.dart';
import 'drawer_page.dart'; import 'drawer_page.dart';
@ -7,32 +8,62 @@ class MyDrawer extends StatelessWidget {
const MyDrawer({Key? key}) : super(key: key); const MyDrawer({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) => Drawer( Widget build(BuildContext context) {
backgroundColor: Color(0xff3f3f3f),
child: ListView(children: [
SizedBox(
height: 75,
),
_listelement('Home', Icons.home, Section.HOME, context),
_listelement('Shuffle', Icons.update, Section.SHUFFLE, context),
_listelement(
'Categories', Icons.category, Section.CATEGORIE, context),
_listelement('Actors', Icons.people, Section.ACTOR, context),
_listelement('Settings', Icons.settings, Section.SETTING, context),
]),
);
Widget _listelement(
String text, IconData icon, Section section, BuildContext context) {
final ctx = DrawerContext.of(context); final ctx = DrawerContext.of(context);
final featureCtx = FeatureContext.of(context);
return ListTile( return Drawer(
title: Text(text, style: TextStyle(color: Color(0xffe9e9e9))), child: ListView(children: [
leading: Icon(icon, color: Color(0xffe9e9e9)), ListTile(
title: const Text('Home'),
leading: const Icon(Icons.home),
onTap: () { onTap: () {
ctx.onChangePage(section); ctx.onChangePage(Section.HOME);
Navigator.pop(context); Navigator.pop(context);
}, },
),
ListTile(
title: const Text('Shuffle'),
leading: const Icon(Icons.update),
onTap: () {
ctx.onChangePage(Section.SHUFFLE);
Navigator.pop(context);
},
),
ListTile(
title: const Text('Categories'),
leading: const Icon(Icons.category),
onTap: () {
ctx.onChangePage(Section.CATEGORIE);
Navigator.pop(context);
},
),
ListTile(
title: const Text('Actors'),
leading: const Icon(Icons.people),
onTap: () {
ctx.onChangePage(Section.ACTOR);
Navigator.pop(context);
},
),
if (featureCtx.tvShowEnabled)
ListTile(
title: const Text('TV Shows'),
leading: const Icon(Icons.tv),
onTap: () {
ctx.onChangePage(Section.TVSHOW);
Navigator.pop(context);
},
),
ListTile(
title: const Text('Settings'),
leading: const Icon(Icons.settings),
onTap: () {
ctx.onChangePage(Section.SETTING);
Navigator.pop(context);
},
),
]),
); );
} }
} }

View File

@ -23,7 +23,7 @@ class _LoginContainerState extends State<LoginContainer> {
} }
void _init() async { void _init() async {
final token = await getToken(); final token = await Token.getInstance().getToken();
Log.i("The token value is $token"); Log.i("The token value is $token");
if (token != null) { if (token != null) {
setState(() { setState(() {

View File

@ -3,9 +3,8 @@ import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import '../api/settings_api.dart'; import '../api/settings_api.dart';
import '../db/settings_db.dart'; import '../api/token.dart';
import '../log/log.dart'; import '../log/log.dart';
import 'login_context.dart'; import 'login_context.dart';
@ -45,13 +44,12 @@ class _LoginScreenState extends State<LoginScreen> {
final json = jsonDecode(resp.body); final json = jsonDecode(resp.body);
final token = json["Token"]; final token = json["Token"];
SettingsT settings = await SettingsDB.getInstance().getSettings(); // todo bit hacky
settings.domain = domain; Token.getInstance().setToken(token, domain, "temp", "temp");
settings.token = token; // we need to call this twice because we need for the loadInitialData an api token
SettingsDB.getInstance().setSettings(settings); final initalData = await loadInitialData();
// we need two steps here because we need an authenticated api call for the videopath Token.getInstance()
settings.videopath = (await loadInitialData()).videoPath; .setToken(token, domain, initalData.videoPath, initalData.tvShowPath);
SettingsDB.getInstance().setSettings(settings);
LoginContext.of(context).onLoggin(true); LoginContext.of(context).onLoggin(true);
return ""; return "";

View File

@ -1,7 +1,6 @@
import 'package:flutter/foundation.dart'; import "package:dart_vlc/dart_vlc.dart";
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:media_kit/media_kit.dart';
import 'app.dart'; import 'app.dart';
import 'db/database.dart'; import 'db/database.dart';
@ -11,17 +10,12 @@ import 'utils/platform.dart';
void main() async { void main() async {
Log.i("App init!"); Log.i("App init!");
WidgetsFlutterBinding.ensureInitialized(); if (isDesktop()) {
MediaKit.ensureInitialized(); DartVLC.initialize();
} else {
if (!kIsWeb && !isDesktop()) {
Log.i("init device info");
await loadDeviceInfo(); await loadDeviceInfo();
} }
Db().init();
Log.i("Mediakit initialized");
await Db().init();
runApp(Shortcuts(shortcuts: <LogicalKeySet, Intent>{ runApp(Shortcuts(shortcuts: <LogicalKeySet, Intent>{
LogicalKeySet(LogicalKeyboardKey.select): ActivateIntent(), LogicalKeySet(LogicalKeyboardKey.select): ActivateIntent(),

View File

@ -1,11 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:openmediacentermobile/db/settings_db.dart'; import '../utils/file_formatter.dart';
import '../api/token.dart'; import '../api/token.dart';
import '../db/database.dart'; import '../db/database.dart';
import '../drawer/my_drawer.dart'; import '../drawer/my_drawer.dart';
import '../login/login_context.dart'; import '../login/login_context.dart';
import '../utils/file_formatter.dart';
class SettingsScreen extends StatefulWidget { class SettingsScreen extends StatefulWidget {
const SettingsScreen({Key? key}) : super(key: key); const SettingsScreen({Key? key}) : super(key: key);
@ -25,7 +24,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
dbsize = v; dbsize = v;
})); }));
getToken().then((value) => setState(() { Token.getInstance().getToken().then((value) => setState(() {
serverUrl = value?.domain ?? "unknown"; serverUrl = value?.domain ?? "unknown";
})); }));
} }
@ -54,8 +53,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
loginCtx.onLoggin(false); loginCtx.onLoggin(false);
SettingsDB.getInstance() Token.getInstance().setToken("", "", "", "");
.setSettings(SettingsT("", "", "", 0));
Db().clear(); Db().clear();
}, },
child: Text("Logout")) child: Text("Logout"))

View File

@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
class TVShowScreen extends StatefulWidget {
const TVShowScreen({Key? key}) : super(key: key);
@override
State<TVShowScreen> createState() => _TVShowScreenState();
}
class _TVShowScreenState extends State<TVShowScreen> {
@override
Widget build(BuildContext context) {
return Container();
}
}

View File

@ -3,9 +3,9 @@ import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import '../utils/platform.dart';
import '../screen_loading.dart'; import '../screen_loading.dart';
import '../types/video.dart'; import '../types/video.dart';
import '../utils/platform.dart';
import 'preview_tile.dart'; import 'preview_tile.dart';
class PreviewGrid extends StatefulWidget { class PreviewGrid extends StatefulWidget {
@ -60,7 +60,7 @@ class _PreviewGridState extends State<PreviewGrid> {
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text("Error" + snapshot.error.toString()), Text("Error"),
TextButton( TextButton(
onPressed: () => loadData(), child: Text("Reload page")) onPressed: () => loadData(), child: Text("Reload page"))
], ],
@ -78,9 +78,7 @@ class _PreviewGridState extends State<PreviewGrid> {
Widget _mainGrid(List<VideoT> data, double width) { Widget _mainGrid(List<VideoT> data, double width) {
return Stack( return Stack(
children: [ children: [
Container( Column(
color: Color(0xff999999),
child: Column(
children: [ children: [
if (widget.headerBuilder != null) widget.headerBuilder!(this), if (widget.headerBuilder != null) widget.headerBuilder!(this),
data.length > 0 data.length > 0
@ -125,7 +123,6 @@ class _PreviewGridState extends State<PreviewGrid> {
if (widget.footerBuilder != null) widget.footerBuilder!(this), if (widget.footerBuilder != null) widget.footerBuilder!(this),
], ],
), ),
),
if (_previewImage != null) ..._buildPreviewImage(), if (_previewImage != null) ..._buildPreviewImage(),
], ],
); );

View File

@ -5,8 +5,8 @@ import 'package:sqflite/sqflite.dart';
import '../api/video_api.dart'; import '../api/video_api.dart';
import '../db/database.dart'; import '../db/database.dart';
import '../log/log.dart'; import '../log/log.dart';
import '../types/video.dart';
import '../utils/platform.dart'; import '../utils/platform.dart';
import '../types/video.dart';
import '../video_screen/videoscreen.dart'; import '../video_screen/videoscreen.dart';
class PreviewTile extends StatefulWidget { class PreviewTile extends StatefulWidget {
@ -76,7 +76,6 @@ class _PreviewTileState extends State<PreviewTile> {
); );
// precache image to avoid loading time to render image // precache image to avoid loading time to render image
if(context.mounted)
await precacheImage(img.image, context); await precacheImage(img.image, context);
return img; return img;
@ -104,28 +103,18 @@ class _PreviewTileState extends State<PreviewTile> {
child: Stack( child: Stack(
children: [ children: [
Container( Container(
color: const Color(0xff3f3f3f),
child: Column( child: Column(
children: [ children: [
ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: image,
),
const SizedBox(
height: 3,
),
Text( Text(
widget.dta.title, widget.dta.title,
style: TextStyle( style: TextStyle(fontSize: isTV() ? 8 : 10.5),
fontSize: isTV() ? 8 : 10.5, color: const Color(0xffe9e9e9)),
overflow: TextOverflow.clip, overflow: TextOverflow.clip,
maxLines: 1, maxLines: 1,
), ),
const SizedBox( image
height: 3,
),
], ],
), ),
color: Color(0x6a94a6ff),
), ),
Positioned.fill( Positioned.fill(
child: Material( child: Material(
@ -158,9 +147,7 @@ class _PreviewTileState extends State<PreviewTile> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ConstrainedBox( return FutureBuilder<Image>(
constraints: const BoxConstraints(minHeight: 200, minWidth: 200),
child: FutureBuilder<Image>(
future: _preview, // a previously-obtained Future<String> or null future: _preview, // a previously-obtained Future<String> or null
builder: (BuildContext context, AsyncSnapshot<Image> snapshot) { builder: (BuildContext context, AsyncSnapshot<Image> snapshot) {
if (snapshot.connectionState != ConnectionState.done) { if (snapshot.connectionState != ConnectionState.done) {
@ -175,7 +162,6 @@ class _PreviewTileState extends State<PreviewTile> {
return _buildLoader(); return _buildLoader();
} }
}, },
),
); );
} }
} }

View File

@ -0,0 +1,92 @@
import 'package:flutter/material.dart';
import 'package:openmediacentermobile/login/login_context.dart';
import '../api/settings_api.dart';
// todo maybe instead of feature context a context for all settings?
class FeatureContext extends InheritedWidget {
const FeatureContext(this.tvShowEnabled, this.fullDeleteEnabled,
{Key? key, required Widget child})
: super(key: key, child: child);
final bool tvShowEnabled;
final bool fullDeleteEnabled;
static FeatureContext of(BuildContext context) {
final FeatureContext? result =
context.dependOnInheritedWidgetOfExactType<FeatureContext>();
assert(result != null, 'No LoginContext found in context');
return result!;
}
@override
bool updateShouldNotify(FeatureContext old) {
return tvShowEnabled != old.tvShowEnabled ||
fullDeleteEnabled != old.fullDeleteEnabled;
}
}
class FeatureContainer extends StatefulWidget {
const FeatureContainer({Key? key, required this.child}) : super(key: key);
final Widget child;
@override
State<FeatureContainer> createState() => _FeatureContainerState();
}
class _FeatureContainerState extends State<FeatureContainer> {
bool? tvShowEnabled;
bool? fullDeleteEnabled;
bool fetcherror = false;
@override
void initState() {
loadInitialData()
.then((value) => setState(
() {
fullDeleteEnabled = value.fullDeleteEnabled;
tvShowEnabled = value.tvShowEnabled;
},
))
.catchError((err) => setState(() => fetcherror = true));
super.initState();
}
@override
Widget build(BuildContext context) {
if (fetcherror) {
final loginctx = LoginContext.of(context);
return MaterialApp(
home: Scaffold(
body: Center(
child:
Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Text("Fetch error"),
MaterialButton(
onPressed: () => loginctx.onLoggin(false),
child: Text("Logout"),
)
]),
),
),
);
} else if (tvShowEnabled == null || fullDeleteEnabled == null) {
return MaterialApp(
home: Scaffold(
body: Container(
color: Colors.white,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [CircularProgressIndicator(), Text("loading features")],
)),
),
),
);
} else {
return FeatureContext(tvShowEnabled!, fullDeleteEnabled!,
child: widget.child);
}
}
}

View File

@ -1,17 +1,18 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:media_kit/media_kit.dart';
import 'package:media_kit_video/media_kit_video.dart';
import 'package:openmediacentermobile/db/settings_db.dart';
import '../api/token.dart';
import '../api/video_api.dart'; import '../api/video_api.dart';
import '../utils/platform.dart';
import '../screen_loading.dart'; import '../screen_loading.dart';
import '../types/video.dart'; import '../types/video.dart';
import '../types/video_data.dart'; import '../types/video_data.dart';
import '../utils/platform.dart';
import 'info_view.dart'; import 'info_view.dart';
import 'videoscreen_desktop.dart'
if (dart.library.html) 'videoscreen_mobile.dart';
import 'videoscreen_mobile.dart';
class VideoScreen extends StatefulWidget { class VideoScreen extends StatefulWidget {
const VideoScreen({Key? key, required this.metaData}) : super(key: key); const VideoScreen({Key? key, required this.metaData}) : super(key: key);
final VideoT metaData; final VideoT metaData;
@ -27,19 +28,16 @@ class _VideoScreenState extends State<VideoScreen> {
PageController _controller = PageController( PageController _controller = PageController(
initialPage: 0, initialPage: 0,
); );
// Create a [Player] to control playback.
late final player = Player();
// Create a [VideoController] to handle video output from [Player].
late final controller = VideoController(player);
String url = ""; String url = "";
void initPlayer() async { void initPlayer() async {
final videodata = await _videoData; final videodata = await _videoData;
final settings = await SettingsDB.getInstance().getSettings(); final token = await Token.getInstance().getToken();
final path = settings.domain + settings.videopath + videodata.movieUrl; if (token == null) return;
player.open(Media(path));
final path = token.domain + token.videoPath + videodata.movieUrl;
url = path; url = path;
} }
@ -55,7 +53,6 @@ class _VideoScreenState extends State<VideoScreen> {
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
player.dispose();
_controller.dispose(); _controller.dispose();
_appBarTimer?.cancel(); _appBarTimer?.cancel();
} }
@ -115,7 +112,13 @@ class _VideoScreenState extends State<VideoScreen> {
controller: _controller, controller: _controller,
children: [ children: [
Center( Center(
child: Video(controller: controller)), child: isDesktop()
? VideoScreenDesktop(
url: url,
)
: VideoScreenMobile(
url: url,
)),
InfoView( InfoView(
videoId: widget.metaData.id, videoId: widget.metaData.id,
) )
@ -130,10 +133,7 @@ class _VideoScreenState extends State<VideoScreen> {
leading: new IconButton( leading: new IconButton(
icon: new Icon(Icons.arrow_back_ios, icon: new Icon(Icons.arrow_back_ios,
color: Colors.grey), color: Colors.grey),
onPressed: () async { onPressed: () => Navigator.of(context).pop(),
await player.stop();
Navigator.of(context).pop();
},
), ),
backgroundColor: backgroundColor:
Theme.of(context).primaryColor.withOpacity(0.3), Theme.of(context).primaryColor.withOpacity(0.3),

View File

@ -0,0 +1,52 @@
import 'dart:math';
import 'package:dart_vlc/dart_vlc.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class VideoScreenDesktop extends StatefulWidget {
const VideoScreenDesktop({Key? key, required this.url}) : super(key: key);
final String url;
@override
State<VideoScreenDesktop> createState() => _VideoScreenDesktopState();
}
class _VideoScreenDesktopState extends State<VideoScreenDesktop> {
Player _player = Player(id: Random().nextInt(0x7fffffff));
@override
Widget build(BuildContext context) {
return Video(
player: _player,
showFullscreenButton: true,
scale: 1.0, // default
showControls: true);
}
@override
void initState() {
super.initState();
final media2 = Media.network(widget.url);
_player.open(
media2,
autoStart: true, // default
);
RawKeyboard.instance.addListener((value) {
if (value.logicalKey == LogicalKeyboardKey.arrowRight) {
_player.seek(_player.position.position! + const Duration(seconds: 5));
} else if (value.logicalKey == LogicalKeyboardKey.arrowLeft) {
_player.seek(_player.position.position! + const Duration(seconds: -5));
}
});
}
@override
void dispose() {
super.dispose();
_player.dispose();
}
}

View File

@ -0,0 +1,59 @@
import 'package:chewie/chewie.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
class VideoScreenMobile extends StatefulWidget {
const VideoScreenMobile({Key? key, required this.url}) : super(key: key);
final String url;
@override
State<VideoScreenMobile> createState() => _VideoScreenMobileState();
}
class _VideoScreenMobileState extends State<VideoScreenMobile> {
ChewieController? _chewieController;
@override
Widget build(BuildContext context) {
if (_chewieController == null) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: const [CircularProgressIndicator(), Text("loading...")],
);
}
return Chewie(
controller: _chewieController!,
);
}
@override
void dispose() {
super.dispose();
_chewieController?.videoPlayerController.dispose();
_chewieController?.dispose();
}
@override
void initState() {
super.initState();
_init();
}
void _init() async {
final VideoPlayerController _controller =
VideoPlayerController.network(widget.url);
await _controller.initialize();
_chewieController = ChewieController(
videoPlayerController: _controller,
autoPlay: true,
looping: true,
allowFullScreen: true,
allowMuting: true,
allowPlaybackSpeedChanging: true,
zoomAndPan: true);
setState(() {});
}
}

View File

@ -6,14 +6,22 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h> #include <dart_vlc/dart_vlc_plugin.h>
#include <media_kit_video/media_kit_video_plugin.h> #include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
#include <screen_retriever/screen_retriever_plugin.h>
#include <window_manager/window_manager_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar = g_autoptr(FlPluginRegistrar) dart_vlc_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "DartVlcPlugin");
media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar); dart_vlc_plugin_register_with_registrar(dart_vlc_registrar);
g_autoptr(FlPluginRegistrar) media_kit_video_registrar = g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
media_kit_video_plugin_register_with_registrar(media_kit_video_registrar); flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
g_autoptr(FlPluginRegistrar) window_manager_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin");
window_manager_plugin_register_with_registrar(window_manager_registrar);
} }

View File

@ -3,12 +3,13 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
media_kit_libs_linux dart_vlc
media_kit_video flutter_secure_storage_linux
screen_retriever
window_manager
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST
media_kit_native_event_loop
) )
set(PLUGIN_BUNDLED_LIBRARIES) set(PLUGIN_BUNDLED_LIBRARIES)

File diff suppressed because it is too large Load Diff

View File

@ -34,23 +34,17 @@ dependencies:
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
flutter_secure_storage: ^6.0.0
logger: ^1.1.0 logger: ^1.1.0
http: ^0.13.4 http: ^0.13.4
flutter_staggered_grid_view: ^0.6.1 flutter_staggered_grid_view: ^0.6.1
device_info_plus: ^8.0.0 dart_vlc: ^0.3.0
device_info_plus: ^4.0.0
video_player: ^2.3.0
chewie: ^1.3.2
sqflite: ^2.0.3+1 sqflite: ^2.0.3+1
path: ^1.8.1 path: ^1.8.1
sqflite_common_ffi: ^2.1.1+1 sqflite_common_ffi: ^2.1.1+1
sqflite_common_ffi_web: '^0.3.6'
media_kit: ^1.0.2 # Primary package.
media_kit_video: ^1.0.2 # For video rendering.
media_kit_native_event_loop: ^1.0.6 # Support for higher number of concurrent instances & better performance.
media_kit_libs_android_video: ^1.1.1 # Android package for video native libraries.
media_kit_libs_linux: ^1.0.2 # GNU/Linux dependency package.
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: