first commit

This commit is contained in:
编码猿
2024-09-27 01:07:59 +08:00
commit 6cd216628a
162 changed files with 21879 additions and 0 deletions

30
.babelrc Executable file
View File

@@ -0,0 +1,30 @@
{
"comments": false,
"env": {
"main": {
"presets": [
["env", {
"targets": { "node": 7 }
}],
"stage-0"
]
},
"renderer": {
"presets": [
["env", {
"modules": false
}],
"stage-0"
]
},
"web": {
"presets": [
["env", {
"modules": false
}],
"stage-0"
]
}
},
"plugins": ["transform-runtime"]
}

1
.electron-vue/.pydio Normal file
View File

@@ -0,0 +1 @@
10fa0628-4dc5-4c15-b1da-9c73cdfe350a

115
.electron-vue/build.js Executable file
View File

@@ -0,0 +1,115 @@
'use strict'
process.env.NODE_ENV = 'production'
const { say } = require('cfonts')
const chalk = require('chalk')
const del = require('del')
const { spawn } = require('child_process')
const webpack = require('webpack')
const Multispinner = require('multispinner')
const mainConfig = require('./webpack.main.config')
const rendererConfig = require('./webpack.renderer.config')
const doneLog = chalk.bgGreen.white(' DONE ') + ' '
const errorLog = chalk.bgRed.white(' ERROR ') + ' '
const okayLog = chalk.bgBlue.white(' OKAY ') + ' '
const isCI = process.env.CI || false
if (process.env.BUILD_TARGET === 'clean') clean()
else build()
function clean () {
del.sync(['build/*', '!build/icons', '!build/icons/icon.*'])
console.log(`\n${doneLog}\n`)
process.exit()
}
function build () {
greeting()
del.sync(['dist/electron/*', '!.gitkeep'])
const tasks = ['main', 'renderer']
const m = new Multispinner(tasks, {
preText: 'building',
postText: 'process'
})
let results = ''
m.on('success', () => {
process.stdout.write('\x1B[2J\x1B[0f')
console.log(`\n\n${results}`)
console.log(`${okayLog}take it away ${chalk.yellow('`electron-builder`')}\n`)
process.exit()
})
pack(mainConfig).then(result => {
results += result + '\n\n'
m.success('main')
}).catch(err => {
m.error('main')
console.log(`\n ${errorLog}failed to build main process`)
console.error(`\n${err}\n`)
process.exit(1)
})
pack(rendererConfig).then(result => {
results += result + '\n\n'
m.success('renderer')
}).catch(err => {
m.error('renderer')
console.log(`\n ${errorLog}failed to build renderer process`)
console.error(`\n${err}\n`)
process.exit(1)
})
}
function pack (config) {
return new Promise((resolve, reject) => {
config.mode = 'production'
webpack(config, (err, stats) => {
if (err) reject(err.stack || err)
else if (stats.hasErrors()) {
let err = ''
stats.toString({
chunks: false,
colors: true
})
.split(/\r?\n/)
.forEach(line => {
err += ` ${line}\n`
})
reject(err)
} else {
resolve(stats.toString({
chunks: false,
colors: true
}))
}
})
})
}
function greeting () {
const cols = process.stdout.columns
let text = ''
if (cols > 85) text = 'lets-build'
else if (cols > 60) text = 'lets-|build'
else text = false
if (text && !isCI) {
say(text, {
colors: ['yellow'],
font: 'simple3d',
space: false
})
} else console.log(chalk.yellow.bold('\n lets-build'))
console.log()
}

40
.electron-vue/dev-client.js Executable file
View File

@@ -0,0 +1,40 @@
const hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
hotClient.subscribe(event => {
/**
* Reload browser when HTMLWebpackPlugin emits a new index.html
*
* Currently disabled until jantimon/html-webpack-plugin#680 is resolved.
* https://github.com/SimulatedGREG/electron-vue/issues/437
* https://github.com/jantimon/html-webpack-plugin/issues/680
*/
// if (event.action === 'reload') {
// window.location.reload()
// }
/**
* Notify `mainWindow` when `main` process is compiling,
* giving notice for an expected reload of the `electron` process
*/
if (event.action === 'compiling') {
document.body.innerHTML += `
<style>
#dev-client {
background: #4fc08d;
border-radius: 4px;
bottom: 20px;
box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.3);
color: #fff;
font-family: 'Source Sans Pro', sans-serif;
left: 20px;
padding: 8px 12px;
position: absolute;
}
</style>
<div id="dev-client">
Compiling Main Process...
</div>
`
}
})

190
.electron-vue/dev-runner.js Executable file
View File

@@ -0,0 +1,190 @@
'use strict'
const chalk = require('chalk')
const electron = require('electron')
const path = require('path')
const { say } = require('cfonts')
const { spawn } = require('child_process')
const webpack = require('webpack')
const WebpackDevServer = require('webpack-dev-server')
const webpackHotMiddleware = require('webpack-hot-middleware')
const mainConfig = require('./webpack.main.config')
const rendererConfig = require('./webpack.renderer.config')
let electronProcess = null
let manualRestart = false
let hotMiddleware
function logStats (proc, data) {
let log = ''
log += chalk.yellow.bold(`${proc} Process ${new Array((19 - proc.length) + 1).join('-')}`)
log += '\n\n'
if (typeof data === 'object') {
data.toString({
colors: true,
chunks: false
}).split(/\r?\n/).forEach(line => {
log += ' ' + line + '\n'
})
} else {
log += ` ${data}\n`
}
log += '\n' + chalk.yellow.bold(`${new Array(28 + 1).join('-')}`) + '\n'
console.log(log)
}
function startRenderer () {
return new Promise((resolve, reject) => {
rendererConfig.entry.renderer = [path.join(__dirname, 'dev-client')].concat(rendererConfig.entry.renderer)
rendererConfig.mode = 'development'
const compiler = webpack(rendererConfig)
hotMiddleware = webpackHotMiddleware(compiler, {
log: false,
heartbeat: 2500
})
compiler.hooks.compilation.tap('compilation', compilation => {
compilation.hooks.htmlWebpackPluginAfterEmit.tapAsync('html-webpack-plugin-after-emit', (data, cb) => {
hotMiddleware.publish({ action: 'reload' })
cb()
})
})
compiler.hooks.done.tap('done', stats => {
logStats('Renderer', stats)
})
const server = new WebpackDevServer(
compiler,
{
contentBase: path.join(__dirname, '../'),
quiet: true,
before (app, ctx) {
app.use(hotMiddleware)
ctx.middleware.waitUntilValid(() => {
resolve()
})
}
}
)
server.listen(9080)
})
}
function startMain () {
return new Promise((resolve, reject) => {
mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main)
mainConfig.mode = 'development'
const compiler = webpack(mainConfig)
compiler.hooks.watchRun.tapAsync('watch-run', (compilation, done) => {
logStats('Main', chalk.white.bold('compiling...'))
hotMiddleware.publish({ action: 'compiling' })
done()
})
compiler.watch({}, (err, stats) => {
if (err) {
console.log(err)
return
}
logStats('Main', stats)
if (electronProcess && electronProcess.kill) {
manualRestart = true
process.kill(electronProcess.pid)
electronProcess = null
startElectron()
setTimeout(() => {
manualRestart = false
}, 5000)
}
resolve()
})
})
}
function startElectron () {
var args = [
'--inspect=5858',
path.join(__dirname, '../dist/electron/main.js')
]
// detect yarn or npm and process commandline args accordingly
if (process.env.npm_execpath.endsWith('yarn.js')) {
args = args.concat(process.argv.slice(3))
} else if (process.env.npm_execpath.endsWith('npm-cli.js')) {
args = args.concat(process.argv.slice(2))
}
electronProcess = spawn(electron, args)
electronProcess.stdout.on('data', data => {
electronLog(data, 'blue')
})
electronProcess.stderr.on('data', data => {
electronLog(data, 'red')
})
electronProcess.on('close', () => {
if (!manualRestart) process.exit()
})
}
function electronLog (data, color) {
let log = ''
data = data.toString().split(/\r?\n/)
data.forEach(line => {
log += ` ${line}\n`
})
if (/[0-9A-z]+/.test(log)) {
console.log(
chalk[color].bold('┏ Electron -------------------') +
'\n\n' +
log +
chalk[color].bold('┗ ----------------------------') +
'\n'
)
}
}
function greeting () {
const cols = process.stdout.columns
let text = ''
if (cols > 104) text = 'electron-vue'
else if (cols > 76) text = 'electron-|vue'
else text = false
if (text) {
say(text, {
colors: ['yellow'],
font: 'simple3d',
space: false
})
} else console.log(chalk.yellow.bold('\n electron-vue'))
console.log(chalk.blue(' getting ready...') + '\n')
}
function init () {
greeting()
Promise.all([startRenderer(), startMain()])
.then(() => {
startElectron()
})
.catch(err => {
console.error(err)
})
}
init()

View File

@@ -0,0 +1,71 @@
'use strict'
process.env.BABEL_ENV = 'main'
const path = require('path')
const { dependencies } = require('../package.json')
const webpack = require('webpack')
const fs= require('fs')
// 检测是否有用户配置文件,没有的话根据模板生成一个
const userConfigPath=path.resolve(__dirname,'../.user-config.js')
if(!fs.existsSync(userConfigPath)){
fs.copyFileSync(path.resolve(__dirname,'../user-config.template.js'), userConfigPath)
}
let mainConfig = {
entry: {
main: path.join(__dirname, '../src/main/index.js')
},
externals: [
...Object.keys(dependencies || {})
],
module: {
rules: [
{
test: /\.node$/,
use: 'node-loader'
},
]
},
node: {
__dirname: process.env.NODE_ENV !== 'production',
__filename: process.env.NODE_ENV !== 'production'
},
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../dist/electron')
},
plugins: [
new webpack.NoEmitOnErrorsPlugin()
],
resolve: {
extensions: ['.js', '.json', '.node']
},
target: 'electron-main'
}
/**
* Adjust mainConfig for development settings
*/
if (process.env.NODE_ENV !== 'production') {
mainConfig.plugins.push(
new webpack.DefinePlugin({
'__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
})
)
}
/**
* Adjust mainConfig for production settings
*/
if (process.env.NODE_ENV === 'production') {
mainConfig.plugins.push(
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
})
)
}
module.exports = mainConfig

View File

@@ -0,0 +1,175 @@
'use strict'
process.env.BABEL_ENV = 'renderer'
const path = require('path')
const { dependencies } = require('../package.json')
const webpack = require('webpack')
// const BabiliWebpackPlugin = require('babili-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
function resolve(dir) {
return path.join(__dirname, '..', dir)
}
/**
* List of node_modules to include in webpack bundle
*
* Required for specific packages like Vue UI libraries
* that provide pure *.vue files that need compiling
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals
*/
// let whiteListedModules = ['vue']
let whiteListedModules = ['vue', 'vue-router', 'axios', 'vuex', 'vue-electron']
let rendererConfig = {
devtool: '#cheap-module-eval-source-map',
entry: {
renderer: path.join(__dirname, '../src/renderer/main.js')
},
externals: [
...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d))
],
module: {
rules: [
{
test: /\.less$/,
use: ['vue-style-loader', 'css-loader', 'less-loader']
},
{
test: /\.css$/,
use: ['vue-style-loader', 'css-loader']
},
{
test: /\.html$/,
use: 'vue-html-loader'
},
{
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src')],
options: {
formatter: require('eslint-friendly-formatter'),
emitWarning: true
}
},
{
test: /\.vue$/,
use: {
loader: 'vue-loader',
options: {
extractCSS: process.env.NODE_ENV === 'production',
loaders: {
less: 'vue-style-loader!css-loader!less-loader'
}
}
}
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'imgs/[name]--[folder].[ext]'
}
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'media/[name]--[folder].[ext]'
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'fonts/[name]--[folder].[ext]'
}
}
}
]
},
node: {
__dirname: process.env.NODE_ENV !== 'production',
__filename: process.env.NODE_ENV !== 'production'
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: 'styles.css'
}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, '../src/index.ejs'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true
},
nodeModules: process.env.NODE_ENV !== 'production' ?
path.resolve(__dirname, '../node_modules') :
false
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
],
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../dist/electron')
},
resolve: {
alias: {
'@': path.join(__dirname, '../src/renderer'),
'vue$': 'vue/dist/vue.esm.js',
'static': path.join(__dirname, '../static')
},
extensions: ['.js', '.vue', '.json', '.css', '.node']
},
target: 'electron-renderer'
}
/**
* Adjust rendererConfig for development settings
*/
if (process.env.NODE_ENV !== 'production') {
rendererConfig.plugins.push(
new webpack.DefinePlugin({
'__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
})
)
}
/**
* Adjust rendererConfig for production settings
*/
if (process.env.NODE_ENV === 'production') {
rendererConfig.devtool = ''
rendererConfig.plugins.push(
new CopyWebpackPlugin([{
from: path.join(__dirname, '../static'),
to: path.join(__dirname, '../dist/electron/static'),
ignore: ['.*']
}]),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
)
}
module.exports = rendererConfig

