add indexer setting

This commit is contained in:
Simon Ding
2024-07-09 15:36:29 +08:00
parent 684846fd88
commit 232042b1e3
7 changed files with 239 additions and 49 deletions

View File

@@ -7,6 +7,9 @@ class APIs {
static final watchlistUrl = "$_baseUrl/api/v1/tv/watchlist";
static final seriesDetailUrl = "$_baseUrl/api/v1/tv/series/";
static final searchAndDownloadUrl = "$_baseUrl/api/v1/indexer/download";
static final allIndexersUrl = "$_baseUrl/api/v1/indexer/";
static final addIndexerUrl = "$_baseUrl/api/v1/indexer/add";
static final delIndexerUrl = "$_baseUrl/api/v1/indexer/del/";
static const tmdbImgBaseUrl = "https://image.tmdb.org/t/p/w500/";

0
ui/lib/discover.dart Normal file
View File

View File

@@ -56,3 +56,40 @@ var tmdbApiSettingProvider = FutureProvider(
},
);
var indexersProvider = FutureProvider((ref) async {
final dio = Dio();
var resp = await dio.get(APIs.allIndexersUrl);
var sp = ServerResponse.fromJson(resp.data);
if (sp.code != 0) {
throw sp.message;
}
List<Indexer> indexers = List.empty(growable: true);
for (final item in sp.data as List) {
indexers.add(Indexer.fromJson(item));
}
print("indexers: ${indexers[0].name}");
return indexers;
});
class Indexer {
String? name;
String? url;
String? apiKey;
int? id;
Indexer({this.name, this.url, this.apiKey});
Indexer.fromJson(Map<String, dynamic> json) {
name = json['name'];
url = json['url'];
apiKey = json['api_key'];
id = json["id"];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['name'] = this.name;
data['url'] = this.url;
data['api_key'] = this.apiKey;
return data;
}
}

View File

@@ -30,54 +30,109 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
Widget build(BuildContext context) {
var key = ref.watch(tmdbApiSettingProvider);
return key.when(
data: (data ) => Container(
padding: const EdgeInsets.fromLTRB(40, 10, 40, 0),
child: Form(
key: _formKey, //设置globalKey用于后面获取FormState
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(
children: [
TextFormField(
autofocus: true,
initialValue: data,
decoration: const InputDecoration(
labelText: "TMDB Api Key",
icon: Icon(Icons.key),
),
//
validator: (v) {
return v!.trim().isNotEmpty ? null : "ApiKey 不能为空";
},
onSaved: (newValue) {
_submitSettings(context, newValue!);
},
),
Center(
child: Padding(
padding: const EdgeInsets.only(top: 28.0),
child: ElevatedButton(
child: const Padding(
padding: EdgeInsets.all(16.0),
child: Text("保存"),
var tmdbSetting = key.when(
data: (data) => Container(
padding: const EdgeInsets.fromLTRB(40, 10, 40, 0),
child: Form(
key: _formKey, //设置globalKey用于后面获取FormState
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(
children: [
TextFormField(
autofocus: true,
initialValue: data,
decoration: const InputDecoration(
labelText: "TMDB Api Key",
icon: Icon(Icons.key),
),
onPressed: () {
// 通过_formKey.currentState 获取FormState后
// 调用validate()方法校验用户名密码是否合法,校验
// 通过后再提交数据。
if ((_formKey.currentState as FormState).validate()) {
(_formKey.currentState as FormState).save();
}
//
validator: (v) {
return v!.trim().isNotEmpty ? null : "ApiKey 不能为空";
},
onSaved: (newValue) {
_submitSettings(context, newValue!);
},
),
),
)
],
Center(
child: Padding(
padding: const EdgeInsets.only(top: 28.0),
child: ElevatedButton(
child: const Padding(
padding: EdgeInsets.all(16.0),
child: Text("保存"),
),
onPressed: () {
// 通过_formKey.currentState 获取FormState后
// 调用validate()方法校验用户名密码是否合法,校验
// 通过后再提交数据。
if ((_formKey.currentState as FormState)
.validate()) {
(_formKey.currentState as FormState).save();
}
},
),
),
)
],
),
),
),
),
),
error: (err, trace) => Text("$err"),
loading: () => const CircularProgressIndicator());
var indexers = ref.watch(indexersProvider);
var indexerSetting = indexers.when(
data: (value) => GridView.builder(
itemCount: value.length + 1,
scrollDirection: Axis.vertical,
shrinkWrap: true,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 6),
itemBuilder: (context, i) {
if (i < value.length) {
var indexer = value[i];
return Card(
margin: const EdgeInsets.all(4),
clipBehavior: Clip.hardEdge,
child: InkWell(
//splashColor: Colors.blue.withAlpha(30),
onTap: () {
showIndexerDetails(context, indexer);
},
child: Text(indexer.name!)));
}
return Card(
margin: const EdgeInsets.all(4),
clipBehavior: Clip.hardEdge,
child: InkWell(
//splashColor: Colors.blue.withAlpha(30),
onTap: () {
showIndexerDetails(context, Indexer());
},
child: const Center(
child: Icon(Icons.add),
)));
}),
error: (err, trace) => Text("$err"),
loading: () => const CircularProgressIndicator());
return ListView(
children: [
ExpansionTile(
tilePadding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
childrenPadding: const EdgeInsets.fromLTRB(50, 0, 50, 0),
initiallyExpanded: true,
title: const Text("TMDB 设置"),
children: [tmdbSetting],
),
ExpansionTile(
tilePadding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
childrenPadding: const EdgeInsets.fromLTRB(50, 0, 50, 0),
initiallyExpanded: true,
title: const Text("索引器设置"),
children: [indexerSetting],
),
],
);
}
void _submitSettings(BuildContext context, String v) async {
@@ -89,4 +144,79 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
}
}
}
Future<void> showIndexerDetails(BuildContext context, Indexer indexer) {
var nameController = TextEditingController(text: indexer.name);
var urlController = TextEditingController(text: indexer.url);
var apiKeyController = TextEditingController(text: indexer.apiKey);
return showDialog<void>(
context: context,
barrierDismissible: true, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: const Text('索引器'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
TextField(
decoration: const InputDecoration(labelText: "名称"),
controller: nameController,
),
TextField(
decoration: const InputDecoration(labelText: "网址"),
controller: urlController,
),
TextField(
decoration: const InputDecoration(labelText: "API Key"),
controller: apiKeyController,
),
],
),
),
actions: <Widget>[
TextButton(
onPressed: () => {
deleteIndexer(context, indexer.id!)
},
child: const Text('删除')),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('取消')),
TextButton(
child: const Text('确定'),
onPressed: () {
addIndexer(context, nameController.text, urlController.text,
apiKeyController.text);
},
),
],
);
});
}
void addIndexer(
BuildContext context, String name, String url, String apiKey) async {
var dio = Dio();
var resp = await dio.post(APIs.addIndexerUrl,
data: Indexer(name: name, url: url, apiKey: apiKey).toJson());
var sp = ServerResponse.fromJson(resp.data);
if (sp.code != 0 && context.mounted) {
Utils.showAlertDialog(context, sp.message);
return;
}
Navigator.of(context).pop();
ref.refresh(indexersProvider);
}
void deleteIndexer(BuildContext context, int id) async {
var dio = Dio();
var resp = await dio.delete("${APIs.delIndexerUrl}$id");
var sp = ServerResponse.fromJson(resp.data);
if (sp.code != 0 && context.mounted) {
Utils.showAlertDialog(context, sp.message);
return;
}
Navigator.of(context).pop();
ref.refresh(indexersProvider);
}
}