Compare commits
1 Commits
master
...
better_pla
Author | SHA1 | Date | |
---|---|---|---|
dc87a4369e |
26
.metadata
26
.metadata
@ -1,30 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# 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:
|
||||
revision: f92f44110e87bad5ff168335c36da6f6053036e6
|
||||
channel: stable
|
||||
revision: 37de8a2a9ad217ec9d731f8af0bd7c83e8f4980c
|
||||
channel: master
|
||||
|
||||
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'
|
||||
|
@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion 33
|
||||
compileSdkVersion flutter.compileSdkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
|
@ -1,12 +1,12 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.6.10'
|
||||
ext.kotlin_version = '1.4.32'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.1.2'
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
|
||||
|
@ -1,6 +1,5 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import '../log/log.dart';
|
||||
import '../types/actor.dart';
|
||||
import 'api.dart';
|
||||
|
||||
@ -11,26 +10,3 @@ Future<List<Actor>> loadAllActors() async {
|
||||
final actors = d.map((e) => Actor.fromJson(e)).toList(growable: false);
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import 'token.dart';
|
||||
|
||||
class TokenException implements Exception {
|
||||
@ -12,7 +11,7 @@ class TokenException implements Exception {
|
||||
class API {
|
||||
static Future<String> query(
|
||||
String apinode, String action, Object payload) async {
|
||||
final t = await getToken();
|
||||
final t = await Token.getInstance().getToken();
|
||||
if (t != null) {
|
||||
final resp = await http.post(
|
||||
Uri.parse(t.domain + '/api/$apinode/$action'),
|
||||
|
@ -1,42 +0,0 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'api.dart';
|
||||
|
||||
class InitialData {
|
||||
bool darkMode;
|
||||
bool password;
|
||||
String mediacenterName;
|
||||
String videoPath;
|
||||
String tvShowPath;
|
||||
bool tvShowEnabled;
|
||||
bool fullDeleteEnabled;
|
||||
|
||||
InitialData(
|
||||
this.darkMode,
|
||||
this.password,
|
||||
this.mediacenterName,
|
||||
this.videoPath,
|
||||
this.tvShowPath,
|
||||
this.tvShowEnabled,
|
||||
this.fullDeleteEnabled);
|
||||
|
||||
factory InitialData.fromJson(dynamic json) {
|
||||
return InitialData(
|
||||
json['DarkMode'] as bool,
|
||||
json['Pasword'] as bool,
|
||||
json['MediacenterName'] as String,
|
||||
json['VideoPath'] as String,
|
||||
json['TVShowPath'] as String,
|
||||
json['TVShowEnabled'] as bool,
|
||||
json['FullDeleteEnabled'] as bool,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<InitialData> loadInitialData() async {
|
||||
final data = await API.query("settings", "loadInitialData", {});
|
||||
|
||||
final d = jsonDecode(data);
|
||||
final video = InitialData.fromJson(d);
|
||||
return video;
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
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");
|
||||
}
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
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 {
|
||||
String token;
|
||||
@ -9,11 +12,47 @@ class TokenT {
|
||||
TokenT(this.token, this.domain);
|
||||
}
|
||||
|
||||
Future<TokenT?> getToken() async {
|
||||
final settings = await SettingsDB.getInstance().getSettings();
|
||||
if (settings.token == "" || settings.domain == "") {
|
||||
return null;
|
||||
} else {
|
||||
return TokenT(settings.token, settings.domain);
|
||||
class Token {
|
||||
static final Token _token = Token._();
|
||||
final _storage = const FlutterSecureStorage();
|
||||
|
||||
String _tokenval = "";
|
||||
String _domain = "";
|
||||
|
||||
static Token getInstance() {
|
||||
return _token;
|
||||
}
|
||||
|
||||
Future<TokenT?> getToken() async {
|
||||
var completer = Completer<TokenT?>();
|
||||
|
||||
if (_tokenval == "" || _domain == "") {
|
||||
Log.d("reading token store");
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
final token = await _storage.read(key: 'jwt');
|
||||
final domain = await _storage.read(key: 'domain');
|
||||
|
||||
// check if value is defined in phone store
|
||||
if (token != null && domain != null) {
|
||||
_tokenval = token;
|
||||
_domain = domain;
|
||||
completer.complete(TokenT(token, domain));
|
||||
} else {
|
||||
Log.d("no token defined");
|
||||
completer.complete(null);
|
||||
}
|
||||
} else {
|
||||
completer.complete(TokenT(_tokenval, _domain));
|
||||
}
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
void setToken(String token, String domain) {
|
||||
_tokenval = token;
|
||||
_domain = domain;
|
||||
_storage.write(key: 'jwt', value: token);
|
||||
_storage.write(key: 'domain', value: domain);
|
||||
}
|
||||
|
||||
Token._();
|
||||
}
|
||||
|
@ -1,11 +1,5 @@
|
||||
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 'api.dart';
|
||||
|
||||
@ -16,55 +10,3 @@ Future<VideoData> loadVideoData(int videoId) async {
|
||||
final video = VideoData.fromJson(d);
|
||||
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));
|
||||
}
|
||||
|
@ -5,14 +5,13 @@ import 'package:openmediacentermobile/log/log.dart';
|
||||
import 'package:openmediacentermobile/login/login_screen.dart';
|
||||
|
||||
import 'drawer/drawer_page.dart';
|
||||
import 'login/login_context.dart';
|
||||
import 'login/logincontext.dart';
|
||||
|
||||
class AppScrollBehavior extends MaterialScrollBehavior {
|
||||
@override
|
||||
Set<PointerDeviceKind> get dragDevices => {
|
||||
PointerDeviceKind.touch,
|
||||
PointerDeviceKind.mouse,
|
||||
PointerDeviceKind.trackpad
|
||||
};
|
||||
}
|
||||
|
||||
@ -29,8 +28,6 @@ class App extends StatelessWidget {
|
||||
return const MaterialApp(home: LoginScreen());
|
||||
} else {
|
||||
return MaterialApp(
|
||||
theme: ThemeData(
|
||||
appBarTheme: AppBarTheme(backgroundColor: Color(0xff0d0d0d))),
|
||||
scrollBehavior: AppScrollBehavior(),
|
||||
home: DrawerPage(
|
||||
title: 'OpenMediaCenter',
|
||||
|
@ -1,47 +1,43 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart' as nativeffi;
|
||||
import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart' as webffi;
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
|
||||
import '../log/log.dart';
|
||||
|
||||
class 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';
|
||||
if (defaultTargetPlatform == TargetPlatform.android ||
|
||||
defaultTargetPlatform == TargetPlatform.iOS) {
|
||||
dbpath = join(await getDatabasesPath(), dbpath);
|
||||
} else if(kIsWeb) {
|
||||
databaseFactory = webffi.databaseFactoryFfiWeb;
|
||||
} else {
|
||||
|
||||
|
||||
// Initialize FFI
|
||||
nativeffi.sqfliteFfiInit();
|
||||
sqfliteFfiInit();
|
||||
// Change the default factory
|
||||
databaseFactory = nativeffi.databaseFactoryFfi;
|
||||
databaseFactory = databaseFactoryFfi;
|
||||
}
|
||||
|
||||
_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,
|
||||
onCreate: (db, version) {
|
||||
final batch = db.batch();
|
||||
batch.execute(
|
||||
'CREATE TABLE previews(id INTEGER PRIMARY KEY, thumbnail BLOB);',
|
||||
// Run the CREATE TABLE statement on the database.
|
||||
return db.execute(
|
||||
'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
|
||||
// path to perform database upgrades and downgrades.
|
||||
version: 2,
|
||||
version: 1,
|
||||
);
|
||||
}
|
||||
|
||||
@ -54,16 +50,10 @@ class Db {
|
||||
|
||||
/// get db size in bytes
|
||||
Future<int> getDbSize() async {
|
||||
final batch = _db.batch();
|
||||
batch.rawQuery("pragma page_count;");
|
||||
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 cnt = (await Db().db().rawQuery("pragma page_count;"))[0]
|
||||
["page_count"] as int;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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._();
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
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';
|
||||
|
||||
@ -15,6 +19,16 @@ class AddActorDialog extends StatefulWidget {
|
||||
class _AddActorDialogState extends State<AddActorDialog> {
|
||||
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
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -32,15 +46,13 @@ class _AddActorDialogState extends State<AddActorDialog> {
|
||||
return Text("Error");
|
||||
} else if (snapshot.hasData) {
|
||||
final data = snapshot.data! as List<Actor>;
|
||||
data.sort((a, b) =>
|
||||
a.name.toLowerCase().compareTo(b.name.toLowerCase()));
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: data
|
||||
.map((e) => ListTile(
|
||||
title: Text(e.name),
|
||||
onTap: () async {
|
||||
await addActorToVideo(e.actorId, widget.movieId);
|
||||
await addActorToVideo(e.actorId);
|
||||
Navigator.pop(context, e);
|
||||
},
|
||||
))
|
||||
|
@ -1,6 +1,9 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../api/tag_api.dart';
|
||||
import '../api/api.dart';
|
||||
import '../log/log.dart';
|
||||
import '../screen_loading.dart';
|
||||
import '../types/tag.dart';
|
||||
|
||||
@ -15,6 +18,24 @@ class AddTagDialog extends StatefulWidget {
|
||||
class _AddTagDialogState extends State<AddTagDialog> {
|
||||
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
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -32,10 +53,6 @@ class _AddTagDialogState extends State<AddTagDialog> {
|
||||
return Text("Error");
|
||||
} else if (snapshot.hasData) {
|
||||
final data = snapshot.data! as List<Tag>;
|
||||
data.sort(
|
||||
(a, b) =>
|
||||
a.tagName.toLowerCase().compareTo(b.tagName.toLowerCase()),
|
||||
);
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: data
|
||||
@ -43,7 +60,7 @@ class _AddTagDialogState extends State<AddTagDialog> {
|
||||
(e) => ListTile(
|
||||
title: Text(e.tagName),
|
||||
onTap: () async {
|
||||
await addTagToVideo(e.tagId, widget.movieId);
|
||||
await addTagToVideo(e.tagId);
|
||||
Navigator.pop(context, e);
|
||||
},
|
||||
),
|
||||
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:openmediacentermobile/navigation/settings_screen.dart';
|
||||
import '../navigation/actor_screen.dart';
|
||||
import '../navigation/categorie_screen.dart';
|
||||
import '../navigation/shuffle_screen.dart';
|
||||
import '../navigation/shufflescreen.dart';
|
||||
import '../navigation/video_feed.dart';
|
||||
import 'drawer_context.dart';
|
||||
|
||||
|
@ -7,32 +7,52 @@ class MyDrawer extends StatelessWidget {
|
||||
const MyDrawer({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Drawer(
|
||||
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) {
|
||||
Widget build(BuildContext context) {
|
||||
final ctx = DrawerContext.of(context);
|
||||
|
||||
return ListTile(
|
||||
title: Text(text, style: TextStyle(color: Color(0xffe9e9e9))),
|
||||
leading: Icon(icon, color: Color(0xffe9e9e9)),
|
||||
onTap: () {
|
||||
ctx.onChangePage(section);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
return Drawer(
|
||||
child: ListView(children: [
|
||||
ListTile(
|
||||
title: const Text('Home'),
|
||||
leading: const Icon(Icons.home),
|
||||
onTap: () {
|
||||
ctx.onChangePage(Section.HOME);
|
||||
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);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Settings'),
|
||||
leading: const Icon(Icons.settings),
|
||||
onTap: () {
|
||||
ctx.onChangePage(Section.SETTING);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LoginContext extends InheritedWidget {
|
||||
const LoginContext(
|
||||
{Key? key,
|
||||
required Widget child,
|
||||
required this.loggedIn,
|
||||
required this.onLoggin})
|
||||
: super(key: key, child: child);
|
||||
|
||||
final bool loggedIn;
|
||||
final void Function(bool) onLoggin;
|
||||
|
||||
static LoginContext of(BuildContext context) {
|
||||
final LoginContext? result =
|
||||
context.dependOnInheritedWidgetOfExactType<LoginContext>();
|
||||
assert(result != null, 'No LoginContext found in context');
|
||||
return result!;
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(LoginContext old) {
|
||||
return loggedIn != old.loggedIn;
|
||||
}
|
||||
}
|
@ -3,11 +3,9 @@ import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../api/settings_api.dart';
|
||||
import '../db/settings_db.dart';
|
||||
import '../log/log.dart';
|
||||
import 'login_context.dart';
|
||||
import 'package:openmediacentermobile/api/token.dart';
|
||||
import 'package:openmediacentermobile/log/log.dart';
|
||||
import 'package:openmediacentermobile/login/logincontext.dart';
|
||||
|
||||
class LoginScreen extends StatefulWidget {
|
||||
const LoginScreen({Key? key}) : super(key: key);
|
||||
@ -41,46 +39,19 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
|
||||
if (resp.statusCode != 200) {
|
||||
return "error" + resp.body;
|
||||
|
||||
// compl.complete(resp.body);
|
||||
} else {
|
||||
final json = jsonDecode(resp.body);
|
||||
final token = json["Token"];
|
||||
|
||||
SettingsT settings = await SettingsDB.getInstance().getSettings();
|
||||
settings.domain = domain;
|
||||
settings.token = token;
|
||||
SettingsDB.getInstance().setSettings(settings);
|
||||
// we need two steps here because we need an authenticated api call for the videopath
|
||||
settings.videopath = (await loadInitialData()).videoPath;
|
||||
SettingsDB.getInstance().setSettings(settings);
|
||||
Token.getInstance().setToken(token, domain);
|
||||
LoginContext.of(context).onLoggin(true);
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> onLoginClick() async {
|
||||
Log.d("logging in");
|
||||
final pwd = _passwordTextController.value.text;
|
||||
final domain = _domainTextController.value.text;
|
||||
|
||||
var err = "";
|
||||
if (domain.startsWith("https://") || domain.startsWith("http://")) {
|
||||
err = await login(pwd, domain);
|
||||
if (err.isEmpty) return;
|
||||
} else {
|
||||
// try to auto infering domain prefix
|
||||
err = await login(pwd, "https://" + domain);
|
||||
if (err.isEmpty) return;
|
||||
err = await login(pwd, "http://" + domain);
|
||||
if (err.isEmpty) return;
|
||||
}
|
||||
|
||||
Log.i(err);
|
||||
setState(() {
|
||||
error = err;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_domainTextController.dispose();
|
||||
@ -161,7 +132,33 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
backgroundColor: const Color(0xff4c505b),
|
||||
child: IconButton(
|
||||
color: Colors.white,
|
||||
onPressed: () async => await onLoginClick(),
|
||||
onPressed: () async {
|
||||
Log.d("clickkked");
|
||||
final pwd =
|
||||
_passwordTextController.value.text;
|
||||
final domain =
|
||||
_domainTextController.value.text;
|
||||
|
||||
var err = "";
|
||||
if (domain.startsWith("https://") ||
|
||||
domain.startsWith("http://")) {
|
||||
err = await login(pwd, domain);
|
||||
if (err.isEmpty) return;
|
||||
} else {
|
||||
// try to auto infering domain prefix
|
||||
err = await login(
|
||||
pwd, "https://" + domain);
|
||||
if (err.isEmpty) return;
|
||||
err = await login(
|
||||
pwd, "http://" + domain);
|
||||
if (err.isEmpty) return;
|
||||
}
|
||||
|
||||
Log.i(err);
|
||||
setState(() {
|
||||
error = err;
|
||||
});
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.arrow_forward,
|
||||
)),
|
||||
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
||||
|
||||
import '../api/token.dart';
|
||||
import '../log/log.dart';
|
||||
import 'login_context.dart';
|
||||
|
||||
class LoginContainer extends StatefulWidget {
|
||||
const LoginContainer({Key? key, required this.child}) : super(key: key);
|
||||
@ -19,23 +18,22 @@ class _LoginContainerState extends State<LoginContainer> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_init();
|
||||
}
|
||||
|
||||
void _init() async {
|
||||
final token = await getToken();
|
||||
Log.i("The token value is $token");
|
||||
if (token != null) {
|
||||
setState(() {
|
||||
_loggedIn = true;
|
||||
_loading = false;
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
_loggedIn = false;
|
||||
_loading = false;
|
||||
});
|
||||
}
|
||||
final token = Token.getInstance();
|
||||
token.getToken().then((value) {
|
||||
Log.i("The token value is $value");
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
_loggedIn = true;
|
||||
_loading = false;
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
_loggedIn = false;
|
||||
_loading = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@ -60,3 +58,27 @@ class _LoginContainerState extends State<LoginContainer> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LoginContext extends InheritedWidget {
|
||||
const LoginContext(
|
||||
{Key? key,
|
||||
required Widget child,
|
||||
required this.loggedIn,
|
||||
required this.onLoggin})
|
||||
: super(key: key, child: child);
|
||||
|
||||
final bool loggedIn;
|
||||
final void Function(bool) onLoggin;
|
||||
|
||||
static LoginContext of(BuildContext context) {
|
||||
final LoginContext? result =
|
||||
context.dependOnInheritedWidgetOfExactType<LoginContext>();
|
||||
assert(result != null, 'No LoginContext found in context');
|
||||
return result!;
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(LoginContext old) {
|
||||
return loggedIn != old.loggedIn;
|
||||
}
|
||||
}
|
@ -1,27 +1,21 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import "package:dart_vlc/dart_vlc.dart";
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:media_kit/media_kit.dart';
|
||||
|
||||
import 'app.dart';
|
||||
import 'db/database.dart';
|
||||
import 'log/log.dart';
|
||||
import 'login/login_container.dart';
|
||||
import 'login/logincontext.dart';
|
||||
import 'utils/platform.dart';
|
||||
|
||||
void main() async {
|
||||
Log.i("App init!");
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
MediaKit.ensureInitialized();
|
||||
|
||||
if (!kIsWeb && !isDesktop()) {
|
||||
Log.i("init device info");
|
||||
if (isDesktop()) {
|
||||
DartVLC.initialize();
|
||||
} else {
|
||||
await loadDeviceInfo();
|
||||
}
|
||||
|
||||
Log.i("Mediakit initialized");
|
||||
|
||||
await Db().init();
|
||||
Db().init();
|
||||
|
||||
runApp(Shortcuts(shortcuts: <LogicalKeySet, Intent>{
|
||||
LogicalKeySet(LogicalKeyboardKey.select): ActivateIntent(),
|
||||
|
@ -1,9 +1,11 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import '../api/tag_api.dart';
|
||||
import '../preview/tag_tile.dart';
|
||||
import 'package:openmediacentermobile/preview/tag_tile.dart';
|
||||
import '../drawer/my_drawer.dart';
|
||||
import '../screen_loading.dart';
|
||||
|
||||
import '../api/api.dart';
|
||||
import '../types/tag.dart';
|
||||
|
||||
class CategorieScreen extends StatefulWidget {
|
||||
@ -16,10 +18,18 @@ class CategorieScreen extends StatefulWidget {
|
||||
class _CategorieScreenState extends State<CategorieScreen> {
|
||||
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
|
||||
void initState() {
|
||||
super.initState();
|
||||
_categories = loadAllTags();
|
||||
_categories = loadVideoData();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -1,11 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:openmediacentermobile/db/settings_db.dart';
|
||||
import 'package:openmediacentermobile/utils/FileFormatter.dart';
|
||||
|
||||
import '../api/token.dart';
|
||||
import '../db/database.dart';
|
||||
import '../drawer/my_drawer.dart';
|
||||
import '../login/login_context.dart';
|
||||
import '../utils/file_formatter.dart';
|
||||
import '../login/logincontext.dart';
|
||||
|
||||
class SettingsScreen extends StatefulWidget {
|
||||
const SettingsScreen({Key? key}) : super(key: key);
|
||||
@ -16,7 +15,6 @@ class SettingsScreen extends StatefulWidget {
|
||||
|
||||
class _SettingsScreenState extends State<SettingsScreen> {
|
||||
int dbsize = 0;
|
||||
String serverUrl = "";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -24,10 +22,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
Db().getDbSize().then((v) => setState(() {
|
||||
dbsize = v;
|
||||
}));
|
||||
|
||||
getToken().then((value) => setState(() {
|
||||
serverUrl = value?.domain ?? "unknown";
|
||||
}));
|
||||
}
|
||||
|
||||
@override
|
||||
@ -40,7 +34,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Text("Current server: $serverUrl"),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
await Db().clear();
|
||||
@ -54,8 +47,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
loginCtx.onLoggin(false);
|
||||
SettingsDB.getInstance()
|
||||
.setSettings(SettingsT("", "", "", 0));
|
||||
Token.getInstance().setToken("", "");
|
||||
Db().clear();
|
||||
},
|
||||
child: Text("Logout"))
|
||||
|
@ -1,9 +1,12 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../api/video_api.dart';
|
||||
import '../drawer/my_drawer.dart';
|
||||
import '../preview/preview_grid.dart';
|
||||
import '../api/api.dart';
|
||||
import '../utils/platform.dart';
|
||||
import '../types/video.dart';
|
||||
|
||||
class ShuffleScreen extends StatefulWidget {
|
||||
const ShuffleScreen({Key? key}) : super(key: key);
|
||||
@ -13,6 +16,18 @@ class ShuffleScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
double width = MediaQuery.of(context).size.width;
|
||||
@ -23,8 +38,7 @@ class _ShuffleScreenState extends State<ShuffleScreen> {
|
||||
),
|
||||
body: PreviewGrid(
|
||||
videoLoader: () {
|
||||
return loadShuffledVideos(
|
||||
(isTV() ? width ~/ 200 : width ~/ 275) * 2);
|
||||
return loadData((isTV() ? width ~/ 200 : width ~/ 275) * 2);
|
||||
},
|
||||
footerBuilder: (state) => Column(
|
||||
children: [
|
@ -1,9 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../api/video_api.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../api/api.dart';
|
||||
import '../drawer/my_drawer.dart';
|
||||
import '../preview/preview_grid.dart';
|
||||
import '../types/tag.dart';
|
||||
import '../types/video.dart';
|
||||
|
||||
enum FilterTypes { DATE, LIKES, RANDOM, NAMES, LENGTH }
|
||||
|
||||
@ -21,6 +24,18 @@ class VideoFeedState extends State<VideoFeed> {
|
||||
FilterTypes filterSelection = FilterTypes.DATE;
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@ -65,7 +80,7 @@ class VideoFeedState extends State<VideoFeed> {
|
||||
),
|
||||
body: PreviewGrid(
|
||||
key: _refreshKey,
|
||||
videoLoader: () => loadVideo(widget.tag, filterSelection.index),
|
||||
videoLoader: () => loadData(),
|
||||
),
|
||||
drawer: widget.tag == null ? MyDrawer() : null);
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../api/video_api.dart';
|
||||
import '../api/api.dart';
|
||||
import 'preview_grid.dart';
|
||||
import '../types/actor.dart';
|
||||
import '../types/video.dart';
|
||||
|
||||
class ActorFeed extends StatefulWidget {
|
||||
const ActorFeed({Key? key, required this.actor}) : super(key: key);
|
||||
@ -13,10 +16,22 @@ class ActorFeed extends StatefulWidget {
|
||||
}
|
||||
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
return PreviewGrid(
|
||||
videoLoader: () => loadVideoByActor(widget.actor),
|
||||
videoLoader: () => loadData(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../preview/actor_feed.dart';
|
||||
import 'package:openmediacentermobile/preview/actor_feed.dart';
|
||||
|
||||
import '../utils/platform.dart';
|
||||
import '../types/actor.dart';
|
||||
|
@ -3,9 +3,9 @@ import 'dart:ui';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||
|
||||
import '../utils/platform.dart';
|
||||
import '../screen_loading.dart';
|
||||
import '../types/video.dart';
|
||||
import '../utils/platform.dart';
|
||||
import 'preview_tile.dart';
|
||||
|
||||
class PreviewGrid extends StatefulWidget {
|
||||
@ -56,15 +56,15 @@ class _PreviewGridState extends State<PreviewGrid> {
|
||||
builder:
|
||||
(BuildContext context, AsyncSnapshot<List<VideoT>> snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text("Error" + snapshot.error.toString()),
|
||||
TextButton(
|
||||
onPressed: () => loadData(), child: Text("Reload page"))
|
||||
],
|
||||
),
|
||||
return Column(
|
||||
children: [
|
||||
Text("Error"),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
loadData();
|
||||
},
|
||||
child: Text("Reload page"))
|
||||
],
|
||||
);
|
||||
} else if (snapshot.hasData) {
|
||||
return _mainGrid(snapshot.data!, width);
|
||||
@ -78,53 +78,50 @@ class _PreviewGridState extends State<PreviewGrid> {
|
||||
Widget _mainGrid(List<VideoT> data, double width) {
|
||||
return Stack(
|
||||
children: [
|
||||
Container(
|
||||
color: Color(0xff999999),
|
||||
child: Column(
|
||||
children: [
|
||||
if (widget.headerBuilder != null) widget.headerBuilder!(this),
|
||||
data.length > 0
|
||||
? Expanded(
|
||||
child: MasonryGridView.count(
|
||||
// every tile should be at max 330 pixels long...
|
||||
crossAxisCount: isTV() ? width ~/ 200 : width ~/ 275,
|
||||
// crossAxisCount: isTV() ? width ~/ 200 : width ~/ 330,
|
||||
itemCount: data.length,
|
||||
mainAxisSpacing: 4,
|
||||
crossAxisSpacing: 4,
|
||||
padding: EdgeInsets.all(5),
|
||||
itemBuilder: (context, index) {
|
||||
return PreviewTile(
|
||||
dta: data[index],
|
||||
onLongPress: (img) {
|
||||
setState(() {
|
||||
_previewImage = img;
|
||||
});
|
||||
},
|
||||
onLongPressEnd: () {
|
||||
setState(() {
|
||||
_previewImage = null;
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 32,
|
||||
),
|
||||
Icon(Icons.warning_amber, size: 52),
|
||||
Text("no item available")
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
if (widget.headerBuilder != null) widget.headerBuilder!(this),
|
||||
data.length > 0
|
||||
? Expanded(
|
||||
child: MasonryGridView.count(
|
||||
// every tile should be at max 330 pixels long...
|
||||
crossAxisCount: isTV() ? width ~/ 200 : width ~/ 275,
|
||||
// crossAxisCount: isTV() ? width ~/ 200 : width ~/ 330,
|
||||
itemCount: data.length,
|
||||
mainAxisSpacing: 4,
|
||||
crossAxisSpacing: 4,
|
||||
padding: EdgeInsets.all(5),
|
||||
itemBuilder: (context, index) {
|
||||
return PreviewTile(
|
||||
dta: data[index],
|
||||
onLongPress: (img) {
|
||||
setState(() {
|
||||
_previewImage = img;
|
||||
});
|
||||
},
|
||||
onLongPressEnd: () {
|
||||
setState(() {
|
||||
_previewImage = null;
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
if (widget.footerBuilder != null) widget.footerBuilder!(this),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 32,
|
||||
),
|
||||
Icon(Icons.warning_amber, size: 52),
|
||||
Text("no item available")
|
||||
],
|
||||
),
|
||||
),
|
||||
if (widget.footerBuilder != null) widget.footerBuilder!(this),
|
||||
],
|
||||
),
|
||||
if (_previewImage != null) ..._buildPreviewImage(),
|
||||
],
|
||||
|
@ -1,12 +1,15 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
|
||||
import '../api/video_api.dart';
|
||||
import '../api/api.dart';
|
||||
import '../db/database.dart';
|
||||
import '../log/log.dart';
|
||||
import '../types/video.dart';
|
||||
import '../utils/platform.dart';
|
||||
import '../types/video.dart';
|
||||
import '../video_screen/videoscreen.dart';
|
||||
|
||||
class PreviewTile extends StatefulWidget {
|
||||
@ -50,17 +53,23 @@ 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 {
|
||||
Uint8List data;
|
||||
final id = widget.dta.id;
|
||||
if (kIsWeb) {
|
||||
data = await fetchThumbnail(id);
|
||||
data = await _fetchThumbnail(id);
|
||||
} else {
|
||||
final List<Map<String, dynamic>> prev =
|
||||
await Db().db().query('previews', where: "id=$id");
|
||||
|
||||
if (prev.isEmpty) {
|
||||
data = await fetchThumbnail(id);
|
||||
data = await _fetchThumbnail(id);
|
||||
insert(id, data);
|
||||
Log.d("Adding $id to db");
|
||||
} else {
|
||||
@ -76,8 +85,7 @@ class _PreviewTileState extends State<PreviewTile> {
|
||||
);
|
||||
|
||||
// precache image to avoid loading time to render image
|
||||
if(context.mounted)
|
||||
await precacheImage(img.image, context);
|
||||
await precacheImage(img.image, context);
|
||||
|
||||
return img;
|
||||
}
|
||||
@ -104,28 +112,18 @@ class _PreviewTileState extends State<PreviewTile> {
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
color: const Color(0xff3f3f3f),
|
||||
child: Column(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
child: image,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 3,
|
||||
),
|
||||
Text(
|
||||
widget.dta.title,
|
||||
style: TextStyle(
|
||||
fontSize: isTV() ? 8 : 10.5, color: const Color(0xffe9e9e9)),
|
||||
style: TextStyle(fontSize: isTV() ? 8 : 10.5),
|
||||
overflow: TextOverflow.clip,
|
||||
maxLines: 1,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 3,
|
||||
),
|
||||
image
|
||||
],
|
||||
),
|
||||
color: Color(0x6a94a6ff),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: Material(
|
||||
@ -158,24 +156,21 @@ class _PreviewTileState extends State<PreviewTile> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(minHeight: 200, minWidth: 200),
|
||||
child: FutureBuilder<Image>(
|
||||
future: _preview, // a previously-obtained Future<String> or null
|
||||
builder: (BuildContext context, AsyncSnapshot<Image> snapshot) {
|
||||
if (snapshot.connectionState != ConnectionState.done) {
|
||||
return _buildLoader();
|
||||
}
|
||||
return FutureBuilder<Image>(
|
||||
future: _preview, // a previously-obtained Future<String> or null
|
||||
builder: (BuildContext context, AsyncSnapshot<Image> snapshot) {
|
||||
if (snapshot.connectionState != ConnectionState.done) {
|
||||
return _buildLoader();
|
||||
}
|
||||
|
||||
if (snapshot.hasError) {
|
||||
return Text("Error");
|
||||
} else if (snapshot.hasData) {
|
||||
return _buildTile(snapshot.data!);
|
||||
} else {
|
||||
return _buildLoader();
|
||||
}
|
||||
},
|
||||
),
|
||||
if (snapshot.hasError) {
|
||||
return Text("Error");
|
||||
} else if (snapshot.hasData) {
|
||||
return _buildTile(snapshot.data!);
|
||||
} else {
|
||||
return _buildLoader();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ class TagTile extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(backgroundColor: Color(0x6a94a6ff)),
|
||||
style: ElevatedButton.styleFrom(primary: Color(0x6a94a6ff)),
|
||||
child: SizedBox(
|
||||
child: Center(child: Text(tag.tagName)),
|
||||
height: 100,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import '../api/actor_api.dart';
|
||||
import '../api/video_api.dart';
|
||||
import '../dialog/add_actor_dialog.dart';
|
||||
import '../dialog/add_tag_dialog.dart';
|
||||
@ -7,6 +8,9 @@ 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 {
|
||||
@ -25,7 +29,7 @@ class _InfoViewState extends State<InfoView> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
setState(() {
|
||||
_data = loadActorsOfVideo(widget.videoId);
|
||||
_data = loadData();
|
||||
vdata = loadVideoData(widget.videoId);
|
||||
});
|
||||
}
|
||||
@ -35,6 +39,19 @@ class _InfoViewState extends State<InfoView> {
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
@ -54,10 +71,15 @@ class _InfoViewState extends State<InfoView> {
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
if (await addLike(videoData.movieId))
|
||||
setState(() {
|
||||
vdata = loadVideoData(widget.videoId);
|
||||
});
|
||||
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(
|
||||
@ -68,8 +90,9 @@ class _InfoViewState extends State<InfoView> {
|
||||
movieId: videoData.movieId,
|
||||
),
|
||||
);
|
||||
Log.d("finished dialog");
|
||||
setState(() {
|
||||
_data = loadActorsOfVideo(widget.videoId);
|
||||
_data = loadData();
|
||||
});
|
||||
},
|
||||
child: Text("Add Actor"),
|
||||
@ -82,6 +105,7 @@ class _InfoViewState extends State<InfoView> {
|
||||
movieId: videoData.movieId,
|
||||
),
|
||||
);
|
||||
Log.d("finished dialog");
|
||||
setState(() {
|
||||
vdata = loadVideoData(widget.videoId);
|
||||
});
|
||||
|
@ -1,17 +1,18 @@
|
||||
import 'dart:async';
|
||||
|
||||
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 '../utils/platform.dart';
|
||||
import '../screen_loading.dart';
|
||||
import '../types/video.dart';
|
||||
import '../types/video_data.dart';
|
||||
import '../utils/platform.dart';
|
||||
import 'info_view.dart';
|
||||
|
||||
import 'videoscreen_desktop.dart'
|
||||
if (dart.library.html) 'videoscreen_mobile.dart';
|
||||
import 'videoscreen_mobile.dart';
|
||||
|
||||
class VideoScreen extends StatefulWidget {
|
||||
const VideoScreen({Key? key, required this.metaData}) : super(key: key);
|
||||
final VideoT metaData;
|
||||
@ -27,19 +28,18 @@ class _VideoScreenState extends State<VideoScreen> {
|
||||
PageController _controller = PageController(
|
||||
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 = "";
|
||||
|
||||
void initPlayer() async {
|
||||
final videodata = await _videoData;
|
||||
|
||||
final settings = await SettingsDB.getInstance().getSettings();
|
||||
final path = settings.domain + settings.videopath + videodata.movieUrl;
|
||||
player.open(Media(path));
|
||||
final token = await Token.getInstance().getToken();
|
||||
if (token == null) return;
|
||||
|
||||
final baseurl = token.domain;
|
||||
// todo not static middle path
|
||||
final path = baseurl + "/videos/vids/" + videodata.movieUrl;
|
||||
|
||||
url = path;
|
||||
}
|
||||
@ -55,7 +55,6 @@ class _VideoScreenState extends State<VideoScreen> {
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
player.dispose();
|
||||
_controller.dispose();
|
||||
_appBarTimer?.cancel();
|
||||
}
|
||||
@ -95,7 +94,7 @@ class _VideoScreenState extends State<VideoScreen> {
|
||||
child: GestureDetector(
|
||||
onPanDown: (details) async {
|
||||
if (_appBarVisible) {
|
||||
await Future.delayed(Duration(milliseconds: 300));
|
||||
await Future.delayed(Duration(milliseconds: 100));
|
||||
setState(() {
|
||||
_appBarVisible = false;
|
||||
});
|
||||
@ -115,7 +114,13 @@ class _VideoScreenState extends State<VideoScreen> {
|
||||
controller: _controller,
|
||||
children: [
|
||||
Center(
|
||||
child: Video(controller: controller)),
|
||||
child: isDesktop()
|
||||
? VideoScreenDesktop(
|
||||
url: url,
|
||||
)
|
||||
: VideoScreenMobile(
|
||||
url: url,
|
||||
)),
|
||||
InfoView(
|
||||
videoId: widget.metaData.id,
|
||||
)
|
||||
@ -130,10 +135,7 @@ class _VideoScreenState extends State<VideoScreen> {
|
||||
leading: new IconButton(
|
||||
icon: new Icon(Icons.arrow_back_ios,
|
||||
color: Colors.grey),
|
||||
onPressed: () async {
|
||||
await player.stop();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
backgroundColor:
|
||||
Theme.of(context).primaryColor.withOpacity(0.3),
|
||||
|
53
lib/video_screen/videoscreen_desktop.dart
Normal file
53
lib/video_screen/videoscreen_desktop.dart
Normal file
@ -0,0 +1,53 @@
|
||||
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,
|
||||
scale: 1.0, // default
|
||||
showControls: true,
|
||||
playlistLength: 1,
|
||||
);
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
53
lib/video_screen/videoscreen_mobile.dart
Normal file
53
lib/video_screen/videoscreen_mobile.dart
Normal file
@ -0,0 +1,53 @@
|
||||
import 'package:better_player/better_player.dart';
|
||||
import 'package:flutter/material.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> {
|
||||
BetterPlayerController? _betterPlayerController;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_betterPlayerController == null) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const [CircularProgressIndicator(), Text("loading...")],
|
||||
);
|
||||
} else {
|
||||
return BetterPlayer(
|
||||
controller: _betterPlayerController!,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_betterPlayerController?.videoPlayerController?.dispose();
|
||||
_betterPlayerController?.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_init();
|
||||
}
|
||||
|
||||
void _init() async {
|
||||
BetterPlayerDataSource betterPlayerDataSource =
|
||||
BetterPlayerDataSource(BetterPlayerDataSourceType.network, widget.url);
|
||||
_betterPlayerController = BetterPlayerController(
|
||||
BetterPlayerConfiguration(
|
||||
autoPlay: true, looping: true, allowedScreenSleep: false),
|
||||
betterPlayerDataSource: betterPlayerDataSource);
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
}
|
@ -6,14 +6,14 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
|
||||
#include <media_kit_video/media_kit_video_plugin.h>
|
||||
#include <dart_vlc/dart_vlc_plugin.h>
|
||||
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin");
|
||||
media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) media_kit_video_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin");
|
||||
media_kit_video_plugin_register_with_registrar(media_kit_video_registrar);
|
||||
g_autoptr(FlPluginRegistrar) dart_vlc_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "DartVlcPlugin");
|
||||
dart_vlc_plugin_register_with_registrar(dart_vlc_registrar);
|
||||
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
||||
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
||||
}
|
||||
|
@ -3,12 +3,11 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
media_kit_libs_linux
|
||||
media_kit_video
|
||||
dart_vlc
|
||||
flutter_secure_storage_linux
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
media_kit_native_event_loop
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
756
pubspec.lock
756
pubspec.lock
File diff suppressed because it is too large
Load Diff
15
pubspec.yaml
15
pubspec.yaml
@ -34,23 +34,16 @@ dependencies:
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.2
|
||||
flutter_secure_storage: ^5.0.2
|
||||
logger: ^1.1.0
|
||||
http: ^0.13.4
|
||||
flutter_staggered_grid_view: ^0.6.1
|
||||
device_info_plus: ^8.0.0
|
||||
dart_vlc: ^0.1.9
|
||||
device_info_plus: ^3.2.3
|
||||
sqflite: ^2.0.3+1
|
||||
path: ^1.8.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.
|
||||
better_player: ^0.0.83
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Loading…
Reference in New Issue
Block a user