diff --git a/db/db.go b/db/db.go index 422f130..79cebce 100644 --- a/db/db.go +++ b/db/db.go @@ -127,13 +127,18 @@ func (c *Client) SaveTorznabInfo(name string, setting TorznabSetting) error { if err != nil { return errors.Wrap(err, "marshal json") } - 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") - } + count := c.ent.Indexers.Query().Where(indexers.Name(name)).CountX(context.TODO()) + if count > 0 { + c.ent.Indexers.Update().Where(indexers.Name(name)).SetSettings(string(data)).Save(context.TODO()) + return err } + + _, 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 } @@ -168,9 +173,15 @@ func (c *Client) GetAllTorznabInfo() []*TorznabInfo { } func (c *Client) SaveTransmission(name, url, user, password string) error { + count := c.ent.DownloadClients.Query().Where(downloadclients.Name(name)).CountX(context.TODO()) + if count != 0 { + err := c.ent.DownloadClients.Update().Where(downloadclients.Name(name)). + SetURL(url).SetUser(user).SetPassword(password).Exec(context.TODO()) + return err + } + _, err := c.ent.DownloadClients.Create().SetEnable(true).SetImplementation("transmission"). SetName(name).SetURL(url).SetUser(user).SetPassword(password).Save(context.TODO()) - return err } @@ -191,3 +202,7 @@ func (c *Client) GetAllDonloadClients() []*ent.DownloadClients { } return cc } + +func (c *Client) DeleteDownloadCLient(id int) { + c.ent.DownloadClients.Delete().Where(downloadclients.ID(id)).Exec(context.TODO()) +} diff --git a/server/resources.go b/server/resources.go index 2b98cf5..118f809 100644 --- a/server/resources.go +++ b/server/resources.go @@ -136,3 +136,13 @@ func (s *Server) GetAllDonloadClients(c *gin.Context) (interface{}, error) { } return res, nil } + +func (s *Server) DeleteDownloadCLient(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.DeleteDownloadCLient(id) + return "success", nil +} \ No newline at end of file diff --git a/server/server.go b/server/server.go index c9998c5..3ab000d 100644 --- a/server/server.go +++ b/server/server.go @@ -74,6 +74,7 @@ func (s *Server) Serve() error { { downloader.GET("/", HttpHandler(s.GetAllDonloadClients)) downloader.POST("/add", HttpHandler(s.AddDownloadClient)) + downloader.DELETE("/del/:id", HttpHandler(s.DeleteDownloadCLient)) } s.language = s.db.GetLanguage() diff --git a/ui/lib/APIs.dart b/ui/lib/APIs.dart index d79eae7..4e0e993 100644 --- a/ui/lib/APIs.dart +++ b/ui/lib/APIs.dart @@ -10,6 +10,9 @@ class APIs { static final allIndexersUrl = "$_baseUrl/api/v1/indexer/"; static final addIndexerUrl = "$_baseUrl/api/v1/indexer/add"; static final delIndexerUrl = "$_baseUrl/api/v1/indexer/del/"; + static final allDownloadClientsUrl = "$_baseUrl/api/v1/downloader"; + static final addDownloadClientUrl = "$_baseUrl/api/v1/downloader/add"; + static final delDownloadClientUrl = "$_baseUrl/api/v1/downloader/del/"; static const tmdbImgBaseUrl = "https://image.tmdb.org/t/p/w500/"; diff --git a/ui/lib/providers/welcome_data.dart b/ui/lib/providers/welcome_data.dart index 8e141d9..1e0c9d7 100644 --- a/ui/lib/providers/welcome_data.dart +++ b/ui/lib/providers/welcome_data.dart @@ -67,7 +67,6 @@ var indexersProvider = FutureProvider((ref) async { for (final item in sp.data as List) { indexers.add(Indexer.fromJson(item)); } - print("indexers: ${indexers[0].name}"); return indexers; }); @@ -93,3 +92,58 @@ class Indexer { return data; } } + +var dwonloadClientsProvider = FutureProvider((ref) async { + final dio = Dio(); + var resp = await dio.get(APIs.allDownloadClientsUrl); + var sp = ServerResponse.fromJson(resp.data); + if (sp.code != 0) { + throw sp.message; + } + List indexers = List.empty(growable: true); + for (final item in sp.data as List) { + indexers.add(DownloadClient.fromJson(item)); + } + return indexers; +}); + +class DownloadClient { + int? id; + bool? enable; + String? name; + String? implementation; + String? url; + bool? removeCompletedDownloads; + bool? removeFailedDownloads; + + DownloadClient( + {this.id, + this.enable, + this.name, + this.implementation, + this.url, + this.removeCompletedDownloads, + this.removeFailedDownloads}); + + DownloadClient.fromJson(Map json) { + id = json['id']; + enable = json['enable']; + name = json['name']; + implementation = json['implementation']; + url = json['url']; + removeCompletedDownloads = json['remove_completed_downloads']; + removeFailedDownloads = json['remove_failed_downloads']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['enable'] = this.enable; + data['name'] = this.name; + data['implementation'] = this.implementation; + data['url'] = this.url; + data['remove_completed_downloads'] = this.removeCompletedDownloads; + data['remove_failed_downloads'] = this.removeFailedDownloads; + return data; + } +} diff --git a/ui/lib/system_settings.dart b/ui/lib/system_settings.dart index 1ccad9a..b3868da 100644 --- a/ui/lib/system_settings.dart +++ b/ui/lib/system_settings.dart @@ -99,7 +99,7 @@ class _SystemSettingsPageState extends ConsumerState { onTap: () { showIndexerDetails(context, indexer); }, - child: Text(indexer.name!))); + child: Center(child: Text(indexer.name!)))); } return Card( margin: const EdgeInsets.all(4), @@ -115,6 +115,43 @@ class _SystemSettingsPageState extends ConsumerState { }), error: (err, trace) => Text("$err"), loading: () => const CircularProgressIndicator()); + + var downloadClients = ref.watch(dwonloadClientsProvider); + var downloadSetting = downloadClients.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 client = value[i]; + return Card( + margin: const EdgeInsets.all(4), + clipBehavior: Clip.hardEdge, + child: InkWell( + //splashColor: Colors.blue.withAlpha(30), + onTap: () { + showDownloadClientDetails(context, client); + }, + child: Center(child: Text(client.name!)))); + } + return Card( + margin: const EdgeInsets.all(4), + clipBehavior: Clip.hardEdge, + child: InkWell( + //splashColor: Colors.blue.withAlpha(30), + onTap: () { + showDownloadClientDetails(context, DownloadClient()); + }, + child: const Center( + child: Icon(Icons.add), + ))); + }), + error: (err, trace) => Text("$err"), + loading: () => const CircularProgressIndicator()); + return ListView( children: [ ExpansionTile( @@ -131,6 +168,13 @@ class _SystemSettingsPageState extends ConsumerState { title: const Text("索引器设置"), children: [indexerSetting], ), + ExpansionTile( + tilePadding: const EdgeInsets.fromLTRB(10, 0, 10, 0), + childrenPadding: const EdgeInsets.fromLTRB(50, 0, 50, 0), + initiallyExpanded: true, + title: const Text("下载器设置"), + children: [downloadSetting], + ), ], ); } @@ -174,11 +218,11 @@ class _SystemSettingsPageState extends ConsumerState { ), ), actions: [ - TextButton( - onPressed: () => { - deleteIndexer(context, indexer.id!) - }, - child: const Text('删除')), + indexer.id == null + ? Text("") + : TextButton( + onPressed: () => {deleteIndexer(context, indexer.id!)}, + child: const Text('删除')), TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('取消')), @@ -217,6 +261,80 @@ class _SystemSettingsPageState extends ConsumerState { return; } Navigator.of(context).pop(); - ref.refresh(indexersProvider); + ref.refresh(dwonloadClientsProvider); + } + + Future showDownloadClientDetails( + BuildContext context, DownloadClient client) { + var nameController = TextEditingController(text: client.name); + var urlController = TextEditingController(text: client.url); + + return showDialog( + context: context, + barrierDismissible: true, // user must tap button! + builder: (BuildContext context) { + return AlertDialog( + title: const Text('索引器'), + content: SingleChildScrollView( + child: ListBody( + children: [ + TextField( + decoration: const InputDecoration(labelText: "名称"), + controller: nameController, + ), + TextField( + decoration: const InputDecoration(labelText: "网址"), + controller: urlController, + ), + ], + ), + ), + actions: [ + client.id == null + ? Text("") + : TextButton( + onPressed: () => + {deleteDownloadClients(context, client.id!)}, + child: const Text('删除')), + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('取消')), + TextButton( + child: const Text('确定'), + onPressed: () { + addDownloadClients( + context, nameController.text, urlController.text); + }, + ), + ], + ); + }); + } + + void addDownloadClients(BuildContext context, String name, String url) async { + var dio = Dio(); + var resp = await dio.post(APIs.addDownloadClientUrl, data: { + "name": name, + "url": url, + }); + var sp = ServerResponse.fromJson(resp.data); + if (sp.code != 0 && context.mounted) { + Utils.showAlertDialog(context, sp.message); + return; + } + Navigator.of(context).pop(); + ref.refresh(dwonloadClientsProvider); + } + + void deleteDownloadClients(BuildContext context, int id) async { + var dio = Dio(); + var resp = await dio.delete("${APIs.delDownloadClientUrl}$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(dwonloadClientsProvider); } }