3
.eslintignore Executable file
View File

@@ -0,0 +1,3 @@
/*.ejs
src/out
docs/*

112
.eslintrc.js Executable file
View File

@@ -0,0 +1,112 @@
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint'
},
"env": {
"browser": true,
"commonjs": true,
"es6": true,
"browser": true,
"node": true,
"amd": true
},
extends: ['plugin:vue/essential', 'airbnb-base'],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly'
},
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
parser: "babel-eslint"
},
plugins: ['vue', 'html'],
// check if imports actually resolve
settings: {
'import/resolver': {
webpack: {
config: '.electron-vue/webpack.renderer.config.js'
}
}
},
rules: {
// 句尾分号可以省略
'semi': ['error', 'never'],
// 代码中console/debugger处理
'no-console': 'off',
'no-debugger': 'off',
// 代码使用4个空格的缩进风格
'indent': ['error', 4],
// 关闭命名function表达式规则
'func-names': 'off',
// 可以行尾空白
'no-trailing-spaces': 'off',
// 关闭拖尾逗号
'comma-dangle': 'off',
// 关闭换行符转换
'linebreak-style': 'off',
// 禁止使用指定语法
'no-restricted-syntax': ['error', 'WithStatement'],
// 关闭语句块之前的空格保持一致
'space-before-blocks': 'off',
// 可以使用++/--
'no-plusplus': 'off',
// 禁止未使用过的变量包括全局变量和函数中的最后一个参数必须使用
'no-unused-vars': [
'error', {
'vars': 'all',
'args': 'after-used'
}
],
// 使用单引号
'quotes': [
'error', 'single'
],
// 强制最大可嵌深度为8
'max-depth': 0,
// 强制函数块中的语句最大50行
'max-statements': [
'error', 50
],
// 强制行的最大长度150,注释200
'max-len': [
'error', {
'code': 200,
'comments': 800
}
],
// NodeJs rules 9.0之后全部使用import
// 关闭require()强制在模块顶部调用
'global-require': 'off',
// ES6 rules
// 箭头函数的箭头前后都要有空格
'arrow-spacing': 'error',
// 接收const被修改的通知
'no-const-assign': 'error',
// 要求使用let或const而不是var
'no-var': 'error',
// 如果一个变量不会被重新赋值则使用const声明
'prefer-const': 'error',
// 关闭强制在花括号内使用一致的换行符
'object-curly-newline': 'off',
// 链接地址中可以使用 javascript:
'no-script-url': 'off',
// 关闭点击元素上强制增加onKey**事件
'click-events-have-key-events': 'off',
// 关闭引用依赖检查
'import/no-extraneous-dependencies': 'off',
// 关闭路径处理依赖
'import/no-cycle': 'off',
"no-param-reassign": 0, //禁止给参数重新赋值
"no-use-before-define": 0,
"no-unused-vars": 'off',
"no-underscore-dangle":0,
"brace-style":0,
// 扩展名处理
'import/extensions': 'off',
}
};

8
.gitignore vendored Executable file
View File

@@ -0,0 +1,8 @@
.DS_Store
dist/*
build/*
node_modules/
npm-debug.log.*
thumbs.db
.user-config.js
!.gitkeep

1
.pydio Normal file
View File

@@ -0,0 +1 @@
ff5d96d1-0826-4699-a12c-e5cd85fb0c37

36
.travis.yml Executable file
View File

@@ -0,0 +1,36 @@
osx_image: xcode8.3
sudo: required
dist: trusty
language: c
matrix:
include:
- os: osx
- os: linux
env: CC=clang CXX=clang++ npm_config_clang=1
compiler: clang
cache:
directories:
- node_modules
- "$HOME/.electron"
- "$HOME/.cache"
addons:
apt:
packages:
- libgnome-keyring-dev
- icnsutils
before_install:
- mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v1.2.1/git-lfs-$([
"$TRAVIS_OS_NAME" == "linux" ] && echo "linux" || echo "darwin")-amd64-1.2.1.tar.gz
| tar -xz -C /tmp/git-lfs --strip-components 1 && /tmp/git-lfs/git-lfs pull
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils; fi
install:
- nvm install 7
- curl -o- -L https://yarnpkg.com/install.sh | bash
- source ~/.bashrc
- npm install -g xvfb-maybe
- yarn
script:
- yarn run build
branches:
only:
- master

1
.vscode/.pydio vendored Normal file
View File

@@ -0,0 +1 @@
a2beb42a-53d7-4c5b-b4be-7f513fcdfdd8

21
.vscode/launch.json vendored Executable file
View File

@@ -0,0 +1,21 @@
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Electron Main",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
"program": "${workspaceFolder}/src/main/index.js"
},
// {
// "type": "node",
// "request": "launch",
// "name": "Launch Program",
// "program": "${workspaceFolder}/src/get-image/500px.js"
// }
]
}

12
.vscode/settings.json vendored Executable file
View File

@@ -0,0 +1,12 @@
{
// 忽略的文件
"csscomb.ignoreFilesOnSave": [
".git/**",
"node_modules/**",
"dist/**",
"dll/**",
],
// 规则路径,也可以为一个对象自定义规则
"csscomb.preset": "csscomb.js",
"csscomb.formatOnSave": true,
}

21
LICENSE Executable file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 peng
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

119
README.md Executable file
View File

@@ -0,0 +1,119 @@
## Strawberry Wallpaper 草莓壁纸🍓
> 采用electron-vue开发的壁纸应用
> 本应用相关图片都源自于网络,壁纸图片的一切版权归壁纸来源网站所有,切不可将版权图片作为商用。
#### 开发
```
npm install
npm run dev
```
#### 打包
1.先执行webpack打包页面
```
npm run build
```
2.按平台打包 `electron-builder` 会默认当前平台进行打包
```
npm run build-mac
npm run build-win
```
## 使用说明
### 1.适用范围
Works on macOS 10.12+, and Windows 10+.
目前只在mac10.14.3与win10上进行了测试
#### 1.安装软件
提供两个打包好的下载地址:
[Strawberry Wallpaper-mac](http://sw.taoacat.com/Strawberry%20Wallpaper-mac.dmg)
[Strawberry Wallpaper-win](http://sw.taoacat.com/Strawberry%20Wallpaper-win.exe)
macOS可以使用[homebrew](https://brew.sh/index_zh-cn)进行安装:
```bash
brew cask install strawberry-wallpaper
```
#### 2.软件介绍
【软件主页】
用于显示壁纸照片,可以切换图库,可以进行关键词搜索,并可存储最近搜索的关键词。
<img src="http://file.qiniu.taoacat.com/stwallpaper-1.png" width="300px" />
【设置】
<img src="http://file.qiniu.taoacat.com/stwallpaper-2.png" width="300px" />
【检测更新】
<img src="http://file.qiniu.taoacat.com/stwallpaper-3.png" width="500px" />
【意见反馈】
<img src="http://file.qiniu.taoacat.com/stwallpaper-4.png" width="500px" />
【公告】
<img src="http://file.qiniu.taoacat.com/stwallpaper-5.png" width="500px" />
【网页模式】
可在网页中对图库图片进行搜索、下载、设置为壁纸
<img src="http://file.qiniu.taoacat.com/stwallpaper-6.png" width="700px" />
## 更新日志
> v1.4.2
- 修复1.4.1已知问题
> v1.4.1
- 修复新安装用户不能设置壁纸问题
> v1.4
- 修复pexels图库加载不成功问题
- 增加设置壁纸的填充方式mac下有效
- 增加是否自动应用到所有屏幕设置mac下有效
- 增加壁纸方向与尺寸的筛选
- 增加网页模式,网页模式下加载相关网页,并可直接在网页中可以下载图片到本地以及设置壁纸
- 增加公告展示
[历史更新](./update-log.md)
## 用户统计
草莓壁纸会针对用户安装量以及用户的活跃量进行统计根据用户的网卡信息生成唯一UID。统计信息只获取用户的电脑系统用户电脑用户名。
可以打开[草莓壁纸后台统计平台](http://sw.taoacat.com)进行查看相关统计数据。后台统计相关接口采用go语言开发[项目地址](https://github.com/wangkaibo/strawberry-wallpaper)https://github.com/wangkaibo/strawberry-wallpaper 欢迎点赞,支持。
## LICENSE
MIT
## 点👍
如果觉得本项目不错,请点击页面右上角的的小星星。也可以为作者点👍,欢迎打赏。
![👍](http://file.qiniu.taoacat.com/github-start.png)
<img src="http://file.qiniu.taoacat.com/wechat-money.png" width="200px" />
## 感谢
- vue
- electron
- vue-electron
- ...

29
appveyor.yml Executable file
View File

@@ -0,0 +1,29 @@
version: 0.1.{build}
branches:
only:
- master
image: Visual Studio 2017
platform:
- x64
cache:
- node_modules
- '%APPDATA%\npm-cache'
- '%USERPROFILE%\.electron'
- '%USERPROFILE%\AppData\Local\Yarn\cache'
init:
- git config --global core.autocrlf input
install:
- ps: Install-Product node 8 x64
- git reset --hard HEAD
- yarn
- node --version
build_script:
- yarn build
test: off

1
build-config/.pydio Normal file
View File

@@ -0,0 +1 @@
ed5f7899-add2-43b2-aff7-2c63e4603fff

View File

@@ -0,0 +1 @@
2e10cfaa-9570-4d45-8179-9a1fbb20ed05

BIN
build-config/icons/256x256.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
build-config/icons/icon.icns Executable file

Binary file not shown.

BIN
build-config/icons/icon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

208
csscomb.js Executable file
View File

@@ -0,0 +1,208 @@
module.exports = {
exclude: [
'.git/**',
'node_modules/**',
'bower_components/**',
'dist/**',
'dll/**',
'src/css/**'
],
'always-semicolon': true, // 总是显示分号
'block-indent': ' ', // 代码块缩进,可以是数字或字符串与空白和制表符等
'color-case': 'lower', // 十六进制颜色统一,可选值'lower'全部小写;'upper'全部大写
'color-hex-case': 'lower', // 颜色值统一小写
'color-shorthand': false, // 十六进制颜色缩写与否
'element-case': 'lower', // 选择器元素统一,可选值'lower'全部小写;'upper'全部大写
'eof-newline': true, // 文件结束后添换行
'leading-zero': true, // 是否需要小数点前的0
quotes: 'double', // 引号风格,可选值'single'单引号,'double'双引号
'remove-empty-rulesets': true, // 是否移除空规则集为true时'a{ }'这样的空规则集将被移除
'space-after-colon': ' ', // 冒号后规则
'space-after-combinator': ' ', // 选择符后规则
'space-between-declarations': '\n', // 属性后的规则
'space-after-opening-brace': '\n', // '{' 之后的规则
'space-after-selector-delimiter': '\n', // 选择器之后的规则
'space-before-closing-brace': '\n', // '}' 之后的规则
'space-before-colon': '', // 冒号前的规则
'space-before-combinator': ' ', // 选择符前规则
'space-before-opening-brace': ' ', // '{' 之前的规则
'space-before-selector-delimiter': '', // 选择器之前的规则
'strip-spaces': true, // 是否修剪尾随的空格
'tab-size': 4, // 缩进大小
'unitless-zero': true, // 是否移除0后的单位值比如'0px'格式化为'0'
'vendor-prefix-align': true, // 是否对齐属性和值中的前缀
'lines-between-rulesets': 1, // 规则与规则之间的换行数
'sort-order': [
// Directions about where and how the box is placed
// ['@include'],
'display',
'grid',
'grid-area',
'grid-auto-flow',
'grid-auto-columns',
'grid-auto-rows',
'grid-gap',
'grid-column',
'grid-column-start',
'grid-column-end',
'grid-column-gap',
'grid-row',
'grid-row-start',
'grid-row-end',
'grid-row-gap',
'grid-template',
'grid-template-areas',
'grid-template-columns',
'grid-template-rows',
'flex',
'flex-basis',
'flex-direction',
'flex-flow',
'flex-grow',
'flex-shrink',
'flex-wrap',
'align-content',
'align-items',
'align-self',
'justify-self',
'justify-content',
'order',
'position',
'top',
'right',
'bottom',
'left',
'columns',
'column-gap',
'column-fill',
'column-rule',
'column-span',
'column-count',
'column-width',
'float',
'clear',
'transform',
'transform-origin',
// can the box be seen?
'visibility',
'opacity',
'z-index',
// Layers of the box model, from outside to inside
'margin',
'margin-top',
'margin-right',
'margin-bottom',
'margin-left',
'outline',
'border',
'border-top',
'border-right',
'border-bottom',
'border-left',
'border-width',
'border-top-width',
'border-right-width',
'border-bottom-width',
'border-left-width',
'border-style',
'border-top-style',
'border-right-style',
'border-bottom-style',
'border-left-style',
'border-color',
'border-top-color',
'border-right-color',
'border-bottom-color',
'border-left-color',
'border-radius',
'border-top-left-radius',
'border-top-right-radius',
'border-bottom-left-radius',
'border-bottom-right-radius',
'box-shadow',
'box-sizing',
// Content dimensions and background and scrollbars
'background',
'background-clip',
'background-color',
'background-image',
'background-position',
'background-repeat',
'background-size',
'cursor',
'width',
'min-width',
'max-width',
'height',
'min-height',
'max-height',
'overflow',
'overflow-x',
'overflow-y',
// (Padding after dimensions because of `box-sizing: border-box`)
'padding',
'padding-top',
'padding-right',
'padding-bottom',
'padding-left',
// Special content types: lists, tables
'list-style',
'caption-side',
'table-layout',
'border-collapse',
'border-spacing',
'empty-cells',
// Textual content
'vertical-align',
'text-align',
'text-decoration',
'text-indent',
'text-overflow',
'text-rendering',
'text-shadow',
'text-transform',
'line-height',
'word-spacing',
'letter-spacing',
'white-space',
'color',
'font',
'font-family',
'font-size',
'font-weight',
'font-style',
'font-smoothing',
'content',
'quotes',
// Transitions change previously defined properties
'transition',
'transition-property',
'transition-duration',
'transition-timing-function',
'transition-delay'
]
}

0
docs/.nojekyll Executable file
View File

1
docs/.pydio Normal file
View File

@@ -0,0 +1 @@
bea3cae9-474d-415a-a5d3-98173ba38eac

41
docs/README.md Executable file
View File

@@ -0,0 +1,41 @@
## Strawberry Wallpaper 草莓壁纸 🍓
### 一款追求更好用的桌面壁纸APP
<img src="./img/bk1.png" width="100%" />
###### `Strawberry Wallpaper` 是一款针对macOS以及windows的一款精美壁纸应用。自动更新各大图库的高清壁纸非常适合寻找各种美的图片的你。
##### 功能:
- 目前支持pexels、500px、paper、unsplash、wallhaven、NASA每日一图、 themoviedb(类似豆瓣)
- 同时支持macOS与windows
- 支持壁纸预览,通过缩略图就可预览众多壁纸,同时对壁纸的尺寸做了明显的标记
- 支持关键词搜索,想要什么样的图片一触即得
- 支持定时自动设置壁纸
- 支持方向、尺寸筛选,
- 支持设置壁纸平铺方式mac系统
- 支持网页模式,在浏览原生网页的时候也可以设置壁纸
- 对设置过的壁纸存放到固定目录,方便随时查看
- 长期更新、完全免费
##### 亮点:
###### 简单而又不失优雅的UI设计仿Pap.er
<img src="./img/bk.png" width="100%" />
###### 支持不同图库的自由切换以及支持自动定时更新壁纸
<img src="./img/bk2.png" width="100%" />
###### 支持网页模式
可在网页中对图库图片进行搜索、下载、设置为壁纸
<img src="http://file.qiniu.taoacat.com/stwallpaper-6.png" width="700px" />
##### [项目地址](https://github.com/aitexiaoy/Strawberry-Wallpaper)
##### [Windows下载](http://sw.taoacat.com/Strawberry%20Wallpaper-win.exe)
##### [Mac下载](http://sw.taoacat.com/Strawberry%20Wallpaper-mac.dmg)

10
docs/_coverpage.md Executable file
View File

@@ -0,0 +1,10 @@
# Strawberry Wallpaper
> 一款非常好用的桌面壁纸APP
[GitHub](https://github.com/aitexiaoy/Strawberry-Wallpaper)
[Get Started](#quick-start)
![](./img/bk.png)
![color](#f0f0f0)

8
docs/_sidebar.md Executable file
View File

@@ -0,0 +1,8 @@
* 开发笔记
* [如何利用Node进行图片的抓取](zh-cn/reptile.md)
* [将图片保存到本地](zh-cn/save-image.md)
* [如何设置壁纸](zh-cn/set-wallpaper.md)
* [项目搭建](zh-cn/start.md)
* [主窗口定位-tarbar](zh-cn/tarbar.md)
* [如何进行远程升级](zh-cn/updata.md)
* [遇到的问题](zh-cn/problem.md)

1
docs/img/.pydio Normal file
View File

@@ -0,0 +1 @@
6ab41161-5a2a-45ad-b5a8-89b0c8421b13

BIN
docs/img/bk.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 KiB

BIN
docs/img/bk1.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 KiB

BIN
docs/img/bk2.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

BIN
docs/img/logo.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
docs/img/stwallpaper-1.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

BIN
docs/img/stwallpaper-2.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

BIN
docs/img/stwallpaper-3.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

BIN
docs/img/stwallpaper-4.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
docs/img/stwallpaper-5.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

BIN
docs/img/stwallpaper-6.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

213
docs/index.html Executable file
View File

@@ -0,0 +1,213 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>strawberrywallpaper - a wallpaper app by electron</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css">
<link rel="stylesheet" href="//unpkg.com/vuep/dist/vuep.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="icon" type="image/x-icon" class="js-site-favicon" href="./image/favicon.ico">
<!-- 引入element样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<style>
.sidebar-nav-parent-li {
position: relative;
}
.sidebar-nav {
padding-right: 10px;
}
.sidebar-nav .ul-after {
content: '';
width: 10px;
height: 30px;
/* background: red; */
position: absolute;
right: 10px;
top: 0px;
display: flex;
align-items: center;
}
.ul-after-sanjiao {
width: 0;
height: 0;
border-left: 8px solid #cecece;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
transition: transform 0.3s;
position: relative;
}
.ul-after-sanjiao::after {
content: '';
width: 0;
height: 0;
position: absolute;
top: -8px;
left: -11px;
border-left: 8px solid #fff;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
}
.ul-after-sanjiao:hover {
border-left: 8px solid #42b983;
}
.sidebar-nav-parent-li-shouqi .ul-after-sanjiao {
transform: rotate(90deg);
transition: transform 0.3s;
}
.return-top {
position: fixed;
bottom: 20px;
right: 20px;
display: none;
width: 50px;
height: 50px;
border-radius: 100%;
background: #d8d7d7;
display: flex;
text-align: center;
cursor: pointer;
}
.return-top-div {
margin-left: 5px;
width: 40px;
height: 40px;
margin-top: 5px;
font-size: 14px;
color: #5f5959;
line-height: 16px;
display: flex;
align-items: center;
justify-content: center;
}
.return-top:hover {
background: #42b983;
}
.return-top:hover .return-top-div {
font-size: 18px;
color: #fff;
}
</style>
</head>
<body>
<nav></nav>
<div id="app">loading...</div>
<!-- vue文件在线编辑 -->
<script src="//unpkg.com/vue/dist/vue.min.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- 引入组件库 -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="//unpkg.com/docsify-demo-box-vue/dist/docsify-demo-box-vue.min.js"></script>
<!-- 在线编辑运行Vue的插件 -->
<script src="//unpkg.com/vuep/dist/vuep.min.js"></script>
<script>
// 引入docsify-demo-box-vue
var jsResources = '<scr' + 'ipt src="//unpkg.com/vue/dist/vue.js"></scr' + 'ipt>'
var cssResources = '@import url("//cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.min.css");'
var bootCode = 'var globalVariable = "leon"'
var globalVariable = "leon"
window.$docsify = {
// name: 'strawberry wallpaper',
loadNavbar: true,
maxLevel: 6,
auto2top: true,
autoHeader: true,
// loadSidebar: true,
subMaxLevel: 3,
// disqus: 'yangpeng',
// 搜索完整配置参数
search: {
placeholder: '请输入关键字',
noData: '暂无结果',
// 搜索标题的最大程级, 1 - 6
depth: 6
},
plugins: [
DemoBoxVue.create(jsResources, cssResources, bootCode),
function (hook) {
hook.doneEach(function () {
//回到顶部
let return_top = $('<div class="return-top"><div class="return-top-div">顶</div></div>')
$('html').append(return_top);
$(window).scroll(function () {
var scroll = $(window).scrollTop();
if (scroll > 1000) {
$(return_top).show();
} else {
$(return_top).hide();
}
})
return_top.on('click', function (e) {
$('html,body').animate({
scrollTop: 0
}, 500)
})
})
}
]
}
</script>
<script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
<!-- 复制到剪切板 -->
<script src="https://unpkg.com/docsify-copy-code@2"></script>
<!-- 搜索 -->
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
<!-- 图片放大 -->
<script src="//unpkg.com/docsify/lib/plugins/zoom-image.js"></script>
<script src="//unpkg.com/docsify/lib/plugins/external-script.min.js"></script>
<!-- Disqus 评论系统-->
<!-- <script src="//unpkg.com/docsify/lib/plugins/disqus.min.js"></script> -->
<!-- 分页导航插件 -->
<!-- <script src="//unpkg.com/docsify-pagination/dist/docsify-pagination.min.js"></script> -->
<!-- 引入代码块的颜色支持 -->
<script src="//unpkg.com/prismjs/components/prism-bash.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-json.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-javascript.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-diff.min.js"></script>
<!-- <script src="//unpkg.com/docsify/lib/plugins/ga.min.js"></script> -->
</body>
</html>

