Ethscriptions commited on
Commit
463f386
·
verified ·
1 Parent(s): e8bd76b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +53 -228
app.py CHANGED
@@ -1,231 +1,56 @@
1
  import streamlit as st
2
  import libtorrent as lt
3
  import os
4
- import threading
5
- import time
6
- import logging
7
- from contextlib import contextmanager
8
-
9
- # 配置日志
10
- logging.basicConfig(level=logging.INFO)
11
-
12
- # 初始化锁
13
- session_lock = threading.Lock()
14
-
15
- @contextmanager
16
- def session_guard():
17
- """线程安全的session_state访问上下文管理器"""
18
- with session_lock:
19
- try:
20
- yield
21
- finally:
22
- pass
23
-
24
- class TorrentSession:
25
- def __init__(self):
26
- self.ses = lt.session()
27
- self.ses.listen_on(6881, 6891)
28
- self.alerts = []
29
- self.last_alert_time = time.time()
30
-
31
- def process_alerts(self):
32
- self.alerts = []
33
- while (alert := self.ses.wait_for_alert(1000)):
34
- self.alerts.append(alert)
35
- self.ses.pop_alert()
36
-
37
- class DownloadState:
38
- _instance = None
39
- _lock = threading.Lock()
40
-
41
- def __new__(cls):
42
- with cls._lock:
43
- if cls._instance is None:
44
- cls._instance = super().__new__(cls)
45
- cls._instance.reset()
46
- return cls._instance
47
-
48
- def reset(self):
49
- self.progress = 0.0
50
- self.is_downloading = False
51
- self.complete = False
52
- self.file_path = None
53
- self.status = "等待开始"
54
- self.info_hash = ""
55
-
56
- # 关键改进点:强化初始化机制
57
- def safe_init_session():
58
- """线程安全的session_state初始化"""
59
- with session_guard():
60
- if 'download' not in st.session_state:
61
- state = DownloadState()
62
- st.session_state.download = {
63
- 'progress': state.progress,
64
- 'is_downloading': state.is_downloading,
65
- 'complete': state.complete,
66
- 'file_path': state.file_path,
67
- 'status': state.status,
68
- 'info_hash': state.info_hash
69
- }
70
- logging.info("Session state initialized")
71
-
72
- def safe_sync_session():
73
- """双重保障的状态同步机制"""
74
- safe_init_session() # 同步前确保初始化
75
- state = DownloadState()
76
- with session_guard():
77
- st.session_state.download.update({
78
- 'progress': state.progress,
79
- 'is_downloading': state.is_downloading,
80
- 'complete': state.complete,
81
- 'file_path': state.file_path,
82
- 'status': state.status,
83
- 'info_hash': state.info_hash
84
- })
85
-
86
- # 初始化session状态(主线程保障)
87
- safe_init_session()
88
-
89
- def download_worker(magnet_link, save_path):
90
- try:
91
- state = DownloadState()
92
- ts = TorrentSession()
93
-
94
- # 重置状态
95
- with session_guard():
96
- state.reset()
97
- safe_sync_session()
98
-
99
- # 使用新版API添加磁力链接
100
- params = {
101
- 'save_path': save_path,
102
- 'storage_mode': lt.storage_mode_t.storage_mode_sparse,
103
- 'flags': lt.torrent_flags.duplicate_is_error | lt.torrent_flags.auto_managed
104
- }
105
-
106
- handle = lt.add_magnet_uri(ts.ses, magnet_link, params)
107
- state.info_hash = str(handle.info_hash())
108
- safe_sync_session()
109
-
110
- # 事件驱动等待元数据
111
- state.status = "等待元数据..."
112
- safe_sync_session()
113
-
114
- metadata_received = False
115
- start_time = time.time()
116
- while not metadata_received:
117
- ts.process_alerts()
118
- for alert in ts.alerts:
119
- if isinstance(alert, lt.metadata_received_alert):
120
- if alert.handle == handle:
121
- metadata_received = True
122
- break
123
- if time.time() - start_time > 300: # 5分钟超时
124
- raise TimeoutError("获取元数据超时")
125
- time.sleep(0.5)
126
-
127
- # 获取文件信息
128
- ti = handle.get_torrent_info()
129
- state.status = f"开始下载 {ti.name()}"
130
- safe_sync_session()
131
-
132
- # 启动下载
133
- handle.set_sequential_download(True)
134
- handle.resume()
135
-
136
- # 下载进度监控
137
- state.is_downloading = True
138
- safe_sync_session()
139
-
140
- while not handle.status().is_seeding:
141
- status = handle.status()
142
- state.progress = status.progress * 100
143
-
144
- # 状态更新
145
- if status.state == lt.torrent_status.downloading_metadata:
146
- state.status = "获取元数据..."
147
- elif status.state == lt.torrent_status.downloading:
148
- dl = status.download_rate / 1000
149
- up = status.upload_rate / 1000
150
- peers = status.num_peers
151
- state.status = f"下载中: {dl:.1f}kB/s ↑{up:.1f}kB/s ↔{peers} peers"
152
-
153
- safe_sync_session()
154
- time.sleep(1)
155
-
156
- # 下载完成
157
- state.is_downloading = False
158
- state.complete = True
159
- state.file_path = save_path
160
- state.status = "下载完成"
161
- safe_sync_session()
162
-
163
- except Exception as e:
164
- logging.error(f"下载错误: {str(e)}")
165
- state.status = f"错误: {str(e)}"
166
- state.is_downloading = False
167
- safe_sync_session()
168
- finally:
169
- safe_sync_session()
170
-
171
- # Streamlit界面
172
- st.title("🚀 磁力链接下载器")
173
-
174
- with st.form("magnet_form"):
175
- magnet = st.text_input("磁力链接", placeholder="magnet:?xt=urn:btih:...")
176
- submitted = st.form_submit_button("开始下载",
177
- disabled=st.session_state.download['is_downloading'])
178
-
179
- if submitted:
180
- if not magnet.startswith("magnet:"):
181
- st.error("无效的磁力链接格式")
182
- else:
183
- save_dir = "./downloads"
184
- os.makedirs(save_dir, exist_ok=True)
185
-
186
- # 启动前同步状态
187
- DownloadState().reset()
188
- safe_sync_session()
189
-
190
- threading.Thread(
191
- target=download_worker,
192
- args=(magnet, save_dir),
193
- daemon=True
194
- ).start()
195
-
196
- # 实时状态显示
197
- safe_sync_session()
198
-
199
- if st.session_state.download['is_downloading']:
200
- cols = st.columns([1,3])
201
- with cols[0]:
202
- st.metric("进度", f"{st.session_state.download['progress']:.1f}%")
203
- with cols[1]:
204
- st.progress(st.session_state.download['progress']/100)
205
- st.info(st.session_state.download['status'])
206
-
207
- if st.session_state.download['complete']:
208
- st.success("下载完成!")
209
- files = [f for f in os.listdir(st.session_state.download['file_path'])
210
- if os.path.isfile(os.path.join(st.session_state.download['file_path'], f))]
211
-
212
- if files:
213
- with st.expander("下载文件"):
214
- selected = st.selectbox("选择文件", files)
215
- with open(os.path.join(st.session_state.download['file_path'], selected), "rb") as f:
216
- st.download_button(
217
- "下载文件",
218
- f,
219
- file_name=selected,
220
- mime="application/octet-stream"
221
- )
222
-
223
- # 状态监控线程
224
- if 'monitor' not in st.session_state:
225
- def status_monitor():
226
- while True:
227
- safe_sync_session()
228
- time.sleep(0.5)
229
-
230
- threading.Thread(target=status_monitor, daemon=True).start()
231
- st.session_state.monitor = True
 
