├── .gitignore ├── README.md ├── add_launch_agent.sh ├── index.js ├── launchagent.temp ├── lib └── lovebizhiapi.js ├── package.json ├── run.sh └── setbackground.scpt /.gitignore: -------------------------------------------------------------------------------- 1 | config 2 | node_modules 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lovebizhi Collector 2 | 3 | This is a nodejs script that uses [lovebizhi](https://www.lovebizhi.com) apis to collect wallpapers, and auto change wallpapers for OS X. [Chinese Post](http://songchenwen.com/tech/2015/06/07/lovebizhi-collector/) 4 | 5 | ## Usage 6 | 7 | If you haven't installed nodejs, you can use [Homebrew](http://brew.sh) to install it. 8 | 9 | ~~~ bash 10 | brew install node 11 | ~~~ 12 | 13 | 1. Clone this script. 14 | 2. `node install` to install all the dependencies. 15 | 3. Add a new file `config`. 16 | 4. Put `OUTPATH="/path/to/your/folder/for/wallpapers"` in `config`. 17 | 18 | The following shell commands will do all the work above. You just need to change "/path/to/your/folder/for/wallpapers" to the absolute path in which you want to store your collected wallpapers. 19 | 20 | ~~~ bash 21 | OUTPATH="/path/to/your/folder/for/wallpapers" 22 | mkdir lovebizhi-collecor 23 | cd lovebizhi-collecor 24 | git clone "https://github.com/songchenwen/lovebizhi-collector.git" . 25 | npm install 26 | touch config 27 | echo "OUTPATH=\"$OUTPATH\"" > config 28 | ~~~ 29 | 30 | Then you can run `run.sh` to begin collectting the wallpapers. 31 | 32 | ~~~ bash 33 | sh run.sh 34 | ~~~ 35 | 36 | ## Auto Start 37 | 38 | There is a shell script you can use to add a `LaunchAgent` to the OS X system to auto run the collector at an interval. 39 | 40 | ~~~ bash 41 | sh add_launch_agent.sh 42 | ~~~ 43 | 44 | ## Configuration 45 | 46 | You can edit the following lines in `index.js` to configure this script. 47 | 48 | ~~~ javascript 49 | var categories = [API.categories.landscape, API.categories.plant]; 50 | var screenWidth = 2560; 51 | var screenHeight = 1600; 52 | var maxFileCount = 100; 53 | ~~~ 54 | 55 | The available catetories are listed below: 56 | 57 | - `API.categories.moviestar` 58 | - `API.categories.landscape` 59 | - `API.categories.beauty` 60 | - `API.categories.plant` 61 | - `API.categories.animal` 62 | - `API.categories.game` 63 | - `API.categories.cartoon` 64 | - `API.categories.festival` 65 | - `API.categories.car` 66 | - `API.categories.food` 67 | - `API.categories.sport` 68 | -------------------------------------------------------------------------------- /add_launch_agent.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 4 | 5 | LAUNCHAGENTPATH="$HOME/Library/LaunchAgents/com.songchenwen.lovebizhicollector.plist" 6 | 7 | if [[ ! -f "$LAUNCHAGENTPATH" ]]; then 8 | 9 | touch "$LAUNCHAGENTPATH" 10 | cat "$CDIR/launchagent.temp" | sed "s|DIRPATH|$CDIR|" > "$LAUNCHAGENTPATH" 11 | launchctl load "$LAUNCHAGENTPATH" 12 | 13 | fi 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | request = require('request'), 3 | async = require('async'), 4 | API = require('./lib/lovebizhiapi.js'); 5 | 6 | var categories = [API.categories.landscape, API.categories.plant]; 7 | var screenWidth = 2560; 8 | var screenHeight = 1600; 9 | var maxFileCount = 100; 10 | 11 | var api = new API(screenWidth, screenHeight); 12 | 13 | var savePath = 'wallpapers'; 14 | 15 | if (process.argv.length >= 3){ 16 | var possiblePath = process.argv[2]; 17 | if(fs.existsSync(savePath)){ 18 | if(fs.statSync(savePath).isDirectory()){ 19 | savePath = possiblePath; 20 | } 21 | } else { 22 | savePath = possiblePath; 23 | } 24 | } 25 | 26 | var picIds = []; 27 | 28 | var fetchApi = function(category, callback){ 29 | request(api.getUrl(category), function(error, response, body){ 30 | if(!error){ 31 | var body = JSON.parse(body); 32 | if(body.data && body.data.length > 0){ 33 | body.data.forEach(function(item, index){ 34 | if(item.file_id){ 35 | if(picIds.indexOf(item.file_id) < 0){ 36 | picIds.push(item.file_id); 37 | } 38 | } 39 | }); 40 | } 41 | } 42 | callback(); 43 | }); 44 | }; 45 | 46 | var finishedPicCount = 0; 47 | 48 | var getPic = function(picId, callback){ 49 | var fileName = savePath + '/' + api.fileName(picId); 50 | if(fs.existsSync(fileName)){ 51 | finishedPicCount++; 52 | callback(); 53 | return; 54 | } 55 | var url = api.fileUrl(picId); 56 | request({url: url, encoding: null, timeout:15000}, function(error, res, body){ 57 | if(finishedPicCount >= maxFileCount - 1){ 58 | finishedPicCount++; 59 | callback(); 60 | return; 61 | } 62 | finishedPicCount++; 63 | if(error){ 64 | console.log('pic ' + picId + ' download error: ' + error); 65 | callback(error); 66 | }else{ 67 | if(res.headers && res.headers['content-length']){ 68 | if(res.headers['content-length'] > body.length){ 69 | callback("Incompleted downloading " + picId); 70 | return; 71 | } 72 | } 73 | fs.writeFile(fileName, body, 'binary', function(error){ 74 | if(error){ 75 | console.log('pic ' + picId + ' save error: ' + error); 76 | fs.unlinkSync(fileName); 77 | callback(error); 78 | }else{ 79 | console.log('pic ' + picId + ' saved ' + finishedPicCount + '/' + picIds.length); 80 | callback(); 81 | } 82 | }); 83 | } 84 | }); 85 | }; 86 | 87 | var prepareOutputDir = function(){ 88 | if(fs.existsSync(savePath)){ 89 | if(!fs.statSync(savePath).isDirectory()){ 90 | throw 'output path is a file'; 91 | } 92 | } else { 93 | fs.mkdirSync(savePath); 94 | } 95 | }; 96 | 97 | var finalPurge = function(){ 98 | var files = fs.readdirSync(savePath); 99 | var fileCount = files.length; 100 | console.log('file count ' + fileCount); 101 | if(fileCount <= maxFileCount){ 102 | return; 103 | } 104 | files = files.sort(function(a, b) { 105 | return fs.statSync(savePath + '/' + a).mtime.getTime() - 106 | fs.statSync(savePath + '/' + b).mtime.getTime(); 107 | }); 108 | async.each(files, function(file, callback){ 109 | if(fileCount <= maxFileCount * 0.7){ 110 | callback(); 111 | return; 112 | } 113 | if(file.indexOf('.jpg') == (file.length - '.jpg'.length)){ 114 | fs.unlinkSync(savePath + '/' + file); 115 | fileCount--; 116 | console.log(file + ' deleted'); 117 | callback(); 118 | } else { 119 | fileCount--; 120 | callback(); 121 | } 122 | }, function(error){ 123 | 124 | }); 125 | } 126 | 127 | prepareOutputDir(); 128 | async.each(categories, fetchApi, function(error){ 129 | if(!error){ 130 | console.log("success got " + picIds.length + " picIds"); 131 | }else{ 132 | console.log("error : " + error); 133 | } 134 | if(picIds.length > 0){ 135 | async.each(picIds, getPic, function(error){ 136 | if(!error){ 137 | console.log("final success"); 138 | } else { 139 | console.log("error : " + error); 140 | } 141 | finalPurge(); 142 | }); 143 | } 144 | }); 145 | -------------------------------------------------------------------------------- /launchagent.temp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | com.songchenwen.lovebizhicollector.plist 7 | Program 8 | DIRPATH/run.sh 9 | KeepAlive 10 | 11 | RunAtLoad 12 | 13 | StartInterval 14 | 14400 15 | 16 | 17 | -------------------------------------------------------------------------------- /lib/lovebizhiapi.js: -------------------------------------------------------------------------------- 1 | var url = "http://api.lovebizhi.com/macos_v4.php?a=category&tid={category}&device=105&uuid=436e4ddc389027ba3aef863a27f6e6f9&mode=0&retina=1&client_id=1008&device_id=31547324&model_id=105&size_id=0&channel_id=70001&screen_width={width}&screen_height={height}&version_code=19&order=newest&color_id=3"; 2 | 3 | API = function(width, height){ 4 | this.width = width; 5 | this.height = height; 6 | } 7 | 8 | API.categories = { 9 | moviestar : 1, 10 | landscape : 2, 11 | beauty : 3, 12 | plant : 4, 13 | animal : 5, 14 | game : 6, 15 | cartoon : 7, 16 | festival : 8, 17 | car : 798, 18 | food : 1546, 19 | sport : 1554 20 | }; 21 | 22 | API.prototype.getUrl = function(category){ 23 | return url.replace('{category}', category).replace('{width}', this.width).replace('{height}', this.height); 24 | } 25 | 26 | API.prototype.fileName = function(id){ 27 | return '' + id + ',' + this.width + ',' + this.height + '.jpg'; 28 | }; 29 | 30 | 31 | API.prototype.fileUrl = function(id){ 32 | return 'http://s.qdcdn.com/c/' + this.fileName(id); 33 | }; 34 | 35 | module.exports = API; 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lovebizhi-collector", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "async": "^0.9.0", 13 | "request": "^2.51.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 4 | 5 | OUTPATH="$CDIR/wallpapers" 6 | 7 | if [[ -f "$CDIR/config" ]]; then 8 | source "$CDIR/config" 9 | else 10 | echo "Make a new file \"config\" and, put OUTPATH=\"/where/to/store/wallpapers\" in it." 11 | fi 12 | 13 | /usr/local/bin/node "$CDIR/index.js" "$OUTPATH" 14 | 15 | cat "$CDIR/setbackground.scpt" | sed "s|OUTPATH|$OUTPATH|" | osascript 16 | -------------------------------------------------------------------------------- /setbackground.scpt: -------------------------------------------------------------------------------- 1 | tell application "System Events" 2 | set pictures folder of every desktop to "OUTPATH" 3 | set picture rotation of every desktop to 1 4 | set change interval of every desktop to 900.0 5 | set random order of every desktop to true 6 | end tell 7 | --------------------------------------------------------------------------------