1
docs/zh-cn/.pydio Normal file
View File

@@ -0,0 +1 @@
d51c4cfd-204c-4d1f-b63f-975d8241cdaa

30
docs/zh-cn/problem.md Executable file
View File

@@ -0,0 +1,30 @@
### 软件在window下会存在重复运行的问题
即双击打开软件在windows下会创建一个新的运行程序。
#### 解决方法
```js
// 创建程序锁,保证只能打开单个实例
if (isWin()) {
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
app.quit()
} else {
app.on('second-instance', (event, commandLine, workingDirectory) => {
if (mainWindow) {
if (!mainWindow.isVisible()) {
mainWindowShow()
}
mainWindow.focus()
}
})
}
}
```
### win.setPosition方法报错
##### 原因setPosition方法中的参数不支持浮点数只支持整数
### 远程升级相关问题
#### 1.远程升级只能在build后生效
#### 2.mac端远程升级需要development ID 到官网申请
#### 3.安装最新的electron-update后运行提示缺少`whenReady()`方法,那是因为`electron-vue`的`electron`版本为2.7的而`whenReady()`为electron3.0.0更新的。解决办法就是升级electron

8
docs/zh-cn/reptile.md Executable file
View File

@@ -0,0 +1,8 @@
### 分析网页接口
> 在此处拿https://www.pexels.com 的图库进行分析
pexels为一个面版权的图库网站
### 模拟请求
### 处理DOM

94
docs/zh-cn/save-image.md Executable file
View File

@@ -0,0 +1,94 @@
#### 文件代码在 src/file.js
起初采用axios进行图片的下载但是axios在服务端不支持进度条最后又换成了request请求。
```js
export const downloadPic = async function (src, mainWindow) {
// 返回一个Promise方便处理
return new Promise((resolve, reject) => {
// 先检测目录,没有相关目录的话会自动创建新的目录
mkdirSync(hostdir)
// 自动重命名
let dstpath = `${hostdir}/${(new Date()).getTime()}_${(new Date()).getMilliseconds()}`
// webp格式的图片标志500px下载的图片为webp格式的
let isWebp = false
// 判断图片格式
if (src.match('webp=true')) {
dstpath += '.webp'
isWebp = true
} else {
dstpath += '.jpg'
isWebp = false
}
// 已经接受的文件的大小
let receivedBytes = 0
// 文件总的字大小
let totalBytes = 0
// 创建一个文件流
const writeStream = fs.createWriteStream(dstpath, {
autoClose: true
})
// 创建一个请求
myRequest = request({
url: src,
headers: browserHeader,
})
myRequest.pipe(writeStream)
myRequest.on('response', (data) => {
// 更新总文件字节大小
totalBytes = parseInt(data.headers['content-length'], 10)
})
myRequest.on('data', (chunk) => {
// 更新下载的文件块字节大小
receivedBytes += chunk.length
// 更新进度条
mainWindow.webContents.send('datainfo', {
type: 'updaterProgress',
data: parseFloat(((receivedBytes / totalBytes) * 100))
})
})
myRequest.on('finish', () => {
myRequest = null
})
myRequest.on('error', () => {
reject()
})
writeStream.on('finish', () => {
writeStream.end()
myRequest = null
if (receivedBytes === totalBytes){
if (isWebp) {
// 将webp图片转成jpg图片
webp.dwebp(dstpath, dstpath.replace('webp', 'jpg'), '-o', (status) => {
// status 101->fails || 100->successful
if (status === '100'){
fs.unlink(dstpath, (err) => {
if (err) throw err
// console.log('文件已删除')
})
resolve(dstpath.replace('webp', 'jpg'))
} else {
reject()
}
})
} else {
resolve(dstpath)
}
} else {
fs.unlink(dstpath, (err) => {
if (err) throw err
})
// 更新进度条
mainWindow.webContents.send('datainfo', {
type: 'updaterProgress',
data: 0
})
reject()
}
})
})
}
```

