Skip to content

Commit

Permalink
feat: complete size limiter feature
Browse files Browse the repository at this point in the history
  • Loading branch information
simon-ding committed Nov 19, 2024
1 parent b4c2002 commit c833f6f
Show file tree
Hide file tree
Showing 4 changed files with 368 additions and 19 deletions.
6 changes: 4 additions & 2 deletions ui/lib/providers/APIs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ class APIs {
static final tvParseUrl = "$_baseUrl/api/v1/setting/parse/tv";
static final movieParseUrl = "$_baseUrl/api/v1/setting/parse/movie";

static final mediaSizeLimiterUrl = "$_baseUrl/api/v1/setting/limiter";

static const tmdbApiKey = "tmdb_api_key";
static const downloadDirKey = "download_dir";

Expand Down Expand Up @@ -131,7 +133,7 @@ class APIs {
if (sp.code != 0) {
throw sp.message;
}
return sp.data==null? []:sp.data as List<String>;
return sp.data == null ? [] : sp.data as List<String>;
}

static Future<List<String>> downloadAllMovies() async {
Expand All @@ -142,7 +144,7 @@ class APIs {
if (sp.code != 0) {
throw sp.message;
}
return sp.data==null? []:sp.data as List<String>;
return sp.data == null ? [] : sp.data as List<String>;
}

static Future<String> parseTvName(String s) async {
Expand Down
109 changes: 109 additions & 0 deletions ui/lib/providers/size_limiter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import 'dart:async';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:ui/providers/APIs.dart';
import 'package:ui/providers/server_response.dart';

var mediaSizeLimiterDataProvider =
AsyncNotifierProvider.autoDispose<MediaSizeLimiterData, MediaSizeLimiter>(
MediaSizeLimiterData.new);

class MediaSizeLimiterData extends AutoDisposeAsyncNotifier<MediaSizeLimiter> {
@override
FutureOr<MediaSizeLimiter> build() async {
final dio = APIs.getDio();
var resp = await dio.get(APIs.mediaSizeLimiterUrl);
var sp = ServerResponse.fromJson(resp.data);
if (sp.code != 0) {
throw sp.message;
}
return MediaSizeLimiter.fromJson(sp.data);
}

Future<void> submit(MediaSizeLimiter limiter) async {
final dio = APIs.getDio();
var resp = await dio.post(APIs.mediaSizeLimiterUrl, data: limiter.toJson());
var sp = ServerResponse.fromJson(resp.data);
if (sp.code != 0) {
throw sp.message;
}
ref.invalidateSelf();
}
}

class MediaSizeLimiter {
SizeLimiter? tvLimiter;
SizeLimiter? movieLimiter;

MediaSizeLimiter({this.tvLimiter, this.movieLimiter});

MediaSizeLimiter.fromJson(Map<String, dynamic> json) {
tvLimiter = json['tv_limiter'] != null
? SizeLimiter.fromJson(json['tv_limiter'])
: null;
movieLimiter = json['movie_limiter'] != null
? SizeLimiter.fromJson(json['movie_limiter'])
: null;
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (tvLimiter != null) {
data['tv_limiter'] = tvLimiter!.toJson();
}
if (movieLimiter != null) {
data['movie_limiter'] = movieLimiter!.toJson();
}
return data;
}
}

class SizeLimiter {
ResLimiter? p720p;
ResLimiter? p1080p;
ResLimiter? p2160p;

SizeLimiter({this.p720p, this.p1080p, this.p2160p});

SizeLimiter.fromJson(Map<String, dynamic> json) {
p720p = json['720p'] != null ? ResLimiter.fromJson(json['720p']) : null;
p1080p = json['1080p'] != null ? ResLimiter.fromJson(json['1080p']) : null;
p2160p = json['2160p'] != null ? ResLimiter.fromJson(json['2160p']) : null;
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (p720p != null) {
data['720p'] = p720p!.toJson();
}
if (p1080p != null) {
data['1080p'] = p1080p!.toJson();
}
if (p2160p != null) {
data['2160p'] = p2160p!.toJson();
}
return data;
}
}

class ResLimiter {
int? maxSize;
int? minSize;
int? preferSize;

ResLimiter({this.maxSize, this.minSize, this.preferSize});

ResLimiter.fromJson(Map<String, dynamic> json) {
maxSize = json['max_size'];
minSize = json['min_size'];
preferSize = json['prefer_size'];
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['max_size'] = maxSize;
data['min_size'] = minSize;
data['prefer_size'] = preferSize;
return data;
}
}
193 changes: 179 additions & 14 deletions ui/lib/settings/downloader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
import 'package:quiver/strings.dart';
import 'package:ui/providers/settings.dart';
import 'package:ui/providers/size_limiter.dart';
import 'package:ui/settings/dialog.dart';
import 'package:ui/widgets/progress_indicator.dart';
import 'package:ui/widgets/widgets.dart';
Expand All @@ -22,20 +23,28 @@ class _DownloaderState extends ConsumerState<DownloaderSettings> {
@override
Widget build(BuildContext context) {
var downloadClients = ref.watch(dwonloadClientsProvider);
return downloadClients.when(
data: (value) => Wrap(
children: List.generate(value.length + 1, (i) {
if (i < value.length) {
var client = value[i];
return SettingsCard(
onTap: () => showDownloadClientDetails(client),
child: Text(client.name ?? ""));
}
return SettingsCard(
onTap: () => showSelections(), child: const Icon(Icons.add));
})),
error: (err, trace) => PoNetworkError(err: err),
loading: () => const MyProgressIndicator());
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
downloadClients.when(
data: (value) => Wrap(
children: List.generate(value.length + 1, (i) {
if (i < value.length) {
var client = value[i];
return SettingsCard(
onTap: () => showDownloadClientDetails(client),
child: Text(client.name ?? ""));
}
return SettingsCard(
onTap: () => showSelections(),
child: const Icon(Icons.add));
})),
error: (err, trace) => PoNetworkError(err: err),
loading: () => const MyProgressIndicator()),
Divider(),
getSizeLimiterWidget()
],
);
}

Future<void> showDownloadClientDetails(DownloadClient client) {
Expand Down Expand Up @@ -199,4 +208,160 @@ class _DownloaderState extends ConsumerState<DownloaderSettings> {
);
});
}

