qdown 1.0.1__tar.gz → 1.0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qdown
3
- Version: 1.0.1
3
+ Version: 1.0.4
4
4
  Summary: Client for QualitegDrive
5
5
  Home-page: https://github.com/qualiteg/qdown
6
6
  Author: Qualiteg Inc.
@@ -20,6 +20,8 @@ Description-Content-Type: text/markdown
20
20
 
21
21
  A Python client for downloading files from QualitegDrive operated by Qualiteg Inc.
22
22
 
23
+ [Japanese](README.ja.md)
24
+
23
25
  # install
24
26
 
25
27
  ```
@@ -49,7 +51,7 @@ Options:
49
51
  ## download example1
50
52
 
51
53
  ```
52
- gdown xxxxxxxxxxxxx -O my_file.txt
54
+ qdown xxxxxxxxxxxxx -O my_file.txt
53
55
  ```
54
56
 
55
57
  ## download example2
@@ -57,7 +59,7 @@ gdown xxxxxxxxxxxxx -O my_file.txt
57
59
  From Your Original HTTP Server
58
60
 
59
61
  ```
60
- gdown xxxxxxxxxxxxx -O my_file.txt -s http://host.docker.internal:3000
62
+ qdown xxxxxxxxxxxxx -O my_file.txt -s http://host.docker.internal:3000
61
63
  ```
62
64
 
63
65
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  A Python client for downloading files from QualitegDrive operated by Qualiteg Inc.
4
4
 
5
+ [Japanese](README.ja.md)
6
+
5
7
  # install
6
8
 
7
9
  ```
@@ -31,7 +33,7 @@ Options:
31
33
  ## download example1
32
34
 
33
35
  ```
34
- gdown xxxxxxxxxxxxx -O my_file.txt
36
+ qdown xxxxxxxxxxxxx -O my_file.txt
35
37
  ```
36
38
 
37
39
  ## download example2
@@ -39,7 +41,7 @@ gdown xxxxxxxxxxxxx -O my_file.txt
39
41
  From Your Original HTTP Server
40
42
 
41
43
  ```
42
- gdown xxxxxxxxxxxxx -O my_file.txt -s http://host.docker.internal:3000
44
+ qdown xxxxxxxxxxxxx -O my_file.txt -s http://host.docker.internal:3000
43
45
  ```
44
46
 
45
47
 