1
docs/zh-cn/set-wallpaper.md Executable file
View File

@@ -0,0 +1 @@
不支持

1
docs/zh-cn/start.md Executable file
View File

@@ -0,0 +1 @@
xiangmudajian

79
docs/zh-cn/tarbar.md Executable file
View File

@@ -0,0 +1,79 @@
#### 定位
```js
appTray.on('click', (event, bounds, position) => {
// mainWindow === null ? createWindow() : mainWindow.close()
// return
// 点击时显示窗口,并修改窗口的显示位置
function setMainWinPosition(win, trayBounds) {
try {
const { screen } = electron
const winWidth = mainWindow.getSize()[0]
const winHeight = mainWindow.getSize()[1]
const cursorPosition = screen.getCursorScreenPoint()
const currentScreen = screen.getDisplayNearestPoint(cursorPosition)
const screens = screen.getAllDisplays()
let screenWidth = 0
// 这目前判断多屏都是横着拼的多屏,
for (let i = 0; i < screens.length; i++) {
screenWidth += screens[i].workAreaSize.width
}
cursorPosition.x = trayBounds.x + trayBounds.width / 2
const parallelType = cursorPosition.x < screenWidth / 2 ? 'left' : 'right'
const verticaType = cursorPosition.y < currentScreen.workAreaSize.height / 2 ? 'top' : 'bottom'
let trayPositionType = '' // 任务栏的位置
let trayPositionSize = 0
if (currentScreen.workAreaSize.height < currentScreen.size.height) {
trayPositionType = verticaType === 'top' ? 'top' : 'bottom'
trayPositionSize = currentScreen.size.height - currentScreen.workAreaSize.height
} else if (currentScreen.workAreaSize.width < currentScreen.size.width) {
trayPositionType = parallelType === 'left' ? 'left' : 'right'
trayPositionSize = currentScreen.size.width - currentScreen.workAreaSize.width
}
let winPositionX = 1
let winPositionY = 1
if (trayPositionType === 'top') {
winPositionX = Math.max(Math.min(screenWidth - winWidth, cursorPosition.x - (winWidth / 2)), 1)
winPositionY = trayBounds.height + 2
} else if (trayPositionType === 'bottom') {
winPositionX = Math.max(Math.min(screenWidth - winWidth, cursorPosition.x - (winWidth / 2)), 1)
winPositionY = currentScreen.size.height - trayPositionSize - winHeight
} else if (trayPositionType === 'left') {
winPositionX = trayPositionSize
winPositionY = Math.max(Math.min(currentScreen.size.height - winHeight, cursorPosition.y - (winHeight / 2)), 1)
} else if (trayPositionType === 'right') {
winPositionX = screenWidth - trayPositionSize - winWidth
winPositionY = Math.max(Math.min(currentScreen.size.height - winHeight, cursorPosition.y - (winHeight / 2)), 1)
}
win.setPosition(parseInt(winPositionX, 10), winPositionY)
} catch (error) {
log.error(error)
}
}
try {
if (mainWindow.isVisible()) {
mainWindow.webContents.send('datainfo', {
type: 'windowShow',
data: false
})
mainWindowHide()
} else {
mainWindow.webContents.send('datainfo', {
type: 'windowShow',
data: true
})
mainWindowShow()
setMainWinPosition(mainWindow, bounds)
}
} catch (error) {
log.error(error)
}
})
```

3
docs/zh-cn/updata.md Executable file
View File

@@ -0,0 +1,3 @@
### 远程升级
采用`electron-updater`模块

1
icons/.pydio Normal file
View File

@@ -0,0 +1 @@
20ff6b22-6de6-486e-829b-9760a83e63dd

BIN
icons/icon_128x128.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

BIN
icons/icon_128x128@2x.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
icons/icon_16x16.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 957 B

BIN
icons/icon_16x16@2x.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
icons/icon_256x256.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
icons/icon_256x256@2x.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
icons/icon_32x32.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
icons/icon_512x512.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
icons/icon_512x512@2x.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

13237
package-lock.json generated Executable file

File diff suppressed because it is too large Load Diff

127
package.json Executable file
View File

@@ -0,0 +1,127 @@
{
"name": "Strawberry Wallpaper",
"version": "v1.4.2",
"author": "taoacat流浪记",
"description": "a wallpaper app by electron",
"license": "",
"main": "./dist/electron/main.js",
"bin": "cli.js",
"scripts": {
"build-r": "node .electron-vue/build.js",
"build-win": "electron-builder --win",
"build-mac": "electron-builder --mac",
"build": "npm run build-r && npm run build-win && npm run build-mac",
"build:dir": "cross-env node .electron-vue/build.js && electron-builder --mac --dir",
"clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js",
"dev": "node .electron-vue/dev-runner.js",
"pack": "npm run pack:main && npm run pack:renderer",
"pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js",
"pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js",
"postinstall": "",
"docs": "docsify serve docs"
},
"build": {
"productName": "Strawberry Wallpaper",
"appId": "9Y7KR6Q842.com.taoacat.wallpaperapplications",
"directories": {
"output": "build"
},
"publish": {
"provider": "generic",
"url": "http://sw.taoacat.com/version/"
},
"files": [
"dist/electron/**/*"
],
"mac": {
"icon": "build-config/icons/icon.icns",
"target": [
"dmg",
"zip"
]
},
"win": {
"icon": "build-config/icons/icon.ico",
"requestedExecutionLevel": "highestAvailable",
"target": [
"nsis",
"zip"
]
},
"linux": {
"icon": "build-config/icons"
}
},
"devDependencies": {
"@babel/node": "^7.10.1",
"ajv": "^6.5.0",
"babel-eslint": "^10.0.1",
"cfonts": "^2.1.2",
"chalk": "^2.4.1",
"copy-webpack-plugin": "^4.5.1",
"cross-env": "^5.1.6",
"css-loader": "^3.0.0",
"del": "^3.0.0",
"devtron": "^1.4.0",
"drop-console-webpack-plugin": "^3.0.2",
"electron": "^9.0.4",
"electron-builder": "^22.7.0",
"electron-debug": "^2.1.0",
"electron-devtools-installer": "^2.2.4",
"eslint": "^5.16.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-friendly-formatter": "^4.0.1",
"eslint-import-resolver-webpack": "^0.11.0",
"eslint-loader": "^2.1.2",
"eslint-plugin-html": "^5.0.3",
"eslint-plugin-import": "^2.17.2",
"eslint-plugin-node": "^8.0.1",
"eslint-plugin-promise": "^4.1.1",
"eslint-plugin-vue": "^5.2.2",
"file-loader": "^1.1.11",
"html-webpack-plugin": "^3.2.0",
"less": "^3.9.0",
"less-loader": "^5.0.0",
"mini-css-extract-plugin": "0.4.0",
"multispinner": "^0.2.1",
"node-loader": "^0.6.0",
"sass-loader": "^7.0.3",
"style-loader": "^0.21.0",
"url-loader": "^1.0.1",
"vue-html-loader": "^1.2.4",
"vue-loader": "^15.2.4",
"vue-style-loader": "^4.1.0",
"vue-template-compiler": "^2.6.7",
"webpack": "^4.15.1",
"webpack-cli": "^3.0.8",
"webpack-dev-server": "^3.11.0",
"webpack-hot-middleware": "^2.22.2"
},
"dependencies": {
"auto-launch": "^5.0.5",
"axios": "^0.18.0",
"cheerio": "^1.0.0-rc.2",
"custom-electron-titlebar": "^3.2.2-hotfix62",
"electron-log": "^3.0.1",
"electron-updater": "^4.0.6",
"element-ui": "^2.5.4",
"fs": "^0.0.1-security",
"http2-client": "^1.3.3",
"jsdom": "^13.2.0",
"macaddress": "^0.2.9",
"node-os-utils": "^1.0.7",
"nodemailer": "^5.1.1",
"path": "^0.12.7",
"progress": "^2.0.3",
"qs": "^6.7.0",
"request": "^2.88.0",
"vue": "^2.6.10",
"vue-electron": "^1.0.6",
"vue-router": "^3.0.1",
"vuex": "^3.0.1",
"vuex-electron": "^1.0.0",
"wallpaper": "^4.2.0",
"wangeditor": "^3.1.1",
"webp-converter": "^2.2.2"
}
}

1
src/.pydio Normal file
View File

@@ -0,0 +1 @@
3c55ee6b-00f5-48af-a641-b8fc0b4cc892

1
src/api/.pydio Normal file
View File

@@ -0,0 +1 @@
9d8d83fd-0709-4fcf-8e6c-08e6596dfee3

105
src/api/api.js Executable file
View File

@@ -0,0 +1,105 @@
import userConfig from '../../.user-config.js'
const axios = require('axios')
const { baiDuTranslationAppId, baiDuTranslationAppKey } = userConfig
const { MD5: swMd5 } = require('./baidu-md5')
const { apiBaseUrl } = require('../utils/config')
const instance = axios.create({
baseURL: apiBaseUrl,
timeout: 1000,
})
/**
* 第一次的时候注册
*/
export const postRegister = data => new Promise((resolve, reject) => {
instance({
url: '/register',
method: 'post',
data,
}).then((res) => {
const { data: result } = res
if (result.code === 0 || result.code === 400){
resolve()
}
else {
reject()
}
}).catch((error) => {
reject(error)
})
})
/**
* 统计用户的使用情况
*/
export const apiStatisticActive = data => instance.post('/active', data)
export const apiGetNotices = () => new Promise((resolve) => {
instance.get('/notice', {
params: {
is_test: 1
}
}).then((res) => {
const { data: result } = res
if (result.code === 0){
resolve(result.data || [])
}
else {
resolve([])
}
}).catch(() => { resolve([]) })
})
/**
* 百度翻译接口,将用户搜索的中文转成英文
*/
export const apiTranslation = val => new Promise((resolve, reject) => {
if (val === ''){
resolve('')
return
}
// 如果包含数字就直接返回搜索值
// eslint-disable-next-line no-restricted-globals
if (!isNaN(Number(val))){
resolve(val)
return
}
const appid = baiDuTranslationAppId
const key = baiDuTranslationAppKey
const salt = (new Date()).getTime()
const query = val
// 多个query可以用\n连接 如 query='apple\norange\nbanana\npear'
const from = 'zh'
const to = 'en'
const str1 = appid + query + salt + key
const sign = swMd5(str1)
axios({
url: 'http://api.fanyi.baidu.com/api/trans/vip/translate',
method: 'get',
params: {
q: val,
appid,
salt,
from,
to,
sign
}
}).then((result) => {
const { trans_result: transResult } = result.data
if (transResult && transResult.length > 0){
const { dst = '' } = transResult[0]
resolve(dst.toLocaleLowerCase())
}
else {
resolve('')
}
}).catch(() => {
resolve('')
})
})

205
src/api/baidu-md5.js Executable file
View File

