Skip to content

Commit

Permalink
异步化重构
Browse files Browse the repository at this point in the history
  • Loading branch information
CXRunfree committed Sep 19, 2024
1 parent 0ad7f66 commit c07ef56
Show file tree
Hide file tree
Showing 7 changed files with 316 additions and 299 deletions.
413 changes: 179 additions & 234 deletions Autovisor.py

Large diffs are not rendered by default.

10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@

------

#### 2024/5/23 rebuild-3.13.1 更新
#### 2024/9/20 rebuild-3.14.1 更新

- **为适应习惯分机制, 此次更新后可重复刷已完成的课程小节**

(需要在config配置文件中将 **"enableRepeat"** 项 设为 **True**)

详细配置方式请见下方的 **"使用须知".**
- 此次更新将程序重构为**异步**代码,通过**协程**的方式播放视频,检测弹题和安全验证;
- 避免了因等待而阻塞主线程导致的卡死问题;
- 有效提高了弹窗检测的频率, 减少因弹窗卡死程序的情况。

------

Expand Down
5 changes: 3 additions & 2 deletions configs.ini
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[user-account]
;配置账号密码,留空则打开网页后需手动输入
username =
password =
password =
[custom-option]
;配置浏览器,可选Edge/Chrome
driver = Edge
Expand All @@ -11,12 +11,13 @@ EXE_PATH =
;允许重复刷已完成的课程(默认:False)
enableRepeat = False
;限制每门课程刷课时长/min (不限制:0)
limitMaxTime = 30
limitMaxTime = 0
;限定播放倍速 (最高:1.8)
limitSpeed = 1.8
[course-url]
;配置要学习的课程链接,可按下面格式继续添加
URL1 = https://studyvideoh5.zhihuishu.com/stuStudy?recruitAndCourseId=***
URL2 = https://studyvideoh5.zhihuishu.com/stuStudy?recruitAndCourseId=***
URL3 = https://studyvideoh5.zhihuishu.com/stuStudy?recruitAndCourseId=***
URL4 = https://studyvideoh5.zhihuishu.com/stuStudy?recruitAndCourseId=***
URLn = https://studyvideoh5.zhihuishu.com/stuStudy?recruitAndCourseId=***
17 changes: 9 additions & 8 deletions res/configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,30 +29,31 @@ def __init__(self, config_path=None):
self.gzh_pop = '''document.getElementsByClassName("course-warn")[0].click();'''
self.close_gjh = '''document.getElementsByClassName("rlready-bound-btn")[0].click();'''
self.close_ques = '''document.dispatchEvent(new KeyboardEvent('keydown', {bubbles: true, keyCode: 27 }));'''
self.close_assist = '''document.getElementsByClassName("show-icon icon-appear")[0].remove();'''
self.remove_assist = '''document.getElementsByClassName("ai-show-icon ai-icon-appear")[0].remove();'''
self.no_hint = '''document.querySelector('.hint_delete a').click();'''
# 其他
self.night_js = '''document.getElementsByClassName("Patternbtn-div")[0].click()'''

def _read_config(self):
def _read_config(self) -> None:
try:
self._config.read(self.config_path, encoding='utf-8')
except UnicodeDecodeError:
self._config.read(self.config_path, encoding='gbk')

def get_driver(self):
def get_driver(self) -> str:
driver = self._config.get('custom-option', 'driver', raw=True)
if not driver:
driver = "Edge"
return driver.lower()

def get_enableRepeat(self):
def get_enableRepeat(self) -> bool:
enableRepeat = self._config.get('custom-option', 'enableRepeat', raw=True).lower()
if enableRepeat == "true":
return True
else:
return False

def get_course_urls(self):
def get_course_urls(self) -> list:
course_urls = []
_options = self._config.options("course-url")
for _option in _options:
Expand All @@ -67,16 +68,16 @@ def get_course_urls(self):
# @property修饰器可设置属性
# 这样写可实时响应配置变化
@property
def limitMaxTime(self):
def limitMaxTime(self) -> float:
self._read_config()
return float(self._config.get('custom-option', 'limitMaxTime'))

@property
def limitSpeed(self):
def limitSpeed(self) -> float:
self._read_config()
return float(self._config.get('custom-option', 'limitSpeed', raw=True))