@@ -0,0 +1,83 @@
1
+ """
2
+
3
+ Copyright (c) 2023 Qualiteg Inc. all rights reserved.
4
+
5
+ This program is dual-licensed under the terms of the:
6
+ 1) GNU Affero General Public License, version 3, or any later version.
7
+ 2) A commercial license agreement provided by Qualiteg Inc.
8
+
9
+ If you choose to use or redistribute this program under the terms of AGPLv3:
10
+ This program is free software: you can redistribute it and/or modify
11
+ it under the terms of the GNU Affero General Public License as
12
+ published by the Free Software Foundation, either version 3 of the
13
+ License, or (at your option) any later version.
14
+ This program is distributed in the hope that it will be useful,
15
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ GNU Affero General Public License for more details.
18
+ You should have received a copy of the GNU Affero General Public License
19
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
20
+
21
+ If you wish to use or redistribute this program under a commercial license:
22
+ Please contact Qualiteg Inc.(https://qualiteg.com/contact) directly to obtain the terms and pricing.
23
+
24
+ """
25
+
26
+ import asyncio
27
+ import re
28
+ from .qdown import QDown
29
+
30
+
31
+ def extract_file_id(url):
32
+ """
33
+ URLからファイルIDを抽出する
34
+
35
+ Args:
36
+ url (str): ファイルURL
37
+ 例1: https://drive.qualiteg.com/file/3kMM-X9S6-bMioFU0Fn8nHjAgQgWmG
38
+ 例2: https://drive.qualiteg.com/download/1Lki-o8QO-DCUmg60JRxGdXfif
39
+
40
+ Returns:
41
+ str: ファイルID
42
+ """
43
+ # /file/XXX 形式のURL
44
+ file_pattern = r'https://drive\.qualiteg\.com/file/([a-zA-Z0-9_-]+)'
45
+ file_match = re.search(file_pattern, url)
46
+ if file_match:
47
+ return file_match.group(1)
48
+
49
+ # /download/XXX 形式のURL
50
+ download_pattern = r'https://drive\.qualiteg\.com/download/([a-zA-Z0-9_-]+)'
51
+ download_match = re.search(download_pattern, url)
52
+ if download_match:
53
+ return download_match.group(1)
54
+
55
+ return url # IDと思われる場合はそのまま返す
56
+
57
+
58
+ def download(download_url, output_path=None, output_dir=None, server_url="https://drive.qualiteg.com", quiet=False):
59
+ """
60
+ URLを指定してファイルをダウンロード
61
+
62
+ Args:
63
+ download_url (str): ダウンロードURL or ファイルID
64
+ output_path (str, optional): 出力ファイルパス
65
+ output_dir (str, optional): 出力ディレクトリ
66
+ server_url (str, optional): サーバーURL
67
+ quiet (bool, optional): 進捗表示を非表示にするかどうか
68
+
69
+ Returns:
70
+ str: ダウンロードしたファイルのパス
71
+ """
72
+ # URLからファイルIDを抽出
73
+ file_id = extract_file_id(download_url)
74
+
75
+ # QDownクライアントの初期化
76
+ client = QDown(server_url=server_url, quiet=quiet)
77
+
78
+ # 同期版メソッドを呼び出す
79
+ return client.download_by_file_id_sync(
80
+ file_id=file_id,
81
+ output=output_path,
82
+ output_dir=output_dir
83
+ )
@@ -0,0 +1,292 @@
1
+ """
2
+ qdown - Client for QualitegDrive
3
+
4
+ 使用方法:
5
+ qdown ID [オプション]
6
+
7
+ オプション:
8
+ -O FILENAME 出力ファイル名を指定
9
+ -o DIR 出力ディレクトリを指定
10
+ -s SERVER サーバーURLを指定 (デフォルト: https://drive.qualiteg.com)
11
+ -q, --quiet 進捗表示を非表示
12
+ -h, --help ヘルプを表示
13
+ """
14
+
15
+ import httpx
16
+ import os
17
+ import sys
18
+ import argparse
19
+ import asyncio
20
+ import urllib.parse
21
+ from pathlib import Path
22
+ from tqdm import tqdm
23
+
24
+
25
+ class QDown:
26
+ """
27
+ ID認証付きファイルサーバー用のPythonクライアント
28
+ """
29
+
30
+ def __init__(self, server_url="https://drive.qualiteg.com", quiet=False):
31
+ """
32
+ クライアントの初期化
33
+
34
+ Args:
35
+ server_url (str): ファイルサーバーのベースURL
36
+ quiet (bool): 進捗表示を非表示にするかどうか
37
+ """
38
+ self.server_url = server_url.rstrip('/')
39
+ self.quiet = quiet
40
+ self.timeout = httpx.Timeout(10.0, connect=60.0)
41
+
42
+ async def download_by_file_id(self, file_id, output=None, output_dir=None):
43
+ """
44
+ ファイルIDを指定してファイルをダウンロード
45
+
46
+ Args:
47
+ file_id (str): ダウンロードするファイルのID (qd_id)
48
+ output (str, optional): 出力ファイル名
49
+ output_dir (str, optional): 出力ディレクトリ
50
+
51
+ Returns:
52
+ str: ダウンロードしたファイルのパス
53
+ """
54
+ url = f"{self.server_url}/download/{file_id}"
55
+
56
+ # 出力ディレクトリの設定
57
+ if output_dir:
58
+ os.makedirs(output_dir, exist_ok=True)
59
+ else:
60
+ output_dir = "."
61
+
62
+ async with httpx.AsyncClient(timeout=self.timeout) as client:
63
+ # まず、ヘッド要求を送信してファイル情報を取得
64
+ try:
65
+ head_response = await client.head(url)
66
+
67
+ if head_response.status_code == 404:
68
+ print(f"エラー: ID '{file_id}' のファイルが見つかりませんでした", file=sys.stderr)
69
+ return None
70
+
71
+ if head_response.status_code != 200:
72
+ print(f"エラー: ステータスコード {head_response.status_code}", file=sys.stderr)
73
+ return None
74
+
75
+ # Content-Dispositionヘッダーからファイル名を取得
76
+ original_filename = None
77
+ if "content-disposition" in head_response.headers:
78
+ cd = head_response.headers["content-disposition"]
79
+ if "filename=" in cd:
80
+ # 安全なファイル名を抽出(URLエンコードの解除)
81
+ filename_part = cd.split("filename=")[1].strip('"')
82
+
83
+ # filename*=UTF-8 形式のエンコードがある場合
84
+ if "filename*=UTF-8''" in cd:
85
+ encoded_part = cd.split("filename*=UTF-8''")[1]
86
+ # セミコロンやダブルクォートがあれば処理
87
+ if '"' in encoded_part:
88
+ encoded_part = encoded_part.split('"')[0]
89
+ if ';' in encoded_part:
90
+ encoded_part = encoded_part.split(';')[0]
91
+ # URLデコードして正しいファイル名を取得
92
+ original_filename = urllib.parse.unquote(encoded_part)
93
+ else:
94
+ # 通常のファイル名(エスケープ処理)
95
+ original_filename = filename_part.replace('"', '').split(';')[0]
96
+
97
+ # 保存用のファイル名を決定
98
+ if not output:
99
+ if original_filename:
100
+ # パスとして安全なファイル名に変換
101
+ safe_filename = os.path.basename(original_filename)
102
+ output_filename = safe_filename
103
+ else:
104
+ output_filename = f"download_{file_id}"
105
+ else:
106
+ output_filename = output
107
+
108
+ file_path = os.path.join(output_dir, output_filename)
109
+
110
+ # ファイルサイズを取得(プログレスバー用)
111
+ total_size = int(head_response.headers.get("content-length", 0))
112
+
113
+ # ストリーミングダウンロードを開始
114
+ async with client.stream("GET", url) as response:
115
+ if response.status_code != 200:
116
+ print(f"エラー: ダウンロード中にエラーが発生しました。ステータスコード: {response.status_code}", file=sys.stderr)
117
+ return None
118
+
119
+ with open(file_path, "wb") as f:
120
+ if not self.quiet and total_size > 0:
121
+ progress_bar = tqdm(
122
+ total=total_size,
123
+ unit="B",
124
+ unit_scale=True,
125
+ desc=f"ダウンロード中: {output_filename}"
126
+ )
127
+
128
+ downloaded = 0
129
+
130
+ async for chunk in response.aiter_bytes():
131
+ f.write(chunk)
132
+ if not self.quiet and total_size > 0:
133
+ downloaded += len(chunk)
134
+ progress_bar.update(len(chunk))
135
+
136
+ if not self.quiet and total_size > 0:
137
+ progress_bar.close()
138
+
139
+ if not self.quiet:
140
+ print(f"[qdown] ファイルを保存しました: {file_path}")
141
+ return file_path
142
+
143
+ except httpx.RequestError as e:
144
+ print(f"エラー: リクエストに失敗しました - {e}", file=sys.stderr)
145
+ return None
146
+ except Exception as e:
147
+ print(f"エラー: {e}", file=sys.stderr)
148
+ return None
149
+
150
+ def download_by_file_id_sync(self, file_id, output=None, output_dir=None):
151
+ """
152
+ ファイルIDを指定してファイルをダウンロード(同期版)
153
+
154
+ Args:
155
+ file_id (str): ダウンロードするファイルのID (qd_id)
156
+ output (str, optional): 出力ファイル名
157
+ output_dir (str, optional): 出力ディレクトリ
158
+
159
+ Returns:
160
+ str: ダウンロードしたファイルのパス
161
+ """
162
+ url = f"{self.server_url}/download/{file_id}"
163
+
164
+ # 出力ディレクトリの設定
165
+ if output_dir:
166
+ os.makedirs(output_dir, exist_ok=True)
167
+ else:
168
+ output_dir = "."
169
+
170
+ with httpx.Client(timeout=self.timeout) as client:
171
+ # まず、ヘッド要求を送信してファイル情報を取得
172
+ try:
173
+ head_response = client.head(url)
174
+
175
+ if head_response.status_code == 404:
176
+ print(f"エラー: ID '{file_id}' のファイルが見つかりませんでした", file=sys.stderr)
177
+ return None
178
+
179
+ if head_response.status_code != 200:
180
+ print(f"エラー: ステータスコード {head_response.status_code}", file=sys.stderr)
181
+ return None
182
+
183
+ # Content-Dispositionヘッダーからファイル名を取得
184
+ original_filename = None
185
+ if "content-disposition" in head_response.headers:
186
+ cd = head_response.headers["content-disposition"]
187
+ if "filename=" in cd:
188
+ # 安全なファイル名を抽出(URLエンコードの解除)
189
+ filename_part = cd.split("filename=")[1].strip('"')
190
+
191
+ # filename*=UTF-8 形式のエンコードがある場合
192
+ if "filename*=UTF-8''" in cd:
193
+ encoded_part = cd.split("filename*=UTF-8''")[1]
194
+ # セミコロンやダブルクォートがあれば処理
195
+ if '"' in encoded_part:
196
+ encoded_part = encoded_part.split('"')[0]
197
+ if ';' in encoded_part:
198
+ encoded_part = encoded_part.split(';')[0]
199
+ # URLデコードして正しいファイル名を取得
200
+ original_filename = urllib.parse.unquote(encoded_part)
201
+ else:
202
+ # 通常のファイル名(エスケープ処理)
203
+ original_filename = filename_part.replace('"', '').split(';')[0]
204
+
205
+ # 保存用のファイル名を決定
206
+ if not output:
207
+ if original_filename:
208
+ # パスとして安全なファイル名に変換
209
+ safe_filename = os.path.basename(original_filename)
210
+ output_filename = safe_filename
211
+ else:
212
+ output_filename = f"download_{file_id}"
213
+ else:
214
+ output_filename = output
215
+
216
+ file_path = os.path.join(output_dir, output_filename)
217
+
218
+ # ファイルサイズを取得(プログレスバー用)
219
+ total_size = int(head_response.headers.get("content-length", 0))
220
+
221
+ # ストリーミングダウンロードを開始
222
+ with client.stream("GET", url) as response:
223
+ if response.status_code != 200:
224
+ print(f"エラー: ダウンロード中にエラーが発生しました。ステータスコード: {response.status_code}", file=sys.stderr)
225
+ return None
226
+
227
+ with open(file_path, "wb") as f:
228
+ if not self.quiet and total_size > 0:
229
+ progress_bar = tqdm(
230
+ total=total_size,
231
+ unit="B",
232
+ unit_scale=True,
233
+ desc=f"ダウンロード中: {output_filename}"
234
+ )
235
+
236
+ downloaded = 0
237
+
238
+ for chunk in response.iter_bytes():
239
+ f.write(chunk)
240
+ if not self.quiet and total_size > 0:
241
+ downloaded += len(chunk)
242
+ progress_bar.update(len(chunk))
243
+
244
+ if not self.quiet and total_size > 0:
245
+ progress_bar.close()
246
+
247
+ if not self.quiet:
248
+ print(f"[qdown] ファイルを保存しました: {file_path}")
249
+ return file_path
250
+
251
+ except httpx.RequestError as e:
252
+ print(f"エラー: リクエストに失敗しました - {e}", file=sys.stderr)
253
+ return None
254
+ except Exception as e:
255
+ print(f"エラー: {e}", file=sys.stderr)
256
+ return None
257
+
258
+ def main():
259
+ parser = argparse.ArgumentParser(
260
+ description="qdown - IDベースファイルダウンロードツール",
261
+ add_help=False
262
+ )
263
+
264
+ parser.add_argument("id", nargs="?", help="ダウンロードするファイルのID")
265
+ parser.add_argument("-O", dest="output", help="出力ファイル名")
266
+ parser.add_argument("-o", dest="output_dir", help="出力ディレクトリ")
267
+ parser.add_argument("-s", dest="server", default="https://drive.qualiteg.com", help="サーバーURL")
268
+ parser.add_argument("-q", "--quiet", action="store_true", help="進捗表示を非表示")
269
+ parser.add_argument("-h", "--help", action="store_true", help="ヘルプを表示")
270
+
271
+ args = parser.parse_args()
272
+
273
+ if args.help or not args.id:
274
+ print(__doc__)
275
+ sys.exit(0)
276
+
277
+ client = QDown(server_url=args.server, quiet=args.quiet)
278
+
279
+ result = asyncio.run(client.download_by_file_id(
280
+ file_id=args.id,
281
+ output=args.output,
282
+ output_dir=args.output_dir
283
+ ))
284
+
285
+ if result:
286
+ sys.exit(0)
287
+ else:
288
+ sys.exit(1)
289
+
290
+
291
+ if __name__ == "__main__":
292
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qdown
3
- Version: 1.0.1
3
+ Version: 1.0.4
4
4
  Summary: Client for QualitegDrive