@@ -0,0 +1,205 @@
/* eslint-disable */
export const MD5 = function (string) {
function RotateLeft(lValue, iShiftBits) {
return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
}
function AddUnsigned(lX, lY) {
let lX4; var lY4; var lX8; var lY8; var lResult;
lX8 = (lX & 0x80000000);
lY8 = (lY & 0x80000000);
lX4 = (lX & 0x40000000);
lY4 = (lY & 0x40000000);
lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
if (lX4 & lY4) {
return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
}
if (lX4 | lY4) {
if (lResult & 0x40000000) {
return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
}
return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
}
return (lResult ^ lX8 ^ lY8);
}
function F(x, y, z) { return (x & y) | ((~x) & z); }
function G(x, y, z) { return (x & z) | (y & (~z)); }
function H(x, y, z) { return (x ^ y ^ z); }
function I(x, y, z) { return (y ^ (x | (~z))); }
function FF(a, b, c, d, x, s, ac) {
a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
return AddUnsigned(RotateLeft(a, s), b);
}
function GG(a, b, c, d, x, s, ac) {
a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
return AddUnsigned(RotateLeft(a, s), b);
}
function HH(a, b, c, d, x, s, ac) {
a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
return AddUnsigned(RotateLeft(a, s), b);
}
function II(a, b, c, d, x, s, ac) {
a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
return AddUnsigned(RotateLeft(a, s), b);
}
function ConvertToWordArray(string) {
let lWordCount;
const lMessageLength = string.length;
const lNumberOfWords_temp1 = lMessageLength + 8;
const lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
const lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
const lWordArray = Array(lNumberOfWords - 1);
let lBytePosition = 0;
let lByteCount = 0;
while (lByteCount < lMessageLength) {
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
lByteCount++;
}
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
return lWordArray;
}
function WordToHex(lValue) {
let WordToHexValue = ''; let WordToHexValue_temp = ''; let lByte; let
lCount;
for (lCount = 0; lCount <= 3; lCount++) {
lByte = (lValue >>> (lCount * 8)) & 255;
// eslint-disable-next-line camelcase
WordToHexValue_temp = `0${lByte.toString(16)}`;
WordToHexValue += WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2);
}
return WordToHexValue;
}
function Utf8Encode(string) {
string = string.replace(/\r\n/g, '\n');
let utftext = '';
for (let n = 0; n < string.length; n++) {
const c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
}
else if ((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
// eslint-disable-next-line no-array-constructor
let x = Array();
let k; let AA; let BB; let CC; let DD; let a; let b; let c; let
d;
const S11 = 7; const S12 = 12; const S13 = 17; const
S14 = 22;
const S21 = 5; const S22 = 9; const S23 = 14; const
S24 = 20;
const S31 = 4; const S32 = 11; const S33 = 16; const
S34 = 23;
const S41 = 6; const S42 = 10; const S43 = 15; const
S44 = 21;
string = Utf8Encode(string);
x = ConvertToWordArray(string);
a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;
for (k = 0; k < x.length; k += 16) {
AA = a; BB = b; CC = c; DD = d;
a = FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
d = FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
c = FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
b = FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
a = FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
d = FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
c = FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
b = FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
a = FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
d = FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
c = FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
b = FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
a = FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
d = FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
c = FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
b = FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
a = GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
d = GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
c = GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
b = GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
a = GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
d = GG(d, a, b, c, x[k + 10], S22, 0x2441453);
c = GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
b = GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
a = GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
d = GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
c = GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
b = GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
a = GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
d = GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
c = GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
b = GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
a = HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
d = HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
c = HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
b = HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
a = HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
d = HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
c = HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
b = HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
a = HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
d = HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
c = HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
b = HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
a = HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
d = HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
c = HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
b = HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
a = II(a, b, c, d, x[k + 0], S41, 0xF4292244);
d = II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
c = II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
b = II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
a = II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
d = II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
c = II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
b = II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
a = II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
d = II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
c = II(c, d, a, b, x[k + 6], S43, 0xA3014314);
b = II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
a = II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
d = II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
c = II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
b = II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
a = AddUnsigned(a, AA);
b = AddUnsigned(b, BB);
c = AddUnsigned(c, CC);
d = AddUnsigned(d, DD);
}
const temp = WordToHex(a) + WordToHex(b) + WordToHex(c) + WordToHex(d);
return temp.toLowerCase();
}

1
src/file/.pydio Normal file
View File

@@ -0,0 +1 @@
de8c304e-5871-4f4d-928d-b9ec94969b87

24
src/file/auto-open.js Executable file
View File

@@ -0,0 +1,24 @@
/*
* @Description: 设置开机启动
* @Author: --
* @Date: 2019-02-19 10:51:01
* @LastEditTime: 2019-04-09 09:48:22
*/
const AutoLaunch = require('auto-launch')
const minecraftAutoLauncher = new AutoLaunch({
name: 'Strawberry Wallpaper',
})
export const openAutoStart = function (){
return minecraftAutoLauncher.enable() // 设置开机自动启动
}
export const openDisStart = function (){
return minecraftAutoLauncher.disable() // 禁止开机自动启动
}
export const openType = async function (){
await minecraftAutoLauncher.isEnabled()
}

154
src/file/file.js Executable file
View File

@@ -0,0 +1,154 @@
/**
* @Description: 下载图片并存到指定文件夹
* @Author: --
* @Date: 2019-01-24 21:35:23
* @LastEditTime: 2019-04-09 09:39:45
*/
const fs = require('fs')
const path = require('path')
const request = require('request')
const webp = require('webp-converter')
const { md5_32: md5 } = require('../utils/md5')
/**
* @type {String} 图片下载地址
*/
const { browserHeader } = require('../utils/config')
/**
* @type {Object} 保存当前的请求对象
*/
let myRequest = null
let currentSaveFilePath = ''
/**
* 创建指定路径文件
* @param {String} dirname
*/
export function mkdirSync(dirname) {
try {
if (fs.existsSync(dirname)) {
return true
}
if (mkdirSync(path.dirname(dirname))) {
fs.mkdirSync(dirname)
return true
}
return false
} catch (error) {
return false
}
}
/**
* 从指定连接下图片
* @param {*} src 下载图片的绝对地址
* @param {*} sendData 发送消息
* @param {Object} userConfig 用户配置
*/
export const downloadPic = async function (src, sendData, userConfig) {
return new Promise((resolve, reject) => {
// 创建文件夹
const hostdir = userConfig.downloadImagePath
// 文件名
const fileName = md5(src)
mkdirSync(hostdir)
let dstpath = `${hostdir}/SW-${fileName}`
let isWebp = false
// 如图图片已经下载完成了
if (fs.existsSync(`${dstpath}.jpg`)){
resolve(`${dstpath}.jpg`)
return
}
if (src.match('webp=true')) {
dstpath += '.webp'
isWebp = true
} else {
dstpath += '.jpg'
isWebp = false
}
currentSaveFilePath = dstpath
let receivedBytes = 0
let totalBytes = 0
const writeStream = fs.createWriteStream(dstpath, {
autoClose: true
})
myRequest = request({
url: src,
headers: browserHeader,
timeout: 120000 // 120s
})
myRequest.pipe(writeStream)
myRequest.on('response', (data) => {
// 更新总文件字节大小
totalBytes = parseInt(data.headers['content-length'], 10)
})
myRequest.on('data', (chunk) => {
// 更新下载的文件块字节大小
receivedBytes += chunk.length
// console.log(receivedBytes, totalBytes)
sendData('datainfo', {
type: 'updaterProgress',
data: parseFloat(((receivedBytes / totalBytes) * 100))
})
})
myRequest.on('error', () => {
deleteDownLoadFile(dstpath)
reject()
})
writeStream.on('finish', () => {
writeStream.end()
myRequest = null
if (receivedBytes === totalBytes) {
if (isWebp) {
webp.dwebp(dstpath, dstpath.replace('webp', 'jpg'), '-o', (status) => {
// status 101->fails || 100->successful
if (status === '100') {
deleteDownLoadFile(dstpath)
resolve(dstpath.replace('webp', 'jpg'))
} else {
reject()
}
})
} else {
resolve(dstpath)
}
} else {
deleteDownLoadFile(dstpath)
reject()
}
})
})
}
/**
* 取消下载
*/
export const cancelDownloadPic = function () {
return new Promise((resolve) => {
if (myRequest) {
myRequest.abort()
deleteDownLoadFile(currentSaveFilePath)
}
resolve()
})
}
function deleteDownLoadFile(filePath){
try {
// 取消下载的时候删除图片
if (fs.existsSync(filePath)){
fs.unlink(filePath, (err) => {
if (err) {
console.log('图片已删除')
}
})
}
} catch (error) {
console.log(error)
}
}

1
src/get-image/.pydio Normal file
View File

@@ -0,0 +1 @@
db0822f1-20bc-4004-a84d-3b55ee4d9c18

131
src/get-image/500px.js Executable file
View File

@@ -0,0 +1,131 @@
/**
* pexels网站 https://500px.com/
*/
// 搜索
// https://api.500px.com/v1/photos/search?type=photos&term=cat&image_size%5B%5D=1&image_size%5B%5D=2&image_size%5B%5D=32&image_size%5B%5D=31&image_size%5B%5D=33&image_size%5B%5D=34&image_size%5B%5D=35&image_size%5B%5D=36&image_size%5B%5D=2048&image_size%5B%5D=4&image_size%5B%5D=14&include_states=true&formats=jpeg%2Clytro&include_tags=true&exclude_nude=true&page=1&rpp=50
// 热门
// https: //api.500px.com/v1/photos?rpp=50&feature=popular&image_size%5B%5D=1&image_size%5B%5D=2&image_size%5B%5D=32&image_size%5B%5D=31&image_size%5B%5D=33&image_size%5B%5D=34&image_size%5B%5D=35&image_size%5B%5D=36&image_size%5B%5D=2048&image_size%5B%5D=4&image_size%5B%5D=14&sort=&include_states=true&include_licensing=true&formats=jpeg%2Clytro&only=&exclude=&personalized_categories=&page=1&rpp=50
const axios = require('axios')
const cheerio = require('cheerio')
const { axiosGet } = require('../utils/axios')
const { CancelToken } = axios
let source = null
let cookies = ''
let csrToken = ''
let htmlAddress = ''
/**
* 先获取页面取得cookie等信息
* @param {*} data
*/
function getHtmlPage(data) {
return new Promise((resolve, reject) => {
if (data.searchKey) {
htmlAddress = `https://500px.com/search?submit=%E6%8F%90%E4%BA%A4&q=${data.searchKey}&type=photos`
} else {
htmlAddress = 'https://500px.com/popular'
}
axios.get(htmlAddress).then((result) => {
cookies = result.headers['set-cookie']
const $ = cheerio.load(result.data)
csrToken = $('meta[name="csrf-token"]')[0].attribs.content
resolve()
}).catch((error) => {
console.log('-------------------500px获取cookie出错')
reject(error)
})
})
}
export const getImage = async function (data) {
if (cookies === '') {
await getHtmlPage(data)
}
return new Promise(async (resolve, reject) => {
if (!data) {
resolve([])
}
let baseUrl = 'https://api.500px.com/v1/photos'
const params = {
image_size: [100, 200, 400, 600, 1600, 2048, 2500, 3000, 4096, 4500, 5120, 5500, 6144, 7168],
page: data.page,
rpp: 50, // 单页条数
formats: 'jpeg,lytro'
}
if (data.searchKey) {
baseUrl = 'https://api.500px.com/v1/photos/search'
params.type = 'photos'
params.term = data.searchKey
params.include_tags = true
params.exclude_nude = true
} else {
baseUrl = 'https://api.500px.com/v1/photos'
params.feature = 'popular'
params.only = 'All photographers,Pulse'
params.include_licensing = true
params.include_states = true
}
source = CancelToken.source()
axiosGet({
url: baseUrl,
params,
responseType: 'json',
headers: {
Accept: 'application/json',
Origin: 'https://500px.com',
Referer: htmlAddress,
'User-Agent': 'Mozilla/5.0 (Macintosh Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',
'x-csrf-token': null,
cookie: cookies
},
cancelToken: source.token
}).then((result) => {
source = null
const urls = []
const { photos } = result
photos.forEach((item) => {
const obj = {
width: item.width,
height: item.height,
url: '',
downloadUrl: '',
}
const { images } = item
let maxSize = 0
for (let i = 0; i < images.length; i++) {
if (images[i].size >= 200 && images[i].size <= 700) {
obj.url = images[i].https_url
}
const realSize = images[i].https_url.match(/m%3D(\d*)/)
if (realSize && realSize[1]){
if (parseInt(realSize[1], 10) > maxSize) {
obj.downloadUrl = images[i].https_url
}
maxSize = parseInt(realSize[1], 10)
}
}
urls.push(obj)
})
resolve(urls)
}).catch((err) => {
source = null
console.log('------------请求失败500px:', baseUrl)
reject()
})
})
}
export const cancelImage = function () {
if (source) {
source.cancel()
}
}

73
src/get-image/nasa.js Executable file
View File

@@ -0,0 +1,73 @@
/* eslint-disable max-len */
const BASEPARAMS = {
size: 50,
from: 0,
sort: 'promo-date-time:desc',
q: '((ubernode-type:image) AND (routes:1446))',
_source_include: 'promo-date-time,master-image,nid,title,topics,missions,collections,other-tags,ubernode-type,primary-tag,secondary-tag,cardfeed-title,type,collection-asset-link,link-or-attachment,pr-leader-sentence,image-feature-caption,attachments,uri'
}
const BASEURL = 'https://www.nasa.gov/api/2/ubernode/_search'
const PUBLICURL = 'https://www.nasa.gov/sites/default/files/styles/image_card_4x3_ratio/public/'
const DOWNLOADPUBLICURL = 'https://www.nasa.gov/sites/default/files/'
const axios = require('axios')
const { CancelToken } = axios
let source = null
const { axiosGet } = require('../utils/axios')
export const getImage = function (data) {
return new Promise((resolve, reject) => {
if (!data) {
resolve([])
return
}
const baseUrl = BASEURL
source = CancelToken.source()
axiosGet({
url: baseUrl,
params: {
...BASEPARAMS,
from: (data.page * BASEPARAMS.size)
},
cancelToken: source.token
}).then((result) => {
source = null
const { hits: { hits } } = result
const urls = []
hits.forEach((item) => {
const { _source: { 'master-image': imageData } } = item
const { width, height, uri } = imageData
const obj = {
width,
height,
url: uri.replace('public://', PUBLICURL),
downloadUrl: uri.replace('public://', DOWNLOADPUBLICURL),
}
urls.push(obj)
})
resolve(urls)
}).catch((error) => {
source = null
console.log('------------请求失败nasa:', error)
reject()
})
})
}
// getImage({
// page: 0,
// searchKey: ''
// })
export const cancelImage = function () {
if (source) {
source.cancel()
}
}

68
src/get-image/paper.js Executable file
View File

@@ -0,0 +1,68 @@
const axios = require('axios')
const { CancelToken } = axios
let source = null
const { axiosGet } = require('../utils/axios')
export const getPaperSetting = function (data){
const returnResult = []
return new Promise((resolve, reject) => {
axiosGet({
url: 'https://service.paper.meiyuan.in/api/v2/columns'
}).then((result) => {
for (const item of result){
returnResult.push({
name: item.langs['zh-Hans-CN'],
value: item._id
})
}
resolve(returnResult)
}).catch(() => {
resolve(returnResult)
})
})
}
export const getImage = function (data) {
return new Promise((resolve, reject) => {
if (!data || !data.searchKey) {
resolve([])
return
}
const baseUrl = `https://service.paper.meiyuan.in/api/v2/columns/flow/${data.searchKey}`
source = CancelToken.source()
axiosGet({
url: baseUrl,
params: {
page: data.page + 1,
per_page: 50
},
cancelToken: source.token
}).then((result) => {
source = null
const urls = []
result.forEach((item) => {
const obj = {
width: item.width,
height: item.height,
url: item.urls.small,
downloadUrl: item.urls.raw,
}
urls.push(obj)
})
resolve(urls)
}).catch((error) => {
source = null
console.log('------------请求失败paper:', baseUrl, data)
reject()
})
})
}
export const cancelImage = function () {
if (source) {
source.cancel()
}
}

100
src/get-image/pexels.js Executable file
View File

@@ -0,0 +1,100 @@
/** *
* pexels网站 https://www.pexels.com
*/
const cheerio = require('cheerio')
const { request } = require('http2-client')
// 下一页的时间参数
let nextPageSeed = ''
// 请求对象
let reqObject = null
const analysisResult = function (result){
const urls = []
const $ = cheerio.load(result)
if ($('.next_page').length){
nextPageSeed = $('.next_page')[0].attribs.href
}
const imageItems = $('.photo-item__img')
// 是否存在
if (nextPageSeed.length){
const imageItemsLength = imageItems.length
for (let i = 0; i < imageItemsLength; i++){
const { attribs } = imageItems[i]
if (attribs['data-big-src']){
const obj = {
width: attribs['data-image-width'],
height: attribs['data-image-height'],
url: attribs['data-large-src'],
downloadUrl: attribs['data-big-src'].split('?')[0]
}
urls.push(obj)
}
}
}
return urls
}
export const getImage = function (data) {
return new Promise((resolve, reject) => {
if (!data) {
resolve([])
}
if (data.page === 0){
nextPageSeed = ''
}
let baseUrl = 'https://www.pexels.com'
if (data.searchKey) {
baseUrl = `https://www.pexels.com/search/${data.searchKey}`
}
const url = baseUrl + nextPageSeed
console.log('------------', url)
reqObject = request(url, {
method: 'GET',
headers: {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36',
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
cookie: '__cfduid=dc0c8a5d9efdac6eb0e5b49745a6ce78e1579152913;'
+ 'locale=en-US; _ga=GA1.2.727888903.1579152916;'
+ '_gid=GA1.2.1309891260.1579152916; _fbp=fb.1.1579152916921.482810349;'
+ '_hjid=dac493c8-a7f3-4834-9d25-10ef85de0227; _gat=1',
},
}, (res) => {
const { http2Clients } = reqObject.getGlobalManager({})
if (res.statusCode === 200){
res.setEncoding('utf8')
let resultData = ''
res.on('data', (chunk) => { resultData += chunk })
res.on('end', () => {
Object.values(http2Clients).forEach((i) => { i.close() })
try {
reqObject = null
resolve(analysisResult(resultData))
} catch (error) {
console.log('------------解析失败pexels:')
reject()
}
})
}
else {
console.log('------------请求失败pexels:')
reject()
Object.values(http2Clients).forEach((i) => { i.close() })
reqObject = null
}
res.end()
})
})
}
export const cancelImage = function () {
if (reqObject && reqObject.getGlobalManager) {
const { http2Clients } = reqObject.getGlobalManager({})
Object.values(http2Clients).forEach((i) => { i.close() })
}
}

53
src/get-image/search.js Executable file
View File

@@ -0,0 +1,53 @@
import { apiTranslation } from '../api/api'
import { imageSourceType } from '../utils/utils'
let type = ''
const pexels = require('./pexels')
const fiveHundred = require('./500px')
const paper = require('./paper')
const unsplash = require('./unsplash')
const wallhaven = require('./wallhaven')
const nasa = require('./nasa')
const themoviedb = require('./themoviedb')
const cancelFn = {
pexels: pexels.cancelImage,
'500px': fiveHundred.cancelImage,
paper: paper.cancelImage,
unsplash: unsplash.cancelImage,
wallhaven: unsplash.cancelImage,
nasa: nasa.cancelImage,
themoviedb: themoviedb.cancelImage
}
const getUrl = {
pexels: pexels.getImage,
'500px': fiveHundred.getImage,
paper: paper.getImage,
unsplash: unsplash.getImage,
wallhaven: wallhaven.getImage,
nasa: nasa.getImage,
themoviedb: themoviedb.getImage
}
export const getUrls = function (data) {
return new Promise(async (resolve, reject) => {
type = data.imageSource
const currentImageSource = imageSourceType.find(i => i.value === type)
data.searchKey = currentImageSource.isSupportChinaSearch ? data.searchKey : await apiTranslation(data.searchKey)
getUrl[type](data).then((urls) => {
resolve(urls)
}).catch((error) => {
reject(error)
}).finally(() => {
type = ''
})
})
}
export const cancelUrls = function () {
if (type !== '') {
cancelFn[type]()
}
}

102
src/get-image/themoviedb.js Executable file
View File

@@ -0,0 +1,102 @@
/* eslint-disable max-len */
import userConfig from '../../.user-config'
const { themoviedbAppKey } = userConfig
const APIKEY = themoviedbAppKey
const MOVELISTAPI = 'https://api.themoviedb.org/3/discover/movie'
const ALLSEARCHAPI = 'https://api.themoviedb.org/3/search/multi'
const MOVELISTPARAMS = {
api_key: APIKEY,
language: 'zh-CN',
sort_by: 'popularity.desc',
include_adult: false,
include_video: false,
page: 1
}
const BASEIMAGEDOWNURL = 'https://image.tmdb.org/t/p/original'
const BASEIMAGEURL = 'https://image.tmdb.org/t/p/w500'
// const GETMOVEIMAGEURL='https://api.themoviedb.org/3/movie/${moveid}/images?api_key='
const axios = require('axios')
const { CancelToken } = axios
let source = null
const { axiosGet } = require('../utils/axios')
const getImages = function (movieId, type){
return new Promise((resolve) => {
axiosGet({
url: `https://api.themoviedb.org/3/${type}/${movieId}/images`,
params: {
api_key: APIKEY
},
}).then((result) => {
const { backdrops } = result
const urls = []
backdrops.forEach((item) => {
const { width, height, file_path: filePath } = item
urls.push({
width,
height,
url: `${BASEIMAGEURL}${filePath}`,
downloadUrl: `${BASEIMAGEDOWNURL}${filePath}`,
})
})
resolve(urls)
}).catch((err) => {
resolve([])
})
})
}
export const getImage = function (data) {
return new Promise((resolve, reject) => {
if (!data) {
resolve([])
return
}
source = CancelToken.source()
let url = MOVELISTAPI
let params = {
...MOVELISTPARAMS,
page: data.page + 1
}
if (data.searchKey){
url = ALLSEARCHAPI
params = {
api_key: APIKEY,
// language: 'zh-CN',
page: data.page + 1,
include_adult: false,
query: data.searchKey
}
}
axiosGet({ url, params, cancelToken: source.token }).then(async (result) => {
const { results } = result
let urls = []
await Promise.all(results.map(async (item) => {
const { id, media_type: mediaType = 'movie' } = item
const movieImages = await getImages(id, mediaType)
urls = [...movieImages, ...urls]
return Promise.resolve
}))
resolve(urls)
}).catch((error) => {
source = null
console.log('------------请求失败tmdb:', error)
reject()
})
})
}
export const cancelImage = function () {
if (source) {
source.cancel()
}
}

87
src/get-image/unsplash.js Executable file
View File

@@ -0,0 +1,87 @@
/** *
* pexels网站 https://unsplash.com/t/wallpapers
*
*
* 获取壁纸
* https://unsplash.com/napi/collections/1065976/photos?
* page=6
* per_page=10
* order_by=latest
* share_key=a4a197fc196734b74c9d87e48cc86838
*
* 主图片
* https://unsplash.com/napi/photos?page=3&per_page=12
*
*
*
* 搜索
* https://unsplash.com/napi/search?query=cat&xp=&per_page=20
*
*/
const axios = require('axios')
const { CancelToken } = axios
let source = null
const { axiosGet } = require('../utils/axios')
export const getImage = function (data) {
return new Promise((resolve, reject) => {
if (!data) {
resolve([])
}
let baseUrl = 'https://unsplash.com/napi/collections/1065976/photos'
let searchFalg = false
let params = {
page: data.page,
order_by: 'latest',
per_page: 50,
share_key: 'a4a197fc196734b74c9d87e48cc86838'
}
if (data.searchKey) {
baseUrl = 'https://unsplash.com/napi/search/photos'
searchFalg = true
params = {
query: data.searchKey,
xp: '',
per_page: 50,
page: data.page,
}
}
source = CancelToken.source()
axiosGet({
url: baseUrl,
params,
cancelToken: source.token
}).then((result) => {
source = null
const urls = []
let newRes = result
if (searchFalg){
newRes = result.results
}
newRes.forEach((item) => {
const obj = {
width: item.width,
height: item.height,
url: item.urls.small,
downloadUrl: item.urls.full,
}
urls.push(obj)
})
resolve(urls)
}).catch(() => {
source = null
console.log('------------请求失败unsplash:', baseUrl)
reject()
})
})
}
export const cancelImage = function () {
if (source) {
source.cancel()
}
}

83
src/get-image/wallhaven.js Executable file
View File

@@ -0,0 +1,83 @@
/** *
* https://wallhaven.cc
*
*
* 获取壁纸
* https://wallhaven.cc/latest?
* page=2
*
*
* 搜索
* https://wallhaven.cc/search?q=cat&categories=111&purity=100&sorting=date_added&order=desc
* page=2
*
*/
const axios = require('axios')
const cheerio = require('cheerio')
const { CancelToken } = axios
let source = null
const { axiosGet } = require('../utils/axios')
export const getImage = function (data) {
return new Promise((resolve, reject) => {
if (!data) {
resolve([])
}
let baseUrl = 'https://wallhaven.cc/latest'
let params = {}
if (data.page > 0){
params.page = data.page + 1
}
if (data.searchKey) {
baseUrl = 'https://wallhaven.cc/search'
params = {
...params,
q: data.searchKey,
categories: 111,
purity: 100,
sorting: 'date_added',
order: 'desc'
}
}
source = CancelToken.source()
axiosGet({
url: baseUrl,
params,
cancelToken: source.token
}).then((result) => {
source = null
const urls = []
const $ = cheerio.load(result)
$('figure').each((index, node) => {
const wallpaperId = node.attribs['data-wallpaper-id']
const url = $(node).find('img').attr('data-src')
const isPng = Boolean($(node).find('.png').length)
const [width, height] = $(node).find('.wall-res').html().split('x')
const downloadUrl = `https://w.wallhaven.cc/full/${wallpaperId.slice(0, 2)}/wallhaven-${wallpaperId}.${isPng ? 'png' : 'jpg'}`
const obj = {
width: width.trim(),
height: height.trim(),
url,
downloadUrl,
}
urls.push(obj)
})
resolve(urls)
}).catch(() => {
source = null
console.log('------------请求失败wallhaven:', baseUrl)
reject()
})
})
}
export const cancelImage = function () {
if (source) {
source.cancel()
}
}

24
src/index.ejs Executable file
View File

@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Strawberry Wallpaper</title>
<% if (htmlWebpackPlugin.options.nodeModules) { %>
<!-- Add `node_modules/` to global paths so `require` works properly in development -->
<script>
require('module').globalPaths.push('<%= htmlWebpackPlugin.options.nodeModules.replace(/\\/g, '\\\\') %>')
</script>
<% } %>
</head>
<body>
<div id="app"></div>
<!-- Set `__static` path to static files in production -->
<% if (!process.browser) { %>
<script>
if (process.env.NODE_ENV !== 'development') window.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
</script>
<% } %>
<!-- webpack builds are automatically injected -->
</body>
</html>

1
src/main/.pydio Normal file
View File

@@ -0,0 +1 @@
ea3afe40-4434-4a1e-8de1-7776424a00b8

58
src/main/full-window.js Executable file
View File

@@ -0,0 +1,58 @@
const electron = require('electron')
const { baseUrl } = require('../utils/utils')
const { BrowserWindow } = electron
let fullWindow = null
/**
* 创建窗口
* @function createWindow
*/
function createWindow() {
fullWindow = new BrowserWindow({
width: 1200,
height: 700,
frame: false,
maximizable: true,
minimizable: true,
// fullscreenable: true,
// fullscreen: true,
})
fullWindow.loadURL(`${baseUrl}#/full`)
fullWindow.on('close', () => {
fullWindow = null
})
fullWindow.on('enter-full-screen', () => {
// console.log('Jin人员')
// fullWindow.setWindowButtonVisibility(true)
})
}
function openWindow(){
if (!fullWindow){
createWindow()
// fullWindow.openDevTools()
}
else {
fullWindow.show()
}
}
function closeWindow(){
if (fullWindow){
fullWindow.hide()
}
}
function getWindow(){
return fullWindow
}
export default {
openWindow,
closeWindow,
getWindow,
}

18
src/main/index.dev.js Executable file
View File

@@ -0,0 +1,18 @@
/**
* This file is used specifically and only for development. It installs
* `electron-debug` & `vue-devtools`. There shouldn't be any need to
* modify this file, but it can be used to extend your development
* environment.
*/
/* eslint-disable */
require('electron').app.on('ready', () => {
// let installExtension = require('electron-devtools-installer')
// installExtension.default(installExtension.VUEJS_DEVTOOLS).then(() => {}).catch(err => {
// console.log('Unable to install `vue-devtools`: \n', err)
// })
})
// Require `main` process to boot app
require('./index')

532
src/main/index.js Executable file
View File

@@ -0,0 +1,532 @@
import fullWindow from './full-window'
const electron = require('electron')
const { app, BrowserWindow, Tray, ipcMain, dialog } = electron
const fs = require('fs')
const path = require('path')
const { autoUpdater } = require('electron-updater')
const log = require('electron-log')
const { setCurrentWallpaper, changeWallpaperScale } = require('../wallpaper/outwallpaper')
const { openAutoStart, openDisStart } = require('../file/auto-open')
const { downloadPic, cancelDownloadPic } = require('../file/file')
const { getUrls, cancelUrls } = require('../get-image/search')
const { newEmail } = require('./mail')
const { isDev, isMac, isWin, baseUrl } = require('../utils/utils')
const { getPaperSetting } = require('../get-image/paper')
log.transports.file.level = 'info'
let mainWindow = null
// 托盘对象
let appTray = null
let openAppFlag = true
let currentScreenIndex = 0 // 当前屏幕的索引
const mainCallBack = {
'autoUpdater.downloadUpdate': () => {
autoUpdater.downloadUpdate()
},
}
appOpenInit()
ipcMainInit()
autoUpdaterInit()
setTimeIntervalInit()
/**
* 创建程序锁,保证只能打开单个实例
* @function appOpenInit
*/
function appOpenInit(){
if (isWin()) {
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
app.quit()
} else {
app.on('second-instance', (event, commandLine, workingDirectory) => {
if (mainWindow) {
if (!mainWindow.isVisible()) {
mainWindowShow()
}
mainWindow.focus()
}
})
}
} else if (isMac()) {
app.dock.hide()
}
app.on('ready', () => {
// fullWindow.openWindow()
setTimeout(() => {
if (!isDev()) {
autoUpdater.logger = log
autoUpdater.autoDownload = false
checkUpdater()
}
appInit()
}, 10)
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (mainWindow === null) {
appInit()
} else {
mainWindowShow()
}
})
}
/**
* 创建窗口
* @function createWindow
*/
function createWindow() {
mainWindow = new BrowserWindow({
height: 600,
width: 310,
frame: false,
transparent: true,
show: false,
alwaysOnTop: true,
resizable: false, // 禁止变化尺寸
hasShadow: true, // 是否阴影
focusable: true,
fullscreenable: false,
skipTaskbar: true,
minimizable: false,
maximizable: false,
closable: false,
fullscreen: false,
titleBarStyle: 'customButtonsOnHover'
})
// mainWindow.openDevTools()
mainWindow.loadURL(baseUrl)
mainWindow.on('blur', () => {
mainWindow.hide()
})
mainWindow.on('closed', () => {
app.quit()
mainWindow = null
})
}
/**
* 设置定时器
*/
function setTimeIntervalInit(){
// 10s执行一次
setInterval(() => {
sendData('intervalTime')
}, 10000)
}
/**
* 创建 Tray
* @function createAppTray
*/
function createAppTray() {
if (isMac()) {
// eslint-disable-next-line no-undef
appTray = new Tray(path.resolve(__static, './img/trayTemplate.png'))
} else if (isWin()) {
// eslint-disable-next-line no-undef
appTray = new Tray(path.resolve(__static, './img/tray.png'))
}
// 系统托盘图标目录
appTray.on('click', (event, bounds, position) => {
// mainWindow === null ? createWindow() : mainWindow.close()
// return
// 点击时显示窗口,并修改窗口的显示位置
function setMainWinPosition(win, trayBounds) {
try {
const { screen } = electron
const winWidth = mainWindow.getSize()[0]
const winHeight = mainWindow.getSize()[1]
const cursorPosition = screen.getCursorScreenPoint()
const currentScreen = screen.getDisplayNearestPoint(cursorPosition)
const screenLists = screen.getAllDisplays()
currentScreenIndex = screenLists.findIndex(i => i.id === currentScreen.id)
// 保证永远在图标的中心位置,因为没有做其他方向的三角,所以暂不考虑高
cursorPosition.x = trayBounds.x + trayBounds.width / 2
const parallelType = cursorPosition.x < currentScreen.bounds.x + currentScreen.workAreaSize.width / 2 ? 'left' : 'right'
const verticaType = cursorPosition.y < currentScreen.bounds.y + currentScreen.workAreaSize.height / 2 ? 'top' : 'bottom'
let trayPositionType = '' // 任务栏的位置 top|bottom|left|right
let trayPositionSize = 0 // 任务栏的尺寸
if (currentScreen.workAreaSize.height < currentScreen.size.height) {
trayPositionType = verticaType === 'top' ? 'top' : 'bottom'
trayPositionSize = currentScreen.size.height - currentScreen.workAreaSize.height
} else if (currentScreen.workAreaSize.width < currentScreen.size.width) {
trayPositionType = parallelType === 'left' ? 'left' : 'right'
trayPositionSize = currentScreen.size.width - currentScreen.workAreaSize.width
}
let winPositionX = 0
let winPositionY = 0
if (trayPositionType === 'top') {
winPositionX = Math.max(Math.min(currentScreen.bounds.width + currentScreen.bounds.x - winWidth, cursorPosition.x - (winWidth / 2)), currentScreen.bounds.x)
winPositionY = currentScreen.bounds.y + trayPositionSize + 2
} else if (trayPositionType === 'bottom') {
winPositionX = Math.max(Math.min(currentScreen.bounds.width + currentScreen.bounds.x - winWidth, cursorPosition.x - (winWidth / 2)), currentScreen.bounds.x)
winPositionY = currentScreen.bounds.height + currentScreen.bounds.y - trayPositionSize - winHeight
} else if (trayPositionType === 'left') {
winPositionX = currentScreen.bounds.x + trayPositionSize
winPositionY = Math.max(Math.min(currentScreen.bounds.height + currentScreen.bounds.y - winHeight, cursorPosition.y - (winHeight / 2)), currentScreen.bounds.y)
} else if (trayPositionType === 'right') {
winPositionX = currentScreen.bounds.x + currentScreen.bounds.width - trayPositionSize - winWidth
winPositionY = Math.max(Math.min(currentScreen.bounds.height + currentScreen.bounds.y - winHeight, cursorPosition.y - (winHeight / 2)), currentScreen.bounds.y)
}
log.info('--------------------------------')
log.info('currentScreen:', currentScreen)
log.info('position:', winPositionX, winPositionY)
log.info('trayPositionType:', trayPositionType)
log.info('trayPositionSize', trayPositionSize)
log.info('cursorPosition:', cursorPosition)
log.info('--------------------------------')
win.setPosition(parseInt(winPositionX, 10), winPositionY)
} catch (error) {
log.error(error)
}
}
try {
if (mainWindow.isVisible()) {
sendData('datainfo', {
type: 'windowShow',
data: false
})
mainWindowHide()
} else {
sendData('datainfo', {
type: 'windowShow',
data: true
})
mainWindowShow()
setMainWinPosition(mainWindow, bounds)
}
} catch (error) {
log.error(error)
}
})
mainWindow.on('show', () => {
appTray.setHighlightMode('never')
})
mainWindow.on('hide', () => {
appTray.setHighlightMode('selection')
})
}
function appInit() {
if (mainWindow == null) {
createWindow() // 创建主窗口
} else {
mainWindowShow()
}
if (appTray == null) {
createAppTray() // 创建系统托盘
}
}
/**
* 给所有的的渲染进程发消息
*/
function sendData(...args){
const fullWindowWindow = fullWindow.getWindow()
const windows = [mainWindow, fullWindowWindow]
windows.forEach((i) => {
if (i){
i.webContents.send(...args)
}
})
}
/**
* 主窗口显示,创建一个动画效果
*/
function mainWindowShow() {
let opacity = 0
mainWindow.show()
const time = setInterval(() => {
if (opacity >= 1) {
opacity = 1
clearInterval(time)
}
mainWindow.setOpacity(opacity)
opacity = parseFloat((opacity + 0.1).toFixed(1))
}, 80)
}
/**
* 主窗口隐藏,创建一个动画效果
*/
function mainWindowHide() {
let opacity = 1
const time = setInterval(() => {
if (opacity <= 0) {
opacity = 0.0
clearInterval(time)
mainWindow.hide()
}
mainWindow.setOpacity(opacity)
opacity = parseFloat((opacity - 0.1).toFixed(1))
}, 80)
}
function ipcMainInit() {
/** * 主进程传一个字符串给渲染进程,渲染进程在传递事件给主进程 用于主进程中的一些函数回调 */
ipcMain.on('maincallback', (event, data, argument) => {
if (typeof mainCallBack[data] !== 'undefined') {
mainCallBack[data](argument)
}
})
// 取消所有请求
// eslint-disable-next-line no-unused-vars
ipcMain.on('cancelAllRequest', (event, data) => {
cancelDownloadPic()
cancelUrls()
})
ipcMain.on('fullWindow', (event, data) => {
if (data){
fullWindow.openWindow()
}
else {
fullWindow.closeWindow()
}
})
ipcMain.on('dataWallpaper', (event, arg) => {
downloadPic(arg.downloadUrl, sendData, arg.userConfig).then((filePath) => {
const { options } = arg
if (isMac() && options.autoSetAllScreens === false){
setCurrentWallpaper(filePath, {
...options,
screen: currentScreenIndex,
})
}
else {
setCurrentWallpaper(filePath)
}
log.info(`设置壁纸成功:${filePath}`)
event.sender.send('dataWallpaper', 'success')
}).catch((error) => {
log.error(`设置壁纸失败:${arg.downloadUrl}`)
log.error(error)
event.sender.send('dataWallpaper', 'error')
})
})
ipcMain.on('getImageUrls', (event, data) => {
getUrls(data).then((result) => {
sendData('datainfo', {
type: 'urls',
data: result
})
}).catch(() => {
sendData('datainfo', {
type: 'urlsError',
data: ''
})
})
})
ipcMain.on('btn', (event, data) => {
if (data.type === 'quit') {
// 窗口设置了closeable为false后不能退出程序手动再设置一下
mainWindow.setClosable(true)
app.quit()
// eslint-disable-next-line no-empty
} else if (data.type === 'searchKey') {
} else if (data.type === 'openStart') {
if (data.data) {
openAutoStart()
} else {
openDisStart()
}
} else if (data.type === 'newEmail') {
newEmail(data.data.data, data.data.telUser, {
version: autoUpdater.currentVersion,
emailType: data.data.emailType
}).then(() => {
event.sender.send('sendnewEmail', 'success', data.data.emailType)
}).catch((error) => {
event.sender.send('sendnewEmail', 'error', data.data.emailType, error)
})
} else if (data.type === 'checkNewVersion') {
checkUpdater()
}
else if (data.type === 'setDefaultDownPath'){
mainWindow.setAlwaysOnTop(false)
dialog.showOpenDialog({
properties: ['openDirectory', 'createDirectory', 'promptToCreate'],
message: '选择要下载图片所在文件夹',
defaultPath: data.data },
(paths) => {
mainWindow.setAlwaysOnTop(true)
if (paths){
// 清空原目录中的文件
delPath(data.data)
event.sender.send('defaultPath', paths[0])
}
}, mainWindow)
}
else if (data.type === 'deleteFile'){
delPath(data.data)
}
else if (data.type === 'changeWallpaperScale'){
changeWallpaperScale({ scale: data.data })
}
})
// 渲染函数运行过来
ipcMain.on('runFunc', async (event, data) => {
// 存放一些函数
const FUNCLIST = {
getPaperSetting, // 获取paper的设置
}
if (FUNCLIST[data]){
FUNCLIST[data]().then((result) => {
event.returnValue = result
}).catch(() => {
event.returnValue = false
})
}
})
}
function checkUpdater() {
autoUpdater.checkForUpdates().then((result) => {
}).catch((error) => {
log.error(error)
})
}
function autoUpdaterInit() {
/** * 下载完成 */
autoUpdater.on('update-downloaded', () => {
autoUpdater.quitAndInstall()
})
autoUpdater.on('error', (info) => {
if (openAppFlag) {
openAppFlag = false
return
}
dialog.showMessageBox({
type: 'error',
buttons: ['关闭'],
title: '版本更新',
message: '版本更新检测出错',
detail: '可能是网路不好或者版本Bug,请提交意见反馈',
// eslint-disable-next-line no-undef
icon: path.resolve(__static, './img/banben.png')
})
})
autoUpdater.on('update-available', (info) => {
dialog.showMessageBox({
type: 'info',
buttons: ['是', '否'],
title: '版本更新',
message: `当前版本:${autoUpdater.currentVersion}`,
detail: `检测到新版本:${info.version},是否升级?`,
// eslint-disable-next-line no-undef
icon: path.resolve(__static, './img/banben.png')
}, (response) => {
if (response === 0) {
autoUpdater.downloadUpdate()
} else if (response === 1) {
console.log('1')
}
})
log.info('检测到新版本', info)
})
autoUpdater.on('checking-for-update', (info) => {
log.info('检测更新已发出', info)
})
autoUpdater.on('update-not-available', (info) => {
if (openAppFlag) {
openAppFlag = false
return
}
log.error('没有检测到新版本', info)
dialog.showMessageBox({
type: 'info',
buttons: ['关闭'],
title: '版本更新',
message: `当前版本:${autoUpdater.currentVersion}`,
detail: '当前已是最新版本,无需更新',
// eslint-disable-next-line no-undef
icon: path.resolve(__static, './img/banben.png')
})
})
// 更新下载进度
autoUpdater.on('download-progress', (progressObj) => {
sendData('datainfo', {
type: 'updaterProgress',
data: progressObj.percent
})
})
}
/**
* 删除指定目录
* @param {String} filePath
*/
function delPath(filePath){
if (!fs.existsSync(filePath)){
return '路径不存在'
}
const info = fs.statSync(filePath)
if (info.isDirectory()){ // 目录
const data = fs.readdirSync(filePath)
if (data.length > 0){
for (let i = 0; i < data.length; i++) {
if (data[i].match('SW-')){
delPath(`${filePath}/${data[i]}`) // 使用递归
if (i === data.length - 1){ // 删了目录里的内容就删掉这个目录
delPath(`${filePath}`)
}
}
}
} else {
fs.rmdirSync(filePath)// 删除空目录
}
} else if (info.isFile()){
fs.unlinkSync(filePath)// 删除文件
}
return true
}

56
src/main/mail.js Executable file
View File

@@ -0,0 +1,56 @@
import userConfig from '../../.user-config.js'
const nodemailer = require('nodemailer')
const { emailUserName, emailPassword } = userConfig
/**
* 发送一封邮件
* @param {Object} data 邮件的主体内容
* @param {*} telUser 主题中第三个框中的内容
* @param {Object} appInfo 邮件的相关信息
*/
// eslint-disable-next-line import/prefer-default-export
export function newEmail(data, telUser, appInfo){
return new Promise((resolve, reject) => {
nodemailer.createTestAccount((err) => {
// 建立一个邮箱连接
const transporter = nodemailer.createTransport({
host: 'smtp.163.com', // 发件箱地址
port: 465,
secure: true, // true for 465, false for other ports
auth: {
user: emailUserName, // 用户名
pass: emailPassword // 密码
}
})
// 设置发件内容
const mailOptions = {
from: 'strawberrypaper@163.com', // 发件人地址
to: 'strawberrypaper@163.com', // 收件人地址
subject: `${appInfo.emailType}:草莓壁纸】[${appInfo.version}]${telUser}`, // 主题
text: '',
html: (() => {
let html = ''
for (const [key, value] of Object.entries(data)){
html += `<div><strong>${key}:</strong><span>${value}</span><div>`
}
return html
})()
}
// 发送
transporter.sendMail(mailOptions, (error, info) => {
if (error || !info) {
reject(error.response)
} else {
resolve(info)
}
})
})
})
}

1
src/renderer/.pydio Normal file
View File

@@ -0,0 +1 @@
61fa188d-1397-4a0e-b2f5-e09587c17998

29
src/renderer/App.vue Executable file
View File

@@ -0,0 +1,29 @@
<template>
<div class="app" id='app'>
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {}
},
mounted() {},
methods: {}
}
</script>
<style lang="less">
.app{
width: 100%;
height: 100%;
overflow: hidden;
box-sizing: border-box;
position: relative;
background-color: transparent;
}
</style>

