初步完善歌词

pull/459/head
lyswhut 2021-03-02 08:32:26 +08:00
parent 9f72831e4a
commit 040fcfba4f
12 changed files with 779 additions and 120 deletions

View File

@ -19,6 +19,7 @@
"plugins": [ "plugins": [
"@babel/plugin-syntax-dynamic-import", "@babel/plugin-syntax-dynamic-import",
"@babel/plugin-transform-modules-umd", "@babel/plugin-transform-modules-umd",
"@babel/plugin-transform-runtime" "@babel/plugin-transform-runtime",
"@babel/plugin-proposal-class-properties"
] ]
} }

150
package-lock.json generated
View File

@ -1548,8 +1548,8 @@
}, },
"@babel/plugin-proposal-class-properties": { "@babel/plugin-proposal-class-properties": {
"version": "7.13.0", "version": "7.13.0",
"resolved": "https://registry.npm.taobao.org/@babel/plugin-proposal-class-properties/download/@babel/plugin-proposal-class-properties-7.13.0.tgz?cache=0&sync_timestamp=1614035098704&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fplugin-proposal-class-properties%2Fdownload%2F%40babel%2Fplugin-proposal-class-properties-7.13.0.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz",
"integrity": "sha1-FGN2AAuU79AB5XpAqIpSWvqrnzc=", "integrity": "sha512-KnTDjFNC1g+45ka0myZNvSBFLhNCLN+GeGYLDEA8Oq7MZ6yMgfLoIRh86GRT0FjtJhZw8JyUskP9uvj5pHM9Zg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/helper-create-class-features-plugin": "^7.13.0", "@babel/helper-create-class-features-plugin": "^7.13.0",
@ -1558,8 +1558,8 @@
"dependencies": { "dependencies": {
"@babel/helper-plugin-utils": { "@babel/helper-plugin-utils": {
"version": "7.13.0", "version": "7.13.0",
"resolved": "https://registry.npm.taobao.org/@babel/helper-plugin-utils/download/@babel/helper-plugin-utils-7.13.0.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz",
"integrity": "sha1-gGUmzhJa7QM3O8QWqCgyHjpqM68=", "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==",
"dev": true "dev": true
} }
} }
@ -2882,14 +2882,13 @@
} }
}, },
"@electron/get": { "@electron/get": {
"version": "1.12.3", "version": "1.12.4",
"resolved": "https://registry.npm.taobao.org/@electron/get/download/@electron/get-1.12.3.tgz", "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.12.4.tgz",
"integrity": "sha1-+icjOFxLVlo0xMgvRgh6oqX79tA=", "integrity": "sha512-6nr9DbJPUR9Xujw6zD3y+rS95TyItEVM0NVjt1EehY2vUWfIgPiIPVHxCvaTS0xr2B+DRxovYVKbuOWqC35kjg==",
"dev": true, "dev": true,
"requires": { "requires": {
"debug": "^4.1.1", "debug": "^4.1.1",
"env-paths": "^2.2.0", "env-paths": "^2.2.0",
"filenamify": "^4.1.0",
"fs-extra": "^8.1.0", "fs-extra": "^8.1.0",
"global-agent": "^2.0.2", "global-agent": "^2.0.2",
"global-tunnel-ng": "^2.7.1", "global-tunnel-ng": "^2.7.1",
@ -4871,8 +4870,8 @@
}, },
"boolean": { "boolean": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npm.taobao.org/boolean/download/boolean-3.0.2.tgz", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.2.tgz",
"integrity": "sha1-3xuqGLaisOcIQEdeHZPsj+dbJXA=", "integrity": "sha512-RwywHlpCRc3/Wh81MiCKun4ydaIFyW5Ea6JbL6sRCVx5q5irDw7pMXBUFYF/jArQ6YrG36q0kpovc9P/Kd3I4g==",
"dev": true, "dev": true,
"optional": true "optional": true
}, },
@ -5085,7 +5084,7 @@
}, },
"buffer-crc32": { "buffer-crc32": {
"version": "0.2.13", "version": "0.2.13",
"resolved": "https://registry.npm.taobao.org/buffer-crc32/download/buffer-crc32-0.2.13.tgz", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=",
"dev": true "dev": true
}, },
@ -5929,8 +5928,8 @@
}, },
"config-chain": { "config-chain": {
"version": "1.1.12", "version": "1.1.12",
"resolved": "https://registry.npm.taobao.org/config-chain/download/config-chain-1.1.12.tgz", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz",
"integrity": "sha1-D96NCRIA616AjK8l/mGMAvSOTvo=", "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@ -7182,8 +7181,8 @@
}, },
"electron": { "electron": {
"version": "9.4.2", "version": "9.4.2",
"resolved": "https://registry.npm.taobao.org/electron/download/electron-9.4.2.tgz", "resolved": "https://registry.npmjs.org/electron/-/electron-9.4.2.tgz",
"integrity": "sha1-DHbfw9MXEIraxmhEuGip4uV9SPU=", "integrity": "sha512-WpnJLDFHtj5eIewAi4hMHxGdbwkzjzmxsMu/BtDFCic3wpruchkskL7EV28Sg/IYTAqo6yN5ISfnFaQcLsIdng==",
"dev": true, "dev": true,
"requires": { "requires": {
"@electron/get": "^1.0.1", "@electron/get": "^1.0.1",
@ -7192,9 +7191,9 @@
}, },
"dependencies": { "dependencies": {
"@types/node": { "@types/node": {
"version": "12.19.15", "version": "12.20.4",
"resolved": "https://registry.npm.taobao.org/@types/node/download/@types/node-12.19.15.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.4.tgz",
"integrity": "sha1-DefpePtD22LaNp2xjqCIpjZzwYI=", "integrity": "sha512-xRCgeE0Q4pT5UZ189TJ3SpYuX/QGl6QIAOAIeDSbAVAd2gX1NxSZup4jNVK7cxIeP8KDSbJgcckun495isP1jQ==",
"dev": true "dev": true
} }
} }
@ -7583,8 +7582,8 @@
}, },
"es6-error": { "es6-error": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npm.taobao.org/es6-error/download/es6-error-4.1.1.tgz", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
"integrity": "sha1-njr0B0Wd7tR+mpH5uIWoTrBcVh0=", "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
"dev": true, "dev": true,
"optional": true "optional": true
}, },
@ -8644,8 +8643,8 @@
}, },
"extract-zip": { "extract-zip": {
"version": "1.7.0", "version": "1.7.0",
"resolved": "https://registry.npm.taobao.org/extract-zip/download/extract-zip-1.7.0.tgz?cache=0&sync_timestamp=1591773082587&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fextract-zip%2Fdownload%2Fextract-zip-1.7.0.tgz", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz",
"integrity": "sha1-VWzDrp339FLEk6DPtRzDAneUCSc=", "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==",
"dev": true, "dev": true,
"requires": { "requires": {
"concat-stream": "^1.6.2", "concat-stream": "^1.6.2",
@ -8656,8 +8655,8 @@
"dependencies": { "dependencies": {
"debug": { "debug": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1607566533140&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true, "dev": true,
"requires": { "requires": {
"ms": "2.0.0" "ms": "2.0.0"
@ -8665,7 +8664,7 @@
}, },
"ms": { "ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&sync_timestamp=1607433842694&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true "dev": true
} }
@ -8737,7 +8736,7 @@
}, },
"fd-slicer": { "fd-slicer": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npm.taobao.org/fd-slicer/download/fd-slicer-1.1.0.tgz", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
"integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -8827,23 +8826,6 @@
"minimatch": "^3.0.4" "minimatch": "^3.0.4"
} }
}, },
"filename-reserved-regex": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/filename-reserved-regex/download/filename-reserved-regex-2.0.0.tgz",
"integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=",
"dev": true
},
"filenamify": {
"version": "4.2.0",
"resolved": "https://registry.npm.taobao.org/filenamify/download/filenamify-4.2.0.tgz?cache=0&sync_timestamp=1600940508592&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffilenamify%2Fdownload%2Ffilenamify-4.2.0.tgz",
"integrity": "sha1-yZcW1naGlYWztdMos/BlkNAy6J8=",
"dev": true,
"requires": {
"filename-reserved-regex": "^2.0.0",
"strip-outer": "^1.0.1",
"trim-repeated": "^1.0.0"
}
},
"fill-range": { "fill-range": {
"version": "7.0.1", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@ -9333,8 +9315,8 @@
}, },
"global-agent": { "global-agent": {
"version": "2.1.12", "version": "2.1.12",
"resolved": "https://registry.npm.taobao.org/global-agent/download/global-agent-2.1.12.tgz", "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.1.12.tgz",
"integrity": "sha1-5K44Ercxqegcv4Jfk3fvRQqOQZU=", "integrity": "sha512-caAljRMS/qcDo69X9BfkgrihGUgGx44Fb4QQToNQjsiWh+YlQ66uqYVAdA8Olqit+5Ng0nkz09je3ZzANMZcjg==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@ -9349,8 +9331,8 @@
"dependencies": { "dependencies": {
"semver": { "semver": {
"version": "7.3.4", "version": "7.3.4",
"resolved": "https://registry.npm.taobao.org/semver/download/semver-7.3.4.tgz?cache=0&sync_timestamp=1606852064928&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-7.3.4.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
"integrity": "sha1-J6qn0uTKdkUvmNOt0JOnLJQ+3Jc=", "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@ -9425,8 +9407,8 @@
}, },
"global-tunnel-ng": { "global-tunnel-ng": {
"version": "2.7.1", "version": "2.7.1",
"resolved": "https://registry.npm.taobao.org/global-tunnel-ng/download/global-tunnel-ng-2.7.1.tgz", "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz",
"integrity": "sha1-0DtRAt/eOmmRT17n2GdhyjXVfY8=", "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@ -9443,9 +9425,9 @@
"dev": true "dev": true
}, },
"globalthis": { "globalthis": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npm.taobao.org/globalthis/download/globalthis-1.0.1.tgz", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz",
"integrity": "sha1-QBFvXZwHH56PsAN2VN8as6g7fvk=", "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@ -11089,8 +11071,8 @@
}, },
"matcher": { "matcher": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npm.taobao.org/matcher/download/matcher-3.0.0.tgz", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
"integrity": "sha1-vZBg9MW3CqgEHMxvgDaHYJlPMMo=", "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@ -11099,8 +11081,8 @@
"dependencies": { "dependencies": {
"escape-string-regexp": { "escape-string-regexp": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-4.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fescape-string-regexp%2Fdownload%2Fescape-string-regexp-4.0.0.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha1-FLqDpdNz49MR5a/KKc9b+tllvzQ=", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true, "dev": true,
"optional": true "optional": true
} }
@ -11684,8 +11666,8 @@
}, },
"npm-conf": { "npm-conf": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npm.taobao.org/npm-conf/download/npm-conf-1.1.3.tgz", "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz",
"integrity": "sha1-JWzEe9DiGMJZxOlVC/QTvCGSr/k=", "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@ -12209,7 +12191,7 @@
}, },
"pend": { "pend": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npm.taobao.org/pend/download/pend-1.2.0.tgz", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
"dev": true "dev": true
}, },
@ -12226,7 +12208,7 @@
}, },
"pify": { "pify": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npm.taobao.org/pify/download/pify-3.0.0.tgz", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true, "dev": true,
"optional": true "optional": true
@ -13094,7 +13076,7 @@
}, },
"proto-list": { "proto-list": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npm.taobao.org/proto-list/download/proto-list-1.2.4.tgz", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
"integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=",
"dev": true, "dev": true,
"optional": true "optional": true
@ -14010,8 +13992,8 @@
}, },
"roarr": { "roarr": {
"version": "2.15.4", "version": "2.15.4",
"resolved": "https://registry.npm.taobao.org/roarr/download/roarr-2.15.4.tgz?cache=0&sync_timestamp=1610472352033&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Froarr%2Fdownload%2Froarr-2.15.4.tgz", "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz",
"integrity": "sha1-9f55W3uDjM/jXcYI4Cgrnrouev0=", "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@ -14025,8 +14007,8 @@
"dependencies": { "dependencies": {
"sprintf-js": { "sprintf-js": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.1.2.tgz", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
"integrity": "sha1-2hdlJiv4wPVxdJ8q1sJjACB65nM=", "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
"dev": true, "dev": true,
"optional": true "optional": true
} }
@ -14113,7 +14095,7 @@
}, },
"semver-compare": { "semver-compare": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/semver-compare/download/semver-compare-1.0.0.tgz", "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
"integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=",
"dev": true, "dev": true,
"optional": true "optional": true
@ -14175,8 +14157,8 @@
}, },
"serialize-error": { "serialize-error": {
"version": "7.0.1", "version": "7.0.1",
"resolved": "https://registry.npm.taobao.org/serialize-error/download/serialize-error-7.0.1.tgz", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz",
"integrity": "sha1-8TYLBEf2H/tIPsQVfHN/q313jhg=", "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@ -14185,8 +14167,8 @@
"dependencies": { "dependencies": {
"type-fest": { "type-fest": {
"version": "0.13.1", "version": "0.13.1",
"resolved": "https://registry.npm.taobao.org/type-fest/download/type-fest-0.13.1.tgz?cache=0&sync_timestamp=1606468844109&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftype-fest%2Fdownload%2Ftype-fest-0.13.1.tgz", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
"integrity": "sha1-AXLLW86AsL1ULqNI21DH4hg02TQ=", "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
"dev": true, "dev": true,
"optional": true "optional": true
} }
@ -15069,15 +15051,6 @@
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true "dev": true
}, },
"strip-outer": {
"version": "1.0.1",
"resolved": "https://registry.npm.taobao.org/strip-outer/download/strip-outer-1.0.1.tgz",
"integrity": "sha1-sv0qv2YEudHmATBXGV34Nrip1jE=",
"dev": true,
"requires": {
"escape-string-regexp": "^1.0.2"
}
},
"stylehacks": { "stylehacks": {
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz",
@ -15218,8 +15191,8 @@
}, },
"sumchecker": { "sumchecker": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npm.taobao.org/sumchecker/download/sumchecker-3.0.1.tgz", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
"integrity": "sha1-Y3fplnlauwttNI6bPh37JDRajkI=", "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==",
"dev": true, "dev": true,
"requires": { "requires": {
"debug": "^4.1.0" "debug": "^4.1.0"
@ -15734,15 +15707,6 @@
"punycode": "^2.1.1" "punycode": "^2.1.1"
} }
}, },
"trim-repeated": {
"version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/trim-repeated/download/trim-repeated-1.0.0.tgz",
"integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=",
"dev": true,
"requires": {
"escape-string-regexp": "^1.0.2"
}
},
"trim-right": { "trim-right": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
@ -15795,8 +15759,8 @@
}, },
"tunnel": { "tunnel": {
"version": "0.0.6", "version": "0.0.6",
"resolved": "https://registry.npm.taobao.org/tunnel/download/tunnel-0.0.6.tgz", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha1-cvExSzSlsZLbASMk3yzFh8pH+Sw=", "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
"dev": true, "dev": true,
"optional": true "optional": true
}, },
@ -17844,7 +17808,7 @@
}, },
"yauzl": { "yauzl": {
"version": "2.10.0", "version": "2.10.0",
"resolved": "https://registry.npm.taobao.org/yauzl/download/yauzl-2.10.0.tgz", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
"integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
"dev": true, "dev": true,
"requires": { "requires": {

View File

@ -163,6 +163,7 @@
"homepage": "https://github.com/lyswhut/lx-music-desktop#readme", "homepage": "https://github.com/lyswhut/lx-music-desktop#readme",
"devDependencies": { "devDependencies": {
"@babel/core": "^7.13.1", "@babel/core": "^7.13.1",
"@babel/plugin-proposal-class-properties": "^7.13.0",
"@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-modules-umd": "^7.13.0", "@babel/plugin-transform-modules-umd": "^7.13.0",
"@babel/plugin-transform-runtime": "^7.13.4", "@babel/plugin-transform-runtime": "^7.13.4",

View File

@ -85,7 +85,7 @@ div(:class="$style.player")
</template> </template>
<script> <script>
import Lyric from 'lrc-file-parser' import Lyric from '@renderer/utils/lyric-font-player'
import { rendererSend, rendererOn, NAMES } from '../../../common/ipc' import { rendererSend, rendererOn, NAMES } from '../../../common/ipc'
import { formatPlayTime2, getRandom, checkPath, setTitle, clipboardWriteText, debounce, throttle, assertApiSupport } from '../../utils' import { formatPlayTime2, getRandom, checkPath, setTitle, clipboardWriteText, debounce, throttle, assertApiSupport } from '../../utils'
import { mapGetters, mapActions, mapMutations } from 'vuex' import { mapGetters, mapActions, mapMutations } from 'vuex'
@ -452,6 +452,9 @@ export default {
}) })
window.lrc = new Lyric({ window.lrc = new Lyric({
className: 'lrc-content',
shadowContent: false,
activeLineClassName: 'active',
onPlay: (line, text) => { onPlay: (line, text) => {
this.lyric.text = text this.lyric.text = text
this.lyric.line = line this.lyric.line = line
@ -463,7 +466,7 @@ export default {
this.lyric.lines = lines this.lyric.lines = lines
this.lyric.line = 0 this.lyric.line = 0
}, },
offset: 80, // offset: 80,
}) })
this.handleRegisterEvent('on') this.handleRegisterEvent('on')
@ -641,6 +644,7 @@ export default {
this.getLrc(targetSong).then(() => { this.getLrc(targetSong).then(() => {
this.musicInfo.lrc = targetSong.lrc this.musicInfo.lrc = targetSong.lrc
this.musicInfo.tlrc = targetSong.tlrc this.musicInfo.tlrc = targetSong.tlrc
this.musicInfo.lxlrc = targetSong.lxlrc
}).catch(() => { }).catch(() => {
this.status = this.statusText = this.$t('core.player.lyric_error') this.status = this.statusText = this.$t('core.player.lyric_error')
}).finally(() => { }).finally(() => {
@ -658,6 +662,7 @@ export default {
this.musicInfo.songmid = null this.musicInfo.songmid = null
this.musicInfo.lrc = null this.musicInfo.lrc = null
this.musicInfo.tlrc = null this.musicInfo.tlrc = null
this.musicInfo.lxlrc = null
this.musicInfo.url = null this.musicInfo.url = null
this.nowPlayTime = 0 this.nowPlayTime = 0
this.maxPlayTime = 0 this.maxPlayTime = 0
@ -848,7 +853,14 @@ export default {
}) })
}, },
setLyric() { setLyric() {
window.lrc.setLyric((this.setting.player.isShowLyricTransition && this.musicInfo.tlrc ? (this.musicInfo.tlrc + '\n') : '') + (this.musicInfo.lrc || '')) window.lrc.setLyric(
this.musicInfo.lxlrc ? this.musicInfo.lxlrc : this.musicInfo.lrc,
// (
// this.setting.player.isShowLyricTransition && this.musicInfo.tlrc
// ? (this.musicInfo.tlrc + '\n')
// : ''
// ) + (this.musicInfo.lrc || ''),
)
if (this.isPlay && (this.musicInfo.url || this.listId == 'download')) { if (this.isPlay && (this.musicInfo.url || this.listId == 'download')) {
window.lrc.play(audio.currentTime * 1000) window.lrc.play(audio.currentTime * 1000)
this.handleUpdateWinLyricInfo('play', audio.currentTime * 1000) this.handleUpdateWinLyricInfo('play', audio.currentTime * 1000)

View File

@ -29,7 +29,8 @@
div(:class="$style.right") div(:class="$style.right")
div(:class="[$style.lyric, lyricEvent.isMsDown ? $style.draging : null]" @wheel="handleWheel" @mousedown="handleLyricMouseDown" ref="dom_lyric") div(:class="[$style.lyric, lyricEvent.isMsDown ? $style.draging : null]" @wheel="handleWheel" @mousedown="handleLyricMouseDown" ref="dom_lyric")
div(:class="$style.lyricSpace") div(:class="$style.lyricSpace")
p(v-for="(info, index) in lyricLines" :key="index" :class="lyric.line == index ? $style.lrcActive : null") {{info.text}} div(:class="[$style.lyricText]" ref="dom_lyric_text")
//- p(v-for="(info, index) in lyricLines" :key="index" :class="lyric.line == index ? $style.lrcActive : null") {{info.text}}
div(:class="$style.lyricSpace") div(:class="$style.lyricSpace")
material-music-comment(:class="$style.comment" :titleFormat="this.setting.download.fileName" :musicInfo="musicInfo" v-model="isShowComment") material-music-comment(:class="$style.comment" :titleFormat="this.setting.download.fileName" :musicInfo="musicInfo" v-model="isShowComment")
@ -158,11 +159,14 @@ export default {
handler(n, o) { handler(n, o) {
this.isSetedLines = true this.isSetedLines = true
if (o) { if (o) {
this.$refs.dom_lyric_text.textContent = ''
this.setLyric(n)
this._lyricLines = n this._lyricLines = n
if (n.length) { if (n.length) {
this.lyricLines = n this.lyricLines = n
this.$nextTick(() => { this.$nextTick(() => {
this.dom_lines = this.$refs.dom_lyric.querySelectorAll('p') this.dom_lines = this.$refs.dom_lyric.querySelectorAll('.lrc-content')
this.handleScrollLrc() this.handleScrollLrc()
}) })
} else { } else {
@ -174,7 +178,7 @@ export default {
if (this.lyricLines === this._lyricLines && this._lyricLines.length) return if (this.lyricLines === this._lyricLines && this._lyricLines.length) return
this.lyricLines = this._lyricLines this.lyricLines = this._lyricLines
this.$nextTick(() => { this.$nextTick(() => {
this.dom_lines = this.$refs.dom_lyric.querySelectorAll('p') this.dom_lines = this.$refs.dom_lyric.querySelectorAll('.lrc-content')
this.handleScrollLrc() this.handleScrollLrc()
}) })
}, 50) }, 50)
@ -182,7 +186,7 @@ export default {
} else { } else {
this.lyricLines = n this.lyricLines = n
this.$nextTick(() => { this.$nextTick(() => {
this.dom_lines = this.$refs.dom_lyric.querySelectorAll('p') this.dom_lines = this.$refs.dom_lyric.querySelectorAll('.lrc-content')
this.handleScrollLrc() this.handleScrollLrc()
}) })
} }
@ -235,6 +239,8 @@ export default {
document.addEventListener('mousemove', this.handleMouseMsMove) document.addEventListener('mousemove', this.handleMouseMsMove)
document.addEventListener('mouseup', this.handleMouseMsUp) document.addEventListener('mouseup', this.handleMouseMsUp)
window.addEventListener('resize', this.handleResize) window.addEventListener('resize', this.handleResize)
console.log('object', this.$refs.dom_lyric_text)
this.setLyric(this.lyricLines)
}, },
beforeDestroy() { beforeDestroy() {
this.clearLyricScrollTimeout() this.clearLyricScrollTimeout()
@ -250,6 +256,13 @@ export default {
...mapMutations('player', [ ...mapMutations('player', [
'visiblePlayerDetail', 'visiblePlayerDetail',
]), ]),
setLyric(lines) {
const dom_lines = document.createDocumentFragment()
for (const line of lines) {
dom_lines.appendChild(line.dom_line)
}
this.$refs.dom_lyric_text.appendChild(dom_lines)
},
handleResize() { handleResize() {
this.setProgressWidth() this.setProgressWidth()
}, },
@ -540,6 +553,7 @@ export default {
&:before { &:before {
position: absolute; position: absolute;
z-index: 1;
top: 0; top: 0;
left: 0; left: 0;
content: ' '; content: ' ';
@ -568,13 +582,39 @@ export default {
&.draging { &.draging {
cursor: grabbing; cursor: grabbing;
} }
p { :global {
padding: 8px 0; .lrc-content {
line-height: 1.2; line-height: 1.2;
padding: 8px 0;
overflow-wrap: break-word; overflow-wrap: break-word;
transition: @transition-theme !important;
transition-property: color, font-size; &.active {
span {
// color: @color-theme;
font-size: 1.2em;
} }
}
span {
transition: @transition-theme !important;
transition-property: font-size;
font-size: 1em;
background-repeat: no-repeat;
background-color: rgba(77, 77, 77, 0.9);
background-image: -webkit-linear-gradient(top, @color-theme, @color-theme);
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
background-size: 0 100%;
}
}
}
// p {
// padding: 8px 0;
// line-height: 1.2;
// overflow-wrap: break-word;
// transition: @transition-theme !important;
// transition-property: color, font-size;
// }
} }
.lyric-space { .lyric-space {
height: 70%; height: 70%;
@ -736,6 +776,7 @@ each(@themes, {
.container { .container {
border-left-color: ~'@{color-@{value}-theme}'; border-left-color: ~'@{color-@{value}-theme}';
background-color: ~'@{color-@{value}-theme_2-background_1}'; background-color: ~'@{color-@{value}-theme_2-background_1}';
// color: ~'@{color-@{value}-theme_2-font}';
} }
.right { .right {
&:before { &:before {
@ -769,9 +810,17 @@ each(@themes, {
box-shadow: 0 0 4px ~'@{color-@{value}-theme-hover}'; box-shadow: 0 0 4px ~'@{color-@{value}-theme-hover}';
// border-color: ~'@{color-@{value}-theme-hover}'; // border-color: ~'@{color-@{value}-theme-hover}';
} }
.lrc-active { :global {
color: ~'@{color-@{value}-theme}'; .lrc-content {
span {
// background-color: ~'@{color-@{value}-theme_2-font}';
background-image: -webkit-linear-gradient(top, ~'@{color-@{value}-theme}', ~'@{color-@{value}-theme}');
} }
}
}
// .lrc-active {
// color: ~'@{color-@{value}-theme}';
// }
.footerLeftControlBtns { .footerLeftControlBtns {
color: ~'@{color-@{value}-theme_2-font}'; color: ~'@{color-@{value}-theme_2-font}';
} }

View File

@ -187,16 +187,21 @@ const actions = {
if (musicInfo.lrc && musicInfo.tlrc != null) { if (musicInfo.lrc && musicInfo.tlrc != null) {
if (musicInfo.lrc.startsWith('\ufeff[id:$00000000]')) { if (musicInfo.lrc.startsWith('\ufeff[id:$00000000]')) {
let str = musicInfo.lrc.replace('\ufeff[id:$00000000]\n', '') let str = musicInfo.lrc.replace('\ufeff[id:$00000000]\n', '')
commit('setLrc', { musicInfo, lyric: str, tlyric: musicInfo.tlrc }) commit('setLrc', { musicInfo, lyric: str, tlyric: musicInfo.tlrc, lxlyric: musicInfo.tlrc })
} else if (musicInfo.lrc.startsWith('[id:$00000000]')) {
let str = musicInfo.lrc.replace('[id:$00000000]\n', '')
commit('setLrc', { musicInfo, lyric: str, tlyric: musicInfo.tlrc, lxlyric: musicInfo.tlrc })
} }
if ((musicInfo.lxlrc == null && musicInfo.source != 'kg') || musicInfo.lxlrc != null) {
return Promise.resolve() return Promise.resolve()
} }
}
// lrcRequest = music[musicInfo.source].getLyric(musicInfo) // lrcRequest = music[musicInfo.source].getLyric(musicInfo)
return getLyric.call(this, musicInfo).then(({ lyric, tlyric }) => { return getLyric.call(this, musicInfo).then(({ lyric, tlyric, lxlyric }) => {
// lrcRequest = null // lrcRequest = null
commit('setLrc', { musicInfo, lyric, tlyric }) commit('setLrc', { musicInfo, lyric, tlyric, lxlyric })
}).catch(err => { }).catch(err => {
// lrcRequest = null // lrcRequest = null
return Promise.reject(err) return Promise.reject(err)
@ -332,6 +337,7 @@ const mutations = {
setLrc(state, datas) { setLrc(state, datas) {
datas.musicInfo.lrc = datas.lyric datas.musicInfo.lrc = datas.lyric
datas.musicInfo.tlrc = datas.tlyric datas.musicInfo.tlrc = datas.tlyric
datas.musicInfo.lxlrc = datas.lxlyric
}, },
setList(state, { list, index }) { setList(state, { list, index }) {
state.playMusicInfo = { state.playMusicInfo = {

View File

@ -0,0 +1,267 @@
const { getNow, TimeoutTools } = require('./utils')
// const fontFormateRxp = /(?=<\d+,\d+>).*?/g
const fontSplitRxp = /(?=<\d+,\d+>).*?/g
const timeRxp = /<(\d+),(\d+)>/
// Create animation
const createAnimation = (dom, duration) => new window.Animation(new window.KeyframeEffect(dom, [
{ backgroundSize: '0 100%' },
{ backgroundSize: '100% 100%' },
], {
duration,
easing: 'linear',
},
), document.timeline)
// https://jsfiddle.net/ceqpnbky/
// https://jsfiddle.net/ceqpnbky/1/
module.exports = class FontPlayer {
constructor({ lyric = '', className = '', shadowContent = false, shadowClassName = '' }) {
this.lyric = lyric
this.className = className
this.shadowContent = shadowContent
this.shadowClassName = shadowClassName
this.isPlay = false
this.curFontNum = 0
this.maxFontNum = 0
this._performanceTime = 0
this._performanceOffsetTime = 0
this.fontContent = null
this.timeoutTools = new TimeoutTools()
this.waitPlayTimeout = new TimeoutTools()
this._init()
}
_init() {
if (this.lyric == null) this.lyric = ''
this.isLineMode = false
this.fontContent = document.createElement('div')
this.fontContent.style = 'position:relative;'
this.fontContent.className = this.className
if (this.shadowContent) {
this.fontShadowContent = document.createElement('div')
this.fontShadowContent.style = 'position:absolute;top:0;left:0;width:100%;z-index:-1;'
this.fontShadowContent.className = this.shadowClassName
this.fontContent.appendChild(this.fontShadowContent)
}
this._parseLyric()
}
_parseLyric() {
const fonts = this.lyric.split(fontSplitRxp)
// console.log(fonts)
this.maxFontNum = fonts.length - 1
this.fonts = []
let text
for (const font of fonts) {
text = font.replace(timeRxp, '')
if (RegExp.$2 == '') return this._handleLineParse()
const time = parseInt(RegExp.$2)
const dom = document.createElement('span')
let shadowDom
dom.textContent = text
const animation = createAnimation(dom, time)
this.fontContent.appendChild(dom)
if (this.shadowContent) {
shadowDom = document.createElement('span')
shadowDom.textContent = text
this.fontShadowContent.appendChild(shadowDom)
}
// dom.style = shadowDom.style = this.fontStyle
// dom.className = shadowDom.className = this.fontClassName
this.fonts.push({
text,
startTime: parseInt(RegExp.$1),
time,
dom,
shadowDom,
animation,
})
}
// console.log(this.fonts)
}
_handleLineParse() {
this.isLineMode = true
const dom = document.createElement('span')
let shadowDom
dom.textContent = this.lyric
if (this.shadowContent) {
shadowDom = document.createElement('span')
shadowDom.textContent = this.lyric
this.fontShadowContent.appendChild(shadowDom)
}
this.fontContent.appendChild(dom)
this.fonts.push({
text: this.lyric,
dom,
shadowDom,
})
}
_currentTime() {
return getNow() - this._performanceTime + this._performanceOffsetTime
}
_findcurFontNum(curTime) {
const length = this.fonts.length
for (let index = 0; index < length; index++) if (curTime <= this.fonts[index].startTime) return index === 0 ? 0 : index - 1
return length - 1
}
_handlePlayMaxFontNum() {
let curFont = this.fonts[this.curFontNum]
// console.log(curFont.text)
const currentTime = this._currentTime()
const driftTime = currentTime - curFont.startTime
if (currentTime > curFont.startTime + curFont.time) {
this._handlePlayFont(curFont, driftTime, true)
this.pause()
} else {
this._handlePlayFont(curFont, driftTime)
this.isPlay = false
}
}
_handlePlayFont(font, currentTime, toFinishe) {
switch (font.animation.playState) {
case 'finished':
break
case 'idle':
font.dom.style.backgroundSize = '100% 100%'
if (!toFinishe) font.animation.play()
break
default:
if (toFinishe) {
font.animation.cancel()
} else {
font.animation.currentTime = currentTime
font.animation.play()
}
break
}
}
_handlePlayLine(isPlayed) {
this.isPlay = false
this.fonts[0].dom.style.backgroundSize = isPlayed ? '100% 100%' : '100% 0'
}
_handlePauseFont(font) {
if (font.animation.playState == 'running') font.animation.pause()
}
_refresh() {
this.curFontNum++
// console.log('curFontNum time', this.fonts[this.curFontNum].time)
if (this.curFontNum === this.maxFontNum) return this._handlePlayMaxFontNum()
let curFont = this.fonts[this.curFontNum]
let nextFont = this.fonts[this.curFontNum + 1]
// console.log(curFont, nextFont, this.curFontNum, this.maxFontNum)
const currentTime = this._currentTime()
// console.log(curFont.text)
const driftTime = currentTime - curFont.startTime
// console.log(currentTime, driftTime)
if (driftTime >= 0 || this.curFontNum == 0) {
this.delay = nextFont.startTime - curFont.startTime - driftTime
if (this.delay > 0) {
this._handlePlayFont(curFont, driftTime)
this.timeoutTools.start(() => {
this._refresh()
}, this.delay)
return
}
} else if (this.curFontNum == 0) {
this.curFontNum--
this.waitPlayTimeout.start(() => {
this._refresh()
}, -driftTime)
return
}
this.curFontNum = this._findcurFontNum(currentTime)
for (let i = 0; i < this.curFontNum; i++) this._handlePlayFont(this.fonts[i], 0, true)
this.curFontNum--
this._refresh()
}
play(curTime = 0) {
// console.log('play', curTime)
if (!this.fonts.length) return
this.pause()
if (this.isLineMode) return this._handlePlayLine(true)
this.isPlay = true
this._performanceTime = getNow() - curTime
this._performanceOffsetTime = 0
if (this._performanceTime < 0) {
this._performanceOffsetTime = -this._performanceTime
this._performanceTime = 0
}
this.curFontNum = this._findcurFontNum(curTime)
for (let i = this.curFontNum; i > -1; i--) {
this._handlePlayFont(this.fonts[i], 0, true)
}
for (let i = this.curFontNum, len = this.fonts.length; i < len; i++) {
let font = this.fonts[i]
font.animation.cancel()
font.dom.style.backgroundSize = '0 100%'
}
this.curFontNum--
this._refresh()
}
pause() {
if (!this.isPlay) return
this.isPlay = false
this.timeoutTools.clear()
this.waitPlayTimeout.clear()
this._handlePauseFont(this.fonts[this.curFontNum])
if (this.curFontNum === this.maxLine) return
const curFontNum = this._findcurFontNum(this._currentTime())
if (this.curFontNum === curFontNum) return
for (let i = 0; i < this.curFontNum; i++) this._handlePlayFont(this.fonts[i], 0, true)
}
finish() {
this.pause()
if (this.isLineMode) return this._handlePlayLine(true)
for (const font of this.fonts) {
font.animation.cancel()
font.dom.style.backgroundSize = '100% 100%'
}
this.curFontNum = this.maxFontNum
}
reset() {
this.pause()
if (this.isLineMode) return this._handlePlayLine(false)
for (const font of this.fonts) {
font.animation.cancel()
font.dom.style.backgroundSize = '0 100%'
}
this.curFontNum = 0
}
}

View File

@ -0,0 +1,148 @@
const LinePlayer = require('./line-player')
const FontPlayer = require('./font-player')
const fontTimeExp = /<(\d+),(\d+)>/g
module.exports = class Lyric {
constructor({
lyric = '',
offset = 150,
className = '',
activeLineClassName = 'active',
shadowClassName = '',
shadowContent = false,
onPlay = function() { },
onSetLyric = function() { },
}) {
this.lyric = lyric
this.offset = offset
this.onPlay = onPlay
this.onSetLyric = onSetLyric
this.className = className
this.activeLineClassName = activeLineClassName
this.shadowClassName = shadowClassName
this.shadowContent = shadowContent
this.playingLineNum = -1
this.isLineMode = false
}
_init() {
this.playingLineNum = -1
this.isLineMode = false
if (this.linePlayer) {
this.linePlayer.setLyric(this.lyric)
} else {
this.linePlayer = new LinePlayer({
lyric: this.lyric,
offset: this.offset,
onPlay: this._handleLinePlayerOnPlay,
onSetLyric: this._handleLinePlayerOnSetLyric,
})
}
}
_handleLinePlayerOnPlay = (num, text, curTime) => {
if (this.isLineMode) {
if (num < this.playingLineNum + 1) {
for (let i = this.playingLineNum; i > num - 1; i--) {
const font = this._lineFonts[i]
font.reset()
font.fontContent.classList.remove(this.activeLineClassName)
}
} else if (num > this.playingLineNum + 1) {
for (let i = Math.max(this.playingLineNum, 0); i < num; i++) {
const font = this._lineFonts[i]
font.reset()
font.fontContent.classList.remove(this.activeLineClassName)
}
} else if (this.playingLineNum > -1) {
const font = this._lineFonts[this.playingLineNum]
font.reset()
font.fontContent.classList.remove(this.activeLineClassName)
}
} else {
if (num < this.playingLineNum + 1) {
for (let i = this.playingLineNum; i > num - 1; i--) {
const font = this._lineFonts[i]
font.fontContent.classList.remove(this.activeLineClassName)
font.reset()
}
} else if (num > this.playingLineNum + 1) {
for (let i = Math.max(this.playingLineNum, 0); i < num; i++) {
const font = this._lineFonts[i]
font.fontContent.classList.remove(this.activeLineClassName)
font.finish()
}
} else if (this.playingLineNum > -1) {
const font = this._lineFonts[this.playingLineNum]
font.fontContent.classList.remove(this.activeLineClassName)
}
}
this.playingLineNum = num
const font = this._lineFonts[num]
font.fontContent.classList.add(this.activeLineClassName)
font.play(curTime - this._lines[num].time)
this.onPlay(num, this._lines[num].text)
}
_handleLinePlayerOnSetLyric = lyricLines => {
// this._lines = lyricsLines
this.isLineMode = lyricLines.length && !/^<\d+,\d+>/.test(lyricLines[0].text)
this._lineFonts = []
if (this.isLineMode) {
this._lines = lyricLines.map(line => {
const fontPlayer = new FontPlayer({
lyric: line.text,
className: this.className,
shadowClassName: this.shadowClassName,
shadowContent: this.shadowContent,
})
this._lineFonts.push(fontPlayer)
return {
text: line.text,
time: line.time,
dom_line: fontPlayer.fontContent,
}
})
} else {
this._lines = lyricLines.map(line => {
const fontPlayer = new FontPlayer({
lyric: line.text,
className: this.className,
shadowClassName: this.shadowClassName,
shadowContent: this.shadowContent,
})
this._lineFonts.push(fontPlayer)
return {
text: line.text.replace(fontTimeExp, ''),
time: line.time,
dom_line: fontPlayer.fontContent,
}
})
}
this.onSetLyric(this._lines)
}
play(curTime) {
if (!this.linePlayer) return
this.linePlayer.play(curTime)
}
pause() {
if (!this.linePlayer) return
this.linePlayer.pause()
if (this.playingLineNum > -1) this._lineFonts[this.playingLineNum].pause()
}
setLyric(lyric) {
this.lyric = lyric
this._init()
}
}

View File

@ -0,0 +1,153 @@
const { getNow, TimeoutTools } = require('./utils')
const timeExp = /^\[([\d:.]*)\]{1}/g
const tagRegMap = {
title: 'ti',
artist: 'ar',
album: 'al',
offset: 'offset',
by: 'by',
}
const timeoutTools = new TimeoutTools()
module.exports = class LinePlayer {
constructor({ lyric = '', offset = 0, onPlay = function() { }, onSetLyric = function() { } } = {}) {
this.lyric = lyric
this.tags = {}
this.lines = null
this.onPlay = onPlay
this.onSetLyric = onSetLyric
this.isPlay = false
this.curLineNum = 0
this.maxLine = 0
this.offset = offset
this.isOffseted = false
this._performanceTime = 0
this._performanceOffsetTime = 0
this._init()
}
_init() {
if (this.lyric == null) this.lyric = ''
this._initTag()
this._initLines()
this.onSetLyric(this.lines)
}
_initTag() {
for (let tag in tagRegMap) {
const matches = this.lyric.match(new RegExp(`\\[${tagRegMap[tag]}:([^\\]]*)]`, 'i'))
this.tags[tag] = (matches && matches[1]) || ''
}
}
_initLines() {
this.lines = []
const lines = this.lyric.split('\n')
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim()
let result = timeExp.exec(line)
if (result) {
const text = line.replace(timeExp, '').trim()
if (text) {
const timeArr = RegExp.$1.split(':')
if (timeArr.length < 3) timeArr.unshift(0)
if (timeArr[2].indexOf('.') > -1) {
timeArr.push(...timeArr[2].split('.'))
timeArr.splice(2, 1)
}
this.lines.push({
time: parseInt(timeArr[0]) * 60 * 60 * 1000 + parseInt(timeArr[1]) * 60 * 1000 + parseInt(timeArr[2]) * 1000 + parseInt(timeArr[3] || 0),
text,
})
}
}
}
this.lines.sort((a, b) => {
return a.time - b.time
})
this.maxLine = this.lines.length - 1
}
_currentTime() {
return getNow() - this._performanceTime + this._performanceOffsetTime
}
_findCurLineNum(curTime) {
const length = this.lines.length
for (let index = 0; index < length; index++) if (curTime <= this.lines[index].time) return index === 0 ? 0 : index - 1
return length - 1
}
_handleMaxLine() {
this.onPlay(this.curLineNum, this.lines[this.curLineNum].text, this._currentTime())
this.pause()
}
_refresh() {
this.curLineNum++
// console.log('curLineNum time', this.lines[this.curLineNum].time)
let curLine = this.lines[this.curLineNum]
let nextLine = this.lines[this.curLineNum + 1]
const currentTime = this._currentTime()
const driftTime = currentTime - curLine.time
if (driftTime >= 0 || this.curLineNum === 0) {
if (this.curLineNum === this.maxLine) return this._handleMaxLine()
this.delay = nextLine.time - curLine.time - driftTime
if (this.delay > 0) {
if (!this.isOffseted && this.delay >= this.offset) {
this._performanceOffsetTime += this.offset
this.delay -= this.offset
this.isOffseted = true
}
timeoutTools.start(() => {
this._refresh()
}, this.delay)
this.onPlay(this.curLineNum, curLine.text, currentTime)
return
}
}
this.curLineNum = this._findCurLineNum(currentTime) - 1
this._refresh()
}
play(curTime = 0) {
if (!this.lines.length) return
this.pause()
this.isPlay = true
this._performanceOffsetTime = 0
this._performanceTime = getNow() - curTime
if (this._performanceTime < 0) {
this._performanceOffsetTime = -this._performanceTime
this._performanceTime = 0
}
this.curLineNum = this._findCurLineNum(curTime) - 1
this._refresh()
}
pause() {
if (!this.isPlay) return
this.isPlay = false
this.isOffseted = false
timeoutTools.clear()
if (this.curLineNum === this.maxLine) return
const currentTime = this._currentTime()
const curLineNum = this._findCurLineNum(currentTime)
if (this.curLineNum !== curLineNum) {
this.curLineNum = curLineNum
this.onPlay(curLineNum, this.lines[curLineNum].text, currentTime)
}
}
setLyric(lyric) {
if (this.isPlay) this.pause()
this.lyric = lyric
this._init()
}
}

View File

@ -0,0 +1,50 @@
const getNow = exports.getNow = typeof performance == 'object' && window.performance.now ? window.performance.now.bind(window.performance) : Date.now.bind(Date)
exports.TimeoutTools = class TimeoutTools {
constructor() {
this.invokeTime = 0
this.animationFrameId = null
this.timeoutId = null
this.callback = null
this.thresholdTime = 200
}
run() {
this.animationFrameId = window.requestAnimationFrame(() => {
this.animationFrameId = null
let diff = this.invokeTime - getNow()
// console.log('diff', diff)
if (diff > 0) {
if (diff < this.thresholdTime) return this.run()
return this.timeoutId = setTimeout(() => {
this.timeoutId = null
this.run()
}, diff - this.thresholdTime)
}
// console.log('diff', diff)
this.callback(diff)
})
}
start(callback = () => {}, timeout = 0) {
// console.log(timeout)
this.callback = callback
this.invokeTime = getNow() + timeout
this.run()
}
clear() {
if (this.animationFrameId) {
window.cancelAnimationFrame(this.animationFrameId)
this.animationFrameId = null
}
if (this.timeoutId) {
window.clearTimeout(this.timeoutId)
this.timeoutId = null
}
}
}

View File

@ -2,10 +2,13 @@ import { httpFetch } from '../../request'
import { decodeLyric } from './util' import { decodeLyric } from './util'
import { decodeName } from '../..' import { decodeName } from '../..'
const headExp = /^.*\[id:\$00000000\]\n/
const parseLyric = str => { const parseLyric = str => {
str = str.replace(/(?:<\d+,\d+,\d+>|\r)/g, '') str = str.replace(/\r/g, '')
if (str.startsWith('\ufeff[id:$00000000]')) str = str.replace('\ufeff[id:$00000000]\n', '') if (headExp.test(str)) str = str.replace(headExp, '')
let trans = str.match(/\[language:([\w=\\/+]+)\]/) let trans = str.match(/\[language:([\w=\\/+]+)\]/)
let lyric
let tlyric let tlyric
if (trans) { if (trans) {
str = str.replace(/\[language:[\w=\\/+]+\]\n/, '') str = str.replace(/\[language:[\w=\\/+]+\]\n/, '')
@ -18,7 +21,7 @@ const parseLyric = str => {
} }
} }
let i = 0 let i = 0
let lyric = str.replace(/\[((\d+),\d+)\].*/g, str => { let lxlyric = str.replace(/\[((\d+),\d+)\].*/g, str => {
let result = str.match(/\[((\d+),\d+)\].*/) let result = str.match(/\[((\d+),\d+)\].*/)
let time = parseInt(result[2]) let time = parseInt(result[2])
let ms = time % 1000 let ms = time % 1000
@ -31,11 +34,14 @@ const parseLyric = str => {
return str.replace(result[1], time) return str.replace(result[1], time)
}) })
tlyric = tlyric ? tlyric.join('\n') : '' tlyric = tlyric ? tlyric.join('\n') : ''
lyric = decodeName(lyric) lxlyric = lxlyric.replace(/<(\d+,\d+),\d+>/g, '<$1>')
lxlyric = decodeName(lxlyric)
lyric = lxlyric.replace(/<\d+,\d+>/g, '')
tlyric = decodeName(tlyric) tlyric = decodeName(tlyric)
return { return {
lyric, lyric,
tlyric, tlyric,
lxlyric,
} }
} }
@ -119,7 +125,7 @@ export default {
let requestObj = this.searchLyric(songInfo.name, songInfo.hash, songInfo._interval || this.getIntv(songInfo.interval)) let requestObj = this.searchLyric(songInfo.name, songInfo.hash, songInfo._interval || this.getIntv(songInfo.interval))
requestObj.promise = requestObj.promise.then(result => { requestObj.promise = requestObj.promise.then(result => {
if (!result) return { lyric: '', tlyric: '' } if (!result) return { lyric: '', tlyric: '', lxlyric: '' }
let requestObj2 = this.getLyricDownload(result.id, result.accessKey) let requestObj2 = this.getLyricDownload(result.id, result.accessKey)

View File

@ -20,7 +20,9 @@ const wy = {
return getLyric(songInfo.songmid) return getLyric(songInfo.songmid)
}, },
getPic(songInfo) { getPic(songInfo) {
return getMusicInfo(songInfo.songmid).then(info => info.al.picUrl) const requestObj = getMusicInfo(songInfo.songmid)
requestObj.promise = requestObj.promise.then(info => info.al.picUrl)
return requestObj
}, },
getMusicDetailPageUrl(songInfo) { getMusicDetailPageUrl(songInfo) {
return `https://music.163.com/#/song?id=${songInfo.songmid}` return `https://music.163.com/#/song?id=${songInfo.songmid}`