Ethscriptions commited on
Commit
1c8f582
·
verified ·
1 Parent(s): 9660e2c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +132 -86
app.py CHANGED
@@ -3,8 +3,24 @@ import libtorrent as lt
3
  import os
4
  import threading
5
  import time
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
- # 线程安全的下载状态管理
8
  class DownloadState:
9
  _instance = None
10
  _lock = threading.Lock()
@@ -13,138 +29,168 @@ class DownloadState:
13
  with cls._lock:
14
  if cls._instance is None:
15
  cls._instance = super().__new__(cls)
16
- cls._instance.progress = 0
17
- cls._instance.is_downloading = False
18
- cls._instance.complete = False
19
- cls._instance.file_path = None
20
- cls._instance.status = ""
21
  return cls._instance
 
 
 
 
 
 
 
 
22
 
23
  def init_session_state():
24
- """初始化session_state"""
25
- if 'download_initialized' not in st.session_state:
26
  state = DownloadState()
27
  st.session_state.download = {
28
  'progress': state.progress,
29
  'is_downloading': state.is_downloading,
30
  'complete': state.complete,
31
  'file_path': state.file_path,
32
- 'status': state.status
 
33
  }
34
- st.session_state.download_initialized = True
35
 
36
  def sync_session_state():
37
- """同步状态到session_state"""
38
  state = DownloadState()
39
- st.session_state.download = {
40
  'progress': state.progress,
41
  'is_downloading': state.is_downloading,
42
  'complete': state.complete,
43
  'file_path': state.file_path,
44
- 'status': state.status
45
- }
 
46
 
47
- def download_torrent(magnet_link, save_path):
48
  try:
49
  state = DownloadState()
 
50
 
51
- # 初始化下载状态
52
- with threading.Lock():
53
- state.progress = 0
54
- state.is_downloading = True
55
- state.complete = False
56
- state.file_path = None
57
- state.status = "正在初始化..."
58
-
59
- ses = lt.session()
60
  params = {
61
  'save_path': save_path,
62
- 'storage_mode': lt.storage_mode_t(2)
 
63
  }
64
 
65
- handle = lt.add_magnet_uri(ses, magnet_link, params)
 
66
 
67
- # 获取元数据
68
- with threading.Lock():
69
- state.status = "正在获取元数据..."
70
- while not handle.has_metadata():
71
- time.sleep(1)
 
 
 
 
 
 
 
 
72
 
73
  # 获取文件信息
74
- torrent_info = handle.get_torrent_info()
75
- files = [torrent_info.file_at(i).path for i in range(torrent_info.num_files())]
 
76
 
77
- # 开始下载
78
- with threading.Lock():
79
- state.status = "开始下载..."
80
 
81
- while handle.status().state != lt.torrent_status.seeding:
 
 
82
  status = handle.status()
83
- with threading.Lock():
84
- state.progress = status.progress * 100
 
 
 
 
 
 
 
 
 
85
  time.sleep(1)
86
 
87
  # 下载完成
88
- with threading.Lock():
89
- state.is_downloading = False
90
- state.complete = True
91
- state.file_path = save_path
92
- state.status = f"下载完成!保存路径:{save_path}"
93
 
94
  except Exception as e:
95
- with threading.Lock():
96
- state.status = f"错误:{str(e)}"
97
- state.is_downloading = False
98
  finally:
99
  sync_session_state()
100
 
101
- # 初始化session状态
102
  init_session_state()
 
103
 
104
- # 界面布局
105
- st.title("🎯 磁力链接下载器")
106
- st.write("输入磁力链接开始下载(仅支持单个文件)")
107
-
108
- magnet_link = st.text_input("磁力链接:", placeholder="magnet:?xt=urn:btih:...")
109
-
110
- if st.button("🚀 开始下载", disabled=st.session_state.download['is_downloading']):
111
- if not magnet_link.startswith("magnet:"):
112
- st.error("请输入有效的磁力链接!")
113
- else:
114
- save_dir = "./downloads"
115
- os.makedirs(save_dir, exist_ok=True)
116
- threading.Thread(
117
- target=download_torrent,
118
- args=(magnet_link, save_dir),
119
- daemon=True
120
- ).start()
 
 
 
 
121
 