View File

@@ -0,0 +1 @@
71619b91-9748-432f-87e7-53e84fc82618

View File

@@ -0,0 +1 @@
3b45e05a-7aeb-48d3-bd56-1f5499136726

100
src/renderer/assets/css/base.css Executable file
View File

@@ -0,0 +1,100 @@
*{
margin: 0px;
font-family: 'PingFang SC',"Microsoft YaHei";
box-sizing: border-box;
}
html,body,.app{
width: 100%;
height: 100%;
background-color: transparent;
}
.el-input__inner {
border: none;
background-color: #383838;
color: #a5a5a5;
}
.el-textarea__inner{
border: none;
background-color: #383838;
color: #a5a5a5;
}
input::-webkit-input-placeholder, textarea::-webkit-input-placeholder {
color: #a5a5a5;
font-size: 12px;
}
.el-button--primary{
width: 100%;
background-color: #383838;
border: none;
color: #a5a5a5;
}
.el-button--primary:focus, .el-button--primary:hover{
background-color: #4d4e4e;
border: none;
border: none;
}
.popper__arrow {
border-bottom-color: #383838;
}
.el-popper[x-placement^="bottom"] .popper__arrow::after {
border-bottom-color: #383838;
}
.el-popper[x-placement^="bottom"] .popper__arrow {
border-bottom-color: #383838;
}
.el-select-dropdown {
background-color: #383838;
color: #000000;
border: none;
}
.el-select-dropdown__item.selected {
color: #ffffff;
}
.el-select-dropdown__item.hover,
.el-select-dropdown__item:hover {
background-color: #767676;
color: #ffffff;
}
.el-message-box__wrapper{
z-index:99999 !important;
}
.v-modal{
z-index:99998 !important;
}
.el-message{
z-index:99997 !important;
}
.page-transition-enter-active,.page-transition-leave-active{
transform: translateX(0);
transition: transform .3s;
}
.page-transition-enter, .page-transition-leave-to /* .fade-leave-active below version 2.1.8 */ {
transform: translateX(100%);
transition: transform .3s;
}
.el-loading-mask{
background-color: rgba(255, 255, 255, 0.5) !important;
font-size: 20px;
color: #fff;
}
.el-loading-spinner i {
color: #fff;
font-size: 50px;
}
.el-radio {
line-height: 22px;
}

