Pyckplayer 1.0.4__tar.gz → 1.0.7__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.
Files changed (60) hide show
  1. {pyckplayer-1.0.4/src/Pyckplayer.egg-info → pyckplayer-1.0.7}/PKG-INFO +1 -1
  2. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/setup.py +1 -1
  3. {pyckplayer-1.0.4 → pyckplayer-1.0.7/src/Pyckplayer.egg-info}/PKG-INFO +1 -1
  4. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/core.py +128 -5
  5. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/LICENSE +0 -0
  6. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/MANIFEST.in +0 -0
  7. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/README.md +0 -0
  8. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/pyproject.toml +0 -0
  9. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/setup.cfg +0 -0
  10. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/Pyckplayer.egg-info/SOURCES.txt +0 -0
  11. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/Pyckplayer.egg-info/dependency_links.txt +0 -0
  12. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/Pyckplayer.egg-info/entry_points.txt +0 -0
  13. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/Pyckplayer.egg-info/requires.txt +0 -0
  14. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/Pyckplayer.egg-info/top_level.txt +0 -0
  15. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/__init__.py +0 -0
  16. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/LICENSE +0 -0
  17. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/css/ckplayer.css +0 -0
  18. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/css/ckplayer.ixigua.css +0 -0
  19. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/css/ckplayer.red.css +0 -0
  20. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/css/images/adclose.png +0 -0
  21. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/css/images/buffer.png +0 -0
  22. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/css/images/ckplayer.48.png +0 -0
  23. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/css/images/ckplayer.png +0 -0
  24. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/css/images/ckplayer_ixigua.48.png +0 -0
  25. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/css/images/ckplayer_ixigua.png +0 -0
  26. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/css/images/ckplayer_red.48.png +0 -0
  27. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/css/images/ckplayer_red.png +0 -0
  28. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/css/images/favicon.ico +0 -0
  29. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/css/images/loading.png +0 -0
  30. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/css/images/logo.png +0 -0
  31. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/css/images/play.png +0 -0
  32. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/flv.js/LICENSE +0 -0
  33. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/flv.js/README.md +0 -0
  34. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/flv.js/flv.js +0 -0
  35. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/flv.js/flv.js.map +0 -0
  36. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/flv.js/flv.min.js +0 -0
  37. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/flv.js/flv.min.js.map +0 -0
  38. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/hls.js/LICENSE +0 -0
  39. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/hls.js/README.md +0 -0
  40. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/hls.js/hls-demo.js +0 -0
  41. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/hls.js/hls-demo.js.map +0 -0
  42. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/hls.js/hls.js +0 -0
  43. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/hls.js/hls.js.d.ts +0 -0
  44. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/hls.js/hls.js.map +0 -0
  45. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/hls.js/hls.light.js +0 -0
  46. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/hls.js/hls.light.js.map +0 -0
  47. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/hls.js/hls.light.min.js +0 -0
  48. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/hls.js/hls.light.min.js.map +0 -0
  49. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/hls.js/hls.min.js +0 -0
  50. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/hls.js/hls.min.js.map +0 -0
  51. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/js/ckplayer.js +0 -0
  52. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/js/ckplayer.min.js +0 -0
  53. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/language/en.js +0 -0
  54. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/language/zh.cn.js +0 -0
  55. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/language/zh.hk.js +0 -0
  56. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/mpegts.js/LICENSE +0 -0
  57. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/mpegts.js/mpegts.js +0 -0
  58. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/mpegts.js/mpegts.js.map +0 -0
  59. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/ckplayer/mpegts.js/mpegts.min.js +0 -0
  60. {pyckplayer-1.0.4 → pyckplayer-1.0.7}/src/pyckplayer/cli.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Pyckplayer
3
- Version: 1.0.4
3
+ Version: 1.0.7
4
4
  Summary: 一个基于CKPlayer的Python视频播放器包装器
5
5
  Home-page: https://github.com/yourusername/pyckplayer
6
6
  Author: lenvy1
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="Pyckplayer",
8
- version="1.0.4",
8
+ version="1.0.7",
9
9
  author="lenvy1",
10
10
  author_email="lenvy1@163.com",
11
11
  description="一个基于CKPlayer的Python视频播放器包装器",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Pyckplayer
3
- Version: 1.0.4
3
+ Version: 1.0.7
4
4
  Summary: 一个基于CKPlayer的Python视频播放器包装器
5
5
  Home-page: https://github.com/yourusername/pyckplayer
6
6
  Author: lenvy1
@@ -6,7 +6,12 @@ import json
6
6
  import atexit
7
7
  from pathlib import Path
8
8
  from urllib.parse import quote
9
- from typing import Optional, Tuple, Union
9
+ from typing import Optional, Tuple, Union, Dict, Any
10
+
11
+ try:
12
+ import cv2
13
+ except ImportError:
14
+ cv2 = None
10
15
 
11
16
 
12
17
  DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
