├── .eslintignore ├── commitlint.config.js ├── static ├── icon.png ├── logo.png ├── test.png └── hr │ ├── 0117cbba35e51b0bce5f8c2f6a838e8a087e8ee7.png │ ├── 02db465212d3c374a43c60fa2625cc1caeaab796.png │ ├── 4aa545dccf7de8d4a93c2b2b8e3265ac0a26d216.png │ ├── 4adb9255ada5b97061e610b682b8636764fe50ed.png │ ├── 71bf2cd56882a2e97f8b3477c9256f8b09f361d3.png │ └── db75225feabec8d8b64ee7d3c7165cd639554cbc.png ├── src ├── renderer │ ├── css │ │ ├── fonts │ │ │ ├── icomoon.eot │ │ │ ├── icomoon.ttf │ │ │ ├── icomoon.woff │ │ │ └── icomoon.svg │ │ └── font.css │ ├── store │ │ ├── index.js │ │ └── modules │ │ │ ├── Running.js │ │ │ ├── index.js │ │ │ ├── Config.js │ │ │ ├── Passage.js │ │ │ └── Sync.js │ ├── utils │ │ ├── RendererFactory.js │ │ ├── PosterFactory.js │ │ ├── constants.js │ │ ├── network.js │ │ ├── biliNetwork.js │ │ ├── biliapi.js │ │ ├── renderer.js │ │ └── languages.js │ ├── components │ │ ├── subcomponents │ │ │ ├── Pending.vue │ │ │ ├── Login.vue │ │ │ ├── UserInfo.vue │ │ │ └── Passage.vue │ │ ├── Setting.vue │ │ ├── Editor.vue │ │ ├── Passages.vue │ │ └── About.vue │ ├── router │ │ └── index.js │ ├── main.js │ └── App.vue ├── main │ ├── index.dev.js │ └── index.js └── index.ejs ├── .codebeatignore ├── test ├── .eslintrc ├── e2e │ ├── specs │ │ └── Launch.spec.js │ ├── index.js │ └── utils.js └── unit │ ├── specs │ └── Editor.spec.js │ ├── index.js │ └── karma.conf.js ├── .gitignore ├── Bilibili-Column-Helper.code-workspace ├── .github └── ISSUE_TEMPLATE │ ├── Bug_report.md │ └── Feature_request.md ├── .eslintrc.js ├── appveyor.yml ├── .babelrc ├── .vscode ├── settings.json └── launch.json ├── .travis.yml ├── LICENSE ├── .electron-vue ├── dev-client.js ├── webpack.main.config.js ├── build.js ├── webpack.web.config.js ├── dev-runner.js └── webpack.renderer.config.js ├── Readme.md ├── CODE_OF_CONDUCT.md ├── CHANGELOG.md ├── package.json └── api.txt /.eslintignore: -------------------------------------------------------------------------------- 1 | test/unit/coverage/** 2 | test/unit/*.js 3 | test/e2e/*.js 4 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ['@commitlint/config-conventional'] } 2 | -------------------------------------------------------------------------------- /static/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yesterday17/Bilibili-Column-Helper/HEAD/static/icon.png -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yesterday17/Bilibili-Column-Helper/HEAD/static/logo.png -------------------------------------------------------------------------------- /static/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yesterday17/Bilibili-Column-Helper/HEAD/static/test.png -------------------------------------------------------------------------------- /src/renderer/css/fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yesterday17/Bilibili-Column-Helper/HEAD/src/renderer/css/fonts/icomoon.eot -------------------------------------------------------------------------------- /src/renderer/css/fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yesterday17/Bilibili-Column-Helper/HEAD/src/renderer/css/fonts/icomoon.ttf -------------------------------------------------------------------------------- /src/renderer/css/fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yesterday17/Bilibili-Column-Helper/HEAD/src/renderer/css/fonts/icomoon.woff -------------------------------------------------------------------------------- /.codebeatignore: -------------------------------------------------------------------------------- 1 | .github/* 2 | .vscode/* 3 | build/* 4 | dist/* 5 | .electron-vue/* 6 | test/* 7 | tests/* 8 | spec/* 9 | specs/* 10 | node_modules/* 11 | -------------------------------------------------------------------------------- /static/hr/0117cbba35e51b0bce5f8c2f6a838e8a087e8ee7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yesterday17/Bilibili-Column-Helper/HEAD/static/hr/0117cbba35e51b0bce5f8c2f6a838e8a087e8ee7.png -------------------------------------------------------------------------------- /static/hr/02db465212d3c374a43c60fa2625cc1caeaab796.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yesterday17/Bilibili-Column-Helper/HEAD/static/hr/02db465212d3c374a43c60fa2625cc1caeaab796.png -------------------------------------------------------------------------------- /static/hr/4aa545dccf7de8d4a93c2b2b8e3265ac0a26d216.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yesterday17/Bilibili-Column-Helper/HEAD/static/hr/4aa545dccf7de8d4a93c2b2b8e3265ac0a26d216.png -------------------------------------------------------------------------------- /static/hr/4adb9255ada5b97061e610b682b8636764fe50ed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yesterday17/Bilibili-Column-Helper/HEAD/static/hr/4adb9255ada5b97061e610b682b8636764fe50ed.png -------------------------------------------------------------------------------- /static/hr/71bf2cd56882a2e97f8b3477c9256f8b09f361d3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yesterday17/Bilibili-Column-Helper/HEAD/static/hr/71bf2cd56882a2e97f8b3477c9256f8b09f361d3.png -------------------------------------------------------------------------------- /static/hr/db75225feabec8d8b64ee7d3c7165cd639554cbc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yesterday17/Bilibili-Column-Helper/HEAD/static/hr/db75225feabec8d8b64ee7d3c7165cd639554cbc.png -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "globals": { 6 | "assert": true, 7 | "expect": true, 8 | "should": true, 9 | "__static": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | dist/electron/* 3 | dist/web/* 4 | build/* 5 | !build/icons 6 | node_modules/ 7 | npm-debug.log 8 | npm-debug.log.* 9 | thumbs.db 10 | !.gitkeep 11 | yarn.lock 12 | *.log 13 | test/unit/coverage -------------------------------------------------------------------------------- /src/renderer/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | import modules from './modules' 5 | 6 | Vue.use(Vuex) 7 | 8 | export default new Vuex.Store({ 9 | modules, 10 | strict: process.env.NODE_ENV !== 'production' 11 | }) 12 | -------------------------------------------------------------------------------- /Bilibili-Column-Helper.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "git.autofetch": true, 9 | "files.autoSave": "onFocusChange", 10 | "editor.tabCompletion": true, 11 | "eslint.alwaysShowStatus": true, 12 | "emmet.triggerExpansionOnTab": true 13 | } 14 | } -------------------------------------------------------------------------------- /src/renderer/store/modules/Running.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | page: 3, 3 | loginStatus: 'pending' 4 | } 5 | 6 | const mutations = { 7 | SET_PAGE: function (state, page) { 8 | state.page = page 9 | }, 10 | UPDATE_LOGIN_STATUS: function (state, status) { 11 | state.loginStatus = status 12 | } 13 | } 14 | 15 | export default { 16 | state, 17 | mutations 18 | } 19 | -------------------------------------------------------------------------------- /test/e2e/specs/Launch.spec.js: -------------------------------------------------------------------------------- 1 | import utils from '../utils' 2 | 3 | describe('Launch', function () { 4 | beforeEach(utils.beforeEach) 5 | afterEach(utils.afterEach) 6 | 7 | it('shows the proper application title', function () { 8 | return this.app.client.getTitle() 9 | .then(title => { 10 | expect(title).to.equal('my-project') 11 | }) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /src/renderer/utils/RendererFactory.js: -------------------------------------------------------------------------------- 1 | import biliZhuanLanMarkdown from 'bilibili-zhuanlan-markdown-tool' 2 | import * as defaultRenderer from './renderer' 3 | 4 | export function getRenderer (option) { 5 | switch (option.module) { 6 | case 'biliZhuanlanMarkdownTool': 7 | return biliZhuanLanMarkdown.md2Html 8 | case 'default': 9 | return (code) => defaultRenderer.render(code) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/unit/specs/Editor.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Editor from '@/components/Editor' 3 | 4 | describe('Editor.vue', () => { 5 | it('should contain both editor and previewer', () => { 6 | const vm = new Vue({ 7 | el: document.createElement('div'), 8 | render: h => h(Editor) 9 | }).$mount() 10 | 11 | expect(vm.$el.querySelector('.col').textContent).to.contain('Welcome to your new project!') 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /src/renderer/store/modules/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The file enables `@/store/index.js` to import all vuex modules 3 | * in a one-shot manner. There should not be any reason to edit this file. 4 | */ 5 | 6 | const files = require.context('.', false, /\.js$/) 7 | const modules = {} 8 | 9 | files.keys().forEach(key => { 10 | if (key === './index.js') return 11 | modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default 12 | }) 13 | 14 | export default modules 15 | -------------------------------------------------------------------------------- /test/e2e/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // Set BABEL_ENV to use proper env config 4 | process.env.BABEL_ENV = 'test' 5 | 6 | // Enable use of ES6+ on required files 7 | require('babel-register')({ 8 | ignore: /node_modules/ 9 | }) 10 | 11 | // Attach Chai APIs to global scope 12 | const { expect, should, assert } = require('chai') 13 | global.expect = expect 14 | global.should = should 15 | global.assert = assert 16 | 17 | // Require all JS files in `./specs` for Mocha to consume 18 | require('require-dir')('./specs') 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: 报告Bug。 4 | 5 | --- 6 | 7 | **描述Bug** 8 | 对Bug现象的简述。 9 | 10 | **复现方式** 11 | 复现该Bug的方法。 12 | 1. 进入 '...' 13 | 2. 点击 '....' 14 | 3. 滑动到 '....' 15 | 4. 发现错误 16 | 17 | **希望达到的结果** 18 | 在这里描述你希望通过以上步骤达到的效果。 19 | 20 | **屏幕截图** 21 | 屏幕截图有助于我们理解Bug的表现形式。 22 | 23 | **错误日志** 24 | 在这里贴上`Gist`或`pastebin`的地址。(错误日志功能在建中) 25 | 26 | **操作系统(请补完下列内容):** 27 | - OS: [e.g. Windows] 28 | - Version [e.g. 1.0.0] 29 | 30 | **附加信息** 31 | 在这里增加其他的说明信息。 32 | -------------------------------------------------------------------------------- /src/renderer/components/subcomponents/Pending.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 18 | 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: 提出建议。 4 | 5 | --- 6 | 7 | **请描述你的建议是否是针对某一问题提出的** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **描述你想要实现的解决方案** 11 | A clear and concise description of what you want to happen. 12 | 13 | **描述你已经考虑过的方案** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **附加信息** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /test/e2e/utils.js: -------------------------------------------------------------------------------- 1 | import electron from 'electron' 2 | import { Application } from 'spectron' 3 | 4 | export default { 5 | afterEach () { 6 | this.timeout(10000) 7 | 8 | if (this.app && this.app.isRunning()) { 9 | return this.app.stop() 10 | } 11 | }, 12 | beforeEach () { 13 | this.timeout(10000) 14 | this.app = new Application({ 15 | path: electron, 16 | args: ['dist/electron/main.js'], 17 | startTimeout: 10000, 18 | waitTimeout: 10000 19 | }) 20 | 21 | return this.app.start() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | Vue.config.devtools = false 3 | Vue.config.productionTip = false 4 | 5 | // require all test files (files that ends with .spec.js) 6 | const testsContext = require.context('./specs', true, /\.spec$/) 7 | testsContext.keys().forEach(testsContext) 8 | 9 | // require all src files except main.js for coverage. 10 | // you can also change this to match only the subset of files that 11 | // you want coverage for. 12 | const srcContext = require.context('../../src/renderer', true, /^\.\/(?!main(\.js)?$)/) 13 | srcContext.keys().forEach(srcContext) 14 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: "babel-eslint", 4 | parserOptions: { 5 | sourceType: "module" 6 | }, 7 | env: { 8 | browser: true, 9 | node: true 10 | }, 11 | extends: "standard", 12 | globals: { 13 | __static: true 14 | }, 15 | plugins: ["html"], 16 | env: { 17 | es6: true 18 | }, 19 | rules: { 20 | "arrow-parens": 0, 21 | "generator-star-spacing": 0, 22 | "no-debugger": process.env.NODE_ENV === "production" ? 2 : 0, 23 | "no-return-await": 0, 24 | "one-var": 0, 25 | "no-undef": 0, 26 | "no-unmodified-loop-condition": 0, 27 | "no-undef-init": 0 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /src/renderer/utils/PosterFactory.js: -------------------------------------------------------------------------------- 1 | import biliZhuanLanMarkdown from 'bilibili-zhuanlan-markdown-tool' 2 | import * as biliNetwork from './biliNetwork' 3 | import * as constants from './constants' 4 | 5 | export function getPoster (option) { 6 | switch (option.module) { 7 | case 'biliZhuanlanMarkdownTool': 8 | return (passage, cookies) => { 9 | biliZhuanLanMarkdown.initStatus(cookies) 10 | biliZhuanLanMarkdown.sendArticle(constants.localMDPath(passage.data.name)) 11 | } 12 | case 'default': 13 | return (passage, cookies) => { 14 | biliNetwork.addUpdate(passage, cookies, option.renderer) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Commented sections below can be used to run tests on the CI server 2 | # https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing 3 | 4 | version: 0.1.{build} 5 | 6 | branches: 7 | only: 8 | - master 9 | 10 | image: Visual Studio 2017 11 | platform: 12 | - x64 13 | 14 | cache: 15 | - node_modules 16 | - '%APPDATA%\npm-cache' 17 | - '%USERPROFILE%\.electron' 18 | - '%USERPROFILE%\AppData\Local\Yarn\cache' 19 | 20 | init: 21 | - git config --global core.autocrlf input 22 | 23 | install: 24 | - ps: Install-Product node 8 x64 25 | - git reset --hard HEAD 26 | - yarn 27 | - node --version 28 | 29 | build_script: 30 | #- yarn test 31 | #- yarn build 32 | yarn dist 33 | 34 | test: off -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "comments": false, 3 | "env": { 4 | "test": { 5 | "presets": [ 6 | ["env", { 7 | "targets": { "node": 7 } 8 | }], 9 | "stage-0" 10 | ], 11 | "plugins": ["istanbul"] 12 | }, 13 | "main": { 14 | "presets": [ 15 | ["env", { 16 | "targets": { "node": 7 } 17 | }], 18 | "stage-0" 19 | ] 20 | }, 21 | "renderer": { 22 | "presets": [ 23 | ["env", { 24 | "modules": false 25 | }], 26 | "stage-0" 27 | ] 28 | }, 29 | "web": { 30 | "presets": [ 31 | ["env", { 32 | "modules": false 33 | }], 34 | "stage-0" 35 | ] 36 | } 37 | }, 38 | "plugins": ["transform-runtime"] 39 | } 40 | -------------------------------------------------------------------------------- /src/renderer/utils/constants.js: -------------------------------------------------------------------------------- 1 | import { remote } from 'electron' 2 | import * as path from 'path' 3 | 4 | export const PATH = path.resolve(remote.app.getPath('userData'), 'Passages') 5 | 6 | export let columnPath = (name) => path.resolve(PATH, name) 7 | export let imagePath = (name) => path.resolve(columnPath(name), 'images') 8 | export let documentPath = (name) => path.resolve(columnPath(name), 'document') 9 | export let indexJson = (name) => path.resolve(columnPath(name), 'index.json') 10 | 11 | export let localMDPath = (name) => path.resolve(documentPath(name), 'local.md') 12 | export let remoteMDPath = (name) => path.resolve(documentPath(name), 'remote.md') 13 | export let localCoverPath = (name) => path.resolve(imagePath(name), 'cover.png') 14 | 15 | export let resolveImagePath = (name, path) 16 | -------------------------------------------------------------------------------- /src/renderer/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | 4 | Vue.use(Router) 5 | 6 | export default new Router({ 7 | routes: [ 8 | { 9 | path: '/passages', 10 | name: 'passages', 11 | component: require('@/components/Passages').default 12 | }, 13 | { 14 | path: '/editor', 15 | name: 'editor', 16 | component: require('@/components/Editor').default 17 | }, 18 | { 19 | path: '/setting', 20 | name: 'setting', 21 | component: require('@/components/Setting').default 22 | }, 23 | { 24 | path: '/about', 25 | name: 'about', 26 | component: require('@/components/About').default 27 | }, 28 | { 29 | path: '*', 30 | redirect: '/setting' 31 | } 32 | ] 33 | }) 34 | -------------------------------------------------------------------------------- /src/main/index.dev.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is used specifically and only for development. It installs 3 | * `electron-debug` & `vue-devtools`. There shouldn't be any need to 4 | * modify this file, but it can be used to extend your development 5 | * environment. 6 | */ 7 | 8 | /* eslint-disable */ 9 | 10 | // Install `electron-debug` with `devtron` 11 | require('electron-debug')({ showDevTools: true }) 12 | 13 | // Install `vue-devtools` 14 | require('electron').app.on('ready', () => { 15 | let installExtension = require('electron-devtools-installer') 16 | installExtension.default(installExtension.VUEJS_DEVTOOLS) 17 | .then(() => {}) 18 | .catch(err => { 19 | console.log('Unable to install `vue-devtools`: \n', err) 20 | }) 21 | }) 22 | 23 | // Require `main` process to boot app 24 | require('./index') 25 | -------------------------------------------------------------------------------- /src/renderer/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import ElementUI from 'element-ui' 3 | import axios from 'axios' 4 | import VueCodemirror from 'vue-codemirror' 5 | import directive from 'element-ui/packages/popover/src/directive' 6 | 7 | import App from './App' 8 | import router from './router' 9 | import store from './store' 10 | 11 | /* Import css files here */ 12 | import 'element-ui/lib/theme-chalk/index.css' 13 | import './css/font.css' 14 | 15 | if (!process.env.IS_WEB) Vue.use(require('vue-electron')) 16 | Vue.http = Vue.prototype.$http = axios 17 | Vue.config.productionTip = false 18 | Vue.use(VueCodemirror) 19 | Vue.use(ElementUI) 20 | Vue.directive('popover', directive) 21 | 22 | /* eslint-disable no-new */ 23 | new Vue({ 24 | components: { App }, 25 | router, 26 | store, 27 | template: '' 28 | }).$mount('#app') 29 | -------------------------------------------------------------------------------- /src/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | bilibili-column-helper 7 | <% if (htmlWebpackPlugin.options.nodeModules) { %> 8 | 9 | 12 | <% } %> 13 | 14 | 15 |
16 | 17 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.autofetch": true, 3 | "files.autoSave": "onFocusChange", 4 | "editor.tabSize": 2, 5 | "editor.tabCompletion": true, 6 | "eslint.alwaysShowStatus": true, 7 | "eslint.validate": [ 8 | "javascript", 9 | "javascriptreact", 10 | "vue", 11 | { 12 | "language": "html", 13 | "autoFix": true 14 | } 15 | ], 16 | "prettier.eslintIntegration": true, 17 | "vetur.format.defaultFormatter.js": "none", 18 | "todohighlight.include": [ 19 | "**/*.js", 20 | "**/*.jsx", 21 | "**/*.ts", 22 | "**/*.tsx", 23 | "**/*.html", 24 | "**/*.php", 25 | "**/*.css", 26 | "**/*.scss", 27 | "**/*.vue" 28 | ], 29 | "todohighlight.isEnable": true, 30 | "vetur.format.defaultFormatter.html": "js-beautify-html", 31 | "emmet.triggerExpansionOnTab": true, 32 | "files.exclude": { 33 | "node_modules/": true, 34 | ".electron-vue": true 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch", 6 | "type": "node", 7 | "request": "launch", 8 | "program": "${workspaceRoot}/.electron-vue/dev-runner.js", 9 | "stopOnEntry": false, 10 | "args": [], 11 | "cwd": "${workspaceRoot}", 12 | // this points to the electron task runner 13 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron", 14 | "runtimeArgs": ["--nolazy"], 15 | "env": { 16 | "NODE_ENV": "production" 17 | }, 18 | "externalConsole": false, 19 | "sourceMaps": false, 20 | "outDir": null 21 | }, 22 | { 23 | "name": "Attach", 24 | "type": "node", 25 | "request": "attach", 26 | "port": 5858, 27 | "sourceMaps": false, 28 | "outDir": null, 29 | "localRoot": "${workspaceRoot}", 30 | "remoteRoot": null 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode8.3 2 | sudo: required 3 | dist: trusty 4 | language: c 5 | matrix: 6 | include: 7 | - os: osx 8 | - os: linux 9 | env: CC=clang CXX=clang++ npm_config_clang=1 10 | compiler: clang 11 | cache: 12 | directories: 13 | - node_modules 14 | - "$HOME/.electron" 15 | - "$HOME/.cache" 16 | addons: 17 | apt: 18 | packages: 19 | - libgnome-keyring-dev 20 | - icnsutils 21 | before_install: 22 | - mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v1.2.1/git-lfs-$([ 23 | "$TRAVIS_OS_NAME" == "linux" ] && echo "linux" || echo "darwin")-amd64-1.2.1.tar.gz 24 | | tar -xz -C /tmp/git-lfs --strip-components 1 && /tmp/git-lfs/git-lfs pull 25 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils; fi 26 | install: 27 | - nvm install 8 28 | - curl -o- -L https://yarnpkg.com/install.sh | bash 29 | - source ~/.bashrc 30 | - npm install -g xvfb-maybe 31 | - yarn 32 | script: 33 | - yarn run dist 34 | branches: 35 | only: 36 | - master 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Yesterday17 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/renderer/utils/network.js: -------------------------------------------------------------------------------- 1 | const request = require('request-promise').defaults({ 2 | jar: true 3 | }) 4 | 5 | export async function get (options) { 6 | return await request(options) 7 | } 8 | 9 | export async function post (options) { 10 | return await request(options) 11 | } 12 | 13 | export async function getBilibili (uri, cookies) { 14 | const options = getConfig(uri, 'GET', cookies) 15 | return JSON.parse(await get(options)) 16 | } 17 | 18 | export async function postBilibili (uri, cookies, form) { 19 | const options = getConfig(uri, 'POST', cookies) 20 | options['form'] = form 21 | return JSON.parse(await post(options)) 22 | } 23 | 24 | function getConfig (uri, method, cookies) { 25 | return { 26 | uri: uri, 27 | method: method, 28 | gzip: true, 29 | headers: { 30 | 'Connection': 'keep-alive', 31 | 'Cache-Control': 'max-age=0', 32 | 'Upgrade-Insecure-Requests': 1, 33 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36', 34 | 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 35 | 'Accept-Encoding': 'gzip, deflate, br', 36 | 'Accept-Language': 'zh-CN,zh;q=0.9', 37 | 'Cookie': cookies 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.electron-vue/dev-client.js: -------------------------------------------------------------------------------- 1 | const hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') 2 | 3 | hotClient.subscribe(event => { 4 | /** 5 | * Reload browser when HTMLWebpackPlugin emits a new index.html 6 | * 7 | * Currently disabled until jantimon/html-webpack-plugin#680 is resolved. 8 | * https://github.com/SimulatedGREG/electron-vue/issues/437 9 | * https://github.com/jantimon/html-webpack-plugin/issues/680 10 | */ 11 | // if (event.action === 'reload') { 12 | // window.location.reload() 13 | // } 14 | 15 | /** 16 | * Notify `mainWindow` when `main` process is compiling, 17 | * giving notice for an expected reload of the `electron` process 18 | */ 19 | if (event.action === 'compiling') { 20 | document.body.innerHTML += ` 21 | 34 | 35 |
36 | Compiling Main Process... 37 |
38 | ` 39 | } 40 | }) 41 | -------------------------------------------------------------------------------- /src/renderer/store/modules/Config.js: -------------------------------------------------------------------------------- 1 | import Store from 'electron-store' 2 | 3 | let s = new Store({ 4 | name: 'config' 5 | }) 6 | const state = { 7 | config: { 8 | version: '', 9 | 10 | renderer: '', 11 | poster: '', 12 | 13 | cookies: [], 14 | cookie: '', 15 | 16 | favoriteTags: [] 17 | } 18 | } 19 | 20 | const mutations = { 21 | LOAD_CONFIG (state) { 22 | // state.xxx = s.get('') 23 | state.config.version = s.get('version', '1.0.0') 24 | state.config.renderer = s.get('renderer', 'default') 25 | state.config.poster = s.get('poster', 'default') 26 | 27 | state.config.cookies = s.get('cookies', []) 28 | state.config.cookie = s.get('cookie', '') 29 | 30 | state.config.favoriteTags = s.get('favoriteTags', [1, 2, 3]) 31 | }, 32 | SAVE_CONFIG (state) { 33 | s.set(state.config) 34 | }, 35 | UPDATE_COOKIES (state, cookies) { 36 | state.config.cookies = JSON.parse(JSON.stringify(cookies)) 37 | state.config.cookie = cookies 38 | .map(cookie => `${cookie.name}=${cookie.value}`) 39 | .join(';') 40 | }, 41 | UPDATE_COOKIE (state, cookie) { 42 | // Clear cookies 43 | state.config.cookies.splice(1, state.config.cookies.splice.length) 44 | state.config.cookie = cookie 45 | for (let item of cookie.match(/([^=; ]+=[^=; ]+)/g)) { 46 | let ans = /([^=]+)=([^=]+)/.exec(item) 47 | state.config.cookies.push({ 48 | name: ans[1], 49 | value: ans[2] 50 | }) 51 | } 52 | }, 53 | RESET_COOKIES (state) { 54 | state.config.cookies.splice(0, state.config.cookies.length) 55 | state.config.cookie = '' 56 | } 57 | } 58 | 59 | export default { 60 | state, 61 | mutations 62 | } 63 | -------------------------------------------------------------------------------- /test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | const merge = require('webpack-merge') 5 | const webpack = require('webpack') 6 | 7 | const baseConfig = require('../../.electron-vue/webpack.renderer.config') 8 | const projectRoot = path.resolve(__dirname, '../../src/renderer') 9 | 10 | // Set BABEL_ENV to use proper preset config 11 | process.env.BABEL_ENV = 'test' 12 | 13 | let webpackConfig = merge(baseConfig, { 14 | devtool: '#inline-source-map', 15 | plugins: [ 16 | new webpack.DefinePlugin({ 17 | 'process.env.NODE_ENV': '"testing"' 18 | }) 19 | ] 20 | }) 21 | 22 | // don't treat dependencies as externals 23 | delete webpackConfig.entry 24 | delete webpackConfig.externals 25 | delete webpackConfig.output.libraryTarget 26 | 27 | // apply vue option to apply isparta-loader on js 28 | webpackConfig.module.rules 29 | .find(rule => rule.use.loader === 'vue-loader').use.options.loaders.js = 'babel-loader' 30 | 31 | module.exports = config => { 32 | config.set({ 33 | browsers: ['visibleElectron'], 34 | client: { 35 | useIframe: false 36 | }, 37 | coverageReporter: { 38 | dir: './coverage', 39 | reporters: [ 40 | { type: 'lcov', subdir: '.' }, 41 | { type: 'text-summary' } 42 | ] 43 | }, 44 | customLaunchers: { 45 | 'visibleElectron': { 46 | base: 'Electron', 47 | flags: ['--show'] 48 | } 49 | }, 50 | frameworks: ['mocha', 'chai'], 51 | files: ['./index.js'], 52 | preprocessors: { 53 | './index.js': ['webpack', 'sourcemap'] 54 | }, 55 | reporters: ['spec', 'coverage'], 56 | singleRun: true, 57 | webpack: webpackConfig, 58 | webpackMiddleware: { 59 | noInfo: true 60 | } 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /src/renderer/utils/biliNetwork.js: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs' 2 | import * as network from './network' 3 | 4 | export async function getUserInfo (cookies) { 5 | const result = await network.getBilibili( 6 | 'https://member.bilibili.com/x/web/data/article', 7 | cookies 8 | ) 9 | if (result['code'] === 0) { 10 | return result['data'] 11 | } else { 12 | return undefined 13 | } 14 | } 15 | 16 | export async function upcover (image, cookies) { 17 | if (typeof cookies !== 'string') return 18 | 19 | const result = await network.postBilibili('https://member.bilibili.com/x/web/article/upcover', cookies, { 20 | cover: image, 21 | csrf: cookies.match(/bili_jct=([^;]+)/)[1] 22 | }) 23 | return result 24 | } 25 | 26 | export async function upcoverLocal (imagePath, cookies) { 27 | return await upcover('data:image/png;base64,' + fs.readFileSync(imagePath).toString('base64'), cookies) 28 | } 29 | 30 | export async function addUpdate (passage, cookies, renderer) { 31 | if (typeof cookies !== 'string') return 32 | 33 | const cover = await upcoverLocal(passage.data.image, cookies) 34 | console.log(cover) 35 | 36 | const result = await network.postBilibili('http://api.bilibili.com/x/article/creative/draft/addupdate', cookies, { 37 | title: passage.data.name, 38 | banner_url: cover['data']['url'], 39 | content: renderer(passage.content.local), // TODO: Use Remote instead. 40 | summary: '', 41 | words: passage.content.local.length, // TODO: Use Remote instead. 42 | category: passage.data.subtype, 43 | list_id: 0, 44 | tid: 4, 45 | reprint: 0, 46 | tags: passage.data.tags, 47 | image_urls: '', 48 | origin_image_urls: '', 49 | dynamic_intro: '', 50 | csrf: cookies.match(/bili_jct=([^;]+)/)[1] 51 | }) 52 | console.log(result) 53 | return result 54 | } 55 | -------------------------------------------------------------------------------- /src/renderer/css/font.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'icomoon'; 3 | src: url('fonts/icomoon.eot?agyqrb'); 4 | src: url('fonts/icomoon.eot?agyqrb#iefix') format('embedded-opentype'), 5 | url('fonts/icomoon.ttf?agyqrb') format('truetype'), 6 | url('fonts/icomoon.woff?agyqrb') format('woff'), 7 | url('fonts/icomoon.svg?agyqrb#icomoon') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | 12 | [class^="icon-"], [class*=" icon-"] { 13 | /* use !important to prevent issues with browser extensions that change fonts */ 14 | font-family: 'icomoon' !important; 15 | speak: none; 16 | font-style: normal; 17 | font-weight: normal; 18 | font-variant: normal; 19 | text-transform: none; 20 | line-height: 1; 21 | 22 | /* Better Font Rendering =========== */ 23 | -webkit-font-smoothing: antialiased; 24 | -moz-osx-font-smoothing: grayscale; 25 | } 26 | 27 | .icon-coin-yen:before { 28 | content: "\e93e"; 29 | } 30 | .icon-enlarge:before { 31 | content: "\e989"; 32 | } 33 | .icon-enlarge2:before { 34 | content: "\e98b"; 35 | } 36 | .icon-eye:before { 37 | content: "\e9ce"; 38 | } 39 | .icon-eye-plus:before { 40 | content: "\e9cf"; 41 | } 42 | .icon-eye-minus:before { 43 | content: "\e9d0"; 44 | } 45 | .icon-eye-blocked:before { 46 | content: "\e9d1"; 47 | } 48 | .icon-star-empty:before { 49 | content: "\e9d7"; 50 | } 51 | .icon-star-half:before { 52 | content: "\e9d8"; 53 | } 54 | .icon-star-full:before { 55 | content: "\e9d9"; 56 | } 57 | .icon-heart:before { 58 | content: "\e9da"; 59 | } 60 | .icon-minus:before { 61 | content: "\ea0b"; 62 | } 63 | .icon-cancel-circle:before { 64 | content: "\ea0d"; 65 | } 66 | .icon-cross:before { 67 | content: "\ea0f"; 68 | } 69 | .icon-exit:before { 70 | content: "\ea14"; 71 | } 72 | .icon-chat:before { 73 | content: "\e901"; 74 | } -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Bilibili Column Helper - Bilibili 专栏助手 2 | 3 | [![Build Status](https://travis-ci.org/Yesterday17/Bilibili-Column-Helper.svg?branch=master)](https://travis-ci.org/Yesterday17/Bilibili-Column-Helper) 4 | [![Build status](https://ci.appveyor.com/api/projects/status/ima7q96vi3y5y8r0?svg=true)](https://ci.appveyor.com/project/Yesterday17/bilibili-column-helper) 5 | [![codebeat badge](https://codebeat.co/badges/1263b75e-d5e8-4ad0-afe2-9da075ae1cde)](https://codebeat.co/projects/github-com-yesterday17-bilibili-column-helper-master-6e10e5a7-4172-448c-9dee-b3ddf67b0aa4) 6 | 7 | ## 原理 8 | 9 | 本程序以 Electron、Vue.js、Marked 以及 CodeMirror 为主要组件编写,旨在以 Markdown 书写目前并不支持 Markdown 的 Bilibili 专栏。 10 | 从原理来看,本程序将要实现的是将 Bilibili 支持的 HTML 特性以原本的方式渲染;而对于不支持的特性(比如表格等),则进行图片的渲染,以达到相同的显示目的。 11 | 12 | ### 用户隐私 13 | 14 | 本程序通过 Bilibili 提供的 Ajax 登录界面,实现 Bilibili 账户的登录。账户的使用仅限图片的上传以及专栏内容的上传。 15 | 16 | ## 合作开发(Contribution) 17 | 18 | 本工程基于`electron-vue`开发,其有相对完善的[中文文档](https://simulatedgreg.gitbooks.io/electron-vue/content/cn/)可供参考。 19 | 前端框架使用的是`Vue.js`,文档[点此访问](https://cn.vuejs.org/)。 20 | UI 使用的是`Element`,文档[点此访问](http://element-cn.eleme.io)。 21 | 以及`Electron`的文档:[点此访问](https://electronjs.org/)。 22 | 23 | ### 构建过程 24 | 25 | 构建基于 yarn,如果没有安装 yarn 的话就需要执行第一步。在部分系统下(如 Ubuntu 等)使用`npm install -g`时需要带上`sudo`。 26 | 27 | ``` 28 | npm install -g yarn 29 | yarn install 30 | ``` 31 | 32 | 如果在上述过程中遇到 electron 无法下载,可以选择使用`cnpm`或`npm`过渡: 33 | 34 | ``` 35 | cnpm install 36 | ``` 37 | 38 | 然后就可以通过以下步骤运行: 39 | 40 | ``` 41 | yarn dev 42 | ``` 43 | 44 | 如果需要打包,则需要运行: 45 | 46 | ``` 47 | yarn run build 48 | ``` 49 | 50 | # Credits 51 | 52 | [Bilibili zhuanlan Markdown-Tool](https://github.com/zihengCat/bilibili-zhuanlan-markdown-tool) 53 | 54 | Icons made by [Freepik](http://www.freepik.com) from [www.flaticon.com](https://www.flaticon.com/) is licensed by [Creative Commons BY 3.0](http://creativecommons.org/licenses/by/3.0/). -------------------------------------------------------------------------------- /src/renderer/components/subcomponents/Login.vue: -------------------------------------------------------------------------------- 1 | 6 | 59 | 60 | 70 | -------------------------------------------------------------------------------- /.electron-vue/webpack.main.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.BABEL_ENV = 'main' 4 | 5 | const path = require('path') 6 | const { dependencies } = require('../package.json') 7 | const webpack = require('webpack') 8 | 9 | const BabiliWebpackPlugin = require('babili-webpack-plugin') 10 | 11 | let mainConfig = { 12 | entry: { 13 | main: path.join(__dirname, '../src/main/index.js') 14 | }, 15 | externals: [...Object.keys(dependencies || {})], 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.(js)$/, 20 | enforce: 'pre', 21 | exclude: /node_modules/, 22 | use: { 23 | loader: 'eslint-loader', 24 | options: { 25 | formatter: require('eslint-friendly-formatter') 26 | } 27 | } 28 | }, 29 | { 30 | test: /\.js$/, 31 | use: 'babel-loader', 32 | exclude: /node_modules/ 33 | }, 34 | { 35 | test: /\.node$/, 36 | use: 'node-loader' 37 | } 38 | ] 39 | }, 40 | node: { 41 | __dirname: process.env.NODE_ENV !== 'production', 42 | __filename: process.env.NODE_ENV !== 'production' 43 | }, 44 | output: { 45 | filename: '[name].js', 46 | libraryTarget: 'commonjs2', 47 | path: path.join(__dirname, '../dist/electron') 48 | }, 49 | plugins: [], 50 | resolve: { 51 | extensions: ['.js', '.json', '.node'] 52 | }, 53 | target: 'electron-main' 54 | } 55 | 56 | /** 57 | * Adjust mainConfig for development settings 58 | */ 59 | if (process.env.NODE_ENV !== 'production') { 60 | mainConfig.plugins.push( 61 | new webpack.DefinePlugin({ 62 | '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"` 63 | }) 64 | ) 65 | } 66 | 67 | /** 68 | * Adjust mainConfig for production settings 69 | */ 70 | if (process.env.NODE_ENV === 'production') { 71 | mainConfig.plugins.push( 72 | new BabiliWebpackPlugin() 73 | ) 74 | } 75 | 76 | module.exports = mainConfig 77 | -------------------------------------------------------------------------------- /src/main/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import { app, BrowserWindow, globalShortcut } from 'electron' 4 | 5 | /** 6 | * Set `__static` path to static files in production 7 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html 8 | */ 9 | if (process.env.NODE_ENV !== 'development') { 10 | global.__statihtmlWebc = require('path') 11 | .join(__dirname, '/static') 12 | .replace(/\\/g, '\\\\') 13 | } 14 | 15 | let mainWindow 16 | const winURL = 17 | process.env.NODE_ENV === 'development' 18 | ? `http://localhost:9080` 19 | : `file://${__dirname}/index.html` 20 | 21 | function createWindow () { 22 | mainWindow = new BrowserWindow({ 23 | height: 720, 24 | minHeight: 720, 25 | useContentSize: true, 26 | width: 1280, 27 | minWidth: 1280, 28 | frame: false, 29 | webPreferences: { 30 | webSecurity: false 31 | } 32 | }) 33 | 34 | mainWindow.loadURL(winURL) 35 | 36 | globalShortcut.register('F8', function () { 37 | let win = BrowserWindow.getFocusedWindow() 38 | if (!win) return 39 | win.webContents.toggleDevTools() 40 | }) 41 | 42 | mainWindow.on('closed', () => { 43 | mainWindow = null 44 | }) 45 | } 46 | 47 | app.on('ready', createWindow) 48 | 49 | app.on('window-all-closed', () => { 50 | if (process.platform !== 'darwin') { 51 | app.quit() 52 | } 53 | }) 54 | 55 | app.on('activate', () => { 56 | if (mainWindow === null) { 57 | createWindow() 58 | } 59 | }) 60 | 61 | /** 62 | * Auto Updater 63 | * 64 | * Uncomment the following code below and install `electron-updater` to 65 | * support auto updating. Code Signing with a valid certificate is required. 66 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-electron-builder.html#auto-updating 67 | */ 68 | 69 | /* 70 | import { autoUpdater } from 'electron-updater' 71 | 72 | autoUpdater.on('update-downloaded', () => { 73 | autoUpdater.quitAndInstall() 74 | }) 75 | 76 | app.on('ready', () => { 77 | if (process.env.NODE_ENV === 'production') autoUpdater.checkForUpdates() 78 | }) 79 | */ 80 | -------------------------------------------------------------------------------- /src/renderer/utils/biliapi.js: -------------------------------------------------------------------------------- 1 | const request = require('../../../../../../AppData/Local/Microsoft/TypeScript/2.9/node_modules/@types/request').defaults({ 2 | jar: true 3 | }) 4 | const fs = require('fs') 5 | 6 | function upcover (file, cookies, csrf, callback) { 7 | request.post( 8 | 'https://member.bilibili.com/x/web/article/upcover', 9 | { 10 | form: { 11 | cover: createBase64Image(fs.readFileSync(file)), 12 | csrf: csrf 13 | }, 14 | headers: { 15 | Origin: 'https://member.bilibili.com', 16 | Host: 'member.bilibili.com', 17 | Connection: 'keep-alive', 18 | 'X-Requested-With': 'XMLHttpRequest', 19 | 'User-Agent': 20 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36', 21 | 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 22 | Referer: 'https://member.bilibili.com/article-text/home?', 23 | Accept: '*/*', 24 | 'Accept-Encoding': 'gzip, deflate, br', 25 | 'Accept-Language': 'zh-CN,zh;q=0.9', 26 | Cookie: cookies 27 | }, 28 | timeout: 3e4 29 | }, 30 | (err, response, body) => { 31 | if (!err) callback(response.body) 32 | } 33 | ) 34 | } 35 | 36 | function addUpdate (cookies, csrf, callback) { 37 | request.post( 38 | 'http://api.bilibili.com/x/article/creative/draft/addupdate', 39 | { 40 | form: { 41 | title: '==', 42 | banner_url: '', 43 | content: '', 44 | summary: '', 45 | words: 0, 46 | category: 0, 47 | list_id: 0, 48 | tid: 4, 49 | reprint: 0, 50 | tags: [], 51 | image_urls: '', 52 | origin_image_urls: '', 53 | dynamic_intro: '', 54 | csrf: csrf 55 | }, 56 | headers: { 57 | Accept: 'application/json, text/javascript, */*; q=0.01', 58 | 'Accept-Encoding': 'gzip, deflate', 59 | 'Accept-Language': 'zh-CN,zh;q=0.9', 60 | Connection: 'keep-alive', 61 | 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 62 | Cookie: cookies, 63 | Host: 'api.bilibili.com', 64 | Origin: 'http://member.bilibili.com', 65 | Referer: 'http://member.bilibili.com/article-text/home?', 66 | 'User-Agent': 67 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36' 68 | } 69 | }, 70 | (err, response, body) => { 71 | if (!err) callback(response.body) 72 | } 73 | ) 74 | } 75 | 76 | function createBase64Image (stream) { 77 | return 'data:image/bmp;base64,' + stream.toString('base64') 78 | } 79 | 80 | exports.upcover = upcover 81 | exports.addUpdate = addUpdate 82 | -------------------------------------------------------------------------------- /src/renderer/components/subcomponents/UserInfo.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 62 | 63 | 101 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at t@yesterday17.cn. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /src/renderer/utils/renderer.js: -------------------------------------------------------------------------------- 1 | import marked from 'marked' 2 | import Prism from 'prismjs' 3 | import * as languages from './languages' 4 | 5 | // https://github.com/jGleitz/markdown-it-prism/blob/master/index.js 6 | 7 | function RendererException (message) { 8 | this.message = message 9 | } 10 | 11 | const cutoff = [ 12 | '//i0.hdslb.com/bfs/article/', 13 | '0117cbba35e51b0bce5f8c2f6a838e8a087e8ee7.png', 14 | '4aa545dccf7de8d4a93c2b2b8e3265ac0a26d216.png', 15 | '71bf2cd56882a2e97f8b3477c9256f8b09f361d3.png', 16 | 'db75225feabec8d8b64ee7d3c7165cd639554cbc.png', 17 | '4adb9255ada5b97061e610b682b8636764fe50ed.png', 18 | '02db465212d3c374a43c60fa2625cc1caeaab796.png' 19 | ] 20 | 21 | function loadPrismLang (lang, oriLang) { 22 | let langObject = Prism.languages[lang] 23 | try { 24 | if (langObject === undefined) { 25 | if ( 26 | languages.unSupported.indexOf(lang) !== -1 || 27 | (!languages.languagesC.has(lang.toUpperCase()) && 28 | !languages.languagesC.has(oriLang.toUpperCase())) 29 | ) { 30 | throw new RendererException(lang) 31 | } 32 | require('prismjs/components/prism-' + lang) 33 | return { isSupported: true, content: Prism.languages[lang] } 34 | } 35 | return { isSupported: true, content: langObject } 36 | } catch (e) { 37 | return { isSupported: false, content: e.message } 38 | } 39 | } 40 | 41 | export function render (code) { 42 | const defaultOption = { 43 | hr: 3 44 | } 45 | 46 | let renderer = new marked.Renderer() 47 | 48 | renderer.hr = () => { 49 | return ( 50 | '
' + 51 | `` + 54 | '
' 55 | ) 56 | } 57 | 58 | renderer.heading = (text, level, raw) => { 59 | console.log(`Text: ${text}\nLevel: ${level}\nRaw: ${raw}`) 60 | if (level === 1) { 61 | return `

${text}

` 62 | } 63 | return `

${raw}

` 64 | } 65 | 66 | renderer.code = (code, language, isEscaped) => { 67 | // 4-space codeblock 68 | if (language === undefined) { 69 | return `

    ${code}

` 70 | } 71 | 72 | // Others 73 | try { 74 | // shell-like problem 75 | let oriLang = language 76 | if (languages.translate.has(language.toUpperCase())) { 77 | language = languages.translate.get(language.toUpperCase()) 78 | } else if (languages.renderName.has(language.toUpperCase())) { 79 | oriLang = languages.renderName.get(language.toUpperCase()).render 80 | language = languages.renderName.get(language.toUpperCase()).ori 81 | } 82 | 83 | let lang = loadPrismLang(language, oriLang) 84 | 85 | if (!lang.isSupported) { 86 | throw new RendererException(lang.content) 87 | } 88 | const rendered = Prism.highlight(code, lang.content) 89 | const dataLang = languages.languagesC.get(oriLang.toUpperCase()) 90 | 91 | return ( 92 | '
' + 93 | `
${rendered}
` + 94 | '
' 95 | ) 96 | } catch (e) { 97 | return `

此段代码块出现渲染错误!不支持语言:${ 98 | e.message 99 | }!请检查代码块是否书写正确!

` 100 | } 101 | } 102 | 103 | return marked(code, { 104 | renderer: renderer, 105 | gfm: true, 106 | breaks: true, 107 | mangle: false, 108 | silent: true 109 | }) 110 | } 111 | -------------------------------------------------------------------------------- /.electron-vue/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.NODE_ENV = 'production' 4 | 5 | const { say } = require('cfonts') 6 | const chalk = require('chalk') 7 | const del = require('del') 8 | const { spawn } = require('child_process') 9 | const webpack = require('webpack') 10 | const Multispinner = require('multispinner') 11 | 12 | 13 | const mainConfig = require('./webpack.main.config') 14 | const rendererConfig = require('./webpack.renderer.config') 15 | const webConfig = require('./webpack.web.config') 16 | 17 | const doneLog = chalk.bgGreen.white(' DONE ') + ' ' 18 | const errorLog = chalk.bgRed.white(' ERROR ') + ' ' 19 | const okayLog = chalk.bgBlue.white(' OKAY ') + ' ' 20 | const isCI = process.env.CI || false 21 | 22 | if (process.env.BUILD_TARGET === 'clean') clean() 23 | else if (process.env.BUILD_TARGET === 'web') web() 24 | else build() 25 | 26 | function clean () { 27 | del.sync(['build/*', '!build/icons', '!build/icons/icon.*']) 28 | console.log(`\n${doneLog}\n`) 29 | process.exit() 30 | } 31 | 32 | function build () { 33 | greeting() 34 | 35 | del.sync(['dist/electron/*', '!.gitkeep']) 36 | 37 | const tasks = ['main', 'renderer'] 38 | const m = new Multispinner(tasks, { 39 | preText: 'building', 40 | postText: 'process' 41 | }) 42 | 43 | let results = '' 44 | 45 | m.on('success', () => { 46 | process.stdout.write('\x1B[2J\x1B[0f') 47 | console.log(`\n\n${results}`) 48 | console.log(`${okayLog}take it away ${chalk.yellow('`electron-builder`')}\n`) 49 | process.exit() 50 | }) 51 | 52 | pack(mainConfig).then(result => { 53 | results += result + '\n\n' 54 | m.success('main') 55 | }).catch(err => { 56 | m.error('main') 57 | console.log(`\n ${errorLog}failed to build main process`) 58 | console.error(`\n${err}\n`) 59 | process.exit(1) 60 | }) 61 | 62 | pack(rendererConfig).then(result => { 63 | results += result + '\n\n' 64 | m.success('renderer') 65 | }).catch(err => { 66 | m.error('renderer') 67 | console.log(`\n ${errorLog}failed to build renderer process`) 68 | console.error(`\n${err}\n`) 69 | process.exit(1) 70 | }) 71 | } 72 | 73 | function pack (config) { 74 | return new Promise((resolve, reject) => { 75 | webpack(config, (err, stats) => { 76 | config.mode = 'production' 77 | if (err) reject(err.stack || err) 78 | else if (stats.hasErrors()) { 79 | let err = '' 80 | 81 | stats.toString({ 82 | chunks: false, 83 | colors: true 84 | }) 85 | .split(/\r?\n/) 86 | .forEach(line => { 87 | err += ` ${line}\n` 88 | }) 89 | 90 | reject(err) 91 | } else { 92 | resolve(stats.toString({ 93 | chunks: false, 94 | colors: true 95 | })) 96 | } 97 | }) 98 | }) 99 | } 100 | 101 | function web () { 102 | del.sync(['dist/web/*', '!.gitkeep']) 103 | webpack(webConfig, (err, stats) => { 104 | webConfig.mode = 'production' 105 | if (err || stats.hasErrors()) console.log(err) 106 | 107 | console.log(stats.toString({ 108 | chunks: false, 109 | colors: true 110 | })) 111 | 112 | process.exit() 113 | }) 114 | } 115 | 116 | function greeting () { 117 | const cols = process.stdout.columns 118 | let text = '' 119 | 120 | if (cols > 85) text = 'lets-build' 121 | else if (cols > 60) text = 'lets-|build' 122 | else text = false 123 | 124 | if (text && !isCI) { 125 | say(text, { 126 | colors: ['yellow'], 127 | font: 'simple3d', 128 | space: false 129 | }) 130 | } else console.log(chalk.yellow.bold('\n lets-build')) 131 | console.log() 132 | } 133 | -------------------------------------------------------------------------------- /.electron-vue/webpack.web.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.BABEL_ENV = 'web' 4 | 5 | const path = require('path') 6 | const webpack = require('webpack') 7 | 8 | const BabiliWebpackPlugin = require('babili-webpack-plugin') 9 | const CopyWebpackPlugin = require('copy-webpack-plugin') 10 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 11 | const HtmlWebpackPlugin = require('html-webpack-plugin') 12 | const { VueLoaderPlugin } = require('vue-loader') 13 | 14 | let webConfig = { 15 | devtool: '#cheap-module-eval-source-map', 16 | entry: { 17 | web: path.join(__dirname, '../src/renderer/main.js') 18 | }, 19 | module: { 20 | rules: [ 21 | { 22 | test: /\.(js|vue)$/, 23 | enforce: 'pre', 24 | exclude: /node_modules/, 25 | use: { 26 | loader: 'eslint-loader', 27 | options: { 28 | formatter: require('eslint-friendly-formatter') 29 | } 30 | } 31 | }, 32 | { 33 | test: /\.css$/, 34 | use: ['vue-style-loader', 'css-loader'] 35 | }, 36 | { 37 | test: /\.html$/, 38 | use: 'vue-html-loader' 39 | }, 40 | { 41 | test: /\.js$/, 42 | use: 'babel-loader', 43 | include: [ path.resolve(__dirname, '../src/renderer') ], 44 | exclude: /node_modules/ 45 | }, 46 | { 47 | test: /\.vue$/, 48 | use: { 49 | loader: 'vue-loader', 50 | options: { 51 | extractCSS: true, 52 | loaders: { 53 | sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', 54 | scss: 'vue-style-loader!css-loader!sass-loader' 55 | } 56 | } 57 | } 58 | }, 59 | { 60 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 61 | use: { 62 | loader: 'url-loader', 63 | query: { 64 | limit: 10000, 65 | name: 'imgs/[name].[ext]' 66 | } 67 | } 68 | }, 69 | { 70 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 71 | use: { 72 | loader: 'url-loader', 73 | query: { 74 | limit: 10000, 75 | name: 'fonts/[name].[ext]' 76 | } 77 | } 78 | } 79 | ] 80 | }, 81 | plugins: [ 82 | new VueLoaderPlugin(), 83 | new MiniCssExtractPlugin({filename: 'styles.css'}), 84 | new HtmlWebpackPlugin({ 85 | filename: 'index.html', 86 | template: path.resolve(__dirname, '../src/index.ejs'), 87 | minify: { 88 | collapseWhitespace: true, 89 | removeAttributeQuotes: true, 90 | removeComments: true 91 | }, 92 | nodeModules: false 93 | }), 94 | new webpack.DefinePlugin({ 95 | 'process.env.IS_WEB': 'true' 96 | }), 97 | new webpack.HotModuleReplacementPlugin(), 98 | new webpack.NoEmitOnErrorsPlugin() 99 | ], 100 | output: { 101 | filename: '[name].js', 102 | path: path.join(__dirname, '../dist/web') 103 | }, 104 | resolve: { 105 | alias: { 106 | '@': path.join(__dirname, '../src/renderer'), 107 | 'vue$': 'vue/dist/vue.esm.js' 108 | }, 109 | extensions: ['.js', '.vue', '.json', '.css'] 110 | }, 111 | target: 'web' 112 | } 113 | 114 | /** 115 | * Adjust webConfig for production settings 116 | */ 117 | if (process.env.NODE_ENV === 'production') { 118 | webConfig.devtool = '' 119 | 120 | webConfig.plugins.push( 121 | new BabiliWebpackPlugin(), 122 | new CopyWebpackPlugin([ 123 | { 124 | from: path.join(__dirname, '../static'), 125 | to: path.join(__dirname, '../dist/web/static'), 126 | ignore: ['.*'] 127 | } 128 | ]), 129 | new webpack.DefinePlugin({ 130 | 'process.env.NODE_ENV': '"production"' 131 | }), 132 | new webpack.LoaderOptionsPlugin({ 133 | minimize: true 134 | }) 135 | ) 136 | } 137 | 138 | module.exports = webConfig 139 | -------------------------------------------------------------------------------- /src/renderer/store/modules/Passage.js: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs' 2 | import * as path from 'path' 3 | import * as rimraf from 'rimraf' 4 | import * as sharp from 'sharp' 5 | import * as constants from '../../utils/constants' 6 | 7 | const state = { 8 | passageData: new Map(), 9 | passageContent: new Map(), 10 | 11 | currentPassage: { 12 | name: '', 13 | text: '' 14 | } 15 | } 16 | 17 | const mutations = { 18 | // For Editor 19 | SET_CONTENT (state, content) { 20 | state.currentPassage.text = content 21 | }, 22 | SET_PASSAGE (state, payload) { 23 | state.currentPassage.name = payload.name 24 | 25 | // TODO: Optimize interface 26 | state.currentPassage.text = state.passageContent.get(payload.name).local 27 | }, 28 | RESET_PASSAGE (state) { 29 | state.currentPassage = { 30 | name: '', 31 | text: '' 32 | } 33 | }, 34 | SAVE_PASSAGE (state, text) { 35 | // TODO: Save files differently 36 | const content = state.passageContent.get(state.currentPassage.name) 37 | state.passageContent.set(state.currentPassage.name, { 38 | ...content, 39 | local: text 40 | }) 41 | fs.writeFileSync(constants.localMDPath(state.currentPassage.name), text, { 42 | encoding: 'utf-8' 43 | }) 44 | }, 45 | 46 | // Passage Library 47 | NEW_PASSAGE (state, payload) { 48 | fs.mkdirSync(constants.columnPath(payload.name)) 49 | fs.mkdirSync(constants.imagePath(payload.name)) 50 | fs.mkdirSync(constants.documentPath(payload.name)) 51 | 52 | // Save File 53 | fs.writeFileSync( 54 | constants.indexJson(payload.name), 55 | JSON.stringify(payload, undefined, 2), 56 | { encoding: 'utf-8' } 57 | ) 58 | // Set local empty 59 | fs.writeFileSync(constants.localMDPath(payload.name), '', { 60 | encoding: 'utf-8' 61 | }) 62 | 63 | // Set remote empty 64 | fs.writeFileSync(constants.remoteMDPath(payload.name), '', { 65 | encoding: 'utf-8' 66 | }) 67 | 68 | // Copy cover to relative path 69 | sharp(payload.image).toFile(constants.localCoverPath(payload.name)) 70 | payload.image = './images/cover.png' 71 | 72 | // Resolve relative path 73 | payload.image = constants.localCoverPath(payload.name) 74 | 75 | state.passageData.set(payload.name, { 76 | // Deep copy payload and add to map 77 | ...JSON.parse(JSON.stringify(payload)) 78 | }) 79 | 80 | state.passageContent.set(payload.name, { 81 | local: '', 82 | remote: '' 83 | }) 84 | }, 85 | DEL_PASSAGE (state, payload) { 86 | state.passageData.delete(payload.name) 87 | rimraf.sync(constants.columnPath(payload.name)) 88 | }, 89 | 90 | LOAD_PASSAGES (state) { 91 | if (!fs.existsSync(constants.PATH)) { 92 | fs.mkdirSync(constants.PATH) 93 | return 94 | } 95 | 96 | const dirStat = fs.readdirSync(constants.PATH) 97 | for (const d of dirStat) { 98 | if (fs.statSync(constants.columnPath(d)).isDirectory()) { 99 | try { 100 | // Load config 101 | const passage = JSON.parse( 102 | fs.readFileSync( 103 | path.resolve(constants.columnPath(d), 'index.json'), 104 | { encoding: 'utf-8' } 105 | ) 106 | ) 107 | // Load local passage 108 | const local = fs.readFileSync(constants.localMDPath(d), { 109 | encoding: 'utf-8' 110 | }) 111 | // Load remote passage 112 | const remote = fs.readFileSync(constants.remoteMDPath(d), { 113 | encoding: 'utf-8' 114 | }) 115 | // Load cover 116 | passage.image = 117 | passage.image[0] === '.' 118 | ? constants.localCoverPath(passage.name) 119 | : passage.image 120 | 121 | // Add to map 122 | state.passageData.set(d, passage) 123 | state.passageContent.set(d, { 124 | local: local, 125 | remote: remote 126 | }) 127 | } catch (e) { 128 | console.error(e.message) 129 | } 130 | } 131 | } 132 | } 133 | } 134 | 135 | export default { 136 | state, 137 | mutations 138 | } 139 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## [0.1.4-180822](https://github.com/Yesterday17/Bilibili-Column-Helper/compare/v0.1.3-180814...v0.1.4-180822) (2018-08-22) 3 | 4 | 5 | ### Bug Fixes 6 | 7 | * renderer can only render \

now ([3798808](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/3798808)) 8 | 9 | 10 | ### Features 11 | 12 | * save file at a relative path & transform to png format ([0edee84](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/0edee84)) 13 | 14 | 15 | 16 | 17 | ## 0.0.1-SNAPSHOT (2018-08-07) 18 | 19 | 20 | ### Bug Fixes 21 | 22 | * **code:** fix tab switch ([730ce18](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/730ce18)) 23 | * fix wrongly displayed header ([8a8cb1f](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/8a8cb1f)) 24 | * **code:** make devtools enabled with F8 ([4c455a1](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/4c455a1)) 25 | * disable commitlint on ci ([626ec27](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/626ec27)) 26 | * fix codemirror display differently in dev and release ([8be971a](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/8be971a)), closes [#4](https://github.com/Yesterday17/Bilibili-Column-Helper/issues/4) 27 | * **code:** remove passage to make sure passage is empty by deafult ([e01a61a](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/e01a61a)) 28 | * fix not saving after deleting a passage ([f20952e](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/f20952e)) 29 | * fix object.assign shallow copy problem and save passages to file ([3530639](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/3530639)) 30 | * fix overriding text of the previous edition ([4c55a2d](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/4c55a2d)) 31 | * fix required category not essential ([923a6cf](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/923a6cf)) 32 | * fix the wrong display of the main window border ([40311e8](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/40311e8)) 33 | * make it a must to write the title and type of a column ([3d0201b](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/3d0201b)) 34 | * make the whole form cleared when closing the dialog ([e521561](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/e521561)) 35 | 36 | 37 | ### Features 38 | 39 | * **code:** add developer tool shortcut ([d8d3cf8](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/d8d3cf8)) 40 | * **code:** add unit test ([051ed41](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/051ed41)) 41 | * add passage tag ([1d1b455](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/1d1b455)) 42 | * **code:** update to the latest electron-vue ([aa16011](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/aa16011)) 43 | * add information increasement ([d8387c4](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/d8387c4)) 44 | * add subtype to meet bilibili's category ([0666c07](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/0666c07)) 45 | * close editing ([39bc040](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/39bc040)) 46 | * default poster ([2fab19e](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/2fab19e)) 47 | * get category list online ([20b9e5d](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/20b9e5d)) 48 | * hide editor page default ([92ba068](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/92ba068)) 49 | * import vue-devtool to project ([62070bc](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/62070bc)) 50 | * link to appveyor ([9cfa1aa](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/9cfa1aa)) 51 | * **style:** add commitlint to generate changelog and regulate commit ([a398444](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/a398444)) 52 | * make category a dedicated part which can sync with server ([f7b3d82](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/f7b3d82)) 53 | * make codemirror fulfill the whole layout ([ce8b6d8](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/ce8b6d8)) 54 | * make column passages sorted by time ([4b90a52](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/4b90a52)) 55 | * save file automatically ([c5e0b56](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/c5e0b56)) 56 | * simple poster logic ([4189185](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/4189185)) 57 | * support login ([269c39d](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/269c39d)) 58 | * upload image by paste ([5cdc5e4](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/5cdc5e4)) 59 | * users can logout now ([f1ce5a1](https://github.com/Yesterday17/Bilibili-Column-Helper/commit/f1ce5a1)) 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/renderer/components/Setting.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 118 | 119 | -------------------------------------------------------------------------------- /src/renderer/components/Editor.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 103 | 104 | -------------------------------------------------------------------------------- /.electron-vue/dev-runner.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const chalk = require('chalk') 4 | const electron = require('electron') 5 | const path = require('path') 6 | const { say } = require('cfonts') 7 | const { spawn } = require('child_process') 8 | const webpack = require('webpack') 9 | const WebpackDevServer = require('webpack-dev-server') 10 | const webpackHotMiddleware = require('webpack-hot-middleware') 11 | 12 | const mainConfig = require('./webpack.main.config') 13 | const rendererConfig = require('./webpack.renderer.config') 14 | 15 | let electronProcess = null 16 | let manualRestart = false 17 | let hotMiddleware 18 | 19 | function logStats (proc, data) { 20 | let log = '' 21 | 22 | log += chalk.yellow.bold(`┏ ${proc} Process ${new Array((19 - proc.length) + 1).join('-')}`) 23 | log += '\n\n' 24 | 25 | if (typeof data === 'object') { 26 | data.toString({ 27 | colors: true, 28 | chunks: false 29 | }).split(/\r?\n/).forEach(line => { 30 | log += ' ' + line + '\n' 31 | }) 32 | } else { 33 | log += ` ${data}\n` 34 | } 35 | 36 | log += '\n' + chalk.yellow.bold(`┗ ${new Array(28 + 1).join('-')}`) + '\n' 37 | 38 | console.log(log) 39 | } 40 | 41 | function startRenderer () { 42 | return new Promise((resolve, reject) => { 43 | rendererConfig.entry.renderer = [path.join(__dirname, 'dev-client')].concat(rendererConfig.entry.renderer) 44 | rendererConfig.mode = 'development' 45 | 46 | const compiler = webpack(rendererConfig) 47 | hotMiddleware = webpackHotMiddleware(compiler, { 48 | log: false, 49 | heartbeat: 2500 50 | }) 51 | compiler.hooks.compilation.tap('compilation', compilation => { 52 | compilation.hooks.htmlWebpackPluginAfterEmit.tapAsync('html-webpack-plugin-after-emit', (data, cb) => { 53 | hotMiddleware.publish({ action: 'reload' }) 54 | cb() 55 | }) 56 | }) 57 | 58 | compiler.hooks.done.tap('done', stats => { 59 | logStats('Renderer', stats) 60 | }) 61 | 62 | const server = new WebpackDevServer( 63 | compiler, 64 | { 65 | contentBase: path.join(__dirname, '../'), 66 | quiet: true, 67 | before (app, ctx) { 68 | app.use(hotMiddleware) 69 | ctx.middleware.waitUntilValid(() => { 70 | resolve() 71 | }) 72 | } 73 | } 74 | ) 75 | 76 | server.listen(9080) 77 | }) 78 | } 79 | 80 | function startMain () { 81 | return new Promise((resolve, reject) => { 82 | mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main) 83 | mainConfig.mode = 'development' 84 | 85 | const compiler = webpack(mainConfig) 86 | 87 | compiler.hooks.watchRun.tapAsync('watch-run', (compilation, done) => { 88 | logStats('Main', chalk.white.bold('compiling...')) 89 | hotMiddleware.publish({ action: 'compiling' }) 90 | done() 91 | }) 92 | 93 | compiler.watch({}, (err, stats) => { 94 | if (err) { 95 | console.log(err) 96 | return 97 | } 98 | 99 | logStats('Main', stats) 100 | 101 | if (electronProcess && electronProcess.kill) { 102 | manualRestart = true 103 | process.kill(electronProcess.pid) 104 | electronProcess = null 105 | startElectron() 106 | 107 | setTimeout(() => { 108 | manualRestart = false 109 | }, 5000) 110 | } 111 | 112 | resolve() 113 | }) 114 | }) 115 | } 116 | 117 | function startElectron () { 118 | electronProcess = spawn(electron, ['--inspect=5858', path.join(__dirname, '../dist/electron/main.js')]) 119 | 120 | electronProcess.stdout.on('data', data => { 121 | electronLog(data, 'blue') 122 | }) 123 | electronProcess.stderr.on('data', data => { 124 | electronLog(data, 'red') 125 | }) 126 | 127 | electronProcess.on('close', () => { 128 | if (!manualRestart) process.exit() 129 | }) 130 | } 131 | 132 | function electronLog (data, color) { 133 | let log = '' 134 | data = data.toString().split(/\r?\n/) 135 | data.forEach(line => { 136 | log += ` ${line}\n` 137 | }) 138 | if (/[0-9A-z]+/.test(log)) { 139 | console.log( 140 | chalk[color].bold('┏ Electron -------------------') + 141 | '\n\n' + 142 | log + 143 | chalk[color].bold('┗ ----------------------------') + 144 | '\n' 145 | ) 146 | } 147 | } 148 | 149 | function greeting () { 150 | const cols = process.stdout.columns 151 | let text = '' 152 | 153 | if (cols > 104) text = 'electron-vue' 154 | else if (cols > 76) text = 'electron-|vue' 155 | else text = false 156 | 157 | if (text) { 158 | say(text, { 159 | colors: ['yellow'], 160 | font: 'simple3d', 161 | space: false 162 | }) 163 | } else console.log(chalk.yellow.bold('\n electron-vue')) 164 | console.log(chalk.blue(' getting ready...') + '\n') 165 | } 166 | 167 | function init () { 168 | greeting() 169 | 170 | Promise.all([startRenderer(), startMain()]) 171 | .then(() => { 172 | startElectron() 173 | }) 174 | .catch(err => { 175 | console.error(err) 176 | }) 177 | } 178 | 179 | init() 180 | -------------------------------------------------------------------------------- /.electron-vue/webpack.renderer.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.BABEL_ENV = 'renderer' 4 | 5 | const path = require('path') 6 | const { dependencies } = require('../package.json') 7 | const webpack = require('webpack') 8 | 9 | const BabiliWebpackPlugin = require('babili-webpack-plugin') 10 | const CopyWebpackPlugin = require('copy-webpack-plugin') 11 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 12 | const HtmlWebpackPlugin = require('html-webpack-plugin') 13 | const { VueLoaderPlugin } = require('vue-loader') 14 | 15 | /** 16 | * List of node_modules to include in webpack bundle 17 | * 18 | * Required for specific packages like Vue UI libraries 19 | * that provide pure *.vue files that need compiling 20 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals 21 | */ 22 | let whiteListedModules = ['vue'] 23 | 24 | let rendererConfig = { 25 | devtool: '#cheap-module-eval-source-map', 26 | entry: { 27 | renderer: path.join(__dirname, '../src/renderer/main.js') 28 | }, 29 | externals: [ 30 | ...Object.keys(dependencies || {}).filter( 31 | d => !whiteListedModules.includes(d) 32 | ) 33 | ], 34 | module: { 35 | rules: [ 36 | { 37 | test: /\.(js|vue)$/, 38 | enforce: 'pre', 39 | exclude: /node_modules/, 40 | use: { 41 | loader: 'eslint-loader', 42 | options: { 43 | formatter: require('eslint-friendly-formatter') 44 | } 45 | } 46 | }, 47 | { 48 | test: /\.css$/, 49 | use: ['vue-style-loader', 'css-loader'] 50 | }, 51 | { 52 | test: /\.html$/, 53 | use: 'vue-html-loader' 54 | }, 55 | { 56 | test: /\.js$/, 57 | use: 'babel-loader', 58 | exclude: /node_modules/ 59 | }, 60 | { 61 | test: /\.node$/, 62 | use: 'node-loader' 63 | }, 64 | { 65 | test: /\.vue$/, 66 | use: { 67 | loader: 'vue-loader', 68 | options: { 69 | extractCSS: process.env.NODE_ENV === 'production', 70 | loaders: { 71 | sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', 72 | scss: 'vue-style-loader!css-loader!sass-loader' 73 | } 74 | } 75 | } 76 | }, 77 | { 78 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 79 | use: { 80 | loader: 'url-loader', 81 | query: { 82 | limit: 10000, 83 | name: 'imgs/[name]--[folder].[ext]' 84 | } 85 | } 86 | }, 87 | { 88 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 89 | loader: 'url-loader', 90 | options: { 91 | limit: 10000, 92 | name: 'media/[name]--[folder].[ext]' 93 | } 94 | }, 95 | { 96 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 97 | use: { 98 | loader: 'url-loader', 99 | query: { 100 | limit: 10000, 101 | name: 'fonts/[name]--[folder].[ext]' 102 | } 103 | } 104 | } 105 | ] 106 | }, 107 | node: { 108 | __dirname: process.env.NODE_ENV !== 'production', 109 | __filename: process.env.NODE_ENV !== 'production' 110 | }, 111 | plugins: [ 112 | new VueLoaderPlugin(), 113 | new MiniCssExtractPlugin({filename: 'styles.css'}), 114 | new HtmlWebpackPlugin({ 115 | filename: 'index.html', 116 | template: path.resolve(__dirname, '../src/index.ejs'), 117 | minify: { 118 | collapseWhitespace: true, 119 | removeAttributeQuotes: true, 120 | removeComments: true 121 | }, 122 | nodeModules: 123 | process.env.NODE_ENV !== 'production' 124 | ? path.resolve(__dirname, '../node_modules') 125 | : false 126 | }), 127 | new webpack.HotModuleReplacementPlugin() 128 | ], 129 | output: { 130 | filename: '[name].js', 131 | libraryTarget: 'commonjs2', 132 | path: path.join(__dirname, '../dist/electron') 133 | }, 134 | resolve: { 135 | alias: { 136 | '@': path.join(__dirname, '../src/renderer'), 137 | 'vue$': 'vue/dist/vue.esm.js' 138 | }, 139 | extensions: ['.js', '.vue', '.json', '.css', '.node'] 140 | }, 141 | target: 'electron-renderer' 142 | } 143 | 144 | /** 145 | * Adjust rendererConfig for development settings 146 | */ 147 | if (process.env.NODE_ENV !== 'production') { 148 | rendererConfig.plugins.push( 149 | new webpack.DefinePlugin({ 150 | '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"` 151 | }) 152 | ) 153 | } 154 | 155 | /** 156 | * Adjust rendererConfig for production settings 157 | */ 158 | if (process.env.NODE_ENV === 'production') { 159 | rendererConfig.devtool = '' 160 | 161 | rendererConfig.plugins.push( 162 | new BabiliWebpackPlugin(), 163 | new CopyWebpackPlugin([ 164 | { 165 | from: path.join(__dirname, '../static'), 166 | to: path.join(__dirname, '../dist/electron/static'), 167 | ignore: ['.*'] 168 | } 169 | ]), 170 | new webpack.LoaderOptionsPlugin({ 171 | minimize: true 172 | }) 173 | ) 174 | } 175 | 176 | module.exports = rendererConfig 177 | -------------------------------------------------------------------------------- /src/renderer/App.vue: -------------------------------------------------------------------------------- 1 | 52 | 53 | 122 | 123 | 219 | -------------------------------------------------------------------------------- /src/renderer/components/subcomponents/Passage.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 105 | 106 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bilibili-column-helper", 3 | "version": "0.1.4-180822", 4 | "author": "Yesterday17 ", 5 | "description": "Transfers markdown to bilibili column passage. Using Electron & Marked.", 6 | "homepage": "https://github.com/Yesterday17/Bilibili-Column-Helper/", 7 | "license": "MIT", 8 | "main": "./dist/electron/main.js", 9 | "scripts": { 10 | "rebuild": "./node_modules/.bin/electron-rebuild", 11 | "dist": "node .electron-vue/build.js && electron-builder --publish always", 12 | "distRelease": "node .electron-vue/build.js && electron-builder --publish onTagOrDraft", 13 | "build": "node .electron-vue/build.js && electron-builder", 14 | "build:dir": "node .electron-vue/build.js && electron-builder --dir", 15 | "build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js", 16 | "build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js", 17 | "dev": "node .electron-vue/dev-runner.js", 18 | "e2e": "npm run pack && mocha test/e2e", 19 | "lint": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter src test", 20 | "lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix src test", 21 | "pack": "npm run pack:main && npm run pack:renderer", 22 | "pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js", 23 | "pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js", 24 | "test": "npm run unit && npm run e2e", 25 | "unit": "karma start test/unit/karma.conf.js", 26 | "postinstall": "npm run lint:fix && electron-builder install-app-deps", 27 | "commitmsg": "commitlint -e $GIT_PARAMS", 28 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s" 29 | }, 30 | "build": { 31 | "productName": "哔哩哔哩专栏助手", 32 | "appId": "cn.yesterday17.bilibili-column-helper", 33 | "directories": { 34 | "output": "build" 35 | }, 36 | "files": [ 37 | "dist/electron/**/*" 38 | ], 39 | "dmg": { 40 | "contents": [ 41 | { 42 | "x": 410, 43 | "y": 150, 44 | "type": "link", 45 | "path": "/Applications" 46 | }, 47 | { 48 | "x": 130, 49 | "y": 150, 50 | "type": "file" 51 | } 52 | ] 53 | }, 54 | "mac": { 55 | "icon": "build/icons/icon.icns", 56 | "category": "public.app-category.utilities" 57 | }, 58 | "win": { 59 | "icon": "build/icons/icon.png", 60 | "target": [ 61 | "nsis", 62 | "portable", 63 | "zip" 64 | ] 65 | }, 66 | "linux": { 67 | "icon": "build/icons", 68 | "category": "Utility", 69 | "target": [ 70 | "deb", 71 | "tar.gz", 72 | "AppImage" 73 | ] 74 | } 75 | }, 76 | "dependencies": { 77 | "bilibili-zhuanlan-markdown-tool": "^1.0.2", 78 | "electron-store": "^2.0.0", 79 | "element-ui": "^2.4.0", 80 | "marked": "^0.4.0", 81 | "prismjs": "^1.15.0", 82 | "request": "^2.83.0", 83 | "request-promise": "^4.2.2", 84 | "rimraf": "^2.6.2", 85 | "sharp": "^0.20.7", 86 | "vue": "^2.5.16", 87 | "vue-codemirror-electron": "^3.0.7", 88 | "vue-electron": "^1.0.6", 89 | "vue-router": "^3.0.1", 90 | "vuex": "^3.0.1" 91 | }, 92 | "devDependencies": { 93 | "@commitlint/cli": "^7.0.0", 94 | "@commitlint/config-conventional": "^7.0.1", 95 | "axios": "^0.18.0", 96 | "babel-core": "^6.25.0", 97 | "babel-eslint": "^8.2.6", 98 | "babel-loader": "^7.1.1", 99 | "babel-plugin-istanbul": "^4.1.1", 100 | "babel-plugin-transform-runtime": "^6.23.0", 101 | "babel-preset-env": "^1.6.0", 102 | "babel-preset-stage-0": "^6.24.1", 103 | "babel-register": "^6.24.1", 104 | "babili-webpack-plugin": "^0.1.2", 105 | "cfonts": "^2.1.3", 106 | "chai": "^4.0.0", 107 | "chalk": "^2.1.0", 108 | "codemirror": "^5.35.0", 109 | "commitlint": "^7.0.0", 110 | "copy-webpack-plugin": "^4.0.1", 111 | "cross-env": "^5.0.5", 112 | "css-loader": "^1.0.0", 113 | "del": "^3.0.0", 114 | "devtron": "^1.4.0", 115 | "electron": "^2.0.6", 116 | "electron-builder": "^20.27.1", 117 | "electron-debug": "^2.0.0", 118 | "electron-devtools-installer": "^2.2.4", 119 | "electron-rebuild": "^1.8.2", 120 | "eslint": "^5.3.0", 121 | "eslint-config-standard": "^11.0.0", 122 | "eslint-friendly-formatter": "^4.0.1", 123 | "eslint-loader": "^2.1.0", 124 | "eslint-plugin-html": "^4.0.5", 125 | "eslint-plugin-import": "^2.7.0", 126 | "eslint-plugin-node": "^7.0.1", 127 | "eslint-plugin-promise": "^3.5.0", 128 | "eslint-plugin-standard": "^3.1.0", 129 | "file-loader": "^1.1.11", 130 | "glob": "^7.1.2", 131 | "html-webpack-plugin": "^3.2.0", 132 | "husky": "^0.14.3", 133 | "inject-loader": "^4.0.1", 134 | "karma": "^2.0.5", 135 | "karma-chai": "^0.1.0", 136 | "karma-coverage": "^1.1.1", 137 | "karma-electron": "^6.0.0", 138 | "karma-mocha": "^1.2.0", 139 | "karma-sourcemap-loader": "^0.3.7", 140 | "karma-spec-reporter": "^0.0.32", 141 | "karma-webpack": "^3.0.0", 142 | "mini-css-extract-plugin": "0.4.0", 143 | "mocha": "^5.2.0", 144 | "multispinner": "^0.2.1", 145 | "node-loader": "^0.6.0", 146 | "require-dir": "^1.0.0", 147 | "spectron": "^3.7.1", 148 | "style-loader": "^0.22.1", 149 | "url-loader": "^1.0.1", 150 | "vue-codemirror": "^4.0.4", 151 | "vue-html-loader": "^1.2.4", 152 | "vue-loader": "^15.3.0", 153 | "vue-style-loader": "^4.1.1", 154 | "vue-template-compiler": "^2.4.2", 155 | "webpack": "^4.16.5", 156 | "webpack-cli": "^3.1.0", 157 | "webpack-dev-server": "^3.1.5", 158 | "webpack-hot-middleware": "^2.18.2", 159 | "webpack-merge": "^4.1.0" 160 | }, 161 | "__npminstall_done": false 162 | } 163 | -------------------------------------------------------------------------------- /src/renderer/store/modules/Sync.js: -------------------------------------------------------------------------------- 1 | import Store from 'electron-store' 2 | import * as biliNetwork from '../../utils/biliNetwork' 3 | 4 | let sync = new Store({ 5 | name: 'sync' 6 | }) 7 | 8 | const defaults = { 9 | category: [ 10 | { 11 | id: 2, 12 | parent_id: 0, 13 | name: '动画', 14 | children: [ 15 | { 16 | id: 4, 17 | parent_id: 2, 18 | name: '动漫杂谈' 19 | }, 20 | { 21 | id: 5, 22 | parent_id: 2, 23 | name: '动漫资讯' 24 | }, 25 | { 26 | id: 31, 27 | parent_id: 2, 28 | name: '动画技术' 29 | } 30 | ] 31 | }, 32 | { 33 | id: 1, 34 | parent_id: 0, 35 | name: '游戏', 36 | children: [ 37 | { 38 | id: 6, 39 | parent_id: 1, 40 | name: '单机游戏' 41 | }, 42 | { 43 | id: 7, 44 | parent_id: 1, 45 | name: '电子竞技' 46 | }, 47 | { 48 | id: 8, 49 | parent_id: 1, 50 | name: '手机游戏' 51 | }, 52 | { 53 | id: 9, 54 | parent_id: 1, 55 | name: '网络游戏' 56 | }, 57 | { 58 | id: 10, 59 | parent_id: 1, 60 | name: '桌游棋牌' 61 | } 62 | ] 63 | }, 64 | { 65 | id: 28, 66 | parent_id: 0, 67 | name: '影视', 68 | children: [ 69 | { 70 | id: 12, 71 | parent_id: 28, 72 | name: '电影' 73 | }, 74 | { 75 | id: 35, 76 | parent_id: 28, 77 | name: '电视剧' 78 | }, 79 | { 80 | id: 36, 81 | parent_id: 28, 82 | name: '纪录片' 83 | }, 84 | { 85 | id: 37, 86 | parent_id: 28, 87 | name: '综艺' 88 | } 89 | ] 90 | }, 91 | { 92 | id: 3, 93 | parent_id: 0, 94 | name: '生活', 95 | children: [ 96 | { 97 | id: 13, 98 | parent_id: 3, 99 | name: '美食' 100 | }, 101 | { 102 | id: 21, 103 | parent_id: 3, 104 | name: '萌宠' 105 | }, 106 | { 107 | id: 14, 108 | parent_id: 3, 109 | name: '时尚' 110 | }, 111 | { 112 | id: 22, 113 | parent_id: 3, 114 | name: '运动' 115 | }, 116 | { 117 | id: 15, 118 | parent_id: 3, 119 | name: '日常' 120 | } 121 | ] 122 | }, 123 | { 124 | id: 29, 125 | parent_id: 0, 126 | name: '兴趣', 127 | children: [ 128 | { 129 | id: 23, 130 | parent_id: 29, 131 | name: '绘画' 132 | }, 133 | { 134 | id: 24, 135 | parent_id: 29, 136 | name: '手工' 137 | }, 138 | { 139 | id: 38, 140 | parent_id: 29, 141 | name: '摄影' 142 | }, 143 | { 144 | id: 39, 145 | parent_id: 29, 146 | name: '音乐舞蹈' 147 | }, 148 | { 149 | id: 11, 150 | parent_id: 29, 151 | name: '模型手办' 152 | } 153 | ] 154 | }, 155 | { 156 | id: 16, 157 | parent_id: 0, 158 | name: '轻小说', 159 | children: [ 160 | { 161 | id: 18, 162 | parent_id: 16, 163 | name: '原创连载' 164 | }, 165 | { 166 | id: 19, 167 | parent_id: 16, 168 | name: '同人连载' 169 | }, 170 | { 171 | id: 32, 172 | parent_id: 16, 173 | name: '短篇小说' 174 | }, 175 | { 176 | id: 20, 177 | parent_id: 16, 178 | name: '小说杂谈' 179 | } 180 | ] 181 | }, 182 | { 183 | id: 17, 184 | parent_id: 0, 185 | name: '科技', 186 | children: [ 187 | { 188 | id: 25, 189 | parent_id: 17, 190 | name: '人文历史' 191 | }, 192 | { 193 | id: 33, 194 | parent_id: 17, 195 | name: '自然' 196 | }, 197 | { 198 | id: 26, 199 | parent_id: 17, 200 | name: '数码' 201 | }, 202 | { 203 | id: 27, 204 | parent_id: 17, 205 | name: '汽车' 206 | }, 207 | { 208 | id: 34, 209 | parent_id: 17, 210 | name: '学习' 211 | } 212 | ] 213 | } 214 | ] 215 | } 216 | 217 | const state = { 218 | category: [], 219 | categoryList: [], 220 | categoryMap: new Map(), 221 | 222 | count: { 223 | view: 0, 224 | reply: 0, 225 | like: 0, 226 | coin: 0, 227 | fav: 0, 228 | 229 | incr_view: 0, 230 | incr_reply: 0, 231 | incr_like: 0, 232 | incr_coin: 0, 233 | incr_fav: 0 234 | } 235 | } 236 | 237 | const mutations = { 238 | LOAD_SYNC_CONFIG (state) { 239 | state.category = sync.get('category', defaults.category) 240 | 241 | for (const c of state.category) { 242 | state.categoryMap.set(c.id, c.name) 243 | state.categoryList.push(c.id) 244 | 245 | for (const cd of c.children) { 246 | state.categoryMap.set(cd.id, cd.name) 247 | } 248 | } 249 | }, 250 | SAVE_SYNC_CONFIG (state) { 251 | sync.set('category', state.category) 252 | }, 253 | UPDATE_USERINFO (state, result) { 254 | state.count.view = result['view'] 255 | state.count.reply = result['reply'] 256 | state.count.like = result['like'] 257 | state.count.coin = result['coin'] 258 | state.count.fav = result['fav'] 259 | state.count.share = result['share'] 260 | 261 | state.count.incr_view = result['incr_view'] 262 | state.count.incr_reply = result['incr_reply'] 263 | state.count.incr_like = result['incr_like'] 264 | state.count.incr_coin = result['incr_coin'] 265 | state.count.incr_fav = result['incr_fav'] 266 | state.count.incr_share = result['incr_share'] 267 | } 268 | } 269 | 270 | const actions = { 271 | SYNC_CONFIG ({ commit }, cookie) { 272 | // TODO: Sync category here. 273 | // state.category 274 | 275 | biliNetwork.getUserInfo(cookie).then(result => { 276 | if (result !== undefined) { 277 | commit('UPDATE_USERINFO', result) 278 | } 279 | }) 280 | } 281 | } 282 | 283 | export default { 284 | state, 285 | mutations, 286 | actions 287 | } 288 | -------------------------------------------------------------------------------- /src/renderer/components/Passages.vue: -------------------------------------------------------------------------------- 1 | 59 | 60 | 167 | 168 | -------------------------------------------------------------------------------- /src/renderer/components/About.vue: -------------------------------------------------------------------------------- 1 | 141 | 142 | 153 | 154 | -------------------------------------------------------------------------------- /src/renderer/css/fonts/icomoon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/renderer/utils/languages.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Regex : Transfer `{text: 'xxx', value: 'xxx'}`(Formatted) to ['xxx', 'xxx'] 3 | * {\n 'text': ([^,]+),\n 'value': ([^\n]+)\n } 4 | * [$1, $2] 5 | */ 6 | 7 | export const languages = new Map([ 8 | ['APL', 'text/apl@apl@APL'], 9 | ['ASP.NET', 'application/x-aspx@asp@ASP.NET'], 10 | ['Asterisk', 'text/x-asterisk@r@Asterisk'], 11 | ['Brainfuck', 'text/x-brainfuck@brainfuck@Brainfuck'], 12 | ['C', 'text/x-csrc@clike@C'], 13 | ['C++', 'text/x-c++src@cpp@C++'], 14 | ['Cobol', 'text/x-cobol@c@Cobol'], 15 | ['C#', 'text/x-csharp@csharp@C#'], 16 | ['Clojure', 'text/x-clojure@clojure@Clojure'], 17 | ['ClojureScript', 'text/x-clojurescript@clojure@ClojureScript'], 18 | ['Closure Stylesheets (GSS)', 'text/x-gss@css@Closure Stylesheets (GSS)'], 19 | ['CMake', 'text/x-cmake@c@CMake'], 20 | ['CoffeeScript', 'coffeescript@coffeescript@CoffeeScript'], 21 | ['Cypher', 'application/x-cypher-query@r@Cypher'], 22 | ['Cython', 'text/x-cython@python@Cython'], 23 | ['Crystal', 'text/x-crystal@crystal@Crystal'], 24 | ['CSS', 'text/css@css@CSS'], 25 | ['CQL', 'text/x-cassandra@sql@CQL'], 26 | ['D', 'text/x-d@d@D'], 27 | ['Dart', 'dart@dart@Dart'], 28 | ['diff', 'text/x-diff@diff@diff'], 29 | ['Django', 'text/x-django@django@Django'], 30 | ['Dockerfile', 'text/x-dockerfile@dockerfile@Dockerfile'], 31 | ['DTD', 'application/xml-dtd@d@DTD'], 32 | ['Dylan', 'text/x-dylan@d@Dylan'], 33 | ['ECL', 'text/x-ecl@c@ECL'], 34 | ['edn', 'application/edn@clojure@edn'], 35 | ['Eiffel', 'text/x-eiffel@eiffel@Eiffel'], 36 | ['Elm', 'text/x-elm@elm@Elm'], 37 | ['Embedded Javascript', 'application/x-ejs@d@Embedded Javascript'], 38 | ['Embedded Ruby', 'application/x-erb@d@Embedded Ruby'], 39 | ['Erlang', 'text/x-erlang@erlang@Erlang'], 40 | ['Esper', 'text/x-esper@sql@Esper'], 41 | ['Factor', 'text/x-factor@r@Factor'], 42 | ['FCL', 'text/x-fcl@c@FCL'], 43 | ['Forth', 'text/x-forth@r@Forth'], 44 | ['Fortran', 'text/x-fortran@fortran@Fortran'], 45 | ['F#', 'text/x-fsharp@fsharp@F#'], 46 | ['Fsharp', 'text/x-fsharp@fsharp@F#'], 47 | ['Gherkin', 'text/x-feature@gherkin@Gherkin'], 48 | ['Go', 'text/x-go@go@Go'], 49 | ['Groovy', 'text/x-groovy@groovy@Groovy'], 50 | ['HAML', 'text/x-haml@haml@HAML'], 51 | ['Haskell', 'text/x-haskell@haskell@Haskell'], 52 | ['Haskell (Literate)', 'text/x-literate-haskell@r@Haskell (Literate)'], 53 | ['Haxe', 'text/x-haxe@haxe@Haxe'], 54 | ['HXML', 'text/x-hxml@haxe@HXML'], 55 | ['HTML', 'text/html@html@HTML'], 56 | ['HTTP', 'message/http@http@HTTP'], 57 | ['IDL', 'text/x-idl@d@IDL'], 58 | ['Pug', 'text/x-pug@pug@Pug'], 59 | ['Java', 'text/x-java@clike@Java'], 60 | ['JavaScript', 'javascript@javascript@JavaScript'], 61 | ['JSON', 'javascript@javascript@JSON'], 62 | ['JSON-LD', 'application/ld+json@javascript@JSON-LD'], 63 | ['JSX', 'text/jsx@jsx@JSX'], 64 | ['Jinja2', 'null@jinja2@Jinja2'], 65 | ['Julia', 'text/x-julia@julia@Julia'], 66 | ['Kotlin', 'text/x-kotlin@clike@Kotlin'], 67 | ['LESS', 'text/x-less@css@LESS'], 68 | ['LiveScript', 'text/x-livescript@livescript@LiveScript'], 69 | ['Lua', 'text/x-lua@lua@Lua'], 70 | ['Markdown', 'text/x-markdown@markdown@Markdown'], 71 | ['mIRC', 'text/mirc@r@mIRC'], 72 | ['MariaDB SQL', 'text/x-mariadb@sql@MariaDB SQL'], 73 | ['Mathematica', 'text/x-mathematica@c@Mathematica'], 74 | ['Modelica', 'text/x-modelica@d@Modelica'], 75 | ['MS SQL', 'text/x-mssql@sql@MS SQL'], 76 | ['MySQL', 'text/x-mysql@sql@MySQL'], 77 | ['Nginx', 'text/x-nginx-conf@nginx@Nginx'], 78 | ['NSIS', 'text/x-nsis@nsis@NSIS'], 79 | ['NTriples', 'ntriples@rip@NTriples'], 80 | ['ObjectiveC', 'text/x-objectivec@clike@Objective-C'], 81 | ['Octave', 'text/x-octave@c@Octave'], 82 | ['Oz', 'text/x-oz@oz@Oz'], 83 | ['Pascal', 'text/x-pascal@pascal@Pascal'], 84 | ['Perl', 'text/x-perl@perl@Perl'], 85 | ['PHP', 'php@php@PHP'], 86 | ['PLSQL', 'text/x-plsql@sql@PLSQL'], 87 | ['PowerShell', 'application/x-powershell@powershell@PowerShell'], 88 | ['Properties', 'text/x-properties@properties@Properties files'], 89 | ['Properties files', 'text/x-properties@properties@Properties files'], 90 | ['ProtoBuf', 'text/x-protobuf@protobuf@ProtoBuf'], 91 | ['Python', 'text/x-python@python@Python'], 92 | ['Puppet', 'text/x-puppet@puppet@Puppet'], 93 | ['R', 'text/x-rsrc@r@R'], 94 | ['RPM Changes', 'text/x-rpm-changes@r@RPM Changes'], 95 | ['RPM Spec', 'text/x-rpm-spec@r@RPM Spec'], 96 | ['Ruby', 'text/x-ruby@ruby@Ruby'], 97 | ['Rust', 'text/x-rustsrc@rust@Rust'], 98 | ['Sass', 'text/x-sass@sass@Sass'], 99 | ['Scala', 'text/x-scala@clike@Scala'], 100 | ['Scheme', 'text/x-scheme@scheme@Scheme'], 101 | ['SCSS', 'text/x-scss@css@SCSS'], 102 | ['Shell', 'shell@shell@Shell'], 103 | ['Smalltalk', 'text/x-stsrc@smalltalk@Smalltalk'], 104 | ['Smarty', 'text/x-smarty@smarty@Smarty'], 105 | ['Solr', 'text/x-solr@r@Solr'], 106 | ['SQL', 'text/x-sql@sql@SQL'], 107 | ['SQLite', 'text/x-sqlite@sql@SQLite'], 108 | ['Squirrel', 'text/x-squirrel@clike@Squirrel'], 109 | ['Stylus', 'text/x-styl@stylus@Stylus'], 110 | ['Swift', 'text/x-swift@swift@Swift'], 111 | ['SystemVerilog', 'text/x-systemverilog@verilog@SystemVerilog'], 112 | ['Tcl', 'text/x-tcl@tcl@Tcl'], 113 | ['Textile', 'text/x-textile@textile@Textile'], 114 | ['TiddlyWiki ', 'text/x-tiddlywiki@wiki@TiddlyWiki '], 115 | ['Tornado', 'text/x-tornado@r@Tornado'], 116 | ['troff', 'text/troff@r@troff'], 117 | ['TTCN', 'text/x-ttcn@c@TTCN'], 118 | ['TTCN_CFG', 'text/x-ttcn-cfg@c@TTCN_CFG'], 119 | ['Turtle', 'text/turtle@r@Turtle'], 120 | ['TypeScript', 'application/typescript@ts@TypeScript'], 121 | ['Tsx', 'text/typescript-jsx@tsx@TypeScript-JSX'], 122 | ['TypeScript-JSX', 'text/typescript-jsx@tsx@TypeScript-JSX'], 123 | ['Twig', 'text/x-twig@twig@Twig'], 124 | ['Web IDL', 'text/x-webidl@d@Web IDL'], 125 | ['VBScript', 'text/vbscript@rip@VBScript'], 126 | ['Velocity', 'text/velocity@c@Velocity'], 127 | ['Verilog', 'text/x-verilogverilog@verilog@Verilog'], 128 | ['VHDL', 'text/x-vhdl@vhdl@VHDL'], 129 | ['XML', 'xml@xml@XML'], 130 | ['XQuery', 'application/xquery@r@XQuery'], 131 | ['Yacas', 'text/x-yacas@c@Yacas'], 132 | ['YAML', 'yaml@yaml@YAML'] 133 | ]) 134 | 135 | export const languagesC = new Map([ 136 | ['APL', 'text/apl@apl@APL'], 137 | ['ASP.NET', 'application/x-aspx@asp@ASP.NET'], 138 | ['ASTERISK', 'text/x-asterisk@r@Asterisk'], 139 | ['BRAINFUCK', 'text/x-brainfuck@brainfuck@Brainfuck'], 140 | ['C', 'text/x-csrc@clike@C'], 141 | ['C++', 'text/x-c++src@cpp@C++'], 142 | ['COBOL', 'text/x-cobol@c@Cobol'], 143 | ['C#', 'text/x-csharp@csharp@C#'], 144 | ['CLOJURE', 'text/x-clojure@clojure@Clojure'], 145 | ['CLOJURESCRIPT', 'text/x-clojurescript@clojure@ClojureScript'], 146 | ['CLOSURE STYLESHEETS (GSS)', 'text/x-gss@css@Closure Stylesheets (GSS)'], 147 | ['CMAKE', 'text/x-cmake@c@CMake'], 148 | ['COFFEESCRIPT', 'coffeescript@coffeescript@CoffeeScript'], 149 | ['CYPHER', 'application/x-cypher-query@r@Cypher'], 150 | ['CYTHON', 'text/x-cython@python@Cython'], 151 | ['CRYSTAL', 'text/x-crystal@crystal@Crystal'], 152 | ['CSS', 'text/css@css@CSS'], 153 | ['CQL', 'text/x-cassandra@sql@CQL'], 154 | ['D', 'text/x-d@d@D'], 155 | ['DART', 'dart@dart@Dart'], 156 | ['DIFF', 'text/x-diff@diff@diff'], 157 | ['DJANGO', 'text/x-django@django@Django'], 158 | ['DOCKERFILE', 'text/x-dockerfile@dockerfile@Dockerfile'], 159 | ['DTD', 'application/xml-dtd@d@DTD'], 160 | ['DYLAN', 'text/x-dylan@d@Dylan'], 161 | ['ECL', 'text/x-ecl@c@ECL'], 162 | ['EDN', 'application/edn@clojure@edn'], 163 | ['EIFFEL', 'text/x-eiffel@eiffel@Eiffel'], 164 | ['ELM', 'text/x-elm@elm@Elm'], 165 | ['EMBEDDED JAVASCRIPT', 'application/x-ejs@d@Embedded Javascript'], 166 | ['EMBEDDED RUBY', 'application/x-erb@d@Embedded Ruby'], 167 | ['ERLANG', 'text/x-erlang@erlang@Erlang'], 168 | ['ESPER', 'text/x-esper@sql@Esper'], 169 | ['FACTOR', 'text/x-factor@r@Factor'], 170 | ['FCL', 'text/x-fcl@c@FCL'], 171 | ['FORTH', 'text/x-forth@r@Forth'], 172 | ['FORTRAN', 'text/x-fortran@fortran@Fortran'], 173 | ['F#', 'text/x-fsharp@fsharp@F#'], 174 | ['FSHARP', 'text/x-fsharp@fsharp@F#'], 175 | ['GHERKIN', 'text/x-feature@gherkin@Gherkin'], 176 | ['GO', 'text/x-go@go@Go'], 177 | ['GROOVY', 'text/x-groovy@groovy@Groovy'], 178 | ['HAML', 'text/x-haml@haml@HAML'], 179 | ['HASKELL', 'text/x-haskell@haskell@Haskell'], 180 | ['HASKELL (LITERATE)', 'text/x-literate-haskell@r@Haskell (Literate)'], 181 | ['HAXE', 'text/x-haxe@haxe@Haxe'], 182 | ['HXML', 'text/x-hxml@haxe@HXML'], 183 | ['HTML', 'text/html@html@HTML'], 184 | ['HTTP', 'message/http@http@HTTP'], 185 | ['IDL', 'text/x-idl@d@IDL'], 186 | ['PUG', 'text/x-pug@pug@Pug'], 187 | ['JAVA', 'text/x-java@clike@Java'], 188 | ['JAVASCRIPT', 'javascript@javascript@JavaScript'], 189 | ['JSON', 'javascript@javascript@JSON'], 190 | ['JSON-LD', 'application/ld+json@javascript@JSON-LD'], 191 | ['JSX', 'text/jsx@jsx@JSX'], 192 | ['JINJA2', 'null@jinja2@Jinja2'], 193 | ['JULIA', 'text/x-julia@julia@Julia'], 194 | ['KOTLIN', 'text/x-kotlin@clike@Kotlin'], 195 | ['LESS', 'text/x-less@css@LESS'], 196 | ['LIVESCRIPT', 'text/x-livescript@livescript@LiveScript'], 197 | ['LUA', 'text/x-lua@lua@Lua'], 198 | ['MARKDOWN', 'text/x-markdown@markdown@Markdown'], 199 | ['MIRC', 'text/mirc@r@mIRC'], 200 | ['MARIADB SQL', 'text/x-mariadb@sql@MariaDB SQL'], 201 | ['MATHEMATICA', 'text/x-mathematica@c@Mathematica'], 202 | ['MODELICA', 'text/x-modelica@d@Modelica'], 203 | ['MS SQL', 'text/x-mssql@sql@MS SQL'], 204 | ['MYSQL', 'text/x-mysql@sql@MySQL'], 205 | ['NGINX', 'text/x-nginx-conf@nginx@Nginx'], 206 | ['NSIS', 'text/x-nsis@nsis@NSIS'], 207 | ['NTRIPLES', 'ntriples@rip@NTriples'], 208 | ['OBJECTIVEC', 'text/x-objectivec@clike@Objective-C'], 209 | ['OCTAVE', 'text/x-octave@c@Octave'], 210 | ['OZ', 'text/x-oz@oz@Oz'], 211 | ['PASCAL', 'text/x-pascal@pascal@Pascal'], 212 | ['PERL', 'text/x-perl@perl@Perl'], 213 | ['PHP', 'php@php@PHP'], 214 | ['PLSQL', 'text/x-plsql@sql@PLSQL'], 215 | ['POWERSHELL', 'application/x-powershell@powershell@PowerShell'], 216 | ['PROPERTIES', 'text/x-properties@properties@Properties files'], 217 | ['PROPERTIES FILES', 'text/x-properties@properties@Properties files'], 218 | ['PROTOBUF', 'text/x-protobuf@protobuf@ProtoBuf'], 219 | ['PYTHON', 'text/x-python@python@Python'], 220 | ['PUPPET', 'text/x-puppet@puppet@Puppet'], 221 | ['R', 'text/x-rsrc@r@R'], 222 | ['RPM CHANGES', 'text/x-rpm-changes@r@RPM Changes'], 223 | ['RPM SPEC', 'text/x-rpm-spec@r@RPM Spec'], 224 | ['RUBY', 'text/x-ruby@ruby@Ruby'], 225 | ['RUST', 'text/x-rustsrc@rust@Rust'], 226 | ['SASS', 'text/x-sass@sass@Sass'], 227 | ['SCALA', 'text/x-scala@clike@Scala'], 228 | ['SCHEME', 'text/x-scheme@scheme@Scheme'], 229 | ['SCSS', 'text/x-scss@css@SCSS'], 230 | ['SHELL', 'shell@shell@Shell'], 231 | ['SMALLTALK', 'text/x-stsrc@smalltalk@Smalltalk'], 232 | ['SMARTY', 'text/x-smarty@smarty@Smarty'], 233 | ['SOLR', 'text/x-solr@r@Solr'], 234 | ['SQL', 'text/x-sql@sql@SQL'], 235 | ['SQLITE', 'text/x-sqlite@sql@SQLite'], 236 | ['SQUIRREL', 'text/x-squirrel@clike@Squirrel'], 237 | ['STYLUS', 'text/x-styl@stylus@Stylus'], 238 | ['SWIFT', 'text/x-swift@swift@Swift'], 239 | ['SYSTEMVERILOG', 'text/x-systemverilog@verilog@SystemVerilog'], 240 | ['TCL', 'text/x-tcl@tcl@Tcl'], 241 | ['TEXTILE', 'text/x-textile@textile@Textile'], 242 | ['TIDDLYWIKI ', 'text/x-tiddlywiki@wiki@TiddlyWiki '], 243 | ['TORNADO', 'text/x-tornado@r@Tornado'], 244 | ['TROFF', 'text/troff@r@troff'], 245 | ['TTCN', 'text/x-ttcn@c@TTCN'], 246 | ['TTCN_CFG', 'text/x-ttcn-cfg@c@TTCN_CFG'], 247 | ['TURTLE', 'text/turtle@r@Turtle'], 248 | ['TYPESCRIPT', 'application/typescript@ts@TypeScript'], 249 | ['TSX', 'text/typescript-jsx@tsx@TypeScript-JSX'], 250 | ['TYPESCRIPT-JSX', 'text/typescript-jsx@tsx@TypeScript-JSX'], 251 | ['TWIG', 'text/x-twig@twig@Twig'], 252 | ['WEB IDL', 'text/x-webidl@d@Web IDL'], 253 | ['VBSCRIPT', 'text/vbscript@rip@VBScript'], 254 | ['VELOCITY', 'text/velocity@c@Velocity'], 255 | ['VERILOG', 'text/x-verilog@verilog@Verilog'], 256 | ['VHDL', 'text/x-vhdl@vhdl@VHDL'], 257 | ['XML', 'xml@xml@XML'], 258 | ['XQUERY', 'application/xquery@r@XQuery'], 259 | ['YACAS', 'text/x-yacas@c@Yacas'], 260 | ['YAML', 'yaml@yaml@YAML'] 261 | ]) 262 | 263 | export let unSupported = [ 264 | 'ABAP', 265 | 'ACTIONSCRIPT', 266 | 'ADA', 267 | 'APACHECONF', 268 | 'APPLESCRIPT', 269 | 'ARDUINO', 270 | 'ARFF', 271 | 'ASCIIDOC', 272 | 'ASM6502', 273 | 'AUTOHOTKEY', 274 | 'AUTOIT', 275 | 'BASIC', 276 | 'BATCH', 277 | 'BISON', 278 | 'BRO', 279 | 'CLIKE', 280 | 'CSP', 281 | 'CSS-EXTRAS', 282 | 'ELIXIR', 283 | 'FLOW', 284 | 'GEDCOM', 285 | 'GIT', 286 | 'GLSL', 287 | 'graphql', 288 | 'HANDLEBARS', 289 | 'HPKP', 290 | 'HSTS', 291 | 'ICHIGOJAM', 292 | 'ICON', 293 | 'INFORM7', 294 | 'INI', 295 | 'IO', 296 | 'J', 297 | 'JOLIE', 298 | 'KEYMAN', 299 | 'LATEX', 300 | 'LIQUID', 301 | 'LISP', 302 | 'LOLCODE', 303 | 'MARKUP', 304 | 'MARKUP-TEMPLATING', 305 | 'MEL', 306 | 'MIZAR', 307 | 'MONKEY', 308 | 'N4JS', 309 | 'NASM', 310 | 'NIM', 311 | 'NIX', 312 | 'OCAML', 313 | 'OPENCL', 314 | 'PARIGP', 315 | 'PARSER', 316 | 'PHP-EXTRAS', 317 | 'PROCESSING', 318 | 'PROLOG', 319 | 'PURE', 320 | 'Q', 321 | 'QORE', 322 | 'RENPY', 323 | 'REASON', 324 | 'REST', 325 | 'RIP', 326 | 'ROBOCONF', 327 | 'SAS', 328 | 'SOY', 329 | 'TAP', 330 | 'TT2', 331 | 'VBNET', 332 | 'VIM', 333 | 'VISUAL-BASIC', 334 | 'WASM', 335 | 'WIKI', 336 | 'XEORA', 337 | 'XOJO' 338 | ] 339 | 340 | /** 341 | * It means that the former should be rendered, 342 | * but the latter can be displayed with prismjs. 343 | */ 344 | export let translate = new Map([ 345 | ['CMAKE', 'makefile'], 346 | ['DOCKERFILE', 'docker'], 347 | ['OCTAVE', 'matlab'], 348 | ['SHELL', 'bash'], 349 | ['JINJA2', 'django'], 350 | ['OCTAVE', 'matlab'] 351 | ]) 352 | 353 | /** 354 | * It means that the former can be displayed with prismjs, 355 | * but the latter should be rendered. 356 | */ 357 | export let renderName = new Map([ 358 | ['MATLAB', { ori: 'matlab', render: 'octave' }], 359 | ['JS', { ori: 'javascript', render: 'javascript' }], 360 | ['TS', { ori: 'typescript', render: 'typescript' }], 361 | ['PROP', { ori: 'properties', render: 'properties' }], 362 | ['MD', { ori: 'markdown', render: 'markdown' }], 363 | ['PY', { ori: 'python', render: 'python' }] 364 | ]) 365 | -------------------------------------------------------------------------------- /api.txt: -------------------------------------------------------------------------------- 1 | 查看分类(需要登录): 2 | https://member.bilibili.com/x/web/article/pre 3 | 4 | {"code":0,"data":{"categories":[{"id":2,"parent_id":0,"name":"动画","children":[{"id":4,"parent_id":2,"name":"动漫杂谈"},{"id":5,"parent_id":2,"name":"动漫资讯"},{"id":31,"parent_id":2,"name":"动画技术"}]},{"id":1,"parent_id":0,"name":"游戏","children":[{"id":6,"parent_id":1,"name":"单机游戏"},{"id":7,"parent_id":1,"name":"电子竞技"},{"id":8,"parent_id":1,"name":"手机游戏"},{"id":9,"parent_id":1,"name":"网络游戏"},{"id":10,"parent_id":1,"name":"桌游棋牌"}]},{"id":28,"parent_id":0,"name":"影视","children":[{"id":12,"parent_id":28,"name":"电影"},{"id":35,"parent_id":28,"name":"电视剧"},{"id":36,"parent_id":28,"name":"纪录片"},{"id":37,"parent_id":28,"name":"综艺"}]},{"id":3,"parent_id":0,"name":"生活","children":[{"id":13,"parent_id":3,"name":"美食"},{"id":21,"parent_id":3,"name":"萌宠"},{"id":14,"parent_id":3,"name":"时尚"},{"id":22,"parent_id":3,"name":"运动"},{"id":15,"parent_id":3,"name":"日常"}]},{"id":29,"parent_id":0,"name":"兴趣","children":[{"id":23,"parent_id":29,"name":"绘画"},{"id":24,"parent_id":29,"name":"手工"},{"id":38,"parent_id":29,"name":"摄影"},{"id":39,"parent_id":29,"name":"音乐舞蹈"},{"id":11,"parent_id":29,"name":"模型手办"}]},{"id":16,"parent_id":0,"name":"轻小说","children":[{"id":18,"parent_id":16,"name":"原创连载"},{"id":19,"parent_id":16,"name":"同人连载"},{"id":32,"parent_id":16,"name":"短篇小说"},{"id":20,"parent_id":16,"name":"小说杂谈"}]},{"id":17,"parent_id":0,"name":"科技","children":[{"id":25,"parent_id":17,"name":"人文历史"},{"id":33,"parent_id":17,"name":"自然"},{"id":26,"parent_id":17,"name":"数码"},{"id":27,"parent_id":17,"name":"汽车"},{"id":34,"parent_id":17,"name":"学习"}]}],"myinfo":{"mid":5756570,"uname":"某昨P","face":"http://i1.hdslb.com/bfs/face/7115c5ff964c5aea3bc70792fdb2073b1f6dcb19.jpg","banned":false,"level":5,"activated":true,"deftime":1527928480,"deftime_end":1529210080,"deftime_msg":"发布时间离当前时间必须大于4小时且小于15天,请重新选择。","commercial":0,"identify_check":{"code":0,"msg":"已实名认证"}},"toplimit":5},"message":"0","ttl":1} 5 | 6 | 7 | 8 | 查看活动(不需要登录): 9 | https://www.bilibili.com/activity/list/article 10 | 11 | {"code":0,"data":[{"id":10558,"oid":0,"type":12,"state":1,"stime":"2018-05-22 00:00:00","etime":"2018-07-23 00:00:00","ctime":"2018-05-10 20:01:31","mtime":"2018-05-22 00:44:28","name":"\u300a\u5e72\u676f\uff01\u4e16\u754c\u676f\u300b\u5e94\u63f4\u5f81\u96c6","author":"xufan","act_url":"https:\/\/www.bilibili.com\/blackboard\/activity-cheersworldcup2018.html","cover":"","letime":"2018-05-26 00:00:00","lstime":"2018-05-22 00:00:00","dic":"","flag":"38","uetime":"0000-00-00 00:00:00","ustime":"2018-05-22 00:00:00","level":"0","h5_cover":"","tags":"\u5e72\u676f\uff01\u4e16\u754c\u676f"}]} 12 | 13 | 14 | 查看专栏信息(需要登录): 15 | http://api.bilibili.com/x/article/creative/draft/view?aid=6159 16 | 17 | {"code":0,"message":"0","ttl":1,"data":{"id":6159,"title":"2","content":"","summary":"","banner_url":"","reason":"","template_id":4,"state":0,"reprint":0,"image_urls":[],"origin_image_urls":[],"tags":[],"category":{"id":0,"parent_id":0,"name":""},"author":{"mid":5756570,"name":"","face":"","pendant":{"pid":0,"name":"","image":"","expire":0},"official_verify":{"type":0,"desc":""},"nameplate":{"nid":0,"name":"","image":"","image_small":"","level":"","condition":""}},"stats":null,"publish_time":0,"ctime":0,"mtime":1527914451,"view_url":"","edit_url":"","is_preview":0,"dynamic_intro":"","list":null}} 18 | 19 | 20 | 查看所有专栏(需要登录): 21 | https://api.bilibili.com/x/article/creative/article/list?group=【①】&sort=【②】&pn= 22 | ① Group 23 | 0: 全部文章 24 | 1: 审核中 25 | 2: 已通过 26 | 3: 未通过 27 | ② Sort 28 | 1: 按创建时间排序 29 | 2: 按喜欢数排序 30 | 3: 按评论数排序 31 | 4: --- 32 | 5: 按收藏数排序 33 | 6: 按投币数排序 34 | 35 | {"artlist":{"articles":[{"id":139179,"title":"【某昨】对Minecraft Mod本地化现状的评价及未来的展望","content":"","summary":"大家好,这里是咕咕咕准备高考的某昨。这期专栏,我们来聊一聊Minecraft Mod本地化的现状和对未来的展望。个人意见,欢迎dalao点评。从Minecraft Mod出现以来,本地化可以说经过了三","banner_url":"https://i0.hdslb.com/bfs/article/63b99aaf7bfb4332e521da161d1b28f7c2e6549a.png","reason":"","template_id":4,"state":0,"reprint":1,"image_urls":["https://i0.hdslb.com/bfs/article/63b99aaf7bfb4332e521da161d1b28f7c2e6549a.png"],"origin_image_urls":["https://i0.hdslb.com/bfs/article/63b99aaf7bfb4332e521da161d1b28f7c2e6549a.png"],"tags":[],"category":{"id":1,"parent_id":0,"name":"游戏"},"author":{"mid":5756570,"name":"","face":"","pendant":{"pid":0,"name":"","image":"","expire":0},"official_verify":{"type":0,"desc":""},"nameplate":{"nid":0,"name":"","image":"","image_small":"","level":"","condition":""}},"stats":{"view":189,"favorite":4,"like":5,"dislike":0,"reply":3,"share":0,"coin":4},"publish_time":1515002136,"ctime":1515001381,"mtime":0,"view_url":"https://www.bilibili.com/read/cv139179","edit_url":"https://member.bilibili.com/article-text/mobile?aid=139179\u0026type=2","is_preview":0,"dynamic_intro":"","list":null},{"id":131983,"title":"[某昨]2017年度歌单总结(个人向)(1/4)","content":"","summary":"大家好,这里是专栏鸽了好久的某昨。伴随着圣诞节的钟声(其实不存在的啦),我们很快就要迎来2018年的到来了。在此即将辞旧迎新之际,就让我们伴随着《ジングルベルがとまらない》的歌声,来看看这一年对我而言","banner_url":"https://i0.hdslb.com/bfs/article/0431e6b2b200fdcf74e772fae759f7fe182fa429.jpg","reason":"","template_id":4,"state":0,"reprint":1,"image_urls":["https://i0.hdslb.com/bfs/article/69b18ef78676e5726ded1b4519a6bd0c7ad05ef1.jpg"],"origin_image_urls":["https://i0.hdslb.com/bfs/article/7e9ed2f8802522ac7b63eb0c7d2f59e615c0b32c.jpg"],"tags":[],"category":{"id":3,"parent_id":0,"name":"生活"},"author":{"mid":5756570,"name":"","face":"","pendant":{"pid":0,"name":"","image":"","expire":0},"official_verify":{"type":0,"desc":""},"nameplate":{"nid":0,"name":"","image":"","image_small":"","level":"","condition":""}},"stats":{"view":480,"favorite":2,"like":3,"dislike":0,"reply":1,"share":1,"coin":0},"publish_time":1514640268,"ctime":1514640090,"mtime":0,"view_url":"https://www.bilibili.com/read/cv131983","edit_url":"https://member.bilibili.com/article-text/mobile?aid=131983\u0026type=2","is_preview":0,"dynamic_intro":"","list":null},{"id":35521,"title":"[Mod Idea]Ep.00 奇怪的脑洞\u0026Minecraft与我","content":"","summary":"大家好,我是某昨,这里又开了个新坑。坑如其名,Mod Idea,就是关于Mod的一些想法。从我接触Minecraft Mod以来,就开始有很多想法涌现。有些成为了大坑(如我Github上的InterC","banner_url":"https://i0.hdslb.com/bfs/article/9d0312d1c577c0c61a30b0375a39fc28ff1eec3f.jpg","reason":"","template_id":4,"state":0,"reprint":1,"image_urls":["https://i0.hdslb.com/bfs/article/1b83019bcf6e25ab0d5417fad21aaa6cef79ac68.jpg"],"origin_image_urls":["https://i0.hdslb.com/bfs/article/9d0312d1c577c0c61a30b0375a39fc28ff1eec3f.jpg"],"tags":[],"category":{"id":1,"parent_id":0,"name":"游戏"},"author":{"mid":5756570,"name":"","face":"","pendant":{"pid":0,"name":"","image":"","expire":0},"official_verify":{"type":0,"desc":""},"nameplate":{"nid":0,"name":"","image":"","image_small":"","level":"","condition":""}},"stats":{"view":721,"favorite":12,"like":8,"dislike":0,"reply":3,"share":0,"coin":2},"publish_time":1508056504,"ctime":1508056170,"mtime":0,"view_url":"https://www.bilibili.com/read/cv35521","edit_url":"https://member.bilibili.com/article-text/mobile?aid=35521\u0026type=2","is_preview":0,"dynamic_intro":"","list":null},{"id":34259,"title":"ForestryMC-1.10 Mod兼容指南① 总序,实用拓展(Actually Additions)","content":"","summary":"之前玩Age of Engineering需要大量用到Actually Additions的油菜。众所周知,AoE的配方被魔改得非常致命(不知道的快去看direwolf20受虐,2333),而林业的农","banner_url":"https://i0.hdslb.com/bfs/article/c30707abd0a82a4c8246e1911a84fdf0b6249121.jpg","reason":"","template_id":4,"state":0,"reprint":1,"image_urls":["https://i0.hdslb.com/bfs/article/bde18a88dcc532f608ae271e92a5c2442c357f16.png"],"origin_image_urls":["https://i0.hdslb.com/bfs/article/0f7027d75dab4ba8651c7edcc15d24110afed3f6.png"],"tags":[],"category":{"id":1,"parent_id":0,"name":"游戏"},"author":{"mid":5756570,"name":"","face":"","pendant":{"pid":0,"name":"","image":"","expire":0},"official_verify":{"type":0,"desc":""},"nameplate":{"nid":0,"name":"","image":"","image_small":"","level":"","condition":""}},"stats":{"view":880,"favorite":20,"like":20,"dislike":0,"reply":5,"share":0,"coin":2},"publish_time":1507960791,"ctime":1507958686,"mtime":0,"view_url":"https://www.bilibili.com/read/cv34259","edit_url":"https://member.bilibili.com/article-text/mobile?aid=34259\u0026type=2","is_preview":0,"dynamic_intro":"","list":null},{"id":26467,"title":"Tranquility 12周目完结撒鱼(花?)(❁´◡`❁)*✲゚*","content":"","summary":"今天好不容易填完MooncakeCraft的小坑,还没来得及感叹1.12看不透,就发现十二周目完结了。正好中间阿仇有段内容没有录上,我就来讲讲我直播时的所见所感吧。[OP!老夫的OP呢!手机不能内嵌视","banner_url":"https://i0.hdslb.com/bfs/article/7a3f775523df76cd1ec121356712ce9d8b3c04ec.png","reason":"","template_id":4,"state":0,"reprint":1,"image_urls":["https://i0.hdslb.com/bfs/article/7a3f775523df76cd1ec121356712ce9d8b3c04ec.png"],"origin_image_urls":["https://i0.hdslb.com/bfs/article/7a3f775523df76cd1ec121356712ce9d8b3c04ec.png"],"tags":[],"category":{"id":1,"parent_id":0,"name":"游戏"},"author":{"mid":5756570,"name":"","face":"","pendant":{"pid":0,"name":"","image":"","expire":0},"official_verify":{"type":0,"desc":""},"nameplate":{"nid":0,"name":"","image":"","image_small":"","level":"","condition":""}},"stats":{"view":167,"favorite":2,"like":6,"dislike":0,"reply":5,"share":0,"coin":1},"publish_time":1507134325,"ctime":1507134054,"mtime":0,"view_url":"https://www.bilibili.com/read/cv26467","edit_url":"https://member.bilibili.com/article-text/mobile?aid=26467\u0026type=2","is_preview":0,"dynamic_intro":"","list":null},{"id":1073,"title":"[FFmpeg探秘]Ep.(2) 从node-fluent-ffmpeg开始","content":"","summary":"这一节,我们从node-fluent-ffmpeg入手,对FFmpeg的流媒体机制进行探秘。node-fluent-ffmpeg是基于Nodejs下基于FFmpeg的流媒体API,提供了操控FFmpe","banner_url":"https://i0.hdslb.com/bfs/article/89e994bd4d8d67565431d1669442c0373bdfe2f6.jpg","reason":"","template_id":4,"state":0,"reprint":1,"image_urls":["https://i0.hdslb.com/bfs/article/5ade60de04b6220fea9b0b0786e72a5196212964.jpg"],"origin_image_urls":["https://i0.hdslb.com/bfs/article/89e994bd4d8d67565431d1669442c0373bdfe2f6.jpg"],"tags":[],"category":{"id":17,"parent_id":0,"name":"科技"},"author":{"mid":5756570,"name":"","face":"","pendant":{"pid":0,"name":"","image":"","expire":0},"official_verify":{"type":0,"desc":""},"nameplate":{"nid":0,"name":"","image":"","image_small":"","level":"","condition":""}},"stats":{"view":461,"favorite":5,"like":4,"dislike":0,"reply":4,"share":0,"coin":0},"publish_time":1501947959,"ctime":1501945698,"mtime":0,"view_url":"https://www.bilibili.com/read/cv1073","edit_url":"https://member.bilibili.com/article-text/mobile?aid=1073\u0026type=2","is_preview":0,"dynamic_intro":"","list":null},{"id":859,"title":"[FFmpeg探秘]Ep.(1) 什么是FFmpeg?","content":"","summary":"FFmpeg是什么?相信很多人都或多或少听说过它的大名,尤其是对于Up主们来说,总会或多或少地瞄到小丸工具箱tools目录下的ffmpeg.exe,或者在压制的日志中看到ffmpeg的影子。大部分人相","banner_url":"https://i0.hdslb.com/bfs/article/b44ad4602b302708adc14480bb3ea840d74360c3.jpg","reason":"","template_id":4,"state":0,"reprint":1,"image_urls":["https://i0.hdslb.com/bfs/article/570509ab7f602b2579f50f33d6669813c1439773.jpg"],"origin_image_urls":["https://i0.hdslb.com/bfs/article/b44ad4602b302708adc14480bb3ea840d74360c3.jpg"],"tags":[],"category":{"id":17,"parent_id":0,"name":"科技"},"author":{"mid":5756570,"name":"","face":"","pendant":{"pid":0,"name":"","image":"","expire":0},"official_verify":{"type":0,"desc":""},"nameplate":{"nid":0,"name":"","image":"","image_small":"","level":"","condition":""}},"stats":{"view":178,"favorite":8,"like":5,"dislike":0,"reply":3,"share":1,"coin":0},"publish_time":1501839500,"ctime":1501838554,"mtime":0,"view_url":"https://www.bilibili.com/read/cv859","edit_url":"https://member.bilibili.com/article-text/mobile?aid=859\u0026type=2","is_preview":0,"dynamic_intro":"","list":null}],"type":{"all":7,"audit":0,"passed":7,"not_passed":0},"page":{"pn":1,"ps":20,"total":7}},"code":0,"message":"0"} 36 | 37 | 38 | 查看专栏统计数据(需要登录): 39 | https://member.bilibili.com/x/web/data/article 40 | 41 | {"code":0,"data":{"view":3096,"reply":27,"like":51,"coin":9,"fav":53,"incr_view":1,"incr_reply":0,"incr_like":0,"incr_coin":0,"incr_fav":0},"message":"0","ttl":1} 42 | 43 | 44 | 查看专栏评论(需要登录): 45 | https://member.bilibili.com/x/web/replies?order=【①】&filter=【②】&is_hidden=【③】&type=【④】&oid=【⑤】&pn=【⑥】&ps=【⑦】 46 | ① Order 47 | ctime: 时间排序 48 | like: 点赞数排序 49 | count: 回复数排序 50 | ② Filter 51 | 无: 全部时间 52 | 0: 最近一天 53 | 1: 最近七天 54 | 3: 最近一年 55 | ③ Is_hidden 56 | ④ Type 57 | ⑤ Oid 58 | 专栏的编号,空默认返回所有专栏的评论 59 | ⑥ Pn 60 | 返回第n页的评论 61 | ⑦ Ps 62 | 每一页的评论数量,最多为50 63 | 64 | {"code":0,"data":[{"message":"回复 @某昨P:还行","id":667982354,"floor":2,"count":0,"root":667972068,"oid":264640,"ctime":"2018-03-03 21:19:09","mtime":"2018-03-03 21:19:09","state":0,"parent":667974566,"mid":11511536,"like":0,"replier":"水天宫_宠儿","uface":"http://i2.hdslb.com/bfs/face/3450ae7870e27335f5e88fb8276080c84c6a4112.jpg","cover":"","title":"","relation":2,"is_elec":0,"type":12,"root_info":{"rpid":667974566,"oid":264640,"type":12,"mid":5756570,"root":667972068,"parent":667972068,"count":0,"rcount":0,"floor":1,"state":0,"attr":0,"ctime":1520082920,"like":0,"action":0,"member":{"mid":"5756570","uname":"某昨P","sex":"男","sign":"高三咸鱼冲一把,二十天过后说不定就在上科大了(肝)。有条件可以来Twitter支持我~: @Yesterday17CN","avatar":"http://i1.hdslb.com/bfs/face/7115c5ff964c5aea3bc70792fdb2073b1f6dcb19.jpg","rank":"10000","DisplayRank":"0","level_info":{"current_level":5,"current_min":0,"current_exp":0,"next_exp":0},"pendant":{"pid":153,"name":"citrus","image":"http://i2.hdslb.com/bfs/face/f2d3a85b611eb2ef13cb64415187e3e9fd487ec8.png","expire":1556208000},"nameplate":{"nid":67,"name":"正义之士","image":"http://i1.hdslb.com/bfs/face/5ad735b804886637348de1c95484360acf2a9987.png","image_small":"http://i1.hdslb.com/bfs/face/ef1397ba9d6eb7bab1f230ee7516086432f349de.png","level":"普通勋章","condition":"累计众裁数 \u003e= 200"},"official_verify":{"type":-1,"desc":""},"vip":{"vipType":2,"vipDueDate":1561564800000,"dueRemark":"","accessStatus":0,"vipStatus":1,"vipStatusWarn":""}},"content":{"message":"emm 这个是随手发的 之前发的测试反而还没过审","ipi":717494505,"plat":1,"device":"","members":null},"replies":null},"parent_info":{"rpid":667974566,"oid":264640,"type":12,"mid":5756570,"root":667972068,"parent":667972068,"count":0,"rcount":0,"floor":1,"state":0,"attr":0,"ctime":1520082920,"like":0,"action":0,"member":{"mid":"5756570","uname":"某昨P","sex":"男","sign":"高三咸鱼冲一把,二十天过后说不定就在上科大了(肝)。有条件可以来Twitter支持我~: @Yesterday17CN","avatar":"http://i1.hdslb.com/bfs/face/7115c5ff964c5aea3bc70792fdb2073b1f6dcb19.jpg","rank":"10000","DisplayRank":"0","level_info":{"current_level":5,"current_min":0,"current_exp":0,"next_exp":0},"pendant":{"pid":153,"name":"citrus","image":"http://i2.hdslb.com/bfs/face/f2d3a85b611eb2ef13cb64415187e3e9fd487ec8.png","expire":1556208000},"nameplate":{"nid":67,"name":"正义之士","image":"http://i1.hdslb.com/bfs/face/5ad735b804886637348de1c95484360acf2a9987.png","image_small":"http://i1.hdslb.com/bfs/face/ef1397ba9d6eb7bab1f230ee7516086432f349de.png","level":"普通勋章","condition":"累计众裁数 \u003e= 200"},"official_verify":{"type":-1,"desc":""},"vip":{"vipType":2,"vipDueDate":1561564800000,"dueRemark":"","accessStatus":0,"vipStatus":1,"vipStatusWarn":""}},"content":{"message":"emm 这个是随手发的 之前发的测试反而还没过审","ipi":717494505,"plat":1,"device":"","members":null},"replies":null}},{"message":"emm 这个是随手发的 之前发的测试反而还没过审","id":667974566,"floor":1,"count":0,"root":667972068,"oid":264640,"ctime":"2018-03-03 21:15:20","mtime":"2018-03-03 21:15:20","state":0,"parent":667972068,"mid":5756570,"like":0,"replier":"某昨P","uface":"http://i1.hdslb.com/bfs/face/7115c5ff964c5aea3bc70792fdb2073b1f6dcb19.jpg","cover":"","title":"","relation":1,"is_elec":1,"type":12,"root_info":{"rpid":667972068,"oid":264640,"type":12,"mid":11511536,"root":0,"parent":0,"count":2,"rcount":2,"floor":1,"state":0,"attr":0,"ctime":1520082844,"like":0,"action":0,"member":{"mid":"11511536","uname":"水天宫_宠儿","sex":"保密","sign":"普通的跑跑玩家","avatar":"http://i2.hdslb.com/bfs/face/3450ae7870e27335f5e88fb8276080c84c6a4112.jpg","rank":"10000","DisplayRank":"0","level_info":{"current_level":5,"current_min":0,"current_exp":0,"next_exp":0},"pendant":{"pid":105,"name":"纳米核心","image":"http://i2.hdslb.com/bfs/face/b67a37542d33d093ff28780848448e0a1c78932a.png","expire":1558022400},"nameplate":{"nid":4,"name":"青铜殿堂","image":"http://i2.hdslb.com/bfs/face/2879cd5fb8518f7c6da75887994c1b2a7fe670bd.png","image_small":"http://i1.hdslb.com/bfs/face/6707c120e00a3445933308fd9b7bd9fad99e9ec4.png","level":"普通勋章","condition":"单个自制视频总播放数\u003e=1万"},"official_verify":{"type":-1,"desc":""},"vip":{"vipType":2,"vipDueDate":1558022400000,"dueRemark":"","accessStatus":0,"vipStatus":1,"vipStatusWarn":""}},"content":{"message":"封面车万惊了","ipi":1885418070,"plat":2,"device":"","members":null},"replies":null},"parent_info":{"rpid":667972068,"oid":264640,"type":12,"mid":11511536,"root":0,"parent":0,"count":2,"rcount":2,"floor":1,"state":0,"attr":0,"ctime":1520082844,"like":0,"action":0,"member":{"mid":"11511536","uname":"水天宫_宠儿","sex":"保密","sign":"普通的跑跑玩家","avatar":"http://i2.hdslb.com/bfs/face/3450ae7870e27335f5e88fb8276080c84c6a4112.jpg","rank":"10000","DisplayRank":"0","level_info":{"current_level":5,"current_min":0,"current_exp":0,"next_exp":0},"pendant":{"pid":105,"name":"纳米核心","image":"http://i2.hdslb.com/bfs/face/b67a37542d33d093ff28780848448e0a1c78932a.png","expire":1558022400},"nameplate":{"nid":4,"name":"青铜殿堂","image":"http://i2.hdslb.com/bfs/face/2879cd5fb8518f7c6da75887994c1b2a7fe670bd.png","image_small":"http://i1.hdslb.com/bfs/face/6707c120e00a3445933308fd9b7bd9fad99e9ec4.png","level":"普通勋章","condition":"单个自制视频总播放数\u003e=1万"},"official_verify":{"type":-1,"desc":""},"vip":{"vipType":2,"vipDueDate":1558022400000,"dueRemark":"","accessStatus":0,"vipStatus":1,"vipStatusWarn":""}},"content":{"message":"封面车万惊了","ipi":1885418070,"plat":2,"device":"","members":null},"replies":null}},{"message":"封面车万惊了","id":667972068,"floor":1,"count":2,"root":0,"oid":264640,"ctime":"2018-03-03 21:14:04","mtime":"2018-03-03 21:19:09","state":0,"parent":0,"mid":11511536,"like":0,"replier":"水天宫_宠儿","uface":"http://i2.hdslb.com/bfs/face/3450ae7870e27335f5e88fb8276080c84c6a4112.jpg","cover":"","title":"","relation":2,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"长达5个月的2-3天( ´_ゝ`)","id":573150095,"floor":4,"count":0,"root":0,"oid":1073,"ctime":"2018-01-06 16:09:37","mtime":"2018-01-06 16:09:37","state":0,"parent":0,"mid":172844097,"like":0,"replier":"ZeroAurora233","uface":"http://i0.hdslb.com/bfs/face/64d304b8f0c34e81f8ed1d1ea4d48e8b8e70e3e6.jpg","cover":"https://i0.hdslb.com/bfs/article/5ade60de04b6220fea9b0b0786e72a5196212964.jpg","title":"[FFmpeg探秘]Ep.(2) 从node-fluent-ffmpeg开始","relation":2,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"Σ(゚д゚;)","id":570414165,"floor":3,"count":0,"root":0,"oid":139179,"ctime":"2018-01-04 12:28:04","mtime":"2018-01-04 12:28:04","state":0,"parent":0,"mid":17288555,"like":0,"replier":"数象位至","uface":"http://i1.hdslb.com/bfs/face/a2eb4e5a09a5ec66dae0193f31604666b75a1ffd.jpg","cover":"https://i0.hdslb.com/bfs/article/63b99aaf7bfb4332e521da161d1b28f7c2e6549a.png","title":"【某昨】对Minecraft Mod本地化现状的评价及未来的展望","relation":2,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"原来你也是高三啊(重点错","id":570230286,"floor":2,"count":0,"root":0,"oid":139179,"ctime":"2018-01-04 07:13:01","mtime":"2018-01-04 07:13:01","state":0,"parent":0,"mid":6411282,"like":0,"replier":"ShuPiR","uface":"http://i2.hdslb.com/bfs/face/b248a3c9aef2910323fd9307deb4ec018c8e10f5.jpg","cover":"https://i0.hdslb.com/bfs/article/63b99aaf7bfb4332e521da161d1b28f7c2e6549a.png","title":"【某昨】对Minecraft Mod本地化现状的评价及未来的展望","relation":2,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"懵着逼看完[小电视_困惑]","id":570164041,"floor":1,"count":0,"root":0,"oid":139179,"ctime":"2018-01-04 02:08:49","mtime":"2018-01-04 02:08:49","state":0,"parent":0,"mid":11885971,"like":0,"replier":"gloomy丶banana","uface":"http://i1.hdslb.com/bfs/face/fd4cfd01dd729549c45f9df222f23d35da7f6605.jpg","cover":"https://i0.hdslb.com/bfs/article/63b99aaf7bfb4332e521da161d1b28f7c2e6549a.png","title":"【某昨】对Minecraft Mod本地化现状的评价及未来的展望","relation":2,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"真·五分钟审核[小电视_赞]","id":564112354,"floor":1,"count":0,"root":0,"oid":131983,"ctime":"2017-12-30 21:29:02","mtime":"2017-12-30 21:29:02","state":0,"parent":0,"mid":5756570,"like":0,"replier":"某昨P","uface":"http://i1.hdslb.com/bfs/face/7115c5ff964c5aea3bc70792fdb2073b1f6dcb19.jpg","cover":"https://i0.hdslb.com/bfs/article/69b18ef78676e5726ded1b4519a6bd0c7ad05ef1.jpg","title":"[某昨]2017年度歌单总结(个人向)(1/4)","relation":1,"is_elec":1,"type":12,"root_info":null,"parent_info":null},{"message":"之后一P呢 ( ´_ゝ`)","id":482663442,"floor":3,"count":0,"root":0,"oid":1073,"ctime":"2017-11-17 13:43:12","mtime":"2017-11-17 13:43:12","state":0,"parent":0,"mid":1926839,"like":0,"replier":"米柚子","uface":"http://i1.hdslb.com/bfs/face/722a413092f7448b1be23113c2ba2e105216999d.jpg","cover":"https://i0.hdslb.com/bfs/article/5ade60de04b6220fea9b0b0786e72a5196212964.jpg","title":"[FFmpeg探秘]Ep.(2) 从node-fluent-ffmpeg开始","relation":2,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"啥时候更新啊[小电视_发愁]","id":453005422,"floor":2,"count":0,"root":0,"oid":1073,"ctime":"2017-10-18 22:45:45","mtime":"2017-10-18 22:45:45","state":0,"parent":0,"mid":4134388,"like":0,"replier":"若隐若现的节操","uface":"http://i1.hdslb.com/bfs/face/2f2db3962655f57faea4a71ace6e6f1fe28410e3.jpg","cover":"https://i0.hdslb.com/bfs/article/5ade60de04b6220fea9b0b0786e72a5196212964.jpg","title":"[FFmpeg探秘]Ep.(2) 从node-fluent-ffmpeg开始","relation":2,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"或许这就是大佬吧","id":450318270,"floor":3,"count":0,"root":0,"oid":35521,"ctime":"2017-10-16 11:55:32","mtime":"2017-10-28 12:44:36","state":0,"parent":0,"mid":17288555,"like":1,"replier":"数象位至","uface":"http://i1.hdslb.com/bfs/face/a2eb4e5a09a5ec66dae0193f31604666b75a1ffd.jpg","cover":"https://i0.hdslb.com/bfs/article/1b83019bcf6e25ab0d5417fad21aaa6cef79ac68.jpg","title":"[Mod Idea]Ep.00 奇怪的脑洞\u0026Minecraft与我","relation":2,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"看了第一段顺着链接去看direwolf20了,过了好几天才突然想起来这文根本就没有看。。","id":450265353,"floor":5,"count":0,"root":0,"oid":34259,"ctime":"2017-10-16 10:29:35","mtime":"2017-10-16 10:29:35","state":0,"parent":0,"mid":8119687,"like":0,"replier":"苏凌10","uface":"http://i1.hdslb.com/bfs/face/48ce0a27d8d3e120e6bcbfde60a03ea737202e3c.jpg","cover":"https://i0.hdslb.com/bfs/article/bde18a88dcc532f608ae271e92a5c2442c357f16.png","title":"ForestryMC-1.10 Mod兼容指南① 总序,实用拓展(Actually Additions)","relation":1,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"催更mf2(๑•ั็ω•็ั๑)","id":449527854,"floor":2,"count":0,"root":0,"oid":35521,"ctime":"2017-10-15 16:56:18","mtime":"2017-10-15 16:56:18","state":0,"parent":0,"mid":8786969,"like":0,"replier":"Ten_CI","uface":"http://i1.hdslb.com/bfs/face/8754451bd07e2b4fd5ff0952bdb4fe3b724a2a89.jpg","cover":"https://i0.hdslb.com/bfs/article/1b83019bcf6e25ab0d5417fad21aaa6cef79ac68.jpg","title":"[Mod Idea]Ep.00 奇怪的脑洞\u0026Minecraft与我","relation":2,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"少年很有想法[小电视_赞]","id":449509377,"floor":1,"count":0,"root":0,"oid":35521,"ctime":"2017-10-15 16:39:05","mtime":"2017-10-15 16:39:05","state":0,"parent":0,"mid":11885971,"like":0,"replier":"gloomy丶banana","uface":"http://i1.hdslb.com/bfs/face/fd4cfd01dd729549c45f9df222f23d35da7f6605.jpg","cover":"https://i0.hdslb.com/bfs/article/1b83019bcf6e25ab0d5417fad21aaa6cef79ac68.jpg","title":"[Mod Idea]Ep.00 奇怪的脑洞\u0026Minecraft与我","relation":2,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"教程?滋磁!","id":448452033,"floor":4,"count":0,"root":0,"oid":34259,"ctime":"2017-10-14 20:31:03","mtime":"2017-11-02 15:40:58","state":0,"parent":0,"mid":7939974,"like":0,"replier":"Sf·Chipen","uface":"http://i1.hdslb.com/bfs/face/c60a099e42c2c327983b3b1c228d96fab5dfad01.jpg","cover":"https://i0.hdslb.com/bfs/article/bde18a88dcc532f608ae271e92a5c2442c357f16.png","title":"ForestryMC-1.10 Mod兼容指南① 总序,实用拓展(Actually Additions)","relation":2,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"支持支持 猿佬","id":448063172,"floor":3,"count":0,"root":0,"oid":34259,"ctime":"2017-10-14 14:33:56","mtime":"2017-10-14 20:08:41","state":6,"parent":0,"mid":27612973,"like":0,"replier":"陌于千鹤","uface":"http://i1.hdslb.com/bfs/face/fb1f49d6404e0fe63bb598cc2322feb227f5360d.jpg","cover":"https://i0.hdslb.com/bfs/article/bde18a88dcc532f608ae271e92a5c2442c357f16.png","title":"ForestryMC-1.10 Mod兼容指南① 总序,实用拓展(Actually Additions)","relation":1,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"菜籽油出原油,炼金术[小电视_吃惊]","id":448055405,"floor":2,"count":0,"root":0,"oid":34259,"ctime":"2017-10-14 14:24:59","mtime":"2017-10-14 15:23:25","state":0,"parent":0,"mid":8786969,"like":1,"replier":"Ten_CI","uface":"http://i1.hdslb.com/bfs/face/8754451bd07e2b4fd5ff0952bdb4fe3b724a2a89.jpg","cover":"https://i0.hdslb.com/bfs/article/bde18a88dcc532f608ae271e92a5c2442c357f16.png","title":"ForestryMC-1.10 Mod兼容指南① 总序,实用拓展(Actually Additions)","relation":2,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"943来的(=・ω・=)","id":448050392,"floor":1,"count":0,"root":0,"oid":34259,"ctime":"2017-10-14 14:19:06","mtime":"2017-10-15 00:05:15","state":0,"parent":0,"mid":172844097,"like":1,"replier":"ZeroAurora233","uface":"http://i0.hdslb.com/bfs/face/64d304b8f0c34e81f8ed1d1ea4d48e8b8e70e3e6.jpg","cover":"https://i0.hdslb.com/bfs/article/bde18a88dcc532f608ae271e92a5c2442c357f16.png","title":"ForestryMC-1.10 Mod兼容指南① 总序,实用拓展(Actually Additions)","relation":2,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"文笔很不错哦","id":437765528,"floor":3,"count":0,"root":0,"oid":26467,"ctime":"2017-10-05 22:30:17","mtime":"2017-10-05 22:30:17","state":0,"parent":0,"mid":4435845,"like":0,"replier":"酒石酸菌","uface":"http://i2.hdslb.com/bfs/face/2e9af19b0fd8d4b58f6a1bbd3302add09f820340.jpg","cover":"https://i0.hdslb.com/bfs/article/7a3f775523df76cd1ec121356712ce9d8b3c04ec.png","title":"Tranquility 12周目完结撒鱼(花?)(❁´◡`❁)*✲゚*","relation":1,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"看完了,非常棒","id":437765121,"floor":1,"count":0,"root":0,"oid":1073,"ctime":"2017-10-05 22:30:02","mtime":"2017-10-28 13:01:22","state":0,"parent":0,"mid":4435845,"like":1,"replier":"酒石酸菌","uface":"http://i2.hdslb.com/bfs/face/2e9af19b0fd8d4b58f6a1bbd3302add09f820340.jpg","cover":"https://i0.hdslb.com/bfs/article/5ade60de04b6220fea9b0b0786e72a5196212964.jpg","title":"[FFmpeg探秘]Ep.(2) 从node-fluent-ffmpeg开始","relation":1,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"虎摸咸鱼","id":436790976,"floor":1,"count":0,"root":436783616,"oid":26467,"ctime":"2017-10-05 10:40:50","mtime":"2017-10-05 10:40:50","state":0,"parent":436783616,"mid":626658,"like":0,"replier":"煎饼XX","uface":"http://i0.hdslb.com/bfs/face/b8bc2e8279a9468a4d07fc2996dbfc2d63477aeb.jpg","cover":"https://i0.hdslb.com/bfs/article/7a3f775523df76cd1ec121356712ce9d8b3c04ec.png","title":"Tranquility 12周目完结撒鱼(花?)(❁´◡`❁)*✲゚*","relation":1,"is_elec":0,"type":12,"root_info":{"rpid":436783616,"oid":26467,"type":12,"mid":17288555,"root":0,"parent":0,"count":1,"rcount":1,"floor":2,"state":0,"attr":0,"ctime":1507170881,"like":0,"action":0,"member":{"mid":"17288555","uname":"数象位至","sex":"保密","sign":"众人皆佬我独咸!(/TДT)/","avatar":"http://i2.hdslb.com/bfs/face/a2eb4e5a09a5ec66dae0193f31604666b75a1ffd.jpg","rank":"10000","DisplayRank":"0","level_info":{"current_level":4,"current_min":0,"current_exp":0,"next_exp":0},"pendant":{"pid":0,"name":"","image":"","expire":0},"nameplate":{"nid":7,"name":"见习搬运工","image":"http://i1.hdslb.com/bfs/face/8478fb7c54026cd47f09daa493a1b1683113a90d.png","image_small":"http://i2.hdslb.com/bfs/face/50eef47c3a30a75659d3cc298cfb09031d1a2ce5.png","level":"普通勋章","condition":"转载视频投稿通过总数\u003e=10"},"official_verify":{"type":-1,"desc":""},"vip":{"vipType":0,"vipDueDate":0,"dueRemark":"","accessStatus":0,"vipStatus":0,"vipStatusWarn":""}},"content":{"message":"_(:з」∠)_","ipi":2871447432,"plat":1,"device":"","members":null},"replies":null},"parent_info":{"rpid":436783616,"oid":26467,"type":12,"mid":17288555,"root":0,"parent":0,"count":1,"rcount":1,"floor":2,"state":0,"attr":0,"ctime":1507170881,"like":0,"action":0,"member":{"mid":"17288555","uname":"数象位至","sex":"保密","sign":"众人皆佬我独咸!(/TДT)/","avatar":"http://i2.hdslb.com/bfs/face/a2eb4e5a09a5ec66dae0193f31604666b75a1ffd.jpg","rank":"10000","DisplayRank":"0","level_info":{"current_level":4,"current_min":0,"current_exp":0,"next_exp":0},"pendant":{"pid":0,"name":"","image":"","expire":0},"nameplate":{"nid":7,"name":"见习搬运工","image":"http://i1.hdslb.com/bfs/face/8478fb7c54026cd47f09daa493a1b1683113a90d.png","image_small":"http://i2.hdslb.com/bfs/face/50eef47c3a30a75659d3cc298cfb09031d1a2ce5.png","level":"普通勋章","condition":"转载视频投稿通过总数\u003e=10"},"official_verify":{"type":-1,"desc":""},"vip":{"vipType":0,"vipDueDate":0,"dueRemark":"","accessStatus":0,"vipStatus":0,"vipStatusWarn":""}},"content":{"message":"_(:з」∠)_","ipi":2871447432,"plat":1,"device":"","members":null},"replies":null}},{"message":"_(:з」∠)_","id":436783616,"floor":2,"count":1,"root":0,"oid":26467,"ctime":"2017-10-05 10:34:41","mtime":"2017-10-05 10:40:50","state":0,"parent":0,"mid":17288555,"like":0,"replier":"数象位至","uface":"http://i1.hdslb.com/bfs/face/a2eb4e5a09a5ec66dae0193f31604666b75a1ffd.jpg","cover":"https://i0.hdslb.com/bfs/article/7a3f775523df76cd1ec121356712ce9d8b3c04ec.png","title":"Tranquility 12周目完结撒鱼(花?)(❁´◡`❁)*✲゚*","relation":2,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"9氏游览:服务器里一群肝帝\n仇式游览:走过炸过千万不要错过(雾","id":436458802,"floor":1,"count":0,"root":436450883,"oid":26467,"ctime":"2017-10-05 00:39:12","mtime":"2017-10-05 00:39:12","state":0,"parent":436450883,"mid":5756570,"like":0,"replier":"某昨P","uface":"http://i1.hdslb.com/bfs/face/7115c5ff964c5aea3bc70792fdb2073b1f6dcb19.jpg","cover":"https://i0.hdslb.com/bfs/article/7a3f775523df76cd1ec121356712ce9d8b3c04ec.png","title":"Tranquility 12周目完结撒鱼(花?)(❁´◡`❁)*✲゚*","relation":1,"is_elec":1,"type":12,"root_info":{"rpid":436450883,"oid":26467,"type":12,"mid":8786969,"root":0,"parent":0,"count":1,"rcount":1,"floor":1,"state":0,"attr":0,"ctime":1507134743,"like":0,"action":0,"member":{"mid":"8786969","uname":"Ten_CI","sex":"男","sign":"信仰圣光吧(物理)","avatar":"http://i1.hdslb.com/bfs/face/8754451bd07e2b4fd5ff0952bdb4fe3b724a2a89.jpg","rank":"10000","DisplayRank":"0","level_info":{"current_level":5,"current_min":0,"current_exp":0,"next_exp":0},"pendant":{"pid":0,"name":"","image":"","expire":0},"nameplate":{"nid":57,"name":"收集萌新","image":"http://i2.hdslb.com/bfs/face/7767275600ea63d351b22fa87ec15a79aa24e5e5.png","image_small":"http://i0.hdslb.com/bfs/face/6589d992655595bf51543f268040eaeaed372fae.png","level":"普通勋章","condition":"同时拥有粉丝勋章\u003e=5个"},"official_verify":{"type":-1,"desc":""},"vip":{"vipType":2,"vipDueDate":1563379200000,"dueRemark":"","accessStatus":0,"vipStatus":1,"vipStatusWarn":""}},"content":{"message":"完解炸龙堆(°∀°)ノ","ipi":3657770900,"plat":2,"device":"","members":null},"replies":null},"parent_info":{"rpid":436450883,"oid":26467,"type":12,"mid":8786969,"root":0,"parent":0,"count":1,"rcount":1,"floor":1,"state":0,"attr":0,"ctime":1507134743,"like":0,"action":0,"member":{"mid":"8786969","uname":"Ten_CI","sex":"男","sign":"信仰圣光吧(物理)","avatar":"http://i1.hdslb.com/bfs/face/8754451bd07e2b4fd5ff0952bdb4fe3b724a2a89.jpg","rank":"10000","DisplayRank":"0","level_info":{"current_level":5,"current_min":0,"current_exp":0,"next_exp":0},"pendant":{"pid":0,"name":"","image":"","expire":0},"nameplate":{"nid":57,"name":"收集萌新","image":"http://i2.hdslb.com/bfs/face/7767275600ea63d351b22fa87ec15a79aa24e5e5.png","image_small":"http://i0.hdslb.com/bfs/face/6589d992655595bf51543f268040eaeaed372fae.png","level":"普通勋章","condition":"同时拥有粉丝勋章\u003e=5个"},"official_verify":{"type":-1,"desc":""},"vip":{"vipType":2,"vipDueDate":1563379200000,"dueRemark":"","accessStatus":0,"vipStatus":1,"vipStatusWarn":""}},"content":{"message":"完解炸龙堆(°∀°)ノ","ipi":3657770900,"plat":2,"device":"","members":null},"replies":null}},{"message":"完解炸龙堆(°∀°)ノ","id":436450883,"floor":1,"count":1,"root":0,"oid":26467,"ctime":"2017-10-05 00:32:23","mtime":"2017-10-05 00:39:12","state":0,"parent":0,"mid":8786969,"like":0,"replier":"Ten_CI","uface":"http://i1.hdslb.com/bfs/face/8754451bd07e2b4fd5ff0952bdb4fe3b724a2a89.jpg","cover":"https://i0.hdslb.com/bfs/article/7a3f775523df76cd1ec121356712ce9d8b3c04ec.png","title":"Tranquility 12周目完结撒鱼(花?)(❁´◡`❁)*✲゚*","relation":2,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"格式转换的内容相对来说是比较简单的,最主要的是控制编码和码率之类的,第二篇有一种简单的压制成flv的方法。一般转的时候限定h.26-4就可以了\n我之后有空多写写吧(","id":371523323,"floor":1,"count":0,"root":368866022,"oid":859,"ctime":"2017-08-11 11:06:10","mtime":"2017-08-11 11:06:10","state":0,"parent":368866022,"mid":5756570,"like":0,"replier":"某昨P","uface":"http://i1.hdslb.com/bfs/face/7115c5ff964c5aea3bc70792fdb2073b1f6dcb19.jpg","cover":"https://i0.hdslb.com/bfs/article/570509ab7f602b2579f50f33d6669813c1439773.jpg","title":"[FFmpeg探秘]Ep.(1) 什么是FFmpeg?","relation":1,"is_elec":1,"type":12,"root_info":{"rpid":368866022,"oid":859,"type":12,"mid":4028313,"root":0,"parent":0,"count":1,"rcount":1,"floor":2,"state":0,"attr":0,"ctime":1502250126,"like":0,"action":0,"member":{"mid":"4028313","uname":"dyzdyz010","sex":"男","sign":"Vocaloid调教新人入坑","avatar":"http://i1.hdslb.com/bfs/face/4f88ab09757d2387d6a924151c41ec2cb7b0d020.jpg","rank":"10000","DisplayRank":"0","level_info":{"current_level":5,"current_min":0,"current_exp":0,"next_exp":0},"pendant":{"pid":0,"name":"","image":"","expire":0},"nameplate":{"nid":0,"name":"","image":"","image_small":"","level":"","condition":""},"official_verify":{"type":-1,"desc":""},"vip":{"vipType":1,"vipDueDate":1531843200000,"dueRemark":"","accessStatus":0,"vipStatus":1,"vipStatusWarn":""}},"content":{"message":"补充,我个人更关注ffmpeg的格式转换功能,包括视频和音频,大部分格式之间都可以相互转换,甚至可以直接从视频中提取出音频输出为文件","ipi":2025962028,"plat":2,"device":"","members":null},"replies":null},"parent_info":{"rpid":368866022,"oid":859,"type":12,"mid":4028313,"root":0,"parent":0,"count":1,"rcount":1,"floor":2,"state":0,"attr":0,"ctime":1502250126,"like":0,"action":0,"member":{"mid":"4028313","uname":"dyzdyz010","sex":"男","sign":"Vocaloid调教新人入坑","avatar":"http://i1.hdslb.com/bfs/face/4f88ab09757d2387d6a924151c41ec2cb7b0d020.jpg","rank":"10000","DisplayRank":"0","level_info":{"current_level":5,"current_min":0,"current_exp":0,"next_exp":0},"pendant":{"pid":0,"name":"","image":"","expire":0},"nameplate":{"nid":0,"name":"","image":"","image_small":"","level":"","condition":""},"official_verify":{"type":-1,"desc":""},"vip":{"vipType":1,"vipDueDate":1531843200000,"dueRemark":"","accessStatus":0,"vipStatus":1,"vipStatusWarn":""}},"content":{"message":"补充,我个人更关注ffmpeg的格式转换功能,包括视频和音频,大部分格式之间都可以相互转换,甚至可以直接从视频中提取出音频输出为文件","ipi":2025962028,"plat":2,"device":"","members":null},"replies":null}},{"message":"前来支持,真么快就出了","id":370947245,"floor":3,"count":0,"root":0,"oid":859,"ctime":"2017-08-10 21:54:12","mtime":"2017-11-17 13:42:19","state":0,"parent":0,"mid":7861137,"like":1,"replier":"STeven8888","uface":"http://i2.hdslb.com/bfs/face/f6b53b876c34d09decbe399ef32a14e008d86108.jpg","cover":"https://i0.hdslb.com/bfs/article/570509ab7f602b2579f50f33d6669813c1439773.jpg","title":"[FFmpeg探秘]Ep.(1) 什么是FFmpeg?","relation":2,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"补充,我个人更关注ffmpeg的格式转换功能,包括视频和音频,大部分格式之间都可以相互转换,甚至可以直接从视频中提取出音频输出为文件","id":368866022,"floor":2,"count":1,"root":0,"oid":859,"ctime":"2017-08-09 11:42:06","mtime":"2017-08-11 11:06:10","state":0,"parent":0,"mid":4028313,"like":0,"replier":"dyzdyz010","uface":"http://i1.hdslb.com/bfs/face/4f88ab09757d2387d6a924151c41ec2cb7b0d020.jpg","cover":"https://i0.hdslb.com/bfs/article/570509ab7f602b2579f50f33d6669813c1439773.jpg","title":"[FFmpeg探秘]Ep.(1) 什么是FFmpeg?","relation":2,"is_elec":0,"type":12,"root_info":null,"parent_info":null},{"message":"期待后文~","id":362810336,"floor":1,"count":0,"root":0,"oid":859,"ctime":"2017-08-04 20:36:12","mtime":"2017-08-05 23:08:26","state":0,"parent":0,"mid":5760446,"like":2,"replier":"花儿不哭","uface":"http://i1.hdslb.com/bfs/face/f4c52f7f52b8d5aaad70b1afd2fb064b673b5466.jpg","cover":"https://i0.hdslb.com/bfs/article/570509ab7f602b2579f50f33d6669813c1439773.jpg","title":"[FFmpeg探秘]Ep.(1) 什么是FFmpeg?","relation":1,"is_elec":0,"type":12,"root_info":null,"parent_info":null}],"message":"0","pager":{"current":1,"size":50,"total":28},"ttl":1} 65 | --------------------------------------------------------------------------------