View File

@@ -0,0 +1 @@
2286d94d-beb4-43dd-a353-4613439e13e4

View File

@@ -0,0 +1,54 @@
/*
* @Description: 常用工具函数合集
* @Author: --
* @Date: 2019-02-14 09:06:39
* @LastEditTime: 2019-04-09 10:16:30
*/
/**
* 深度克隆一个对象
* @param {*} obj
* @param {*} cache
*/
export const deepClone = function (obj, cache = []) {
if (obj === null || typeof obj !== 'object') {
return obj
}
const hit = cache.find(c => c.original === obj)
if (hit) {
return hit.copy
}
const copy = Array.isArray(obj) ? [] : {}
cache.push({
original: obj,
copy
})
Object.keys(obj).forEach((key) => {
copy[key] = deepClone(obj[key], cache)
})
return copy
}
/**
* 判断类型
* @param {*} obj
*/
export const typeOf = (obj) => {
const { toString } = Object.prototype
const map = {
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object String]': 'string',
'[object Function]': 'function',
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regExp',
'[object Undefined]': 'undefined',
'[object Null]': 'null',
'[object Object]': 'object'
}
return map[toString.call(obj)]
}

View File

@@ -0,0 +1,53 @@
/**
* 储存localStorage
* @param {String} name
* @param {Object|String|Boolearn|Number} value
*/
const setStore = (name, value) => {
if (!name) {
return false
}
if (typeof value !== 'string') {
value = JSON.stringify(value)
}
window.localStorage.setItem(name, value)
return true
}
/**
* 获取指定的localstorage
* @param {String} name
*/
const getStore = (name) => {
if (!name) return false
try {
if (window.localStorage.getItem(name) && window.localStorage.getItem(name).length > 1) {
const returnString = window.localStorage.getItem(name)
try {
return JSON.parse(returnString)
} catch (error) {
return returnString
}
}
} catch (error) {
console.log(error)
}
return ''
}
/**
* 删除指定的localStorage
* @param {String} name
*/
const removeStore = (name) => {
if (!name || !window.localStorage.getItem(name)) return false
window.localStorage.removeItem(name)
return true
}
export {
setStore,
getStore,
removeStore
}

