mirror of
https://github.com/Molunerfinn/PicGo.git
synced 2026-10-21 05:21:26 +00:00
🐛 Fix(custom): data report
This commit is contained in:
+2
-1
@@ -23,4 +23,5 @@ scripts/*.yml
|
||||
#Electron-builder output
|
||||
/dist_electron
|
||||
.serena/
|
||||
dist/*
|
||||
dist/*
|
||||
test.js
|
||||
+5
-2
@@ -28,6 +28,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.2",
|
||||
"@numairawan/video-duration": "^1.0.0",
|
||||
"@picgo/i18n": "^1.0.0",
|
||||
"@picgo/store": "^2.1.0",
|
||||
"axios": "^0.19.0",
|
||||
@@ -44,19 +45,21 @@
|
||||
"lodash-id": "^0.14.0",
|
||||
"lowdb": "^1.0.0",
|
||||
"marked": "^7.0.4",
|
||||
"mime-types": "^3.0.2",
|
||||
"mitt": "^3.0.1",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"picgo": "^1.6.0",
|
||||
"picgo": "^1.6.2",
|
||||
"qrcode.vue": "^3.3.3",
|
||||
"semver": "^7.7.3",
|
||||
"shell-path": "2.1.0",
|
||||
"systeminformation": "^5.27.14",
|
||||
"tunnel": "^0.0.6",
|
||||
"uuid": "^9.0.0",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.2",
|
||||
"vue3-lazyload": "^0.3.6",
|
||||
"vue3-photo-preview": "^0.3.0",
|
||||
"write-file-atomic": "^4.0.1"
|
||||
"write-file-atomic": "^7.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-sdk/client-s3": "^3.276.0",
|
||||
|
||||
Generated
+69
-23
@@ -11,6 +11,9 @@ importers:
|
||||
'@element-plus/icons-vue':
|
||||
specifier: ^2.3.2
|
||||
version: 2.3.2(vue@3.5.25(typescript@5.9.3))
|
||||
'@numairawan/video-duration':
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
'@picgo/i18n':
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
@@ -59,6 +62,9 @@ importers:
|
||||
marked:
|
||||
specifier: ^7.0.4
|
||||
version: 7.0.5
|
||||
mime-types:
|
||||
specifier: ^3.0.2
|
||||
version: 3.0.2
|
||||
mitt:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
@@ -66,8 +72,8 @@ importers:
|
||||
specifier: ^1.4.5-lts.1
|
||||
version: 1.4.5-lts.2
|
||||
picgo:
|
||||
specifier: ^1.6.0
|
||||
version: 1.6.0
|
||||
specifier: ^1.6.2
|
||||
version: 1.6.2
|
||||
qrcode.vue:
|
||||
specifier: ^3.3.3
|
||||
version: 3.6.0(vue@3.5.25(typescript@5.9.3))
|
||||
@@ -77,6 +83,9 @@ importers:
|
||||
shell-path:
|
||||
specifier: 2.1.0
|
||||
version: 2.1.0
|
||||
systeminformation:
|
||||
specifier: ^5.27.14
|
||||
version: 5.27.14
|
||||
tunnel:
|
||||
specifier: ^0.0.6
|
||||
version: 0.0.6
|
||||
@@ -96,8 +105,8 @@ importers:
|
||||
specifier: ^0.3.0
|
||||
version: 0.3.0(vue@3.5.25(typescript@5.9.3))
|
||||
write-file-atomic:
|
||||
specifier: ^4.0.1
|
||||
version: 4.0.2
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.0
|
||||
devDependencies:
|
||||
'@aws-sdk/client-s3':
|
||||
specifier: ^3.276.0
|
||||
@@ -1034,6 +1043,9 @@ packages:
|
||||
resolution: {integrity: sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
|
||||
'@numairawan/video-duration@1.0.0':
|
||||
resolution: {integrity: sha512-C819BxippZk2ajzfJt/S8kD5NJqAxuDZc1W4rqHVjzgoUI5Wjo7ehoH4dTIGesUEOxTobIXh6iOKAhhvGEDYpg==}
|
||||
|
||||
'@picgo/bump-version@2.0.0':
|
||||
resolution: {integrity: sha512-7ClH+1f0vNchLOzUq7OlYXic3dIDH154aC809BIwHFkVzoj4z4YPT5TrR1syICFDCJVixg8Tvlli0z891iO0vg==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
@@ -1853,6 +1865,9 @@ packages:
|
||||
axios@0.27.2:
|
||||
resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==}
|
||||
|
||||
axios@1.13.2:
|
||||
resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==}
|
||||
|
||||
babel-polyfill@6.26.0:
|
||||
resolution: {integrity: sha512-F2rZGQnAdaHWQ8YAoeRbukc7HS9QgdgeyJ0rQDd485v9opwuPvjpPFcOOT/WmkKTdgy9ESgSPXDcTNpzrGr6iQ==}
|
||||
|
||||
@@ -3995,10 +4010,6 @@ packages:
|
||||
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
mime-db@1.50.0:
|
||||
resolution: {integrity: sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mime-db@1.52.0:
|
||||
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -4007,14 +4018,14 @@ packages:
|
||||
resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mime-types@2.1.33:
|
||||
resolution: {integrity: sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mime-types@2.1.35:
|
||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mime-types@3.0.2:
|
||||
resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
mime@2.6.0:
|
||||
resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
@@ -4454,8 +4465,8 @@ packages:
|
||||
pend@1.2.0:
|
||||
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
|
||||
|
||||
picgo@1.6.0:
|
||||
resolution: {integrity: sha512-BwWsXfx7LJEFBTCVs1QcEyhmXKqcgSemx0fZpp1Jus/LoEaybLQy8yEdEftKSf9c5BdOYfqqskQfZQ8L1p79hA==}
|
||||
picgo@1.6.2:
|
||||
resolution: {integrity: sha512-8o7ldUGHNywDX3keHeuiuSZZHt/hCyHepzzGA3Y3OdAZLIWdHI7NRjgUIQ2fsspQnkKaB4bm5xphoENhDiHuig==}
|
||||
engines: {node: '>= 20.0.0'}
|
||||
hasBin: true
|
||||
|
||||
@@ -4591,6 +4602,9 @@ packages:
|
||||
proto-list@1.2.4:
|
||||
resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
|
||||
|
||||
proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
|
||||
pseudomap@1.0.2:
|
||||
resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
|
||||
|
||||
@@ -5189,6 +5203,12 @@ packages:
|
||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
systeminformation@5.27.14:
|
||||
resolution: {integrity: sha512-3DoNDYSZBLxBwaJtQGWNpq0fonga/VZ47HY1+7/G3YoIPaPz93Df6egSzzTKbEMmlzUpy3eQ0nR9REuYIycXGg==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
|
||||
hasBin: true
|
||||
|
||||
tailwindcss@3.4.18:
|
||||
resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@@ -5619,6 +5639,10 @@ packages:
|
||||
resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==}
|
||||
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
|
||||
|
||||
write-file-atomic@7.0.0:
|
||||
resolution: {integrity: sha512-YnlPC6JqnZl6aO4uRc+dx5PHguiR9S6WeoLtpxNT9wIG+BDya7ZNE1q7KOjVgaA73hKhKLpVPgJ5QA9THQ5BRg==}
|
||||
engines: {node: ^20.17.0 || >=22.9.0}
|
||||
|
||||
xml-name-validator@4.0.0:
|
||||
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -6862,6 +6886,13 @@ snapshots:
|
||||
dependencies:
|
||||
semver: 7.7.3
|
||||
|
||||
'@numairawan/video-duration@1.0.0':
|
||||
dependencies:
|
||||
axios: 1.13.2
|
||||
mime-types: 2.1.35
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
|
||||
'@picgo/bump-version@2.0.0(@types/node@20.19.26)(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@commitlint/cli': 7.6.1
|
||||
@@ -7895,6 +7926,14 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
|
||||
axios@1.13.2:
|
||||
dependencies:
|
||||
follow-redirects: 1.15.11
|
||||
form-data: 4.0.5
|
||||
proxy-from-env: 1.1.0
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
|
||||
babel-polyfill@6.26.0:
|
||||
dependencies:
|
||||
babel-runtime: 6.26.0
|
||||
@@ -9415,7 +9454,7 @@ snapshots:
|
||||
combined-stream: 1.0.8
|
||||
es-set-tostringtag: 2.1.0
|
||||
hasown: 2.0.2
|
||||
mime-types: 2.1.33
|
||||
mime-types: 2.1.35
|
||||
|
||||
formstream@1.5.2:
|
||||
dependencies:
|
||||
@@ -10431,20 +10470,18 @@ snapshots:
|
||||
braces: 3.0.3
|
||||
picomatch: 2.3.1
|
||||
|
||||
mime-db@1.50.0: {}
|
||||
|
||||
mime-db@1.52.0: {}
|
||||
|
||||
mime-db@1.54.0: {}
|
||||
|
||||
mime-types@2.1.33:
|
||||
dependencies:
|
||||
mime-db: 1.50.0
|
||||
|
||||
mime-types@2.1.35:
|
||||
dependencies:
|
||||
mime-db: 1.52.0
|
||||
|
||||
mime-types@3.0.2:
|
||||
dependencies:
|
||||
mime-db: 1.54.0
|
||||
|
||||
mime@2.6.0: {}
|
||||
|
||||
mimic-fn@1.2.0: {}
|
||||
@@ -10862,7 +10899,7 @@ snapshots:
|
||||
|
||||
pend@1.2.0: {}
|
||||
|
||||
picgo@1.6.0:
|
||||
picgo@1.6.2:
|
||||
dependencies:
|
||||
'@picgo/i18n': 1.0.0
|
||||
'@picgo/store': 2.1.0
|
||||
@@ -10883,7 +10920,7 @@ snapshots:
|
||||
js-yaml: 4.1.1
|
||||
lodash: 4.17.21
|
||||
md5: 2.3.0
|
||||
mime-types: 2.1.33
|
||||
mime-types: 3.0.2
|
||||
minimatch: 3.1.2
|
||||
minimist: 1.2.8
|
||||
qiniu: 7.14.0
|
||||
@@ -10996,6 +11033,8 @@ snapshots:
|
||||
|
||||
proto-list@1.2.4: {}
|
||||
|
||||
proxy-from-env@1.1.0: {}
|
||||
|
||||
pseudomap@1.0.2: {}
|
||||
|
||||
pump@3.0.3:
|
||||
@@ -11672,6 +11711,8 @@ snapshots:
|
||||
|
||||
supports-preserve-symlinks-flag@1.0.0: {}
|
||||
|
||||
systeminformation@5.27.14: {}
|
||||
|
||||
tailwindcss@3.4.18:
|
||||
dependencies:
|
||||
'@alloc/quick-lru': 5.2.0
|
||||
@@ -12138,6 +12179,11 @@ snapshots:
|
||||
imurmurhash: 0.1.4
|
||||
signal-exit: 3.0.7
|
||||
|
||||
write-file-atomic@7.0.0:
|
||||
dependencies:
|
||||
imurmurhash: 0.1.4
|
||||
signal-exit: 4.1.0
|
||||
|
||||
xml-name-validator@4.0.0: {}
|
||||
|
||||
xmlbuilder@15.1.1: {}
|
||||
|
||||
@@ -10,14 +10,13 @@ import axios from 'axios'
|
||||
import windowManager from '../window/windowManager'
|
||||
import { showNotification } from '~/main/utils/common'
|
||||
import { isDev } from '~/universal/utils/common'
|
||||
import { STORE_PATH } from '~/main/utils/env'
|
||||
|
||||
// for test
|
||||
const REMOTE_NOTICE_URL = isDev ? 'http://localhost:8181/remote-notice.json' : 'https://picgo-1251750343.cos.accelerate.myqcloud.com/remote-notice.yml'
|
||||
const REMOTE_NOTICE_URL = isDev ? 'http://localhost:8181/remote-notice.json' : 'https://release.picgo.app/remote-notice.yml'
|
||||
|
||||
const REMOTE_NOTICE_LOCAL_STORAGE_FILE = 'picgo-remote-notice.json'
|
||||
|
||||
const STORE_PATH = app.getPath('userData')
|
||||
|
||||
const REMOTE_NOTICE_LOCAL_STORAGE_PATH = path.join(STORE_PATH, REMOTE_NOTICE_LOCAL_STORAGE_FILE)
|
||||
|
||||
class RemoteNoticeHandler {
|
||||
|
||||
@@ -58,7 +58,7 @@ export const uploadClipboardFiles = async (): Promise<string> => {
|
||||
}
|
||||
}
|
||||
|
||||
export const uploadChoosedFiles = async (webContents: WebContents, files: IFileWithPath[]): Promise<string[]> => {
|
||||
export const uploadSelectedFiles = async (webContents: WebContents, files: IFileWithPath[]): Promise<string[]> => {
|
||||
const input = files.map(item => item.path)
|
||||
const imgs = await uploader.setWebContents(webContents).upload(input)
|
||||
const result = []
|
||||
|
||||
@@ -11,9 +11,9 @@ import db from '~/main/apis/core/datastore'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { IWindowList } from '#/types/enum'
|
||||
import util from 'util'
|
||||
import { IPicGo } from 'picgo'
|
||||
import { showNotification, calcDurationRange, getClipboardFilePathList } from '~/main/utils/common'
|
||||
import { GET_RENAME_FILE_NAME, RENAME_FILE_NAME, TALKING_DATA_EVENT } from '~/universal/events/constants'
|
||||
import type { IPicGo } from 'picgo'
|
||||
import { showNotification, getClipboardFilePathList } from '~/main/utils/common'
|
||||
import { GET_RENAME_FILE_NAME, RENAME_FILE_NAME } from '~/universal/events/constants'
|
||||
import logger from '@core/picgo/logger'
|
||||
import { T } from '~/main/i18n'
|
||||
import fse from 'fs-extra'
|
||||
@@ -23,6 +23,7 @@ import writeFile from 'write-file-atomic'
|
||||
import { CLIPBOARD_IMAGE_FOLDER } from '~/universal/utils/static'
|
||||
import { cleanupFormUploaderFiles } from '~/main/utils/cleanupFormUploaderFiles'
|
||||
import { IpcMainEvent } from 'electron/main'
|
||||
import { dataReportManager } from '~/main/utils/dataReport'
|
||||
|
||||
const waitForRename = (window: BrowserWindow, id: number): Promise<string|null> => {
|
||||
return new Promise((resolve) => {
|
||||
@@ -39,20 +40,6 @@ const waitForRename = (window: BrowserWindow, id: number): Promise<string|null>
|
||||
})
|
||||
}
|
||||
|
||||
const handleTalkingData = (webContents: WebContents, options: IAnalyticsData) => {
|
||||
const data: ITalkingDataOptions = {
|
||||
EventId: 'upload',
|
||||
Label: options.type,
|
||||
MapKv: {
|
||||
by: options.fromClipboard ? 'clipboard' : 'files', // 上传剪贴板图片还是选择的文文件
|
||||
count: options.count, // 上传的数量
|
||||
duration: calcDurationRange(options.duration || 0), // 上传耗时
|
||||
type: options.type
|
||||
}
|
||||
}
|
||||
webContents.send(TALKING_DATA_EVENT, data)
|
||||
}
|
||||
|
||||
class Uploader {
|
||||
private webContents: WebContents | null = null
|
||||
// private uploading: boolean = false
|
||||
@@ -155,12 +142,11 @@ class Uploader {
|
||||
const output = await picgo.upload(img)
|
||||
if (Array.isArray(output) && output.some((item: ImgInfo) => item.imgUrl)) {
|
||||
if (this.webContents) {
|
||||
handleTalkingData(this.webContents, {
|
||||
dataReportManager.reportUploadData(this.webContents, {
|
||||
fromClipboard: !img,
|
||||
type: db.get('picBed.uploader') || db.get('picBed.current') || 'smms',
|
||||
count: img ? img.length : 1,
|
||||
duration: Date.now() - startTime
|
||||
} as IAnalyticsData)
|
||||
duration: Date.now() - startTime,
|
||||
outputList: output
|
||||
});
|
||||
}
|
||||
return output.filter(item => item.imgUrl)
|
||||
} else {
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
UPLOAD_WITH_CLIPBOARD_FILES,
|
||||
UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE,
|
||||
GET_WINDOW_ID,
|
||||
GET_WINDOW_ID_REPONSE,
|
||||
GET_WINDOW_ID_RESPONSE,
|
||||
GET_SETTING_WINDOW_ID,
|
||||
GET_SETTING_WINDOW_ID_RESPONSE
|
||||
} from './constants'
|
||||
@@ -56,7 +56,7 @@ export const uploadWithFiles = (pathList: IFileWithPath[]): Promise<{
|
||||
// miniWindow or settingWindow or trayWindow
|
||||
export const getWindowId = (): Promise<number> => {
|
||||
return new Promise((resolve) => {
|
||||
bus.once(GET_WINDOW_ID_REPONSE, (id: number) => {
|
||||
bus.once(GET_WINDOW_ID_RESPONSE, (id: number) => {
|
||||
resolve(id)
|
||||
})
|
||||
bus.emit(GET_WINDOW_ID)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const GET_WINDOW_ID = 'GET_WINDOW_ID' // get a current window
|
||||
export const GET_WINDOW_ID_REPONSE = 'GET_WINDOW_ID_REPONSE'
|
||||
export const GET_WINDOW_ID_RESPONSE = 'GET_WINDOW_ID_RESPONSE'
|
||||
export const GET_SETTING_WINDOW_ID = 'GET_SETTING_WINDOW_ID' // get setting window
|
||||
export const GET_SETTING_WINDOW_ID_RESPONSE = 'GET_SETTING_WINDOW_ID_RESPONSE'
|
||||
export const UPLOAD_WITH_FILES = 'UPLOAD_WITH_FILES'
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import fs from 'fs-extra'
|
||||
import writeFile from 'write-file-atomic'
|
||||
import path from 'path'
|
||||
import { app as APP } from 'electron'
|
||||
import { getLogger } from '@core/utils/localLogger'
|
||||
import dayjs from 'dayjs'
|
||||
import { T } from '~/main/i18n'
|
||||
import { FORM_IMAGE_FOLDER } from '~/universal/utils/static'
|
||||
const STORE_PATH = APP.getPath('userData')
|
||||
import { STORE_PATH } from '~/main/utils/env'
|
||||
const configFilePath = path.join(STORE_PATH, 'data.json')
|
||||
const configFileBackupPath = path.join(STORE_PATH, 'data.bak.json')
|
||||
export const defaultConfigPath = configFilePath
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import bus from '@core/bus'
|
||||
import {
|
||||
uploadClipboardFiles,
|
||||
uploadChoosedFiles
|
||||
uploadSelectedFiles
|
||||
} from 'apis/app/uploader/apis'
|
||||
import {
|
||||
createMenu
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
UPLOAD_WITH_CLIPBOARD_FILES,
|
||||
UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE,
|
||||
GET_WINDOW_ID,
|
||||
GET_WINDOW_ID_REPONSE,
|
||||
GET_WINDOW_ID_RESPONSE,
|
||||
GET_SETTING_WINDOW_ID,
|
||||
GET_SETTING_WINDOW_ID_RESPONSE,
|
||||
CREATE_APP_MENU
|
||||
@@ -39,13 +39,13 @@ async function busCallUploadClipboardFiles () {
|
||||
|
||||
async function busCallUploadFiles (pathList: IFileWithPath[]) {
|
||||
const win = windowManager.getAvailableWindow()
|
||||
const urls = await uploadChoosedFiles(win.webContents, pathList)
|
||||
const urls = await uploadSelectedFiles(win.webContents, pathList)
|
||||
bus.emit(UPLOAD_WITH_FILES_RESPONSE, urls)
|
||||
}
|
||||
|
||||
function busCallGetWindowId () {
|
||||
const win = windowManager.getAvailableWindow()
|
||||
bus.emit(GET_WINDOW_ID_REPONSE, win.id)
|
||||
bus.emit(GET_WINDOW_ID_RESPONSE, win.id)
|
||||
}
|
||||
|
||||
function busCallGetSettingWindowId () {
|
||||
|
||||
@@ -31,15 +31,14 @@ import {
|
||||
} from '#/events/constants'
|
||||
import {
|
||||
uploadClipboardFiles,
|
||||
uploadChoosedFiles
|
||||
uploadSelectedFiles
|
||||
} from '~/main/apis/app/uploader/apis'
|
||||
import picgoCoreIPC from './picgoCoreIPC'
|
||||
import { handleCopyUrl } from '~/main/utils/common'
|
||||
import { buildMainPageMenu, buildMiniPageMenu, buildPluginPageMenu, buildPicBedListMenu } from './remotes/menu'
|
||||
import path from 'path'
|
||||
import { T } from '~/main/i18n'
|
||||
|
||||
const STORE_PATH = app.getPath('userData')
|
||||
import { STORE_PATH } from '~/main/utils/env'
|
||||
|
||||
export default {
|
||||
listen () {
|
||||
@@ -74,7 +73,7 @@ export default {
|
||||
})
|
||||
|
||||
ipcMain.on('uploadChoosedFiles', async (evt: IpcMainEvent, files: IFileWithPath[]) => {
|
||||
return uploadChoosedFiles(evt.sender, files)
|
||||
return uploadSelectedFiles(evt.sender, files)
|
||||
})
|
||||
|
||||
ipcMain.on('updateShortKey', (evt: IpcMainEvent, item: IShortKeyConfig, oldKey: string, from: string) => {
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
migrateGalleryFromVersion230
|
||||
} from '~/main/migrate'
|
||||
import {
|
||||
uploadChoosedFiles,
|
||||
uploadSelectedFiles,
|
||||
uploadClipboardFiles
|
||||
} from 'apis/app/uploader/apis'
|
||||
import {
|
||||
@@ -48,7 +48,7 @@ const handleStartUpFiles = (argv: string[], cwd: string) => {
|
||||
} else {
|
||||
logger.info('cli -> uploading files from cli', ...files.map(item => item.path))
|
||||
const win = windowManager.getAvailableWindow()
|
||||
uploadChoosedFiles(win.webContents, files)
|
||||
uploadSelectedFiles(win.webContents, files)
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
} from './utils'
|
||||
import logger from '@core/picgo/logger'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis'
|
||||
import { uploadSelectedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis'
|
||||
import path from 'path'
|
||||
import { dbPathDir } from 'apis/core/datastore/dbChecker'
|
||||
const STORE_PATH = dbPathDir()
|
||||
@@ -51,7 +51,7 @@ router.post('/upload', async ({
|
||||
}
|
||||
})
|
||||
const win = windowManager.getAvailableWindow()
|
||||
const res = await uploadChoosedFiles(win.webContents, pathList)
|
||||
const res = await uploadSelectedFiles(win.webContents, pathList)
|
||||
logger.info('[PicGo Server] upload result', res.join(' ; '))
|
||||
if (res.length) {
|
||||
handleResponse({
|
||||
|
||||
@@ -3,6 +3,7 @@ import db from '~/main/apis/core/datastore'
|
||||
import { clipboard, Notification, dialog } from 'electron'
|
||||
import { handleUrlEncode } from '~/universal/utils/common'
|
||||
import { readClipboardFilePaths } from 'clip-filepaths'
|
||||
import crypto from 'node:crypto'
|
||||
|
||||
export const handleCopyUrl = (str: string): void => {
|
||||
if (db.get('settings.autoCopyUrl') !== false) {
|
||||
@@ -54,7 +55,7 @@ export const showMessageBox = (options: any) => {
|
||||
})
|
||||
}
|
||||
|
||||
export const calcDurationRange = (duration: number) => {
|
||||
export const calcUploadProcessDurationRange = (duration: number) => {
|
||||
if (duration < 1000) {
|
||||
return 500
|
||||
} else if (duration < 1500) {
|
||||
@@ -78,6 +79,44 @@ export const calcDurationRange = (duration: number) => {
|
||||
return 100000
|
||||
}
|
||||
|
||||
// 1 2 3 4 5 6 7 8 9 10 20 30 40 50 60 70 80 90 100 200 300 ...
|
||||
export const calcUploadBigFileSizeRange = (fileSizeMB: number) => {
|
||||
if (fileSizeMB < 10) {
|
||||
// 3.2 -> 3, 3.6 -> 4
|
||||
const result = Math.round(fileSizeMB);
|
||||
return result === 0 && fileSizeMB > 0 ? 1 : result;
|
||||
}
|
||||
else if (fileSizeMB < 100) {
|
||||
// 13 -> 1.3 -> 1 -> 10
|
||||
// 17 -> 1.7 -> 2 -> 20
|
||||
return Math.round(fileSizeMB / 10) * 10;
|
||||
}
|
||||
else {
|
||||
// 135 -> 1.35 -> 1 -> 100
|
||||
// 160 -> 1.60 -> 2 -> 200
|
||||
return Math.round(fileSizeMB / 100) * 100;
|
||||
}
|
||||
}
|
||||
|
||||
// 1 2 3 4 5 6 7 8 9 10 20 30 40 50 60 70 80 90 100 200 300 ...
|
||||
export const calcVideoDurationRange = (durationSec: number) => {
|
||||
if (durationSec < 10) {
|
||||
// 3.2 -> 3, 3.6 -> 4
|
||||
const result = Math.round(durationSec);
|
||||
return result === 0 && durationSec > 0 ? 1 : result;
|
||||
}
|
||||
else if (durationSec < 100) {
|
||||
// 13 -> 1.3 -> 1 -> 10
|
||||
// 17 -> 1.7 -> 2 -> 20
|
||||
return Math.round(durationSec / 10) * 10;
|
||||
}
|
||||
else {
|
||||
// 135 -> 1.35 -> 1 -> 100
|
||||
// 160 -> 1.60 -> 2 -> 200
|
||||
return Math.round(durationSec / 100) * 100;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* macOS public.file-url will get encoded file path,
|
||||
* so we need to decode it
|
||||
@@ -137,3 +176,7 @@ export const getHost = (url: string = '') => {
|
||||
export const removeProtocolAndSuffix = (url: string = '') => {
|
||||
return url.replace(/^(https?:\/\/)?/, '').replace(/\/$/, '')
|
||||
}
|
||||
|
||||
export const md5 = (str: string): string => {
|
||||
return crypto.createHash('md5').update(str).digest('hex')
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export const MB = 1024 * 1024
|
||||
export const SECOND = 1000
|
||||
@@ -0,0 +1,91 @@
|
||||
import { ipcMain, IpcMainEvent, type WebContents } from "electron";
|
||||
import { deviceIdManager } from "./deviceId";
|
||||
import { REGISTER_DEVICE_ID, TALKING_DATA_EVENT } from "~/universal/events/constants";
|
||||
import type { IImgInfo } from "picgo";
|
||||
import { calcUploadBigFileSizeRange, calcUploadProcessDurationRange, calcVideoDurationRange } from "./common";
|
||||
import { app } from "electron/main";
|
||||
import db from "~/main/apis/core/datastore";
|
||||
import fs from 'fs-extra'
|
||||
// @ts-ignore
|
||||
import { videoDuration } from "@numairawan/video-duration";
|
||||
import { MB, SECOND } from "./constants";
|
||||
|
||||
export interface IReportUploadDataOptions {
|
||||
fromClipboard: boolean;
|
||||
duration: number;
|
||||
outputList: IImgInfo[];
|
||||
}
|
||||
|
||||
class DataReportManager {
|
||||
private deviceId: string | null = null;
|
||||
private hasRegisterDeviceID: boolean = false;
|
||||
constructor () {
|
||||
this.init()
|
||||
this.handleRegisterDeviceID()
|
||||
}
|
||||
private handleRegisterDeviceID() {
|
||||
ipcMain.once(REGISTER_DEVICE_ID, (_evt: IpcMainEvent) => {
|
||||
this.hasRegisterDeviceID = true;
|
||||
console.log('Device ID registered');
|
||||
});
|
||||
}
|
||||
private async init () {
|
||||
if (this.deviceId) return;
|
||||
this.deviceId = await deviceIdManager.getId()
|
||||
}
|
||||
public async reportUploadData(webContents: WebContents, options: IReportUploadDataOptions) {
|
||||
await this.init();
|
||||
await this.registerDeviceID(webContents);
|
||||
const { fromClipboard, duration, outputList } = options;
|
||||
const fileList = outputList.map(item => {
|
||||
return {
|
||||
fileName: item.fileName,
|
||||
filePath: item.filePath,
|
||||
mimeType: item.mimeType
|
||||
}
|
||||
})
|
||||
const uploadEventData: ITalkingDataOptions = {
|
||||
EventId: 'upload',
|
||||
Label: '',
|
||||
MapKv: {
|
||||
by: fromClipboard ? 'clipboard' : 'files', // 上传剪贴板图片还是选择的文文件
|
||||
count: fileList.length, // 上传的数量
|
||||
duration: calcUploadProcessDurationRange(duration || 0), // 上传耗时
|
||||
type: db.get('picBed.uploader') || db.get('picBed.current') || 'smms',
|
||||
deviceId: this.deviceId,
|
||||
version: app.getVersion()
|
||||
}
|
||||
}
|
||||
webContents.send(TALKING_DATA_EVENT, uploadEventData)
|
||||
fileList.forEach(async file => {
|
||||
if (file?.mimeType?.startsWith('video/') && file.filePath) {
|
||||
const [stats, metadata] = await Promise.all([
|
||||
fs.stat(file.filePath),
|
||||
videoDuration(file.filePath)
|
||||
]);
|
||||
// report video duration and size range
|
||||
const sizeMB = stats.size / MB;
|
||||
const durationSec = (metadata.ms / SECOND) || 0;
|
||||
const videoEventData: ITalkingDataOptions = {
|
||||
EventId: 'upload_video',
|
||||
Label: '',
|
||||
MapKv: {
|
||||
deviceId: this.deviceId,
|
||||
sizeRange: calcUploadBigFileSizeRange(sizeMB),
|
||||
durationRange: calcVideoDurationRange(durationSec),
|
||||
version: app.getVersion()
|
||||
}
|
||||
};
|
||||
webContents.send(TALKING_DATA_EVENT, videoEventData);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async registerDeviceID(webContents: WebContents) {
|
||||
if (this.hasRegisterDeviceID) return;
|
||||
await this.init();
|
||||
webContents.send(REGISTER_DEVICE_ID, this.deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
export const dataReportManager = new DataReportManager()
|
||||
@@ -0,0 +1,62 @@
|
||||
import { networkInterfaceDefault } from 'systeminformation'
|
||||
import { md5 } from './common'
|
||||
import writeFile from 'write-file-atomic'
|
||||
import { DEVICE_ID_PATH } from './env'
|
||||
import fs from 'fs-extra'
|
||||
|
||||
class DeviceIdManager {
|
||||
private deviceId: string | null = null
|
||||
|
||||
constructor() {
|
||||
this.init()
|
||||
}
|
||||
|
||||
private async init() {
|
||||
await this.loadDeviceId()
|
||||
}
|
||||
|
||||
private async getDeviceIdWithFallback(): Promise<string> {
|
||||
try {
|
||||
if (fs.existsSync(DEVICE_ID_PATH)) {
|
||||
const deviceId = await fs.readFile(DEVICE_ID_PATH, 'utf-8')
|
||||
if (deviceId && deviceId?.trim().length > 0) {
|
||||
return deviceId.trim()
|
||||
}
|
||||
}
|
||||
const netInterfaceMac = await networkInterfaceDefault()
|
||||
if (netInterfaceMac) {
|
||||
return md5(netInterfaceMac)
|
||||
} else {
|
||||
// random fallback
|
||||
return md5(`${new Date().getTime()}-${Math.random().toString(36).substring(2, 15)}`)
|
||||
}
|
||||
} catch (error) {
|
||||
// random fallback
|
||||
return md5(`${new Date().getTime()}-${Math.random().toString(36).substring(2, 15)}`)
|
||||
}
|
||||
}
|
||||
|
||||
private async saveDeviceId(id: string): Promise<void> {
|
||||
try {
|
||||
await writeFile(DEVICE_ID_PATH, id, { encoding: 'utf-8' })
|
||||
} catch (error) {
|
||||
console.error('Failed to save device ID:', error)
|
||||
}
|
||||
}
|
||||
|
||||
private async loadDeviceId(): Promise<string> {
|
||||
this.deviceId = await this.getDeviceIdWithFallback()
|
||||
await this.saveDeviceId(this.deviceId)
|
||||
return this.deviceId
|
||||
}
|
||||
|
||||
public async getId(): Promise<string> {
|
||||
if (this.deviceId) {
|
||||
return this.deviceId
|
||||
}
|
||||
this.deviceId = await this.loadDeviceId()
|
||||
return this.deviceId
|
||||
}
|
||||
}
|
||||
|
||||
export const deviceIdManager = new DeviceIdManager()
|
||||
@@ -29,3 +29,7 @@ export const buildRendererUrl = (hash?: string) => {
|
||||
}
|
||||
|
||||
export const getStaticPath = () => process.env.STATIC_PATH || defaultStaticPath
|
||||
|
||||
// paths
|
||||
export const STORE_PATH = app.getPath('userData')
|
||||
export const DEVICE_ID_PATH = path.join(STORE_PATH, 'picgo-device-id')
|
||||
|
||||
@@ -31,7 +31,6 @@ app.config.globalProperties.$builtInPicBed = [
|
||||
'aliyun',
|
||||
'github'
|
||||
]
|
||||
app.config.unwrapInjectedRef = true
|
||||
|
||||
app.config.globalProperties.$$db = db
|
||||
app.config.globalProperties.$http = axios
|
||||
|
||||
@@ -66,7 +66,7 @@ import { reactive, ref, onBeforeUnmount, onBeforeMount } from 'vue'
|
||||
import { ipcRenderer } from 'electron'
|
||||
import $$db from '@/utils/db'
|
||||
import { T as $T } from '@/i18n/index'
|
||||
import { IResult } from '@picgo/store/dist/types'
|
||||
import type { IResult } from '@picgo/store/dist/types'
|
||||
import { PASTE_TEXT, OPEN_WINDOW } from '#/events/constants'
|
||||
import { IWindowList } from '#/types/enum'
|
||||
import { sendToMain } from '@/utils/dataSender'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
TALKING_DATA_APPID, TALKING_DATA_EVENT
|
||||
REGISTER_DEVICE_ID,
|
||||
TALKING_DATA_APPID, TALKING_DATA_DEVICE_ID_EVENT, TALKING_DATA_EVENT
|
||||
} from '~/universal/events/constants'
|
||||
import pkg from 'root/package.json'
|
||||
import { ipcRenderer } from 'electron'
|
||||
@@ -10,7 +11,7 @@ export const initTalkingData = () => {
|
||||
setTimeout(() => {
|
||||
const talkingDataScript = document.createElement('script')
|
||||
|
||||
talkingDataScript.src = `http://sdk.talkingdata.com/app/h5/v1?appid=${TALKING_DATA_APPID}&vn=${version}&vc=${version}`
|
||||
talkingDataScript.src = `https://jic.talkingdata.com/app/h5/v1?appid=${TALKING_DATA_APPID}&vn=${version}&vc=${version}`
|
||||
|
||||
const head = document.getElementsByTagName('head')[0]
|
||||
head.appendChild(talkingDataScript)
|
||||
@@ -20,3 +21,16 @@ export const initTalkingData = () => {
|
||||
ipcRenderer.on(TALKING_DATA_EVENT, (_, data: ITalkingDataOptions) => {
|
||||
handleTalkingDataEvent(data)
|
||||
})
|
||||
// 0:ANONYMOUS,匿名账号;
|
||||
// 1:REGISTERED,自有帐户显性注册;
|
||||
ipcRenderer.on(TALKING_DATA_DEVICE_ID_EVENT, (_, deviceId: string) => {
|
||||
window.TDAPP.register({
|
||||
profileId: deviceId,
|
||||
profileType: 1
|
||||
})
|
||||
window.TDAPP.login({
|
||||
profileId: deviceId,
|
||||
profileType: 1
|
||||
})
|
||||
ipcRenderer.send(REGISTER_DEVICE_ID)
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ export const SHOW_INPUT_BOX_RESPONSE = 'SHOW_INPUT_BOX_RESPONSE'
|
||||
export const TOGGLE_SHORTKEY_MODIFIED_MODE = 'TOGGLE_SHORTKEY_MODIFIED_MODE'
|
||||
export const TALKING_DATA_APPID = '7E6832BCE3F1438696579E541DFEBFDA'
|
||||
export const TALKING_DATA_EVENT = 'TALKING_DATA_EVENT'
|
||||
export const TALKING_DATA_DEVICE_ID_EVENT = 'TALKING_DATA_DEVICE_ID_EVENT'
|
||||
export const SHOW_PRIVACY_MESSAGE = 'SHOW_PRIVACY_MESSAGE'
|
||||
export const PICGO_SAVE_CONFIG = 'PICGO_SAVE_CONFIG'
|
||||
export const PICGO_GET_CONFIG = 'PICGO_GET_CONFIG'
|
||||
@@ -37,6 +38,7 @@ export const OPEN_WINDOW = 'OPEN_WINDOW'
|
||||
export const GET_PICBEDS = 'GET_PICBEDS'
|
||||
export const RPC_ACTIONS = 'RPC_ACTIONS'
|
||||
export const GET_PICBED_CONFIG = 'GET_PICBED_CONFIG'
|
||||
export const REGISTER_DEVICE_ID = 'REGISTER_DEVICE_ID'
|
||||
// i18n
|
||||
export const GET_CURRENT_LANGUAGE = 'GET_CURRENT_LANGUAGE'
|
||||
export const GET_LANGUAGE_LIST = 'GET_LANGUAGE_LIST'
|
||||
|
||||
Vendored
+8
@@ -19,6 +19,14 @@ declare global {
|
||||
electronApi: ElectronApi
|
||||
TDAPP: {
|
||||
onEvent: (EventId: string, Label?: string, MapKv?: IStringKeyMap) => void
|
||||
register: (opt: {
|
||||
profileId: string,
|
||||
profileType: number,
|
||||
}) => void
|
||||
login: (opt: {
|
||||
profileId: string,
|
||||
profileType: number,
|
||||
}) => void
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user