@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
# Changesets |
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works |
||||
with multi-package repos, or single-package repos to help you version and publish your code. You can |
||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets) |
||||
|
||||
We have a quick list of common questions to get you started engaging with this project in |
||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) |
@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
|
||||
{ |
||||
"$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", |
||||
"changelog": [ |
||||
"@changesets/changelog-github", |
||||
{ |
||||
"repo": "027xiguapi/pear-rec" |
||||
} |
||||
], |
||||
"commit": false, |
||||
"fixed": [], |
||||
"linked": [], |
||||
"access": "public", |
||||
"baseBranch": "main", |
||||
"updateInternalDependencies": "patch", |
||||
"ignore": [] |
||||
} |
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
root = true |
||||
|
||||
[*] |
||||
charset = utf-8 |
||||
end_of_line = lf |
||||
|
||||
[*.{html,css,ts,json,tsx,js,scss}] |
||||
indent_style = space |
||||
indent_size = 2 |
||||
insert_final_newline = true |
||||
trim_trailing_whitespace = true |
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
module.exports = { |
||||
extends: [require.resolve("@umijs/fabric/dist/eslint")], |
||||
|
||||
// in antd-design-pro
|
||||
globals: { |
||||
ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true, |
||||
page: true, |
||||
}, |
||||
|
||||
rules: { |
||||
// your rules
|
||||
"@typescript-eslint/no-explicit-any": ["off"], |
||||
}, |
||||
}; |
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
name: Build |
||||
'on': |
||||
push: |
||||
tags: |
||||
- v* |
||||
jobs: |
||||
build: |
||||
name: build and release electron app |
||||
runs-on: '${{ matrix.os }}' |
||||
strategy: |
||||
matrix: |
||||
os: [macos-latest, windows-latest, ubuntu-latest] |
||||
steps: |
||||
- name: Checkout |
||||
uses: actions/checkout@v4 |
||||
|
||||
- name: Install Node.js |
||||
uses: actions/setup-node@v4 |
||||
with: |
||||
node-version: 20 |
||||
|
||||
- name: Install pnpm |
||||
uses: pnpm/action-setup@v3 |
||||
with: |
||||
version: 8 |
||||
|
||||
- name: Install Dependencies |
||||
run: pnpm install |
||||
|
||||
- name: Build Electron App |
||||
run: 'pnpm run build:desktop' |
||||
env: |
||||
GITHUB_TOKEN: '${{ secrets.ACCESS_TOKEN }}' |
||||
|
||||
- name: Upload artifacts |
||||
uses: actions/upload-artifact@v4 |
||||
with: |
||||
name: '${{ matrix.os }}' |
||||
path: packages/desktop/release |
||||
|
||||
- name: Release |
||||
uses: softprops/action-gh-release@v2 |
||||
if: startsWith(github.ref, 'refs/tags/') |
||||
with: |
||||
files: | |
||||
packages/desktop/release/*.AppImage |
||||
packages/desktop/release/*.exe |
||||
packages/desktop/release/*.dmg |
||||
packages/desktop/release/*.yml |
||||
env: |
||||
GITHUB_TOKEN: '${{ secrets.ACCESS_TOKEN }}' |
@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
|
||||
name: syncToGitee |
||||
'on': |
||||
push: |
||||
branches: |
||||
- main |
||||
jobs: |
||||
repo-sync: |
||||
runs-on: ubuntu-latest |
||||
steps: |
||||
- name: Mirror with force push (git push -f) |
||||
uses: Yikun/hub-mirror-action@master |
||||
with: |
||||
src: github/027xiguapi |
||||
dst: gitee/xiguapi027 |
||||
dst_key: '${{ secrets.GITEE_PRIVATE_KEY }}' |
||||
dst_token: '${{ secrets.GITEE_TOKEN }}' |
||||
static_list: pear-rec |
||||
force_update: true |
||||
debug: true |
||||
- name: Build Gitee Pages |
||||
uses: yanglbme/gitee-pages-action@main |
||||
with: |
||||
# 注意替换为你的 Gitee 用户名 |
||||
gitee-username: xiguapi027 |
||||
# 注意在 Settings->Secrets 配置 GITEE_PASSWORD |
||||
gitee-password: '${{ secrets.GITEE_PASSWORD }}' |
||||
# 注意替换为你的 Gitee 仓库,仓库名严格区分大小写,请准确填写,否则会出错 |
||||
gitee-repo: xiguapi027/pear-rec |
||||
# 要部署的分支,默认是 master,若是其他分支,则需要指定(指定的分支必须存在) |
||||
branch: gh-pages |
@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
|
||||
# Logs |
||||
logs |
||||
*.log |
||||
npm-debug.log* |
||||
yarn-debug.log* |
||||
yarn-error.log* |
||||
pnpm-debug.log* |
||||
lerna-debug.log* |
||||
|
||||
node_modules |
||||
dist |
||||
dist-ssr |
||||
lib |
||||
dist-electron |
||||
release |
||||
*.local |
||||
dev-dist |
||||
|
||||
stats.html |
||||
|
||||
# Editor directories and files |
||||
.vscode |
||||
.idea |
||||
.DS_Store |
||||
*.suo |
||||
*.ntvs* |
||||
*.njsproj |
||||
*.sln |
||||
*.sw? |
||||
|
||||
#lockfile |
||||
package-lock.json |
||||
pnpm-lock.yaml |
||||
yarn.lock |
||||
/test-results/ |
||||
/playwright-report/ |
||||
/playwright/.cache/ |
||||
Pear Files/ |
||||
|
||||
# docs |
||||
cache/ |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
shamefully-hoist=true |
||||
|
||||
# 在国内使用pnpm安装electron需要配置一下electron的下载路径 |
||||
registry="https://registry.npmmirror.com/" |
||||
electron_mirror="https://registry.npmmirror.com/-/binary/electron/" |
||||
electron_builder_binaries_mirror="https://registry.npmmirror.com/-/binary/electron-builder-binaries/" |
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
# Logs |
||||
logs |
||||
*.log |
||||
npm-debug.log* |
||||
yarn-debug.log* |
||||
yarn-error.log* |
||||
pnpm-debug.log* |
||||
lerna-debug.log* |
||||
|
||||
node_modules |
||||
packages/*/node_modules |
||||
dist |
||||
dist-ssr |
||||
dist-electron |
||||
release |
||||
*.local |
||||
|
||||
# Editor directories and files |
||||
.vscode/.debug.env |
||||
.idea |
||||
.DS_Store |
||||
*.suo |
||||
*.ntvs* |
||||
*.njsproj |
||||
*.sln |
||||
*.sw? |
||||
|
||||
#lockfile |
||||
package-lock.json |
||||
pnpm-lock.yaml |
||||
yarn.lock |
||||
/test-results/ |
||||
/playwright-report/ |
||||
/playwright/.cache/ |
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
const fabric = require('@umijs/fabric'); |
||||
|
||||
module.exports = { |
||||
...fabric.prettier, |
||||
}; |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
module.exports = { |
||||
extends: [require.resolve("@umijs/fabric/dist/stylelint")], |
||||
rules: { |
||||
// your rules
|
||||
}, |
||||
}; |
@ -0,0 +1,201 @@
@@ -0,0 +1,201 @@
|
||||
Apache License |
||||
Version 2.0, January 2004 |
||||
http://www.apache.org/licenses/ |
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
||||
|
||||
1. Definitions. |
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, |
||||
and distribution as defined by Sections 1 through 9 of this document. |
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by |
||||
the copyright owner that is granting the License. |
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all |
||||
other entities that control, are controlled by, or are under common |
||||
control with that entity. For the purposes of this definition, |
||||
"control" means (i) the power, direct or indirect, to cause the |
||||
direction or management of such entity, whether by contract or |
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
||||
outstanding shares, or (iii) beneficial ownership of such entity. |
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity |
||||
exercising permissions granted by this License. |
||||
|
||||
"Source" form shall mean the preferred form for making modifications, |
||||
including but not limited to software source code, documentation |
||||
source, and configuration files. |
||||
|
||||
"Object" form shall mean any form resulting from mechanical |
||||
transformation or translation of a Source form, including but |
||||
not limited to compiled object code, generated documentation, |
||||
and conversions to other media types. |
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or |
||||
Object form, made available under the License, as indicated by a |
||||
copyright notice that is included in or attached to the work |
||||
(an example is provided in the Appendix below). |
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object |
||||
form, that is based on (or derived from) the Work and for which the |
||||
editorial revisions, annotations, elaborations, or other modifications |
||||
represent, as a whole, an original work of authorship. For the purposes |
||||
of this License, Derivative Works shall not include works that remain |
||||
separable from, or merely link (or bind by name) to the interfaces of, |
||||
the Work and Derivative Works thereof. |
||||
|
||||
"Contribution" shall mean any work of authorship, including |
||||
the original version of the Work and any modifications or additions |
||||
to that Work or Derivative Works thereof, that is intentionally |
||||
submitted to Licensor for inclusion in the Work by the copyright owner |
||||
or by an individual or Legal Entity authorized to submit on behalf of |
||||
the copyright owner. For the purposes of this definition, "submitted" |
||||
means any form of electronic, verbal, or written communication sent |
||||
to the Licensor or its representatives, including but not limited to |
||||
communication on electronic mailing lists, source code control systems, |
||||
and issue tracking systems that are managed by, or on behalf of, the |
||||
Licensor for the purpose of discussing and improving the Work, but |
||||
excluding communication that is conspicuously marked or otherwise |
||||
designated in writing by the copyright owner as "Not a Contribution." |
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity |
||||
on behalf of whom a Contribution has been received by Licensor and |
||||
subsequently incorporated within the Work. |
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
copyright license to reproduce, prepare Derivative Works of, |
||||
publicly display, publicly perform, sublicense, and distribute the |
||||
Work and such Derivative Works in Source or Object form. |
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
(except as stated in this section) patent license to make, have made, |
||||
use, offer to sell, sell, import, and otherwise transfer the Work, |
||||
where such license applies only to those patent claims licensable |
||||
by such Contributor that are necessarily infringed by their |
||||
Contribution(s) alone or by combination of their Contribution(s) |
||||
with the Work to which such Contribution(s) was submitted. If You |
||||
institute patent litigation against any entity (including a |
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work |
||||
or a Contribution incorporated within the Work constitutes direct |
||||
or contributory patent infringement, then any patent licenses |
||||
granted to You under this License for that Work shall terminate |
||||
as of the date such litigation is filed. |
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the |
||||
Work or Derivative Works thereof in any medium, with or without |
||||
modifications, and in Source or Object form, provided that You |
||||
meet the following conditions: |
||||
|
||||
(a) You must give any other recipients of the Work or |
||||
Derivative Works a copy of this License; and |
||||
|
||||
(b) You must cause any modified files to carry prominent notices |
||||
stating that You changed the files; and |
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works |
||||
that You distribute, all copyright, patent, trademark, and |
||||
attribution notices from the Source form of the Work, |
||||
excluding those notices that do not pertain to any part of |
||||
the Derivative Works; and |
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its |
||||
distribution, then any Derivative Works that You distribute must |
||||
include a readable copy of the attribution notices contained |
||||
within such NOTICE file, excluding those notices that do not |
||||
pertain to any part of the Derivative Works, in at least one |
||||
of the following places: within a NOTICE text file distributed |
||||
as part of the Derivative Works; within the Source form or |
||||
documentation, if provided along with the Derivative Works; or, |
||||
within a display generated by the Derivative Works, if and |
||||
wherever such third-party notices normally appear. The contents |
||||
of the NOTICE file are for informational purposes only and |
||||
do not modify the License. You may add Your own attribution |
||||
notices within Derivative Works that You distribute, alongside |
||||
or as an addendum to the NOTICE text from the Work, provided |
||||
that such additional attribution notices cannot be construed |
||||
as modifying the License. |
||||
|
||||
You may add Your own copyright statement to Your modifications and |
||||
may provide additional or different license terms and conditions |
||||
for use, reproduction, or distribution of Your modifications, or |
||||
for any such Derivative Works as a whole, provided Your use, |
||||
reproduction, and distribution of the Work otherwise complies with |
||||
the conditions stated in this License. |
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, |
||||
any Contribution intentionally submitted for inclusion in the Work |
||||
by You to the Licensor shall be under the terms and conditions of |
||||
this License, without any additional terms or conditions. |
||||
Notwithstanding the above, nothing herein shall supersede or modify |
||||
the terms of any separate license agreement you may have executed |
||||
with Licensor regarding such Contributions. |
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade |
||||
names, trademarks, service marks, or product names of the Licensor, |
||||
except as required for reasonable and customary use in describing the |
||||
origin of the Work and reproducing the content of the NOTICE file. |
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or |
||||
agreed to in writing, Licensor provides the Work (and each |
||||
Contributor provides its Contributions) on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
||||
implied, including, without limitation, any warranties or conditions |
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
||||
PARTICULAR PURPOSE. You are solely responsible for determining the |
||||
appropriateness of using or redistributing the Work and assume any |
||||
risks associated with Your exercise of permissions under this License. |
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, |
||||
whether in tort (including negligence), contract, or otherwise, |
||||
unless required by applicable law (such as deliberate and grossly |
||||
negligent acts) or agreed to in writing, shall any Contributor be |
||||
liable to You for damages, including any direct, indirect, special, |
||||
incidental, or consequential damages of any character arising as a |
||||
result of this License or out of the use or inability to use the |
||||
Work (including but not limited to damages for loss of goodwill, |
||||
work stoppage, computer failure or malfunction, or any and all |
||||
other commercial damages or losses), even if such Contributor |
||||
has been advised of the possibility of such damages. |
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing |
||||
the Work or Derivative Works thereof, You may choose to offer, |
||||
and charge a fee for, acceptance of support, warranty, indemnity, |
||||
or other liability obligations and/or rights consistent with this |
||||
License. However, in accepting such obligations, You may act only |
||||
on Your own behalf and on Your sole responsibility, not on behalf |
||||
of any other Contributor, and only if You agree to indemnify, |
||||
defend, and hold each Contributor harmless for any liability |
||||
incurred by, or claims asserted against, such Contributor by reason |
||||
of your accepting any such warranty or additional liability. |
||||
|
||||
END OF TERMS AND CONDITIONS |
||||
|
||||
APPENDIX: How to apply the Apache License to your work. |
||||
|
||||
To apply the Apache License to your work, attach the following |
||||
boilerplate notice, with the fields enclosed by brackets "[]" |
||||
replaced with your own identifying information. (Don't include |
||||
the brackets!) The text should be enclosed in the appropriate |
||||
comment syntax for the file format. We also recommend that a |
||||
file or class name and description of purpose be included on the |
||||
same "printed page" as the copyright notice for easier |
||||
identification within third-party archives. |
||||
|
||||
Copyright [yyyy] [name of copyright owner] |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
@ -0,0 +1,94 @@
@@ -0,0 +1,94 @@
|
||||
<p align="center"> |
||||
<img src="https://027xiguapi.github.io/pear-rec/logo.png" height="120" /> |
||||
<h1>pear-rec</h1> |
||||
<p> |
||||
<img src="https://img.shields.io/github/stars/027xiguapi/pear-rec" alt="stars"> |
||||
<img src="https://img.shields.io/badge/react-v18-blue" alt="react"> |
||||
<img src="https://img.shields.io/badge/electron-v26-blue" alt="electron"> |
||||
<img src="https://img.shields.io/badge/nestjs-v3-blue" alt="nestjs"> |
||||
<img src="https://img.shields.io/badge/-TypeScript-blue?logo=typescript&logoColor=white" alt="typescript"> |
||||
<img src="https://img.shields.io/badge/-Vite-blue?logo=vite&logoColor=white" alt="vite"> |
||||
</p> |
||||
</p> |
||||
|
||||
--- |
||||
|
||||
## README |
||||
|
||||
[中文](README.zh-CN.md) | [English](README.md) | [Deutsch](README.de-DE.md) |
||||
|
||||
## Dokumentation |
||||
|
||||
> pear-rec (pear rec) ist eine plattformübergreifende Software für Bildschirmfotos, Bildschirmaufnahmen, Audio- und Videoaufnahmen. |
||||
> |
||||
> pear-rec(pear rec) ist ein Projekt basierend auf react + electron + vite + viewerjs + plyr + aplayer + react-screenshots. |
||||
> |
||||
> Weitere Funktionen und APIs finden Sie auf der offiziellen [Website](https://027xiguapi.github.io/pear-rec). |
||||
|
||||
## Installation |
||||
|
||||
``` |
||||
gitee: https://gitee.com/xiguapi027/pear-rec |
||||
github: https://github.com/027xiguapi/pear-rec |
||||
``` |
||||
|
||||
## Verwendung |
||||
|
||||
### Erste Schritte |
||||
|
||||
Bevor Sie dieses Repository klonen und ausführen, müssen Sie [Git](https://git-scm.com), [Node.js](https://nodejs.org/en/download/) (mit [npm](https://www.npmjs.com/)) und [pnpm](https://pnpm.io/) auf Ihrem Computer installiert haben. Kommandozeilenaufrufe: |
||||
|
||||
```shell |
||||
# Clone this repository |
||||
git clone https://github.com/027xiguapi/pear-rec.git |
||||
# Go into the repository |
||||
cd pear-rec |
||||
# Install dependencies |
||||
pnpm install |
||||
# Run the web |
||||
pnpm run dev:web |
||||
# Run the server |
||||
pnpm run dev:server |
||||
# Run the desktop |
||||
pnpm run dev:desktop |
||||
# Run the web |
||||
pnpm run start:web |
||||
# Run the desktop |
||||
pnpm run start:desktop |
||||
# Build the web |
||||
pnpm run build:web |
||||
# Build the desktop |
||||
pnpm run build:desktop |
||||
# Clear node_modules |
||||
pnpm run clear |
||||
``` |
||||
|
||||
## Internationalization(I18n) |
||||
|
||||
- [x] Chinesisch |
||||
- [x] Englisch |
||||
- [x] Deutsch |
||||
|
||||
## Test |
||||
|
||||
| OS | Windows | Linux | Macos | |
||||
| ---- | ------- | ----- | ----- | |
||||
| Test | 🟢 | ◯ | ◯ | |
||||
|
||||
## Download |
||||
|
||||
| OS | Windows | Linux | Macos | |
||||
| ---- | ----------------------------------------------------------- | ----- | ----- | |
||||
| link | [Download](https://github.com/027xiguapi/pear-rec/releases) | ◯ | ◯ | |
||||
|
||||
## Feedback |
||||
|
||||
- QQ group |
||||
|
||||
<p align="center"> |
||||
<img src="https://027xiguapi.github.io/pear-rec/imgs/pear-rec_qq_qrcode.png" /> |
||||
</p> |
||||
|
||||
## Lizenz |
||||
|
||||
[pear-rec wird unter der Apache License V2 bereitgestellt.](LICENSE) |
@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
|
||||
{ |
||||
"name": "pear-rec", |
||||
"version": "1.4.0", |
||||
"description": " pear-rec is a cross platform software with screenshot, screen recording, audio recording and video recording.", |
||||
"scripts": { |
||||
"start:desktop": "concurrently --names \"WEB,DESKTOP\" -c \"red,blue\" \"npm run dev:web\" \"wait-on tcp:0.0.0.0:9191 && pnpm run dev:desktop\"", |
||||
"dev:desktop": "pnpm run -C packages/desktop dev", |
||||
"build:desktop": "pnpm run -C packages/desktop build && pnpm run copy:web && pnpm run -C packages/desktop build:win", |
||||
"copy:web": "pnpm run -C packages/web build && node tools/copy-files-web2desktop.js", |
||||
"copy:server": "pnpm run -C packages/server build && node tools/copy-files-server2desktop.js", |
||||
"dev:web": "pnpm run -C packages/web dev", |
||||
"build:web": "pnpm run -C packages/web build", |
||||
"only-build:web": "pnpm run -C packages/web build", |
||||
"watch:web": "pnpm run -C packages/web watch", |
||||
"project:web": "pnpm run -C packages/web build && node tools/copy-files-web2server.js", |
||||
"dev:server": "pnpm run -C packages/server dev", |
||||
"build:server": "pnpm run -C packages/server build", |
||||
"dev:docs": "pnpm run -C packages/docs dev", |
||||
"build:docs": "pnpm run -C packages/docs build", |
||||
"preview:docs": "pnpm run -C packages/docs preview", |
||||
"deploy:docs": "pnpm run -C packages/docs deploy", |
||||
"dev:timer": "pnpm run -C packages/timer dev", |
||||
"build:timer": "pnpm run -C packages/timer build", |
||||
"watch:timer": "pnpm run -C packages/timer watch", |
||||
"dev:recorder": "pnpm run -C packages/recorder dev", |
||||
"build:recorder": "pnpm run -C packages/recorder build", |
||||
"watch:recorder": "pnpm run -C packages/recorder watch", |
||||
"dev:screenshot": "pnpm run -C packages/screenshot dev", |
||||
"build:screenshot": "pnpm run -C packages/screenshot build", |
||||
"watch:screenshot": "pnpm run -C packages/screenshot watch", |
||||
"clear": "concurrently --names \"SERVER,WEB,DESKTOP\" \"pnpm run -C packages/web clear\" \"pnpm run -C packages/server clear\" \"pnpm run -C packages/desktop clear\"", |
||||
"copy": "node tools/copy-web-files.js", |
||||
"changeset": "changeset", |
||||
"vp": "changeset version", |
||||
"commit": "cz" |
||||
}, |
||||
"devDependencies": { |
||||
"@changesets/changelog-github": "^0.4.8", |
||||
"@changesets/cli": "^2.26.2", |
||||
"@umijs/fabric": "^4.0.1", |
||||
"commitizen": "^4.3.0", |
||||
"concurrently": "^8.2.1", |
||||
"cz-conventional-changelog": "^3.3.0", |
||||
"typescript": "^5.2.2", |
||||
"wait-on": "^7.2.0" |
||||
}, |
||||
"keywords": [ |
||||
"electron", |
||||
"react", |
||||
"antd", |
||||
"aplayer", |
||||
"plyr", |
||||
"viewerjs", |
||||
"tui-image-editor", |
||||
"react-screenshots", |
||||
"react-timer-hook", |
||||
"webav", |
||||
"gif.js" |
||||
], |
||||
"author": "027xiguapi", |
||||
"homepage": "https://027xiguapi.github.io/pear-rec", |
||||
"repository": { |
||||
"type": "git", |
||||
"url": "git@github.com:027xiguapi/pear-rec.git" |
||||
}, |
||||
"bugs": { |
||||
"url": "https://github.com/027xiguapi/pear-rec/issues" |
||||
}, |
||||
"license": "Apache-2.0" |
||||
} |
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
# port 端口号 |
||||
VITE_API_URL = http://localhost:9190/ |
||||
VITE_WEB_URL = http://localhost:9191/ |
||||
|
||||
PORT=9190 |
@ -0,0 +1,101 @@
@@ -0,0 +1,101 @@
|
||||
# @pear-rec/desktop |
||||
|
||||
## 1.3.14 |
||||
|
||||
feat: 增加视频转换 |
||||
|
||||
## 1.3.13 |
||||
|
||||
perf: 升级 electron |
||||
|
||||
## 1.3.12 |
||||
|
||||
fix: 配置修改 |
||||
|
||||
## 1.3.11 |
||||
|
||||
feat: 增加画布 |
||||
|
||||
## 1.3.9 |
||||
|
||||
feat: 首页增加编辑动图 |
||||
|
||||
## 1.3.8 |
||||
|
||||
perf: 录制动图优化 |
||||
|
||||
## 1.3.7 |
||||
|
||||
feat: 增加日志 |
||||
|
||||
## 1.3.6 |
||||
|
||||
feat: 修改 GIF |
||||
|
||||
## 1.3.5 |
||||
|
||||
feat: 增加服务子进程 |
||||
|
||||
## 1.3.4 |
||||
|
||||
feat: 重置设置、显示快捷键 |
||||
|
||||
## 1.3.3 |
||||
|
||||
fix: 录屏 bug |
||||
|
||||
## 1.3.2 |
||||
|
||||
feat: 自动更新软件 |
||||
|
||||
## 1.3.1 |
||||
|
||||
feat: 增加录全屏 |
||||
|
||||
## 1.3.0 |
||||
|
||||
feat: 增加钉图功能 |
||||
|
||||
## 1.2.11 |
||||
|
||||
feat: 打包发布到 git |
||||
|
||||
## 1.2.10 |
||||
|
||||
perf: 设置保存地址 |
||||
|
||||
## 1.2.9 |
||||
|
||||
feat: 设置快捷键 |
||||
|
||||
## 1.2.8 |
||||
|
||||
perf: 优化桌面录屏 |
||||
|
||||
## 1.2.7 |
||||
|
||||
feat: 增加记录 |
||||
|
||||
## 1.2.6 |
||||
|
||||
fix: electron 修改图片加载 bug, perf: 修改端口 |
||||
|
||||
## 1.2.5 |
||||
|
||||
fix: 以管理运行、点击运行软件 |
||||
|
||||
## 1.2.4 |
||||
|
||||
fix: 打包服务无法启动 bug |
||||
|
||||
## 1.2.3 |
||||
|
||||
fix: 录屏下载 bug |
||||
|
||||
## 1.2.2 |
||||
|
||||
feat: 引入@pear-rec/server 服务 |
||||
|
||||
## 1.2.1 |
||||
|
||||
fix: fluent-ffmpeg 引入 bug |
After Width: | Height: | Size: 415 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 6.0 KiB |
After Width: | Height: | Size: 155 KiB |
After Width: | Height: | Size: 8.7 KiB |
After Width: | Height: | Size: 261 KiB |
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
import { test, expect, _electron as electron } from "@playwright/test"; |
||||
|
||||
test("homepage has title and links to intro page", async () => { |
||||
const app = await electron.launch({ args: [".", "--no-sandbox"] }); |
||||
const page = await app.firstWindow(); |
||||
expect(await page.title()).toBe("Electron + Vite + React"); |
||||
await page.screenshot({ path: "e2e/screenshots/example.png" }); |
||||
}); |
After Width: | Height: | Size: 102 KiB |
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
/** |
||||
* @see https://www.electron.build/configuration/configuration |
||||
*/ |
||||
{ |
||||
appId: 'com.electron.pear-rec', |
||||
productName: 'pear-rec', |
||||
copyright: 'Copyright © 2024 ${author}', |
||||
asar: true, |
||||
directories: { |
||||
output: 'release', |
||||
}, |
||||
files: ['dist-electron', 'dist', '.env'], |
||||
mac: { |
||||
icon: 'build/icons/mac/icon.icns', |
||||
target: { target: 'dmg' }, |
||||
artifactName: '${productName}-Mac-${version}-Installer.${ext}', |
||||
}, |
||||
linux: { |
||||
icon: 'build/icons/png', |
||||
target: ['AppImage'], |
||||
artifactName: '${productName}-Linux-${version}.${ext}', |
||||
}, |
||||
win: { |
||||
icon: 'build/icons/win/icon.ico', |
||||
target: [ |
||||
{ |
||||
target: 'nsis', |
||||
}, |
||||
], |
||||
artifactName: '${productName}-Windows-${version}-Setup.${ext}', |
||||
requestedExecutionLevel: 'requireAdministrator', |
||||
}, |
||||
nsis: { |
||||
oneClick: false, |
||||
perMachine: false, |
||||
allowToChangeInstallationDirectory: true, |
||||
deleteAppDataOnUninstall: false, |
||||
}, |
||||
publish: { |
||||
provider: 'github', |
||||
repo: 'pear-rec', |
||||
owner: '027xiguapi', |
||||
}, |
||||
} |
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
/// <reference types="vite-electron-plugin/electron-env" />
|
||||
|
||||
declare namespace NodeJS { |
||||
interface ProcessEnv { |
||||
VSCODE_DEBUG?: "true"; |
||||
DIST_ELECTRON: string; |
||||
DIST: string; |
||||
/** /dist/ or /public/ */ |
||||
VITE_PUBLIC: string; |
||||
} |
||||
} |
@ -0,0 +1,106 @@
@@ -0,0 +1,106 @@
|
||||
{ |
||||
"tray": { |
||||
"screenshot": "Bildschirmfoto", |
||||
"audioRecording": "Audioaufnahme", |
||||
"screenRecording": "Bildschirmaufnahme", |
||||
"videoRecording": "Videoaufnahme", |
||||
"viewImage": "Bild anzeigen", |
||||
"playAudio": "Audio abspielen", |
||||
"watchVideo": "Video abspielen", |
||||
"home": "Startseite", |
||||
"setting": "Einstellung", |
||||
"help": "Hilfe", |
||||
"relaunch": "Neustart", |
||||
"quit": "Beenden" |
||||
}, |
||||
"nav": { |
||||
"setting": "Einstellung", |
||||
"minimize": "minimieren", |
||||
"maximize": "maximieren", |
||||
"close": "schließen", |
||||
"zoomIn": "zoomIn", |
||||
"zoomOut": "zoomOut", |
||||
"oneToOne": "oneToOne", |
||||
"download": "download", |
||||
"alwaysOnTopWin": "alwaysOnTopWin", |
||||
"openFile": "openFile", |
||||
"uploadFile": "uploadFile", |
||||
"scan": "scan", |
||||
"search": "search", |
||||
"prev": "prev", |
||||
"next": "next", |
||||
"rotateLeft": "rotateLeft", |
||||
"rotateRight": "rotateRight", |
||||
"flipHorizontal": "flipHorizontal", |
||||
"flipVertical": "flipVertical" |
||||
}, |
||||
"home": { |
||||
"audioRecording": "Audioaufnahme", |
||||
"screenRecording": "Bildschirmaufnahme", |
||||
"videoRecording": "Videoaufnahme", |
||||
"screenshot": "Bildschirmfoto", |
||||
"fullScreen": "Vollbild", |
||||
"viewImage": "Bild anzeigen", |
||||
"playAudio": "Audio abspielen", |
||||
"watchVideo": "Video abspielen", |
||||
"history": "Verlauf" |
||||
}, |
||||
"editImage": { |
||||
"save": "speichern" |
||||
}, |
||||
"recorderAudio": { |
||||
"mute": "stummschalten", |
||||
"unmute": "stummschalten aus", |
||||
"delete": "löschen", |
||||
"save": "speichern", |
||||
"play": "abspielen", |
||||
"resume": "fortsetzen", |
||||
"pause": "pausieren" |
||||
}, |
||||
"recorderScreen": { |
||||
"tip": "Zum Starten der Aufnahme auf 'Start' klicken", |
||||
"saving": "speichern", |
||||
"mute": "stummschalten", |
||||
"unmute": "stummschalten aus", |
||||
"resume": "fortsetzen", |
||||
"pause": "pausieren", |
||||
"save": "speichern", |
||||
"play": "abspielen", |
||||
"width": "Breite", |
||||
"height": "Höhe", |
||||
"shotScreen": "shotScreen" |
||||
}, |
||||
"recorderVideo": { |
||||
"tip": "Zum Starten der Aufnahme auf 'Start' klicken", |
||||
"saving": "speichern", |
||||
"mute": "stummschalten", |
||||
"unmute": "stummschalten aus", |
||||
"resume": "fortsetzen", |
||||
"pause": "pausieren", |
||||
"save": "speichern", |
||||
"play": "abspielen", |
||||
"shotScreen": "shotScreen" |
||||
}, |
||||
"viewImage": { |
||||
"uploadText": "Bild auswählen oder reinziehen", |
||||
"uploadHint": "Formate: .jpg, .jpeg, .jfif, .pjpeg, .pjp, .png, .apng, .webp, .avif, .bmp, .gif, .webp, .ico" |
||||
}, |
||||
"viewVideo": { |
||||
"emptyDescription": "Kein Video", |
||||
"emptyBtn": "Video öffnen" |
||||
}, |
||||
"setting": { |
||||
"userSetting": "Nutzereinstellungen", |
||||
"basicSetting": "Basiseinstellungen", |
||||
"serverSetting": "server", |
||||
"shortcutSetting": "shortcut", |
||||
"address": "web address", |
||||
"openFilePath": "open folder", |
||||
"language": "Sprache", |
||||
"filePath": "Dateipfad", |
||||
"openAtLogin": "Bei Anmeldung öffnen", |
||||
"open": "öffnen", |
||||
"close": "schließen", |
||||
"download": "Download" |
||||
} |
||||
} |
@ -0,0 +1,109 @@
@@ -0,0 +1,109 @@
|
||||
{ |
||||
"tray": { |
||||
"screenshot": "screenshot", |
||||
"audioRecording": "sound recording", |
||||
"screenRecording": "screen recording", |
||||
"videoRecording": "video recording", |
||||
"viewImage": "view image", |
||||
"playAudio": "play audio", |
||||
"watchVideo": "watch video", |
||||
"home": "home", |
||||
"setting": "setting", |
||||
"help": "help", |
||||
"relaunch": "restart", |
||||
"quit": "quit" |
||||
}, |
||||
"nav": { |
||||
"setting": "setting", |
||||
"minimize": "minimize", |
||||
"maximize": "maximize", |
||||
"close": "close", |
||||
"zoomIn": "zoomIn", |
||||
"zoomOut": "zoomOut", |
||||
"oneToOne": "oneToOne", |
||||
"download": "download", |
||||
"alwaysOnTopWin": "alwaysOnTopWin", |
||||
"openFile": "openFile", |
||||
"uploadFile": "uploadFile", |
||||
"scan": "scan", |
||||
"search": "search", |
||||
"prev": "prev", |
||||
"next": "next", |
||||
"rotateLeft": "rotateLeft", |
||||
"rotateRight": "rotateRight", |
||||
"flipHorizontal": "flipHorizontal", |
||||
"flipVertical": "flipVertical" |
||||
}, |
||||
"home": { |
||||
"audioRecording": "sound recording", |
||||
"screenRecording": "screen recording", |
||||
"videoRecording": "video recording", |
||||
"screenshot": "screenshot", |
||||
"fullScreen": "full", |
||||
"viewImage": "view image", |
||||
"playAudio": "play audio", |
||||
"watchVideo": "watch video", |
||||
"history": "history" |
||||
}, |
||||
"editImage": { |
||||
"save": "save" |
||||
}, |
||||
"recorderAudio": { |
||||
"mute": "mute", |
||||
"unmute": "unmute", |
||||
"delete": "delete", |
||||
"save": "save", |
||||
"play": "play", |
||||
"resume": "resume", |
||||
"pause": "pause" |
||||
}, |
||||
"recorderScreen": { |
||||
"tip": "click the start button below to start recording", |
||||
"saving": "saving", |
||||
"mute": "mute", |
||||
"unmute": "unmute", |
||||
"resume": "resume", |
||||
"pause": "pause", |
||||
"save": "save", |
||||
"play": "play", |
||||
"width": "width", |
||||
"height": "height", |
||||
"shotScreen": "shotScreen" |
||||
}, |
||||
"recorderVideo": { |
||||
"tip": "click the start button below to start recording", |
||||
"saving": "saving", |
||||
"mute": "mute", |
||||
"unmute": "unmute", |
||||
"resume": "resume", |
||||
"pause": "pause", |
||||
"save": "save", |
||||
"play": "play", |
||||
"shotScreen": "shotScreen" |
||||
}, |
||||
"viewImage": { |
||||
"uploadText": "click or drag the image", |
||||
"uploadHint": "allow .jpg、.jpeg、.jfif、.pjpeg、.pjp、.png、.apng、.webp、.avif、.bmp、.gif、.webp、.ico" |
||||
}, |
||||
"viewVideo": { |
||||
"emptyDescription": "暂无视频", |
||||
"emptyBtn": "open video" |
||||
}, |
||||
"setting": { |
||||
"userSetting": "user", |
||||
"basicSetting": "basic", |
||||
"serverSetting": "server", |
||||
"shortcutSetting": "shortcut", |
||||
"address": "web address", |
||||
"openFilePath": "open folder", |
||||
"reset": "reset", |
||||
"language": "language", |
||||
"filePath": "filePath", |
||||
"openAtLogin": "openAtLogin", |
||||
"open": "open", |
||||
"close": "close", |
||||
"download": "download", |
||||
"openServer": "open server", |
||||
"serverPath": "server path" |
||||
} |
||||
} |
@ -0,0 +1,109 @@
@@ -0,0 +1,109 @@
|
||||
{ |
||||
"tray": { |
||||
"screenshot": "截图", |
||||
"audioRecording": "录音", |
||||
"screenRecording": "录屏", |
||||
"videoRecording": "录像", |
||||
"viewImage": "查看图片", |
||||
"playAudio": "查看音频", |
||||
"watchVideo": "查看视频", |
||||
"home": "主页面", |
||||
"setting": "设置", |
||||
"help": "教程帮助", |
||||
"relaunch": "重启", |
||||
"quit": "退出" |
||||
}, |
||||
"nav": { |
||||
"setting": "设置", |
||||
"minimize": "最小化", |
||||
"maximize": "最大化", |
||||
"close": "关闭", |
||||
"zoomIn": "放大", |
||||
"zoomOut": "缩小", |
||||
"oneToOne": "还原", |
||||
"download": "下载", |
||||
"alwaysOnTopWin": "置顶", |
||||
"openFile": "打开图片", |
||||
"uploadFile": "打开文件夹", |
||||
"scan": "扫码", |
||||
"search": "搜图", |
||||
"prev": "上一个", |
||||
"next": "下一个", |
||||
"rotateLeft": "左转", |
||||
"rotateRight": "右转", |
||||
"flipHorizontal": "水平翻转", |
||||
"flipVertical": "垂直翻转" |
||||
}, |
||||
"home": { |
||||
"audioRecording": "录音", |
||||
"screenRecording": "录屏", |
||||
"videoRecording": "录像", |
||||
"screenshot": "截图", |
||||
"fullScreen": "全屏", |
||||
"viewImage": "查看图片", |
||||
"playAudio": "查看音频", |
||||
"watchVideo": "查看视频", |
||||
"history": "历史" |
||||
}, |
||||
"editImage": { |
||||
"save": "保存" |
||||
}, |
||||
"recorderAudio": { |
||||
"mute": "静音", |
||||
"unmute": "打开声音", |
||||
"delete": "删除", |
||||
"save": "保存", |
||||
"play": "开始", |
||||
"resume": "继续", |
||||
"pause": "暂停" |
||||
}, |
||||
"recorderScreen": { |
||||
"tip": "点击下面开始按钮开始录制", |
||||
"saving": "正在保存", |
||||
"mute": "静音", |
||||
"unmute": "打开声音", |
||||
"resume": "继续", |
||||
"pause": "暂停", |
||||
"save": "保存", |
||||
"play": "开始", |
||||
"width": "长", |
||||
"height": "高", |
||||
"shotScreen": "截图" |
||||
}, |
||||
"recorderVideo": { |
||||
"tip": "点击下面开始按钮开始录制", |
||||
"saving": "正在保存", |
||||
"mute": "静音", |
||||
"unmute": "打开声音", |
||||
"resume": "继续", |
||||
"pause": "暂停", |
||||
"save": "保存", |
||||
"play": "开始", |
||||
"shotScreen": "截图" |
||||
}, |
||||
"viewImage": { |
||||
"uploadText": "点击或拖着图片", |
||||
"uploadHint": "支持.jpg、.jpeg、.jfif、.pjpeg、.pjp、.png、.apng、.webp、.avif、.bmp、.gif、.webp、.ico" |
||||
}, |
||||
"viewVideo": { |
||||
"emptyDescription": "暂无视频", |
||||
"emptyBtn": "打开视频" |
||||
}, |
||||
"setting": { |
||||
"userSetting": "账户设置", |
||||
"basicSetting": "通用设置", |
||||
"serverSetting": "服务设置", |
||||
"shortcutSetting": "快捷键", |
||||
"address": "软件地址", |
||||
"openFilePath": "打开下载文件夹", |
||||
"reset": "重置", |
||||
"language": "语言", |
||||
"filePath": "保存地址", |
||||
"openAtLogin": "开机自启动", |
||||
"open": "开启", |
||||
"close": "关闭", |
||||
"download": "浏览器下载", |
||||
"openServer": "打开服务", |
||||
"serverPath": "服务地址" |
||||
} |
||||
} |
@ -0,0 +1,46 @@
@@ -0,0 +1,46 @@
|
||||
import express, { Application, Request, Response } from 'express'; |
||||
import cors from 'cors'; |
||||
import { HttpsProxyAgent } from 'https-proxy-agent'; |
||||
import { createProxyMiddleware, fixRequestBody } from 'http-proxy-middleware'; |
||||
|
||||
export function initApp() { |
||||
const app: Application = express(); |
||||
|
||||
app.use(cors()); |
||||
|
||||
app.get('/', (req: Request, res: Response) => { |
||||
res.send('Hello World!'); |
||||
}); |
||||
|
||||
app.use( |
||||
'/apiGoogle', |
||||
createProxyMiddleware({ |
||||
target: 'https://lens.google.com', |
||||
changeOrigin: true, |
||||
secure: false, |
||||
logLevel: 'debug', |
||||
onProxyReq: fixRequestBody, |
||||
agent: new HttpsProxyAgent('http://127.0.0.1:7890'), |
||||
pathRewrite: { |
||||
'^/apiGoogle': '', |
||||
}, |
||||
}), |
||||
); |
||||
|
||||
app.use( |
||||
'/apiBaidu', |
||||
createProxyMiddleware({ |
||||
target: 'https://graph.baidu.com/', |
||||
changeOrigin: true, |
||||
secure: false, |
||||
logLevel: 'debug', |
||||
pathRewrite: { |
||||
'^/apiBaidu': '', |
||||
}, |
||||
}), |
||||
); |
||||
|
||||
app.listen(9190, () => { |
||||
console.log('Express app listening on port 9190!'); |
||||
}); |
||||
} |
@ -0,0 +1,181 @@
@@ -0,0 +1,181 @@
|
||||
import { homedir } from 'node:os'; |
||||
import path from 'node:path'; |
||||
|
||||
process.env.DIST_ELECTRON = path.join(__dirname, '../'); |
||||
process.env.DIST = path.join(process.env.DIST_ELECTRON, '../dist'); |
||||
|
||||
export const isMac = process.platform === 'darwin'; |
||||
export const isLinux = process.platform == 'linux'; |
||||
export const isWin = process.platform == 'win32'; |
||||
|
||||
export const url = import.meta.env.VITE_DEV_SERVER_URL; |
||||
export const WEB_URL = import.meta.env.VITE_WEB_URL; |
||||
export const VITE_API_URL = import.meta.env.VITE_API_URL; |
||||
|
||||
export const preload = path.join(__dirname, '../preload/index.js'); |
||||
export const serverPath = path.join(__dirname, '../server/main.js'); |
||||
export const DIST_ELECTRON = path.join(__dirname, '../'); |
||||
export const DIST = path.join(DIST_ELECTRON, '../dist'); |
||||
|
||||
export const PUBLIC = url ? path.join(DIST_ELECTRON, '../public') : process.env.DIST; |
||||
|
||||
export const ICON = path.join(PUBLIC, './imgs/icons/png/32x32.png'); |
||||
|
||||
export const DOCS_PATH = path.join(homedir(), 'Documents'); |
||||
|
||||
export const PEAR_FILES_PATH = path.join(DOCS_PATH, 'Pear Files'); |
||||
|
||||
export const CONFIG_FILE_PATH = path.join(PEAR_FILES_PATH, `config.json`); |
||||
|
||||
export const DEFAULT_CONFIG_FILE_PATH = path.join(PEAR_FILES_PATH, `default-config.json`); |
||||
|
||||
export const DB_PATH = path.join(PEAR_FILES_PATH, 'db/pear-rec.db'); |
||||
|
||||
export const LOG_PATH = path.join(PEAR_FILES_PATH, 'log'); |
||||
|
||||
export const WIN_CONFIG = { |
||||
main: { |
||||
html: path.join(process.env.DIST, 'index.html'), |
||||
width: 660, |
||||
height: 410, |
||||
autoHideMenuBar: true, |
||||
maximizable: false, |
||||
resizable: false, |
||||
}, |
||||
canvas: { |
||||
html: path.join(process.env.DIST, 'canvas.html'), |
||||
height: 768, |
||||
width: 1024, |
||||
autoHideMenuBar: true, |
||||
}, |
||||
clipScreen: { |
||||
html: path.join(process.env.DIST, 'clipScreen.html'), |
||||
autoHideMenuBar: true, |
||||
frame: false, // 无边框窗口
|
||||
resizable: true, // 窗口大小是否可调整
|
||||
transparent: true, // 使窗口透明
|
||||
fullscreenable: false, // 窗口是否可以进入全屏状态
|
||||
alwaysOnTop: true, |
||||
skipTaskbar: true, |
||||
}, |
||||
editGif: { |
||||
html: path.join(process.env.DIST, 'editGif.html'), |
||||
height: 768, |
||||
width: 1024, |
||||
autoHideMenuBar: true, |
||||
}, |
||||
editImage: { |
||||
html: path.join(process.env.DIST, 'editImage.html'), |
||||
height: 768, |
||||
width: 1024, |
||||
autoHideMenuBar: true, |
||||
}, |
||||
videoConverter: { |
||||
html: path.join(process.env.DIST, 'videoConverter.html'), |
||||
height: 768, |
||||
width: 1024, |
||||
autoHideMenuBar: true, |
||||
}, |
||||
pinImage: { |
||||
html: path.join(process.env.DIST, 'pinImage.html'), |
||||
frame: false, // 无边框窗口
|
||||
transparent: true, // 使窗口透明
|
||||
fullscreenable: false, // 窗口是否可以进入全屏状态
|
||||
alwaysOnTop: true, // 窗口是否永远在别的窗口的上面
|
||||
autoHideMenuBar: true, |
||||
}, |
||||
pinVideo: { |
||||
html: path.join(process.env.DIST, 'pinVideo.html'), |
||||
height: 450, |
||||
width: 600, |
||||
frame: false, // 无边框窗口
|
||||
resizable: true, // 窗口大小是否可调整
|
||||
transparent: true, // 使窗口透明
|
||||
fullscreenable: false, // 窗口是否可以进入全屏状态
|
||||
alwaysOnTop: true, // 窗口是否永远在别的窗口的上面
|
||||
autoHideMenuBar: true, // 自动隐藏菜单栏
|
||||
}, |
||||
recorderAudio: { |
||||
html: path.join(process.env.DIST, 'recorderAudio.html'), |
||||
height: 768, |
||||
width: 1024, |
||||
autoHideMenuBar: true, // 自动隐藏菜单栏
|
||||
}, |
||||
recorderFullScreen: { |
||||
html: path.join(process.env.DIST, 'recorderFullScreen.html'), |
||||
height: 40, |
||||
width: 365, |
||||
center: true, |
||||
transparent: true, // 使窗口透明
|
||||
autoHideMenuBar: true, // 自动隐藏菜单栏
|
||||
frame: false, // 无边框窗口
|
||||
hasShadow: false, // 窗口是否有阴影
|
||||
fullscreenable: false, // 窗口是否可以进入全屏状态
|
||||
alwaysOnTop: true, // 窗口是否永远在别的窗口的上面
|
||||
skipTaskbar: true, |
||||
resizable: false, |
||||
}, |
||||
recorderScreen: { |
||||
html: path.join(process.env.DIST, 'recorderScreen.html'), |
||||
width: 340, |
||||
height: 130, |
||||
autoHideMenuBar: true, // 自动隐藏菜单栏
|
||||
maximizable: false, // 最大
|
||||
hasShadow: false, // 窗口是否有阴影
|
||||
fullscreenable: false, // 窗口是否可以进入全屏状态
|
||||
alwaysOnTop: true, // 窗口是否永远在别的窗口的上面
|
||||
skipTaskbar: true, |
||||
// resizable: false,
|
||||
}, |
||||
recorderVideo: { |
||||
html: path.join(process.env.DIST, 'recorderVideo.html'), |
||||
height: 768, |
||||
width: 1024, |
||||
autoHideMenuBar: true, |
||||
}, |
||||
records: { |
||||
html: path.join(process.env.DIST, 'records.html'), |
||||
width: 1024, |
||||
autoHideMenuBar: true, |
||||
}, |
||||
setting: { |
||||
html: path.join(process.env.DIST, 'setting.html'), |
||||
autoHideMenuBar: true, |
||||
width: 600, |
||||
height: 380, |
||||
}, |
||||
shotScreen: { |
||||
html: path.join(process.env.DIST, 'shotScreen.html'), |
||||
autoHideMenuBar: true, // 自动隐藏菜单栏
|
||||
useContentSize: true, // width 和 height 将设置为 web 页面的尺寸
|
||||
movable: false, // 是否可移动
|
||||
frame: false, // 无边框窗口
|
||||
resizable: false, // 窗口大小是否可调整
|
||||
hasShadow: false, // 窗口是否有阴影
|
||||
transparent: true, // 使窗口透明
|
||||
fullscreenable: true, // 窗口是否可以进入全屏状态
|
||||
fullscreen: true, // 窗口是否全屏
|
||||
simpleFullscreen: true, // 在 macOS 上使用 pre-Lion 全屏
|
||||
alwaysOnTop: true, |
||||
skipTaskbar: true, |
||||
}, |
||||
spliceImage: { |
||||
html: path.join(process.env.DIST, 'spliceImage.html'), |
||||
height: 768, |
||||
width: 1024, |
||||
autoHideMenuBar: true, |
||||
}, |
||||
viewAudio: { |
||||
html: path.join(process.env.DIST, 'viewAudio.html'), |
||||
autoHideMenuBar: true, |
||||
}, |
||||
viewImage: { |
||||
html: path.join(process.env.DIST, 'viewImage.html'), |
||||
frame: false, |
||||
autoHideMenuBar: true, |
||||
}, |
||||
viewVideo: { |
||||
html: path.join(process.env.DIST, 'viewVideo.html'), |
||||
autoHideMenuBar: true, |
||||
}, |
||||
}; |
@ -0,0 +1,77 @@
@@ -0,0 +1,77 @@
|
||||
import { globalShortcut } from 'electron'; |
||||
import { hideShotScreenWin, showShotScreenWin } from '../win/shotScreenWin'; |
||||
import { openRecorderAudioWin } from '../win/recorderAudioWin'; |
||||
import { openClipScreenWin } from '../win/clipScreenWin'; |
||||
import { openRecorderVideoWin } from '../win/recorderVideoWin'; |
||||
|
||||
function registerGlobalShortcut(data) { |
||||
globalShortcut.register(data['screenshot'], () => { |
||||
showShotScreenWin(); |
||||
}); |
||||
|
||||
globalShortcut.register(data['screenRecording'], () => { |
||||
openClipScreenWin(); |
||||
}); |
||||
|
||||
globalShortcut.register(data['audioRecording'], () => { |
||||
openRecorderAudioWin(); |
||||
}); |
||||
|
||||
globalShortcut.register(data['videoRecording'], () => { |
||||
openRecorderVideoWin(); |
||||
}); |
||||
|
||||
globalShortcut.register('Esc', () => { |
||||
hideShotScreenWin(); |
||||
}); |
||||
} |
||||
|
||||
function registerShotScreenShortcut(data) { |
||||
globalShortcut.unregister(data.oldKey); |
||||
globalShortcut.register(data.key, () => { |
||||
showShotScreenWin(); |
||||
}); |
||||
} |
||||
|
||||
function registerRecorderScreenShortcut(data) { |
||||
globalShortcut.unregister(data.oldKey); |
||||
globalShortcut.register(data.key, () => { |
||||
openClipScreenWin(); |
||||
}); |
||||
} |
||||
|
||||
function registerRecorderAudioShortcut(data) { |
||||
globalShortcut.unregister(data.oldKey); |
||||
globalShortcut.register(data.key, () => { |
||||
openRecorderAudioWin(); |
||||
}); |
||||
} |
||||
|
||||
function registerRecorderVideoShortcut(data) { |
||||
globalShortcut.unregister(data.oldKey); |
||||
globalShortcut.register(data.key, () => { |
||||
openRecorderVideoWin(); |
||||
}); |
||||
} |
||||
|
||||
function unregisterGlobalShortcut() { |
||||
globalShortcut.unregister('Alt+Shift+q'); |
||||
globalShortcut.unregister('Alt+Shift+s'); |
||||
globalShortcut.unregister('Alt+Shift+a'); |
||||
globalShortcut.unregister('Alt+Shift+v'); |
||||
globalShortcut.unregister('Esc'); |
||||
} |
||||
|
||||
function unregisterAllGlobalShortcut() { |
||||
globalShortcut.unregisterAll(); |
||||
} |
||||
|
||||
export { |
||||
registerGlobalShortcut, |
||||
unregisterGlobalShortcut, |
||||
unregisterAllGlobalShortcut, |
||||
registerShotScreenShortcut, |
||||
registerRecorderScreenShortcut, |
||||
registerRecorderAudioShortcut, |
||||
registerRecorderVideoShortcut, |
||||
}; |
@ -0,0 +1,83 @@
@@ -0,0 +1,83 @@
|
||||
import { getConfig, initConfig } from '@pear-rec/server/src/config'; |
||||
import { BrowserWindow, app } from 'electron'; |
||||
import { release } from 'node:os'; |
||||
import * as mainWin from '../win/mainWin'; |
||||
import * as shotScreenWin from '../win/shotScreenWin'; |
||||
import { isWin } from './constant'; |
||||
import { unregisterAllGlobalShortcut } from './globalShortcut'; |
||||
import './ipcMain'; |
||||
import './logger'; |
||||
import { protocolHandle, registerSchemesAsPrivileged } from './protocol'; |
||||
import { initTray } from './tray'; |
||||
import { update } from './update'; |
||||
import { initApp } from './app'; |
||||
|
||||
initConfig(); |
||||
initApp(); |
||||
|
||||
// The built directory structure
|
||||
//
|
||||
// ├─┬ dist-electron
|
||||
// │ ├─┬ main
|
||||
// │ │ └── index.js > Electron-Main
|
||||
// │ ├─┬ preload
|
||||
// │ │ └── index.js > Preload-Scripts
|
||||
// │ └─┬ server
|
||||
// │ └── index.js > Server-Scripts
|
||||
// ├─┬ dist
|
||||
// │ └── index.html > Electron-Renderer
|
||||
//
|
||||
|
||||
// Disable GPU Acceleration for Windows 7
|
||||
if (release().startsWith('6.1')) app.disableHardwareAcceleration(); |
||||
|
||||
// Set application name for Windows 10+ notifications
|
||||
if (isWin) app.setAppUserModelId(app.getName()); |
||||
|
||||
app.commandLine.appendSwitch('in-process-gpu'); |
||||
|
||||
if (!app.requestSingleInstanceLock()) { |
||||
app.quit(); |
||||
process.exit(0); |
||||
} |
||||
|
||||
// Remove electron security warnings
|
||||
// This warning only shows in development mode
|
||||
// Read more on https://www.electronjs.org/docs/latest/tutorial/security
|
||||
// process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
|
||||
|
||||
async function createWindow() { |
||||
mainWin.openMainWin(); |
||||
shotScreenWin.openShotScreenWin(); |
||||
} |
||||
|
||||
registerSchemesAsPrivileged(); |
||||
|
||||
app.whenReady().then(() => { |
||||
const config = getConfig(); |
||||
protocolHandle(); |
||||
createWindow(); |
||||
initTray(config.language); |
||||
update(); |
||||
}); |
||||
|
||||
app.on('will-quit', () => { |
||||
unregisterAllGlobalShortcut(); |
||||
}); |
||||
|
||||
app.on('window-all-closed', () => { |
||||
// if (process.platform !== 'darwin') app.quit();
|
||||
}); |
||||
|
||||
app.on('second-instance', () => { |
||||
mainWin.focusMainWin(); |
||||
}); |
||||
|
||||
app.on('activate', () => { |
||||
const allWindows = BrowserWindow.getAllWindows(); |
||||
if (allWindows.length) { |
||||
allWindows[0].focus(); |
||||
} else { |
||||
createWindow(); |
||||
} |
||||
}); |
@ -0,0 +1,447 @@
@@ -0,0 +1,447 @@
|
||||
import { editConfig } from '@pear-rec/server/src/config'; |
||||
import { |
||||
BrowserWindow, |
||||
app, |
||||
desktopCapturer, |
||||
dialog, |
||||
ipcMain, |
||||
screen, |
||||
shell, |
||||
webContents, |
||||
} from 'electron'; |
||||
import * as canvasWin from '../win/canvasWin'; |
||||
import * as clipScreenWin from '../win/clipScreenWin'; |
||||
import * as editGifWin from '../win/editGifWin'; |
||||
import * as editImageWin from '../win/editImageWin'; |
||||
import * as mainWin from '../win/mainWin'; |
||||
import * as pinImageWin from '../win/pinImageWin'; |
||||
import * as pinVideoWin from '../win/pinVideoWin'; |
||||
import * as recorderAudioWin from '../win/recorderAudioWin'; |
||||
import * as recorderFullScreenWin from '../win/recorderFullScreenWin'; |
||||
import * as recorderScreenWin from '../win/recorderScreenWin'; |
||||
import * as recorderVideoWin from '../win/recorderVideoWin'; |
||||
import * as recordsWin from '../win/recordsWin'; |
||||
import * as settingWin from '../win/settingWin'; |
||||
import * as shotScreenWin from '../win/shotScreenWin'; |
||||
import * as spliceImageWin from '../win/spliceImageWin'; |
||||
import * as viewAudioWin from '../win/viewAudioWin'; |
||||
import * as viewImageWin from '../win/viewImageWin'; |
||||
import * as viewVideoWin from '../win/viewVideoWin'; |
||||
import * as videoConverterWin from '../win/videoConverterWin'; |
||||
import * as globalShortcut from './globalShortcut'; |
||||
import logger from './logger'; |
||||
import { showNotification } from './notification'; |
||||
import * as utils from './utils'; |
||||
|
||||
const selfWindws = async () => |
||||
await Promise.all( |
||||
webContents |
||||
.getAllWebContents() |
||||
.filter((item) => { |
||||
const win = BrowserWindow.fromWebContents(item); |
||||
return win && win.isVisible(); |
||||
}) |
||||
.map(async (item) => { |
||||
const win = BrowserWindow.fromWebContents(item); |
||||
const thumbnail = await win?.capturePage(); |
||||
return { |
||||
name: win?.getTitle() + (item.devToolsWebContents === null ? '' : '-dev'), // 给dev窗口加上后缀
|
||||
id: win?.getMediaSourceId(), |
||||
thumbnail, |
||||
display_id: '', |
||||
appIcon: null, |
||||
}; |
||||
}), |
||||
); |
||||
|
||||
function initIpcMain() { |
||||
// 日志
|
||||
ipcMain.on('lg:send-msg', (e, msg) => { |
||||
logger.info(msg); |
||||
}); |
||||
|
||||
// 通知
|
||||
ipcMain.on('nt:send-msg', (e, options) => { |
||||
showNotification(options); |
||||
}); |
||||
|
||||
// 主页
|
||||
ipcMain.on('ma:open-win', () => { |
||||
mainWin.openMainWin(); |
||||
}); |
||||
ipcMain.on('ma:close-win', () => { |
||||
mainWin.closeMainWin(); |
||||
}); |
||||
// 录屏
|
||||
ipcMain.on('rs:open-win', (e, search) => { |
||||
clipScreenWin.closeClipScreenWin(); |
||||
recorderScreenWin.closeRecorderScreenWin(); |
||||
mainWin.closeMainWin(); |
||||
recorderScreenWin.openRecorderScreenWin(search); |
||||
}); |
||||
ipcMain.on('rs:close-win', (e, filePath) => { |
||||
recorderScreenWin.closeRecorderScreenWin(); |
||||
}); |
||||
ipcMain.on('rs:hide-win', () => { |
||||
recorderScreenWin.hideRecorderScreenWin(); |
||||
}); |
||||
ipcMain.on('rs:minimize-win', () => { |
||||
recorderScreenWin.minimizeRecorderScreenWin(); |
||||
}); |
||||
ipcMain.handle('rs:get-desktop-capturer-source', async () => { |
||||
return [...(await desktopCapturer.getSources({ types: ['screen'] })), ...(await selfWindws())]; |
||||
}); |
||||
ipcMain.handle('rs:get-bounds-clip', async () => { |
||||
return clipScreenWin.getBoundsClipScreenWin(); |
||||
}); |
||||
ipcMain.on('rs:start-record', (event) => { |
||||
clipScreenWin.setMovableClipScreenWin(false); |
||||
clipScreenWin.setResizableClipScreenWin(false); |
||||
clipScreenWin.setIgnoreMouseEventsClipScreenWin(event, true, { |
||||
forward: true, |
||||
}); |
||||
clipScreenWin.setIsPlayClipScreenWin(true); |
||||
}); |
||||
ipcMain.on('rs:pause-record', (event) => { |
||||
clipScreenWin.setMovableClipScreenWin(true); |
||||
clipScreenWin.setResizableClipScreenWin(true); |
||||
clipScreenWin.setIgnoreMouseEventsClipScreenWin(event, false); |
||||
clipScreenWin.setIsPlayClipScreenWin(false); |
||||
}); |
||||
ipcMain.on('rs:stop-record', (event) => { |
||||
clipScreenWin.setMovableClipScreenWin(true); |
||||
clipScreenWin.setResizableClipScreenWin(true); |
||||
clipScreenWin.setIgnoreMouseEventsClipScreenWin(event, false); |
||||
clipScreenWin.setIsPlayClipScreenWin(false); |
||||
}); |
||||
ipcMain.handle('rs:get-cursor-screen-point', () => { |
||||
return recorderScreenWin.getCursorScreenPointRecorderScreenWin(); |
||||
}); |
||||
ipcMain.handle('rs:is-focused', () => { |
||||
return recorderScreenWin.isFocusedRecorderScreenWin(); |
||||
}); |
||||
ipcMain.on('rs:focus', () => { |
||||
recorderScreenWin.focusRecorderScreenWin(); |
||||
}); |
||||
// 录屏截图
|
||||
ipcMain.on('cs:open-win', (e, search) => { |
||||
clipScreenWin.closeClipScreenWin(); |
||||
clipScreenWin.openClipScreenWin(search); |
||||
}); |
||||
ipcMain.on('cs:close-win', () => { |
||||
clipScreenWin.closeClipScreenWin(); |
||||
recorderScreenWin.closeRecorderScreenWin(); |
||||
}); |
||||
ipcMain.on('cs:hide-win', () => { |
||||
clipScreenWin.hideClipScreenWin(); |
||||
recorderScreenWin.hideRecorderScreenWin(); |
||||
}); |
||||
ipcMain.on('cs:minimize-win', () => { |
||||
clipScreenWin.minimizeClipScreenWin(); |
||||
}); |
||||
ipcMain.on('cs:set-bounds', (event, bounds) => { |
||||
clipScreenWin.setBoundsClipScreenWin(bounds); |
||||
}); |
||||
ipcMain.on('cs:set-ignore-mouse-events', (event, ignore, options) => { |
||||
recorderScreenWin.setIgnoreMouseEventsRecorderScreenWin(event, ignore, options); |
||||
}); |
||||
ipcMain.handle('cs:get-bounds', () => clipScreenWin.getBoundsClipScreenWin()); |
||||
// 截图
|
||||
ipcMain.handle('ss:get-shot-screen-img', async () => { |
||||
const { id } = screen.getPrimaryDisplay(); |
||||
const { width, height } = utils.getScreenSize(); |
||||
const sources = [ |
||||
...(await desktopCapturer.getSources({ |
||||
types: ['screen'], |
||||
thumbnailSize: { |
||||
width, |
||||
height, |
||||
}, |
||||
})), |
||||
]; |
||||
|
||||
let source = sources.filter((e: any) => parseInt(e.display_id, 10) == id)[0]; |
||||
source || (source = sources[0]); |
||||
const img = source.thumbnail.toDataURL(); |
||||
return img; |
||||
}); |
||||
ipcMain.on('ss:open-win', () => { |
||||
shotScreenWin.hideShotScreenWin(); |
||||
shotScreenWin.showShotScreenWin(); |
||||
}); |
||||
ipcMain.on('ss:close-win', () => { |
||||
shotScreenWin.hideShotScreenWin(); |
||||
}); |
||||
ipcMain.on('ss:start-win', async () => { |
||||
mainWin.closeMainWin(); |
||||
setTimeout(() => { |
||||
shotScreenWin.hideShotScreenWin(); |
||||
shotScreenWin.showShotScreenWin(); |
||||
}, 100 * 2); |
||||
}); |
||||
ipcMain.on('ss:save-img', async (e, downloadUrl) => { |
||||
shotScreenWin.downloadURLShotScreenWin(downloadUrl); |
||||
}); |
||||
ipcMain.on('ss:download-img', async (e, downloadUrl) => { |
||||
shotScreenWin.downloadURLShotScreenWin(downloadUrl, true); |
||||
}); |
||||
ipcMain.on('ss:open-external', async (e, tabUrl) => { |
||||
shell.openExternal(tabUrl); |
||||
}); |
||||
ipcMain.handle('ss:get-desktop-capturer-source', async () => { |
||||
return [...(await desktopCapturer.getSources({ types: ['screen'] })), ...(await selfWindws())]; |
||||
}); |
||||
ipcMain.on('ss:copy-img', (e, imgUrl) => { |
||||
shotScreenWin.copyImg(imgUrl); |
||||
}); |
||||
// 图片展示
|
||||
ipcMain.on('vi:open-win', (e, search) => { |
||||
viewImageWin.closeViewImageWin(); |
||||
viewImageWin.openViewImageWin(search); |
||||
}); |
||||
ipcMain.on('vi:close-win', () => { |
||||
viewImageWin.closeViewImageWin(); |
||||
}); |
||||
ipcMain.on('vi:hide-win', () => { |
||||
viewImageWin.hideViewImageWin(); |
||||
}); |
||||
ipcMain.on('vi:minimize-win', () => { |
||||
viewImageWin.minimizeViewImageWin(); |
||||
}); |
||||
ipcMain.on('vi:maximize-win', () => { |
||||
viewImageWin.maximizeViewImageWin(); |
||||
}); |
||||
ipcMain.on('vi:unmaximize-win', () => { |
||||
viewImageWin.unmaximizeViewImageWin(); |
||||
}); |
||||
ipcMain.on('vi:open-file', (e, imgUrl) => { |
||||
shell.openExternal(imgUrl); |
||||
}); |
||||
ipcMain.on('vi:alwaysOnTop-win', (e, isTop) => { |
||||
viewImageWin.setIsAlwaysOnTopViewImageWin(isTop); |
||||
}); |
||||
ipcMain.handle('vi:set-always-on-top', () => { |
||||
const isAlwaysOnTop = viewImageWin.getIsAlwaysOnTopViewImageWin(); |
||||
return viewImageWin.setIsAlwaysOnTopViewImageWin(!isAlwaysOnTop); |
||||
}); |
||||
ipcMain.handle('vi:get-imgs', async (e, img) => { |
||||
const imgs = await viewImageWin.getImgs(img); |
||||
return imgs; |
||||
}); |
||||
ipcMain.on('vi:download-img', async (e, imgUrl) => { |
||||
viewImageWin.downloadImg(imgUrl); |
||||
}); |
||||
|
||||
// 图片编辑
|
||||
ipcMain.on('ei:close-win', () => { |
||||
editImageWin.closeEditImageWin(); |
||||
}); |
||||
ipcMain.on('ei:open-win', (e, search) => { |
||||
editImageWin.openEditImageWin(search); |
||||
}); |
||||
ipcMain.on('ei:download-img', (e, imgUrl) => { |
||||
editImageWin.downloadImg(imgUrl); |
||||
}); |
||||
|
||||
// 图片拼接
|
||||
ipcMain.on('si:close-win', () => { |
||||
spliceImageWin.closeSpliceImageWin(); |
||||
}); |
||||
ipcMain.on('si:open-win', () => { |
||||
spliceImageWin.openSpliceImageWin(); |
||||
}); |
||||
|
||||
// 视频转换
|
||||
ipcMain.on('vc:close-win', () => { |
||||
videoConverterWin.closeVideoConverterWin(); |
||||
}); |
||||
ipcMain.on('vc:open-win', (e, search) => { |
||||
videoConverterWin.openVideoConverterWin(search); |
||||
}); |
||||
|
||||
// 动图编辑
|
||||
ipcMain.on('eg:close-win', () => { |
||||
editGifWin.closeEditGifWin(); |
||||
}); |
||||
ipcMain.on('eg:open-win', (e, search) => { |
||||
editGifWin.openEditGifWin(search); |
||||
}); |
||||
|
||||
// 画画
|
||||
ipcMain.on('ca:close-win', () => { |
||||
canvasWin.closeCanvasWin(); |
||||
}); |
||||
ipcMain.on('ca:open-win', () => { |
||||
canvasWin.openCanvasWin(); |
||||
}); |
||||
|
||||
// 视频音频展示;
|
||||
ipcMain.on('vv:open-win', (e, search) => { |
||||
viewVideoWin.closeViewVideoWin(); |
||||
viewVideoWin.openViewVideoWin(search); |
||||
}); |
||||
ipcMain.on('vv:close-win', () => { |
||||
viewVideoWin.closeViewVideoWin(); |
||||
}); |
||||
ipcMain.on('vv:hide-win', () => { |
||||
viewVideoWin.hideViewVideoWin(); |
||||
}); |
||||
ipcMain.on('vv:minimize-win', () => { |
||||
viewVideoWin.minimizeViewVideoWin(); |
||||
}); |
||||
ipcMain.on('vv:maximize-win', () => { |
||||
viewVideoWin.maximizeViewVideoWin(); |
||||
}); |
||||
ipcMain.on('vv:unmaximize-win', () => { |
||||
viewVideoWin.unmaximizeViewVideoWin(); |
||||
}); |
||||
ipcMain.on('vv:set-always-on-top', (e, isAlwaysOnTop) => { |
||||
viewVideoWin.setAlwaysOnTopViewVideoWin(isAlwaysOnTop); |
||||
}); |
||||
|
||||
// 录音;
|
||||
ipcMain.on('ra:open-win', () => { |
||||
recorderAudioWin.closeRecorderAudioWin(); |
||||
recorderAudioWin.openRecorderAudioWin(); |
||||
}); |
||||
ipcMain.on('ra:close-win', () => { |
||||
recorderAudioWin.closeRecorderAudioWin(); |
||||
}); |
||||
ipcMain.on('ra:hide-win', () => { |
||||
recorderAudioWin.hideRecorderAudioWin(); |
||||
}); |
||||
ipcMain.on('ra:minimize-win', () => { |
||||
recorderAudioWin.minimizeRecorderAudioWin(); |
||||
}); |
||||
ipcMain.on('ra:download-record', (e, downloadUrl) => { |
||||
recorderAudioWin.downloadURLRecorderAudioWin(downloadUrl); |
||||
}); |
||||
ipcMain.on('ra:start-record', () => { |
||||
recorderAudioWin.setSizeRecorderAudioWin(285, 43); |
||||
}); |
||||
ipcMain.on('ra:pause-record', () => { |
||||
recorderAudioWin.setSizeRecorderAudioWin(260, 43); |
||||
}); |
||||
ipcMain.on('ra:stop-record', () => {}); |
||||
// 录像
|
||||
ipcMain.on('rv:open-win', () => { |
||||
recorderVideoWin.closeRecorderVideoWin(); |
||||
mainWin.hideMainWin(); |
||||
recorderVideoWin.openRecorderVideoWin(); |
||||
}); |
||||
ipcMain.on('rv:close-win', () => { |
||||
recorderVideoWin.closeRecorderVideoWin(); |
||||
}); |
||||
ipcMain.on('rv:download-record', (e, downloadUrl) => { |
||||
recorderVideoWin.downloadURLRecorderVideoWin(downloadUrl); |
||||
}); |
||||
// 音频
|
||||
ipcMain.on('va:open-win', (e, search) => { |
||||
viewAudioWin.closeViewAudioWin(); |
||||
viewAudioWin.openViewAudioWin(search); |
||||
}); |
||||
ipcMain.handle('va:get-audios', async (e, audioUrl) => { |
||||
const audios = await viewAudioWin.getAudios(audioUrl); |
||||
return audios; |
||||
}); |
||||
// 设置
|
||||
ipcMain.on('se:open-win', () => { |
||||
settingWin.closeSettingWin(); |
||||
settingWin.openSettingWin(); |
||||
}); |
||||
ipcMain.on('se:close-win', () => { |
||||
settingWin.closeSettingWin(); |
||||
}); |
||||
ipcMain.handle('se:set-filePath', async (e, filePath) => { |
||||
let res = await dialog.showOpenDialog({ |
||||
properties: ['openDirectory'], |
||||
}); |
||||
if (!res.canceled) { |
||||
filePath = res.filePaths[0]; |
||||
} |
||||
return filePath; |
||||
}); |
||||
ipcMain.on('se:set-openAtLogin', (e, isOpen) => { |
||||
app.setLoginItemSettings({ openAtLogin: isOpen }); |
||||
}); |
||||
ipcMain.on('se:set-language', (e, lng) => { |
||||
editConfig('language', lng, () => { |
||||
app.relaunch(); |
||||
app.exit(0); |
||||
}); |
||||
}); |
||||
ipcMain.on('se:set-shortcut', (e, data) => { |
||||
if (data.name == 'screenshot') { |
||||
globalShortcut.registerShotScreenShortcut(data); |
||||
} else if (data.name == 'videoRecording') { |
||||
globalShortcut.registerRecorderVideoShortcut(data); |
||||
} else if (data.name == 'screenRecording') { |
||||
globalShortcut.registerRecorderScreenShortcut(data); |
||||
} else if (data.name == 'audioRecording') { |
||||
globalShortcut.registerRecorderAudioShortcut(data); |
||||
} |
||||
}); |
||||
ipcMain.on('se:set-shortcuts', (e, data) => { |
||||
globalShortcut.registerGlobalShortcut(data); |
||||
}); |
||||
ipcMain.handle('se:get-openAtLogin', () => { |
||||
return app.getLoginItemSettings(); |
||||
}); |
||||
|
||||
// 记录
|
||||
ipcMain.on('re:open-win', () => { |
||||
recordsWin.closeRecordsWin(); |
||||
recordsWin.openRecordsWin(); |
||||
}); |
||||
|
||||
// 钉图
|
||||
ipcMain.on('pi:set-size-win', (e, size) => { |
||||
pinImageWin.setSizePinImageWin(size); |
||||
}); |
||||
ipcMain.on('pi:open-win', (e, search) => { |
||||
pinImageWin.openPinImageWin(search); |
||||
}); |
||||
ipcMain.on('pi:close-win', () => { |
||||
pinImageWin.closePinImageWin(); |
||||
}); |
||||
ipcMain.on('pi:minimize-win', () => { |
||||
pinImageWin.minimizePinImageWin(); |
||||
}); |
||||
ipcMain.on('pi:maximize-win', () => { |
||||
pinImageWin.maximizePinImageWin(); |
||||
}); |
||||
ipcMain.on('pi:unmaximize-win', () => { |
||||
pinImageWin.unmaximizePinImageWin(); |
||||
}); |
||||
ipcMain.handle('pi:get-size-win', () => { |
||||
return pinImageWin.getSizePinImageWin(); |
||||
}); |
||||
|
||||
// 钉图
|
||||
ipcMain.on('pv:open-win', (e, search) => { |
||||
pinVideoWin.openPinVideoWin(search); |
||||
}); |
||||
ipcMain.on('pv:close-win', () => { |
||||
pinVideoWin.closePinVideoWin(); |
||||
}); |
||||
ipcMain.on('pv:minimize-win', () => { |
||||
pinVideoWin.minimizePinVideoWin(); |
||||
}); |
||||
ipcMain.on('pv:maximize-win', () => { |
||||
pinVideoWin.maximizePinVideoWin(); |
||||
}); |
||||
ipcMain.on('pv:unmaximize-win', () => { |
||||
pinVideoWin.unmaximizePinVideoWin(); |
||||
}); |
||||
|
||||
// 录全屏
|
||||
ipcMain.on('rfs:open-win', () => { |
||||
recorderFullScreenWin.closeRecorderFullScreenWin(); |
||||
recorderFullScreenWin.openRecorderFullScreenWin(); |
||||
}); |
||||
ipcMain.on('rfs:close-win', () => { |
||||
recorderFullScreenWin.closeRecorderFullScreenWin(); |
||||
}); |
||||
} |
||||
|
||||
initIpcMain(); |
@ -0,0 +1,79 @@
@@ -0,0 +1,79 @@
|
||||
// logger.ts
|
||||
import { app } from 'electron'; |
||||
import log from 'electron-log'; |
||||
import path from 'node:path'; |
||||
import { LOG_PATH } from './constant'; |
||||
|
||||
// 关闭控制台打印
|
||||
log.transports.console.level = false; |
||||
log.transports.file.level = 'debug'; |
||||
log.transports.file.maxSize = 10024300; // 文件最大不超过 10M
|
||||
// 输出格式
|
||||
log.transports.file.format = '[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}]{scope} {text}'; |
||||
let date = new Date(); |
||||
let dateStr = date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate(); |
||||
// 文件位置及命名方式
|
||||
// 默认位置为:C:\Users\[user]\AppData\Roaming\[appname]\electron_log\
|
||||
// 文件名为:年-月-日.log
|
||||
// 自定义文件保存位置为安装目录下 \log\年-月-日.log
|
||||
log.transports.file.resolvePathFn = () => path.join(LOG_PATH, dateStr + '.log'); |
||||
|
||||
// 有六个日志级别error, warn, info, verbose, debug, silly。默认是silly
|
||||
export default { |
||||
info(param: any) { |
||||
log.info(param); |
||||
}, |
||||
warn(param: any) { |
||||
log.warn(param); |
||||
}, |
||||
error(param: any) { |
||||
log.error(param); |
||||
}, |
||||
debug(param: any) { |
||||
log.debug(param); |
||||
}, |
||||
verbose(param: any) { |
||||
log.verbose(param); |
||||
}, |
||||
silly(param: any) { |
||||
log.silly(param); |
||||
}, |
||||
}; |
||||
|
||||
app.on('ready', async () => { |
||||
// 渲染进程崩溃
|
||||
// app.on('renderer-process-crashed', (event, webContents, killed) => {
|
||||
// log.error(
|
||||
// `APP-ERROR:renderer-process-crashed; event: ${JSON.stringify(
|
||||
// event,
|
||||
// )}; webContents:${JSON.stringify(webContents)}; killed:${JSON.stringify(killed)}`,
|
||||
// );
|
||||
// });
|
||||
|
||||
// // GPU进程崩溃
|
||||
// app.on('gpu-process-crashed', (event, killed) => {
|
||||
// log.error(
|
||||
// `APP-ERROR:gpu-process-crashed; event: ${JSON.stringify(event)}; killed: ${JSON.stringify(
|
||||
// killed,
|
||||
// )}`,
|
||||
// );
|
||||
// });
|
||||
|
||||
// 渲染进程结束
|
||||
app.on('render-process-gone', async (event, webContents, details) => { |
||||
log.error( |
||||
`APP-ERROR:render-process-gone; event: ${JSON.stringify(event)}; webContents:${JSON.stringify( |
||||
webContents, |
||||
)}; details:${JSON.stringify(details)}`,
|
||||
); |
||||
}); |
||||
|
||||
// 子进程结束
|
||||
app.on('child-process-gone', async (event, details) => { |
||||
log.error( |
||||
`APP-ERROR:child-process-gone; event: ${JSON.stringify(event)}; details:${JSON.stringify( |
||||
details, |
||||
)}`,
|
||||
); |
||||
}); |
||||
}); |
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
import { Notification } from 'electron'; |
||||
import { ICON } from './constant'; |
||||
|
||||
export function showNotification(_options) { |
||||
const options = { icon: ICON, ..._options }; |
||||
new Notification(options).show(); |
||||
} |
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
import { net, protocol } from 'electron'; |
||||
|
||||
export function registerSchemesAsPrivileged() { |
||||
protocol.registerSchemesAsPrivileged([ |
||||
{ |
||||
scheme: 'pearrec', |
||||
privileges: { |
||||
standard: true, |
||||
secure: true, |
||||
supportFetchAPI: true, |
||||
stream: true, |
||||
}, |
||||
}, |
||||
]); |
||||
} |
||||
|
||||
export function protocolHandle() { |
||||
protocol.handle('pearrec', (req) => { |
||||
const { host, pathname } = new URL(req.url); |
||||
|
||||
try { |
||||
return net.fetch(`file://${host}:\\${pathname}`); |
||||
// return net.fetch(`file://C://Users/Administrator/Desktop/pear-rec_1709102672477.mp4`);
|
||||
} catch (error) { |
||||
console.error('protocolHandle', error); |
||||
} |
||||
}); |
||||
} |
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
import { type UtilityProcess, utilityProcess } from 'electron'; |
||||
import { url, serverPath } from './constant'; |
||||
import logger from './logger'; |
||||
|
||||
let serverProcess: null | UtilityProcess = null; |
||||
|
||||
export function initServerProcess() { |
||||
serverProcess = |
||||
url || |
||||
utilityProcess.fork(serverPath, [], { |
||||
stdio: 'pipe', |
||||
}); |
||||
|
||||
serverProcess.on?.('spawn', () => { |
||||
serverProcess.stdout?.on('data', (data) => { |
||||
console.log(`serverProcess output: ${data}`); |
||||
logger.info(`serverProcess output: ${data}`); |
||||
}); |
||||
serverProcess.stderr?.on('data', (data) => { |
||||
console.error(`serverProcess err: ${data}`); |
||||
logger.error(`serverProcess output: ${data}`); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
export function quitServerProcess() { |
||||
url || serverProcess?.kill(); |
||||
} |
@ -0,0 +1,126 @@
@@ -0,0 +1,126 @@
|
||||
import { Menu, Tray, app, shell } from 'electron'; |
||||
import { ICON } from './constant'; |
||||
import { openMainWin } from '../win/mainWin'; |
||||
import { showShotScreenWin } from '../win/shotScreenWin'; |
||||
import { openClipScreenWin } from '../win/clipScreenWin'; |
||||
import { openRecorderAudioWin } from '../win/recorderAudioWin'; |
||||
import { openRecorderVideoWin } from '../win/recorderVideoWin'; |
||||
import { openViewImageWin } from '../win/viewImageWin'; |
||||
import { openViewVideoWin } from '../win/viewVideoWin'; |
||||
import { openViewAudioWin } from '../win/viewAudioWin'; |
||||
import { openSettingWin } from '../win/settingWin'; |
||||
import * as zhCN from '../i18n/zh-CN.json'; |
||||
import * as enUS from '../i18n/en-US.json'; |
||||
import * as deDE from '../i18n/de-DE.json'; |
||||
|
||||
const lngMap = { |
||||
zh: zhCN, |
||||
en: enUS, |
||||
de: deDE, |
||||
} as any; |
||||
|
||||
export function initTray(lng: string) { |
||||
let appIcon = new Tray(ICON); |
||||
const t = lngMap[lng].tray; |
||||
const contextMenu = Menu.buildFromTemplate([ |
||||
{ |
||||
label: t.screenshot, |
||||
click: () => { |
||||
showShotScreenWin(); |
||||
}, |
||||
}, |
||||
{ |
||||
label: t.screenRecording, |
||||
click: () => { |
||||
openClipScreenWin(); |
||||
}, |
||||
}, |
||||
{ |
||||
label: t.audioRecording, |
||||
click: () => { |
||||
openRecorderAudioWin(); |
||||
}, |
||||
}, |
||||
{ |
||||
label: t.videoRecording, |
||||
click: () => { |
||||
openRecorderVideoWin(); |
||||
}, |
||||
}, |
||||
{ |
||||
type: 'separator', |
||||
}, |
||||
{ |
||||
label: t.viewImage, |
||||
click: () => { |
||||
openViewImageWin(); |
||||
}, |
||||
}, |
||||
{ |
||||
label: t.watchVideo, |
||||
click: () => { |
||||
openViewVideoWin(); |
||||
}, |
||||
}, |
||||
{ |
||||
label: t.playAudio, |
||||
click: () => { |
||||
openViewAudioWin(); |
||||
}, |
||||
}, |
||||
// {
|
||||
// type: "separator",
|
||||
// },
|
||||
// {
|
||||
// label: "开机自启动",
|
||||
// type: "checkbox",
|
||||
// checked: true,
|
||||
// click: (i) => {
|
||||
// app.setLoginItemSettings({ openAtLogin: i.checked });
|
||||
// },
|
||||
// },
|
||||
{ |
||||
type: 'separator', |
||||
}, |
||||
{ |
||||
label: t.home, |
||||
click: () => { |
||||
openMainWin(); |
||||
}, |
||||
}, |
||||
{ |
||||
label: t.setting, |
||||
click: () => { |
||||
openSettingWin(); |
||||
}, |
||||
}, |
||||
{ |
||||
label: t.help, |
||||
click: () => { |
||||
shell.openExternal('https://027xiguapi.github.io/pear-rec/'); |
||||
}, |
||||
}, |
||||
{ |
||||
type: 'separator', |
||||
}, |
||||
{ |
||||
label: t.relaunch, |
||||
click: () => { |
||||
app.relaunch(); |
||||
app.exit(0); |
||||
}, |
||||
}, |
||||
{ |
||||
label: t.quit, |
||||
click: () => { |
||||
app.quit(); |
||||
}, |
||||
}, |
||||
]); |
||||
appIcon.setToolTip('梨子REC'); |
||||
appIcon.setContextMenu(contextMenu); |
||||
appIcon.addListener('click', function () { |
||||
openMainWin(); |
||||
}); |
||||
return appIcon; |
||||
} |
@ -0,0 +1,80 @@
@@ -0,0 +1,80 @@
|
||||
import { app, ipcMain } from 'electron'; |
||||
import { type ProgressInfo, type UpdateDownloadedEvent, autoUpdater } from 'electron-updater'; |
||||
import * as mainWin from '../win/mainWin'; |
||||
|
||||
export function update() { |
||||
// When set to false, the update download will be triggered through the API
|
||||
autoUpdater.autoDownload = false; |
||||
autoUpdater.disableWebInstaller = false; |
||||
autoUpdater.allowDowngrade = false; |
||||
// start check
|
||||
autoUpdater.on('checking-for-update', function () { |
||||
console.log('Checking for update.'); |
||||
}); |
||||
// update available
|
||||
autoUpdater.on('update-available', (arg) => { |
||||
mainWin.sendEuUpdateCanAvailable(arg, true); |
||||
// win.webContents.send('eu:update-can-available', {
|
||||
// update: true,
|
||||
// version: app.getVersion(),
|
||||
// newVersion: arg?.version,
|
||||
// });
|
||||
}); |
||||
// update not available
|
||||
autoUpdater.on('update-not-available', (arg) => { |
||||
mainWin.sendEuUpdateCanAvailable(arg, false); |
||||
// win.webContents.send('eu:update-can-available', {
|
||||
// update: false,
|
||||
// version: app.getVersion(),
|
||||
// newVersion: arg?.version,
|
||||
// });
|
||||
}); |
||||
|
||||
// Checking for updates
|
||||
ipcMain.handle('eu:check-update', async () => { |
||||
if (!app.isPackaged) { |
||||
const error = new Error('The update feature is only available after the package.'); |
||||
return { message: error.message, error }; |
||||
} |
||||
|
||||
try { |
||||
return await autoUpdater.checkForUpdatesAndNotify(); |
||||
} catch (error) { |
||||
return { message: 'Network error', error }; |
||||
} |
||||
}); |
||||
|
||||
// Start downloading and feedback on progress
|
||||
ipcMain.handle('eu:start-download', (event) => { |
||||
startDownload( |
||||
(error, progressInfo) => { |
||||
if (error) { |
||||
// feedback download error message
|
||||
event.sender.send('eu:update-error', { message: error.message, error }); |
||||
} else { |
||||
// feedback update progress message
|
||||
event.sender.send('eu:download-progress', progressInfo); |
||||
} |
||||
}, |
||||
() => { |
||||
// feedback update downloaded message
|
||||
event.sender.send('eu:update-downloaded'); |
||||
}, |
||||
); |
||||
}); |
||||
|
||||
// Install now
|
||||
ipcMain.handle('eu:quit-and-install', () => { |
||||
autoUpdater.quitAndInstall(false, true); |
||||
}); |
||||
} |
||||
|
||||
function startDownload( |
||||
callback: (error: Error | null, info: ProgressInfo | null) => void, |
||||
complete: (event: UpdateDownloadedEvent) => void, |
||||
) { |
||||
autoUpdater.on('download-progress', (info) => callback(null, info)); |
||||
autoUpdater.on('error', (error) => callback(error, null)); |
||||
autoUpdater.on('update-downloaded', complete); |
||||
autoUpdater.downloadUpdate(); |
||||
} |
@ -0,0 +1,142 @@
@@ -0,0 +1,142 @@
|
||||
import { screen } from 'electron'; |
||||
import * as fs from 'node:fs'; |
||||
import path from 'node:path'; |
||||
import { PEAR_FILES_PATH } from './constant'; |
||||
|
||||
function getScreenSize() { |
||||
const { size, scaleFactor } = screen.getPrimaryDisplay(); |
||||
return { |
||||
width: size.width * scaleFactor, |
||||
height: size.height * scaleFactor, |
||||
}; |
||||
} |
||||
|
||||
function downloadFile(fileInfo: any) { |
||||
let { base64String, fileName, fileType = 'png' } = fileInfo; |
||||
fileName || (fileName = Number(new Date())); |
||||
const url = base64String.split(',')[1]; // 移除前缀,获取base64数据部分
|
||||
const buffer = Buffer.from(url, 'base64'); // 将base64数据转化为buffer
|
||||
const imagePath = path.join(PEAR_FILES_PATH, `/ss/${fileName}.${fileType}`); // 下载路径
|
||||
|
||||
const directory = path.dirname(imagePath); // 获取文件目录
|
||||
if (!fs.existsSync(directory)) { |
||||
// 检查目录是否存在
|
||||
fs.mkdirSync(directory, { recursive: true }); // 不存在则创建目录
|
||||
} |
||||
fs.writeFileSync(imagePath, buffer); // 将buffer写入本地文件
|
||||
} |
||||
|
||||
function saveFile(fileInfo: any) { |
||||
let { base64String, fileName, fileType = 'png' } = fileInfo; |
||||
fileName || (fileName = Number(new Date())); |
||||
const url = base64String.split(',')[1]; // 移除前缀,获取base64数据部分
|
||||
const buffer = Buffer.from(url, 'base64'); // 将base64数据转化为buffer
|
||||
const filePath = path.join(PEAR_FILES_PATH, `/ss/${fileName}.${fileType}`); // 下载路径
|
||||
|
||||
const directory = path.dirname(filePath); // 获取文件目录
|
||||
if (!fs.existsSync(directory)) { |
||||
// 检查目录是否存在
|
||||
fs.mkdirSync(directory, { recursive: true }); // 不存在则创建目录
|
||||
} |
||||
fs.writeFileSync(filePath, buffer); // 将buffer写入本地文件
|
||||
} |
||||
|
||||
function getImgsByImgUrl(imgUrl: string) { |
||||
const directoryPath = path.dirname(imgUrl); |
||||
const files = fs.readdirSync(directoryPath); // 读取目录内容
|
||||
let imgs: any[] = []; |
||||
let index = 0; |
||||
let currentIndex = 0; |
||||
files.forEach((file) => { |
||||
const filePath = path.join(directoryPath, file); |
||||
|
||||
if (isImageFile(filePath)) { |
||||
filePath == imgUrl && (currentIndex = index); |
||||
imgs.push({ url: `pearrec:///${filePath}`, index }); |
||||
index++; |
||||
} |
||||
}); |
||||
return { imgs, currentIndex }; |
||||
} |
||||
|
||||
function isImageFile(filePath: string): boolean { |
||||
const ext = path.extname(filePath).toLowerCase(); |
||||
return [ |
||||
'.jpg', |
||||
'.jpeg', |
||||
'.jfif', |
||||
'.pjpeg', |
||||
'.pjp', |
||||
'.png', |
||||
'apng', |
||||
'.gif', |
||||
'.bmp', |
||||
'.avif', |
||||
'.webp', |
||||
'.ico', |
||||
].includes(ext); |
||||
} |
||||
|
||||
function getAudiosByAudioUrl(audioUrl: string) { |
||||
const directoryPath = path.dirname(audioUrl); |
||||
const files = fs.readdirSync(directoryPath); // 读取目录内容
|
||||
let audios: any[] = []; |
||||
let index = 0; |
||||
files.forEach((file) => { |
||||
const filePath = path.join(directoryPath, file); |
||||
if (isAudioFile(filePath)) { |
||||
const fileName = path.basename(filePath); |
||||
if (filePath == audioUrl) { |
||||
audios.unshift({ |
||||
url: `pearrec:///${filePath}`, |
||||
name: fileName, |
||||
cover: './imgs/music.png', |
||||
}); |
||||
} else { |
||||
audios.push({ |
||||
url: `pearrec:///${filePath}`, |
||||
name: fileName, |
||||
cover: './imgs/music.png', |
||||
}); |
||||
} |
||||
index++; |
||||
} |
||||
}); |
||||
return audios; |
||||
} |
||||
|
||||
function isAudioFile(filePath: string): boolean { |
||||
const ext = path.extname(filePath).toLowerCase(); |
||||
return [ |
||||
'.mp3', |
||||
'.wav', |
||||
'.aac', |
||||
'.ogg', |
||||
'.flac', |
||||
'.aiff', |
||||
'aif', |
||||
'.m4a', |
||||
'.alac', |
||||
'.ac3', |
||||
].includes(ext); |
||||
} |
||||
|
||||
function readDirectoryVideo(filePath: string) { |
||||
filePath = filePath.replace(/\\/g, '/'); |
||||
return filePath && `pearrec:///${filePath}`; |
||||
} |
||||
|
||||
function readDirectoryImg(filePath: string) { |
||||
filePath = filePath.replace(/\\/g, '/'); |
||||
return filePath && `pearrec:///${filePath}`; |
||||
} |
||||
|
||||
export { |
||||
downloadFile, |
||||
getAudiosByAudioUrl, |
||||
getImgsByImgUrl, |
||||
getScreenSize, |
||||
readDirectoryImg, |
||||
readDirectoryVideo, |
||||
saveFile, |
||||
}; |
@ -0,0 +1,164 @@
@@ -0,0 +1,164 @@
|
||||
import { contextBridge, ipcRenderer } from 'electron'; |
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', { |
||||
// logger
|
||||
sendLogger: (msg: any) => ipcRenderer.send('lg:send-msg', msg), |
||||
|
||||
// notification
|
||||
sendNotification: (options: any) => ipcRenderer.send('nt:send-msg', options), |
||||
|
||||
// mainWin
|
||||
sendMaOpenWin: () => ipcRenderer.send('ma:open-win'), |
||||
sendMaCloseWin: () => ipcRenderer.send('ma:close-win'), |
||||
|
||||
//raWin
|
||||
sendRaCloseWin: () => ipcRenderer.send('ra:close-win'), |
||||
sendRaOpenWin: () => ipcRenderer.send('ra:open-win'), |
||||
sendRaDownloadRecord: (url: string) => ipcRenderer.send('ra:download-record', url), |
||||
|
||||
//rsWin
|
||||
sendRsOpenWin: (search?: any) => ipcRenderer.send('rs:open-win', search), |
||||
sendRsCloseWin: () => ipcRenderer.send('rs:close-win'), |
||||
sendRsHideWin: () => ipcRenderer.send('rs:hide-win'), |
||||
sendRsMinimizeWin: () => ipcRenderer.send('rs:minimize-win'), |
||||
sendRsStartRecord: () => ipcRenderer.send('rs:start-record'), |
||||
sendRsPauseRecord: () => ipcRenderer.send('rs:pause-record'), |
||||
sendRsStopRecord: () => ipcRenderer.send('rs:stop-record'), |
||||
invokeRsGetBoundsClip: () => ipcRenderer.invoke('rs:get-bounds-clip'), |
||||
invokeRsGetDesktopCapturerSource: () => { |
||||
return ipcRenderer.invoke('rs:get-desktop-capturer-source'); |
||||
}, |
||||
invokeRsGetCursorScreenPoint: () => ipcRenderer.invoke('rs:get-cursor-screen-point'), |
||||
invokeRsIsFocused: () => ipcRenderer.invoke('rs:is-focused'), |
||||
sendRsFocus: () => ipcRenderer.send('rs:focus'), |
||||
sendRsSetIgnoreMouseEvents: (ignore: boolean, options: any) => { |
||||
ipcRenderer.send('rs:set-ignore-mouse-events', ignore, options); |
||||
}, |
||||
handleRsGetSizeClipWin: (callback: any) => ipcRenderer.on('rs:get-size-clip-win', callback), |
||||
handleRsGetShotScreen: (callback: any) => ipcRenderer.on('rs:get-shot-screen', callback), |
||||
handleRsGetEndRecord: (callback: any) => ipcRenderer.on('rs:get-end-record', callback), |
||||
|
||||
//csWin
|
||||
sendCsOpenWin: (search?: any) => ipcRenderer.send('cs:open-win', search), |
||||
sendCsCloseWin: () => ipcRenderer.send('cs:close-win'), |
||||
sendCsHideWin: () => ipcRenderer.send('cs:hide-win'), |
||||
sendCsMinimizeWin: () => ipcRenderer.send('cs:minimize-win'), |
||||
sendCsSetIgnoreMouseEvents: (ignore: boolean, options: any) => { |
||||
ipcRenderer.send('cs:set-ignore-mouse-events', ignore, options); |
||||
}, |
||||
invokeCsGetBounds: () => ipcRenderer.invoke('cs:get-bounds'), |
||||
handleCsSetIsPlay: (callback: any) => ipcRenderer.on('cs:set-isPlay', callback), |
||||
sendCsSetBounds: (bounds: any) => { |
||||
ipcRenderer.send('cs:set-bounds', bounds); |
||||
}, |
||||
|
||||
//rvWin
|
||||
sendRvCloseWin: () => ipcRenderer.send('rv:close-win'), |
||||
sendRvOpenWin: () => ipcRenderer.send('rv:open-win'), |
||||
sendRvDownloadRecord: (url: string) => ipcRenderer.send('rv:download-record', url), |
||||
|
||||
//ssWin
|
||||
sendSsOpenWin: () => ipcRenderer.send('ss:open-win'), |
||||
sendSsCloseWin: () => ipcRenderer.send('ss:close-win'), |
||||
sendSsStartWin: () => ipcRenderer.send('ss:start-win'), |
||||
sendSsShowWin: (callback) => ipcRenderer.on('ss:show-win', (e, img) => callback(img)), |
||||
sendSsHideWin: (callback) => ipcRenderer.on('ss:hide-win', () => callback()), |
||||
invokeSsGetShotScreenImg: () => ipcRenderer.invoke('ss:get-shot-screen-img'), |
||||
sendSsDownloadImg: (imgUrl: string) => ipcRenderer.send('ss:download-img', imgUrl), |
||||
sendSsSaveImg: (imgUrl: string) => ipcRenderer.send('ss:save-img', imgUrl), |
||||
sendSsOpenExternal: (tabUrl: string) => ipcRenderer.send('ss:open-external', tabUrl), |
||||
sendSsCopyImg: (imgUrl: string) => ipcRenderer.send('ss:copy-img', imgUrl), |
||||
|
||||
//viWin
|
||||
sendViCloseWin: () => ipcRenderer.send('vi:close-win'), |
||||
sendViOpenWin: (search?: string) => ipcRenderer.send('vi:open-win', search), |
||||
sendViMinimizeWin: () => ipcRenderer.send('vi:minimize-win'), |
||||
sendViMaximizeWin: () => ipcRenderer.send('vi:maximize-win'), |
||||
sendViUnmaximizeWin: () => ipcRenderer.send('vi:unmaximize-win'), |
||||
sendViAlwaysOnTopWin: (isTop: boolean) => ipcRenderer.send('vi:alwaysOnTop-win', isTop), |
||||
sendViOpenFile: (imgUrl: string) => ipcRenderer.send('vi:open-file', imgUrl), |
||||
invokeViSetIsAlwaysOnTop: () => ipcRenderer.invoke('vi:set-always-on-top'), |
||||
invokeViGetImgs: (imgUrl: string) => ipcRenderer.invoke('vi:get-imgs', imgUrl), |
||||
sendViDownloadImg: (img: string) => ipcRenderer.send('vi:download-img', img), |
||||
sendViSetHistoryImg: (img: string) => { |
||||
ipcRenderer.send('vi:set-historyImg', img); |
||||
}, |
||||
|
||||
//eiWin
|
||||
sendEiCloseWin: () => ipcRenderer.send('ei:close-win'), |
||||
sendEiOpenWin: (search?: string) => ipcRenderer.send('ei:open-win', search), |
||||
sendEiDownloadImg: (imgUrl?: string) => ipcRenderer.send('ei:download-img', imgUrl), |
||||
|
||||
//egWin
|
||||
sendEgCloseWin: () => ipcRenderer.send('eg:close-win'), |
||||
sendEgOpenWin: (search?: string) => ipcRenderer.send('eg:open-win', search), |
||||
|
||||
//vcWin
|
||||
sendVcCloseWin: () => ipcRenderer.send('vc:close-win'), |
||||
sendVcOpenWin: (search?: string) => ipcRenderer.send('vc:open-win', search), |
||||
|
||||
//caWin
|
||||
sendCaCloseWin: () => ipcRenderer.send('ca:close-win'), |
||||
sendCaOpenWin: () => ipcRenderer.send('ca:open-win'), |
||||
|
||||
//siWin
|
||||
sendSiCloseWin: () => ipcRenderer.send('si:close-win'), |
||||
sendSiOpenWin: () => ipcRenderer.send('si:open-win'), |
||||
|
||||
//vvWin
|
||||
sendVvOpenWin: (search?: string) => ipcRenderer.send('vv:open-win', search), |
||||
sendVvCloseWin: () => ipcRenderer.send('vv:close-win'), |
||||
invokeVvGetHistoryVideo: () => ipcRenderer.invoke('vv:get-historyVideo'), |
||||
sendVvSetHistoryVideo: (img: string) => ipcRenderer.send('vv:set-historyVideo', img), |
||||
|
||||
//vaWin
|
||||
sendVaOpenWin: (search?: any) => ipcRenderer.send('va:open-win', search), |
||||
invokeVaGetAudios: (audioUrl: any) => ipcRenderer.invoke('va:get-audios', audioUrl), |
||||
//seWin 设置
|
||||
sendSeOpenWin: () => ipcRenderer.send('se:open-win'), |
||||
invokeSeGetUser: () => ipcRenderer.invoke('se:get-user'), |
||||
invokeSeSetFilePath: (filePath: string) => ipcRenderer.invoke('se:set-filePath', filePath), |
||||
invokeSeGetFilePath: () => ipcRenderer.invoke('se:get-filePath'), |
||||
sendSeSetOpenAtLogin: (isOpen: boolean) => ipcRenderer.send('se:set-openAtLogin', isOpen), |
||||
sendSeSetLanguage: (lng: string) => ipcRenderer.send('se:set-language', lng), |
||||
sendSeSetShortcut: (data: string) => ipcRenderer.send('se:set-shortcut', data), |
||||
sendSeSetShortcuts: (data: string) => ipcRenderer.send('se:set-shortcuts', data), |
||||
invokeSeGetOpenAtLogin: () => ipcRenderer.invoke('se:get-openAtLogin'), |
||||
|
||||
//re 记录
|
||||
sendReOpenWin: () => ipcRenderer.send('re:open-win'), |
||||
|
||||
//pi 钉图
|
||||
sendPiSetSizeWin: (size: any) => ipcRenderer.send('pi:set-size-win', size), |
||||
sendPiOpenWin: (search?: any) => ipcRenderer.send('pi:open-win', search), |
||||
sendPiCloseWin: () => ipcRenderer.send('pi:close-win'), |
||||
sendPiMinimizeWin: () => ipcRenderer.send('pi:minimize-win'), |
||||
sendPiMaximizeWin: () => ipcRenderer.send('pi:maximize-win'), |
||||
sendPiUnmaximizeWin: () => ipcRenderer.send('pi:unmaximize-win'), |
||||
invokePiGetSizeWin: () => ipcRenderer.invoke('pi:get-size-win'), |
||||
|
||||
//pi 钉视频
|
||||
sendPvOpenWin: (search?: any) => ipcRenderer.send('pv:open-win', search), |
||||
sendPvCloseWin: () => ipcRenderer.send('pv:close-win'), |
||||
sendPvMinimizeWin: () => ipcRenderer.send('pv:minimize-win'), |
||||
sendPvMaximizeWin: () => ipcRenderer.send('pv:maximize-win'), |
||||
sendPvUnmaximizeWin: () => ipcRenderer.send('pv:unmaximize-win'), |
||||
|
||||
// rfs 全屏录屏
|
||||
sendRfsOpenWin: () => ipcRenderer.send('rfs:open-win'), |
||||
sendRfsCloseWin: () => ipcRenderer.send('rfs:close-win'), |
||||
|
||||
// Eu 自动更新
|
||||
handleEuUpdateCanAvailable: (callback: any) => |
||||
ipcRenderer.on('eu:update-can-available', callback), |
||||
handleEuUpdateeError: (callback: any) => ipcRenderer.on('eu:update-error', callback), |
||||
handleEuDownloadProgress: (callback: any) => ipcRenderer.on('eu:download-progress', callback), |
||||
handleEuUpdateDownloaded: (callback: any) => ipcRenderer.on('eu:update-downloaded', callback), |
||||
invokeEuQuitAndInstall: () => ipcRenderer.invoke('eu:quit-and-install'), |
||||
invokeEuStartDownload: () => ipcRenderer.invoke('eu:start-download'), |
||||
invokeEuCheckUpdate: () => ipcRenderer.invoke('eu:check-update'), |
||||
offEuUpdateCanAvailable: (callback: any) => ipcRenderer.on('eu:update-can-available', callback), |
||||
offEuUpdateeError: (callback: any) => ipcRenderer.on('eu:update-error', callback), |
||||
offEuDownloadProgress: (callback: any) => ipcRenderer.on('eu:download-progress', callback), |
||||
offEuUpdateDownloaded: (callback: any) => ipcRenderer.on('eu:update-downloaded', callback), |
||||
}); |
@ -0,0 +1,166 @@
@@ -0,0 +1,166 @@
|
||||
import './electronAPI'; |
||||
|
||||
function domReady(condition: DocumentReadyState[] = ['complete', 'interactive']) { |
||||
return new Promise((resolve) => { |
||||
if (condition.includes(document.readyState)) { |
||||
resolve(true); |
||||
} else { |
||||
document.addEventListener('readystatechange', () => { |
||||
if (condition.includes(document.readyState)) { |
||||
resolve(true); |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
const safeDOM = { |
||||
append(parent: HTMLElement, child: HTMLElement) { |
||||
if (!Array.from(parent.children).find((e) => e === child)) { |
||||
return parent.appendChild(child); |
||||
} |
||||
}, |
||||
remove(parent: HTMLElement, child: HTMLElement) { |
||||
if (Array.from(parent.children).find((e) => e === child)) { |
||||
return parent.removeChild(child); |
||||
} |
||||
}, |
||||
}; |
||||
|
||||
function useLoading() { |
||||
const className = `loaders-css__square-spin`; |
||||
const styleContent = ` |
||||
@keyframes square-spin { |
||||
25% { transform: perspective(100px) rotateX(180deg) rotateY(0); } |
||||
50% { transform: perspective(100px) rotateX(180deg) rotateY(180deg); } |
||||
75% { transform: perspective(100px) rotateX(0) rotateY(180deg); } |
||||
100% { transform: perspective(100px) rotateX(0) rotateY(0); } |
||||
} |
||||
.${className} > div { |
||||
animation-fill-mode: both; |
||||
width: 50px; |
||||
height: 50px; |
||||
background: #fff; |
||||
animation: square-spin 3s 0s cubic-bezier(0.09, 0.57, 0.49, 0.9) infinite; |
||||
} |
||||
.app-loading-wrap { |
||||
position: fixed; |
||||
top: 0; |
||||
left: 0; |
||||
width: 100vw; |
||||
height: 100vh; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
background: #282c34; |
||||
z-index: 9; |
||||
} |
||||
`;
|
||||
const oStyle = document.createElement('style'); |
||||
const oDiv = document.createElement('div'); |
||||
|
||||
oStyle.id = 'app-loading-style'; |
||||
oStyle.innerHTML = styleContent; |
||||
oDiv.className = 'app-loading-wrap'; |
||||
oDiv.innerHTML = `<div class="${className}"><div></div></div>`; |
||||
|
||||
return { |
||||
appendLoading() { |
||||
safeDOM.append(document.head, oStyle); |
||||
safeDOM.append(document.body, oDiv); |
||||
}, |
||||
removeLoading() { |
||||
safeDOM.remove(document.head, oStyle); |
||||
safeDOM.remove(document.body, oDiv); |
||||
}, |
||||
}; |
||||
} |
||||
|
||||
function useSkeleton() { |
||||
const styleContent = ` |
||||
.app-skeleton-wrap { |
||||
position: fixed; |
||||
top: 0; |
||||
left: 0; |
||||
width: 100vw; |
||||
height: 100vh; |
||||
background: #fff; |
||||
z-index: 9; |
||||
} |
||||
.app-skeleton-nav { |
||||
padding: 0 0 20px; |
||||
border-bottom: 1px solid #ccc; |
||||
} |
||||
.app-skeleton-content { |
||||
height: calc(100% - 60px); |
||||
} |
||||
.app-skeleton-footer { |
||||
background-color: rgba(204, 201, 201, 0.2); |
||||
height: 40px; |
||||
width: 100%; |
||||
} |
||||
.wavesurfer { |
||||
position: absolute; |
||||
bottom: 40px; |
||||
left: 0; |
||||
width: 100%; |
||||
height: 140px; |
||||
--c: rgb(118, 218, 255); |
||||
--w1: radial-gradient(100% 57% at top, #0000 100%, var(--c) 100.5%) no-repeat; |
||||
--w2: radial-gradient(100% 57% at bottom, var(--c) 100%, #0000 100.5%) no-repeat; |
||||
background: var(--w1), var(--w2), var(--w1), var(--w2); |
||||
background-position-x: |
||||
-200%, |
||||
-100%, |
||||
0%, |
||||
100%; |
||||
background-position-y: 100%; |
||||
background-size: 50.5% 100%; |
||||
} |
||||
|
||||
`;
|
||||
const oStyle = document.createElement('style'); |
||||
const oDiv = document.createElement('div'); |
||||
|
||||
oStyle.id = 'app-skeleton-style'; |
||||
oStyle.innerHTML = styleContent; |
||||
oDiv.className = 'app-skeleton-wrap'; |
||||
oDiv.innerHTML = `<div class="app-skeleton-nav"></div><div class="app-skeleton-content"></div><div class="wavesurfer"></div><div class="app-skeleton-footer"></div>`; |
||||
|
||||
return { |
||||
appendSkeleton() { |
||||
safeDOM.append(document.head, oStyle); |
||||
safeDOM.append(document.body, oDiv); |
||||
}, |
||||
removeSkeleton() { |
||||
safeDOM.remove(document.head, oStyle); |
||||
safeDOM.remove(document.body, oDiv); |
||||
}, |
||||
}; |
||||
} |
||||
|
||||
// ----------------------------------------------------------------------
|
||||
const { appendLoading, removeLoading } = useLoading(); |
||||
const { appendSkeleton, removeSkeleton } = useSkeleton(); |
||||
|
||||
if (location.pathname.includes('/index.html')) { |
||||
domReady().then(appendSkeleton); |
||||
setTimeout(removeSkeleton, 4999); |
||||
} else { |
||||
if ( |
||||
!location.pathname.includes('/shotScreen.html') && |
||||
!location.pathname.includes('/clipScreen.html') && |
||||
!location.pathname.includes('/canvas.html') && |
||||
!location.pathname.includes('/recorderScreen.html') |
||||
) { |
||||
domReady().then(appendLoading); |
||||
|
||||
setTimeout(removeLoading, 4999); |
||||
} |
||||
} |
||||
|
||||
window.onmessage = (ev) => { |
||||
if (ev.data.payload === 'removeLoading') { |
||||
location.pathname.includes('/index.html') ? removeSkeleton() : removeLoading(); |
||||
} |
||||
}; |
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
import { BrowserWindow } from 'electron'; |
||||
import { ICON, WEB_URL, WIN_CONFIG, preload, url } from '../main/constant'; |
||||
|
||||
let canvasWin: BrowserWindow | null = null; |
||||
|
||||
function createCanvasWin(): BrowserWindow { |
||||
canvasWin = new BrowserWindow({ |
||||
title: 'pear-rec 画布', |
||||
icon: ICON, |
||||
height: WIN_CONFIG.canvas.height, |
||||
width: WIN_CONFIG.canvas.width, |
||||
autoHideMenuBar: WIN_CONFIG.canvas.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
|
||||
// canvasWin.webContents.openDevTools();
|
||||
if (url) { |
||||
canvasWin.loadURL(WEB_URL + `canvas.html`); |
||||
} else { |
||||
canvasWin.loadFile(WIN_CONFIG.canvas.html); |
||||
} |
||||
|
||||
canvasWin.once('ready-to-show', async () => { |
||||
canvasWin?.show(); |
||||
}); |
||||
|
||||
return canvasWin; |
||||
} |
||||
|
||||
function openCanvasWin() { |
||||
if (!canvasWin || canvasWin?.isDestroyed()) { |
||||
canvasWin = createCanvasWin(); |
||||
} |
||||
canvasWin.show(); |
||||
} |
||||
|
||||
function closeCanvasWin() { |
||||
canvasWin?.close(); |
||||
} |
||||
|
||||
export { closeCanvasWin, createCanvasWin, openCanvasWin }; |
@ -0,0 +1,124 @@
@@ -0,0 +1,124 @@
|
||||
import { BrowserWindow } from 'electron'; |
||||
import { ICON, WEB_URL, WIN_CONFIG, preload, url } from '../main/constant'; |
||||
import { |
||||
minimizeRecorderScreenWin, |
||||
openRecorderScreenWin, |
||||
setBoundsRecorderScreenWin, |
||||
showRecorderScreenWin, |
||||
} from './recorderScreenWin'; |
||||
|
||||
let clipScreenWin: BrowserWindow | null = null; |
||||
|
||||
function createClipScreenWin(): BrowserWindow { |
||||
clipScreenWin = new BrowserWindow({ |
||||
title: 'pear-rec', |
||||
icon: ICON, |
||||
autoHideMenuBar: WIN_CONFIG.clipScreen.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
frame: WIN_CONFIG.clipScreen.frame, // 无边框窗口
|
||||
resizable: WIN_CONFIG.clipScreen.resizable, // 窗口大小是否可调整
|
||||
transparent: WIN_CONFIG.clipScreen.transparent, // 使窗口透明
|
||||
fullscreenable: WIN_CONFIG.clipScreen.fullscreenable, // 窗口是否可以进入全屏状态
|
||||
alwaysOnTop: WIN_CONFIG.clipScreen.alwaysOnTop, // 窗口是否永远在别的窗口的上面
|
||||
skipTaskbar: WIN_CONFIG.clipScreen.skipTaskbar, |
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
|
||||
// clipScreenWin.webContents.openDevTools();
|
||||
if (url) { |
||||
clipScreenWin.loadURL(WEB_URL + 'clipScreen.html'); |
||||
} else { |
||||
clipScreenWin.loadFile(WIN_CONFIG.clipScreen.html); |
||||
} |
||||
|
||||
// clipScreenWin.on('resize', () => {
|
||||
// const clipScreenWinBounds = getBoundsClipScreenWin();
|
||||
// setBoundsRecorderScreenWin(clipScreenWinBounds);
|
||||
// });
|
||||
|
||||
// clipScreenWin.on('move', () => {
|
||||
// const clipScreenWinBounds = getBoundsClipScreenWin();
|
||||
// setBoundsRecorderScreenWin(clipScreenWinBounds);
|
||||
// });
|
||||
|
||||
// clipScreenWin.on('restore', () => {
|
||||
// showRecorderScreenWin();
|
||||
// });
|
||||
|
||||
// clipScreenWin.on('minimize', () => {
|
||||
// hideRecorderScreenWin();
|
||||
// });
|
||||
|
||||
return clipScreenWin; |
||||
} |
||||
|
||||
function closeClipScreenWin() { |
||||
clipScreenWin?.isDestroyed() || clipScreenWin?.close(); |
||||
clipScreenWin = null; |
||||
} |
||||
|
||||
function showClipScreenWin() { |
||||
clipScreenWin?.show(); |
||||
} |
||||
|
||||
function openClipScreenWin(search?: any) { |
||||
if (!clipScreenWin || clipScreenWin?.isDestroyed()) { |
||||
clipScreenWin = createClipScreenWin(); |
||||
} |
||||
|
||||
clipScreenWin?.show(); |
||||
openRecorderScreenWin(search); |
||||
} |
||||
|
||||
function getBoundsClipScreenWin() { |
||||
return clipScreenWin?.getBounds(); |
||||
} |
||||
|
||||
function hideClipScreenWin() { |
||||
clipScreenWin?.hide(); |
||||
} |
||||
|
||||
function setAlwaysOnTopClipScreenWin(isAlwaysOnTop: boolean) { |
||||
clipScreenWin?.setAlwaysOnTop(isAlwaysOnTop); |
||||
} |
||||
|
||||
function setMovableClipScreenWin(movable: boolean) { |
||||
clipScreenWin?.setMovable(movable); |
||||
} |
||||
|
||||
function setResizableClipScreenWin(resizable: boolean) { |
||||
clipScreenWin?.setResizable(resizable); |
||||
} |
||||
|
||||
function minimizeClipScreenWin() { |
||||
clipScreenWin?.minimize(); |
||||
minimizeRecorderScreenWin(); |
||||
} |
||||
|
||||
function setIgnoreMouseEventsClipScreenWin(event: any, ignore: boolean, options?: any) { |
||||
clipScreenWin?.setIgnoreMouseEvents(ignore, options); |
||||
} |
||||
|
||||
function setIsPlayClipScreenWin(isPlay: boolean) { |
||||
clipScreenWin?.webContents.send('cs:set-isPlay', isPlay); |
||||
} |
||||
|
||||
function setBoundsClipScreenWin(bounds: any) { |
||||
clipScreenWin?.setBounds({ ...bounds }); |
||||
} |
||||
|
||||
export { |
||||
closeClipScreenWin, |
||||
getBoundsClipScreenWin, |
||||
hideClipScreenWin, |
||||
minimizeClipScreenWin, |
||||
openClipScreenWin, |
||||
setAlwaysOnTopClipScreenWin, |
||||
setBoundsClipScreenWin, |
||||
setIgnoreMouseEventsClipScreenWin, |
||||
setIsPlayClipScreenWin, |
||||
setMovableClipScreenWin, |
||||
setResizableClipScreenWin, |
||||
showClipScreenWin, |
||||
}; |
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
import { BrowserWindow } from 'electron'; |
||||
import { ICON, WEB_URL, WIN_CONFIG, preload, url } from '../main/constant'; |
||||
|
||||
let editGifWin: BrowserWindow | null = null; |
||||
|
||||
function createEditGifWin(search?: any): BrowserWindow { |
||||
editGifWin = new BrowserWindow({ |
||||
title: 'pear-rec 动图编辑', |
||||
icon: ICON, |
||||
height: WIN_CONFIG.editGif.height, |
||||
width: WIN_CONFIG.editGif.width, |
||||
autoHideMenuBar: WIN_CONFIG.editGif.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
|
||||
const videoUrl = search?.videoUrl || ''; |
||||
const filePath = search?.filePath || ''; |
||||
const imgUrl = search?.imgUrl || ''; |
||||
const recordId = search?.recordId || ''; |
||||
|
||||
// editGifWin.webContents.openDevTools();
|
||||
if (url) { |
||||
editGifWin.loadURL( |
||||
WEB_URL + |
||||
`editGif.html?${filePath ? 'filePath=' + filePath : ''}${imgUrl ? 'imgUrl=' + imgUrl : ''}${ |
||||
recordId ? 'recordId=' + recordId : '' |
||||
}${videoUrl ? 'videoUrl=' + videoUrl : ''}`,
|
||||
); |
||||
} else { |
||||
editGifWin.loadFile(WIN_CONFIG.editGif.html, { |
||||
search: `?${filePath ? 'filePath=' + filePath : ''}${imgUrl ? 'imgUrl=' + imgUrl : ''}${ |
||||
recordId ? 'recordId=' + recordId : '' |
||||
}${videoUrl ? 'videoUrl=' + videoUrl : ''}`,
|
||||
}); |
||||
} |
||||
|
||||
return editGifWin; |
||||
} |
||||
|
||||
function openEditGifWin(search?: any) { |
||||
if (!editGifWin || editGifWin?.isDestroyed()) { |
||||
editGifWin = createEditGifWin(search); |
||||
} |
||||
editGifWin.show(); |
||||
} |
||||
|
||||
function closeEditGifWin() { |
||||
editGifWin?.close(); |
||||
} |
||||
|
||||
export { closeEditGifWin, createEditGifWin, openEditGifWin }; |
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
import { BrowserWindow, dialog, nativeImage } from 'electron'; |
||||
import { writeFile } from 'node:fs'; |
||||
import { ICON, WEB_URL, WIN_CONFIG, preload, url } from '../main/constant'; |
||||
|
||||
let editImageWin: BrowserWindow | null = null; |
||||
|
||||
function createEditImageWin(search?: any): BrowserWindow { |
||||
editImageWin = new BrowserWindow({ |
||||
title: 'pear-rec 图片编辑', |
||||
icon: ICON, |
||||
height: WIN_CONFIG.editImage.height, |
||||
width: WIN_CONFIG.editImage.width, |
||||
autoHideMenuBar: WIN_CONFIG.editImage.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
|
||||
const imgUrl = search?.imgUrl || ''; |
||||
|
||||
// editImageWin.webContents.openDevTools();
|
||||
if (url) { |
||||
editImageWin.loadURL(WEB_URL + `editImage.html?imgUrl=${imgUrl}`); |
||||
} else { |
||||
editImageWin.loadFile(WIN_CONFIG.editImage.html, { |
||||
search: `?imgUrl=${imgUrl}`, |
||||
}); |
||||
} |
||||
|
||||
editImageWin.once('ready-to-show', async () => { |
||||
editImageWin?.show(); |
||||
}); |
||||
|
||||
return editImageWin; |
||||
} |
||||
|
||||
function openEditImageWin(search?: any) { |
||||
if (!editImageWin || editImageWin?.isDestroyed()) { |
||||
editImageWin = createEditImageWin(search); |
||||
} |
||||
editImageWin.show(); |
||||
} |
||||
|
||||
function closeEditImageWin() { |
||||
editImageWin?.close(); |
||||
} |
||||
|
||||
async function downloadImg(imgUrl: any) { |
||||
let defaultPath = `pear-rec_${+new Date()}.png`; |
||||
let res = await dialog.showSaveDialog({ |
||||
defaultPath: defaultPath, |
||||
filters: [{ name: 'Images', extensions: ['png', 'jpg', 'gif'] }], |
||||
}); |
||||
if (!res.canceled) { |
||||
const imgData = nativeImage.createFromDataURL(imgUrl).toPNG(); |
||||
writeFile(res.filePath, imgData, (err) => { |
||||
if (err) { |
||||
console.error(err); |
||||
} else { |
||||
console.log(`${defaultPath}:图片保存成功`); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
|
||||
export { closeEditImageWin, createEditImageWin, downloadImg, openEditImageWin }; |
@ -0,0 +1,100 @@
@@ -0,0 +1,100 @@
|
||||
import { BrowserWindow, app, shell } from 'electron'; |
||||
import { ICON, WEB_URL, WIN_CONFIG, preload, url } from '../main/constant'; |
||||
|
||||
let mainWin: BrowserWindow | null = null; |
||||
|
||||
const createMainWin = (): BrowserWindow => { |
||||
mainWin = new BrowserWindow({ |
||||
title: 'pear-rec', |
||||
icon: ICON, |
||||
width: WIN_CONFIG.main.width, // 宽度(px)
|
||||
height: WIN_CONFIG.main.height, // 高度(px)
|
||||
autoHideMenuBar: WIN_CONFIG.main.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
maximizable: WIN_CONFIG.main.maximizable, |
||||
resizable: WIN_CONFIG.main.resizable, // gnome下为false时无法全屏
|
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
|
||||
if (url) { |
||||
mainWin.loadURL(WEB_URL + 'index.html'); |
||||
} else { |
||||
mainWin.loadFile(WIN_CONFIG.main.html); |
||||
} |
||||
// mainWin.webContents.openDevTools();
|
||||
|
||||
// Make all links open with the browser, not with the application
|
||||
mainWin.webContents.setWindowOpenHandler(({ url }) => { |
||||
if (url.startsWith('https:')) shell.openExternal(url); |
||||
return { action: 'deny' }; |
||||
}); |
||||
|
||||
// mainWin.onbeforeunload = (e) => {
|
||||
// console.log('I do not want to be closed');
|
||||
|
||||
// // 与通常的浏览器不同,会提示给用户一个消息框,
|
||||
// //返回非空值将默认取消关闭
|
||||
// //建议使用对话框 API 让用户确认关闭应用程序.
|
||||
// e.returnValue = false;
|
||||
// };
|
||||
|
||||
// window.addEventListener('beforeunload', (e) => {
|
||||
// e.returnValue = false;
|
||||
// });
|
||||
|
||||
return mainWin; |
||||
}; |
||||
|
||||
function closeMainWin() { |
||||
if (mainWin && !mainWin?.isDestroyed()) { |
||||
mainWin?.close(); |
||||
} |
||||
mainWin = null; |
||||
} |
||||
|
||||
function openMainWin() { |
||||
if (!mainWin || mainWin?.isDestroyed()) { |
||||
mainWin = createMainWin(); |
||||
} |
||||
mainWin?.show(); |
||||
} |
||||
|
||||
function hideMainWin() { |
||||
mainWin!.hide(); |
||||
} |
||||
|
||||
function minimizeMainWin() { |
||||
mainWin!.minimize(); |
||||
} |
||||
|
||||
function focusMainWin() { |
||||
if (!mainWin || mainWin?.isDestroyed()) { |
||||
mainWin = createMainWin(); |
||||
} else { |
||||
// Focus on the main window if the user tried to open another
|
||||
if (mainWin.isMinimized()) mainWin.restore(); |
||||
if (!mainWin.isVisible()) mainWin.show(); |
||||
mainWin.focus(); |
||||
} |
||||
} |
||||
|
||||
function sendEuUpdateCanAvailable(arg, update) { |
||||
if (mainWin && !mainWin?.isDestroyed()) { |
||||
mainWin.webContents.send('eu:update-can-available', { |
||||
update: update, |
||||
version: app.getVersion(), |
||||
newVersion: arg?.version, |
||||
}); |
||||
} |
||||
} |
||||
|
||||
export { |
||||
closeMainWin, |
||||
createMainWin, |
||||
focusMainWin, |
||||
hideMainWin, |
||||
minimizeMainWin, |
||||
openMainWin, |
||||
sendEuUpdateCanAvailable, |
||||
}; |
@ -0,0 +1,93 @@
@@ -0,0 +1,93 @@
|
||||
import { BrowserWindow } from 'electron'; |
||||
import { ICON, preload, url, WEB_URL, WIN_CONFIG } from '../main/constant'; |
||||
|
||||
let pinImageWin: BrowserWindow | null = null; |
||||
|
||||
function createPinImageWin(search?: any): BrowserWindow { |
||||
const imgUrl = search?.imgUrl || ''; |
||||
const recordId = search?.recordId || ''; |
||||
const width = search?.width || 800; |
||||
const height = search?.height || 600; |
||||
|
||||
pinImageWin = new BrowserWindow({ |
||||
title: 'pear-rec 图片', |
||||
icon: ICON, |
||||
width: width, |
||||
height: height, |
||||
frame: WIN_CONFIG.pinImage.frame, // 无边框窗口
|
||||
transparent: WIN_CONFIG.pinImage.transparent, // 使窗口透明
|
||||
fullscreenable: WIN_CONFIG.pinImage.fullscreenable, // 窗口是否可以进入全屏状态
|
||||
alwaysOnTop: WIN_CONFIG.pinImage.alwaysOnTop, // 窗口是否永远在别的窗口的上面
|
||||
autoHideMenuBar: WIN_CONFIG.pinImage.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
|
||||
// pinImageWin.webContents.openDevTools();
|
||||
if (url) { |
||||
pinImageWin.loadURL( |
||||
WEB_URL + |
||||
`pinImage.html?${imgUrl ? 'imgUrl=' + imgUrl : ''}${ |
||||
recordId ? 'recordId=' + recordId : '' |
||||
}`,
|
||||
); |
||||
} else { |
||||
pinImageWin.loadFile(WIN_CONFIG.pinImage.html, { |
||||
search: `?${imgUrl ? 'imgUrl=' + imgUrl : ''}${recordId ? 'recordId=' + recordId : ''}`, |
||||
}); |
||||
} |
||||
|
||||
return pinImageWin; |
||||
} |
||||
|
||||
function setSizePinImageWin(size: any) { |
||||
pinImageWin.setBounds(size); |
||||
} |
||||
|
||||
function closePinImageWin() { |
||||
pinImageWin?.isDestroyed() || pinImageWin?.close(); |
||||
pinImageWin = null; |
||||
} |
||||
|
||||
function openPinImageWin(search?: any) { |
||||
pinImageWin = createPinImageWin(search); |
||||
pinImageWin?.show(); |
||||
} |
||||
|
||||
function showPinImageWin() { |
||||
pinImageWin?.show(); |
||||
} |
||||
|
||||
function hidePinImageWin() { |
||||
pinImageWin?.hide(); |
||||
} |
||||
|
||||
function minimizePinImageWin() { |
||||
pinImageWin?.minimize(); |
||||
} |
||||
|
||||
function maximizePinImageWin() { |
||||
pinImageWin?.maximize(); |
||||
} |
||||
|
||||
function unmaximizePinImageWin() { |
||||
pinImageWin?.unmaximize(); |
||||
} |
||||
|
||||
function getSizePinImageWin() { |
||||
return pinImageWin.getBounds(); |
||||
} |
||||
|
||||
export { |
||||
setSizePinImageWin, |
||||
closePinImageWin, |
||||
createPinImageWin, |
||||
hidePinImageWin, |
||||
maximizePinImageWin, |
||||
minimizePinImageWin, |
||||
openPinImageWin, |
||||
showPinImageWin, |
||||
unmaximizePinImageWin, |
||||
getSizePinImageWin, |
||||
}; |
@ -0,0 +1,73 @@
@@ -0,0 +1,73 @@
|
||||
import { BrowserWindow } from 'electron'; |
||||
import { ICON, preload, url, WEB_URL, WIN_CONFIG } from '../main/constant'; |
||||
|
||||
let pinVideoWin: BrowserWindow | null = null; |
||||
|
||||
function createPinVideoWin(search?: any): BrowserWindow { |
||||
pinVideoWin = new BrowserWindow({ |
||||
title: 'pear-rec 视频', |
||||
icon: ICON, |
||||
height: WIN_CONFIG.pinVideo.height, |
||||
width: WIN_CONFIG.pinVideo.width, |
||||
frame: WIN_CONFIG.pinVideo.frame, // 无边框窗口
|
||||
resizable: WIN_CONFIG.pinVideo.resizable, // 窗口大小是否可调整
|
||||
transparent: WIN_CONFIG.pinVideo.transparent, // 使窗口透明
|
||||
fullscreenable: WIN_CONFIG.pinVideo.fullscreenable, // 窗口是否可以进入全屏状态
|
||||
alwaysOnTop: WIN_CONFIG.pinVideo.alwaysOnTop, // 窗口是否永远在别的窗口的上面
|
||||
autoHideMenuBar: WIN_CONFIG.pinVideo.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
|
||||
// pinVideoWin.webContents.openDevTools();
|
||||
if (url) { |
||||
pinVideoWin.loadURL(WEB_URL + `pinVideo.html`); |
||||
} else { |
||||
pinVideoWin.loadFile(WIN_CONFIG.pinVideo.html); |
||||
} |
||||
|
||||
return pinVideoWin; |
||||
} |
||||
|
||||
// 打开关闭录屏窗口
|
||||
function closePinVideoWin() { |
||||
pinVideoWin?.isDestroyed() || pinVideoWin?.close(); |
||||
pinVideoWin = null; |
||||
} |
||||
|
||||
function openPinVideoWin(search?: any) { |
||||
pinVideoWin = createPinVideoWin(search); |
||||
pinVideoWin?.show(); |
||||
} |
||||
|
||||
function showPinVideoWin() { |
||||
pinVideoWin?.show(); |
||||
} |
||||
|
||||
function hidePinVideoWin() { |
||||
pinVideoWin?.hide(); |
||||
} |
||||
|
||||
function minimizePinVideoWin() { |
||||
pinVideoWin?.minimize(); |
||||
} |
||||
|
||||
function maximizePinVideoWin() { |
||||
pinVideoWin?.maximize(); |
||||
} |
||||
|
||||
function unmaximizePinVideoWin() { |
||||
pinVideoWin?.unmaximize(); |
||||
} |
||||
|
||||
export { |
||||
closePinVideoWin, |
||||
createPinVideoWin, |
||||
hidePinVideoWin, |
||||
maximizePinVideoWin, |
||||
minimizePinVideoWin, |
||||
openPinVideoWin, |
||||
showPinVideoWin, |
||||
unmaximizePinVideoWin, |
||||
}; |
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
import { BrowserWindow } from 'electron'; |
||||
import { ICON, preload, url, WEB_URL, WIN_CONFIG } from '../main/constant'; |
||||
|
||||
let recorderAudioWin: BrowserWindow | null = null; |
||||
|
||||
function createRecorderAudioWin(): BrowserWindow { |
||||
recorderAudioWin = new BrowserWindow({ |
||||
title: 'pear-rec 录音', |
||||
icon: ICON, |
||||
width: WIN_CONFIG.recorderAudio.width, // 宽度(px), 默认值为 800
|
||||
height: WIN_CONFIG.recorderAudio.height, // 高度(px), 默认值为 600
|
||||
autoHideMenuBar: WIN_CONFIG.recorderAudio.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
|
||||
// recorderAudioWin.webContents.openDevTools();
|
||||
if (url) { |
||||
recorderAudioWin.loadURL(WEB_URL + 'recorderAudio.html'); |
||||
} else { |
||||
recorderAudioWin.loadFile(WIN_CONFIG.recorderAudio.html); |
||||
} |
||||
|
||||
return recorderAudioWin; |
||||
} |
||||
|
||||
// 打开关闭录屏窗口
|
||||
function closeRecorderAudioWin() { |
||||
recorderAudioWin?.isDestroyed() || recorderAudioWin?.close(); |
||||
recorderAudioWin = null; |
||||
} |
||||
|
||||
function openRecorderAudioWin() { |
||||
if (!recorderAudioWin || recorderAudioWin?.isDestroyed()) { |
||||
recorderAudioWin = createRecorderAudioWin(); |
||||
} |
||||
recorderAudioWin?.show(); |
||||
} |
||||
|
||||
function hideRecorderAudioWin() { |
||||
recorderAudioWin?.hide(); |
||||
} |
||||
|
||||
function minimizeRecorderAudioWin() { |
||||
recorderAudioWin?.minimize(); |
||||
} |
||||
|
||||
function downloadURLRecorderAudioWin(downloadUrl: string) { |
||||
// recorderAudioWin?.webContents.downloadURL(downloadUrl);
|
||||
// downloadSet.add(downloadUrl);
|
||||
} |
||||
|
||||
function setSizeRecorderAudioWin(width: number, height: number) { |
||||
recorderAudioWin?.setResizable(true); |
||||
recorderAudioWin?.setSize(width, height); |
||||
recorderAudioWin?.setResizable(false); |
||||
} |
||||
|
||||
export { |
||||
closeRecorderAudioWin, |
||||
createRecorderAudioWin, |
||||
downloadURLRecorderAudioWin, |
||||
hideRecorderAudioWin, |
||||
minimizeRecorderAudioWin, |
||||
openRecorderAudioWin, |
||||
setSizeRecorderAudioWin, |
||||
}; |
@ -0,0 +1,77 @@
@@ -0,0 +1,77 @@
|
||||
import { BrowserWindow } from 'electron'; |
||||
import { ICON, WEB_URL, WIN_CONFIG, preload, url } from '../main/constant'; |
||||
|
||||
let recorderFullScreenWin: BrowserWindow | null = null; |
||||
|
||||
function createRecorderFullScreenWin(): BrowserWindow { |
||||
recorderFullScreenWin = new BrowserWindow({ |
||||
title: 'pear-rec 录屏', |
||||
icon: ICON, |
||||
height: WIN_CONFIG.recorderFullScreen.height, |
||||
width: WIN_CONFIG.recorderFullScreen.width, |
||||
center: WIN_CONFIG.recorderFullScreen.center, |
||||
transparent: WIN_CONFIG.recorderFullScreen.transparent, // 使窗口透明
|
||||
autoHideMenuBar: WIN_CONFIG.recorderFullScreen.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
frame: WIN_CONFIG.recorderFullScreen.frame, // 无边框窗口
|
||||
hasShadow: WIN_CONFIG.recorderFullScreen.hasShadow, // 窗口是否有阴影
|
||||
fullscreenable: WIN_CONFIG.recorderFullScreen.fullscreenable, // 窗口是否可以进入全屏状态
|
||||
alwaysOnTop: WIN_CONFIG.recorderFullScreen.alwaysOnTop, // 窗口是否永远在别的窗口的上面
|
||||
skipTaskbar: WIN_CONFIG.recorderFullScreen.skipTaskbar, |
||||
resizable: WIN_CONFIG.recorderFullScreen.resizable, |
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
recorderFullScreenWin?.setBounds({ y: 0 }); |
||||
if (url) { |
||||
recorderFullScreenWin.loadURL(WEB_URL + `recorderFullScreen.html`); |
||||
} else { |
||||
recorderFullScreenWin.loadFile(WIN_CONFIG.recorderFullScreen.html); |
||||
} |
||||
// recorderFullScreenWin.webContents.openDevTools();
|
||||
|
||||
return recorderFullScreenWin; |
||||
} |
||||
|
||||
function closeRecorderFullScreenWin() { |
||||
recorderFullScreenWin?.isDestroyed() || recorderFullScreenWin?.close(); |
||||
recorderFullScreenWin = null; |
||||
} |
||||
|
||||
function openRecorderFullScreenWin() { |
||||
if (!recorderFullScreenWin || recorderFullScreenWin?.isDestroyed()) { |
||||
recorderFullScreenWin = createRecorderFullScreenWin(); |
||||
} |
||||
recorderFullScreenWin?.show(); |
||||
} |
||||
|
||||
function hideRecorderFullScreenWin() { |
||||
recorderFullScreenWin?.hide(); |
||||
} |
||||
|
||||
function showRecorderFullScreenWin() { |
||||
recorderFullScreenWin?.show(); |
||||
} |
||||
|
||||
function minimizeRecorderFullScreenWin() { |
||||
recorderFullScreenWin?.minimize(); |
||||
} |
||||
|
||||
function setMovableRecorderFullScreenWin(movable: boolean) { |
||||
recorderFullScreenWin?.setMovable(movable); |
||||
} |
||||
|
||||
function setAlwaysOnTopRecorderFullScreenWin(isAlwaysOnTop: boolean) { |
||||
recorderFullScreenWin?.setAlwaysOnTop(isAlwaysOnTop); |
||||
} |
||||
|
||||
export { |
||||
closeRecorderFullScreenWin, |
||||
createRecorderFullScreenWin, |
||||
hideRecorderFullScreenWin, |
||||
minimizeRecorderFullScreenWin, |
||||
openRecorderFullScreenWin, |
||||
setAlwaysOnTopRecorderFullScreenWin, |
||||
setMovableRecorderFullScreenWin, |
||||
showRecorderFullScreenWin, |
||||
}; |
@ -0,0 +1,164 @@
@@ -0,0 +1,164 @@
|
||||
import { BrowserWindow, Rectangle, screen } from 'electron'; |
||||
import { ICON, WEB_URL, WIN_CONFIG, preload, url } from '../main/constant'; |
||||
import { |
||||
closeClipScreenWin, |
||||
getBoundsClipScreenWin, |
||||
showClipScreenWin, |
||||
minimizeClipScreenWin, |
||||
} from './clipScreenWin'; |
||||
|
||||
let recorderScreenWin: BrowserWindow | null = null; |
||||
|
||||
function createRecorderScreenWin(search?: any): BrowserWindow { |
||||
const { x, y, width, height } = getBoundsClipScreenWin() as Rectangle; |
||||
// let recorderScreenWinX = x + (width - WIN_CONFIG.recorderScreen.width) / 2;
|
||||
let recorderScreenWinX = x + width + 4 - WIN_CONFIG.recorderScreen.width; |
||||
let recorderScreenWinY = y + height; |
||||
|
||||
recorderScreenWin = new BrowserWindow({ |
||||
title: 'pear-rec 录屏', |
||||
icon: ICON, |
||||
x: recorderScreenWinX, |
||||
y: recorderScreenWinY, |
||||
width: WIN_CONFIG.recorderScreen.width, |
||||
height: WIN_CONFIG.recorderScreen.height, |
||||
autoHideMenuBar: WIN_CONFIG.recorderScreen.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
maximizable: WIN_CONFIG.recorderScreen.maximizable, |
||||
hasShadow: WIN_CONFIG.recorderScreen.hasShadow, // 窗口是否有阴影
|
||||
fullscreenable: WIN_CONFIG.recorderScreen.fullscreenable, // 窗口是否可以进入全屏状态
|
||||
alwaysOnTop: WIN_CONFIG.recorderScreen.alwaysOnTop, // 窗口是否永远在别的窗口的上面
|
||||
// skipTaskbar: WIN_CONFIG.recorderScreen.skipTaskbar,
|
||||
// resizable: WIN_CONFIG.recorderScreen.resizable,
|
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
// recorderScreenWin.webContents.openDevTools();
|
||||
if (url) { |
||||
recorderScreenWin.loadURL(WEB_URL + `recorderScreen.html?type=${search?.type || ''}`); |
||||
} else { |
||||
recorderScreenWin.loadFile(WIN_CONFIG.recorderScreen.html, { |
||||
search: `?type=${search?.type || ''}`, |
||||
}); |
||||
} |
||||
|
||||
// recorderScreenWin.on('move', () => {
|
||||
// const recorderScreenWinBounds = getBoundsRecorderScreenWin() as Rectangle;
|
||||
// const clipScreenWinBounds = getBoundsClipScreenWin() as Rectangle;
|
||||
// setBoundsClipScreenWin({
|
||||
// x: recorderScreenWinBounds.x,
|
||||
// y: recorderScreenWinBounds.y - clipScreenWinBounds.height,
|
||||
// width: clipScreenWinBounds.width,
|
||||
// height: clipScreenWinBounds.height,
|
||||
// });
|
||||
// });
|
||||
|
||||
recorderScreenWin.on('restore', () => { |
||||
showClipScreenWin(); |
||||
}); |
||||
|
||||
recorderScreenWin.on('minimize', () => { |
||||
minimizeClipScreenWin(); |
||||
}); |
||||
|
||||
recorderScreenWin.on('close', () => { |
||||
closeClipScreenWin(); |
||||
}); |
||||
|
||||
return recorderScreenWin; |
||||
} |
||||
|
||||
// 打开关闭录屏窗口
|
||||
function closeRecorderScreenWin() { |
||||
recorderScreenWin?.isDestroyed() || recorderScreenWin?.close(); |
||||
recorderScreenWin = null; |
||||
} |
||||
|
||||
function openRecorderScreenWin(search?: any) { |
||||
if (!recorderScreenWin || recorderScreenWin?.isDestroyed()) { |
||||
recorderScreenWin = createRecorderScreenWin(search); |
||||
} |
||||
recorderScreenWin?.show(); |
||||
} |
||||
|
||||
function hideRecorderScreenWin() { |
||||
recorderScreenWin?.hide(); |
||||
} |
||||
|
||||
function showRecorderScreenWin() { |
||||
recorderScreenWin?.show(); |
||||
} |
||||
|
||||
function minimizeRecorderScreenWin() { |
||||
recorderScreenWin?.minimize(); |
||||
} |
||||
|
||||
function setSizeRecorderScreenWin(width: number, height: number) { |
||||
// recorderScreenWin?.setResizable(true);
|
||||
// recorderScreenWin?.setSize(width, height);
|
||||
// recorderScreenWin?.setResizable(false);
|
||||
} |
||||
|
||||
function getBoundsRecorderScreenWin() { |
||||
return recorderScreenWin?.getBounds(); |
||||
} |
||||
|
||||
function setMovableRecorderScreenWin(movable: boolean) { |
||||
recorderScreenWin?.setMovable(movable); |
||||
} |
||||
|
||||
function setResizableRecorderScreenWin(resizable: boolean) { |
||||
recorderScreenWin?.setResizable(resizable); |
||||
} |
||||
|
||||
function setAlwaysOnTopRecorderScreenWin(isAlwaysOnTop: boolean) { |
||||
recorderScreenWin?.setAlwaysOnTop(isAlwaysOnTop); |
||||
} |
||||
|
||||
function isFocusedRecorderScreenWin() { |
||||
return recorderScreenWin?.isFocused(); |
||||
} |
||||
|
||||
function focusRecorderScreenWin() { |
||||
recorderScreenWin?.focus(); |
||||
} |
||||
|
||||
function getCursorScreenPointRecorderScreenWin() { |
||||
return screen.getCursorScreenPoint(); |
||||
} |
||||
|
||||
function setBoundsRecorderScreenWin(clipScreenWinBounds: any) { |
||||
let { x, y, width, height } = clipScreenWinBounds; |
||||
let recorderScreenWinX = x; |
||||
let recorderScreenWinY = y + height; |
||||
recorderScreenWin?.setBounds({ |
||||
x: recorderScreenWinX, |
||||
y: recorderScreenWinY, |
||||
width: width, |
||||
}); |
||||
recorderScreenWin?.webContents.send('rs:get-size-clip-win', clipScreenWinBounds); |
||||
} |
||||
|
||||
function setIgnoreMouseEventsRecorderScreenWin(event: any, ignore: boolean, options: any) { |
||||
const win = BrowserWindow.fromWebContents(event.sender); |
||||
win?.setIgnoreMouseEvents(ignore, options); |
||||
} |
||||
|
||||
export { |
||||
closeRecorderScreenWin, |
||||
createRecorderScreenWin, |
||||
focusRecorderScreenWin, |
||||
getBoundsRecorderScreenWin, |
||||
getCursorScreenPointRecorderScreenWin, |
||||
hideRecorderScreenWin, |
||||
isFocusedRecorderScreenWin, |
||||
minimizeRecorderScreenWin, |
||||
openRecorderScreenWin, |
||||
setAlwaysOnTopRecorderScreenWin, |
||||
setBoundsRecorderScreenWin, |
||||
setIgnoreMouseEventsRecorderScreenWin, |
||||
setMovableRecorderScreenWin, |
||||
setResizableRecorderScreenWin, |
||||
setSizeRecorderScreenWin, |
||||
showRecorderScreenWin, |
||||
}; |
@ -0,0 +1,76 @@
@@ -0,0 +1,76 @@
|
||||
import { BrowserWindow } from 'electron'; |
||||
import { ICON, preload, url, WEB_URL, WIN_CONFIG } from '../main/constant'; |
||||
|
||||
let recorderVideoWin: BrowserWindow | null = null; |
||||
let downloadSet: Set<string> = new Set(); |
||||
|
||||
function createRecorderVideoWin(): BrowserWindow { |
||||
recorderVideoWin = new BrowserWindow({ |
||||
title: 'pear-rec 录像', |
||||
icon: ICON, |
||||
height: WIN_CONFIG.recorderVideo.height, |
||||
width: WIN_CONFIG.recorderVideo.width, |
||||
autoHideMenuBar: WIN_CONFIG.recorderVideo.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
|
||||
if (url) { |
||||
recorderVideoWin.loadURL(WEB_URL + 'recorderVideo.html'); |
||||
// recorderVideoWin.webContents.openDevTools();
|
||||
} else { |
||||
recorderVideoWin.loadFile(WIN_CONFIG.recorderVideo.html); |
||||
} |
||||
|
||||
recorderVideoWin?.webContents.session.on( |
||||
'will-download', |
||||
async (event: any, item: any, webContents: any) => { |
||||
const url = item.getURL(); |
||||
if (downloadSet.has(url)) { |
||||
// const fileName = item.getFilename();
|
||||
// const filePath = (await getFilePath()) as string;
|
||||
// const rvFilePath = join(`${filePath}/rv`, `${fileName}`);
|
||||
// item.setSavePath(rvFilePath);
|
||||
// item.once('done', (event: any, state: any) => {
|
||||
// if (state === 'completed') {
|
||||
// setHistoryVideo(rvFilePath);
|
||||
// setTimeout(() => {
|
||||
// closeRecorderVideoWin();
|
||||
// // shell.showItemInFolder(filePath);
|
||||
// }, 1000);
|
||||
// } else {
|
||||
// dialog.showErrorBox('下载失败', `文件 ${item.getFilename()} 因为某些原因被中断下载`);
|
||||
// }
|
||||
// });
|
||||
} |
||||
}, |
||||
); |
||||
|
||||
return recorderVideoWin; |
||||
} |
||||
|
||||
// 打开关闭录屏窗口
|
||||
function closeRecorderVideoWin() { |
||||
recorderVideoWin?.isDestroyed() || recorderVideoWin?.close(); |
||||
recorderVideoWin = null; |
||||
} |
||||
|
||||
function openRecorderVideoWin() { |
||||
if (!recorderVideoWin || recorderVideoWin?.isDestroyed()) { |
||||
recorderVideoWin = createRecorderVideoWin(); |
||||
} |
||||
recorderVideoWin?.show(); |
||||
} |
||||
|
||||
function downloadURLRecorderVideoWin(downloadUrl: string) { |
||||
recorderVideoWin?.webContents.downloadURL(downloadUrl); |
||||
downloadSet.add(downloadUrl); |
||||
} |
||||
|
||||
export { |
||||
closeRecorderVideoWin, |
||||
createRecorderVideoWin, |
||||
downloadURLRecorderVideoWin, |
||||
openRecorderVideoWin, |
||||
}; |
@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
|
||||
import { BrowserWindow } from 'electron'; |
||||
import { ICON, preload, url, WEB_URL, WIN_CONFIG } from '../main/constant'; |
||||
|
||||
let recordsWin: BrowserWindow | null = null; |
||||
|
||||
function createRecordsWin(): BrowserWindow { |
||||
recordsWin = new BrowserWindow({ |
||||
title: 'pear-rec 记录', |
||||
icon: ICON, |
||||
width: WIN_CONFIG.records.width, |
||||
autoHideMenuBar: WIN_CONFIG.records.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
|
||||
// recordsWin.webContents.openDevTools();
|
||||
if (url) { |
||||
recordsWin.loadURL(WEB_URL + 'records.html'); |
||||
} else { |
||||
recordsWin.loadFile(WIN_CONFIG.records.html); |
||||
} |
||||
|
||||
return recordsWin; |
||||
} |
||||
|
||||
// 打开关闭录屏窗口
|
||||
function closeRecordsWin() { |
||||
recordsWin?.isDestroyed() || recordsWin?.close(); |
||||
recordsWin = null; |
||||
} |
||||
|
||||
function openRecordsWin() { |
||||
if (!recordsWin || recordsWin?.isDestroyed()) { |
||||
recordsWin = createRecordsWin(); |
||||
} |
||||
recordsWin?.show(); |
||||
} |
||||
|
||||
function showRecordsWin() { |
||||
recordsWin?.show(); |
||||
} |
||||
|
||||
function hideRecordsWin() { |
||||
recordsWin?.hide(); |
||||
} |
||||
|
||||
export { closeRecordsWin, createRecordsWin, hideRecordsWin, openRecordsWin, showRecordsWin }; |
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
import { BrowserWindow, shell } from 'electron'; |
||||
import { ICON, WEB_URL, WIN_CONFIG, preload, url } from '../main/constant'; |
||||
|
||||
let settingWin: BrowserWindow | null = null; |
||||
|
||||
function createSettingWin(): BrowserWindow { |
||||
settingWin = new BrowserWindow({ |
||||
title: 'pear-rec 设置', |
||||
icon: ICON, |
||||
autoHideMenuBar: WIN_CONFIG.setting.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
width: WIN_CONFIG.setting.width, // 宽度(px)
|
||||
height: WIN_CONFIG.setting.height, // 高度(px)
|
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
|
||||
// settingWin.webContents.openDevTools();
|
||||
if (url) { |
||||
settingWin.loadURL(WEB_URL + 'setting.html'); |
||||
} else { |
||||
settingWin.loadFile(WIN_CONFIG.setting.html); |
||||
} |
||||
|
||||
settingWin.webContents.setWindowOpenHandler(({ url }) => { |
||||
if (url.startsWith('https:')) shell.openExternal(url); |
||||
return { action: 'deny' }; |
||||
}); |
||||
|
||||
return settingWin; |
||||
} |
||||
|
||||
// 打开关闭录屏窗口
|
||||
function closeSettingWin() { |
||||
settingWin?.isDestroyed() || settingWin?.close(); |
||||
settingWin = null; |
||||
} |
||||
|
||||
function openSettingWin() { |
||||
if (!settingWin || settingWin?.isDestroyed()) { |
||||
settingWin = createSettingWin(); |
||||
} |
||||
settingWin?.show(); |
||||
} |
||||
|
||||
function showSettingWin() { |
||||
settingWin?.show(); |
||||
} |
||||
|
||||
function hideSettingWin() { |
||||
settingWin?.hide(); |
||||
} |
||||
|
||||
export { closeSettingWin, createSettingWin, hideSettingWin, openSettingWin, showSettingWin }; |
@ -0,0 +1,130 @@
@@ -0,0 +1,130 @@
|
||||
import { BrowserWindow, clipboard, dialog, nativeImage, screen, desktopCapturer } from 'electron'; |
||||
import { ICON, WEB_URL, WIN_CONFIG, preload, url } from '../main/constant'; |
||||
import * as utils from '../main/utils'; |
||||
|
||||
let shotScreenWin: BrowserWindow | null = null; |
||||
let savePath: string = ''; |
||||
let downloadSet: Set<string> = new Set(); |
||||
|
||||
function createShotScreenWin(): BrowserWindow { |
||||
shotScreenWin = new BrowserWindow({ |
||||
title: 'pear-rec 截图', |
||||
icon: ICON, |
||||
show: false, |
||||
autoHideMenuBar: WIN_CONFIG.shotScreen.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
useContentSize: WIN_CONFIG.shotScreen.useContentSize, // width 和 height 将设置为 web 页面的尺寸
|
||||
movable: WIN_CONFIG.shotScreen.movable, // 是否可移动
|
||||
frame: WIN_CONFIG.shotScreen.frame, // 无边框窗口
|
||||
resizable: WIN_CONFIG.shotScreen.resizable, // 窗口大小是否可调整
|
||||
hasShadow: WIN_CONFIG.shotScreen.hasShadow, // 窗口是否有阴影
|
||||
transparent: WIN_CONFIG.shotScreen.transparent, // 使窗口透明
|
||||
fullscreenable: WIN_CONFIG.shotScreen.fullscreenable, // 窗口是否可以进入全屏状态
|
||||
fullscreen: WIN_CONFIG.shotScreen.fullscreen, // 窗口是否全屏
|
||||
simpleFullscreen: WIN_CONFIG.shotScreen.simpleFullscreen, // 在 macOS 上使用 pre-Lion 全屏
|
||||
alwaysOnTop: WIN_CONFIG.shotScreen.alwaysOnTop, |
||||
skipTaskbar: WIN_CONFIG.shotScreen.skipTaskbar, |
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
|
||||
// shotScreenWin.webContents.openDevTools();
|
||||
|
||||
if (url) { |
||||
shotScreenWin.loadURL(WEB_URL + 'shotScreen.html'); |
||||
} else { |
||||
shotScreenWin.loadFile(WIN_CONFIG.shotScreen.html); |
||||
} |
||||
shotScreenWin.maximize(); |
||||
shotScreenWin.setFullScreen(true); |
||||
|
||||
return shotScreenWin; |
||||
} |
||||
|
||||
// 打开关闭录屏窗口
|
||||
function closeShotScreenWin() { |
||||
shotScreenWin?.isDestroyed() || shotScreenWin?.close(); |
||||
shotScreenWin = null; |
||||
} |
||||
|
||||
function openShotScreenWin() { |
||||
if (!shotScreenWin || shotScreenWin?.isDestroyed()) { |
||||
shotScreenWin = createShotScreenWin(); |
||||
} |
||||
// shotScreenWin?.show();
|
||||
} |
||||
|
||||
async function showShotScreenWin() { |
||||
const { id } = screen.getPrimaryDisplay(); |
||||
const { width, height } = utils.getScreenSize(); |
||||
const sources = [ |
||||
...(await desktopCapturer.getSources({ |
||||
types: ['screen'], |
||||
thumbnailSize: { |
||||
width, |
||||
height, |
||||
}, |
||||
})), |
||||
]; |
||||
|
||||
let source = sources.filter((e: any) => parseInt(e.display_id, 10) == id)[0]; |
||||
source || (source = sources[0]); |
||||
const img = source.thumbnail.toDataURL(); |
||||
shotScreenWin?.webContents.send('ss:show-win', img); |
||||
if (!shotScreenWin || shotScreenWin?.isDestroyed()) { |
||||
shotScreenWin = createShotScreenWin(); |
||||
} |
||||
shotScreenWin?.show(); |
||||
} |
||||
|
||||
function hideShotScreenWin() { |
||||
shotScreenWin?.webContents.send('ss:hide-win'); |
||||
shotScreenWin?.hide(); |
||||
} |
||||
|
||||
function minimizeShotScreenWin() { |
||||
shotScreenWin?.minimize(); |
||||
} |
||||
|
||||
function maximizeShotScreenWin() { |
||||
shotScreenWin?.maximize(); |
||||
} |
||||
|
||||
function unmaximizeShotScreenWin() { |
||||
shotScreenWin?.unmaximize(); |
||||
} |
||||
|
||||
async function downloadURLShotScreenWin(downloadUrl: string, isShowDialog?: boolean) { |
||||
savePath = ''; |
||||
isShowDialog && (savePath = await showOpenDialogShotScreenWin()); |
||||
downloadSet.add(downloadUrl); |
||||
shotScreenWin?.webContents.downloadURL(downloadUrl); |
||||
} |
||||
|
||||
async function showOpenDialogShotScreenWin() { |
||||
let res = await dialog.showOpenDialog({ |
||||
properties: ['openDirectory'], |
||||
}); |
||||
|
||||
const savePath = res.filePaths[0] || ''; |
||||
|
||||
return savePath; |
||||
} |
||||
|
||||
function copyImg(filePath: string) { |
||||
const image = nativeImage.createFromDataURL(filePath); |
||||
clipboard.writeImage(image); |
||||
} |
||||
|
||||
export { |
||||
closeShotScreenWin, |
||||
copyImg, |
||||
createShotScreenWin, |
||||
downloadURLShotScreenWin, |
||||
hideShotScreenWin, |
||||
maximizeShotScreenWin, |
||||
minimizeShotScreenWin, |
||||
openShotScreenWin, |
||||
showShotScreenWin, |
||||
unmaximizeShotScreenWin, |
||||
}; |
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
import { BrowserWindow } from 'electron'; |
||||
import { ICON, WEB_URL, WIN_CONFIG, preload, url } from '../main/constant'; |
||||
|
||||
let spliceImageWin: BrowserWindow | null = null; |
||||
|
||||
function createSpliceImageWin(): BrowserWindow { |
||||
spliceImageWin = new BrowserWindow({ |
||||
title: 'pear-rec 拼接图片', |
||||
icon: ICON, |
||||
height: WIN_CONFIG.spliceImage.height, |
||||
width: WIN_CONFIG.spliceImage.width, |
||||
autoHideMenuBar: WIN_CONFIG.spliceImage.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
|
||||
// spliceImageWin.webContents.openDevTools();
|
||||
if (url) { |
||||
spliceImageWin.loadURL(WEB_URL + `spliceImage.html`); |
||||
} else { |
||||
spliceImageWin.loadFile(WIN_CONFIG.spliceImage.html); |
||||
} |
||||
|
||||
spliceImageWin.once('ready-to-show', async () => { |
||||
spliceImageWin?.show(); |
||||
}); |
||||
|
||||
return spliceImageWin; |
||||
} |
||||
|
||||
function openSpliceImageWin() { |
||||
if (!spliceImageWin || spliceImageWin?.isDestroyed()) { |
||||
spliceImageWin = createSpliceImageWin(); |
||||
} |
||||
spliceImageWin.show(); |
||||
} |
||||
|
||||
function closeSpliceImageWin() { |
||||
spliceImageWin?.close(); |
||||
} |
||||
|
||||
export { closeSpliceImageWin, createSpliceImageWin, openSpliceImageWin }; |
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
import { BrowserWindow, dialog, nativeImage } from 'electron'; |
||||
import { ICON, WEB_URL, WIN_CONFIG, preload, url } from '../main/constant'; |
||||
|
||||
let videoConverterWin: BrowserWindow | null = null; |
||||
|
||||
function createVideoConverterWin(search?: any): BrowserWindow { |
||||
videoConverterWin = new BrowserWindow({ |
||||
title: 'pear-rec 图片编辑', |
||||
icon: ICON, |
||||
height: WIN_CONFIG.videoConverter.height, |
||||
width: WIN_CONFIG.videoConverter.width, |
||||
autoHideMenuBar: WIN_CONFIG.videoConverter.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
|
||||
const videoUrl = search?.videoUrl || ''; |
||||
|
||||
// videoConverterWin.webContents.openDevTools();
|
||||
if (url) { |
||||
videoConverterWin.loadURL(WEB_URL + `videoConverter.html?videoUrl=${videoUrl}`); |
||||
} else { |
||||
videoConverterWin.loadFile(WIN_CONFIG.videoConverter.html, { |
||||
search: `?videoUrl=${videoUrl}`, |
||||
}); |
||||
} |
||||
|
||||
videoConverterWin.once('ready-to-show', async () => { |
||||
videoConverterWin?.show(); |
||||
}); |
||||
|
||||
return videoConverterWin; |
||||
} |
||||
|
||||
function openVideoConverterWin(search?: any) { |
||||
if (!videoConverterWin || videoConverterWin?.isDestroyed()) { |
||||
videoConverterWin = createVideoConverterWin(search); |
||||
} |
||||
videoConverterWin.show(); |
||||
} |
||||
|
||||
function closeVideoConverterWin() { |
||||
videoConverterWin?.close(); |
||||
} |
||||
|
||||
export { closeVideoConverterWin, createVideoConverterWin, openVideoConverterWin }; |
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
import { BrowserWindow } from 'electron'; |
||||
import { ICON, WEB_URL, WIN_CONFIG, preload, url } from '../main/constant'; |
||||
import { getAudiosByAudioUrl } from '../main/utils'; |
||||
|
||||
let viewAudioWin: BrowserWindow | null = null; |
||||
|
||||
function createViewAudioWin(search?: any): BrowserWindow { |
||||
viewAudioWin = new BrowserWindow({ |
||||
title: 'pear-rec 音频', |
||||
icon: ICON, |
||||
autoHideMenuBar: WIN_CONFIG.viewAudio.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
|
||||
const audioUrl = search?.audioUrl || ''; |
||||
const recordId = search?.recordId || ''; |
||||
// Open devTool if the app is not packaged
|
||||
// viewAudioWin.webContents.openDevTools();
|
||||
|
||||
if (url) { |
||||
viewAudioWin.loadURL( |
||||
WEB_URL + |
||||
`viewAudio.html?${audioUrl ? 'audioUrl=' + audioUrl : ''}${ |
||||
recordId ? 'recordId=' + recordId : '' |
||||
}`,
|
||||
); |
||||
} else { |
||||
viewAudioWin.loadFile(WIN_CONFIG.viewAudio.html, { |
||||
search: `${audioUrl ? 'audioUrl=' + audioUrl : ''}${recordId ? 'recordId=' + recordId : ''}`, |
||||
}); |
||||
} |
||||
|
||||
viewAudioWin.once('ready-to-show', async () => { |
||||
viewAudioWin?.show(); |
||||
}); |
||||
|
||||
return viewAudioWin; |
||||
} |
||||
|
||||
function openViewAudioWin(search?: any) { |
||||
if (!viewAudioWin || viewAudioWin?.isDestroyed()) { |
||||
viewAudioWin = createViewAudioWin(search); |
||||
} |
||||
viewAudioWin.show(); |
||||
} |
||||
|
||||
function closeViewAudioWin() { |
||||
if (!viewAudioWin?.isDestroyed()) { |
||||
viewAudioWin?.close(); |
||||
} |
||||
viewAudioWin = null; |
||||
} |
||||
|
||||
async function getAudios(audioUrl?: any) { |
||||
let audios = await getAudiosByAudioUrl(audioUrl); |
||||
return audios; |
||||
} |
||||
|
||||
export { closeViewAudioWin, createViewAudioWin, getAudios, openViewAudioWin }; |
@ -0,0 +1,124 @@
@@ -0,0 +1,124 @@
|
||||
import { BrowserWindow, dialog } from 'electron'; |
||||
import { readFile, writeFile } from 'node:fs'; |
||||
import { ICON, WEB_URL, WIN_CONFIG, preload, url } from '../main/constant'; |
||||
import { getImgsByImgUrl } from '../main/utils'; |
||||
|
||||
let viewImageWin: BrowserWindow | null = null; |
||||
|
||||
function createViewImageWin(search?: any): BrowserWindow { |
||||
viewImageWin = new BrowserWindow({ |
||||
title: 'pear-rec 图片', |
||||
icon: ICON, |
||||
frame: WIN_CONFIG.viewImage.frame, |
||||
autoHideMenuBar: WIN_CONFIG.viewImage.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
|
||||
const imgUrl = search?.imgUrl || ''; |
||||
const recordId = search?.recordId || ''; |
||||
// viewImageWin.webContents.openDevTools();
|
||||
if (url) { |
||||
viewImageWin.loadURL( |
||||
WEB_URL + |
||||
`viewImage.html?${imgUrl ? 'imgUrl=' + imgUrl : ''}${ |
||||
recordId ? 'recordId=' + recordId : '' |
||||
}`,
|
||||
); |
||||
// Open devTool if the app is not packaged
|
||||
} else { |
||||
viewImageWin.loadFile(WIN_CONFIG.viewImage.html, { |
||||
search: `?${imgUrl ? 'imgUrl=' + imgUrl : ''}${recordId ? 'recordId=' + recordId : ''}`, |
||||
}); |
||||
} |
||||
|
||||
return viewImageWin; |
||||
} |
||||
|
||||
function openViewImageWin(search?: any) { |
||||
if (!viewImageWin || viewImageWin?.isDestroyed()) { |
||||
viewImageWin = createViewImageWin(search); |
||||
} |
||||
viewImageWin.show(); |
||||
} |
||||
|
||||
function closeViewImageWin() { |
||||
if (!(viewImageWin && viewImageWin.isDestroyed())) { |
||||
viewImageWin?.close(); |
||||
} |
||||
viewImageWin = null; |
||||
} |
||||
|
||||
function destroyViewImageWin() { |
||||
viewImageWin?.destroy(); |
||||
viewImageWin = null; |
||||
} |
||||
|
||||
function hideViewImageWin() { |
||||
viewImageWin?.hide(); |
||||
} |
||||
|
||||
function minimizeViewImageWin() { |
||||
viewImageWin?.minimize(); |
||||
} |
||||
|
||||
function maximizeViewImageWin() { |
||||
viewImageWin?.maximize(); |
||||
} |
||||
|
||||
function unmaximizeViewImageWin() { |
||||
viewImageWin?.unmaximize(); |
||||
} |
||||
|
||||
function getIsAlwaysOnTopViewImageWin() { |
||||
return viewImageWin?.isAlwaysOnTop(); |
||||
} |
||||
|
||||
function setIsAlwaysOnTopViewImageWin(isAlwaysOnTop: boolean) { |
||||
viewImageWin?.setAlwaysOnTop(isAlwaysOnTop); |
||||
return isAlwaysOnTop; |
||||
} |
||||
|
||||
async function getImgs(imgUrl: any) { |
||||
let imgs = await getImgsByImgUrl(imgUrl); |
||||
return imgs; |
||||
} |
||||
|
||||
async function downloadImg(imgUrl: any) { |
||||
let defaultPath = `pear-rec_${+new Date()}.png`; |
||||
let res = await dialog.showSaveDialog({ |
||||
defaultPath: defaultPath, |
||||
filters: [{ name: 'Images', extensions: ['png', 'jpg', 'gif'] }], |
||||
}); |
||||
if (!res.canceled) { |
||||
readFile(imgUrl, (err, imgData) => { |
||||
if (err) { |
||||
console.error(err); |
||||
} else { |
||||
writeFile(res.filePath, imgData, (err) => { |
||||
if (err) { |
||||
console.error(err); |
||||
} else { |
||||
console.log(`${defaultPath}:图片保存成功`); |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
} |
||||
return imgUrl; |
||||
} |
||||
|
||||
export { |
||||
closeViewImageWin, |
||||
createViewImageWin, |
||||
downloadImg, |
||||
getImgs, |
||||
getIsAlwaysOnTopViewImageWin, |
||||
hideViewImageWin, |
||||
maximizeViewImageWin, |
||||
minimizeViewImageWin, |
||||
openViewImageWin, |
||||
setIsAlwaysOnTopViewImageWin, |
||||
unmaximizeViewImageWin, |
||||
}; |
@ -0,0 +1,95 @@
@@ -0,0 +1,95 @@
|
||||
import { BrowserWindow } from 'electron'; |
||||
import { ICON, WEB_URL, WIN_CONFIG, preload, url } from '../main/constant'; |
||||
|
||||
let viewVideoWin: BrowserWindow | null = null; |
||||
|
||||
function createViewVideoWin(search?: any): BrowserWindow { |
||||
viewVideoWin = new BrowserWindow({ |
||||
title: 'pear-rec 视频', |
||||
icon: ICON, |
||||
autoHideMenuBar: WIN_CONFIG.viewVideo.autoHideMenuBar, // 自动隐藏菜单栏
|
||||
webPreferences: { |
||||
preload, |
||||
}, |
||||
}); |
||||
|
||||
const videoUrl = search?.videoUrl || ''; |
||||
const recordId = search?.recordId || ''; |
||||
// Open devTool if the app is not packaged
|
||||
// viewVideoWin.webContents.openDevTools();
|
||||
if (url) { |
||||
viewVideoWin.loadURL( |
||||
WEB_URL + |
||||
`viewVideo.html?${videoUrl ? 'videoUrl=' + videoUrl : ''}${ |
||||
recordId ? 'recordId=' + recordId : '' |
||||
}`,
|
||||
); |
||||
} else { |
||||
viewVideoWin.loadFile(WIN_CONFIG.viewVideo.html, { |
||||
search: `?${videoUrl ? 'videoUrl=' + videoUrl : ''}${recordId ? 'recordId=' + recordId : ''}`, |
||||
}); |
||||
} |
||||
|
||||
viewVideoWin.once('ready-to-show', async () => { |
||||
viewVideoWin?.show(); |
||||
}); |
||||
|
||||
return viewVideoWin; |
||||
} |
||||
|
||||
function openViewVideoWin(search?: any) { |
||||
if (!viewVideoWin || viewVideoWin?.isDestroyed()) { |
||||
viewVideoWin = createViewVideoWin(search); |
||||
} |
||||
viewVideoWin.show(); |
||||
} |
||||
|
||||
function closeViewVideoWin() { |
||||
if (!(viewVideoWin && viewVideoWin.isDestroyed())) { |
||||
viewVideoWin?.close(); |
||||
} |
||||
viewVideoWin = null; |
||||
} |
||||
|
||||
function hideViewVideoWin() { |
||||
viewVideoWin?.hide(); |
||||
} |
||||
|
||||
function minimizeViewVideoWin() { |
||||
viewVideoWin?.minimize(); |
||||
} |
||||
|
||||
function maximizeViewVideoWin() { |
||||
viewVideoWin?.maximize(); |
||||
} |
||||
|
||||
function unmaximizeViewVideoWin() { |
||||
viewVideoWin?.unmaximize(); |
||||
} |
||||
|
||||
function setAlwaysOnTopViewVideoWin(isAlwaysOnTop: boolean) { |
||||
viewVideoWin?.setAlwaysOnTop(isAlwaysOnTop); |
||||
} |
||||
|
||||
async function getHistoryVideoPath() { |
||||
// const historyVideoPath = ((await getHistoryVideo()) as string) || '';
|
||||
// return historyVideoPath;
|
||||
} |
||||
|
||||
async function sendHistoryVideo() { |
||||
// const filePath = await getHistoryVideoPath();
|
||||
// let video = await readDirectoryVideo(filePath);
|
||||
// return video;
|
||||
} |
||||
|
||||
export { |
||||
closeViewVideoWin, |
||||
createViewVideoWin, |
||||
hideViewVideoWin, |
||||
maximizeViewVideoWin, |
||||
minimizeViewVideoWin, |
||||
openViewVideoWin, |
||||
sendHistoryVideo, |
||||
setAlwaysOnTopViewVideoWin, |
||||
unmaximizeViewVideoWin, |
||||
}; |
@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
|
||||
<head> |
||||
<meta charset="UTF-8" /> |
||||
<link rel="icon" type="image/x-icon" href="/imgs/logo/favicon.ico" /> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" /> |
||||
<title>pear-rec</title> |
||||
</head> |
||||
|
||||
<body> |
||||
<div id="root"></div> |
||||
</body> |
||||
|
||||
</html> |
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
{ |
||||
"name": "@pear-rec/desktop", |
||||
"version": "1.3.15", |
||||
"main": "dist-electron/main/index.js", |
||||
"description": "pear-rec", |
||||
"author": "027xiguapi", |
||||
"license": "Apache-2.0", |
||||
"private": true, |
||||
"debug": { |
||||
"env": { |
||||
"VITE_DEV_SERVER_URL": "http://127.0.0.1:7777/" |
||||
} |
||||
}, |
||||
"scripts": { |
||||
"dev": "vite", |
||||
"build": "tsc && vite build", |
||||
"build:win": "rimraf release && electron-builder", |
||||
"preview": "vite preview", |
||||
"pree2e": "vite build --mode=test", |
||||
"e2e": "playwright test", |
||||
"release": "electron-builder -p always", |
||||
"server": "node dist-electron/server/main", |
||||
"clear": "rimraf node_modules" |
||||
}, |
||||
"dependencies": { |
||||
"electron-updater": "^6.1.8" |
||||
}, |
||||
"devDependencies": { |
||||
"@pear-rec/server": "workspace:^", |
||||
"@playwright/test": "^1.37.1", |
||||
"@types/jsonfile": "^6.1.3", |
||||
"@types/uuid": "^9.0.6", |
||||
"@vitejs/plugin-react": "^4.0.4", |
||||
"electron": "^29.1.3", |
||||
"electron-builder": "^24.13.3", |
||||
"electron-log": "^5.1.2", |
||||
"jsonfile": "^6.1.0", |
||||
"typescript": "^5.2.2", |
||||
"uuid": "^9.0.1", |
||||
"vite": "^5.1.5", |
||||
"vite-plugin-electron": "^0.28.2" |
||||
} |
||||
} |
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
import type { PlaywrightTestConfig } from "@playwright/test"; |
||||
|
||||
/** |
||||
* Read environment variables from file. |
||||
* https://github.com/motdotla/dotenv
|
||||
*/ |
||||
// require('dotenv').config();
|
||||
|
||||
/** |
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/ |
||||
const config: PlaywrightTestConfig = { |
||||
testDir: "./e2e", |
||||
/* Maximum time one test can run for. */ |
||||
timeout: 30 * 1000, |
||||
expect: { |
||||
/** |
||||
* Maximum time expect() should wait for the condition to be met. |
||||
* For example in `await expect(locator).toHaveText();` |
||||
*/ |
||||
timeout: 5000, |
||||
}, |
||||
/* Run tests in files in parallel */ |
||||
fullyParallel: true, |
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */ |
||||
forbidOnly: !!process.env.CI, |
||||
/* Retry on CI only */ |
||||
retries: process.env.CI ? 2 : 0, |
||||
/* Opt out of parallel tests on CI. */ |
||||
workers: process.env.CI ? 1 : undefined, |
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */ |
||||
reporter: "html", |
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ |
||||
use: { |
||||
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ |
||||
actionTimeout: 0, |
||||
/* Base URL to use in actions like `await page.goto('/')`. */ |
||||
// baseURL: 'http://localhost:3000',
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ |
||||
trace: "on-first-retry", |
||||
}, |
||||
|
||||
/* Folder for test artifacts such as screenshots, videos, traces, etc. */ |
||||
// outputDir: 'test-results/',
|
||||
|
||||
/* Run your local dev server before starting the tests */ |
||||
// webServer: {
|
||||
// command: 'npm run start',
|
||||
// port: 3000,
|
||||
// },
|
||||
}; |
||||
|
||||
export default config; |
After Width: | Height: | Size: 402 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 757 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 139 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 353 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 155 KiB |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
{ |
||||
"compilerOptions": { |
||||
"target": "ESNext", |
||||
"experimentalDecorators": true, |
||||
"emitDecoratorMetadata": true, |
||||
"esModuleInterop": true, |
||||
"useDefineForClassFields": true, |
||||
"lib": [ |
||||
"DOM", |
||||
"DOM.Iterable", |
||||
"ESNext" |
||||
], |
||||
"allowJs": false, |
||||
"skipLibCheck": true, |
||||
"allowSyntheticDefaultImports": true, |
||||
"strict": false, |
||||
"forceConsistentCasingInFileNames": true, |
||||
"module": "ESNext", |
||||
"moduleResolution": "Node", |
||||
"resolveJsonModule": true, |
||||
"isolatedModules": true, |
||||
"noEmit": true, |
||||
"jsx": "react-jsx", |
||||
"baseUrl": "./", |
||||
"paths": { |
||||
"@/*": [ |
||||
"src/*" |
||||
] |
||||
} |
||||
}, |
||||
"include": [ |
||||
"src", |
||||
"electron" |
||||
], |
||||
"references": [ |
||||
{ |
||||
"path": "./tsconfig.node.json" |
||||
} |
||||
] |
||||
} |
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
{ |
||||
"compilerOptions": { |
||||
"composite": true, |
||||
"module": "ESNext", |
||||
"moduleResolution": "Node", |
||||
"resolveJsonModule": true, |
||||
"allowSyntheticDefaultImports": true |
||||
}, |
||||
"include": [ |
||||
"vite.config.ts", |
||||
"package.json" |
||||
] |
||||
} |
@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
|
||||
import { defineConfig } from 'vite'; |
||||
import electron from 'vite-plugin-electron/simple'; |
||||
import pkg from './package.json'; |
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(({ command }) => { |
||||
// rmSync('dist-electron', { recursive: true, force: true });
|
||||
|
||||
const isServe = command === 'serve'; |
||||
const isBuild = command === 'build'; |
||||
const sourcemap = isServe || !!process.env.VSCODE_DEBUG; |
||||
|
||||
return { |
||||
plugins: [ |
||||
electron({ |
||||
main: { |
||||
entry: 'electron/main/index.ts', |
||||
vite: { |
||||
build: { |
||||
sourcemap: sourcemap ? 'inline' : undefined, // #332
|
||||
minify: isBuild, |
||||
outDir: 'dist-electron/main', |
||||
rollupOptions: { |
||||
external: Object.keys('dependencies' in pkg ? pkg.dependencies : {}), |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
preload: { |
||||
input: 'electron/preload/index.ts', |
||||
vite: { |
||||
build: { |
||||
sourcemap: sourcemap ? 'inline' : undefined, // #332
|
||||
minify: isBuild, |
||||
outDir: 'dist-electron/preload', |
||||
rollupOptions: { |
||||
external: Object.keys('dependencies' in pkg ? pkg.dependencies : {}), |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}), |
||||
], |
||||
server: |
||||
process.env.VSCODE_DEBUG && |
||||
(() => { |
||||
const url = new URL(pkg.debug.env.VITE_DEV_SERVER_URL); |
||||
return { |
||||
host: url.hostname, |
||||
port: +url.port, |
||||
}; |
||||
})(), |
||||
clearScreen: false, |
||||
}; |
||||
}); |
@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
|
||||
import { defineConfig } from 'vitepress'; |
||||
import react from '@vitejs/plugin-react'; |
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({ |
||||
vite: { |
||||
plugins: [react()], |
||||
}, |
||||
title: 'pear-rec', |
||||
base: '/pear-rec', |
||||
description: '一个跨平台的截图、录屏、录音、录像软件', |
||||
head: [['link', { rel: 'icon', href: '/favicon.ico' }]], |
||||
themeConfig: { |
||||
logo: '/favicon.ico', |
||||
siteTitle: '『 pear-rec 』', |
||||
outlineTitle: '🔴🟠🟡🟢🔵🟣🟤⚫⚪', |
||||
outline: [2, 6], |
||||
// 顶部导航
|
||||
nav: [ |
||||
{ text: 'Home', link: '/' }, |
||||
{ text: '文档', link: '/desktop/examples.md' }, |
||||
{ text: '下载', link: 'https://github.com/027xiguapi/pear-rec/releases' }, |
||||
], |
||||
// 侧边栏
|
||||
sidebar: [ |
||||
{ |
||||
text: '文档', |
||||
items: [ |
||||
{ text: '桌面软件', link: '/desktop/examples.md' }, |
||||
{ text: '截图插件', link: '/screenshot/examples' }, |
||||
{ text: '录音插件', link: '/recorder/examples.md' }, |
||||
{ text: '计时插件', link: '/timer/examples' }, |
||||
{ text: '网页应用', link: '/web/examples.md' }, |
||||
// { text: "markdown", link: "/markdown-examples.md" },
|
||||
// { text: "Runtime API Examples", link: "/api-examples" },
|
||||
], |
||||
}, |
||||
], |
||||
|
||||
socialLinks: [{ icon: 'github', link: 'https://github.com/027xiguapi/pear-rec' }], |
||||
// 页脚
|
||||
footer: { |
||||
message: |
||||
'<a href="https://www.apache.org/licenses/LICENSE-2.0" target="_blank">pear-rec is available under the Apache License V2.</a>', |
||||
copyright: 'Copyright © 2023 西瓜皮', |
||||
}, |
||||
}, |
||||
}); |
After Width: | Height: | Size: 705 KiB |
After Width: | Height: | Size: 943 KiB |
After Width: | Height: | Size: 313 KiB |
After Width: | Height: | Size: 155 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 1022 KiB |
After Width: | Height: | Size: 195 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 1.0 MiB |