1
  import streamlit as st
2
  import libtorrent as lt
3
  import os
4
+ from tqdm import tqdm
5
+ import shutil
6
+
7
+ def download_magnet(magnet_uri, download_path):
8
+ # 创建会话
9
+ ses = lt.session()
10
+ ses.listen_on(6881, 6891)
11
+ # 添加磁力链接
12
+ params = {
13
+ 'save_path': download_path,
14
+ 'storage_mode': lt.storage_mode_t(2)
15
+ }
16
+ handle = lt.add_magnet_uri(ses, magnet_uri, params)
17
+ st.write(f"正在下载:{handle.name()}")
18
+ # 等待元数据下载完成
19
+ while not handle.has_metadata():
20
+ pass
21
+ # 获取文件信息
22
+ file_info = handle.get_torrent_info()
23
+ total_size = file_info.total_size()
24
+ # 创建进度条
25
+ progress_bar = st.progress(0)
26
+ # 下载文件
27
+ for i in tqdm(range(100)):
28
+ s = handle.status()
29
+ progress = s.total_done / total_size
30
+ progress_bar.progress(progress)
31
+ if s.state == lt.torrent_status.seeding:
32
+ break
33
+ # 下载完成,返回文件路径
34
+ return os.path.join(download_path, handle.name())
35
+
36
+ def main():
37
+ st.title("磁力链接下载器")
38
+ magnet_uri = st.text_input("请输入磁力链接:")
39
+ if magnet_uri:
40
+ download_path = os.path.join(os.getcwd(), "downloads")
41
+ if not os.path.exists(download_path):
42
+ os.makedirs(download_path)
43
+ file_path = download_magnet(magnet_uri, download_path)
44
+ st.write(f"下载完成:{file_path}")
45
+ with open(file_path, "rb") as f:
46
+ st.download_button(
47
+ label="下载文件",
48
+ data=f,
49
+ file_name=os.path.basename(file_path),
50
+ mime="application/octet-stream"
51
+ )
52
+ # 下载完成后,删除文件
53
+ os.remove(file_path)
54
+
55
+ if __name__ == "__main__":
56
+ main()