122
- # 实时更新状态
123
  sync_session_state()
124
 
125
- # 显示下载状态
126
  if st.session_state.download['is_downloading']:
127
- st.progress(int(st.session_state.download['progress']))
128
- st.write(f"🔍 进度:{st.session_state.download['progress']:.1f}%")
 
 
 
129
  st.info(st.session_state.download['status'])
130
 
131
  if st.session_state.download['complete']:
132
- st.success("下载完成!")
133
-
134
- # 文件下载部分
135
- download_dir = st.session_state.download['file_path']
136
- files = [f for f in os.listdir(download_dir) if os.path.isfile(os.path.join(download_dir, f))]
137
 
138
  if files:
139
- selected_file = st.selectbox("选择要下载的文件:", files)
140
- file_path = os.path.join(download_dir, selected_file)
141
-
142
- with open(file_path, "rb") as f:
143
- st.download_button(
144
- "⬇️ 下载文件",
145
- f,
146
- file_name=selected_file,
147
- mime="application/octet-stream"
148
- )
149
- else:
150
- st.warning("未找到下载文件")
 
 
 
 
 
 
 
 
3
  import os
4
  import threading
5
  import time
6
+ import logging
7
+
8
+ # 配置日志
9
+ logging.basicConfig(level=logging.INFO)
10
+
11
+ class TorrentSession:
12
+ def __init__(self):
13
+ self.ses = lt.session()
14
+ self.ses.listen_on(6881, 6891)
15
+ self.alerts = []
16
+ self.last_alert_time = time.time()
17
+
18
+ def process_alerts(self):
19
+ self.alerts = []
20
+ while (alert := self.ses.wait_for_alert(1000)):
21
+ self.alerts.append(alert)
22
+ self.ses.pop_alert()
23
 
 
24
  class DownloadState:
25
  _instance = None
26
  _lock = threading.Lock()
 
29
  with cls._lock:
30
  if cls._instance is None:
31
  cls._instance = super().__new__(cls)
32
+ cls._instance.reset()
 
 
 
 
33
  return cls._instance
34
+
35
+ def reset(self):
36
+ self.progress = 0.0
37
+ self.is_downloading = False
38
+ self.complete = False
39
+ self.file_path = None
40
+ self.status = "等待开始"
41
+ self.info_hash = ""
42
 
43
  def init_session_state():
44
+ if 'download' not in st.session_state:
 
45
  state = DownloadState()
46
  st.session_state.download = {
47
  'progress': state.progress,
48
  'is_downloading': state.is_downloading,
49
  'complete': state.complete,
50
  'file_path': state.file_path,
51
+ 'status': state.status,
52
+ 'info_hash': state.info_hash
53
  }
 
54
 
55
  def sync_session_state():
 
56
  state = DownloadState()
57
+ st.session_state.download.update({
58
  'progress': state.progress,
59
  'is_downloading': state.is_downloading,
60
  'complete': state.complete,
61
  'file_path': state.file_path,
62
+ 'status': state.status,
63
+ 'info_hash': state.info_hash
64
+ })
65
 
66
+ def download_worker(magnet_link, save_path):
67
  try:
68
  state = DownloadState()
69
+ ts = TorrentSession()
70
 
71
+ # 使用新版API添加磁力链接
 
 
 
 
 
 
 
 
72
  params = {
73
  'save_path': save_path,
74
+ 'storage_mode': lt.storage_mode_t.storage_mode_sparse,
75
+ 'flags': lt.torrent_flags.duplicate_is_error | lt.torrent_flags.auto_managed
76
  }
77
 
78
+ handle = lt.add_magnet_uri(ts.ses, magnet_link, params)
79
+ state.info_hash = str(handle.info_hash())
80
 
