qdown 1.0.1__tar.gz → 1.0.3__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.
- {qdown-1.0.1 → qdown-1.0.3}/PKG-INFO +3 -3
- {qdown-1.0.1 → qdown-1.0.3}/README.md +2 -2
- qdown-1.0.3/qdown/__init__.py +83 -0
- qdown-1.0.1/qdown/gdown.py → qdown-1.0.3/qdown/qdown.py +51 -34
- {qdown-1.0.1 → qdown-1.0.3}/qdown.egg-info/PKG-INFO +3 -3
- {qdown-1.0.1 → qdown-1.0.3}/qdown.egg-info/SOURCES.txt +3 -2
- qdown-1.0.3/qdown.egg-info/entry_points.txt +2 -0
- {qdown-1.0.1 → qdown-1.0.3}/setup.py +3 -3
- qdown-1.0.3/z_examples/example.py +4 -0
- qdown-1.0.1/qdown/__init__.py +0 -24
- qdown-1.0.1/qdown.egg-info/entry_points.txt +0 -2
- {qdown-1.0.1 → qdown-1.0.3}/MANIFEST.in +0 -0
- {qdown-1.0.1 → qdown-1.0.3}/qdown.egg-info/dependency_links.txt +0 -0
- {qdown-1.0.1 → qdown-1.0.3}/qdown.egg-info/requires.txt +0 -0
- {qdown-1.0.1 → qdown-1.0.3}/qdown.egg-info/top_level.txt +0 -0
- {qdown-1.0.1 → qdown-1.0.3}/setup.cfg +0 -0
- {qdown-1.0.1 → qdown-1.0.3}/z_examples/__init__.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: qdown
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.3
|
4
4
|
Summary: Client for QualitegDrive
|
5
5
|
Home-page: https://github.com/qualiteg/qdown
|
6
6
|
Author: Qualiteg Inc.
|
@@ -49,7 +49,7 @@ Options:
|
|
49
49
|
## download example1
|
50
50
|
|
51
51
|
```
|
52
|
-
|
52
|
+
qdown xxxxxxxxxxxxx -O my_file.txt
|
53
53
|
```
|
54
54
|
|
55
55
|
## download example2
|
@@ -57,7 +57,7 @@ gdown xxxxxxxxxxxxx -O my_file.txt
|
|
57
57
|
From Your Original HTTP Server
|
58
58
|
|
59
59
|
```
|
60
|
-
|
60
|
+
qdown xxxxxxxxxxxxx -O my_file.txt -s http://host.docker.internal:3000
|
61
61
|
```
|
62
62
|
|
63
63
|
|
@@ -31,7 +31,7 @@ Options:
|
|
31
31
|
## download example1
|
32
32
|
|
33
33
|
```
|
34
|
-
|
34
|
+
qdown xxxxxxxxxxxxx -O my_file.txt
|
35
35
|
```
|
36
36
|
|
37
37
|
## download example2
|
@@ -39,7 +39,7 @@ gdown xxxxxxxxxxxxx -O my_file.txt
|
|
39
39
|
From Your Original HTTP Server
|
40
40
|
|
41
41
|
```
|
42
|
-
|
42
|
+
qdown xxxxxxxxxxxxx -O my_file.txt -s http://host.docker.internal:3000
|
43
43
|
```
|
44
44
|
|
45
45
|
|
@@ -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 asyncio.run(client.download_by_file_id(
|
80
|
+
file_id=file_id,
|
81
|
+
output=output_path,
|
82
|
+
output_dir=output_dir
|
83
|
+
))
|
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
"""
|
3
2
|
qdown - Client for QualitegDrive
|
4
3
|
|
@@ -18,6 +17,7 @@ import os
|
|
18
17
|
import sys
|
19
18
|
import argparse
|
20
19
|
import asyncio
|
20
|
+
import urllib.parse
|
21
21
|
from pathlib import Path
|
22
22
|
from tqdm import tqdm
|
23
23
|
|
@@ -30,7 +30,7 @@ class QDown:
|
|
30
30
|
def __init__(self, server_url="https://drive.qualiteg.com", quiet=False):
|
31
31
|
"""
|
32
32
|
クライアントの初期化
|
33
|
-
|
33
|
+
|
34
34
|
Args:
|
35
35
|
server_url (str): ファイルサーバーのベースURL
|
36
36
|
quiet (bool): 進捗表示を非表示にするかどうか
|
@@ -38,91 +38,108 @@ class QDown:
|
|
38
38
|
self.server_url = server_url.rstrip('/')
|
39
39
|
self.quiet = quiet
|
40
40
|
self.timeout = httpx.Timeout(10.0, connect=60.0)
|
41
|
-
|
42
|
-
async def
|
41
|
+
|
42
|
+
async def download_by_file_id(self, file_id, output=None, output_dir=None):
|
43
43
|
"""
|
44
44
|
ファイルIDを指定してファイルをダウンロード
|
45
|
-
|
45
|
+
|
46
46
|
Args:
|
47
47
|
file_id (str): ダウンロードするファイルのID (qd_id)
|
48
48
|
output (str, optional): 出力ファイル名
|
49
49
|
output_dir (str, optional): 出力ディレクトリ
|
50
|
-
|
50
|
+
|
51
51
|
Returns:
|
52
52
|
str: ダウンロードしたファイルのパス
|
53
53
|
"""
|
54
54
|
url = f"{self.server_url}/download/{file_id}"
|
55
|
-
|
55
|
+
|
56
56
|
# 出力ディレクトリの設定
|
57
57
|
if output_dir:
|
58
58
|
os.makedirs(output_dir, exist_ok=True)
|
59
59
|
else:
|
60
60
|
output_dir = "."
|
61
|
-
|
61
|
+
|
62
62
|
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
63
63
|
# まず、ヘッド要求を送信してファイル情報を取得
|
64
64
|
try:
|
65
65
|
head_response = await client.head(url)
|
66
|
-
|
66
|
+
|
67
67
|
if head_response.status_code == 404:
|
68
68
|
print(f"エラー: ID '{file_id}' のファイルが見つかりませんでした", file=sys.stderr)
|
69
69
|
return None
|
70
|
-
|
70
|
+
|
71
71
|
if head_response.status_code != 200:
|
72
72
|
print(f"エラー: ステータスコード {head_response.status_code}", file=sys.stderr)
|
73
73
|
return None
|
74
|
-
|
74
|
+
|
75
75
|
# Content-Dispositionヘッダーからファイル名を取得
|
76
76
|
original_filename = None
|
77
77
|
if "content-disposition" in head_response.headers:
|
78
78
|
cd = head_response.headers["content-disposition"]
|
79
79
|
if "filename=" in cd:
|
80
|
-
|
81
|
-
|
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
|
+
|
82
97
|
# 保存用のファイル名を決定
|
83
98
|
if not output:
|
84
99
|
if original_filename:
|
85
|
-
|
100
|
+
# パスとして安全なファイル名に変換
|
101
|
+
safe_filename = os.path.basename(original_filename)
|
102
|
+
output_filename = safe_filename
|
86
103
|
else:
|
87
104
|
output_filename = f"download_{file_id}"
|
88
105
|
else:
|
89
106
|
output_filename = output
|
90
|
-
|
107
|
+
|
91
108
|
file_path = os.path.join(output_dir, output_filename)
|
92
|
-
|
109
|
+
|
93
110
|
# ファイルサイズを取得(プログレスバー用)
|
94
111
|
total_size = int(head_response.headers.get("content-length", 0))
|
95
|
-
|
112
|
+
|
96
113
|
# ストリーミングダウンロードを開始
|
97
114
|
async with client.stream("GET", url) as response:
|
98
115
|
if response.status_code != 200:
|
99
116
|
print(f"エラー: ダウンロード中にエラーが発生しました。ステータスコード: {response.status_code}", file=sys.stderr)
|
100
117
|
return None
|
101
|
-
|
118
|
+
|
102
119
|
with open(file_path, "wb") as f:
|
103
120
|
if not self.quiet and total_size > 0:
|
104
121
|
progress_bar = tqdm(
|
105
|
-
total=total_size,
|
106
|
-
unit="B",
|
107
|
-
unit_scale=True,
|
122
|
+
total=total_size,
|
123
|
+
unit="B",
|
124
|
+
unit_scale=True,
|
108
125
|
desc=f"ダウンロード中: {output_filename}"
|
109
126
|
)
|
110
|
-
|
127
|
+
|
111
128
|
downloaded = 0
|
112
|
-
|
129
|
+
|
113
130
|
async for chunk in response.aiter_bytes():
|
114
131
|
f.write(chunk)
|
115
132
|
if not self.quiet and total_size > 0:
|
116
133
|
downloaded += len(chunk)
|
117
134
|
progress_bar.update(len(chunk))
|
118
|
-
|
135
|
+
|
119
136
|
if not self.quiet and total_size > 0:
|
120
137
|
progress_bar.close()
|
121
|
-
|
138
|
+
|
122
139
|
if not self.quiet:
|
123
|
-
print(f"
|
140
|
+
print(f"[qdown] ファイルを保存しました: {file_path}")
|
124
141
|
return file_path
|
125
|
-
|
142
|
+
|
126
143
|
except httpx.RequestError as e:
|
127
144
|
print(f"エラー: リクエストに失敗しました - {e}", file=sys.stderr)
|
128
145
|
return None
|
@@ -136,28 +153,28 @@ def main():
|
|
136
153
|
description="qdown - IDベースファイルダウンロードツール",
|
137
154
|
add_help=False
|
138
155
|
)
|
139
|
-
|
156
|
+
|
140
157
|
parser.add_argument("id", nargs="?", help="ダウンロードするファイルのID")
|
141
158
|
parser.add_argument("-O", dest="output", help="出力ファイル名")
|
142
159
|
parser.add_argument("-o", dest="output_dir", help="出力ディレクトリ")
|
143
160
|
parser.add_argument("-s", dest="server", default="https://drive.qualiteg.com", help="サーバーURL")
|
144
161
|
parser.add_argument("-q", "--quiet", action="store_true", help="進捗表示を非表示")
|
145
162
|
parser.add_argument("-h", "--help", action="store_true", help="ヘルプを表示")
|
146
|
-
|
163
|
+
|
147
164
|
args = parser.parse_args()
|
148
|
-
|
165
|
+
|
149
166
|
if args.help or not args.id:
|
150
167
|
print(__doc__)
|
151
168
|
sys.exit(0)
|
152
|
-
|
169
|
+
|
153
170
|
client = QDown(server_url=args.server, quiet=args.quiet)
|
154
|
-
|
155
|
-
result = asyncio.run(client.
|
171
|
+
|
172
|
+
result = asyncio.run(client.download_by_file_id(
|
156
173
|
file_id=args.id,
|
157
174
|
output=args.output,
|
158
175
|
output_dir=args.output_dir
|
159
176
|
))
|
160
|
-
|
177
|
+
|
161
178
|
if result:
|
162
179
|
sys.exit(0)
|
163
180
|
else:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: qdown
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.3
|
4
4
|
Summary: Client for QualitegDrive
|
5
5
|
Home-page: https://github.com/qualiteg/qdown
|
6
6
|
Author: Qualiteg Inc.
|
@@ -49,7 +49,7 @@ Options:
|
|
49
49
|
## download example1
|
50
50
|
|
51
51
|
```
|
52
|
-
|
52
|
+
qdown xxxxxxxxxxxxx -O my_file.txt
|
53
53
|
```
|
54
54
|
|
55
55
|
## download example2
|
@@ -57,7 +57,7 @@ gdown xxxxxxxxxxxxx -O my_file.txt
|
|
57
57
|
From Your Original HTTP Server
|
58
58
|
|
59
59
|
```
|
60
|
-
|
60
|
+
qdown xxxxxxxxxxxxx -O my_file.txt -s http://host.docker.internal:3000
|
61
61
|
```
|
62
62
|
|
63
63
|
|
@@ -2,11 +2,12 @@ MANIFEST.in
|
|
2
2
|
README.md
|
3
3
|
setup.py
|
4
4
|
qdown/__init__.py
|
5
|
-
qdown/
|
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
|
@@ -10,10 +10,10 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
10
10
|
|
11
11
|
setup(
|
12
12
|
name="qdown",
|
13
|
-
version="1.0.
|
13
|
+
version="1.0.3", # バージョン更新
|
14
14
|
description="Client for QualitegDrive",
|
15
15
|
long_description=long_description,
|
16
|
-
long_description_content_type="text/markdown",
|
16
|
+
long_description_content_type="text/markdown",
|
17
17
|
author="Qualiteg Inc.",
|
18
18
|
author_email="qualiteger@qualiteg.com",
|
19
19
|
url="https://github.com/qualiteg/qdown",
|
@@ -24,7 +24,7 @@ setup(
|
|
24
24
|
],
|
25
25
|
entry_points={
|
26
26
|
"console_scripts": [
|
27
|
-
"qdown=qdown.
|
27
|
+
"qdown=qdown.qdown:main",
|
28
28
|
],
|
29
29
|
},
|
30
30
|
classifiers=[
|
qdown-1.0.1/qdown/__init__.py
DELETED
@@ -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
|
-
"""
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|