mirror of
https://github.com/simon-ding/polaris.git
synced 2026-02-06 15:10:49 +08:00
add indexer setting
This commit is contained in:
20
db/db.go
20
db/db.go
@@ -127,22 +127,29 @@ func (c *Client) SaveTorznabInfo(name string, setting TorznabSetting) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshal json")
|
||||
}
|
||||
_, err = c.ent.Indexers.Create().
|
||||
SetName(name).SetImplementation(IndexerTorznabImpl).SetPriority(1).SetSettings(string(data)).Save(context.TODO())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "save db")
|
||||
if _, err = c.ent.Indexers.Update().Where(indexers.Name(name)).SetSettings(string(data)).Save(context.TODO()); err != nil {
|
||||
_, err = c.ent.Indexers.Create().
|
||||
SetName(name).SetImplementation(IndexerTorznabImpl).SetPriority(1).SetSettings(string(data)).Save(context.TODO())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "save db")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) DeleteTorznab(id int) {
|
||||
c.ent.Indexers.Delete().Where(indexers.ID(id)).Exec(context.TODO())
|
||||
}
|
||||
|
||||
type TorznabInfo struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
TorznabSetting
|
||||
}
|
||||
|
||||
func (c *Client) GetAllTorznabInfo() []*TorznabInfo {
|
||||
res := c.ent.Indexers.Query().Where(indexers.Implementation(IndexerTorznabImpl)).AllX(context.TODO())
|
||||
|
||||
|
||||
var l = make([]*TorznabInfo, 0, len(res))
|
||||
for _, r := range res {
|
||||
var ss TorznabSetting
|
||||
@@ -152,7 +159,8 @@ func (c *Client) GetAllTorznabInfo() []*TorznabInfo {
|
||||
continue
|
||||
}
|
||||
l = append(l, &TorznabInfo{
|
||||
Name: r.Name,
|
||||
ID: r.ID,
|
||||
Name: r.Name,
|
||||
TorznabSetting: ss,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"polaris/log"
|
||||
"polaris/pkg/torznab"
|
||||
"polaris/pkg/transmission"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pkg/errors"
|
||||
@@ -48,6 +49,16 @@ func (s *Server) AddTorznabInfo(c *gin.Context) (interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteTorznabInfo(c *gin.Context) (interface{}, error) {
|
||||
var ids = c.Param("id")
|
||||
id, err := strconv.Atoi(ids)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("id is not correct: %v", ids)
|
||||
}
|
||||
s.db.DeleteTorznab(id)
|
||||
return "success", nil
|
||||
}
|
||||
|
||||
func (s *Server) GetAllIndexers(c *gin.Context) (interface{}, error) {
|
||||
indexers := s.db.GetAllTorznabInfo()
|
||||
if len(indexers) == 0 {
|
||||
@@ -124,4 +135,4 @@ func (s *Server) GetAllDonloadClients(c *gin.Context) (interface{}, error) {
|
||||
return nil, fmt.Errorf("no download client")
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ func (s *Server) Serve() error {
|
||||
indexer.GET("/", HttpHandler(s.GetAllIndexers))
|
||||
indexer.POST("/add", HttpHandler(s.AddTorznabInfo))
|
||||
indexer.POST("/download", HttpHandler(s.SearchAndDownload))
|
||||
indexer.DELETE("/del/:id", HttpHandler(s.DeleteTorznabInfo))
|
||||
}
|
||||
|
||||
downloader := api.Group("/downloader")
|
||||
|
||||
@@ -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
0
ui/lib/discover.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user