81
+ # 事件驱动等待元数据
82
+ state.status = "等待元数据..."
83
+ logging.info("等待元数据...")
84
+
85
+ metadata_received = False
86
+ while not metadata_received:
87
+ ts.process_alerts()
88
+ for alert in ts.alerts:
89
+ if isinstance(alert, lt.metadata_received_alert):
90
+ if alert.handle == handle:
91
+ metadata_received = True
92
+ break
93
+ time.sleep(0.5)
94
 
95
  # 获取文件信息
96
+ ti = handle.get_torrent_info()
97
+ state.status = f"开始下载 {ti.name()}"
98
+ logging.info(f"开始下载: {ti.name()}")
99
 
100
+ # 启动下载
101
+ handle.set_sequential_download(True)
102
+ handle.resume()
103
 
104
+ # 下载进度监控
105
+ state.is_downloading = True
106
+ while not handle.status().is_seeding:
107
  status = handle.status()
108
+ state.progress = status.progress * 100
109
+
110
+ # 检查种子状态
111
+ if status.state == lt.torrent_status.downloading_metadata:
112
+ state.status = "获取元数据..."
113
+ elif status.state == lt.torrent_status.downloading:
114
+ dl = status.download_rate / 1000
115
+ up = status.upload_rate / 1000
116
+ peers = status.num_peers
117
+ state.status = f"下载中: {dl:.1f}kB/s ↑{up:.1f}kB/s ↔{peers} peers"
118
+
119
  time.sleep(1)
120
 
121
  # 下载完成
122
+ state.is_downloading = False
123
+ state.complete = True
124
+ state.file_path = save_path
125
+ state.status = "下载完成"
126
+ logging.info("下载完成")
127
 
128
  except Exception as e:
129
+ logging.error(f"下载错误: {str(e)}")
130
+ state.status = f"错误: {str(e)}"
131
+ state.is_downloading = False
132
  finally:
133
  sync_session_state()
134
 
135
+ # Streamlit界面
136
  init_session_state()
137
+ st.title("🚀 磁力链接下载器")
138
 
139
+ with st.form("magnet_form"):
140
+ magnet = st.text_input("磁力链接", placeholder="magnet:?xt=urn:btih:...")
141
+ submitted = st.form_submit_button("开始下载",
142
+ disabled=st.session_state.download['is_downloading'])
143
+
144
+ if submitted:
145
+ if not magnet.startswith("magnet:"):
146
+ st.error("无效的磁力链接格式")
147
+ else:
148
+ save_dir = "./downloads"
149
+ os.makedirs(save_dir, exist_ok=True)
150
+
151
+ state = DownloadState()
152
+ state.reset()
153
+ sync_session_state()
154
+
155
+ threading.Thread(
156
+ target=download_worker,
157
+ args=(magnet, save_dir),
158
+ daemon=True
159
+ ).start()
160
 
161
+ # 状态显示
162
  sync_session_state()
163
 
 
164
  if st.session_state.download['is_downloading']:
165
+ cols = st.columns([1,3])
166
+ with cols[0]:
167
+ st.metric("进度", f"{st.session_state.download['progress']:.1f}%")
168
+ with cols[1]:
169
+ st.progress(st.session_state.download['progress']/100)
170
  st.info(st.session_state.download['status'])
171
 
172
  if st.session_state.download['complete']:
173
+ st.success("下载完成!")
174
+ files = [f for f in os.listdir(st.session_state.download['file_path'])
175
+ if os.path.isfile(os.path.join(st.session_state.download['file_path'], f))]
 
 
176
 
177
  if files:
178
+ with st.expander("下载文件"):
179
+ selected = st.selectbox("选择文件", files)
180
+ with open(os.path.join(st.session_state.download['file_path'], selected), "rb") as f:
181
+ st.download_button(
182
+ "下载文件",
183
+ f,
184
+ file_name=selected,
185
+ mime="application/octet-stream"
186
+ )
187
+
188
+ # 状态监控线程
189
+ if 'monitor' not in st.session_state:
190
+ def status_monitor():
191
+ while True:
192
+ sync_session_state()
193
+ time.sleep(0.5)
194
+
195
+ threading.Thread(target=status_monitor, daemon=True).start()
196
+ st.session_state.monitor = True