Widget getSizeLimiterWidget() {
var data = ref.watch(mediaSizeLimiterDataProvider);
final _formKey = GlobalKey<FormBuilderState>();

return Container(
padding: EdgeInsets.only(left: 20, right: 20, top: 20),
child: data.when(
data: (value) {
return FormBuilder(
key: _formKey,
initialValue: {
"tv_720p_min": toMbString(value.tvLimiter!.p720p!.minSize!),
"tv_720p_max": toMbString(value.tvLimiter!.p720p!.maxSize!),
"tv_1080p_min": toMbString(value.tvLimiter!.p1080p!.minSize!),
"tv_1080p_max": toMbString(value.tvLimiter!.p1080p!.maxSize!),
"tv_2160p_min": toMbString(value.tvLimiter!.p2160p!.minSize!),
"tv_2160p_max": toMbString(value.tvLimiter!.p2160p!.maxSize!),
"movie_720p_min":
toMbString(value.movieLimiter!.p720p!.minSize!),
"movie_720p_max":
toMbString(value.movieLimiter!.p720p!.maxSize!),
"movie_1080p_min":
toMbString(value.movieLimiter!.p1080p!.minSize!),
"movie_1080p_max":
toMbString(value.movieLimiter!.p1080p!.maxSize!),
"movie_2160p_min":
toMbString(value.movieLimiter!.p2160p!.minSize!),
"movie_2160p_max":
toMbString(value.movieLimiter!.p2160p!.maxSize!),
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"剧集大小限制",
style: TextStyle(fontSize: 18),
),
Divider(),
minMaxRow(" 720p", "tv_720p_min", "tv_720p_max"),
minMaxRow("1080p", "tv_1080p_min", "tv_1080p_max"),
minMaxRow("2160p", "tv_2160p_min", "tv_2160p_max"),
Text(
"电影大小限制",
style: TextStyle(fontSize: 18),
),
Divider(),
minMaxRow(" 720p", "movie_720p_min", "movie_720p_max"),
minMaxRow("1080p", "movie_1080p_min", "movie_1080p_max"),
minMaxRow("2160p", "movie_2160p_min", "movie_2160p_max"),
Center(
child: Padding(
padding: EdgeInsets.all(20),
child: LoadingElevatedButton(
onPressed: () async {
if (_formKey.currentState!.saveAndValidate()) {
var values = _formKey.currentState!.value;

return ref
.read(mediaSizeLimiterDataProvider.notifier)
.submit(MediaSizeLimiter(
tvLimiter: SizeLimiter(
p720p: ResLimiter(
minSize:
toByteInt(values["tv_720p_min"]),
maxSize:
toByteInt(values["tv_720p_max"])),
p1080p: ResLimiter(
minSize:
toByteInt(values["tv_1080p_min"]),
maxSize: toByteInt(
values["tv_1080p_max"])),
p2160p: ResLimiter(
minSize:
toByteInt(values["tv_2160p_min"]),
maxSize: toByteInt(
values["tv_2160p_max"])),
),
movieLimiter: SizeLimiter(
p720p: ResLimiter(
minSize: toByteInt(
values["movie_720p_min"]),
maxSize: toByteInt(
values["movie_720p_max"])),
p1080p: ResLimiter(
minSize: toByteInt(
values["movie_1080p_min"]),
maxSize: toByteInt(
values["movie_1080p_max"])),
p2160p: ResLimiter(
minSize: toByteInt(
values["movie_2160p_min"]),
maxSize: toByteInt(
values["movie_2160p_max"])),
)));
} else {
throw "validation_error";
}
},
label: Text("保存"),
),
),
)
],
),
);
},
error: (err, trace) => Container(),
loading: () => const MyProgressIndicator()),
);
}

Widget minMaxRow(String title, String nameMin, String nameMax) {
return Row(
children: [
Flexible(flex: 2, child: Container()),
Flexible(
flex: 2,
child: Text(
title,
style: TextStyle(fontSize: 16),
)),
Flexible(flex: 1, child: Container()),
Flexible(
flex: 6,
child: FormBuilderTextField(
name: nameMin,
decoration: InputDecoration(suffixText: "MB", labelText: "最小"),
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(),
FormBuilderValidators.numeric()
]),
)),
Flexible(flex: 1, child: Text(" - ")),
Flexible(
flex: 6,
child: FormBuilderTextField(
name: nameMax,
decoration: InputDecoration(suffixText: "MB", labelText: "最大"),
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(),
FormBuilderValidators.numeric()
]),
)),
Flexible(flex: 2, child: Container()),
],
);
}
}

String toMbString(int size) {
return (size / 1000 / 1000).toString();
}

int toByteInt(String s) {
return int.parse(s) * 1000 * 1000;
}
Loading

0 comments on commit c833f6f

Please sign in to comment.