Compare commits

..

6 Commits

Author SHA1 Message Date
acgnhik
8547834c2a release: 1.12.0
fix #129
fix #132
fix #136
2022-12-04 14:54:01 +08:00
acgnhik
08130d5e61 refactor: refactor danmaku client 2022-12-04 14:29:07 +08:00
acgnhiki
46811d4677 Merge pull request #135 from sihuan/custom_tg_api
自定义 telegram bot api 地址
2022-11-27 13:43:29 +08:00
SiHuan
dd819e65e8 添加自定义 telegram bot api 地址 2022-11-26 15:21:00 +08:00
acgnhik
4cd3acd09d release: 1.11.1 2022-11-06 13:23:53 +08:00
acgnhik
f6249c1fe3 fix: meta.json files not been removed 2022-11-06 13:17:36 +08:00
16 changed files with 102 additions and 43 deletions

View File

@@ -1,5 +1,14 @@
# 更新日志
## 1.12.0
- 支持自定义 Telegram bot api 地址
- 重构弹幕客户端: 避免接收的数据有问题导致崩溃,调整弹幕接收超时时间。
## 1.11.1
- 修复 `meta.json` 文件没被删除
## 1.11.0
- 改善 HLS 标准录制模式的稳定性

View File

@@ -1,3 +1,3 @@
__prog__ = 'blrec'
__version__ = '1.11.0'
__version__ = '1.12.0'
__github__ = 'https://github.com/acgnhiki/blrec'

View File

@@ -52,7 +52,7 @@ class DanmakuClient(EventEmitter[DanmakuListener], AsyncStoppableMixin):
webapi: WebApi,
room_id: int,
*,
max_retries: int = 10,
max_retries: int = 60,
headers: Optional[Dict[str, str]] = None,
) -> None:
super().__init__()
@@ -237,7 +237,11 @@ class DanmakuClient(EventEmitter[DanmakuListener], AsyncStoppableMixin):
try:
await self._ws.send_bytes(data)
except Exception as exc:
logger.debug(f'Failed to send heartbeat due to: {repr(exc)}')
logger.warning(f'Failed to send heartbeat: {repr(exc)}')
await self._emit('error_occurred', exc)
task = asyncio.create_task(self.restart())
task.add_done_callback(exception_callback)
break
await asyncio.sleep(self._HEARTBEAT_INTERVAL)
async def _create_message_loop(self) -> None:
@@ -261,48 +265,53 @@ class DanmakuClient(EventEmitter[DanmakuListener], AsyncStoppableMixin):
await self._emit('danmaku_received', msg)
async def _receive(self) -> List[Dict[str, Any]]:
self._retry_count = 0
self._retry_delay = 0
self._reset_retry()
while True:
try:
wsmsg = await self._ws.receive(timeout=self._HEARTBEAT_INTERVAL)
except asyncio.TimeoutError as e:
logger.debug(f'Failed to receive message due to: {repr(e)}')
continue
wsmsg = await self._ws.receive(timeout=self._HEARTBEAT_INTERVAL * 2)
except Exception as e:
await self._handle_error(e)
await self._handle_receive_error(e)
else:
if wsmsg.type == aiohttp.WSMsgType.BINARY:
if result := await self._handle_data(wsmsg.data):
return result
elif wsmsg.type == aiohttp.WSMsgType.ERROR:
await self._handle_error(cast(Exception, wsmsg.data))
await self._handle_receive_error(cast(Exception, wsmsg.data))
elif wsmsg.type == aiohttp.WSMsgType.CLOSED:
msg = 'WebSocket Closed'
exc = aiohttp.WebSocketError(self._ws.close_code or 1006, msg)
await self._handle_error(exc)
await self._handle_receive_error(exc)
else:
await self._handle_error(ValueError(wsmsg))
await self._handle_receive_error(ValueError(wsmsg))
@staticmethod
async def _handle_data(data: bytes) -> Optional[List[Dict[str, Any]]]:
loop = asyncio.get_running_loop()
op, msg = await loop.run_in_executor(None, Frame.decode, data)
if op == WS.OP_MESSAGE:
msg = cast(List[str], msg)
return [json.loads(m) for m in msg]
elif op == WS.OP_HEARTBEAT_REPLY:
return None
else:
return None
try:
op, msg = await loop.run_in_executor(None, Frame.decode, data)
if op == WS.OP_MESSAGE:
msg = cast(List[str], msg)
return [json.loads(m) for m in msg]
elif op == WS.OP_HEARTBEAT_REPLY:
pass
except Exception as e:
logger.warning(f'Failed to handle data: {repr(e)}, data: {repr(data)}')
async def _handle_error(self, exc: Exception) -> None:
logger.debug(f'Failed to receive message due to: {repr(exc)}')
return None
async def _handle_receive_error(self, exc: Exception) -> None:
logger.warning(f'Failed to receive message: {repr(exc)}')
await self._emit('error_occurred', exc)
if isinstance(exc, asyncio.TimeoutError):
return
await self._retry()
def _reset_retry(self) -> None:
self._retry_count = 0
self._retry_delay = 0
async def _retry(self) -> None:
if self._retry_count < self._MAX_RETRIES:
if self._retry_delay > 0:

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -10,6 +10,6 @@
<body>
<app-root></app-root>
<noscript>Please enable JavaScript to continue using this application.</noscript>
<script src="runtime.0560bf422fb1ab67.js" type="module"></script><script src="polyfills.4b08448aee19bb22.js" type="module"></script><script src="main.dbd09d2079405adc.js" type="module"></script>
<script src="runtime.a904720a2e39ffc3.js" type="module"></script><script src="polyfills.4b08448aee19bb22.js" type="module"></script><script src="main.dbd09d2079405adc.js" type="module"></script>
</body></html>

