Skip to content

Commit d32ebee

Browse files
first version of cli downloader done
1 parent d686b83 commit d32ebee

File tree

6 files changed

+601
-0
lines changed

6 files changed

+601
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# https://dart.dev/guides/libraries/private-files
2+
# Created by `dart pub`
3+
.dart_tool/

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 1.0.0
2+
3+
- Initial version.

analysis_options.yaml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# This file configures the static analysis results for your project (errors,
2+
# warnings, and lints).
3+
#
4+
# This enables the 'recommended' set of lints from `package:lints`.
5+
# This set helps identify many issues that may lead to problems when running
6+
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
7+
# style and format.
8+
#
9+
# If you want a smaller set of lints you can change this to specify
10+
# 'package:lints/core.yaml'. These are just the most critical lints
11+
# (the recommended set includes the core lints).
12+
# The core lints are also what is used by pub.dev for scoring packages.
13+
14+
include: package:lints/recommended.yaml
15+
16+
# Uncomment the following section to specify additional rules.
17+
18+
# linter:
19+
# rules:
20+
# - camel_case_types
21+
22+
# analyzer:
23+
# exclude:
24+
# - path/to/excluded/files/**
25+
26+
# For more information about the core and recommended set of lints, see
27+
# https://dart.dev/go/core-lints
28+
29+
# For additional information about configuring this file, see
30+
# https://dart.dev/guides/language/analysis-options

bin/downloader.dart

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import 'dart:io';
2+
3+
import 'package:args/args.dart';
4+
import 'package:dio/dio.dart';
5+
import 'package:validators/validators.dart';
6+
7+
void main(List<String> args) async {
8+
try {
9+
final argParser = buildParser();
10+
11+
final results = argParser.parse(args);
12+
if (results.wasParsed('help')) {
13+
printUsage(argParser);
14+
return;
15+
}
16+
17+
if (args.length > 1) {
18+
throw 'Just one argument is allowed';
19+
}
20+
if (args.isEmpty) {
21+
printUsage(argParser);
22+
return;
23+
}
24+
25+
if (!isURL(results.arguments.first)) {
26+
throw 'Not a valid URL';
27+
}
28+
final filePath = await download(results.arguments.first);
29+
30+
print('\nFile downloaded to "$filePath"');
31+
} catch (e) {
32+
print(e.toString());
33+
}
34+
}
35+
36+
ArgParser buildParser() {
37+
return ArgParser()
38+
..addFlag(
39+
'help',
40+
abbr: 'h',
41+
negatable: false,
42+
);
43+
}
44+
45+
void printUsage(ArgParser argParser) {
46+
print('Usage: downld [URL]');
47+
print(argParser.usage);
48+
}
49+
50+
Future<String> download(String url) async {
51+
try {
52+
final dio = Dio();
53+
54+
final directory = getDownloadsDirectory();
55+
56+
final savePath = '${directory.path}\\${Uri.parse(url).pathSegments.last}';
57+
58+
final response = await dio.download(
59+
url,
60+
savePath,
61+
onReceiveProgress: (received, total) {
62+
final progress = (received / total * 100).toInt();
63+
showProgress(progress);
64+
},
65+
);
66+
67+
final contentType =
68+
(response.headers.map['content-type'] as List)[0] as String;
69+
70+
final extion = contentType.split('/').last;
71+
72+
final newSavePath = renameIfNoExtenion(savePath, extion);
73+
74+
if (newSavePath != null) return newSavePath;
75+
76+
return savePath;
77+
} catch (e) {
78+
rethrow;
79+
}
80+
}
81+
82+
Directory getDownloadsDirectory() {
83+
final userProfile = Platform.environment['USERPROFILE'];
84+
if (userProfile != null) {
85+
final downloadsDirectory = '$userProfile\\Downloads';
86+
final dir = Directory(downloadsDirectory);
87+
dir.createSync(recursive: true);
88+
return dir;
89+
}
90+
return Directory.current;
91+
}
92+
93+
void showProgress(int progress) {
94+
try {
95+
assert(
96+
progress >= 0 && progress <= 100, 'Progress must be between 0 and 100');
97+
const int total = 100;
98+
const int barLength = 40;
99+
100+
if (progress > total) progress = total;
101+
102+
// Calculate the number of "=" signs based on progress
103+
int filledLength = (barLength * progress) ~/ total;
104+
String bar = '=' * filledLength + ' ' * (barLength - filledLength);
105+
106+
stdout.write('\r[$bar] $progress%');
107+
} catch (e) {
108+
rethrow;
109+
}
110+
}
111+
112+
String? renameIfNoExtenion(String filePath, String extenion) {
113+
final fileName = filePath.split('\\').last;
114+
115+
if (fileName.contains('.')) return null;
116+
117+
final file = File(filePath);
118+
var path = file.path;
119+
var lastSeparator = path.lastIndexOf(Platform.pathSeparator);
120+
var newPath = '${path.substring(0, lastSeparator + 1)}$fileName.$extenion';
121+
return file.renameSync(newPath).path;
122+
}

0 commit comments

Comments
 (0)