5
5
  Home-page: https://github.com/qualiteg/qdown
6
6
  Author: Qualiteg Inc.
@@ -20,6 +20,8 @@ Description-Content-Type: text/markdown
20
20
 
21
21
  A Python client for downloading files from QualitegDrive operated by Qualiteg Inc.
22
22
 
23
+ [Japanese](README.ja.md)
24
+
23
25
  # install
24
26
 
25
27
  ```
@@ -49,7 +51,7 @@ Options:
49
51
  ## download example1
50
52
 
51
53
  ```
52
- gdown xxxxxxxxxxxxx -O my_file.txt
54
+ qdown xxxxxxxxxxxxx -O my_file.txt
53
55
  ```
54
56
 
55
57
  ## download example2
@@ -57,7 +59,7 @@ gdown xxxxxxxxxxxxx -O my_file.txt
57
59
  From Your Original HTTP Server
58
60
 
59
61
  ```
60
- gdown xxxxxxxxxxxxx -O my_file.txt -s http://host.docker.internal:3000
62
+ qdown xxxxxxxxxxxxx -O my_file.txt -s http://host.docker.internal:3000
61
63
  ```
62
64
 
63
65
 
@@ -2,11 +2,12 @@ MANIFEST.in
2
2
  README.md
3
3
  setup.py
4
4
  qdown/__init__.py
5
- qdown/gdown.py
5
+ qdown/qdown.py
6
6
  qdown.egg-info/PKG-INFO
7
7
  qdown.egg-info/SOURCES.txt
8
8
  qdown.egg-info/dependency_links.txt
9
9
  qdown.egg-info/entry_points.txt
10
10
  qdown.egg-info/requires.txt
11
11
  qdown.egg-info/top_level.txt
12
- z_examples/__init__.py
12
+ z_examples/__init__.py
13
+ z_examples/example.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ qdown = qdown.qdown:main
@@ -2,7 +2,6 @@
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
4
  from setuptools import setup, find_packages
5
- import os
6
5
 
7
6
  # README.mdの内容を読み込む
8
7
  with open("README.md", "r", encoding="utf-8") as fh:
@@ -10,10 +9,10 @@ with open("README.md", "r", encoding="utf-8") as fh:
10
9
 
11
10
  setup(
12
11
  name="qdown",
13
- version="1.0.1",
12
+ version="1.0.4",
14
13
  description="Client for QualitegDrive",
15
14
  long_description=long_description,
16
- long_description_content_type="text/markdown", # これが重要
15
+ long_description_content_type="text/markdown",
17
16
  author="Qualiteg Inc.",
18
17
  author_email="qualiteger@qualiteg.com",
19
18
  url="https://github.com/qualiteg/qdown",
@@ -24,7 +23,7 @@ setup(
24
23
  ],
25
24
  entry_points={
26
25
  "console_scripts": [
27
- "qdown=qdown.gdown:main",
26
+ "qdown=qdown.qdown:main",
28
27
  ],
29
28
  },
30
29
  classifiers=[
@@ -0,0 +1,4 @@
1
+ import qdown
2
+
3
+ # ファイル共有リンクからダウンロード
4
+ file_path = qdown.download("https://drive.qualiteg.com/file/xxxxxx")
@@ -1,24 +0,0 @@
1
- """
2
-
3
- Copyright (c) 2023 Qualiteg Inc. all rights reserved.
4
-
5
- This program is dual-licensed under the terms of the:
6
- 1) GNU Affero General Public License, version 3, or any later version.
7
- 2) A commercial license agreement provided by Qualiteg Inc.
8
-
9
- If you choose to use or redistribute this program under the terms of AGPLv3:
10
- This program is free software: you can redistribute it and/or modify
11
- it under the terms of the GNU Affero General Public License as
12
- published by the Free Software Foundation, either version 3 of the
13
- License, or (at your option) any later version.
14
- This program is distributed in the hope that it will be useful,
15
- but WITHOUT ANY WARRANTY; without even the implied warranty of
16
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
- GNU Affero General Public License for more details.
18
- You should have received a copy of the GNU Affero General Public License
19
- along with this program. If not, see <https://www.gnu.org/licenses/>.
20
-
21
- If you wish to use or redistribute this program under a commercial license:
22
- Please contact Qualiteg Inc.(https://qualiteg.com/contact) directly to obtain the terms and pricing.
23
-
24
- """
@@ -1,168 +0,0 @@
1
-
2
- """
3
- qdown - Client for QualitegDrive
4
-
5
- 使用方法:
6
- qdown ID [オプション]
7
-
8
- オプション:
9
- -O FILENAME 出力ファイル名を指定
10
- -o DIR 出力ディレクトリを指定
11
- -s SERVER サーバーURLを指定 (デフォルト: https://drive.qualiteg.com)
12
- -q, --quiet 進捗表示を非表示
13
- -h, --help ヘルプを表示
14
- """
15
-
16
- import httpx
17
- import os
18
- import sys
19
- import argparse
20
- import asyncio
21
- from pathlib import Path
22
- from tqdm import tqdm
23
-
24
-
25
- class QDown:
26
- """
27
- ID認証付きファイルサーバー用のPythonクライアント
28
- """
29
-
30
- def __init__(self, server_url="https://drive.qualiteg.com", quiet=False):
31
- """
32
- クライアントの初期化
33
-
34
- Args:
35
- server_url (str): ファイルサーバーのベースURL
36
- quiet (bool): 進捗表示を非表示にするかどうか
37
- """
38
- self.server_url = server_url.rstrip('/')
39
- self.quiet = quiet
40
- self.timeout = httpx.Timeout(10.0, connect=60.0)
41
-
42
- async def download(self, file_id, output=None, output_dir=None):
43
- """
44
- ファイルIDを指定してファイルをダウンロード
45
-
46
- Args:
47
- file_id (str): ダウンロードするファイルのID (qd_id)
48
- output (str, optional): 出力ファイル名
49
- output_dir (str, optional): 出力ディレクトリ
50
-
51
- Returns:
52
- str: ダウンロードしたファイルのパス
53
- """
54
- url = f"{self.server_url}/download/{file_id}"
55
-
56
- # 出力ディレクトリの設定
57
- if output_dir:
58
- os.makedirs(output_dir, exist_ok=True)
59
- else:
60
- output_dir = "."
61
-
62
- async with httpx.AsyncClient(timeout=self.timeout) as client:
63
- # まず、ヘッド要求を送信してファイル情報を取得
64
- try:
65
- head_response = await client.head(url)
66
-
67
- if head_response.status_code == 404:
68
- print(f"エラー: ID '{file_id}' のファイルが見つかりませんでした", file=sys.stderr)
69
- return None
70
-
71
- if head_response.status_code != 200:
72
- print(f"エラー: ステータスコード {head_response.status_code}", file=sys.stderr)
73
- return None
74
-
75
- # Content-Dispositionヘッダーからファイル名を取得
76
- original_filename = None
77
- if "content-disposition" in head_response.headers:
78
- cd = head_response.headers["content-disposition"]
79
- if "filename=" in cd:
80
- original_filename = cd.split("filename=")[1].strip('"')
81
-
82
- # 保存用のファイル名を決定
83
- if not output:
84
- if original_filename:
85
- output_filename = original_filename
86
- else:
87
- output_filename = f"download_{file_id}"
88
- else:
89
- output_filename = output
90
-
91
- file_path = os.path.join(output_dir, output_filename)
92
-
93
- # ファイルサイズを取得(プログレスバー用)
94
- total_size = int(head_response.headers.get("content-length", 0))
95
-
96
- # ストリーミングダウンロードを開始
97
- async with client.stream("GET", url) as response:
98
- if response.status_code != 200:
99
- print(f"エラー: ダウンロード中にエラーが発生しました。ステータスコード: {response.status_code}", file=sys.stderr)
100
- return None
101
-
102
- with open(file_path, "wb") as f:
103
- if not self.quiet and total_size > 0:
104
- progress_bar = tqdm(
105
- total=total_size,
106
- unit="B",
107
- unit_scale=True,
108
- desc=f"ダウンロード中: {output_filename}"
109
- )
110
-
111
- downloaded = 0
112
-
113
- async for chunk in response.aiter_bytes():
114
- f.write(chunk)
115
- if not self.quiet and total_size > 0:
116
- downloaded += len(chunk)
117
- progress_bar.update(len(chunk))
118
-
119
- if not self.quiet and total_size > 0:
120
- progress_bar.close()
121
-
122
- if not self.quiet:
123
- print(f"\nファイルを保存しました: {file_path}")
124
- return file_path
125
-
126
- except httpx.RequestError as e:
127
- print(f"エラー: リクエストに失敗しました - {e}", file=sys.stderr)
128
- return None
129
- except Exception as e:
130
- print(f"エラー: {e}", file=sys.stderr)
131
- return None
132
-
133
-
134
- def main():
135
- parser = argparse.ArgumentParser(
136
- description="qdown - IDベースファイルダウンロードツール",
137
- add_help=False
138
- )
139
-
140
- parser.add_argument("id", nargs="?", help="ダウンロードするファイルのID")
141
- parser.add_argument("-O", dest="output", help="出力ファイル名")
142
- parser.add_argument("-o", dest="output_dir", help="出力ディレクトリ")
143
- parser.add_argument("-s", dest="server", default="https://drive.qualiteg.com", help="サーバーURL")
144
- parser.add_argument("-q", "--quiet", action="store_true", help="進捗表示を非表示")
145
- parser.add_argument("-h", "--help", action="store_true", help="ヘルプを表示")
146
-
147
- args = parser.parse_args()
148
-
149
- if args.help or not args.id:
150
- print(__doc__)
151
- sys.exit(0)
152
-
153
- client = QDown(server_url=args.server, quiet=args.quiet)
154
-
155
- result = asyncio.run(client.download(
156
- file_id=args.id,
157
- output=args.output,
158
- output_dir=args.output_dir
159
- ))
160
-
161
- if result:
162
- sys.exit(0)
163
- else:
164
- sys.exit(1)
165
-
166
-
167
- if __name__ == "__main__":
168
- main()
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- qdown = qdown.gdown:main
File without changes
File without changes
File without changes
File without changes