View File

@@ -1,6 +1,6 @@
{
"configVersion": 1,
"timestamp": 1667707262978,
"timestamp": 1669447100875,
"index": "/index.html",
"assetGroups": [
{
@@ -15,14 +15,14 @@
"/146.5a8902910bda9e87.js",
"/183.ee55fc76717674c3.js",
"/45.c90c3cea2bf1a66e.js",
"/548.91bbb60199d9e944.js",
"/91.3c224fe84835dadd.js",
"/548.4789e17f7acce023.js",
"/91.07ca0767ccc21566.js",
"/common.858f777e9296e6f2.js",
"/index.html",
"/main.dbd09d2079405adc.js",
"/manifest.webmanifest",
"/polyfills.4b08448aee19bb22.js",
"/runtime.0560bf422fb1ab67.js",
"/runtime.a904720a2e39ffc3.js",
"/styles.2e152d608221c2ee.css"
],
"patterns": []
@@ -1638,8 +1638,8 @@
"/146.5a8902910bda9e87.js": "d9c33c7073662699f00f46f3a384ae5b749fdef9",
"/183.ee55fc76717674c3.js": "2628c996ec80a6c6703d542d34ac95194283bcf8",
"/45.c90c3cea2bf1a66e.js": "e5bfb8cf3803593e6b8ea14c90b3d3cb6a066764",
"/548.91bbb60199d9e944.js": "062b3a6424284294e5774bcb08ca76df7b0c4216",
"/91.3c224fe84835dadd.js": "2e3cdb6c44a8cf3241fe8dd89b27c37f212768f8",
"/548.4789e17f7acce023.js": "3b8aaf921bd400fb32cc15135dd4de09deb2c824",
"/91.07ca0767ccc21566.js": "4105beda647cedabf52678640e8fe450671e2e45",
"/assets/animal/panda.js": "fec2868bb3053dd2da45f96bbcb86d5116ed72b1",
"/assets/animal/panda.svg": "bebd302cdc601e0ead3a6d2710acf8753f3d83b1",
"/assets/fill/.gitkeep": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
@@ -3234,11 +3234,11 @@
"/assets/twotone/warning.js": "fb2d7ea232f3a99bf8f080dbc94c65699232ac01",
"/assets/twotone/warning.svg": "8c7a2d3e765a2e7dd58ac674870c6655cecb0068",
"/common.858f777e9296e6f2.js": "b68ca68e1e214a2537d96935c23410126cc564dd",
"/index.html": "17482e27906b5ae0447920edbf4bf4f4c0c1838b",
"/index.html": "9ba0d26d371e607af065904e06d098a0698f75a3",
"/main.dbd09d2079405adc.js": "2f7284b616ed9fc433b612c9dca53dc06a0f3aa1",
"/manifest.webmanifest": "62c1cb8c5ad2af551a956b97013ab55ce77dd586",
"/manifest.webmanifest": "0c4534b4c868d756691b1b4372cecb2efce47c6d",
"/polyfills.4b08448aee19bb22.js": "8e73f2d42cc13ca353cea5c886d930bd6da08d0d",
"/runtime.0560bf422fb1ab67.js": "74c07903a5fd6d43a0d7690a93b0927f8eead22c",
"/runtime.a904720a2e39ffc3.js": "d9eb86363e3840a15e5659af6f04f29e19df9233",
"/styles.2e152d608221c2ee.css": "9830389a46daa5b4511e0dd343aad23ca9f9690f"
},
"navigationUrls": [

View File

@@ -1 +1 @@
(()=>{"use strict";var e,v={},m={};function r(e){var f=m[e];if(void 0!==f)return f.exports;var t=m[e]={exports:{}};return v[e].call(t.exports,t,t.exports,r),t.exports}r.m=v,e=[],r.O=(f,t,i,o)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,i,o]=e[n],c=!0,l=0;l<t.length;l++)(!1&o||a>=o)&&Object.keys(r.O).every(p=>r.O[p](t[l]))?t.splice(l--,1):(c=!1,o<a&&(a=o));if(c){e.splice(n--,1);var d=i();void 0!==d&&(f=d)}}return f}o=o||0;for(var n=e.length;n>0&&e[n-1][2]>o;n--)e[n]=e[n-1];e[n]=[t,i,o]},r.n=e=>{var f=e&&e.__esModule?()=>e.default:()=>e;return r.d(f,{a:f}),f},r.d=(e,f)=>{for(var t in f)r.o(f,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:f[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((f,t)=>(r.f[t](e,f),f),[])),r.u=e=>(592===e?"common":e)+"."+{45:"c90c3cea2bf1a66e",91:"3c224fe84835dadd",103:"5b5d2a6e5a8a7479",146:"5a8902910bda9e87",183:"ee55fc76717674c3",548:"91bbb60199d9e944",592:"858f777e9296e6f2"}[e]+".js",r.miniCssF=e=>{},r.o=(e,f)=>Object.prototype.hasOwnProperty.call(e,f),(()=>{var e={},f="blrec:";r.l=(t,i,o,n)=>{if(e[t])e[t].push(i);else{var a,c;if(void 0!==o)for(var l=document.getElementsByTagName("script"),d=0;d<l.length;d++){var u=l[d];if(u.getAttribute("src")==t||u.getAttribute("data-webpack")==f+o){a=u;break}}a||(c=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",f+o),a.src=r.tu(t)),e[t]=[i];var s=(g,p)=>{a.onerror=a.onload=null,clearTimeout(b);var _=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),_&&_.forEach(h=>h(p)),g)return g(p)},b=setTimeout(s.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=s.bind(null,a.onerror),a.onload=s.bind(null,a.onload),c&&document.head.appendChild(a)}}})(),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e;r.tu=f=>(void 0===e&&(e={createScriptURL:t=>t},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e.createScriptURL(f))})(),r.p="",(()=>{var e={666:0};r.f.j=(i,o)=>{var n=r.o(e,i)?e[i]:void 0;if(0!==n)if(n)o.push(n[2]);else if(666!=i){var a=new Promise((u,s)=>n=e[i]=[u,s]);o.push(n[2]=a);var c=r.p+r.u(i),l=new Error;r.l(c,u=>{if(r.o(e,i)&&(0!==(n=e[i])&&(e[i]=void 0),n)){var s=u&&("load"===u.type?"missing":u.type),b=u&&u.target&&u.target.src;l.message="Loading chunk "+i+" failed.\n("+s+": "+b+")",l.name="ChunkLoadError",l.type=s,l.request=b,n[1](l)}},"chunk-"+i,i)}else e[i]=0},r.O.j=i=>0===e[i];var f=(i,o)=>{var l,d,[n,a,c]=o,u=0;if(n.some(b=>0!==e[b])){for(l in a)r.o(a,l)&&(r.m[l]=a[l]);if(c)var s=c(r)}for(i&&i(o);u<n.length;u++)r.o(e,d=n[u])&&e[d]&&e[d][0](),e[n[u]]=0;return r.O(s)},t=self.webpackChunkblrec=self.webpackChunkblrec||[];t.forEach(f.bind(null,0)),t.push=f.bind(null,t.push.bind(t))})()})();
(()=>{"use strict";var e,v={},m={};function r(e){var f=m[e];if(void 0!==f)return f.exports;var t=m[e]={exports:{}};return v[e].call(t.exports,t,t.exports,r),t.exports}r.m=v,e=[],r.O=(f,t,i,o)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,i,o]=e[n],c=!0,l=0;l<t.length;l++)(!1&o||a>=o)&&Object.keys(r.O).every(b=>r.O[b](t[l]))?t.splice(l--,1):(c=!1,o<a&&(a=o));if(c){e.splice(n--,1);var d=i();void 0!==d&&(f=d)}}return f}o=o||0;for(var n=e.length;n>0&&e[n-1][2]>o;n--)e[n]=e[n-1];e[n]=[t,i,o]},r.n=e=>{var f=e&&e.__esModule?()=>e.default:()=>e;return r.d(f,{a:f}),f},r.d=(e,f)=>{for(var t in f)r.o(f,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:f[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((f,t)=>(r.f[t](e,f),f),[])),r.u=e=>(592===e?"common":e)+"."+{45:"c90c3cea2bf1a66e",91:"07ca0767ccc21566",103:"5b5d2a6e5a8a7479",146:"5a8902910bda9e87",183:"ee55fc76717674c3",548:"4789e17f7acce023",592:"858f777e9296e6f2"}[e]+".js",r.miniCssF=e=>{},r.o=(e,f)=>Object.prototype.hasOwnProperty.call(e,f),(()=>{var e={},f="blrec:";r.l=(t,i,o,n)=>{if(e[t])e[t].push(i);else{var a,c;if(void 0!==o)for(var l=document.getElementsByTagName("script"),d=0;d<l.length;d++){var u=l[d];if(u.getAttribute("src")==t||u.getAttribute("data-webpack")==f+o){a=u;break}}a||(c=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",f+o),a.src=r.tu(t)),e[t]=[i];var s=(g,b)=>{a.onerror=a.onload=null,clearTimeout(p);var _=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),_&&_.forEach(h=>h(b)),g)return g(b)},p=setTimeout(s.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=s.bind(null,a.onerror),a.onload=s.bind(null,a.onload),c&&document.head.appendChild(a)}}})(),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e;r.tu=f=>(void 0===e&&(e={createScriptURL:t=>t},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e.createScriptURL(f))})(),r.p="",(()=>{var e={666:0};r.f.j=(i,o)=>{var n=r.o(e,i)?e[i]:void 0;if(0!==n)if(n)o.push(n[2]);else if(666!=i){var a=new Promise((u,s)=>n=e[i]=[u,s]);o.push(n[2]=a);var c=r.p+r.u(i),l=new Error;r.l(c,u=>{if(r.o(e,i)&&(0!==(n=e[i])&&(e[i]=void 0),n)){var s=u&&("load"===u.type?"missing":u.type),p=u&&u.target&&u.target.src;l.message="Loading chunk "+i+" failed.\n("+s+": "+p+")",l.name="ChunkLoadError",l.type=s,l.request=p,n[1](l)}},"chunk-"+i,i)}else e[i]=0},r.O.j=i=>0===e[i];var f=(i,o)=>{var l,d,[n,a,c]=o,u=0;if(n.some(p=>0!==e[p])){for(l in a)r.o(a,l)&&(r.m[l]=a[l]);if(c)var s=c(r)}for(i&&i(o);u<n.length;u++)r.o(e,d=n[u])&&e[d]&&e[d][0](),e[n[u]]=0;return r.O(s)},t=self.webpackChunkblrec=self.webpackChunkblrec||[];t.forEach(f.bind(null,0)),t.push=f.bind(null,t.push.bind(t))})()})();

View File

@@ -218,10 +218,13 @@ class TelegramResponse(TypedDict):
class Telegram(MessagingProvider):
def __init__(self, token: str = '', chatid: str = '') -> None:
_server: Final = 'https://api.telegram.org'
def __init__(self, token: str = '', chatid: str = '', server: str = '') -> None:
super().__init__()
self.token = token
self.chatid = chatid
self.server = server
async def send_message(
self, title: str, content: str, msg_type: MessageType
@@ -238,11 +241,12 @@ class Telegram(MessagingProvider):
async def _post_message(
self, title: str, content: str, msg_type: TelegramMessageType
) -> None:
url = f'https://api.telegram.org/bot{self.token}/sendMessage'
url = urljoin(self.server or self._server, f'/bot{self.token}/sendMessage')
payload = {
'chat_id': self.chatid,
'text': title + '\n\n' + content,
'parse_mode': 'MarkdownV2' if msg_type == 'markdown' else 'HTML',
'disable_web_page_preview': True,
}
async with aiohttp.ClientSession(raise_for_status=True) as session:

View File

@@ -155,9 +155,6 @@ class Postprocessor(
remuxing_result,
) = await self._remux_video_to_mp4(video_path)
if not self._debug:
await discard_file(
extra_metadata_path(video_path), 'DEBUG'
)
if self._should_delete_source_files(remuxing_result):
await discard_file(video_path)
elif self.inject_extra_metadata:
@@ -165,6 +162,8 @@ class Postprocessor(
result_path = await self._inject_extra_metadata(video_path)
else:
result_path = video_path
if not self._debug:
await discard_file(extra_metadata_path(video_path), 'DEBUG')
elif video_path.endswith('.m3u8'):
if self.remux_to_mp4:

View File

@@ -404,6 +404,7 @@ class PushplusSettings(BaseModel):
class TelegramSettings(BaseModel):
token: str = ''
chatid: str = ''
server: str = ''
@validator('token')
def _validate_token(cls, value: str) -> str:
@@ -417,6 +418,11 @@ class TelegramSettings(BaseModel):
raise ValueError('chatid is invalid')
return value
@validator('server')
def _validate_server(cls, value: str) -> str:
if value != '' and not re.fullmatch(r'^https?:\/\/[a-zA-Z0-9-_.]+(:[0-9]+)?', value):
raise ValueError('server is invalid')
return value
class BarkSettings(BaseModel):
server: str = ''

View File

@@ -378,6 +378,7 @@ class SettingsManager:
def _apply_telegram_settings(self, telegram: Telegram) -> None:
telegram.token = self._settings.telegram_notification.token
telegram.chatid = self._settings.telegram_notification.chatid
telegram.server = self._settings.telegram_notification.server
def _apply_bark_settings(self, bark: Bark) -> None:
bark.server = self._settings.bark_notification.server

View File

@@ -57,4 +57,28 @@
</ng-template>
</nz-form-control>
</nz-form-item>
<nz-form-item class="setting-item">
<nz-form-label
class="setting-label align-required"
nzFor="server"
nzNoColon
>server</nz-form-label
>
<nz-form-control
class="setting-control input"
nzHasFeedback
[nzErrorTip]="serverErrorTip"
[nzWarningTip]="syncFailedWarningTip"
[nzValidateStatus]="
serverControl.valid && !syncStatus.server ? 'warning' : serverControl
"
>
<input id="server" type="url" placeholder="默认为官方接口 https://api.telegram.org" nz-input formControlName="server" />
<ng-template #serverErrorTip let-control>
<ng-container *ngIf="control.hasError('pattern')">
server 无效
</ng-container>
</ng-template>
</nz-form-control>
</nz-form-item>
</form>

View File

@@ -45,6 +45,7 @@ export class TelegramSettingsComponent implements OnInit, OnChanges {
this.settingsForm = formBuilder.group({
token: ['', [Validators.required, Validators.pattern(/^[0-9]{8,10}:[a-zA-Z0-9_-]{35}$/)]],
chatid: ['', [Validators.required, Validators.pattern(/^(-|[0-9]){0,}$/)]],
server: ['', [Validators.pattern(/^https?:\/\/[a-zA-Z0-9-_.]+(:[0-9]+)?/)]],
});
}
@@ -56,8 +57,13 @@ export class TelegramSettingsComponent implements OnInit, OnChanges {
return this.settingsForm.get('chatid') as FormControl;
}
get serverControl() {
return this.settingsForm.get('server') as FormControl;
}
ngOnChanges(): void {
this.syncStatus = mapValues(this.settings, () => true);
console.log(this.settings);
this.settingsForm.setValue(this.settings);
}
@@ -67,7 +73,7 @@ export class TelegramSettingsComponent implements OnInit, OnChanges {
'telegramNotification',
this.settings,
this.settingsForm.valueChanges.pipe(
filterValueChanges<TelegramSettings>(this.settingsForm)
filterValueChanges<Partial<TelegramSettings>>(this.settingsForm)
)
)
.subscribe((detail) => {

View File

@@ -161,9 +161,10 @@ export const KEYS_OF_PUSHPLUS_SETTINGS = ['token', 'topic'] as const;
export interface TelegramSettings {
token: string;
chatid: string;
server: string;
}
export const KEYS_OF_TELEGRAM_SETTINGS = ['token', 'chatid'] as const;
export const KEYS_OF_TELEGRAM_SETTINGS = ['token', 'chatid', 'server'] as const;
export interface NotifierSettings {
enabled: boolean;