View File

@@ -0,0 +1,23 @@
/*
* @Description: 函数注册
* @Author: --
* @Date: 2019-01-21 18:29:06
* @LastEditTime: 2019-04-09 20:57:19
*/
import axios from 'axios'
const { ipcRenderer } = require('electron')
const localStorage = require('./local-storage')
const { deepClone, typeOf } = require('./common-fn')
export default {
version: '0.0.1',
install(Vue) {
Vue.prototype.$deepClone = deepClone
Vue.prototype.$localStorage = localStorage
Vue.prototype.$typeOf = typeOf
Vue.prototype.$ipcRenderer = ipcRenderer
Vue.prototype.$http = axios
},
}

View File

@@ -0,0 +1 @@
5e3ce05f-7d0c-44d9-b3d5-06da9c26335f

View File

@@ -0,0 +1 @@
0548aa3a-ab35-420e-a82c-282f39eb3514

View File

@@ -0,0 +1,56 @@
<template>
<div :class="['chrome-icon',{nodisabled:!disabled}]" @click="handleClick" style="-webkit-app-region: no-drag">
<i :class="['iconfont',icon]" ></i>
</div>
</template>
<script>
export default {
name: 'chromeIcon',
props: {
disabled: {
type: Boolean,
default: false
},
icon: {
type: String,
default: ''
}
},
methods: {
handleClick(){
this.$emit('click')
}
}
}
</script>
<style lang="less" scoped>
.chrome-icon {
background-color: transparent;
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
margin-right:5px;
border-radius: 100%;
cursor:not-allowed;
.iconfont{
font-size: 24px;
color: #aaaaaa;
}
&.nodisabled {
cursor: default;
.iconfont{
color: #ffffff;
}
}
&.nodisabled:hover {
background-color: #aaaaaa;
}
}
</style>

View File

@@ -0,0 +1 @@
a9389a88-3049-40d9-a37f-a069d8792f77

View File

@@ -0,0 +1,37 @@
<template>
<div class="content">
<div class="content-main" :class="{'content-win':osType=='win'}">
<slot></slot>
</div>
</div>
</template>
<script>
import { osType } from '../../../utils/utils'
export default {
name: 'content-div',
data(){
return {
osType
}
}
}
</script>
<style lang="less" scoped>
.content {
width: 100%;
overflow: hidden;
.content-main {
width: calc(~"100% + 15px");
height: 100%;
overflow-y: scroll;
overflow-x: hidden;
}
.content-win {
width: calc(~"100% + 17px");
}
}
</style>

View File

@@ -0,0 +1 @@
887c1011-8169-4561-afc2-d4bb71db9b0d

View File

@@ -0,0 +1,25 @@
<template>
<i class="icon" @click.stop="handleClick"></i>
</template>
<script>
export default {
name: 'icon',
methods: {
handleClick(){
this.$emit('click')
}
}
}
</script>
<style lang="less" scoped>
.icon {
color: #dddddd;
&:hover{
color: #ffffff;
}
}
</style>

View File

@@ -0,0 +1 @@
885b5bac-6a22-4221-bfe0-5b0f532afc29

Some files were not shown because too many files have changed in this diff Show More