@property
def revise_speed_name(self):
def revise_speed_name(self) -> str:
return f'''document.getElementsByClassName("speedTab15")[0].innerText = "X {self.limitSpeed}";'''

49 changes: 0 additions & 49 deletions res/process.py

This file was deleted.

49 changes: 49 additions & 0 deletions res/progress.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# encoding=utf-8
from playwright.async_api import Page, TimeoutError


async def move_mouse(page: Page):
try:
await page.wait_for_selector(".videoArea", state="attached", timeout=5000)
elem = page.locator(".videoArea")
await elem.hover(timeout=4000)
pos = await elem.bounding_box()
if not pos:
return
# Calculate the target position to move the mouse
target_x = pos['x'] + 30
target_y = pos['y'] + 30
await page.mouse.move(target_x, target_y)
except TimeoutError:
return


async def get_progress(page: Page):
def parse_time(h, m, s):
return int(h) * 3600 + int(m) * 60 + int(s)

curtime = "0%"
await move_mouse(page)
cur_play = await page.query_selector(".current_play")
progress = await cur_play.query_selector(".progress-num")
total_time_selector = await cur_play.query_selector(".time.fl")
total_time_str = await total_time_selector.text_content()
total_time = parse_time(*total_time_str.split(":"))
if not progress:
finish = await cur_play.query_selector(".time_icofinish")
if finish:
curtime = "100%"
else:
curtime = await progress.text_content()

return curtime, total_time


def show_progress(desc, cur_str=None, enableRepeat=False):
percent = int(cur_str.split("%")[0]) + 1 # Handles a 1% rendering error
if percent >= 80 and not enableRepeat: # In learning mode, 80% progress is considered complete
percent = 100
length = int(percent * 30 // 100)
progress = ("█" * length).ljust(30, " ")
percent_str = str(percent) + "%"
print(f"\r{desc} |{progress}| {percent_str}\t", end="", flush=True)
72 changes: 72 additions & 0 deletions res/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from typing import List
from playwright.async_api import Page, Locator
from res.configs import Config
from res.progress import move_mouse
import time


async def optimize_page(page: Page, config: Config) -> None:
try:
await page.evaluate(config.pop_js)
hour = time.localtime().tm_hour
if hour >= 18 or hour < 7:
await page.wait_for_selector(".Patternbtn-div")
await page.evaluate(config.night_js)
await page.wait_for_selector(".ai-show-icon.ai-icon-appear")
await page.evaluate(config.remove_assist)
await page.evaluate(config.no_hint)
await page.evaluate(config.gzh_pop)
await page.wait_for_selector(".warn-box", timeout=1500)
await page.evaluate(config.close_gjh)
except TimeoutError:
return


async def get_lesson_name(page: Page) -> str:
title_ele = await page.wait_for_selector("#lessonOrder")
await page.wait_for_timeout(500)
title_ = await title_ele.get_attribute("title")
return title_


async def video_optimize(page: Page, config: Config) -> None:
try:
await move_mouse(page)
volumeBox = await page.wait_for_selector(".volumeBox")
await volumeBox.click()
await page.wait_for_timeout(200)
definiBox = await page.wait_for_selector(".definiBox")
await definiBox.hover()
low_quality = await page.wait_for_selector(".line1bq")
await low_quality.hover()
await low_quality.click()
await page.wait_for_timeout(200)
speedBox = await page.wait_for_selector(".speedBox")
await speedBox.hover()
await page.evaluate(config.revise_speed_name)
max_speed = await page.wait_for_selector(".speedTab15")
await max_speed.hover()
revise_speed = page.locator("div[rate=\"1.5\"]")
await revise_speed.evaluate(
f'revise => revise.setAttribute("rate","{config.limitSpeed}");'
)
await max_speed.click()
except Exception as e:
print(f"\n[Warn]{repr(e)}")


async def get_filtered_class(page: Page, enableRepeat=False) -> List[Locator]:
try:
await page.wait_for_selector(".time_icofinish", timeout=1000)
except TimeoutError:
pass
all_class = await page.locator(".clearfix.video").all()
if enableRepeat:
return all_class
else:
new_class = []
for each in all_class:
isDone = await each.locator(".time_icofinish").count()
if not isDone:
new_class.append(each)
return new_class

0 comments on commit c07ef56

Please sign in to comment.