qdown 1.0.0__py3-none-any.whl → 1.0.3__py3-none-any.whl

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/__init__.py CHANGED
@@ -22,3 +22,62 @@ If you wish to use or redistribute this program under a commercial license:
22
22
  Please contact Qualiteg Inc.(https://qualiteg.com/contact) directly to obtain the terms and pricing.
23
23
 
24
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
+ ))
qdown/gdown.py CHANGED
@@ -120,7 +120,7 @@ class QDown:
120
120
  progress_bar.close()
121
121
 
122
122
  if not self.quiet:
123
- print(f"\nファイルを保存しました: {file_path}")
123
+ print(f"[qdown] ファイルを保存しました: {file_path}")
124
124
  return file_path
125
125
 
126
126
  except httpx.RequestError as e:
qdown/qdown.py ADDED
@@ -0,0 +1,185 @@
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
+
151
+ def main():
152
+ parser = argparse.ArgumentParser(
153
+ description="qdown - IDベースファイルダウンロードツール",
154
+ add_help=False
155
+ )
156
+
157
+ parser.add_argument("id", nargs="?", help="ダウンロードするファイルのID")
158
+ parser.add_argument("-O", dest="output", help="出力ファイル名")
159
+ parser.add_argument("-o", dest="output_dir", help="出力ディレクトリ")
160
+ parser.add_argument("-s", dest="server", default="https://drive.qualiteg.com", help="サーバーURL")
161
+ parser.add_argument("-q", "--quiet", action="store_true", help="進捗表示を非表示")
162
+ parser.add_argument("-h", "--help", action="store_true", help="ヘルプを表示")
163
+
164
+ args = parser.parse_args()
165
+
166
+ if args.help or not args.id:
167
+ print(__doc__)
168
+ sys.exit(0)
169
+
170
+ client = QDown(server_url=args.server, quiet=args.quiet)
171
+
172
+ result = asyncio.run(client.download_by_file_id(
173
+ file_id=args.id,
174
+ output=args.output,
175
+ output_dir=args.output_dir
176
+ ))
177
+
178
+ if result:
179
+ sys.exit(0)
180
+ else:
181
+ sys.exit(1)
182
+
183
+
184
+ if __name__ == "__main__":
185
+ main()
@@ -0,0 +1,70 @@
1
+ Metadata-Version: 2.1
2
+ Name: qdown
3
+ Version: 1.0.3
4
+ Summary: Client for QualitegDrive
5
+ Home-page: https://github.com/qualiteg/qdown
6
+ Author: Qualiteg Inc.
7
+ Author-email: qualiteger@qualiteg.com
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.7
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Requires-Python: >=3.7
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: httpx (>=0.23.0)
19
+ Requires-Dist: tqdm (>=4.64.0)
20
+
21
+ # qdown
22
+
23
+ A Python client for downloading files from QualitegDrive operated by Qualiteg Inc.
24
+
25
+ # install
26
+
27
+ ```
28
+ pip install qdown
29
+ ```
30
+
31
+ or
32
+
33
+ ```
34
+ pip install git+https://github.com/qualiteg/qdown.git
35
+ ```
36
+
37
+ # usage
38
+
39
+ ```
40
+
41
+ qdown ID [options]
42
+
43
+ Options:
44
+ -O FILENAME Specify output filename
45
+ -o DIR Specify output directory
46
+ -s SERVER Specify server URL (default: https://drive.qualiteg.com)
47
+ -q, --quiet Hide progress display
48
+ -h, --help Display help
49
+ ```
50
+
51
+ ## download example1
52
+
53
+ ```
54
+ qdown xxxxxxxxxxxxx -O my_file.txt
55
+ ```
56
+
57
+ ## download example2
58
+
59
+ From Your Original HTTP Server
60
+
61
+ ```
62
+ qdown xxxxxxxxxxxxx -O my_file.txt -s http://host.docker.internal:3000
63
+ ```
64
+
65
+
66
+ # uninstall
67
+
68
+ ```
69
+ pip uninstall qdown -y
70
+ ```
@@ -0,0 +1,10 @@
1
+ qdown/__init__.py,sha256=iAC1eU57pgDdmgWVyu2MdOddLXcel2BxR5fH7m2OImg,2983
2
+ qdown/gdown.py,sha256=3MpnwlBpEFGi1rdAMzixdBETtw8q5xItaHU1QmtPrEo,6421
3
+ qdown/qdown.py,sha256=NJhqUCgceCd9ojSQCbNuOdNOAVKJywKv_kYohpQblYg,7309
4
+ z_examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ z_examples/example.py,sha256=3FQoW22Frgyx5ulb7JOgwgcEBd8K3FI_BuFZP2E54ck,137
6
+ qdown-1.0.3.dist-info/METADATA,sha256=5_W60o0W3VTDOfhj-zDjDDB4xXZhKU-1BcJYYosai5k,1487
7
+ qdown-1.0.3.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
8
+ qdown-1.0.3.dist-info/entry_points.txt,sha256=4uunDwX_8iGbNA0DKwggOftuKUXvoHxGzvXUd2SW9LM,43
9
+ qdown-1.0.3.dist-info/top_level.txt,sha256=eVEHrbec1mx2PWv03GzKwFTbdvQqFOAps3GuveF2Ap8,17
10
+ qdown-1.0.3.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ qdown = qdown.qdown:main
z_examples/example.py ADDED
@@ -0,0 +1,4 @@
1
+ import qdown
2
+
3
+ # ファイル共有リンクからダウンロード
4
+ file_path = qdown.download("https://drive.qualiteg.com/file/xxxxxx")
@@ -1,19 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: qdown
3
- Version: 1.0.0
4
- Summary: Client for QualitegDrive
5
- Home-page: https://github.com/qualiteg/qdown
6
- Author: Qualiteg Inc.
7
- Author-email: qualiteger@qualiteg.com
8
- Classifier: Development Status :: 3 - Alpha
9
- Classifier: Intended Audience :: Developers
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.7
13
- Classifier: Programming Language :: Python :: 3.8
14
- Classifier: Programming Language :: Python :: 3.9
15
- Classifier: Programming Language :: Python :: 3.10
16
- Requires-Python: >=3.7
17
- Requires-Dist: httpx (>=0.23.0)
18
- Requires-Dist: tqdm (>=4.64.0)
19
-
@@ -1,8 +0,0 @@
1
- qdown/__init__.py,sha256=xLF9Cf0AXdryxhj3hm9gZW432d3hpGioUfTvh8zNraI,1137
2
- qdown/gdown.py,sha256=c5RP0N7r1Z7lderibzCLDsiLkaPZ2-Gyrn0li2cHk34,6415
3
- z_examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- qdown-1.0.0.dist-info/METADATA,sha256=YpyMamMMB3Fvzdbf6W5wTfk_fI30A45xviuTanJcLHo,682
5
- qdown-1.0.0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
6
- qdown-1.0.0.dist-info/entry_points.txt,sha256=oL6bqY5z0iEMyLURdbjHtvGMTdeZgqR44MtHdlOa7ZM,43
7
- qdown-1.0.0.dist-info/top_level.txt,sha256=eVEHrbec1mx2PWv03GzKwFTbdvQqFOAps3GuveF2Ap8,17
8
- qdown-1.0.0.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- qdown = qdown.gdown:main
File without changes