@@ -91,8 +96,106 @@ def download_cover(poster_url: Optional[str], silent: bool = True) -> Optional[s
91
96
  return None
92
97
 
93
98
 
99
+ def is_streaming_protocol(url: str) -> bool:
100
+ streaming_protocols = [
101
+ 'rtsp://', 'rtmp://', 'rtmps://', 'rtmpt://', 'rtmpte://',
102
+ 'http://', 'https://'
103
+ ]
104
+ url_lower = url.lower()
105
+ return any(url_lower.startswith(protocol) for protocol in streaming_protocols)
106
+
107
+
108
+ def get_content_type(url: str) -> Optional[str]:
109
+ try:
110
+ response = requests.head(url, timeout=5, allow_redirects=True)
111
+ if 'Content-Type' in response.headers:
112
+ return response.headers['Content-Type']
113
+ except Exception:
114
+ pass
115
+ return None
116
+
117
+
118
+ def is_streaming_content(content_type: str) -> bool:
119
+ streaming_content_types = [
120
+ 'application/vnd.apple.mpegurl',
121
+ 'application/dash+xml',
122
+ 'audio/mpegurl',
123
+ 'video/mp4',
124
+ 'video/flv',
125
+ 'video/quicktime',
126
+ 'application/x-mpegURL',
127
+ 'application/octet-stream'
128
+ ]
129
+ if content_type:
130
+ content_type_lower = content_type.lower()
131
+ return any(ct in content_type_lower for ct in streaming_content_types)
132
+ return False
133
+
134
+
135
+ def get_video_info(video_url: str) -> Optional[Dict[str, Any]]:
136
+ if cv2 is None:
137
+ return None
138
+
139
+ cap = cv2.VideoCapture(video_url)
140
+ if not cap.isOpened():
141
+ return None
142
+
143
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
144
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
145
+ fps = cap.get(cv2.CAP_PROP_FPS)
146
+ frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
147
+ duration = frame_count / fps if fps > 0 else 0
148
+ fourcc_code = int(cap.get(cv2.CAP_PROP_FOURCC))
149
+ fourcc = (
150
+ chr((fourcc_code >> 0) & 0xFF)
151
+ + chr((fourcc_code >> 8) & 0xFF)
152
+ + chr((fourcc_code >> 16) & 0xFF)
153
+ + chr((fourcc_code >> 24) & 0xFF)
154
+ )
155
+ bitrate = int(cap.get(cv2.CAP_PROP_BITRATE))
156
+
157
+ cap.release()
158
+
159
+ return {
160
+ "width": width,
161
+ "height": height,
162
+ "resolution": f"{width}x{height}",
163
+ "fps": round(fps, 2),
164
+ "frame_count": frame_count,
165
+ "duration": round(duration, 2),
166
+ "fourcc": fourcc,
167
+ "bitrate": bitrate,
168
+ }
169
+
170
+
171
+ def is_live_url(url: str) -> bool:
172
+ url_lower = url.lower()
173
+ live_keywords = [
174
+ '/live/', '/stream/', '/broadcast/', '/live-stream/',
175
+ 'live.', 'stream.', 'live_', 'stream_',
176
+ '?live', '&live', '=live',
177
+ 'douyincdn.com', 'live.kuaishou.com', 'live.bilibili.com',
178
+ 'live.douyin.com', 'live.qq.com', 'live.weibo.com'
179
+ ]
180
+ if any(keyword in url_lower for keyword in live_keywords):
181
+ return True
182
+
183
+ video_info = get_video_info(url)
184
+ if video_info is not None:
185
+ duration = video_info.get("duration", 0)
186
+ if duration < 0:
187
+ return True
188
+
189
+ content_type = get_content_type(url)
190
+ if content_type:
191
+ return is_streaming_content(content_type)
192
+
193
+ return False
194
+
195
+
94
196
  def detect_stream_type(url: str) -> str:
95
197
  url_lower = url.lower()
198
+
96
199
  if '.m3u8' in url_lower or '/hls/' in url_lower:
97
200
  return 'hls'
98
201
  elif '.flv' in url_lower:
@@ -101,8 +204,22 @@ def detect_stream_type(url: str) -> str:
101
204
  return 'ts'
102
205
  elif '.mp4' in url_lower:
103
206
  return 'mp4'
104
- else:
105
- return 'auto'
207
+ elif url_lower.startswith('rtsp://'):
208
+ return 'rtsp'
209
+ elif url_lower.startswith('rtmp://') or url_lower.startswith('rtmps://'):
210
+ return 'rtmp'
211
+
212
+ content_type = get_content_type(url)
213
+ if content_type:
214
+ content_type_lower = content_type.lower()
215
+ if 'mpegurl' in content_type_lower or 'hls' in content_type_lower:
216
+ return 'hls'
217
+ elif 'flv' in content_type_lower:
218
+ return 'flv'
219
+ elif 'dash' in content_type_lower:
220
+ return 'dash'
221
+
222
+ return 'auto'
106
223
 
107
224
 
108
225
  def generate_html(video_url: str,
@@ -114,6 +231,7 @@ def generate_html(video_url: str,
114
231
  favicon_path = quote((script_dir / "ckplayer" / "css" / "images" / "logo.png").as_posix())
115
232
 
116
233
  stream_type = detect_stream_type(video_url)
234
+ is_live = is_live_url(video_url)
117
235
 
118
236
  video_object = {
119
237
  "container": "#player",
@@ -126,12 +244,17 @@ def generate_html(video_url: str,
126
244
 
127
245
  if stream_type == 'hls':
128
246
  video_object["plug"] = "hls.js"
129
- video_object["live"] = True
247
+ if is_live:
248
+ video_object["live"] = True
130
249
  elif stream_type == 'flv':
131
250
  video_object["plug"] = "flv.js"
132
- video_object["live"] = True
251
+ if is_live:
252
+ video_object["live"] = True
133
253
  elif stream_type == 'ts':
134
254
  video_object["plug"] = "mpegts.js"
255
+ if is_live:
256
+ video_object["live"] = True
257
+ elif stream_type == 'mp4' and is_live:
135
258
  video_object["live"] = True
136
259
 
137
260
  if poster_url:
File without changes
File without changes
File without changes
File without changes
File without changes