1.1.0 - Translations, downloads, bug fixes
This commit is contained in:
parent
2f8bca3dfb
commit
fbafb4286d
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@ app/dist/
|
||||
app/client/node_modules/
|
||||
electron_dist/
|
||||
freezer-*.tgz
|
||||
translations.zip
|
||||
|
@ -10,6 +10,6 @@ module.exports = {
|
||||
"ecmaVersion": 12
|
||||
},
|
||||
"rules": {
|
||||
"allowEmptyCatch": true
|
||||
"allowEmptyCatch": 0
|
||||
}
|
||||
};
|
||||
|
2
app/client/.env
Normal file
2
app/client/.env
Normal file
@ -0,0 +1,2 @@
|
||||
VUE_APP_I18N_LOCALE=en
|
||||
VUE_APP_I18N_FALLBACK_LOCALE=en
|
260
app/client/package-lock.json
generated
260
app/client/package-lock.json
generated
@ -80,6 +80,27 @@
|
||||
"postcss": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"@intlify/vue-i18n-loader": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/vue-i18n-loader/-/vue-i18n-loader-1.0.0.tgz",
|
||||
"integrity": "sha512-y7LlpKEQ01u7Yq14l4VNlbFYEHMmSEH1QXXASOMWspj9ZcIdCebhhvHCHqk5Oy5Epw3PtoxyRJNpb6Wle5udgA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"js-yaml": "^3.13.1",
|
||||
"json5": "^2.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"json5": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
|
||||
"integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@mdi/font": {
|
||||
"version": "5.5.55",
|
||||
"resolved": "https://registry.npmjs.org/@mdi/font/-/font-5.5.55.tgz",
|
||||
@ -2170,6 +2191,50 @@
|
||||
"integrity": "sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA==",
|
||||
"dev": true
|
||||
},
|
||||
"cli-table3": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz",
|
||||
"integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"colors": "^1.1.2",
|
||||
"object-assign": "^4.1.0",
|
||||
"string-width": "^2.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
|
||||
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
|
||||
"dev": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
||||
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
"strip-ansi": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
||||
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli-width": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
|
||||
@ -2288,6 +2353,13 @@
|
||||
"integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"colors": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
|
||||
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
@ -3322,6 +3394,16 @@
|
||||
"domelementtype": "1"
|
||||
}
|
||||
},
|
||||
"dot-object": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/dot-object/-/dot-object-1.9.0.tgz",
|
||||
"integrity": "sha512-7MPN6y7XhAO4vM4eguj5+5HNKLjJYfkVG1ZR1Aput4Q4TR6SYeSjhpVQ77IzJHoSHffKbDxBC+48aCiiRurDPw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "^2.20.0",
|
||||
"glob": "^7.1.4"
|
||||
}
|
||||
},
|
||||
"dot-prop": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
|
||||
@ -3765,6 +3847,12 @@
|
||||
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
|
||||
"dev": true
|
||||
},
|
||||
"esm": {
|
||||
"version": "3.2.25",
|
||||
"resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz",
|
||||
"integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==",
|
||||
"dev": true
|
||||
},
|
||||
"espree": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz",
|
||||
@ -4264,6 +4352,12 @@
|
||||
"pinkie-promise": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"flat": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
|
||||
"integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
|
||||
"dev": true
|
||||
},
|
||||
"flat-cache": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
|
||||
@ -5524,6 +5618,12 @@
|
||||
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
|
||||
"dev": true
|
||||
},
|
||||
"is-valid-glob": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz",
|
||||
"integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=",
|
||||
"dev": true
|
||||
},
|
||||
"is-windows": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
|
||||
@ -9783,6 +9883,54 @@
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz",
|
||||
"integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ=="
|
||||
},
|
||||
"vue-cli-plugin-i18n": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-cli-plugin-i18n/-/vue-cli-plugin-i18n-1.0.1.tgz",
|
||||
"integrity": "sha512-sLo6YzudaWgn5dOMvrKixE5bb/onYGxcxm+0YexqoOx0QtR+7hZ/P5WPFBMM9v/2i1ec2YYe2PvKTBel7KE+tA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"debug": "^4.1.0",
|
||||
"deepmerge": "^4.2.0",
|
||||
"dotenv": "^8.2.0",
|
||||
"flat": "^5.0.0",
|
||||
"rimraf": "^3.0.0",
|
||||
"vue": "^2.6.11",
|
||||
"vue-i18n": "^8.17.0",
|
||||
"vue-i18n-extract": "1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
|
||||
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"deepmerge": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
|
||||
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
|
||||
"dev": true
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"vue-cli-plugin-vuetify": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/vue-cli-plugin-vuetify/-/vue-cli-plugin-vuetify-2.0.7.tgz",
|
||||
@ -9854,6 +10002,118 @@
|
||||
"integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==",
|
||||
"dev": true
|
||||
},
|
||||
"vue-i18n": {
|
||||
"version": "8.22.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.22.1.tgz",
|
||||
"integrity": "sha512-JNgiEJ5a8YPfk5y2lKyfOAGLmkpAVfhaUi+T4wGpSppRYZ3XSyawSDDketY5KV2CsAiBLAGEIO6jO+0l2hQubg=="
|
||||
},
|
||||
"vue-i18n-extract": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-i18n-extract/-/vue-i18n-extract-1.0.2.tgz",
|
||||
"integrity": "sha512-+zwDKvle4KcfloXZnj5hF01ViKDiFr5RMx5507D7oyDXpSleRpekF5YHgZa/+Ra6Go68//z0Nya58J9tKFsCjw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cli-table3": "^0.5.1",
|
||||
"dot-object": "^1.7.1",
|
||||
"esm": "^3.2.13",
|
||||
"glob": "^7.1.3",
|
||||
"is-valid-glob": "^1.0.0",
|
||||
"yargs": "^13.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"cliui": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
|
||||
"integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^3.1.0",
|
||||
"strip-ansi": "^5.2.0",
|
||||
"wrap-ansi": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
|
||||
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
|
||||
"dev": true
|
||||
},
|
||||
"find-up": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
|
||||
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"locate-path": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
|
||||
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^7.0.1",
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
"strip-ansi": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
|
||||
"integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.0",
|
||||
"string-width": "^3.0.0",
|
||||
"strip-ansi": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"yargs": {
|
||||
"version": "13.3.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
|
||||
"integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^5.0.0",
|
||||
"find-up": "^3.0.0",
|
||||
"get-caller-file": "^2.0.1",
|
||||
"require-directory": "^2.1.1",
|
||||
"require-main-filename": "^2.0.0",
|
||||
"set-blocking": "^2.0.0",
|
||||
"string-width": "^3.0.0",
|
||||
"which-module": "^2.0.0",
|
||||
"y18n": "^4.0.0",
|
||||
"yargs-parser": "^13.1.2"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
|
||||
"integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"vue-loader": {
|
||||
"version": "15.9.3",
|
||||
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.3.tgz",
|
||||
|
@ -5,8 +5,9 @@
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"watch": "vue-cli-service build --watch",
|
||||
"lint": "vue-cli-service lint"
|
||||
"lint": "vue-cli-service lint",
|
||||
"i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'",
|
||||
"watch": "vue-cli-service build --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mdi/font": "^5.5.55",
|
||||
@ -14,11 +15,13 @@
|
||||
"roboto-fontface": "*",
|
||||
"vue": "^2.6.11",
|
||||
"vue-esc": "^3.0.1",
|
||||
"vue-i18n": "^8.17.3",
|
||||
"vue-router": "^3.2.0",
|
||||
"vue-socket.io": "^3.0.10",
|
||||
"vuetify": "^2.2.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@intlify/vue-i18n-loader": "^1.0.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-plugin-router": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
@ -26,6 +29,7 @@
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"sass": "^1.19.0",
|
||||
"sass-loader": "^8.0.0",
|
||||
"vue-cli-plugin-i18n": "~1.0.1",
|
||||
"vue-cli-plugin-vuetify": "~2.0.7",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"vuetify-loader": "^1.3.0"
|
||||
|
@ -31,7 +31,7 @@
|
||||
<v-list-item-icon>
|
||||
<v-icon>mdi-home</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-title>Home</v-list-item-title>
|
||||
<v-list-item-title>{{$t('Home')}}</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<!-- Browse link -->
|
||||
@ -39,10 +39,10 @@
|
||||
<v-list-item-icon>
|
||||
<v-icon>mdi-earth</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-title>Browse</v-list-item-title>
|
||||
<v-list-item-title>{{$t('Browse')}}</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<v-subheader inset>Library</v-subheader>
|
||||
<v-subheader inset>{{$t('Library')}}</v-subheader>
|
||||
<v-divider></v-divider>
|
||||
|
||||
<!-- Tracks -->
|
||||
@ -50,7 +50,7 @@
|
||||
<v-list-item-icon>
|
||||
<v-icon>mdi-music-note</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-title>Tracks</v-list-item-title>
|
||||
<v-list-item-title>{{$t('Tracks')}}</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<!-- Playlists -->
|
||||
@ -58,7 +58,7 @@
|
||||
<v-list-item-icon>
|
||||
<v-icon>mdi-playlist-music</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-title>Playlists</v-list-item-title>
|
||||
<v-list-item-title>{{$t('Playlists')}}</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<!-- Albums -->
|
||||
@ -66,7 +66,7 @@
|
||||
<v-list-item-icon>
|
||||
<v-icon>mdi-album</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-title>Albums</v-list-item-title>
|
||||
<v-list-item-title>{{$t('Albums')}}</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<!-- Artists -->
|
||||
@ -74,10 +74,10 @@
|
||||
<v-list-item-icon>
|
||||
<v-icon>mdi-account-music</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-title>Artists</v-list-item-title>
|
||||
<v-list-item-title>{{$t('Artists')}}</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<v-subheader inset>More</v-subheader>
|
||||
<v-subheader inset>{{$t('More')}}</v-subheader>
|
||||
<v-divider></v-divider>
|
||||
|
||||
<!-- Settings -->
|
||||
@ -85,30 +85,30 @@
|
||||
<v-list-item-icon>
|
||||
<v-icon>mdi-cog</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-title>Settings</v-list-item-title>
|
||||
<v-list-item-title>{{$t('Settings')}}</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<!-- Downloads -->
|
||||
<v-list-item link to='/downloads'>
|
||||
<!-- Downloads, shitty hack if downloads not yet loaded -->
|
||||
<v-list-item link to='/downloads' v-if='$root.downloads.queue'>
|
||||
|
||||
<!-- Download icon -->
|
||||
<v-list-item-icon v-if='!$root.download && $root.downloads.length == 0'>
|
||||
<v-list-item-icon v-if='!$root.downloads.downloading && $root.downloads.queue.length == 0'>
|
||||
<v-icon>mdi-download</v-icon>
|
||||
</v-list-item-icon>
|
||||
|
||||
<!-- Paused download -->
|
||||
<v-list-item-icon v-if='!$root.download && $root.downloads.length > 0'>
|
||||
<v-list-item-icon v-if='!$root.downloads.downloading && $root.downloads.queue.length > 0'>
|
||||
<v-icon>mdi-pause</v-icon>
|
||||
</v-list-item-icon>
|
||||
|
||||
<!-- Download in progress -->
|
||||
<v-list-item-icon v-if='$root.download'>
|
||||
<v-list-item-icon v-if='$root.downloads.downloading'>
|
||||
<v-progress-circular :value='downloadPercentage' style='top: -2px' class='text-caption'>
|
||||
{{$root.downloads.length + 1}}
|
||||
{{$root.downloads.queue.length + $root.downloads.threads.length}}
|
||||
</v-progress-circular>
|
||||
</v-list-item-icon>
|
||||
|
||||
<v-list-item-title>Downloads</v-list-item-title>
|
||||
<v-list-item-title>{{$t('Downloads')}}</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
</v-list>
|
||||
@ -133,7 +133,7 @@
|
||||
solo
|
||||
clearable
|
||||
hide-no-data
|
||||
placeholder='Search or paste Deezer URL. Use "/" to quickly focus.'
|
||||
:placeholder='$t("Search or paste Deezer URL. Use / to quickly focus.")'
|
||||
:loading='searchLoading'
|
||||
@keyup='search'
|
||||
ref='searchBar'
|
||||
@ -141,6 +141,7 @@
|
||||
:search-input.sync='searchInput'
|
||||
:items='suggestions'
|
||||
></v-autocomplete>
|
||||
|
||||
|
||||
</v-app-bar>
|
||||
|
||||
@ -355,12 +356,19 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
qualityText() {
|
||||
return `${this.$root.playbackInfo.format} ${this.$root.playbackInfo.quality}`;
|
||||
return `${this.$root.playbackInfo.qualityString}`;
|
||||
},
|
||||
downloadPercentage() {
|
||||
if (!this.$root.download) return 0;
|
||||
let p = (this.$root.download.downloaded / this.$root.download.size) * 100;
|
||||
if (isNaN(p)) return 0;
|
||||
if (!this.$root.downloads.downloading) return 0;
|
||||
|
||||
let downloaded = this.$root.downloads.threads.reduce((a, b) => a + b.downloaded, 0);
|
||||
let size = this.$root.downloads.threads.reduce((a, b) => a + b.size, 0);
|
||||
if (size == 0)
|
||||
size = 1;
|
||||
|
||||
let p = (downloaded / size) * 100;
|
||||
if (p > 100)
|
||||
p = 100;
|
||||
return Math.round(p);
|
||||
}
|
||||
},
|
||||
@ -413,6 +421,8 @@ export default {
|
||||
this.suggestions = [];
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.$root.settings.showAutocomplete) return;
|
||||
this.searchLoading = true;
|
||||
//Prevent spam
|
||||
setTimeout(() => {
|
||||
|
@ -33,7 +33,7 @@
|
||||
<v-icon>mdi-play</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Play</v-list-item-title>
|
||||
<v-list-item-title>{{$t("Play")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<!-- Add to library -->
|
||||
@ -42,7 +42,7 @@
|
||||
<v-icon>mdi-heart</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Add to library</v-list-item-title>
|
||||
<v-list-item-title>{{$t("Add to library")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<!-- Download -->
|
||||
@ -51,7 +51,7 @@
|
||||
<v-icon>mdi-download</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Download</v-list-item-title>
|
||||
<v-list-item-title>{{$t("Download")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{artist.name}}</v-list-item-title>
|
||||
<v-list-item-subtitle v-if='!tiny'>{{$abbreviation(artist.fans)}} fans</v-list-item-subtitle>
|
||||
<v-list-item-subtitle v-if='!tiny'>{{$abbreviation(artist.fans)}} {{$t("fans")}}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
<v-list-item-action>
|
||||
<!-- Context menu -->
|
||||
@ -23,7 +23,7 @@
|
||||
<v-icon>mdi-heart</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Add to library</v-list-item-title>
|
||||
<v-list-item-title>{{$t("Add to library")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
|
@ -5,29 +5,29 @@
|
||||
<v-card>
|
||||
|
||||
<v-card-title class='headline'>
|
||||
Download {{tracks.length}} tracks
|
||||
{{$t("Download")}} {{tracks.length}} {{$t("tracks")}}
|
||||
</v-card-title>
|
||||
<v-card-text class='pb-0'>
|
||||
|
||||
<v-select
|
||||
label='Quality'
|
||||
:label='$t("Quality")'
|
||||
persistent-hint
|
||||
:items='qualities'
|
||||
v-model='qualityString'
|
||||
:hint='"Estimated size: " + $filesize(estimatedSize)'
|
||||
:hint='$t("Estimated size:") + " " + $filesize(estimatedSize)'
|
||||
></v-select>
|
||||
|
||||
<v-checkbox
|
||||
v-model='autostart'
|
||||
label='Start downloading'
|
||||
:label='$t("Start downloading")'
|
||||
></v-checkbox>
|
||||
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn text @click='$emit("close")'>Cancel</v-btn>
|
||||
<v-btn text @click='download'>Download</v-btn>
|
||||
<v-btn text @click='$emit("close")'>{{$t("Cancel")}}</v-btn>
|
||||
<v-btn text @click='download'>{{$t("Download")}}</v-btn>
|
||||
</v-card-actions>
|
||||
|
||||
</v-card>
|
||||
|
@ -12,10 +12,10 @@
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>
|
||||
Streaming logging is disabled!
|
||||
{{$t("Stream logging is disabled!")}}
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
Enable it in settings for history to work properly.
|
||||
{{$t("Enable it in settings for history to work properly.")}}
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
@ -56,7 +56,7 @@ export default {
|
||||
//Load as queue and play
|
||||
play(index) {
|
||||
this.$root.queue.source = {
|
||||
text: 'History',
|
||||
text: this.$t('History'),
|
||||
source: 'history',
|
||||
data: null
|
||||
};
|
||||
|
@ -8,7 +8,7 @@
|
||||
<!-- Create playlist -->
|
||||
<v-btn class='ma-2 ml-3' color='primary' @click='popup = true'>
|
||||
<v-icon left>mdi-playlist-plus</v-icon>
|
||||
Create new playlist
|
||||
{{$t("Create new playlist")}}
|
||||
</v-btn>
|
||||
|
||||
<v-dialog max-width="400px" v-model='popup'>
|
||||
|
@ -2,10 +2,10 @@
|
||||
<div v-scroll.self='scroll'>
|
||||
<div class='px-4 pt-2 d-flex' style='max-height: 50px;'>
|
||||
<div class='text-overline px-2 pt-1'>
|
||||
{{count}} TRACKS.
|
||||
{{count}} {{$t("TRACKS")}}
|
||||
</div>
|
||||
<div style="max-width: 200px;" class='d-flex mx-2'>
|
||||
<v-select class='px-2' dense solo :items='sortTypes' @change='sort' label='Sort By'>
|
||||
<v-select class='px-2' dense solo :items='sortTypes' @change='sort' :label='$t("Sort by")'>
|
||||
</v-select>
|
||||
</div>
|
||||
<div class='px-2' @click='reverseSort'>
|
||||
@ -46,10 +46,10 @@ export default {
|
||||
tracks: [],
|
||||
count: 0,
|
||||
sortTypes: [
|
||||
'Date Added',
|
||||
'Name (A-Z)',
|
||||
'Artist (A-Z)',
|
||||
'Album (A-Z)'
|
||||
this.$t('Date Added'),
|
||||
this.$t('Name (A-Z)'),
|
||||
this.$t('Artist (A-Z)'),
|
||||
this.$t('Album (A-Z)')
|
||||
],
|
||||
tracksUnsorted: null,
|
||||
isReversed: false
|
||||
|
@ -34,7 +34,7 @@
|
||||
<!-- Error -->
|
||||
<div v-if='!loading && (!lyrics || (lyrics.text.length == 0 && lyrics.lyrics.length == 0))' class='pa-4 text-center'>
|
||||
<span class='red--text text-h5'>
|
||||
Error loading lyrics or lyrics not found!
|
||||
{{$t("Error loading lyrics or lyrics not found!")}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
<!-- Create playlist -->
|
||||
<v-card class='text-center pa-2' v-if='!addToPlaylist'>
|
||||
<v-card-text>
|
||||
<p primary-title class='display-1'>Create playlist</p>
|
||||
<p primary-title class='display-1'>{{$t("Create playlist")}}</p>
|
||||
<v-text-field label='Title' class='ma-2' v-model='title'></v-text-field>
|
||||
<v-textarea class='mx-2' v-model='description' label='Description' rows='1' auto-grow></v-textarea>
|
||||
<v-select class='mx-2' v-model='type' :items='types' label='Type'></v-select>
|
||||
@ -12,17 +12,17 @@
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn class='primary' :loading='createLoading' @click='create'>Create</v-btn>
|
||||
<v-btn class='primary' :loading='createLoading' @click='create'>{{$t("Create")}}</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
|
||||
<!-- Add to playlist -->
|
||||
<v-card class='text-center pa-2' v-if='addToPlaylist'>
|
||||
<v-card-text>
|
||||
<p primary-title class='display-1'>Add to playlist</p>
|
||||
<p primary-title class='display-1'>{{$t("Add to playlist")}}</p>
|
||||
<v-btn block class='mb-1' @click='addToPlaylist = false'>
|
||||
<v-icon left>mdi-playlist-plus</v-icon>
|
||||
Create New
|
||||
{{$t("Create new")}}
|
||||
</v-btn>
|
||||
<v-list>
|
||||
<div v-for='playlist in playlists' :key='playlist.id'>
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{playlist.title}}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{$numberString(playlist.trackCount)}} tracks</v-list-item-subtitle>
|
||||
<v-list-item-subtitle>{{$numberString(playlist.trackCount)}} {{$t("tracks")}}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
<v-list-item-action>
|
||||
<!-- Context menu -->
|
||||
@ -32,7 +32,7 @@
|
||||
<v-icon>mdi-play</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Play</v-list-item-title>
|
||||
<v-list-item-title>{{$t('Play')}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
<v-icon>mdi-playlist-remove</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Remove</v-list-item-title>
|
||||
<v-list-item-title>{{$t('Remove')}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
@ -52,7 +52,7 @@
|
||||
<v-icon>mdi-download</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Download</v-list-item-title>
|
||||
<v-list-item-title>{{$t('Download')}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
|
@ -45,7 +45,7 @@
|
||||
<v-icon>mdi-playlist-plus</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Play next</v-list-item-title>
|
||||
<v-list-item-title>{{$t("Play next")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<!-- Add to end of queue -->
|
||||
@ -54,7 +54,7 @@
|
||||
<v-icon>mdi-playlist-plus</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Add to queue</v-list-item-title>
|
||||
<v-list-item-title>{{$t("Add to queue")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<!-- Add to library -->
|
||||
@ -63,7 +63,7 @@
|
||||
<v-icon>mdi-heart</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Add to library</v-list-item-title>
|
||||
<v-list-item-title>{{$t("Add to library")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<!-- Remove from library -->
|
||||
@ -72,7 +72,7 @@
|
||||
<v-icon>mdi-heart-remove</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Remove from library</v-list-item-title>
|
||||
<v-list-item-title>{{$t("Remove from library")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<!-- Add to playlist -->
|
||||
@ -81,7 +81,7 @@
|
||||
<v-icon>mdi-playlist-plus</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Add to playlist</v-list-item-title>
|
||||
<v-list-item-title>{{$t("Add to playlist")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<!-- Remove from playlist -->
|
||||
@ -90,7 +90,16 @@
|
||||
<v-icon>mdi-playlist-remove</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Remove from playlist</v-list-item-title>
|
||||
<v-list-item-title>{{$t("Remove from playlist")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<!-- Play track mix -->
|
||||
<v-list-item dense @click='trackMix'>
|
||||
<v-list-item-icon>
|
||||
<v-icon>mdi-playlist-music</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{$t("Play track mix")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<!-- Go to album -->
|
||||
@ -99,7 +108,7 @@
|
||||
<v-icon>mdi-album</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Go to "{{track.album.title}}"</v-list-item-title>
|
||||
<v-list-item-title>{{$t("Go to")}} "{{track.album.title}}"</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<!-- Go to artists -->
|
||||
@ -113,7 +122,7 @@
|
||||
<v-icon>mdi-account-music</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Go to "{{artist.name}}"</v-list-item-title>
|
||||
<v-list-item-title>{{$t("Go to")}} "{{artist.name}}"</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
@ -123,7 +132,7 @@
|
||||
<v-icon>mdi-download</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Download</v-list-item-title>
|
||||
<v-list-item-title>{{$t("Download")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
@ -207,8 +216,18 @@ export default {
|
||||
this.$emit('remove');
|
||||
},
|
||||
//Download track
|
||||
async download() {
|
||||
download() {
|
||||
this.downloadDialog = true;
|
||||
},
|
||||
async trackMix() {
|
||||
let res = await this.$axios.get('/trackmix/' + this.track.id);
|
||||
this.$root.queue.source = {
|
||||
text: this.$t('Track Mix'),
|
||||
source: 'trackmix',
|
||||
data: this.track.id
|
||||
};
|
||||
this.$root.replaceQueue(res.data);
|
||||
this.$root.playIndex(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
23
app/client/src/js/i18n.js
Normal file
23
app/client/src/js/i18n.js
Normal file
@ -0,0 +1,23 @@
|
||||
import Vue from 'vue'
|
||||
import VueI18n from 'vue-i18n'
|
||||
|
||||
Vue.use(VueI18n);
|
||||
|
||||
function loadLocaleMessages () {
|
||||
const locales = require.context('../locales', true, /[A-Za-z0-9-_,\s]+\.json$/i)
|
||||
const messages = {}
|
||||
locales.keys().forEach(key => {
|
||||
const matched = key.match(/([A-Za-z0-9-_]+)\./i)
|
||||
if (matched && matched.length > 1) {
|
||||
const locale = matched[1]
|
||||
messages[locale] = locales(key)
|
||||
}
|
||||
})
|
||||
return messages
|
||||
}
|
||||
|
||||
export default new VueI18n({
|
||||
locale: process.env.VUE_APP_I18N_LOCALE || 'en',
|
||||
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
|
||||
messages: loadLocaleMessages()
|
||||
})
|
@ -8,6 +8,6 @@ Vue.use(Vuetify);
|
||||
|
||||
export default new Vuetify({
|
||||
theme: {
|
||||
dark: true
|
||||
dark: true,
|
||||
}
|
||||
});
|
||||
|
112
app/client/src/locales/ar.json
Normal file
112
app/client/src/locales/ar.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "القائمة الرئيسية",
|
||||
"Browse": "تصفح",
|
||||
"Library": "المكتبة",
|
||||
"Tracks": "أغاني",
|
||||
"Playlists": "قوائم تشغيل",
|
||||
"Albums": "البومات",
|
||||
"Artists": "فنانون",
|
||||
"More": "المزيد",
|
||||
"Settings": "الإعدادات",
|
||||
"Downloads": "التنزيلات",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "ابحث أو الصق رابط ديزر, استخدم \"/\" للتركيز السريع.",
|
||||
"Play": "تشغيل",
|
||||
"Add to library": "إضافة إلى المكتبة",
|
||||
"Download": "تنزيل",
|
||||
"fans": "المتابِعين",
|
||||
"tracks": "أغاني",
|
||||
"Quality": "الجودة",
|
||||
"Estimated size:": "الحجم المتوقع:",
|
||||
"Start downloading": "بدء التنزيل",
|
||||
"Cancel": "الغاء",
|
||||
"Stream logging is disabled!": "تسجيل البث معطل!",
|
||||
"Enable it in settings for history to work properly.": "فعله في الإعدادات لتفعيل تاريخ السماع بشكل صحيح.",
|
||||
"History": "تاريخ السماع",
|
||||
"Create new playlist": "انشاء قائمة تشغيل جديدة",
|
||||
"TRACKS": "أغاني",
|
||||
"Sort by": "ترتيب حسب",
|
||||
"Date Added": "تاريخ الإضافة",
|
||||
"Name (A-Z)": "الإسم (أ - ي)",
|
||||
"Artist (A-Z)": "الفنان (أ - ي)",
|
||||
"Album (A-Z)": "الألبوم (أ - ي)",
|
||||
"Error loading lyrics or lyrics not found!": "خطأ في تحميل كلمات الاغنية او الكلمات غير موجودة!",
|
||||
"Create playlist": "إنشاء قائمة التشغيل",
|
||||
"Create": "إنشاء",
|
||||
"Add to playlist": "اضافة الى قائمة التشغيل",
|
||||
"Create new": "إنشاء جديد",
|
||||
"Remove": "إزالة",
|
||||
"Play next": "شغل التالي",
|
||||
"Add to queue": "إضافة إلى قائمة الانتظار",
|
||||
"Remove from library": "إزالة من المكتبة",
|
||||
"Remove from playlist": "إزالة من قائمة التشغيل",
|
||||
"Play track mix": "تشغيل مزيج الاغاني",
|
||||
"Go to": "الذهاب الى",
|
||||
"Track Mix": "مزيج الاغاني",
|
||||
"Duration": "المدة",
|
||||
"Released": "تم إصداره",
|
||||
"Disk": "القرص",
|
||||
"albums": "البومات",
|
||||
"Play top": "تشغيل الأفضل",
|
||||
"Radio": "راديو",
|
||||
"Show all albums": "اضهار كل الالبومات",
|
||||
"Show all singles": "إظهار كل الأغاني المنفردة",
|
||||
"Show more": "اظهار المزيد",
|
||||
"Downloaded": "تم التنزيل",
|
||||
"Queue": "قائمة الانتظار",
|
||||
"Total": "المجموع",
|
||||
"Stop": "إيقاف",
|
||||
"Start": "بدء",
|
||||
"Show folder": "عرض المجلدات",
|
||||
"Clear queue": "تفريغ قائمة الإنتظار",
|
||||
"Playing from": "التشغيل من",
|
||||
"Info": "معلومات",
|
||||
"Lyrics": "كلمات الأغنية",
|
||||
"Track number": "رقم الأغنية",
|
||||
"Disk number": "رقم القرص",
|
||||
"Explicit": "صريحة (شتم)",
|
||||
"Source": "المصدر",
|
||||
"ID": "الرقم التعريفي",
|
||||
"Error logging in!": "خطأ في تسجيل الدخول!",
|
||||
"Please try again later, or try another account.": "الرجاء المحاولة مرة أخرى لاحقا، أو حاول حساب آخر.",
|
||||
"Logout": "تسجيل الخروج",
|
||||
"Login using browser": "تسجيل الدخول باستخدام المتصفح",
|
||||
"Please login using your Deezer account:": "يرجى تسجيل الدخول باستخدام حساب ديزر الخاص بك:",
|
||||
"...or paste your ARL/Token below:": "...أو لصق ARL/الرمز الخاص بك أدناه:",
|
||||
"ARL/Token": "ARL/الرمز المميز",
|
||||
"Login": "تسجيل الدخول",
|
||||
"By using this program, you disagree with Deezer's ToS.": "باستخدام هذا البرنامج، أنت لا توافق على شروط خدمة ديزر.",
|
||||
"Only in Electron version!": "فقط في إصدار إلكترون!",
|
||||
"Search results for:": "نتائج البحث عن:",
|
||||
"Error loading data!": "خطأ في تحميل البيانات!",
|
||||
"Try again later!": "حاول مرة اخرى لاحقا!",
|
||||
"Search": "بحث",
|
||||
"Streaming Quality": "جودة التشغيل",
|
||||
"Download Quality": "جودة التنزيل",
|
||||
"Downloads Directory": "مسار التنزيل",
|
||||
"Simultaneous downloads": "عدد التحميلات في نفس الوقت",
|
||||
"Always show download confirm dialog before downloading.": "اضهار مربع تأكيد التنزيل دائماً قبل التنزيل.",
|
||||
"Show download dialog": "عرض مربع تأكيد التنزيل",
|
||||
"Create folders for artists": "إنشاء ملفات للفنان",
|
||||
"Create folders for albums": "إنشاء ملفات للالبوم",
|
||||
"Download lyrics": "تنزيل ملف كلمات الاغنية. Lrc",
|
||||
"Variables": "المتغيرات",
|
||||
"UI": "واجهة المستخدم",
|
||||
"Show autocomplete in search": "إظهار الإكمال التلقائي في البحث",
|
||||
"Integrations": "الدمج",
|
||||
"This allows listening history, flow and recommendations to work properly.": "وهذا يتيح لسجل الاستماع و فلو والتوصيات, العمل على نحو سليم.",
|
||||
"Log track listens to Deezer": "سِجِل استماع الاغاني الى ديزر",
|
||||
"Connect your LastFM account to allow scrobbling.": "قم بتوصيل حساب LastFM الخاص بك للسماح بالتسجيل.",
|
||||
"Login with LastFM": "تسجيل الدخول في LastFM",
|
||||
"Disconnect LastFM": "تسجيل الخروج من LastFm",
|
||||
"Requires restart to apply!": "يتطلب إعادة التشغيل من أجل التطبيق!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "تمكين فعالية دسكورد، يتطلب إعادة تشغيل للتبديل!",
|
||||
"Discord Rich Presence": "فعالية دسكورد",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "تمكين زر انضمام دسكورد لمزامنة الاغاني، يتطلب إعادة تشغيل للتبديل!",
|
||||
"Discord Join Button": "زر الانضمام في دسكورد",
|
||||
"Other": "أخرى",
|
||||
"Minimize to tray": "تصغير إلى شريط المهام",
|
||||
"Don't minimize to tray": "عدم التصغير إلى شريط المهام",
|
||||
"Close on exit": "إغلاق عند الخروج",
|
||||
"Settings saved!": "تم حفظ الإعدادات!",
|
||||
"Available only in Electron version!": "متاح فقط في اصدار الإلكترون!"
|
||||
}
|
112
app/client/src/locales/de.json
Normal file
112
app/client/src/locales/de.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Start",
|
||||
"Browse": "Durchsuchen",
|
||||
"Library": "Mediathek",
|
||||
"Tracks": "Titel",
|
||||
"Playlists": "Wiedergabelisten",
|
||||
"Albums": "Alben",
|
||||
"Artists": "Künstler",
|
||||
"More": "Mehr",
|
||||
"Settings": "Einstellungen",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Suche oder füge Deezer URL ein. Benutze \"/\" um schnell zu fokussieren.",
|
||||
"Play": "Wiedergeben",
|
||||
"Add to library": "Zur Mediathek hinzufügen",
|
||||
"Download": "Download",
|
||||
"fans": "Fans",
|
||||
"tracks": "Titel",
|
||||
"Quality": "Qualität",
|
||||
"Estimated size:": "Geschätzte Zeit:",
|
||||
"Start downloading": "Download beginnen",
|
||||
"Cancel": "Abbrechen",
|
||||
"Stream logging is disabled!": "Streamprotokollierung ist deaktiviert!",
|
||||
"Enable it in settings for history to work properly.": "Aktiviere es in den Einstellungen, damit der Verlauf korrekt funktioniert.",
|
||||
"History": "Verlauf",
|
||||
"Create new playlist": "Neue Wiedergabeliste erstellen",
|
||||
"TRACKS": "Titel",
|
||||
"Sort by": "Sortieren nach",
|
||||
"Date Added": "Hinzugefügt am",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Künstler (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Fehler beim Laden der Songtexte oder Songtexte nicht gefunden!",
|
||||
"Create playlist": "Wiedergabeliste erstellen",
|
||||
"Create": "Erstellen",
|
||||
"Add to playlist": "Zur Wiedergabeliste hinzufügen",
|
||||
"Create new": "Neu erstellen",
|
||||
"Remove": "Entfernen",
|
||||
"Play next": "Als nächstes spielen",
|
||||
"Add to queue": "Zur Warteschleife hinzufügen",
|
||||
"Remove from library": "Aus der Mediathek entfernen",
|
||||
"Remove from playlist": "Aus Wiedergabeliste entfernen",
|
||||
"Play track mix": "Track Mix abspielen",
|
||||
"Go to": "Gehe zu",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Dauer",
|
||||
"Released": "Veröffentlicht",
|
||||
"Disk": "Disk",
|
||||
"albums": "Alben",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Zeige alle Alben",
|
||||
"Show all singles": "Zeige alle Singles",
|
||||
"Show more": "Mehr anzeigen",
|
||||
"Downloaded": "Heruntergeladen",
|
||||
"Queue": "Warteschleife",
|
||||
"Total": "Gesamt",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Ordner anzeigen",
|
||||
"Clear queue": "Warteschleife löschen",
|
||||
"Playing from": "Wiedergabe von",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Titelnummer",
|
||||
"Disk number": "Disk-Nummer",
|
||||
"Explicit": "Explizit",
|
||||
"Source": "Quelle",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Fehler beim einloggen!",
|
||||
"Please try again later, or try another account.": "Bitte versuche es später noch einmal oder versuche es mit einem anderen Konto.",
|
||||
"Logout": "Abmelden",
|
||||
"Login using browser": "Anmeldung über Browser",
|
||||
"Please login using your Deezer account:": "Bitte melde dich mit deinem Deezer-Konto an:",
|
||||
"...or paste your ARL/Token below:": "...oder füge dein ARL/Token unten ein:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Anmeldung",
|
||||
"By using this program, you disagree with Deezer's ToS.": "Durch die Verwendung dieses Programms lehnen Sie Deezer's ToS ab.",
|
||||
"Only in Electron version!": "Nur in der Electron-Version!",
|
||||
"Search results for:": "Suchergebnisse für:",
|
||||
"Error loading data!": "Fehler beim Laden der Daten!",
|
||||
"Try again later!": "Versuch's später nochmal!",
|
||||
"Search": "Suche",
|
||||
"Streaming Quality": "Streamqualität",
|
||||
"Download Quality": "Download-Qualität",
|
||||
"Downloads Directory": "Downloadverzeichnis",
|
||||
"Simultaneous downloads": "Gleichzeitige Downloads",
|
||||
"Always show download confirm dialog before downloading.": "Downloadbestätigungsdialog immer vor dem Download anzeigen.",
|
||||
"Show download dialog": "Download-Dialog anzeigen",
|
||||
"Create folders for artists": "Ordner für Künstler erstellen",
|
||||
"Create folders for albums": "Ordner für Alben erstellen",
|
||||
"Download lyrics": "Download Lyrics",
|
||||
"Variables": "Variablen",
|
||||
"UI": "Benutzeroberfläche",
|
||||
"Show autocomplete in search": "Auto-Vervollständigung in der Suche anzeigen",
|
||||
"Integrations": "Integrationen",
|
||||
"This allows listening history, flow and recommendations to work properly.": "Dies ermöglicht das korrekte Arbeiten von Wiedergabeverlauf, Flow und Empfehlungen.",
|
||||
"Log track listens to Deezer": "Prokotolliere gehörte Titel auf Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Verbinde dein LastFM-Konto, um das Scrobbing zu erlauben.",
|
||||
"Login with LastFM": "Anmelden mit LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Erfordert einen Neustart der App!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Discord Rich Presence aktivieren, erfordert einen Neustart zum Umschalten!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Aktiviere Discord Join für die Synchronisierung von Titel, benötigt einen Neustart zum Umschalten!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Andere",
|
||||
"Minimize to tray": "In Statusleiste minimieren",
|
||||
"Don't minimize to tray": "Nicht in Statusleiste minimieren",
|
||||
"Close on exit": "Beim Beenden schließen",
|
||||
"Settings saved!": "Einstellungen gespeichert!",
|
||||
"Available only in Electron version!": "Nur in der Electron-Version verfügbar!"
|
||||
}
|
112
app/client/src/locales/el.json
Normal file
112
app/client/src/locales/el.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/en.json
Normal file
112
app/client/src/locales/en.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/es.json
Normal file
112
app/client/src/locales/es.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/fa.json
Normal file
112
app/client/src/locales/fa.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/fil.json
Normal file
112
app/client/src/locales/fil.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/fr.json
Normal file
112
app/client/src/locales/fr.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/he.json
Normal file
112
app/client/src/locales/he.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/hi.json
Normal file
112
app/client/src/locales/hi.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/hr.json
Normal file
112
app/client/src/locales/hr.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/hu.json
Normal file
112
app/client/src/locales/hu.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/id.json
Normal file
112
app/client/src/locales/id.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/it.json
Normal file
112
app/client/src/locales/it.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/ko.json
Normal file
112
app/client/src/locales/ko.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/pl.json
Normal file
112
app/client/src/locales/pl.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/pt.json
Normal file
112
app/client/src/locales/pt.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/ro.json
Normal file
112
app/client/src/locales/ro.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/ru.json
Normal file
112
app/client/src/locales/ru.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/tr.json
Normal file
112
app/client/src/locales/tr.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/uk.json
Normal file
112
app/client/src/locales/uk.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
112
app/client/src/locales/ur.json
Normal file
112
app/client/src/locales/ur.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Browse": "Browse",
|
||||
"Library": "Library",
|
||||
"Tracks": "Tracks",
|
||||
"Playlists": "Playlists",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Artists",
|
||||
"More": "More",
|
||||
"Settings": "Settings",
|
||||
"Downloads": "Downloads",
|
||||
"Search or paste Deezer URL. Use / to quickly focus.": "Search or paste Deezer URL. Use \"/\" to quickly focus.",
|
||||
"Play": "Play",
|
||||
"Add to library": "Add to library",
|
||||
"Download": "Download",
|
||||
"fans": "fans",
|
||||
"tracks": "tracks",
|
||||
"Quality": "Quality",
|
||||
"Estimated size:": "Estimated size:",
|
||||
"Start downloading": "Start downloading",
|
||||
"Cancel": "Cancel",
|
||||
"Stream logging is disabled!": "Stream logging is disabled!",
|
||||
"Enable it in settings for history to work properly.": "Enable it in settings for history to work properly.",
|
||||
"History": "History",
|
||||
"Create new playlist": "Create new playlist",
|
||||
"TRACKS": "TRACKS",
|
||||
"Sort by": "Sort by",
|
||||
"Date Added": "Date Added",
|
||||
"Name (A-Z)": "Name (A-Z)",
|
||||
"Artist (A-Z)": "Artist (A-Z)",
|
||||
"Album (A-Z)": "Album (A-Z)",
|
||||
"Error loading lyrics or lyrics not found!": "Error loading lyrics or lyrics not found!",
|
||||
"Create playlist": "Create playlist",
|
||||
"Create": "Create",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Create new": "Create new",
|
||||
"Remove": "Remove",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Remove from library": "Remove from library",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Play track mix": "Play track mix",
|
||||
"Go to": "Go to",
|
||||
"Track Mix": "Track Mix",
|
||||
"Duration": "Duration",
|
||||
"Released": "Released",
|
||||
"Disk": "Disk",
|
||||
"albums": "albums",
|
||||
"Play top": "Play top",
|
||||
"Radio": "Radio",
|
||||
"Show all albums": "Show all albums",
|
||||
"Show all singles": "Show all singles",
|
||||
"Show more": "Show more",
|
||||
"Downloaded": "Downloaded",
|
||||
"Queue": "Queue",
|
||||
"Total": "Total",
|
||||
"Stop": "Stop",
|
||||
"Start": "Start",
|
||||
"Show folder": "Show folder",
|
||||
"Clear queue": "Clear queue",
|
||||
"Playing from": "Playing from",
|
||||
"Info": "Info",
|
||||
"Lyrics": "Lyrics",
|
||||
"Track number": "Track number",
|
||||
"Disk number": "Disk number",
|
||||
"Explicit": "Explicit",
|
||||
"Source": "Source",
|
||||
"ID": "ID",
|
||||
"Error logging in!": "Error logging in!",
|
||||
"Please try again later, or try another account.": "Please try again later, or try another account.",
|
||||
"Logout": "Logout",
|
||||
"Login using browser": "Login using browser",
|
||||
"Please login using your Deezer account:": "Please login using your Deezer account:",
|
||||
"...or paste your ARL/Token below:": "...or paste your ARL/Token below:",
|
||||
"ARL/Token": "ARL/Token",
|
||||
"Login": "Login",
|
||||
"By using this program, you disagree with Deezer's ToS.": "By using this program, you disagree with Deezer's ToS.",
|
||||
"Only in Electron version!": "Only in Electron version!",
|
||||
"Search results for:": "Search results for:",
|
||||
"Error loading data!": "Error loading data!",
|
||||
"Try again later!": "Try again later!",
|
||||
"Search": "Search",
|
||||
"Streaming Quality": "Streaming Quality",
|
||||
"Download Quality": "Download Quality",
|
||||
"Downloads Directory": "Downloads Directory",
|
||||
"Simultaneous downloads": "Simultaneous downloads",
|
||||
"Always show download confirm dialog before downloading.": "Always show download confirm dialog before downloading.",
|
||||
"Show download dialog": "Show download dialog",
|
||||
"Create folders for artists": "Create folders for artists",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Download lyrics": "Download lyrics",
|
||||
"Variables": "Variables",
|
||||
"UI": "UI",
|
||||
"Show autocomplete in search": "Show autocomplete in search",
|
||||
"Integrations": "Integrations",
|
||||
"This allows listening history, flow and recommendations to work properly.": "This allows listening history, flow and recommendations to work properly.",
|
||||
"Log track listens to Deezer": "Log track listens to Deezer",
|
||||
"Connect your LastFM account to allow scrobbling.": "Connect your LastFM account to allow scrobbling.",
|
||||
"Login with LastFM": "Login with LastFM",
|
||||
"Disconnect LastFM": "Disconnect LastFM",
|
||||
"Requires restart to apply!": "Requires restart to apply!",
|
||||
"Enable Discord Rich Presence, requires restart to toggle!": "Enable Discord Rich Presence, requires restart to toggle!",
|
||||
"Discord Rich Presence": "Discord Rich Presence",
|
||||
"Enable Discord join button for syncing tracks, requires restart to toggle!": "Enable Discord join button for syncing tracks, requires restart to toggle!",
|
||||
"Discord Join Button": "Discord Join Button",
|
||||
"Other": "Other",
|
||||
"Minimize to tray": "Minimize to tray",
|
||||
"Don't minimize to tray": "Don't minimize to tray",
|
||||
"Close on exit": "Close on exit",
|
||||
"Settings saved!": "Settings saved!",
|
||||
"Available only in Electron version!": "Available only in Electron version!"
|
||||
}
|
@ -5,6 +5,7 @@ import vuetify from './js/vuetify';
|
||||
import axios from 'axios';
|
||||
import VueEsc from 'vue-esc';
|
||||
import VueSocketIO from 'vue-socket.io';
|
||||
import i18n from './js/i18n';
|
||||
|
||||
//Globals
|
||||
let ipcRenderer;
|
||||
@ -52,7 +53,8 @@ Vue.prototype.$filesize = (bytes) => {
|
||||
|
||||
//Sockets
|
||||
Vue.use(new VueSocketIO({
|
||||
connection: window.location.origin
|
||||
connection: window.location.toString(),
|
||||
options: {path: '/socket'}
|
||||
}));
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
@ -66,10 +68,7 @@ new Vue({
|
||||
authorized: false,
|
||||
loadingPromise: null,
|
||||
|
||||
//Downloads
|
||||
downloading: false,
|
||||
downloads: [],
|
||||
download: null,
|
||||
downloads: {},
|
||||
|
||||
//Player
|
||||
track: null,
|
||||
@ -111,6 +110,7 @@ new Vue({
|
||||
//Used to prevent double listen logging
|
||||
logListenId: null
|
||||
},
|
||||
|
||||
methods: {
|
||||
// PLAYBACK METHODS
|
||||
isPlaying() {
|
||||
@ -199,7 +199,12 @@ new Vue({
|
||||
if (this.audio) this.audio.currentTime = 0;
|
||||
|
||||
//Load track meta
|
||||
this.playbackInfo = await this.loadPlaybackInfo(track.streamUrl, track.duration);
|
||||
let playbackInfo = await this.loadPlaybackInfo(track.streamUrl, track.duration);
|
||||
if (!playbackInfo) {
|
||||
this.skipNext();
|
||||
return;
|
||||
}
|
||||
this.playbackInfo = playbackInfo;
|
||||
|
||||
//Stream URL
|
||||
let url = `${window.location.origin}${this.playbackInfo.url}`;
|
||||
@ -290,6 +295,7 @@ new Vue({
|
||||
//Update media session with current track metadata
|
||||
updateMediaSession() {
|
||||
if (!this.track || !('mediaSession' in navigator)) return;
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
navigator.mediaSession.metadata = new MediaMetadata({
|
||||
title: this.track.title,
|
||||
@ -315,11 +321,25 @@ new Vue({
|
||||
//Get playback info
|
||||
let quality = this.settings.streamQuality;
|
||||
let infoUrl = `/streaminfo/${streamUrl}?q=${quality}`;
|
||||
let res = await this.$axios.get(infoUrl);
|
||||
let res;
|
||||
try {
|
||||
res = await this.$axios.get(infoUrl);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let info = res.data;
|
||||
//Calculate flac bitrate
|
||||
if (!info.quality.includes('kbps')) {
|
||||
info.quality = Math.round((parseInt(info.quality, 10)*8) / duration) + 'kbps';
|
||||
//Generate qualityString
|
||||
switch (info.quality) {
|
||||
case 9:
|
||||
info.qualityString = 'FLAC ' + Math.round((info.size*8) / duration) + 'kbps';
|
||||
break;
|
||||
case 3:
|
||||
info.qualityString = 'MP3 320kbps';
|
||||
break;
|
||||
case 1:
|
||||
info.qualityString = 'MP3 128kbps';
|
||||
break;
|
||||
}
|
||||
return info;
|
||||
},
|
||||
@ -341,6 +361,10 @@ new Vue({
|
||||
//Load meta
|
||||
this.gapless.track = this.queue.data[this.queue.index + 1];
|
||||
let info = await this.loadPlaybackInfo(this.gapless.track.streamUrl, this.gapless.track.duration);
|
||||
if (!info) {
|
||||
this.resetGapless();
|
||||
if (this.gapless.promise) resolve();
|
||||
}
|
||||
this.gapless.info = info
|
||||
this.gapless.audio = new Audio(`${window.location.origin}${info.url}`);
|
||||
|
||||
@ -382,12 +406,12 @@ new Vue({
|
||||
//Get downloads from server
|
||||
async getDownloads() {
|
||||
let res = await this.$axios.get('/downloads');
|
||||
this.downloading = res.data.downloading;
|
||||
this.downloads = res.data.downloads;
|
||||
if (res.data)
|
||||
this.downloads = res.data;
|
||||
},
|
||||
//Start stop downloading
|
||||
async toggleDownload() {
|
||||
if (this.downloading) {
|
||||
if (this.downloads.downloading) {
|
||||
await this.$axios.delete('/download');
|
||||
} else {
|
||||
await this.$axios.put('/download');
|
||||
@ -411,8 +435,12 @@ new Vue({
|
||||
//Send state update to integrations
|
||||
async updateState() {
|
||||
//Wait for duration
|
||||
if (this.state == 2 && (this.duration() == null || isNaN(this.duration())))
|
||||
await new Promise((res) => setTimeout(res, 1000));
|
||||
if (this.state == 2 && (this.duration() == null || isNaN(this.duration()))) {
|
||||
setTimeout(() => {
|
||||
this.updateState();
|
||||
}, 500);
|
||||
return;
|
||||
}
|
||||
this.$socket.emit('stateChange', {
|
||||
position: this.position,
|
||||
duration: this.duration(),
|
||||
@ -424,14 +452,20 @@ new Vue({
|
||||
if (this.settings.electron) {
|
||||
ipcRenderer.send('playing', this.state == 2);
|
||||
}
|
||||
},
|
||||
updateLanguage(l) {
|
||||
i18n.locale = l;
|
||||
}
|
||||
},
|
||||
|
||||
async created() {
|
||||
//Load settings, create promise so `/login` can await it
|
||||
let r;
|
||||
this.loadingPromise = new Promise((resolve) => r = resolve);
|
||||
let res = await this.$axios.get('/settings');
|
||||
this.settings = res.data;
|
||||
this.$vuetify.theme.themes.dark.primary = this.settings.primaryColor;
|
||||
i18n.locale = this.settings.language;
|
||||
this.volume = this.settings.volume;
|
||||
|
||||
//Restore playback data
|
||||
@ -480,17 +514,17 @@ new Vue({
|
||||
}
|
||||
|
||||
//Get downloads
|
||||
this.getDownloads();
|
||||
await this.getDownloads();
|
||||
|
||||
//Sockets
|
||||
|
||||
//Queue change
|
||||
this.sockets.subscribe('downloads', (data) => {
|
||||
this.downloading = data.downloading;
|
||||
this.downloads = data.downloads;
|
||||
this.downloads = data;
|
||||
});
|
||||
//Current download change
|
||||
this.sockets.subscribe('download', (data) => {
|
||||
this.download = data;
|
||||
this.sockets.subscribe('currentlyDownloading', (data) => {
|
||||
this.downloads.threads = data;
|
||||
});
|
||||
//Play at offset (for integrations)
|
||||
this.sockets.subscribe('playOffset', async (data) => {
|
||||
@ -501,6 +535,7 @@ new Vue({
|
||||
|
||||
r();
|
||||
},
|
||||
|
||||
mounted() {
|
||||
//Save settings on unload
|
||||
window.addEventListener('beforeunload', () => {
|
||||
@ -548,6 +583,7 @@ new Vue({
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
watch: {
|
||||
//Watch state for integrations
|
||||
state() {
|
||||
@ -558,5 +594,6 @@ new Vue({
|
||||
|
||||
router,
|
||||
vuetify,
|
||||
i18n,
|
||||
render: function (h) { return h(App) }
|
||||
}).$mount('#app');
|
||||
|
@ -16,24 +16,24 @@
|
||||
<h1>{{album.title}}</h1>
|
||||
<h3>{{album.artistString}}</h3>
|
||||
<div class='mt-2' v-if='!loading'>
|
||||
<span class='text-subtitle-2'>{{album.tracks.length}} tracks</span><br>
|
||||
<span class='text-subtitle-2'>Duration: {{duration}}</span><br>
|
||||
<span class='text-subtitle-2'>{{album.tracks.length}} {{$t("tracks")}}</span><br>
|
||||
<span class='text-subtitle-2'>{{$t("Duration")}}: {{duration}}</span><br>
|
||||
<span class='text-subtitle-2'>{{$numberString(album.fans)}} fans</span><br>
|
||||
<span class='text-subtitle-2'>Released: {{album.releaseDate}}</span><br>
|
||||
<span class='text-subtitle-2'>{{$t("Released")}}: {{album.releaseDate}}</span><br>
|
||||
</div>
|
||||
|
||||
<div class='my-2'>
|
||||
<v-btn color='primary' class='mx-1' @click='play'>
|
||||
<v-icon left>mdi-play</v-icon>
|
||||
Play
|
||||
{{$t("Play")}}
|
||||
</v-btn>
|
||||
<v-btn color='red' class='mx-1' @click='library' :loading='libraryLoading'>
|
||||
<v-icon left>mdi-heart</v-icon>
|
||||
Library
|
||||
{{$t("Library")}}
|
||||
</v-btn>
|
||||
<v-btn color='green' class='mx-1' @click='download'>
|
||||
<v-icon left>mdi-download</v-icon>
|
||||
Download
|
||||
{{$t("Download")}}
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
@ -48,7 +48,7 @@
|
||||
v-if='index == 0 || track.diskNumber != album.tracks[index-1].diskNumber'
|
||||
class='mx-4 text-subtitle-1'
|
||||
>
|
||||
Disk {{track.diskNumber}}
|
||||
{{$t("Disk")}} {{track.diskNumber}}
|
||||
</div>
|
||||
|
||||
<TrackTile :track='track' @click='playTrack(index)'></TrackTile>
|
||||
|
@ -16,18 +16,22 @@
|
||||
</v-overlay>
|
||||
<h1>{{artist.name}}</h1>
|
||||
<div class='mt-2' v-if='!loading'>
|
||||
<span class='text-subtitle-2'>{{artist.albumCount}} albums</span><br>
|
||||
<span class='text-subtitle-2'>{{$numberString(artist.fans)}} fans</span><br>
|
||||
<span class='text-subtitle-2'>{{artist.albumCount}} {{$t("albums")}}</span><br>
|
||||
<span class='text-subtitle-2'>{{$numberString(artist.fans)}} {{$t("fans")}}</span><br>
|
||||
</div>
|
||||
|
||||
<div class='my-2'>
|
||||
<v-btn color='primary' class='mx-1' @click='play'>
|
||||
<v-icon left>mdi-play</v-icon>
|
||||
Play top
|
||||
{{$t("Play top")}}
|
||||
</v-btn>
|
||||
<v-btn color='red' class='mx-1' @click='library' :loading='libraryLoading'>
|
||||
<v-icon left>mdi-heart</v-icon>
|
||||
Library
|
||||
{{$t("Library")}}
|
||||
</v-btn>
|
||||
<v-btn color='green' class='mx-1' @click='radio' v-if='artist.radio'>
|
||||
<v-icon left>mdi-radio</v-icon>
|
||||
{{$t("Radio")}}
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
@ -67,7 +71,7 @@
|
||||
|
||||
<!-- Show all albums -->
|
||||
<v-list-item v-if='!allAlbums && index == 3' @click='allAlbums = true'>
|
||||
<v-list-item-title>Show all albums</v-list-item-title>
|
||||
<v-list-item-title>{{$t("Show all albums")}}</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
</div>
|
||||
@ -91,7 +95,7 @@
|
||||
|
||||
<!-- Show all albums -->
|
||||
<v-list-item v-if='!allSingles && index == 3' @click='showAllSingles'>
|
||||
<v-list-item-title>Show all singles</v-list-item-title>
|
||||
<v-list-item-title>{{$t('Show all singles')}}</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
</div>
|
||||
@ -179,6 +183,19 @@ export default {
|
||||
this.allSingles = true;
|
||||
this.loadMoreAlbums();
|
||||
},
|
||||
async radio() {
|
||||
//Load
|
||||
let res = await this.$axios.get('/smartradio/' + this.artist.id);
|
||||
if (res.data) {
|
||||
this.$root.queue.source = {
|
||||
text: this.artist.name,
|
||||
source: 'radio',
|
||||
data: this.artist.id
|
||||
};
|
||||
this.$root.replaceQueue(res.data);
|
||||
this.$root.playIndex(0);
|
||||
}
|
||||
},
|
||||
//On scroll load more albums
|
||||
scroll(event) {
|
||||
if (!this.allAlbums && !this.allSingles) return;
|
||||
|
@ -23,7 +23,7 @@
|
||||
</div>
|
||||
<div v-if='section.hasMore' class='mx-2 align-center justify-center d-flex'>
|
||||
<v-btn @click='showMore(section)' color='primary'>
|
||||
Show more
|
||||
{{$t("Show more")}}
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,51 +1,51 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
<h1 class='pb-2'>Downloads</h1>
|
||||
|
||||
<v-card v-if='$root.download' max-width='100%'>
|
||||
|
||||
<v-list-item three-line>
|
||||
<v-list-item-avatar>
|
||||
<v-img :src='$root.download.track.albumArt.thumb'></v-img>
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{$root.download.track.title}}</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
Downloaded: {{$filesize($root.download.downloaded)}} / {{$filesize($root.download.size)}}<br>
|
||||
Path: {{$root.download.path}}
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
<h1 class='pb-2'>{{$t("Downloads")}}</h1>
|
||||
<div v-if='$root.downloads.downloading'>
|
||||
<v-card v-for='(download, index) in $root.downloads.threads' :key='"t" + index.toString()' max-width='100%'>
|
||||
<v-list-item>
|
||||
<v-list-item-avatar>
|
||||
<v-img :src='download.track.albumArt.thumb'></v-img>
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{download.track.title}}</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
{{$t('Downloaded')}}: {{$filesize(download.downloaded)}} / {{$filesize(download.size)}}<br>
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-card>
|
||||
|
||||
<h1 class='pb-2'>Queue:</h1>
|
||||
<div class='text-h6 mr-4 pb-2 d-flex'>Total: {{$root.downloads.length}}
|
||||
</v-card>
|
||||
</div>
|
||||
|
||||
<h1 class='pb-2'>{{$t("Queue")}}:</h1>
|
||||
<div class='text-h6 mr-4 pb-2 d-flex'>{{$t("Total")}}: {{$root.downloads.queue.length}}
|
||||
<v-btn @click='$root.toggleDownload' class='ml-4' color='primary'>
|
||||
<div v-if='$root.downloading'>
|
||||
<div v-if='$root.downloads.downloading'>
|
||||
<v-icon>mdi-stop</v-icon>
|
||||
Stop
|
||||
{{$t("Stop")}}
|
||||
</div>
|
||||
<div v-if='!$root.downloading'>
|
||||
<div v-if='!$root.downloads.downloading'>
|
||||
<v-icon>mdi-download</v-icon>
|
||||
Start
|
||||
{{$t("Start")}}
|
||||
</div>
|
||||
</v-btn>
|
||||
<!-- Open dir -->
|
||||
<v-btn @click='openDir' class='ml-4' v-if='$root.settings.electron'>
|
||||
<v-icon>mdi-folder</v-icon>
|
||||
Show folder
|
||||
{{$t("Show folder")}}
|
||||
</v-btn>
|
||||
<!-- Delete all -->
|
||||
<v-btn @click='deleteDownload(-1)' class='ml-4' color='red'>
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
Clear queue
|
||||
{{$t("Clear queue")}}
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<!-- Downloads -->
|
||||
<!-- Queue -->
|
||||
<v-list dense>
|
||||
<div v-for='(download, index) in $root.downloads' :key='download.id'>
|
||||
<div v-for='(download, index) in $root.downloads.queue' :key='download.id'>
|
||||
<v-list-item dense>
|
||||
<v-list-item-avatar>
|
||||
<v-img :src='download.track.albumArt.thumb'></v-img>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<v-btn icon @click='close'>
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
<v-toolbar-title>Playing from: {{$root.queue.source.text}}</v-toolbar-title>
|
||||
<v-toolbar-title>{{$t("Playing from")}}: {{$root.queue.source.text}}</v-toolbar-title>
|
||||
</v-app-bar>
|
||||
|
||||
<!-- Split to half -->
|
||||
@ -122,13 +122,13 @@
|
||||
<v-col class='col-6 pt-4'>
|
||||
<v-tabs v-model='tab'>
|
||||
<v-tab key='queue'>
|
||||
Queue
|
||||
{{$t("Queue")}}
|
||||
</v-tab>
|
||||
<v-tab key='info'>
|
||||
Info
|
||||
{{$t("Info")}}
|
||||
</v-tab>
|
||||
<v-tab key='lyrics'>
|
||||
Lyrics
|
||||
{{$t("Lyrics")}}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
|
||||
@ -172,14 +172,13 @@
|
||||
></ArtistTile>
|
||||
</v-list>
|
||||
<!-- Meta -->
|
||||
<h3>Duration: <span>{{$duration($root.track.duration)}}</span></h3>
|
||||
<h3>Track number: {{$root.track.trackNumber}}</h3>
|
||||
<h3>Disk number: {{$root.track.diskNumber}}</h3>
|
||||
<h3>Explicit: {{$root.track.explicit?"Yes":"No"}}</h3>
|
||||
<h3>Source: {{$root.playbackInfo.source}}</h3>
|
||||
<h3>Format: {{$root.playbackInfo.format}}</h3>
|
||||
<h3>Quality: {{$root.playbackInfo.quality}}</h3>
|
||||
<h3>ID: {{$root.track.id}}</h3>
|
||||
<h3>{{$t("Duration")}}: <span>{{$duration($root.track.duration)}}</span></h3>
|
||||
<h3>{{$t("Track number")}}: {{$root.track.trackNumber}}</h3>
|
||||
<h3>{{$t("Disk number")}}: {{$root.track.diskNumber}}</h3>
|
||||
<h3>{{$t("Explicit")}}: {{$root.track.explicit?"Yes":"No"}}</h3>
|
||||
<h3>{{$t("Source")}}: {{$root.playbackInfo.source}}</h3>
|
||||
<h3>{{$t("Quality")}}: {{$root.playbackInfo.qualityString}}</h3>
|
||||
<h3>{{$t("ID")}}: {{$root.track.id}}</h3>
|
||||
</v-list>
|
||||
</v-tab-item>
|
||||
<!-- Lyrics -->
|
||||
@ -261,7 +260,7 @@ export default {
|
||||
let offsetp = (v.pageX - seeker.$el.offsetLeft) / seeker.$el.clientWidth;
|
||||
let pos = offsetp * this.$root.duration();
|
||||
this.$root.seek(pos);
|
||||
this.position = pos;
|
||||
this.position = pos / 1000;
|
||||
this.seeking = false;
|
||||
},
|
||||
//Add/Remove track from library
|
||||
@ -305,7 +304,9 @@ export default {
|
||||
this.inLibrary = this.$root.libraryTracks.includes(this.$root.track.id);
|
||||
},
|
||||
'$root.position'() {
|
||||
if (!this.seeking) this.position = this.$root.position / 1000;
|
||||
if (!this.seeking) {
|
||||
this.position = this.$root.position / 1000;
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
@ -1,29 +1,29 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Library</h1>
|
||||
<h1>{{$t("Library")}}</h1>
|
||||
|
||||
<v-tabs v-model='tab'>
|
||||
<v-tab key='tracks'>
|
||||
Tracks
|
||||
{{$t("Tracks")}}
|
||||
</v-tab>
|
||||
<v-tab key='albums'>
|
||||
Albums
|
||||
{{$t("Albums")}}
|
||||
</v-tab>
|
||||
<v-tab key='artists'>
|
||||
Artists
|
||||
{{$t("Artists")}}
|
||||
</v-tab>
|
||||
<v-tab key='playlists'>
|
||||
Playlists
|
||||
{{$t("Playlists")}}
|
||||
</v-tab>
|
||||
<v-tab key='history'>
|
||||
History
|
||||
{{$t("History")}}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
|
||||
<v-tabs-items v-model='tab'>
|
||||
<!-- Tracks -->
|
||||
<v-tab-item key='tracks'>
|
||||
<LibraryTracks height='calc(100vh - 240px)'></LibraryTracks>
|
||||
<LibraryTracks height='calc(100vh - 290px)'></LibraryTracks>
|
||||
</v-tab-item>
|
||||
|
||||
<!-- Albums -->
|
||||
|
@ -9,11 +9,11 @@
|
||||
|
||||
<!-- Error -->
|
||||
<v-card class='text-center pa-4' v-if='error'>
|
||||
<h1 class='text--red'>Error logging in!</h1>
|
||||
<h3>Please try again later, or try another account.</h3>
|
||||
<h1 class='text--red'>{{$t("Error logging in!")}}</h1>
|
||||
<h3>{{$t("Please try again later, or try another account.")}}</h3>
|
||||
<v-btn large class='my-4' @click='logout'>
|
||||
<v-icon left>mdi-logout-variant</v-icon>
|
||||
Logout
|
||||
{{$t("Logout")}}
|
||||
</v-btn>
|
||||
</v-card>
|
||||
|
||||
@ -21,23 +21,24 @@
|
||||
<div v-if='showForm' class='text-center'>
|
||||
<v-img src='banner.png' contain max-width='400px' class='py-8'></v-img>
|
||||
|
||||
<h3>Please login using your Deezer account:</h3>
|
||||
<h3>{{$t("Please login using your Deezer account:")}}</h3>
|
||||
<v-btn large class='my-2 mb-4 primary' @click='browserLogin'>
|
||||
<v-icon left>mdi-open-in-app</v-icon>
|
||||
Login using browser
|
||||
{{$t("Login using browser")}}
|
||||
</v-btn>
|
||||
|
||||
<h3 class='mt-4'>...or paste your ARL/Token below:</h3>
|
||||
<v-text-field label='ARL/Token' v-model='arl'>
|
||||
<h3 class='mt-4'>{{$t("...or paste your ARL/Token below:")}}</h3>
|
||||
<v-text-field :label='$t("ARL/Token")' v-model='arl'>
|
||||
</v-text-field>
|
||||
|
||||
<v-btn large class='my-4 primary' :loading='authorizing' @click='login'>
|
||||
<v-icon left>mdi-login-variant</v-icon>Login
|
||||
<v-icon left>mdi-login-variant</v-icon>
|
||||
{{$t("Login")}}
|
||||
</v-btn>
|
||||
|
||||
<br>
|
||||
<span class='mt-8 text-caption'>
|
||||
By using this program, you disagree with Deezer's ToS.
|
||||
{{$t("By using this program, you disagree with Deezer's ToS.")}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -99,7 +100,7 @@ export default {
|
||||
},
|
||||
//Login using browser
|
||||
browserLogin() {
|
||||
if (!this.$root.settings.electron) return alert('Only in Electron version!');
|
||||
if (!this.$root.settings.electron) return alert(this.$t('Only in Electron version!'));
|
||||
|
||||
const {ipcRenderer} = window.require('electron');
|
||||
ipcRenderer.on('browserLogin', (event, newArl) => {
|
||||
|
@ -17,23 +17,23 @@
|
||||
<h3>{{playlist.user.name}}</h3>
|
||||
<h5>{{playlist.description}}</h5>
|
||||
<div class='mt-2' v-if='!loading'>
|
||||
<span class='text-subtitle-2'>{{playlist.trackCount}} tracks</span><br>
|
||||
<span class='text-subtitle-2'>Duration: {{$duration(playlist.duration)}}</span><br>
|
||||
<span class='text-subtitle-2'>{{$numberString(playlist.fans)}} fans</span><br>
|
||||
<span class='text-subtitle-2'>{{playlist.trackCount}} {{$t("tracks")}}</span><br>
|
||||
<span class='text-subtitle-2'>{{$t("Duration")}}: {{$duration(playlist.duration)}}</span><br>
|
||||
<span class='text-subtitle-2'>{{$numberString(playlist.fans)}} {{$t('fans')}}</span><br>
|
||||
</div>
|
||||
|
||||
<div class='my-1'>
|
||||
<v-btn color='primary' class='mr-1' @click='play'>
|
||||
<v-icon left>mdi-play</v-icon>
|
||||
Play
|
||||
{{$t('Play')}}
|
||||
</v-btn>
|
||||
<v-btn color='red' class='mx-1' @click='library' :loading='libraryLoading'>
|
||||
<v-icon left>mdi-heart</v-icon>
|
||||
Library
|
||||
{{$t('Library')}}
|
||||
</v-btn>
|
||||
<v-btn color='green' class='mx-1' @click='download'>
|
||||
<v-icon left>mdi-download</v-icon>
|
||||
Download
|
||||
{{$t('Download')}}
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
<h1 class='pb-2'>Search results for: "{{query}}"</h1>
|
||||
<h1 class='pb-2'>{{$t('Search results for:')}} "{{query}}"</h1>
|
||||
|
||||
<!-- Loading overlay -->
|
||||
<v-overlay opacity='0.9' :value='loading' z-index='3'>
|
||||
@ -12,24 +12,24 @@
|
||||
|
||||
<!-- Error overlay -->
|
||||
<v-overlay opacity='0.9' :value='error' z-index="3">
|
||||
<h1 class='red--text'>Error loading data!</h1><br>
|
||||
<h3>Try again later!</h3>
|
||||
<h1 class='red--text'>{{$t("Error loading data!")}}</h1><br>
|
||||
<h3>{{$t("Try again later!")}}</h3>
|
||||
</v-overlay>
|
||||
|
||||
<!-- Tabs -->
|
||||
<v-tabs v-model="tab">
|
||||
<v-tabs-slider></v-tabs-slider>
|
||||
<v-tab key="tracks">
|
||||
<v-icon left>mdi-music-note</v-icon>Tracks
|
||||
<v-icon left>mdi-music-note</v-icon>{{$t("Tracks")}}
|
||||
</v-tab>
|
||||
<v-tab>
|
||||
<v-icon left>mdi-album</v-icon>Albums
|
||||
<v-icon left>mdi-album</v-icon>{{$t("Albums")}}
|
||||
</v-tab>
|
||||
<v-tab>
|
||||
<v-icon left>mdi-account-music</v-icon>Artists
|
||||
<v-icon left>mdi-account-music</v-icon>{{$t("Artists")}}
|
||||
</v-tab>
|
||||
<v-tab>
|
||||
<v-icon left>mdi-playlist-music</v-icon>Playlists
|
||||
<v-icon left>mdi-playlist-music</v-icon>{{$t("Playlists")}}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
|
||||
@ -130,7 +130,7 @@ export default {
|
||||
//On click for track tile
|
||||
playTrack(i) {
|
||||
this.$root.queue.source = {
|
||||
text: "Search",
|
||||
text: this.$t("Search"),
|
||||
source: "search",
|
||||
data: this.query
|
||||
};
|
||||
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1 class='pb-2'>Settings</h1>
|
||||
<h1 class='pb-2'>{{$t('Settings')}}</h1>
|
||||
<v-list>
|
||||
<v-select
|
||||
class='px-4'
|
||||
label='Streaming Quality'
|
||||
:label='$t("Streaming Quality")'
|
||||
persistent-hint
|
||||
:items='qualities'
|
||||
@change='updateStreamingQuality'
|
||||
@ -13,7 +13,7 @@
|
||||
|
||||
<v-select
|
||||
class='px-4'
|
||||
label='Download Quality'
|
||||
:label='$t("Download Quality")'
|
||||
persistent-hint
|
||||
:items='qualities'
|
||||
@change='updateDownloadQuality'
|
||||
@ -23,11 +23,23 @@
|
||||
<!-- Download path -->
|
||||
<v-text-field
|
||||
class='px-4'
|
||||
label='Downloads Directory'
|
||||
:label='$t("Downloads Directory")'
|
||||
v-model='$root.settings.downloadsPath'
|
||||
append-icon='mdi-open-in-app'
|
||||
@click:append='selectDownloadPath'
|
||||
></v-text-field>
|
||||
|
||||
<!-- Download threads -->
|
||||
<v-slider
|
||||
:label='$t("Simultaneous downloads")'
|
||||
min='1'
|
||||
max='16'
|
||||
thumb-label
|
||||
step='1'
|
||||
ticks
|
||||
class='px-4'
|
||||
v-model='$root.settings.downloadThreads'
|
||||
></v-slider>
|
||||
|
||||
<!-- Download dialog -->
|
||||
<v-list-item>
|
||||
@ -35,8 +47,8 @@
|
||||
<v-checkbox v-model='$root.settings.downloadDialog'></v-checkbox>
|
||||
</v-list-item-action>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Show download dialog</v-list-item-title>
|
||||
<v-list-item-subtitle>Always show download confirm dialog before downloading.</v-list-item-subtitle>
|
||||
<v-list-item-title>{{$t("Show download dialog")}}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{$t("Always show download confirm dialog before downloading.")}}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
@ -46,7 +58,7 @@
|
||||
<v-checkbox v-model='$root.settings.createArtistFolder'></v-checkbox>
|
||||
</v-list-item-action>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Create folders for artists</v-list-item-title>
|
||||
<v-list-item-title>{{$t("Create folders for artists")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<!-- Create album folder -->
|
||||
@ -55,9 +67,19 @@
|
||||
<v-checkbox v-model='$root.settings.createAlbumFolder'></v-checkbox>
|
||||
</v-list-item-action>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Create folders for albums</v-list-item-title>
|
||||
<v-list-item-title>{{$t("Create folders for albums")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<!-- Download lyrics -->
|
||||
<v-list-item>
|
||||
<v-list-item-action>
|
||||
<v-checkbox v-model='$root.settings.downloadLyrics'></v-checkbox>
|
||||
</v-list-item-action>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{$t("Download lyrics")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
|
||||
<!-- Download naming -->
|
||||
<v-text-field
|
||||
@ -65,11 +87,34 @@
|
||||
label='Download Filename'
|
||||
persistent-hint
|
||||
v-model='$root.settings.downloadFilename'
|
||||
hint='Variables: %title%, %artists%, %artist%, %feats%, %trackNumber%, %0trackNumber%, %album%'
|
||||
:hint='$t("Variables") + ": %title%, %artists%, %artist%, %feats%, %trackNumber%, %0trackNumber%, %album%, %year%"'
|
||||
></v-text-field>
|
||||
|
||||
<!-- UI -->
|
||||
<v-subheader>{{$t("UI")}}</v-subheader>
|
||||
<v-divider></v-divider>
|
||||
|
||||
<!-- Language -->
|
||||
<v-select
|
||||
class='mt-2 px-4'
|
||||
label='Language'
|
||||
persistent-hint
|
||||
:items='languageNames'
|
||||
@change='updateLanguage'
|
||||
></v-select>
|
||||
|
||||
<!-- Autocomplete -->
|
||||
<v-list-item>
|
||||
<v-list-item-action>
|
||||
<v-checkbox v-model='$root.settings.showAutocomplete'></v-checkbox>
|
||||
</v-list-item-action>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{$t("Show autocomplete in search")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
<!-- Accounts -->
|
||||
<v-subheader>Integrations</v-subheader>
|
||||
<v-subheader>{{$t("Integrations")}}</v-subheader>
|
||||
<v-divider></v-divider>
|
||||
|
||||
<!-- Log listening -->
|
||||
@ -78,8 +123,8 @@
|
||||
<v-checkbox v-model='$root.settings.logListen'></v-checkbox>
|
||||
</v-list-item-action>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Log track listens to Deezer</v-list-item-title>
|
||||
<v-list-item-subtitle>This allows listening history, flow and recommendations to work properly.</v-list-item-subtitle>
|
||||
<v-list-item-title>{{$t("Log track listens to Deezer")}}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{$t("This allows listening history, flow and recommendations to work properly.")}}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<!-- LastFM -->
|
||||
@ -88,8 +133,8 @@
|
||||
<v-img src='lastfm.svg'></v-img>
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Login with LastFM</v-list-item-title>
|
||||
<v-list-item-subtitle>Connect your LastFM account to allow scrobbling.</v-list-item-subtitle>
|
||||
<v-list-item-title>{{$t("Login with LastFM")}}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{$t("Connect your LastFM account to allow scrobbling.")}}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-list-item v-if='$root.settings.lastFM' @click='disconnectLastFM'>
|
||||
@ -97,32 +142,32 @@
|
||||
<v-icon>mdi-logout</v-icon>
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title class='red--text'>Disconnect LastFM</v-list-item-title>
|
||||
<v-list-item-title class='red--text'>{{$t("Disconnect LastFM")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<!-- Discord -->
|
||||
<v-list-item>
|
||||
<v-list-item-action>
|
||||
<v-checkbox v-model='$root.settings.enableDiscord' @click='snackbarText = "Requires restart to apply!"; snackbar = true'></v-checkbox>
|
||||
<v-checkbox v-model='$root.settings.enableDiscord' @click='snackbarText = $t("Requires restart to apply!"); snackbar = true'></v-checkbox>
|
||||
</v-list-item-action>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Discord Rich Presence</v-list-item-title>
|
||||
<v-list-item-subtitle>Enable Discord Rich Presence, requires restart to toggle!</v-list-item-subtitle>
|
||||
<v-list-item-title>{{$t("Discord Rich Presence")}}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{$t("Enable Discord Rich Presence, requires restart to toggle!")}}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<!-- Discord Join Button -->
|
||||
<v-list-item>
|
||||
<v-list-item-action>
|
||||
<v-checkbox v-model='$root.settings.discordJoin' @click='snackbarText = "Requires restart to apply!"; snackbar = true'></v-checkbox>
|
||||
<v-checkbox v-model='$root.settings.discordJoin' @click='snackbarText = $t("Requires restart to apply!"); snackbar = true'></v-checkbox>
|
||||
</v-list-item-action>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Discord Join Button</v-list-item-title>
|
||||
<v-list-item-subtitle>Enable Discord join button for syncing tracks, requires restart to toggle!</v-list-item-subtitle>
|
||||
<v-list-item-title>{{$t("Discord Join Button")}}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{$t("Enable Discord join button for syncing tracks, requires restart to toggle!")}}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
<!-- Misc -->
|
||||
<v-subheader>Other</v-subheader>
|
||||
<v-subheader>{{$t("Other")}}</v-subheader>
|
||||
<v-divider></v-divider>
|
||||
|
||||
<!-- Minimize to tray -->
|
||||
@ -131,7 +176,7 @@
|
||||
<v-checkbox v-model='$root.settings.minimizeToTray'></v-checkbox>
|
||||
</v-list-item-action>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Minimize to tray</v-list-item-title>
|
||||
<v-list-item-title>{{$t("Minimize to tray")}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<!-- Close on exit -->
|
||||
@ -140,22 +185,21 @@
|
||||
<v-checkbox v-model='$root.settings.closeOnExit'></v-checkbox>
|
||||
</v-list-item-action>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Close on exit</v-list-item-title>
|
||||
<v-list-item-subtitle>Don't minimize to tray</v-list-item-subtitle>
|
||||
<v-list-item-title>{{$t("Close on exit")}}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{$t("Don't minimize to tray")}}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
<!-- Logout -->
|
||||
<v-btn block color='red' class='mt-4' @click='logout'>
|
||||
<v-icon>mdi-logout</v-icon>
|
||||
Logout
|
||||
{{$t("Logout")}}
|
||||
</v-btn>
|
||||
|
||||
</v-list>
|
||||
|
||||
<v-btn class='my-4' large color='primary' :loading='saving' block @click='save'>
|
||||
<v-btn fab color='primary' absolute bottom right class='mb-12' @click='save' :loading='saving'>
|
||||
<v-icon>mdi-content-save</v-icon>
|
||||
Save
|
||||
</v-btn>
|
||||
|
||||
<!-- Info snackbar -->
|
||||
@ -192,7 +236,17 @@ export default {
|
||||
downloadQuality: null,
|
||||
devToolsCounter: 0,
|
||||
snackbarText: null,
|
||||
snackbar: false
|
||||
snackbar: false,
|
||||
language: 'en',
|
||||
languages: [
|
||||
{code: 'en', name: 'English'},
|
||||
{code: 'ar', name: 'Arabic'},
|
||||
{code: 'de', name: 'German'},
|
||||
],
|
||||
primaryColorIndex: 0,
|
||||
primaries: ['#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5', '#2196F3', '#03A9F4',
|
||||
'#00BCD4', '#009688', '#4CAF50', '#8BC34A', '#CDDC39', '#FFEB3B', '#FFC107', '#FF9800', '#FF5722',
|
||||
'#795548', '#607D8B', '#9E9E9E', '#333333', '#000000']
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -202,6 +256,8 @@ export default {
|
||||
this.$root.saveSettings();
|
||||
//Artificial wait to make it seem like something happened.
|
||||
setTimeout(() => {this.saving = false;}, 500);
|
||||
this.snackbarText = this.$t("Settings saved!");
|
||||
this.snackbar = true;
|
||||
},
|
||||
getQuality(v) {
|
||||
let i = this.qualities.indexOf(v);
|
||||
@ -228,7 +284,7 @@ export default {
|
||||
selectDownloadPath() {
|
||||
//Electron check
|
||||
if (!this.$root.settings.electron) {
|
||||
alert('Available only in Electron version!');
|
||||
alert(this.$t("Available only in Electron version!"));
|
||||
return;
|
||||
}
|
||||
const {ipcRenderer} = window.require('electron');
|
||||
@ -252,6 +308,24 @@ export default {
|
||||
this.$root.settings.lastFM = null;
|
||||
await this.$root.saveSettings();
|
||||
window.location.reload();
|
||||
},
|
||||
changeColor() {
|
||||
this.$vuetify.theme.themes.dark.primary = this.primaries[this.primaryColorIndex];
|
||||
this.$root.settings.primaryColor = this.primaries[this.primaryColorIndex];
|
||||
this.primaryColorIndex++;
|
||||
if (this.primaryColorIndex == this.primaries.length)
|
||||
this.primaryColorIndex = 0;
|
||||
},
|
||||
updateLanguage(l) {
|
||||
let code = this.languages.filter(lang => lang.name == l)[0].code;
|
||||
this.language = code;
|
||||
this.$root.updateLanguage(code);
|
||||
this.$root.settings.language = code;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
languageNames() {
|
||||
return this.languages.map(l => l.name);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -272,6 +346,18 @@ export default {
|
||||
remote.getCurrentWindow().toggleDevTools();
|
||||
}
|
||||
}
|
||||
|
||||
//Shhhhhh
|
||||
if (event.code == 'KeyC' && event.shiftKey) {
|
||||
this.changeColor();
|
||||
}
|
||||
|
||||
//SSHHSHSHHSH
|
||||
if (event.code == 'KeyG' && event.shiftKey && event.altKey) {
|
||||
setInterval(() => {
|
||||
this.changeColor();
|
||||
}, 400);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,14 @@
|
||||
module.exports = {
|
||||
"transpileDependencies": [
|
||||
"vuetify"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
pluginOptions: {
|
||||
i18n: {
|
||||
locale: 'en',
|
||||
fallbackLocale: 'en',
|
||||
localeDir: 'locales',
|
||||
enableInSFC: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
6
app/package-lock.json
generated
6
app/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "freezer",
|
||||
"version": "1.0.9",
|
||||
"version": "1.0.10",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -1505,8 +1505,8 @@
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
||||
},
|
||||
"nodeezcryptor": {
|
||||
"version": "git+https://notabug.org/xefglm/nodeezcryptor#26d049cba14fa1f5ee32a52f23f4eda05d9feeb4",
|
||||
"from": "git+https://notabug.org/xefglm/nodeezcryptor",
|
||||
"version": "git+https://codeberg.org/exttex/nodeezcryptor#78d99b64127256a1590d452b4804c4e38db24e97",
|
||||
"from": "git+https://codeberg.org/exttex/nodeezcryptor",
|
||||
"requires": {
|
||||
"bindings": "^1.5.0",
|
||||
"node-addon-api": "^2.0.0"
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "freezer",
|
||||
"private": true,
|
||||
"version": "1.0.9",
|
||||
"version": "1.1.0",
|
||||
"description": "",
|
||||
"main": "background.js",
|
||||
"scripts": {
|
||||
@ -17,7 +17,7 @@
|
||||
"lastfmapi": "^0.1.1",
|
||||
"metaflac-js2": "^1.0.7",
|
||||
"nedb": "^1.8.0",
|
||||
"nodeezcryptor": "git+https://notabug.org/xefglm/nodeezcryptor",
|
||||
"nodeezcryptor": "git+https://codeberg.org/exttex/nodeezcryptor",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"socket.io": "^2.3.0",
|
||||
"winston": "^3.3.3"
|
||||
|
@ -2,7 +2,8 @@ const crypto = require('crypto');
|
||||
const axios = require('axios');
|
||||
const decryptor = require('nodeezcryptor');
|
||||
const querystring = require('querystring');
|
||||
const {Transform} = require('stream');
|
||||
const https = require('https');
|
||||
const {Transform, Readable} = require('stream');
|
||||
const {Track} = require('./definitions');
|
||||
const logger = require('./winston');
|
||||
|
||||
@ -63,6 +64,11 @@ class DeezerAPI {
|
||||
}
|
||||
}
|
||||
|
||||
//Invalid CSRF
|
||||
if (data.data.error && data.data.error.VALID_TOKEN_REQUIRED) {
|
||||
await this.callApi('deezer.getUserData');
|
||||
return await this.callApi(method, args, gatewayInput);
|
||||
}
|
||||
|
||||
return data.data;
|
||||
}
|
||||
@ -118,6 +124,13 @@ class DeezerAPI {
|
||||
});
|
||||
|
||||
data = JSON.parse(data.toString('utf-8'));
|
||||
|
||||
//Invalid CSRF
|
||||
if (data.error && data.error.VALID_TOKEN_REQUIRED) {
|
||||
await this.callApi('deezer.getUserData');
|
||||
return await this.callApi(method, args, gatewayInput);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -131,6 +144,15 @@ class DeezerAPI {
|
||||
return true;
|
||||
}
|
||||
|
||||
async callPublicApi(path, params) {
|
||||
let res = await axios({
|
||||
url: `https://api.deezer.com/${encodeURIComponent(path)}/${encodeURIComponent(params)}`,
|
||||
responseType: 'json',
|
||||
method: 'GET'
|
||||
});
|
||||
return res.data;
|
||||
}
|
||||
|
||||
//Get track URL
|
||||
static getUrl(trackId, md5origin, mediaVersion, quality = 3) {
|
||||
const magic = Buffer.from([0xa4]);
|
||||
@ -165,46 +187,118 @@ class DeezerAPI {
|
||||
return `https://e-cdns-proxy-${md5origin.substring(0, 1)}.dzcdn.net/mobile/1/${step3}`;
|
||||
}
|
||||
|
||||
//Quality fallback
|
||||
async qualityFallback(info, quality = 3) {
|
||||
if (quality == 1) return {
|
||||
quality: '128kbps',
|
||||
format: 'MP3',
|
||||
source: 'stream',
|
||||
url: `/stream/${info}?q=1`
|
||||
};
|
||||
|
||||
async fallback(info, quality = 3) {
|
||||
let qualityInfo = Track.getUrlInfo(info);
|
||||
|
||||
//User uploaded MP3s
|
||||
if (qualityInfo.trackId.startsWith('-')) {
|
||||
qualityInfo.quality = 3;
|
||||
return qualityInfo;
|
||||
}
|
||||
|
||||
//Quality fallback
|
||||
let newQuality = await this.qualityFallback(qualityInfo, quality);
|
||||
if (newQuality != null) {
|
||||
return qualityInfo;
|
||||
}
|
||||
//ID Fallback
|
||||
let trackData = await this.callApi('deezer.pageTrack', {sng_id: qualityInfo.trackId});
|
||||
try {
|
||||
let tdata = Track.getUrlInfo(info);
|
||||
let res = await axios.head(DeezerAPI.getUrl(tdata.trackId, tdata.md5origin, tdata.mediaVersion, quality));
|
||||
if (quality == 3) {
|
||||
return {
|
||||
quality: '320kbps',
|
||||
format: 'MP3',
|
||||
source: 'stream',
|
||||
url: `/stream/${info}?q=3`
|
||||
}
|
||||
}
|
||||
//Bitrate will be calculated in client
|
||||
return {
|
||||
quality: res.headers['content-length'],
|
||||
format: 'FLAC',
|
||||
source: 'stream',
|
||||
url: `/stream/${info}?q=9`
|
||||
if (trackData.results.DATA.FALLBACK.SNG_ID.toString() != qualityInfo.trackId) {
|
||||
let newId = trackData.results.DATA.FALLBACK.SNG_ID.toString();
|
||||
let newTrackData = await this.callApi('deezer.pageTrack', {sng_id: newId});
|
||||
let newTrack = new Track(newTrackData.results.DATA);
|
||||
return this.fallback(newTrack.streamUrl);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn('Qualiy fallback: ' + e);
|
||||
logger.warn('TrackID Fallback failed: ' + e);
|
||||
}
|
||||
//ISRC Fallback
|
||||
try {
|
||||
let publicTrack = this.callPublicApi('track', 'isrc:' + trackData.results.DATA.ISRC);
|
||||
let newId = publicTrack.id.toString();
|
||||
let newTrackData = await this.callApi('deezer.pageTrack', {sng_id: newId});
|
||||
let newTrack = new Track(newTrackData.results.DATA);
|
||||
return this.fallback(newTrack.streamUrl);
|
||||
} catch (e) {
|
||||
logger.warn('ISRC Fallback failed: ' + e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//Fallback thru available qualities, -1 if none work
|
||||
async qualityFallback(info, quality = 3) {
|
||||
try {
|
||||
let res = await axios.head(DeezerAPI.getUrl(info.trackId, info.md5origin, info.mediaVersion, quality));
|
||||
if (res.status > 400) throw new Error(`Status code: ${res.status}`);
|
||||
//Make sure it's an int
|
||||
info.quality = parseInt(quality.toString(), 10);
|
||||
info.size = parseInt(res.headers['content-length'], 10);
|
||||
return info;
|
||||
} catch (e) {
|
||||
logger.warn('Quality fallback: ' + e);
|
||||
//Fallback
|
||||
//9 - FLAC
|
||||
//3 - MP3 320
|
||||
//1 - MP3 128
|
||||
let q = quality;
|
||||
if (quality == 9) q = 3;
|
||||
if (quality == 3) q = 1;
|
||||
return this.qualityFallback(info, q);
|
||||
let nq = -1;
|
||||
if (quality == 3) nq = 1;
|
||||
if (quality == 9) nq = 3;
|
||||
if (quality == 1) return null;
|
||||
return this.qualityFallback(info, nq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DeezerStream extends Readable {
|
||||
constructor(qualityInfo, options) {
|
||||
super(options);
|
||||
this.qualityInfo = qualityInfo;
|
||||
this.ended = false;
|
||||
}
|
||||
|
||||
|
||||
async open(offset, end) {
|
||||
//Prepare decryptor
|
||||
this.decryptor = new DeezerDecryptionStream(this.qualityInfo.trackId, {offset});
|
||||
this.decryptor.on('end', () => {
|
||||
this.ended = true;
|
||||
});
|
||||
|
||||
//Calculate headers
|
||||
let offsetBytes = offset - (offset % 2048);
|
||||
end = (end == -1) ? '' : end;
|
||||
let url = DeezerAPI.getUrl(this.qualityInfo.trackId, this.qualityInfo.md5origin, this.qualityInfo.mediaVersion, this.qualityInfo.quality);
|
||||
|
||||
//Open request
|
||||
await new Promise((res) => {
|
||||
this.request = https.get(url, {headers: {'Range': `bytes=${offsetBytes}-${end}`}}, (r) => {
|
||||
r.pipe(this.decryptor);
|
||||
this.size = parseInt(r.headers['content-length'], 10) + offsetBytes;
|
||||
res();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async _read() {
|
||||
//Decryptor ended
|
||||
if (this.ended)
|
||||
return this.push(null);
|
||||
|
||||
this.decryptor.once('readable', () => {
|
||||
this.push(this.decryptor.read());
|
||||
});
|
||||
}
|
||||
|
||||
_destroy(err, callback) {
|
||||
this.request.destroy();
|
||||
this.decryptor.destroy();
|
||||
callback();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DeezerDecryptionStream extends Transform {
|
||||
|
||||
constructor(trackId, options = {offset: 0}) {
|
||||
@ -258,4 +352,4 @@ class DeezerDecryptionStream extends Transform {
|
||||
}
|
||||
|
||||
|
||||
module.exports = {DeezerAPI, DeezerDecryptionStream};
|
||||
module.exports = {DeezerAPI, DeezerDecryptionStream, DeezerStream};
|
@ -1,3 +1,4 @@
|
||||
|
||||
//Datatypes, constructor parameters = gw_light API call.
|
||||
class Track {
|
||||
constructor(json) {
|
||||
@ -11,8 +12,8 @@ class Track {
|
||||
this.artistString = this.artists.map((a) => a.name).join(', ');
|
||||
|
||||
this.album = new Album(json);
|
||||
this.trackNumber = parseInt((json.TRACK_NUMBER || 0).toString(), 10);
|
||||
this.diskNumber = parseInt((json.DISK_NUMBER || 0).toString(), 10);
|
||||
this.trackNumber = parseInt((json.TRACK_NUMBER || 1).toString(), 10);
|
||||
this.diskNumber = parseInt((json.DISK_NUMBER || 1).toString(), 10);
|
||||
this.explicit = json['EXPLICIT_LYRICS'] == 1 ? true:false;
|
||||
this.lyricsId = json.LYRICS_ID;
|
||||
|
||||
@ -35,7 +36,7 @@ class Track {
|
||||
if (info.charAt(32) == '1') md5origin += '.mp3';
|
||||
let mediaVersion = parseInt(info.substring(33, 34)).toString();
|
||||
let trackId = info.substring(35);
|
||||
return {trackId, md5origin, mediaVersion};
|
||||
return new QualityInfo(md5origin, mediaVersion, trackId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,6 +77,7 @@ class Artist {
|
||||
this.albumCount = albumsJson.total;
|
||||
this.albums = albumsJson.data.map((a) => new Album(a));
|
||||
this.topTracks = topJson.data.map((t) => new Track(t));
|
||||
this.radio = json.SMARTRADIO;
|
||||
}
|
||||
}
|
||||
|
||||
@ -277,5 +279,27 @@ class Lyric {
|
||||
}
|
||||
}
|
||||
|
||||
class QualityInfo {
|
||||
constructor(md5origin, mediaVersion, trackId, quality = 1, source='stream') {
|
||||
this.md5origin = md5origin;
|
||||
this.mediaVersion = mediaVersion;
|
||||
this.trackId = trackId;
|
||||
this.quality = quality;
|
||||
this.source = source;
|
||||
//For FLAC bitrate calculation
|
||||
this.size = 1;
|
||||
|
||||
this.url = '';
|
||||
}
|
||||
|
||||
//Generate direct stream URL
|
||||
generateUrl() {
|
||||
let md5 = this.md5origin.replace('.mp3', '');
|
||||
let md5mp3bit = this.md5origin.includes('.mp3') ? '1' : '0';
|
||||
let mv = this.mediaVersion.toString().padStart(2, '0');
|
||||
this.url = `/stream/${md5}${md5mp3bit}${mv}${this.trackId}?q=${this.quality}`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {Track, Album, Artist, Playlist, User, SearchResults,
|
||||
DeezerImage, DeezerProfile, DeezerLibrary, DeezerPage, Lyrics};
|
@ -1,158 +1,46 @@
|
||||
const {Settings} = require('./settings');
|
||||
const {Track} = require('./definitions');
|
||||
const decryptor = require('nodeezcryptor');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const logger = require('./winston');
|
||||
const https = require('https');
|
||||
const {DeezerAPI} = require('./deezer');
|
||||
const Datastore = require('nedb');
|
||||
const {Settings} = require('./settings');
|
||||
const fs = require('fs');
|
||||
const https = require('https');
|
||||
const logger = require('./winston');
|
||||
const path = require('path');
|
||||
const decryptor = require('nodeezcryptor');
|
||||
const sanitize = require('sanitize-filename');
|
||||
const ID3Writer = require('browser-id3-writer');
|
||||
const Metaflac = require('metaflac-js2');
|
||||
const sanitize = require("sanitize-filename");
|
||||
const { DeezerAPI } = require('./deezer');
|
||||
const { Track, Lyrics } = require('./definitions');
|
||||
|
||||
class Downloads {
|
||||
constructor(settings, qucb) {
|
||||
this.downloads = [];
|
||||
this.downloading = false;
|
||||
this.download;
|
||||
let deezer;
|
||||
|
||||
class DownloadManager {
|
||||
|
||||
constructor(settings, callback) {
|
||||
this.settings = settings;
|
||||
//Queue update callback
|
||||
this.qucb = qucb;
|
||||
}
|
||||
|
||||
//Add track to queue
|
||||
async add(track, quality = null) {
|
||||
if (this.downloads.filter((e => e.id == track.id)).length > 0) {
|
||||
//Track already in queue
|
||||
return;
|
||||
}
|
||||
|
||||
//Sanitize quality
|
||||
let q = this.settings.downloadsQuality;
|
||||
if (quality) q = parseInt(quality.toString(), 10);
|
||||
|
||||
//Create download
|
||||
let outpath = this.generateTrackPath(track, q);
|
||||
let d = new Download(
|
||||
track,
|
||||
outpath,
|
||||
q,
|
||||
() => {this._downloadDone();}
|
||||
);
|
||||
this.downloads.push(d);
|
||||
|
||||
//Update callback
|
||||
if (this.qucb) this.qucb();
|
||||
|
||||
//Save to DB
|
||||
await new Promise((res, rej) => {
|
||||
this.db.insert(d.toDB(), (e) => {
|
||||
res();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
generateTrackPath(track, quality) {
|
||||
//Generate filename
|
||||
let fn = this.settings.downloadFilename;
|
||||
|
||||
//Disable feats for single artist
|
||||
let feats = '';
|
||||
if (track.artists.length >= 2) feats = track.artists.slice(1).map((a) => a.name).join(', ');
|
||||
|
||||
let props = {
|
||||
'%title%': track.title,
|
||||
'%artists%': track.artistString,
|
||||
'%artist%': track.artists[0].name,
|
||||
'%feats%': feats,
|
||||
'%trackNumber%': (track.trackNumber ? track.trackNumber : 1).toString(),
|
||||
'%0trackNumber%': (track.trackNumber ? track.trackNumber : 1).toString().padStart(2, '0'),
|
||||
'%album%': track.album.title
|
||||
};
|
||||
for (let k of Object.keys(props)) {
|
||||
fn = fn.replace(new RegExp(k, 'g'), sanitize(props[k]));
|
||||
}
|
||||
//Generate folders
|
||||
let p = this.settings.downloadsPath;
|
||||
if (this.settings.createArtistFolder) p = path.join(p, sanitize(track.artists[0].name));
|
||||
if (this.settings.createAlbumFolder) p = path.join(p, sanitize(track.album.title));
|
||||
|
||||
return path.join(p, fn);
|
||||
}
|
||||
|
||||
async start() {
|
||||
//Already downloading
|
||||
if (this.download || this.downloads.length == 0) return;
|
||||
|
||||
this.downloading = true;
|
||||
await this._downloadDone();
|
||||
}
|
||||
|
||||
async stop() {
|
||||
//Not downloading
|
||||
if (!this.download || !this.downloading) return;
|
||||
this.downloading = false;
|
||||
await this.download.stop();
|
||||
this.callback = callback;
|
||||
|
||||
//Back to queue if undone
|
||||
if (this.download.state < 3) this.downloads.unshift(this.download);
|
||||
|
||||
this.download = null;
|
||||
this.queue = [];
|
||||
this.threads = [];
|
||||
|
||||
//Update callback
|
||||
if (this.qucb) this.qucb();
|
||||
this.updateRequests = 0;
|
||||
}
|
||||
|
||||
//On download finished
|
||||
async _downloadDone() {
|
||||
//Save to DB
|
||||
if (this.download) {
|
||||
await new Promise((res, rej) => {
|
||||
this.db.update({_id: this.download.id}, {
|
||||
state: this.download.state,
|
||||
fallback: this.download.fallback,
|
||||
}, (e) => {
|
||||
res();
|
||||
});
|
||||
// this.db.remove({_id: this.download.id}, (e) => {
|
||||
// res();
|
||||
// });
|
||||
});
|
||||
}
|
||||
|
||||
this.download = null;
|
||||
|
||||
//All downloads done
|
||||
if (this.downloads.length == 0 || this.downloading == false) {
|
||||
this.downloading = false;
|
||||
if (this.qucb) this.qucb();
|
||||
return;
|
||||
}
|
||||
|
||||
this.download = this.downloads[0];
|
||||
this.downloads = this.downloads.slice(1);
|
||||
this.download.start();
|
||||
|
||||
//Update callback
|
||||
if (this.qucb) this.qucb();
|
||||
//Update DeezerAPI global
|
||||
setDeezer(d) {
|
||||
deezer = d;
|
||||
}
|
||||
|
||||
//Load downloads info
|
||||
async load() {
|
||||
this.db = new Datastore({filename: Settings.getDownloadsDB(), autoload: true});
|
||||
//Load downloads
|
||||
await new Promise((res, rej) => {
|
||||
this.db.find({}, (err, docs) => {
|
||||
if (err) return rej();
|
||||
if (!docs) return;
|
||||
|
||||
for (let d of docs) {
|
||||
if (d.state < 3 && d.state >= 0) this.downloads.push(Download.fromDB(d, () => {this._downloadDone();}));
|
||||
//TODO: Ignore for now completed
|
||||
//Load from DB
|
||||
await new Promise((resolve) => {
|
||||
this.db.find({state: 0}, (err, docs) => {
|
||||
if (!err) {
|
||||
this.queue = docs.map(d => Download.fromDB(d));
|
||||
}
|
||||
res();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
@ -162,138 +50,211 @@ class Downloads {
|
||||
}
|
||||
}
|
||||
|
||||
//Remove download
|
||||
async delete(index) {
|
||||
//Clear all
|
||||
if (index == -1) {
|
||||
this.downloads = [];
|
||||
await new Promise((res, rej) => {
|
||||
this.db.remove({state: 0}, {multi: true}, (e) => {});
|
||||
res();
|
||||
});
|
||||
async start() {
|
||||
this.downloading = true;
|
||||
await this.updateQueue();
|
||||
}
|
||||
|
||||
if (this.qucb) this.qucb();
|
||||
async stop() {
|
||||
this.downloading = false;
|
||||
//Stop all threads
|
||||
let nThreads = this.threads.length;
|
||||
for (let i=nThreads-1; i>=0; i--) {
|
||||
await this.threads[i].stop();
|
||||
}
|
||||
this.updateQueue();
|
||||
}
|
||||
|
||||
async add(track, quality) {
|
||||
//Sanitize quality
|
||||
let q = this.settings.downloadsQuality;
|
||||
if (quality)
|
||||
q = parseInt(quality.toString(), 10);
|
||||
let download = new Download(track, q, 0);
|
||||
|
||||
//Check if in queue
|
||||
if (this.queue.some(d => d.track.id == track.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Remove single
|
||||
if (index >= this.downloads.length) return;
|
||||
await new Promise((res, rej) => {
|
||||
this.db.remove({_id: this.downloads[index].id}, {}, (e) => {});
|
||||
res();
|
||||
});
|
||||
this.downloads.splice(index, 1);
|
||||
//Check if in DB
|
||||
let dbDownload = await new Promise((resolve) => {
|
||||
this.db.find({_id: download.track.id}, (err, docs) => {
|
||||
if (err) return resolve(null);
|
||||
if (docs.length == 0) return resolve(null);
|
||||
|
||||
if (this.qucb) this.qucb();
|
||||
//Update download as not done, will be skipped while downloading
|
||||
this.db.update({_id: download.track.id}, {state: 0, quality: download.quality}, {}, () => {
|
||||
resolve(Download.fromDB(docs[0]));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
//Insert to DB
|
||||
if (!dbDownload) {
|
||||
await new Promise((resolve) => {
|
||||
this.db.insert(download.toDB(), () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//Queue
|
||||
this.queue.push(download);
|
||||
this.updateQueue();
|
||||
}
|
||||
|
||||
async delete(index) {
|
||||
//-1 = Delete all
|
||||
if (index == -1) {
|
||||
let ids = this.queue.map(q => q.track.id);
|
||||
this.queue = [];
|
||||
//Remove from DB
|
||||
await new Promise((res) => {
|
||||
this.db.remove({_id: {$in: ids}}, {multi: true}, () => {
|
||||
res();
|
||||
})
|
||||
});
|
||||
this.updateQueue();
|
||||
return;
|
||||
}
|
||||
|
||||
//Remove single item
|
||||
let id = this.queue[index].track.id;
|
||||
this.queue.splice(index, 1);
|
||||
await new Promise((res) => {
|
||||
this.db.remove({_id: id}, {}, () => {
|
||||
res();
|
||||
})
|
||||
})
|
||||
this.updateQueue();
|
||||
}
|
||||
|
||||
//Thread safe update
|
||||
async updateQueue() {
|
||||
this.updateRequests++;
|
||||
if (this._updatePromise) return;
|
||||
this._updatePromise = this._updateQueue();
|
||||
await this._updatePromise;
|
||||
this._updatePromise = null;
|
||||
this.updateRequests--;
|
||||
if (this.updateRequests > 0) {
|
||||
this.updateRequests--;
|
||||
this.updateQueue();
|
||||
}
|
||||
}
|
||||
|
||||
async _updateQueue() {
|
||||
//Finished downloads
|
||||
if (this.threads.length > 0) {
|
||||
for (let i=this.threads.length-1; i>=0; i--) {
|
||||
if (this.threads[i].download.state == 3 || this.threads[i].download.state == -1) {
|
||||
//Update DB
|
||||
await new Promise((resolve) => {
|
||||
this.db.update({_id: this.threads[i].download.track.id}, {state: this.threads[i].download.state}, {}, () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
this.threads.splice(i, 1);
|
||||
} else {
|
||||
//Remove if stopped
|
||||
if (this.threads[i].stopped) {
|
||||
this.queue.unshift(this.threads[i].download);
|
||||
this.threads.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//Create new threads
|
||||
if (this.downloading) {
|
||||
let nThreads = this.settings.downloadThreads - this.threads.length;
|
||||
for (let i=0; i<nThreads; i++) {
|
||||
if (this.queue.length > 0) {
|
||||
let thread = new DownloadThread(this.queue[0], () => {this.updateQueue();}, this.settings);
|
||||
thread.start();
|
||||
this.threads.push(thread);
|
||||
this.queue.splice(0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
//Stop downloading if queues empty
|
||||
if (this.queue.length == 0 && this.threads.length == 0 && this.downloading)
|
||||
this.downloading = false;
|
||||
|
||||
//Update UI
|
||||
if (this.callback)
|
||||
this.callback();
|
||||
}
|
||||
}
|
||||
|
||||
class Download {
|
||||
constructor(track, path, quality, onDone) {
|
||||
this.track = track;
|
||||
this.id = track.id;
|
||||
this.path = path;
|
||||
this.quality = quality;
|
||||
this.onDone = onDone;
|
||||
|
||||
//States:
|
||||
//0 - none/stopped
|
||||
//1 - downloading
|
||||
//2 - post-processing
|
||||
//3 - done
|
||||
//-1 - download error
|
||||
this.state = 0;
|
||||
this.fallback = false;
|
||||
|
||||
this._request;
|
||||
//Post Processing Promise
|
||||
this._ppp;
|
||||
|
||||
this.downloaded = 0;
|
||||
this.size = 0;
|
||||
class DownloadThread {
|
||||
constructor (download, callback, settings) {
|
||||
this.download = download;
|
||||
this.callback = callback;
|
||||
this.settings = settings;
|
||||
this.stopped = true;
|
||||
this.isUserUploaded = download.track.id.toString().startsWith('-');
|
||||
}
|
||||
|
||||
//Serialize to database json
|
||||
toDB() {
|
||||
return {
|
||||
_id: this.id,
|
||||
path: this.path,
|
||||
quality: this.quality,
|
||||
track: this.track,
|
||||
state: this.state,
|
||||
fallback: this.fallback
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Create download from DB document
|
||||
static fromDB(doc, onDone) {
|
||||
let d = new Download(doc.track, doc.path, doc.quality, onDone);
|
||||
d.fallback = doc.fallback ? true : false; //Null check
|
||||
d.state = doc.state;
|
||||
return d;
|
||||
//Callback wrapper
|
||||
_cb() {
|
||||
if (this.callback) this.callback();
|
||||
}
|
||||
|
||||
async start() {
|
||||
this.state = 1;
|
||||
this.download.state = 1;
|
||||
this.stopped = false;
|
||||
|
||||
//Fallback
|
||||
this.qualityInfo = await deezer.fallback(this.download.track.streamUrl, this.download.quality);
|
||||
if (!this.qualityInfo) {
|
||||
this.download.state = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
//Get track info
|
||||
if (!this.isUserUploaded) {
|
||||
this.rawTrack = await deezer.callApi('deezer.pageTrack', {'sng_id': this.download.track.id});
|
||||
this.track = new Track(this.rawTrack.results.DATA);
|
||||
this.publicTrack = await deezer.callPublicApi('track', this.track.id);
|
||||
this.publicAlbum = await deezer.callPublicApi('album', this.track.album.id);
|
||||
}
|
||||
|
||||
//Check if exists
|
||||
let outPath = this.generatePath(this.qualityInfo.quality);
|
||||
try {
|
||||
await fs.promises.access(outPath, fs.constants.R_OK);
|
||||
//File exists
|
||||
this.download.state = 3;
|
||||
return this._cb();
|
||||
} catch (_) {}
|
||||
|
||||
//Path to temp file
|
||||
let tmp = path.join(Settings.getTempDownloads(), `${this.track.id}.ENC`);
|
||||
let tmp = path.join(Settings.getTempDownloads(), `${this.download.track.id}.ENC`);
|
||||
//Get start offset
|
||||
let start = 0;
|
||||
try {
|
||||
let stat = await fs.promises.stat(tmp);
|
||||
if (stat.size) start = stat.size;
|
||||
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (e) {}
|
||||
this.downloaded = start;
|
||||
this.download.downloaded = start;
|
||||
|
||||
//Get download info
|
||||
let streamInfo = Track.getUrlInfo(this.track.streamUrl);
|
||||
this.url = DeezerAPI.getUrl(streamInfo.trackId, streamInfo.md5origin, streamInfo.mediaVersion, this.quality);
|
||||
this._request = https.get(this.url, {headers: {'Range': `bytes=${start}-`}}, (r) => {
|
||||
//Download
|
||||
let url = DeezerAPI.getUrl(this.qualityInfo.trackId, this.qualityInfo.md5origin, this.qualityInfo.mediaVersion, this.qualityInfo.quality);
|
||||
if (this.stopped) return;
|
||||
this._request = https.get(url, {headers: {'Range': `bytes=${start}-`}}, (r) => {
|
||||
this._response = r;
|
||||
let outFile = fs.createWriteStream(tmp, {flags: 'a'});
|
||||
let skip = false;
|
||||
//Error
|
||||
if (r.statusCode >= 400) {
|
||||
//Fallback on error
|
||||
if (this.quality > 1) {
|
||||
if (this.quality == 3) this.quality = 1;
|
||||
if (this.quality == 9) this.quality = 3;
|
||||
this.url = null;
|
||||
this.fallback = true;
|
||||
return this.start();
|
||||
};
|
||||
//Error
|
||||
this.state = -1;
|
||||
logger.error(`Undownloadable track ID: ${this.track.id}`);
|
||||
return this.onDone();
|
||||
} else {
|
||||
this.path += (this.quality == 9) ? '.flac' : '.mp3';
|
||||
|
||||
//Check if file exits
|
||||
fs.access(this.path, (err) => {
|
||||
if (err) {
|
||||
|
||||
} else {
|
||||
logger.warn('File already exists! Skipping...');
|
||||
outFile.close();
|
||||
skip = true;
|
||||
this._request.end();
|
||||
this.state = 3;
|
||||
return this.onDone();
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
//On download done
|
||||
r.on('end', () => {
|
||||
if (skip) return;
|
||||
if (this.downloaded != this.size) return;
|
||||
if (this.download.size != this.download.downloaded) return;
|
||||
|
||||
outFile.on('finish', () => {
|
||||
outFile.close(() => {
|
||||
this._finished(tmp);
|
||||
this.postPromise = this._post(tmp);
|
||||
});
|
||||
});
|
||||
outFile.end();
|
||||
@ -301,7 +262,7 @@ class Download {
|
||||
//Progress
|
||||
r.on('data', (c) => {
|
||||
outFile.write(c);
|
||||
this.downloaded += c.length;
|
||||
this.download.downloaded += c.length;
|
||||
});
|
||||
|
||||
r.on('error', (e) => {
|
||||
@ -311,53 +272,136 @@ class Download {
|
||||
|
||||
//Save size
|
||||
this.size = parseInt(r.headers['content-length'], 10) + start;
|
||||
|
||||
this.download.size = this.size;
|
||||
});
|
||||
}
|
||||
|
||||
//Stop current request
|
||||
async stop() {
|
||||
this._request.destroy();
|
||||
this._request = null;
|
||||
this.state = 0;
|
||||
if (this._ppp) await this._ppp;
|
||||
//If post processing, wait for it
|
||||
if (this.postPromise) {
|
||||
await this._postPromise;
|
||||
return this._cb();
|
||||
}
|
||||
|
||||
//Cancel download
|
||||
if (this._response)
|
||||
this._response.destroy();
|
||||
if (this._request)
|
||||
this._request.destroy();
|
||||
|
||||
// this._response = null;
|
||||
// this._request = null;
|
||||
|
||||
this.stopped = true;
|
||||
this.download.state = 0;
|
||||
this._cb();
|
||||
}
|
||||
|
||||
async _finished(tmp) {
|
||||
this.state = 2;
|
||||
|
||||
//Create post processing promise
|
||||
let resolve;
|
||||
this._ppp = new Promise((res, rej) => {
|
||||
resolve = res;
|
||||
});
|
||||
|
||||
//Prepare output directory
|
||||
try {
|
||||
await fs.promises.mkdir(path.dirname(this.path), {recursive: true})
|
||||
} catch (e) {};
|
||||
async _post(tmp) {
|
||||
this.download.state = 2;
|
||||
|
||||
//Decrypt
|
||||
//this.path += (this.quality == 9) ? '.flac' : '.mp3';
|
||||
decryptor.decryptFile(decryptor.getKey(this.track.id), tmp, `${tmp}.DEC`);
|
||||
await fs.promises.copyFile(`${tmp}.DEC`, this.path);
|
||||
//Delete encrypted
|
||||
await fs.promises.unlink(tmp);
|
||||
decryptor.decryptFile(decryptor.getKey(this.qualityInfo.trackId), tmp, `${tmp}.DEC`);
|
||||
let outPath = this.generatePath(this.qualityInfo.quality);
|
||||
await fs.promises.mkdir(path.dirname(outPath), {recursive: true});
|
||||
await fs.promises.copyFile(`${tmp}.DEC`, outPath);
|
||||
await fs.promises.unlink(`${tmp}.DEC`);
|
||||
await fs.promises.unlink(tmp);
|
||||
|
||||
//Tags
|
||||
await this.tagAudio(this.path, this.track);
|
||||
if (!this.isUserUploaded) {
|
||||
//Tag
|
||||
await this.tagTrack(outPath);
|
||||
|
||||
//Finish
|
||||
this.state = 3;
|
||||
resolve();
|
||||
this._ppp = null;
|
||||
this.onDone();
|
||||
//Lyrics
|
||||
if (this.settings.downloadLyrics) {
|
||||
let lrcFile = outPath.split('.').slice(0, -1).join('.') + '.lrc';
|
||||
let lrc;
|
||||
try {
|
||||
lrc = this.generateLRC();
|
||||
} catch (e) {
|
||||
logger.warn('Error getting lyrics! ' + e);
|
||||
}
|
||||
if (lrc) {
|
||||
await fs.promises.writeFile(lrcFile, lrc, {encoding: 'utf-8'});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.download.state = 3;
|
||||
this._cb();
|
||||
}
|
||||
|
||||
async tagTrack(path) {
|
||||
let cover;
|
||||
try {
|
||||
cover = await this.downloadCover(this.track.albumArt.full);
|
||||
} catch (e) {}
|
||||
|
||||
//Genre tag
|
||||
let genres = [];
|
||||
if (this.publicAlbum.genres && this.publicAlbum.genres.data)
|
||||
genres = this.publicAlbum.genres.data.map(g => g.name);
|
||||
|
||||
if (path.toLowerCase().endsWith('.mp3')) {
|
||||
//Load
|
||||
const audioData = await fs.promises.readFile(path);
|
||||
const writer = new ID3Writer(audioData);
|
||||
|
||||
writer.setFrame('TIT2', this.track.title);
|
||||
writer.setFrame('TPE1', this.track.artists.map((a) => a.name));
|
||||
if (this.publicAlbum.artist) writer.setFrame('TPE2', this.publicAlbum.artist.name);
|
||||
writer.setFrame('TALB', this.track.album.title);
|
||||
writer.setFrame('TRCK', this.track.trackNumber);
|
||||
writer.setFrame('TPOS', this.track.diskNumber);
|
||||
writer.setFrame('TCON', genres);
|
||||
let date = new Date(this.publicTrack.release_date);
|
||||
writer.setFrame('TYER', date.getFullYear());
|
||||
writer.setFrame('TDAT', `${date.getMonth().toString().padStart(2, '0')}${date.getDay().toString().padStart(2, '0')}`);
|
||||
if (this.publicTrack.bpm > 2) writer.setFrame('TBPM', this.publicTrack.bpm);
|
||||
if (this.publicAlbum.label) writer.setFrame('TPUB', this.publicAlbum.label);
|
||||
writer.setFrame('TSRC', this.publicTrack.isrc);
|
||||
if (this.rawTrack.results.LYRICS) writer.setFrame('USLT', {
|
||||
lyrics: this.rawTrack.results.LYRICS.LYRICS_TEXT,
|
||||
language: 'eng',
|
||||
description: 'Unsychronised lyrics'
|
||||
});
|
||||
|
||||
if (cover) writer.setFrame('APIC', {type: 3, data: cover, description: 'Cover'});
|
||||
writer.addTag();
|
||||
|
||||
//Write
|
||||
await fs.promises.writeFile(path, Buffer.from(writer.arrayBuffer));
|
||||
return;
|
||||
}
|
||||
|
||||
//Tag FLAC
|
||||
if (path.toLowerCase().endsWith('.flac')) {
|
||||
const flac = new Metaflac(path);
|
||||
flac.removeAllTags();
|
||||
|
||||
flac.setTag(`TITLE=${this.track.title}`);
|
||||
flac.setTag(`ALBUM=${this.track.album.title}`);
|
||||
flac.setTag(`ARTIST=${this.track.artistString}`);
|
||||
flac.setTag(`TRACKNUMBER=${this.track.trackNumber}`);
|
||||
flac.setTag(`DISCNUMBER=${this.track.diskNumber}`);
|
||||
if (this.publicAlbum.artist) flac.setTag(`ALBUMARTIST=${this.publicAlbum.artist.name}`);
|
||||
flac.setTag(`GENRE=${genres.join(", ")}`);
|
||||
flac.setTag(`DATE=${this.publicTrack.release_date}`);
|
||||
if (this.publicTrack.bpm > 2) flac.setTag(`BPM=${this.publicTrack.bpm}`);
|
||||
if (this.publicAlbum.label) flac.setTag(`LABEL=${this.publicAlbum.label}`);
|
||||
flac.setTag(`ISRC=${this.publicTrack.isrc}`);
|
||||
if (this.publicAlbum.upc) flac.setTag(`BARCODE=${this.publicAlbum.upc}`);
|
||||
if (this.rawTrack.results.LYRICS) flac.setTag(`LYRICS=${this.rawTrack.results.LYRICS.LYRICS_TEXT}`);
|
||||
|
||||
if (cover) flac.importPicture(cover);
|
||||
|
||||
flac.save();
|
||||
}
|
||||
}
|
||||
|
||||
//Download cover to buffer
|
||||
async downloadCover(url) {
|
||||
return await new Promise((res, rej) => {
|
||||
return await new Promise((res) => {
|
||||
let out = Buffer.alloc(0);
|
||||
https.get(url, (r) => {
|
||||
r.on('data', (d) => {
|
||||
@ -370,49 +414,105 @@ class Download {
|
||||
});
|
||||
}
|
||||
|
||||
//Write tags to audio file
|
||||
async tagAudio(path, track) {
|
||||
let cover;
|
||||
try {
|
||||
cover = await this.downloadCover(track.albumArt.full);
|
||||
} catch (e) {}
|
||||
generateLRC() {
|
||||
//Check if exists
|
||||
if (!this.rawTrack.results.LYRICS || !this.rawTrack.results.LYRICS.LYRICS_SYNC_JSON) return;
|
||||
let lyrics = new Lyrics(this.rawTrack.results.LYRICS);
|
||||
if (lyrics.lyrics.length == 0) return;
|
||||
//Metadata
|
||||
let out = `[ar:${this.track.artistString}]\r\n[al:${this.track.album.title}]\r\n[ti:${this.track.title}]\r\n`;
|
||||
//Lyrics
|
||||
for (let l of lyrics.lyrics) {
|
||||
if (l.lrcTimestamp && l.text)
|
||||
out += `${l.lrcTimestamp}${l.text}\r\n`;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
generatePath(quality) {
|
||||
//User uploaded mp3s
|
||||
if (this.isUserUploaded) {
|
||||
//Generate path
|
||||
let p = this.settings.downloadsPath;
|
||||
if (this.settings.createArtistFolder && this.download.track.artists[0].name.length > 0)
|
||||
p = path.join(p, sanitize(this.download.track.artists[0].name));
|
||||
if (this.settings.createAlbumFolder && this.download.track.album.title.length > 0)
|
||||
p = path.join(p, sanitize(this.download.track.album.title));
|
||||
//Filename
|
||||
let out = path.join(p, sanitize(this.download.track.title));
|
||||
if (!out.includes('.'))
|
||||
out += '.mp3';
|
||||
return out;
|
||||
}
|
||||
|
||||
//Generate filename
|
||||
let fn = this.settings.downloadFilename;
|
||||
|
||||
//Disable feats for single artist
|
||||
let feats = '';
|
||||
if (this.track.artists.length >= 2)
|
||||
feats = this.track.artists.slice(1).map((a) => a.name).join(', ');
|
||||
|
||||
if (path.toLowerCase().endsWith('.mp3')) {
|
||||
//Load
|
||||
const audioData = await fs.promises.readFile(path);
|
||||
const writer = new ID3Writer(audioData);
|
||||
|
||||
writer.setFrame('TIT2', track.title);
|
||||
if (track.artists) writer.setFrame('TPE1', track.artists.map((a) => a.name));
|
||||
if (track.album) writer.setFrame('TALB', track.album.title);
|
||||
if (track.trackNumber) writer.setFrame('TRCK', track.trackNumber);
|
||||
if (cover) writer.setFrame('APIC', {
|
||||
type: 3,
|
||||
data: cover,
|
||||
description: 'Cover'
|
||||
});
|
||||
writer.addTag();
|
||||
|
||||
//Write
|
||||
await fs.promises.writeFile(path, Buffer.from(writer.arrayBuffer));
|
||||
//Date
|
||||
let date = new Date(this.publicTrack.release_date);
|
||||
|
||||
let props = {
|
||||
'%title%': this.track.title,
|
||||
'%artists%': this.track.artistString,
|
||||
'%artist%': this.track.artists[0].name,
|
||||
'%feats%': feats,
|
||||
'%trackNumber%': (this.track.trackNumber ? this.track.trackNumber : 1).toString(),
|
||||
'%0trackNumber%': (this.track.trackNumber ? this.track.trackNumber : 1).toString().padStart(2, '0'),
|
||||
'%album%': this.track.album.title,
|
||||
'%year%': date.getFullYear().toString(),
|
||||
};
|
||||
for (let k of Object.keys(props)) {
|
||||
fn = fn.replace(new RegExp(k, 'g'), sanitize(props[k]));
|
||||
}
|
||||
//Tag FLAC
|
||||
if (path.toLowerCase().endsWith('.flac')) {
|
||||
const flac = new Metaflac(path);
|
||||
flac.removeAllTags();
|
||||
//Generate folders
|
||||
let p = this.settings.downloadsPath;
|
||||
if (this.settings.createArtistFolder) p = path.join(p, sanitize(this.track.artists[0].name));
|
||||
if (this.settings.createAlbumFolder) p = path.join(p, sanitize(this.track.album.title));
|
||||
|
||||
flac.setTag(`TITLE=${track.title}`);
|
||||
if (track.album)flac.setTag(`ALBUM=${track.album.title}`);
|
||||
if (track.trackNumber) flac.setTag(`TRACKNUMBER=${track.trackNumber}`);
|
||||
if (track.artistString) flac.setTag(`ARTIST=${track.artistString}`);
|
||||
if (cover) flac.importPicture(cover);
|
||||
|
||||
flac.save();
|
||||
//Extension
|
||||
if (quality.toString() == '9') {
|
||||
fn += '.flac';
|
||||
} else {
|
||||
fn += '.mp3';
|
||||
}
|
||||
|
||||
return path.join(p, fn);
|
||||
}
|
||||
}
|
||||
|
||||
class Download {
|
||||
constructor (track, quality, state) {
|
||||
this.track = track;
|
||||
this.quality = quality;
|
||||
// 0 - none
|
||||
// 1 - downloading
|
||||
// 2 - postprocess
|
||||
// 3 - done
|
||||
// -1 - error
|
||||
this.state = state;
|
||||
|
||||
module.exports = {Downloads, Download};
|
||||
//Updated from threads
|
||||
this.downloaded = 0;
|
||||
this.size = 1;
|
||||
}
|
||||
|
||||
toDB() {
|
||||
return {
|
||||
_id: this.track.id,
|
||||
track: this.track,
|
||||
quality: this.quality,
|
||||
state: this.state
|
||||
}
|
||||
}
|
||||
|
||||
static fromDB(json) {
|
||||
return new Download(json.track, json.quality, json.state);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {DownloadManager}
|
@ -4,15 +4,15 @@ const https = require('https');
|
||||
const fs = require('fs');
|
||||
const axios = require('axios').default;
|
||||
const logger = require('./winston');
|
||||
const {DeezerAPI, DeezerDecryptionStream} = require('./deezer');
|
||||
const {DeezerAPI, DeezerStream} = require('./deezer');
|
||||
const {Settings} = require('./settings');
|
||||
const {Track, Album, Artist, Playlist, DeezerProfile, SearchResults, DeezerLibrary, DeezerPage, Lyrics} = require('./definitions');
|
||||
const {Downloads} = require('./downloads');
|
||||
const {DownloadManager} = require('./downloads');
|
||||
const {Integrations} = require('./integrations');
|
||||
|
||||
let settings;
|
||||
let deezer;
|
||||
let downloads;
|
||||
let downloadManager;
|
||||
let integrations;
|
||||
|
||||
let sockets = [];
|
||||
@ -23,13 +23,16 @@ app.use(express.json({limit: '50mb'}));
|
||||
app.use(express.static(path.join(__dirname, '../client', 'dist')));
|
||||
//Server
|
||||
const server = require('http').createServer(app);
|
||||
const io = require('socket.io').listen(server);
|
||||
const io = require('socket.io').listen(server, {
|
||||
path: '/socket',
|
||||
});
|
||||
|
||||
//Get playback info
|
||||
app.get('/playback', async (req, res) => {
|
||||
try {
|
||||
let data = await fs.promises.readFile(Settings.getPlaybackInfoPath(), 'utf-8');
|
||||
return res.json(data);
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (e) {}
|
||||
|
||||
return res.json({});
|
||||
@ -53,7 +56,7 @@ app.get('/settings', (req, res) => {
|
||||
app.post('/settings', async (req, res) => {
|
||||
if (req.body) {
|
||||
Object.assign(settings, req.body);
|
||||
downloads.settings = settings;
|
||||
downloadManager.settings = settings;
|
||||
integrations.updateSettings(settings);
|
||||
await settings.save();
|
||||
}
|
||||
@ -70,6 +73,9 @@ app.post('/authorize', async (req, res) => {
|
||||
settings.arl = req.body.arl;
|
||||
|
||||
if (await (deezer.authorize())) {
|
||||
//Update download manager
|
||||
downloadManager.setDeezer(deezer);
|
||||
|
||||
res.status(200).send('OK');
|
||||
return;
|
||||
}
|
||||
@ -238,16 +244,22 @@ app.put('/library/:type', async (req, res) => {
|
||||
app.get('/streaminfo/:info', async (req, res) => {
|
||||
let info = req.params.info;
|
||||
let quality = req.query.q ? req.query.q : 3;
|
||||
return res.json(await deezer.qualityFallback(info, quality));
|
||||
let qualityInfo = await deezer.fallback(info, quality);
|
||||
|
||||
if (qualityInfo == null)
|
||||
return res.sendStatus(404).end();
|
||||
|
||||
//Generate stream URL before sending
|
||||
qualityInfo.generateUrl();
|
||||
return res.json(qualityInfo);
|
||||
});
|
||||
|
||||
// S T R E A M I N G
|
||||
app.get('/stream/:info', (req, res) => {
|
||||
app.get('/stream/:info', async (req, res) => {
|
||||
//Parse stream info
|
||||
let quality = req.query.q ? req.query.q : 3;
|
||||
let streamInfo = Track.getUrlInfo(req.params.info);
|
||||
let url = DeezerAPI.getUrl(streamInfo.trackId, streamInfo.md5origin, streamInfo.mediaVersion, quality);
|
||||
let trackId = req.params.info.substring(35);
|
||||
streamInfo.quality = quality;
|
||||
|
||||
//MIME type of audio
|
||||
let mime = 'audio/mp3';
|
||||
@ -258,59 +270,38 @@ app.get('/stream/:info', (req, res) => {
|
||||
if (req.headers.range) range = req.headers.range;
|
||||
let rangeParts = range.replace(/bytes=/, '').split('-');
|
||||
let start = parseInt(rangeParts[0], 10);
|
||||
let end = '';
|
||||
let end = -1;
|
||||
if (rangeParts.length >= 2) end = rangeParts[1];
|
||||
if (end == '' || end == ' ') end = -1;
|
||||
|
||||
//Round to 2048 for deezer
|
||||
let dStart = start - (start % 2048);
|
||||
//Create Stream
|
||||
let stream = new DeezerStream(streamInfo, {});
|
||||
await stream.open(start, end);
|
||||
|
||||
//Make request to Deezer CDN
|
||||
let _request = https.get(url, {headers: {'Range': `bytes=${dStart}-${end}`}}, (r) => {
|
||||
//Error from Deezer
|
||||
//TODO: Quality fallback
|
||||
if (r.statusCode < 200 || r.statusCode > 300) {
|
||||
res.status(404);
|
||||
return res.end();
|
||||
}
|
||||
//Range header
|
||||
if (req.headers.range) {
|
||||
end = (end == -1) ? stream.size - 1 : end;
|
||||
res.writeHead(206, {
|
||||
'Content-Range': `bytes ${start}-${end}/${stream.size}`,
|
||||
'Accept-Ranges': 'bytes',
|
||||
'Content-Length': stream.size - start,
|
||||
'Content-Type': mime
|
||||
});
|
||||
|
||||
//Normal (non range) request
|
||||
} else {
|
||||
res.writeHead(200, {
|
||||
'Content-Length': stream.size,
|
||||
'Content-Type': mime
|
||||
});
|
||||
}
|
||||
|
||||
let decryptor = new DeezerDecryptionStream(trackId, {offset: start});
|
||||
|
||||
//Get total size
|
||||
let chunkSize = parseInt(r.headers["content-length"], 10)
|
||||
let total = chunkSize;
|
||||
if (start > 0) total += start;
|
||||
|
||||
//Ranged request
|
||||
if (req.headers.range) {
|
||||
end = total - 1
|
||||
|
||||
res.writeHead(206, {
|
||||
'Content-Range': `bytes ${start}-${end}/${total}`,
|
||||
'Accept-Ranges': 'bytes',
|
||||
'Content-Length': chunkSize,
|
||||
'Content-Type': mime
|
||||
});
|
||||
|
||||
//Normal (non range) request
|
||||
} else {
|
||||
res.writeHead(200, {
|
||||
'Content-Length': total,
|
||||
'Content-Type': mime
|
||||
});
|
||||
}
|
||||
|
||||
//Pipe: Deezer -> Decryptor -> Response
|
||||
decryptor.pipe(res);
|
||||
r.pipe(decryptor);
|
||||
|
||||
});
|
||||
//Internet/Request error
|
||||
_request.on('error', () => {
|
||||
//console.log('Streaming error: ' + e);
|
||||
//HTML audio will restart automatically
|
||||
//Should force HTML5 to retry
|
||||
stream.on('error', () => {
|
||||
res.destroy();
|
||||
});
|
||||
|
||||
stream.pipe(res);
|
||||
});
|
||||
|
||||
//Get deezer page
|
||||
@ -361,6 +352,18 @@ app.get('/smarttracklist/:id', async (req, res) => {
|
||||
return res.send(tracks);
|
||||
});
|
||||
|
||||
//Artist smart radio
|
||||
app.get('/smartradio/:id', async (req, res) => {
|
||||
let data = await deezer.callApi('smart.getSmartRadio', {art_id: req.params.id});
|
||||
res.send(data.results.data.map(t => new Track(t)));
|
||||
});
|
||||
|
||||
//Track Mix
|
||||
app.get('/trackmix/:id', async (req, res) => {
|
||||
let data = await deezer.callApi('song.getContextualTrackMix', {sng_ids: [req.params.id]});
|
||||
res.send(data.results.data.map(t => new Track(t)));
|
||||
});
|
||||
|
||||
//Load lyrics, ID = SONG ID
|
||||
app.get('/lyrics/:id', async (req, res) => {
|
||||
let data = await deezer.callApi('song.getLyrics', {
|
||||
@ -390,7 +393,7 @@ app.post('/downloads', async (req, res) => {
|
||||
let tracks = req.body;
|
||||
let quality = req.query.q;
|
||||
for (let track of tracks) {
|
||||
downloads.add(track, quality);
|
||||
downloadManager.add(track, quality);
|
||||
}
|
||||
|
||||
res.status(200).send('OK');
|
||||
@ -398,30 +401,29 @@ app.post('/downloads', async (req, res) => {
|
||||
|
||||
//PUT to /download to start
|
||||
app.put('/download', async (req, res) => {
|
||||
await downloads.start();
|
||||
await downloadManager.start();
|
||||
res.status(200).send('OK');
|
||||
});
|
||||
|
||||
//DELETE to /download to stop/pause
|
||||
app.delete('/download', async (req, res) => {
|
||||
await downloads.stop();
|
||||
await downloadManager.stop();
|
||||
res.status(200).send('OK');
|
||||
})
|
||||
|
||||
//Get all downloads
|
||||
app.get('/downloads', async (req, res) => {
|
||||
res.json({
|
||||
downloading: downloads.downloading,
|
||||
downloads: downloads.downloads.map((d) => {
|
||||
return d.toDB();
|
||||
})
|
||||
downloading: downloadManager.downloading,
|
||||
queue: downloadManager.queue,
|
||||
threads: downloadManager.threads.map(t => t.download)
|
||||
});
|
||||
});
|
||||
|
||||
//Delete singel download
|
||||
//Delete single download
|
||||
app.delete('/downloads/:index', async (req, res) => {
|
||||
let index = parseInt(req.params.index, 10);
|
||||
await downloads.delete(index);
|
||||
await downloadManager.delete(index);
|
||||
res.status(200).end();
|
||||
});
|
||||
|
||||
@ -499,32 +501,27 @@ async function createServer(electron = false, ecb) {
|
||||
deezer = new DeezerAPI(settings.arl, electron);
|
||||
|
||||
//Prepare downloads
|
||||
downloads = new Downloads(settings, () => {
|
||||
downloadManager = new DownloadManager(settings, () => {
|
||||
//Emit queue change to socket
|
||||
sockets.forEach((s) => {
|
||||
s.emit('downloads', {
|
||||
downloading: downloads.downloading,
|
||||
downloads: downloads.downloads
|
||||
downloading: downloadManager.downloading,
|
||||
queue: downloadManager.queue,
|
||||
threads: downloadManager.threads.map(t => t.download)
|
||||
});
|
||||
});
|
||||
});
|
||||
await downloads.load();
|
||||
await downloadManager.load();
|
||||
downloadManager.setDeezer(deezer);
|
||||
//Emit download progress updates
|
||||
setInterval(() => {
|
||||
sockets.forEach((s) => {
|
||||
if (!downloads.download) {
|
||||
s.emit('download', null);
|
||||
return;
|
||||
}
|
||||
s.emit('download', {
|
||||
id: downloads.download.id,
|
||||
size: downloads.download.size,
|
||||
downloaded: downloads.download.downloaded,
|
||||
track: downloads.download.track,
|
||||
path: downloads.download.path
|
||||
});
|
||||
if (!downloadManager.downloading && downloadManager.threads.length == 0)
|
||||
return;
|
||||
|
||||
s.emit('currentlyDownloading', downloadManager.threads.map(t => t.download));
|
||||
});
|
||||
}, 350);
|
||||
}, 400);
|
||||
|
||||
//Integrations (lastfm, discord)
|
||||
integrations = new Integrations(settings);
|
||||
|
@ -28,6 +28,12 @@ class Settings {
|
||||
this.lastFM = null;
|
||||
this.enableDiscord = false;
|
||||
this.discordJoin = false;
|
||||
|
||||
this.showAutocomplete = true;
|
||||
this.downloadThreads = 4;
|
||||
this.downloadLyrics = true;
|
||||
this.primaryColor = '#2196F3';
|
||||
this.language = 'en';
|
||||
}
|
||||
|
||||
//Based on electorn app.getPath
|
||||
@ -57,7 +63,12 @@ class Settings {
|
||||
}
|
||||
//Get path to downloads database
|
||||
static getDownloadsDB() {
|
||||
return path.join(Settings.getDir(), 'downloads.db');
|
||||
//Delete old DB if exists
|
||||
let oldPath = path.join(Settings.getDir(), 'downloads.db');
|
||||
if (fs.existsSync(oldPath))
|
||||
fs.unlink(oldPath, () => {});
|
||||
|
||||
return path.join(Settings.getDir(), 'downloads2.db');
|
||||
}
|
||||
//Get path to temporary / unfinished downlaods
|
||||
static getTempDownloads() {
|
||||
|
BIN
build/installerIcon.ico
Normal file
BIN
build/installerIcon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 182 KiB |
BIN
build/uninstallerIcon.ico
Normal file
BIN
build/uninstallerIcon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 188 KiB |
17
generate_translations.py
Normal file
17
generate_translations.py
Normal file
@ -0,0 +1,17 @@
|
||||
import zipfile
|
||||
import json
|
||||
|
||||
def generate():
|
||||
with zipfile.ZipFile('translations.zip') as zip:
|
||||
for file in zip.namelist():
|
||||
if 'freezerpc.json' in file:
|
||||
data = zip.open(file).read()
|
||||
lang = file.split('/')[0].split('-')[0].lower()
|
||||
if lang != 'en':
|
||||
with open('app/client/src/locales/' + lang + '.json', 'wb') as f:
|
||||
f.write(data)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
generate()
|
11
package.json
11
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "freezer",
|
||||
"private": true,
|
||||
"version": "1.0.9",
|
||||
"version": "1.1.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"pack": "electron-builder --dir",
|
||||
@ -16,6 +16,7 @@
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.exttex.freezer",
|
||||
"productName": "Freezer",
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "app/assets/**",
|
||||
@ -29,13 +30,19 @@
|
||||
],
|
||||
"win": {
|
||||
"target": [
|
||||
"portable"
|
||||
"portable", "nsis"
|
||||
],
|
||||
"icon": "build/icon.ico",
|
||||
"asarUnpack": [
|
||||
"app/node_modules/nodeezcryptor/**"
|
||||
]
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": true,
|
||||
"perMachine": false,
|
||||
"allowElevation": false,
|
||||
"allowToChangeInstallationDirectory": false
|
||||
},
|
||||
"linux": {
|
||||
"target": [
|
||||
"AppImage"
|
||||
|
Loading…
Reference in New Issue
Block a user