nbpl 0.1.0__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.
- nbpl/__init__.py +25 -0
- nbpl/__main__.py +3 -0
- nbpl/cli.py +25 -0
- nbpl/watcher.py +128 -0
- nbpl-0.1.0.dist-info/METADATA +84 -0
- nbpl-0.1.0.dist-info/RECORD +11 -0
- nbpl-0.1.0.dist-info/WHEEL +5 -0
- nbpl-0.1.0.dist-info/entry_points.txt +2 -0
- nbpl-0.1.0.dist-info/licenses/LICENSE +21 -0
- nbpl-0.1.0.dist-info/top_level.txt +2 -0
- sitecustomize.py +9 -0
nbpl/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""检测运行中的 Python 脚本,并打开对应的视频。"""
|
|
2
|
+
|
|
3
|
+
from .watcher import (
|
|
4
|
+
BILIBILI_URL,
|
|
5
|
+
YOUTUBE_URL,
|
|
6
|
+
ScriptProcess,
|
|
7
|
+
detect_country_code,
|
|
8
|
+
find_running_python_scripts,
|
|
9
|
+
maybe_open_for_current_python_script,
|
|
10
|
+
open_video_for_country,
|
|
11
|
+
run_once,
|
|
12
|
+
watch,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"BILIBILI_URL",
|
|
17
|
+
"YOUTUBE_URL",
|
|
18
|
+
"ScriptProcess",
|
|
19
|
+
"detect_country_code",
|
|
20
|
+
"find_running_python_scripts",
|
|
21
|
+
"maybe_open_for_current_python_script",
|
|
22
|
+
"open_video_for_country",
|
|
23
|
+
"run_once",
|
|
24
|
+
"watch",
|
|
25
|
+
]
|
nbpl/__main__.py
ADDED
nbpl/cli.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""nbpl 的命令行入口。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
from .watcher import watch
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main() -> None:
|
|
11
|
+
parser = argparse.ArgumentParser(
|
|
12
|
+
description="检测到 Python 脚本运行时,按所在地区打开对应的视频。"
|
|
13
|
+
)
|
|
14
|
+
parser.add_argument(
|
|
15
|
+
"--interval",
|
|
16
|
+
type=float,
|
|
17
|
+
default=1.0,
|
|
18
|
+
help="进程检查间隔(秒),默认:1",
|
|
19
|
+
)
|
|
20
|
+
parser.add_argument(
|
|
21
|
+
"--country-code",
|
|
22
|
+
help="覆盖公网 IP 地区检测,适合测试,例如 CN",
|
|
23
|
+
)
|
|
24
|
+
args = parser.parse_args()
|
|
25
|
+
watch(interval=args.interval, country_code=args.country_code)
|
nbpl/watcher.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""nbpl 的进程监控与地区检测逻辑。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
import time
|
|
8
|
+
import webbrowser
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Callable, Optional
|
|
12
|
+
|
|
13
|
+
BILIBILI_URL = "https://www.bilibili.com/video/BV1GJ411x7h7"
|
|
14
|
+
YOUTUBE_URL = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
|
|
15
|
+
IP_API_URL = "https://ipapi.co/json/"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class ScriptProcess:
|
|
20
|
+
"""一个关联到 Python 源文件的运行中 Python 解释器。"""
|
|
21
|
+
|
|
22
|
+
pid: int
|
|
23
|
+
script: str
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _is_python_interpreter(executable: str) -> bool:
|
|
27
|
+
name = Path(executable).name.lower()
|
|
28
|
+
return name.startswith("python") or name in {"py", "py.exe"}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _script_from_cmdline(cmdline: list[str]) -> Optional[str]:
|
|
32
|
+
"""从 Python 解释器的命令行中返回第一个直接传入的源文件。"""
|
|
33
|
+
if not cmdline or not _is_python_interpreter(cmdline[0]):
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
for argument in cmdline[1:]:
|
|
37
|
+
if argument.lower().endswith((".py", ".pyw")):
|
|
38
|
+
return argument
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def find_running_python_scripts() -> list[ScriptProcess]:
|
|
43
|
+
"""列出正在执行 Python 源文件的其他进程。"""
|
|
44
|
+
try:
|
|
45
|
+
import psutil
|
|
46
|
+
except ImportError as exc:
|
|
47
|
+
raise RuntimeError("nbpl 依赖 psutil,请重新安装本包") from exc
|
|
48
|
+
|
|
49
|
+
found: list[ScriptProcess] = []
|
|
50
|
+
this_pid = os.getpid()
|
|
51
|
+
for process in psutil.process_iter(["pid", "cmdline"]):
|
|
52
|
+
try:
|
|
53
|
+
info = process.info
|
|
54
|
+
pid = info["pid"]
|
|
55
|
+
if pid == this_pid:
|
|
56
|
+
continue
|
|
57
|
+
script = _script_from_cmdline(info.get("cmdline") or [])
|
|
58
|
+
if script:
|
|
59
|
+
found.append(ScriptProcess(pid=pid, script=script))
|
|
60
|
+
except (psutil.AccessDenied, psutil.NoSuchProcess, psutil.ZombieProcess):
|
|
61
|
+
continue
|
|
62
|
+
return found
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def detect_country_code(timeout: float = 5.0) -> Optional[str]:
|
|
66
|
+
"""返回公网 IP 的国家/地区代码;无法判断时返回 None。"""
|
|
67
|
+
try:
|
|
68
|
+
import requests
|
|
69
|
+
except ImportError as exc:
|
|
70
|
+
raise RuntimeError("nbpl 依赖 requests,请重新安装本包") from exc
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
response = requests.get(IP_API_URL, timeout=timeout)
|
|
74
|
+
response.raise_for_status()
|
|
75
|
+
country_code = response.json().get("country_code")
|
|
76
|
+
return country_code.upper() if isinstance(country_code, str) else None
|
|
77
|
+
except (requests.RequestException, ValueError):
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def open_video_for_country(country_code: Optional[str]) -> str:
|
|
82
|
+
"""按国家/地区代码打开并返回相应的视频 URL。"""
|
|
83
|
+
url = BILIBILI_URL if country_code == "CN" else YOUTUBE_URL
|
|
84
|
+
webbrowser.open(url)
|
|
85
|
+
return url
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def maybe_open_for_current_python_script() -> Optional[str]:
|
|
89
|
+
"""当前解释器执行 Python 源文件时自动打开视频。
|
|
90
|
+
|
|
91
|
+
设置环境变量 NBPL_DISABLE_AUTO_OPEN=1 可关闭自动行为。
|
|
92
|
+
"""
|
|
93
|
+
if os.environ.get("NBPL_DISABLE_AUTO_OPEN") == "1":
|
|
94
|
+
return None
|
|
95
|
+
script = sys.argv[0] if sys.argv else ""
|
|
96
|
+
if not script.lower().endswith((".py", ".pyw")):
|
|
97
|
+
return None
|
|
98
|
+
return open_video_for_country(detect_country_code())
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def run_once(country_code: Optional[str] = None) -> Optional[str]:
|
|
102
|
+
"""发现其他 Python 脚本正在运行时,仅打开一次视频。
|
|
103
|
+
|
|
104
|
+
可传入 country_code 以方便测试;未传入时会通过公网 IP 检测。
|
|
105
|
+
"""
|
|
106
|
+
if not find_running_python_scripts():
|
|
107
|
+
return None
|
|
108
|
+
return open_video_for_country(
|
|
109
|
+
detect_country_code() if country_code is None else country_code
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def watch(
|
|
114
|
+
interval: float = 1.0,
|
|
115
|
+
country_code: Optional[str] = None,
|
|
116
|
+
on_open: Optional[Callable[[str], None]] = None,
|
|
117
|
+
) -> None:
|
|
118
|
+
"""等待 Python 脚本出现,打开视频后返回。"""
|
|
119
|
+
if interval <= 0:
|
|
120
|
+
raise ValueError("interval 必须大于零")
|
|
121
|
+
|
|
122
|
+
while True:
|
|
123
|
+
url = run_once(country_code=country_code)
|
|
124
|
+
if url:
|
|
125
|
+
if on_open:
|
|
126
|
+
on_open(url)
|
|
127
|
+
return
|
|
128
|
+
time.sleep(interval)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nbpl
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: 检测正在运行的 Python 脚本,并按地区打开对应视频。
|
|
5
|
+
Author: hekuo5310
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: psutil>=5.9
|
|
11
|
+
Requires-Dist: requests>=2.28
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
14
|
+
# nbpl
|
|
15
|
+
|
|
16
|
+
安装 nbpl 后,不需要在任何脚本中导入它。它会通过 Python 的 sitecustomize 启动钩子,在执行 Python 源文件时自动使用系统默认浏览器打开一个视频:
|
|
17
|
+
|
|
18
|
+
| 公网 IP 所在国家/地区代码 | 打开的链接 |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| CN(中国大陆) | https://www.bilibili.com/video/BV1GJ411x7h7 |
|
|
21
|
+
| 其他代码,或定位失败 | https://www.youtube.com/watch?v=dQw4w9WgXcQ |
|
|
22
|
+
|
|
23
|
+
地区查询使用 https://ipapi.co/json/。本包仅会读取进程命令行并发起一次 IP 地理位置查询;不会上传检测到的脚本路径。
|
|
24
|
+
|
|
25
|
+
## 安装
|
|
26
|
+
|
|
27
|
+
~~~bash
|
|
28
|
+
pip install nbpl
|
|
29
|
+
~~~
|
|
30
|
+
|
|
31
|
+
也可以直接从本仓库安装:
|
|
32
|
+
|
|
33
|
+
~~~bash
|
|
34
|
+
pip install git+https://github.com/hekuo5310/nbpl.git
|
|
35
|
+
~~~
|
|
36
|
+
|
|
37
|
+
## 自动使用
|
|
38
|
+
|
|
39
|
+
安装后,直接运行任意 Python 文件即可自动触发:
|
|
40
|
+
|
|
41
|
+
~~~bash
|
|
42
|
+
python app.py
|
|
43
|
+
~~~
|
|
44
|
+
|
|
45
|
+
如果只想作为独立监控器使用,仍可运行:
|
|
46
|
+
|
|
47
|
+
~~~bash
|
|
48
|
+
nbpl
|
|
49
|
+
~~~
|
|
50
|
+
|
|
51
|
+
独立监控器默认每秒检查一次;发现其他 Python 脚本后打开对应链接并退出。修改检查间隔:
|
|
52
|
+
|
|
53
|
+
~~~bash
|
|
54
|
+
nbpl --interval 2
|
|
55
|
+
~~~
|
|
56
|
+
|
|
57
|
+
本地测试时可跳过 IP 查询,直接指定地区:
|
|
58
|
+
|
|
59
|
+
~~~bash
|
|
60
|
+
nbpl --country-code CN
|
|
61
|
+
~~~
|
|
62
|
+
|
|
63
|
+
## 临时关闭自动触发
|
|
64
|
+
|
|
65
|
+
为避免自动打开浏览器,可在运行命令前设置环境变量:
|
|
66
|
+
|
|
67
|
+
~~~bash
|
|
68
|
+
NBPL_DISABLE_AUTO_OPEN=1 python app.py
|
|
69
|
+
~~~
|
|
70
|
+
|
|
71
|
+
Windows PowerShell:
|
|
72
|
+
|
|
73
|
+
~~~powershell
|
|
74
|
+
$env:NBPL_DISABLE_AUTO_OPEN = "1"; python app.py
|
|
75
|
+
~~~
|
|
76
|
+
|
|
77
|
+
## Python API
|
|
78
|
+
|
|
79
|
+
~~~python
|
|
80
|
+
from nbpl import run_once, watch
|
|
81
|
+
|
|
82
|
+
run_once() # 仅当已有其他 Python 脚本在运行时才打开链接
|
|
83
|
+
watch(interval=1.0) # 等待脚本出现,打开链接后返回
|
|
84
|
+
~~~
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
sitecustomize.py,sha256=CPEL8xVzOw_J_MYYCOKieZ3syNVaAGBStr8OiTgxZ0g,273
|
|
2
|
+
nbpl/__init__.py,sha256=S3DQWho0BkbavEr3nYIvAM4yuyXNYDGC2uEFe4iFkNg,542
|
|
3
|
+
nbpl/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30
|
|
4
|
+
nbpl/cli.py,sha256=jQoxbkS5dxQI27O4SlvcQnOnfZImurPbAuRvv6USLLo,654
|
|
5
|
+
nbpl/watcher.py,sha256=jzjPXp2kOM2nKuf__9HfpIbg6-fpgMkN9KMf4ivWYgc,4081
|
|
6
|
+
nbpl-0.1.0.dist-info/licenses/LICENSE,sha256=nPn8ikjQJsLFY8JnSqwps8ud39e1GGiIax5cXA00R5w,1066
|
|
7
|
+
nbpl-0.1.0.dist-info/METADATA,sha256=YIVSKcFLQvwJyZ2965pa1hHg7rRRDHvNjk5YIRpuvr4,1948
|
|
8
|
+
nbpl-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
9
|
+
nbpl-0.1.0.dist-info/entry_points.txt,sha256=0S2Q9Oiyvjx-atubBXxeRpee2iF1RZC5oJYNTSscqRI,39
|
|
10
|
+
nbpl-0.1.0.dist-info/top_level.txt,sha256=gfBXdr3y2pLYiwRzq3o9dB3ZDtSLAWWVak2xTVoNFN4,19
|
|
11
|
+
nbpl-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 牢废物
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|