From a6f6c503450945f0b21c59db8c6e9d3e6456c091 Mon Sep 17 00:00:00 2001 From: stratuma Date: Mon, 20 May 2024 16:44:16 +0200 Subject: [PATCH] added better error handeling for database stuff --- components/Crunchyroll/ListAnimes.ts | 2 - package.json | 3 +- pnpm-lock.yaml | 175 +++++++- src/api/api.ts | 81 +++- src/api/db/database.ts | 2 +- .../crunchyroll/crunchyroll.controller.ts | 5 + .../routes/crunchyroll/crunchyroll.service.ts | 48 +++ src/api/routes/service/service.controller.ts | 2 + src/api/routes/service/service.service.ts | 377 ++++++++++++++---- 9 files changed, 592 insertions(+), 103 deletions(-) diff --git a/components/Crunchyroll/ListAnimes.ts b/components/Crunchyroll/ListAnimes.ts index 3886734..6450a2a 100644 --- a/components/Crunchyroll/ListAnimes.ts +++ b/components/Crunchyroll/ListAnimes.ts @@ -5,7 +5,6 @@ import type { CrunchyAnimeFetch, CrunchySearchFetch } from './Types' export async function searchCrunchy(q: string) { var isProxyActive: boolean | undefined - ;(window as any).myAPI.getProxyActive().then((result: boolean) => { isProxyActive = result }) @@ -112,7 +111,6 @@ export async function searchCrunchy(q: string) { export async function getCRSeries(q: string) { var isProxyActive: boolean | undefined - ;(window as any).myAPI.getProxyActive().then((result: boolean) => { isProxyActive = result }) diff --git a/package.json b/package.json index 6fa14d2..f4cb86e 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,8 @@ "node-cron": "^3.0.3", "protobufjs": "^7.2.6", "sequelize": "^6.37.3", - "sqlite3": "5.1.6" + "sqlite3": "5.1.6", + "winston": "^3.13.0" }, "build": { "files": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 073c332..e2d8280 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,6 +56,9 @@ importers: sqlite3: specifier: 5.1.6 version: 5.1.6(encoding@0.1.13) + winston: + specifier: ^3.13.0 + version: 3.13.0 devDependencies: 7zip-bin: specifier: ^5.2.0 @@ -343,6 +346,10 @@ packages: resolution: {integrity: sha512-EeEjMobfuJrwoctj7FA1y1KEbM0+Q1xSjobIEyie9k4haVEBB7vkDvsasw1pM3rO39mL2akxIAzLMUAtrMHZhA==} engines: {node: '>=16.13'} + '@colors/colors@1.6.0': + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + '@csstools/selector-resolve-nested@1.1.0': resolution: {integrity: sha512-uWvSaeRcHyeNenKg8tp17EVDRkpflmdyvbE0DHo6D/GdBb6PDnCYYU6gRpXhtICMGMcahQmj2zGxwFM/WC8hCg==} engines: {node: ^14 || ^16 || >=18} @@ -355,6 +362,9 @@ packages: peerDependencies: postcss-selector-parser: ^6.0.13 + '@dabh/diagnostics@2.0.3': + resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + '@develar/schema-utils@2.6.5': resolution: {integrity: sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==} engines: {node: '>= 8.9.0'} @@ -1298,6 +1308,9 @@ packages: '@types/serve-static@1.15.7': resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + '@types/validator@13.11.9': resolution: {integrity: sha512-FCTsikRozryfayPuiI46QzH3fnrOoctTjvOYZkho9BTFLCOZ2rgZJHMOVgCOfttjPJcgOx52EpkY0CMfy87MIw==} @@ -2206,16 +2219,25 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + color-support@1.1.3: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} hasBin: true + color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + colord@2.9.3: resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + colorspace@1.1.4: + resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -2670,6 +2692,9 @@ packages: resolution: {integrity: sha512-97qcDM6mUA1jAeX6cktw7akc5awIGA+VIkA5MygKOKA+c2Vseo/xwKN0JNJTUhZUtPwZboKVD2p1xu+sV/F4xA==} engines: {node: '>= 0.8.0'} + enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} @@ -3031,6 +3056,9 @@ packages: fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -3081,6 +3109,9 @@ packages: resolution: {integrity: sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==} engines: {node: '>=0.8.0'} + fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + focus-trap@7.5.4: resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==} @@ -3510,6 +3541,9 @@ packages: is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} @@ -3810,6 +3844,9 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + launch-editor@2.6.1: resolution: {integrity: sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==} @@ -3905,6 +3942,10 @@ packages: resolution: {integrity: sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==} engines: {node: '>=4'} + logform@2.6.0: + resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==} + engines: {node: '>= 12.0.0'} + long@5.2.3: resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} @@ -4395,6 +4436,9 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + onetime@2.0.1: resolution: {integrity: sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==} engines: {node: '>=4'} @@ -4570,11 +4614,11 @@ packages: pino-abstract-transport@1.2.0: resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} - pino-std-serializers@6.2.2: - resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} + pino-std-serializers@7.0.0: + resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} - pino@9.0.0: - resolution: {integrity: sha512-uI1ThkzTShNSwvsUM6b4ND8ANzWURk9zTELMztFkmnCQeR/4wkomJ+echHee5GMWGovoSfjwdeu80DsFIt7mbA==} + pino@9.1.0: + resolution: {integrity: sha512-qUcgfrlyOtjwhNLdbhoL7NR4NkHjzykAPw0V2QLFbvu/zss29h4NkRnibyFzBrNCbzCOY3WZ9hhKSwfOkNggYA==} hasBin: true pirates@4.0.6: @@ -5271,6 +5315,9 @@ packages: simple-git@3.24.0: resolution: {integrity: sha512-QqAKee9Twv+3k8IFOFfPB2hnk6as6Y6ACUpwCtQvRYBAes23Wv3SZlHVobAzqcE8gfsisCvPw3HGW3HYM+VYYw==} + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + simple-update-notifier@2.0.0: resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} engines: {node: '>=10'} @@ -5317,8 +5364,8 @@ packages: resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - sonic-boom@3.8.1: - resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} + sonic-boom@4.0.1: + resolution: {integrity: sha512-hTSD/6JMLyT4r9zeof6UtuBDpjJ9sO08/nmS5djaA9eozT9oOlNdpXSnzcgj4FTqpk3nkLrs61l4gip9r1HCrQ==} source-map-js@1.2.0: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} @@ -5378,6 +5425,9 @@ packages: resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} engines: {node: '>= 8'} + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + standard-as-callback@2.1.0: resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} @@ -5576,6 +5626,9 @@ packages: engines: {node: '>=10'} hasBin: true + text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -5586,8 +5639,8 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - thread-stream@2.7.0: - resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} + thread-stream@3.0.0: + resolution: {integrity: sha512-oUIFjxaUT6knhPtWgDMc29zF1FcSl0yXpapkyrQrCGEfYA2HUZXCilUtKyYIv6HkCyqSPAMkY+EG0GbyIrNDQg==} through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} @@ -5635,6 +5688,10 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + truncate-utf8-bytes@1.0.2: resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==} @@ -6141,6 +6198,14 @@ packages: wide-align@1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + winston-transport@4.7.0: + resolution: {integrity: sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==} + engines: {node: '>= 12.0.0'} + + winston@3.13.0: + resolution: {integrity: sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==} + engines: {node: '>= 12.0.0'} + wkx@0.5.0: resolution: {integrity: sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==} @@ -6479,6 +6544,8 @@ snapshots: dependencies: mime: 3.0.0 + '@colors/colors@1.6.0': {} + '@csstools/selector-resolve-nested@1.1.0(postcss-selector-parser@6.0.16)': dependencies: postcss-selector-parser: 6.0.16 @@ -6487,6 +6554,12 @@ snapshots: dependencies: postcss-selector-parser: 6.0.16 + '@dabh/diagnostics@2.0.3': + dependencies: + colorspace: 1.1.4 + enabled: 2.0.0 + kuler: 2.0.0 + '@develar/schema-utils@2.6.5': dependencies: ajv: 6.12.6 @@ -7656,6 +7729,8 @@ snapshots: '@types/node': 20.12.11 '@types/send': 0.17.4 + '@types/triple-beam@1.3.5': {} + '@types/validator@13.11.9': {} '@types/verror@1.10.10': @@ -8948,12 +9023,27 @@ snapshots: color-name@1.1.4: {} + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + color-support@1.1.3: {} + color@3.2.1: + dependencies: + color-convert: 1.9.3 + color-string: 1.9.1 + colord@2.9.3: {} colorette@2.0.20: {} + colorspace@1.1.4: + dependencies: + color: 3.2.1 + text-hex: 1.0.0 + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -9415,6 +9505,8 @@ snapshots: empty-dir@1.0.0: {} + enabled@2.0.0: {} + encodeurl@1.0.2: {} encoding@0.1.13: @@ -9970,7 +10062,7 @@ snapshots: fast-json-stringify: 5.15.1 find-my-way: 8.2.0 light-my-request: 5.13.0 - pino: 9.0.0 + pino: 9.1.0 process-warning: 3.0.0 proxy-addr: 2.0.7 rfdc: 1.3.1 @@ -9988,6 +10080,8 @@ snapshots: dependencies: pend: 1.2.0 + fecha@4.2.3: {} + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 @@ -10051,6 +10145,8 @@ snapshots: async: 3.2.5 which: 1.3.1 + fn.name@1.1.0: {} + focus-trap@7.5.4: dependencies: tabbable: 6.2.0 @@ -10561,6 +10657,8 @@ snapshots: is-arrayish@0.2.1: {} + is-arrayish@0.3.2: {} + is-bigint@1.0.4: dependencies: has-bigints: 1.0.2 @@ -10850,6 +10948,8 @@ snapshots: kolorist@1.8.0: {} + kuler@2.0.0: {} + launch-editor@2.6.1: dependencies: picocolors: 1.0.0 @@ -10948,6 +11048,15 @@ snapshots: dependencies: chalk: 2.4.2 + logform@2.6.0: + dependencies: + '@colors/colors': 1.6.0 + '@types/triple-beam': 1.3.5 + fecha: 4.2.3 + ms: 2.1.3 + safe-stable-stringify: 2.4.3 + triple-beam: 1.4.1 + long@5.2.3: {} lowercase-keys@2.0.0: {} @@ -11671,6 +11780,10 @@ snapshots: dependencies: wrappy: 1.0.2 + one-time@1.0.0: + dependencies: + fn.name: 1.1.0 + onetime@2.0.1: dependencies: mimic-fn: 1.2.0 @@ -11859,21 +11972,21 @@ snapshots: readable-stream: 4.5.2 split2: 4.2.0 - pino-std-serializers@6.2.2: {} + pino-std-serializers@7.0.0: {} - pino@9.0.0: + pino@9.1.0: dependencies: atomic-sleep: 1.0.0 fast-redact: 3.5.0 on-exit-leak-free: 2.1.2 pino-abstract-transport: 1.2.0 - pino-std-serializers: 6.2.2 + pino-std-serializers: 7.0.0 process-warning: 3.0.0 quick-format-unescaped: 4.0.4 real-require: 0.2.0 safe-stable-stringify: 2.4.3 - sonic-boom: 3.8.1 - thread-stream: 2.7.0 + sonic-boom: 4.0.1 + thread-stream: 3.0.0 pirates@4.0.6: {} @@ -12577,6 +12690,10 @@ snapshots: transitivePeerDependencies: - supports-color + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + simple-update-notifier@2.0.0: dependencies: semver: 7.6.1 @@ -12628,7 +12745,7 @@ snapshots: ip-address: 9.0.5 smart-buffer: 4.2.0 - sonic-boom@3.8.1: + sonic-boom@4.0.1: dependencies: atomic-sleep: 1.0.0 @@ -12692,6 +12809,8 @@ snapshots: minipass: 3.3.6 optional: true + stack-trace@0.0.10: {} + standard-as-callback@2.1.0: {} stat-mode@1.0.0: {} @@ -12933,6 +13052,8 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 + text-hex@1.0.0: {} + text-table@0.2.0: {} thenify-all@1.6.0: @@ -12943,7 +13064,7 @@ snapshots: dependencies: any-promise: 1.3.0 - thread-stream@2.7.0: + thread-stream@3.0.0: dependencies: real-require: 0.2.0 @@ -12977,6 +13098,8 @@ snapshots: tree-kill@1.2.2: {} + triple-beam@1.4.1: {} + truncate-utf8-bytes@1.0.2: dependencies: utf8-byte-length: 1.0.4 @@ -13569,6 +13692,26 @@ snapshots: dependencies: string-width: 4.2.3 + winston-transport@4.7.0: + dependencies: + logform: 2.6.0 + readable-stream: 3.6.2 + triple-beam: 1.4.1 + + winston@3.13.0: + dependencies: + '@colors/colors': 1.6.0 + '@dabh/diagnostics': 2.0.3 + async: 3.2.5 + is-stream: 2.0.1 + logform: 2.6.0 + one-time: 1.0.0 + readable-stream: 3.6.2 + safe-stable-stringify: 2.4.3 + stack-trace: 0.0.10 + triple-beam: 1.4.1 + winston-transport: 4.7.0 + wkx@0.5.0: dependencies: '@types/node': 20.12.11 diff --git a/src/api/api.ts b/src/api/api.ts index d6697c3..53b5488 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -4,21 +4,18 @@ import NodeCache from 'node-cache' import crunchyrollRoutes from './routes/crunchyroll/crunchyroll.route' import { sequelize } from './db/database' import serviceRoutes from './routes/service/service.route' -;(async () => { - try { - await sequelize.authenticate() - console.log('Connection has been established successfully.') - } catch (error) { - console.error('Unable to connect to the database:', error) - } +import { app } from 'electron' +import winston from 'winston' - try { - await sequelize.sync() - console.log('All models were synchronized successfully.') - } catch (error) { - console.log('Failed to synchronize Models') - } -})() +const logger = winston.createLogger({ + level: 'info', + format: winston.format.json(), + defaultMeta: { service: 'user-service' }, + transports: [ + new winston.transports.File({ filename: app.getPath('documents') + '/Crunchyroll Downloader/logs/error.log', level: 'error' }), + new winston.transports.File({ filename: app.getPath('documents') + '/Crunchyroll Downloader/logs/combined.log' }) + ] +}) const CacheController = new NodeCache({ stdTTL: 100, checkperiod: 120 }) @@ -38,14 +35,26 @@ declare module 'fastify' { } } +// Logger Type +declare module 'fastify' { + interface FastifyInstance { + logger: winston.Logger + } +} + // Cache Controller server.decorate('CacheController', CacheController) +// Logger +server.decorate('logger', logger) + // Routes server.register(crunchyrollRoutes, { prefix: 'api/crunchyroll' }) server.register(serviceRoutes, { prefix: 'api/service' }) -function startAPI() { +async function startAPI() { + await startDB() + server.listen({ port: 9941 }, (err, address) => { if (err) { console.error(err) @@ -55,4 +64,46 @@ function startAPI() { }) } +async function startDB() { + try { + await sequelize.authenticate() + console.log('Connection has been established successfully.') + logger.log({ + level: 'info', + message: 'Connection has been established successfully.', + timestamp: new Date().toISOString(), + section: 'databaseConnection' + }) + } catch (error) { + console.error('Unable to connect to the database:', error) + logger.log({ + level: 'error', + message: 'Unable to connect to the database', + error: error, + timestamp: new Date().toISOString(), + section: 'databaseConnection' + }) + } + + try { + await sequelize.sync() + console.log('All models were synchronized successfully.') + logger.log({ + level: 'info', + message: 'All models were synchronized successfully.', + timestamp: new Date().toISOString(), + section: 'databaseSync' + }) + } catch (error) { + console.log('Failed to synchronize Models') + logger.log({ + level: 'error', + message: 'Failed to synchronize Models', + error: error, + timestamp: new Date().toISOString(), + section: 'databaseSync' + }) + } +} + export default startAPI diff --git a/src/api/db/database.ts b/src/api/db/database.ts index 1b87d24..24bec5c 100644 --- a/src/api/db/database.ts +++ b/src/api/db/database.ts @@ -5,7 +5,7 @@ import { ADNEpisode } from '../types/adn' const sequelize = new Sequelize({ dialect: 'sqlite', - storage: app.getPath('documents') + '/crd-dbv2.db' + storage: app.getPath('documents') + '/Crunchyroll Downloader/databases/v1/data.db' }) interface AccountAttributes { diff --git a/src/api/routes/crunchyroll/crunchyroll.controller.ts b/src/api/routes/crunchyroll/crunchyroll.controller.ts index a2e04ad..0d0b08f 100644 --- a/src/api/routes/crunchyroll/crunchyroll.controller.ts +++ b/src/api/routes/crunchyroll/crunchyroll.controller.ts @@ -1,6 +1,7 @@ import type { FastifyReply, FastifyRequest } from 'fastify' import { crunchyLogin } from './crunchyroll.service' import { loggedInCheck } from '../service/service.service' +import { server } from '../../api' export async function loginController( request: FastifyRequest<{ @@ -15,6 +16,10 @@ export async function loginController( const account = await loggedInCheck('CR') if (!account) { + server.logger.log({ + level: 'error', + message: 'Not Logged in' + }) return reply.code(401).send({ message: 'Not Logged in' }) } diff --git a/src/api/routes/crunchyroll/crunchyroll.service.ts b/src/api/routes/crunchyroll/crunchyroll.service.ts index 86e9356..ae198fa 100644 --- a/src/api/routes/crunchyroll/crunchyroll.service.ts +++ b/src/api/routes/crunchyroll/crunchyroll.service.ts @@ -58,16 +58,40 @@ export async function crunchyLogin(user: string, passw: string, geo: string) { 'Failed to login to Crunchyroll', crErrors.find((r) => r.error === (error?.error as string)) ? crErrors.find((r) => r.error === (error?.error as string))?.response : (error.error as string) ) + server.logger.log({ + level: 'error', + message: 'Failed to login to Crunchyroll', + data: data, + error: error, + timestamp: new Date().toISOString(), + section: 'loginCrunchyrollFetch' + }) return { data: null, error: error.error } } if (!data) { messageBox('error', ['Cancel'], 2, 'Failed to login', 'Failed to login to Crunchyroll', 'Crunchyroll returned null') + server.logger.log({ + level: 'error', + message: 'Failed to login to Crunchyroll', + data: data, + error: error, + timestamp: new Date().toISOString(), + section: 'loginCrunchyrollFetch' + }) return { data: null, error: 'Crunchyroll returned null' } } if (!data.access_token) { messageBox('error', ['Cancel'], 2, 'Failed to login', 'Failed to login to Crunchyroll', 'Crunchyroll returned malformed data') + server.logger.log({ + level: 'error', + message: 'Failed to login to Crunchyroll', + data: data, + error: error, + timestamp: new Date().toISOString(), + section: 'loginCrunchyrollFetch' + }) return { data: null, error: 'Crunchyroll returned malformed data' } } @@ -88,16 +112,40 @@ export async function crunchyLogin(user: string, passw: string, geo: string) { 'Failed to login to Crunchyroll', crErrors.find((r) => r.error === (error?.error as string)) ? crErrors.find((r) => r.error === (error?.error as string))?.response : (error.error as string) ) + server.logger.log({ + level: 'error', + message: 'Failed to login to Crunchyroll', + data: data, + error: error, + timestamp: new Date().toISOString(), + section: 'loginCrunchyrollFetch' + }) return { data: null, error: error.error } } if (!data) { messageBox('error', ['Cancel'], 2, 'Failed to login', 'Failed to login to Crunchyroll', 'Crunchyroll returned null') + server.logger.log({ + level: 'error', + message: 'Failed to login to Crunchyroll', + data: data, + error: error, + timestamp: new Date().toISOString(), + section: 'loginCrunchyrollFetch' + }) return { data: null, error: 'Crunchyroll returned null' } } if (!data.access_token) { messageBox('error', ['Cancel'], 2, 'Failed to login', 'Failed to login to Crunchyroll', 'Crunchyroll returned malformed data') + server.logger.log({ + level: 'error', + message: 'Failed to login to Crunchyroll', + data: data, + error: error, + timestamp: new Date().toISOString(), + section: 'loginCrunchyrollFetch' + }) return { data: null, error: 'Crunchyroll returned malformed data' } } diff --git a/src/api/routes/service/service.controller.ts b/src/api/routes/service/service.controller.ts index c20ea87..0ad484b 100644 --- a/src/api/routes/service/service.controller.ts +++ b/src/api/routes/service/service.controller.ts @@ -117,6 +117,8 @@ export async function addPlaylistController( export async function getPlaylistController(request: FastifyRequest, reply: FastifyReply) { const playlist = await getPlaylist() + if (!playlist) return + for (const v of playlist) { if (v.dataValues.status === 'downloading') { const found = await getDownloading(v.dataValues.id) diff --git a/src/api/routes/service/service.service.ts b/src/api/routes/service/service.service.ts index 2400b47..f4a8ad6 100644 --- a/src/api/routes/service/service.service.ts +++ b/src/api/routes/service/service.service.ts @@ -25,61 +25,143 @@ const exec = util.promisify(require('child_process').exec) // Get All Accounts export async function getAllAccounts() { - const accounts = await Account.findAll({ - attributes: { exclude: ['password'] } - }) + try { + const accounts = await Account.findAll({ + attributes: { exclude: ['password'] } + }) - return accounts + return accounts + } catch (e) { + messageBox('error', ['Cancel'], 2, 'Database Error', 'Failed to get all accounts', JSON.stringify(e)) + server.logger.log({ + level: 'error', + message: 'Failed to get all accounts', + error: e, + timestamp: new Date().toISOString(), + section: 'loginGetAccountsDatabase' + }) + } } // Delete Account export async function deleteAccountID(id: number) { - const account = await Account.destroy({ - where: { - id: id - } - }) + try { + const account = await Account.destroy({ + where: { + id: id + } + }) - return account + return account + } catch (e) { + messageBox('error', ['Cancel'], 2, 'Database Error', 'Failed to delete account', JSON.stringify(e)) + server.logger.log({ + level: 'error', + message: 'Failed to delete account', + error: e, + timestamp: new Date().toISOString(), + section: 'loginDeleteDatabase' + }) + } } // DB Account existence check export async function loggedInCheck(service: string) { - const login = await Account.findOne({ - where: { - service: service - } - }) + try { + const login = await Account.findOne({ + where: { + service: service + } + }) - return login?.get() + return login?.get() + } catch (e) { + messageBox('error', ['Cancel'], 2, 'Database Error', 'Failed to check if logged in', JSON.stringify(e)) + server.logger.log({ + level: 'error', + message: 'Failed to check if logged in', + error: e, + timestamp: new Date().toISOString(), + section: 'loginCheckDatabase' + }) + } } // Save Login Data in DB export async function safeLoginData(user: string, password: string, service: string) { - const login = await Account.create({ - username: user, - password: password, - service: service - }) + try { + const login = await Account.create({ + username: user, + password: password, + service: service + }) - return login?.get() + return login?.get() + } catch (e) { + messageBox('error', ['Cancel'], 2, 'Database Error', 'Failed to save login data', JSON.stringify(e)) + server.logger.log({ + level: 'error', + message: 'Failed to save login data', + error: e, + timestamp: new Date().toISOString(), + section: 'loginSaveDatabase' + }) + } } // Get Playlist export async function getPlaylist() { - const episodes = await Playlist.findAll() + try { + const episodes = await Playlist.findAll() - return episodes + return episodes + } catch (e) { + messageBox('error', ['Cancel'], 2, 'Database Error', 'Failed to get Playlist', JSON.stringify(e)) + server.logger.log({ + level: 'error', + message: 'Failed to get Playlist', + error: e, + timestamp: new Date().toISOString(), + section: 'playlistGetDatabase' + }) + } } // Delete Playlist and TEMP folders After Start async function deletePlaylistandTMP() { - await Playlist.truncate() + try { + await deleteTemporaryFolders() - deleteTemporaryFolders() + console.log('All TMP Folders and Files deleted') + server.logger.log({ + level: 'info', + message: 'All TMP Folders and Files deleted', + timestamp: new Date().toISOString(), + section: 'playlistClearDatabase' + }) + + await Playlist.truncate() + + console.log('Playlist cleared') + server.logger.log({ + level: 'info', + message: 'Playlist cleared', + timestamp: new Date().toISOString(), + section: 'playlistClearDatabase' + }) + } catch (e) { + messageBox('error', ['Cancel'], 2, 'Database Error', 'Failed to delete Playlist and tmp folders', JSON.stringify(e)) + server.logger.log({ + level: 'error', + message: 'Failed to delete Playlist and tmp folders', + error: e, + timestamp: new Date().toISOString(), + section: 'playlistClearDatabase' + }) + } } -deletePlaylistandTMP() +setTimeout(deletePlaylistandTMP, 500) // Update Playlist Item export async function updatePlaylistByID( @@ -87,7 +169,27 @@ export async function updatePlaylistByID( status?: 'waiting' | 'preparing' | 'downloading' | 'merging' | 'decrypting' | 'completed' | 'failed', quality?: 1080 | 720 | 480 | 360 | 240 ) { - await Playlist.update({ status: status, quality: quality }, { where: { id: id } }) + try { + await Playlist.update({ status: status, quality: quality }, { where: { id: id } }) + + server.logger.log({ + level: 'info', + message: `Updated Playlist Item ${id}`, + status: status || undefined, + quality: quality || undefined, + timestamp: new Date().toISOString(), + section: 'playlistItemUpdateDatabase' + }) + } catch (e) { + messageBox('error', ['Cancel'], 2, 'Database Error', 'Failed to update playlist item', JSON.stringify(e)) + server.logger.log({ + level: 'error', + message: 'Failed to update playlist item', + error: e, + timestamp: new Date().toISOString(), + section: 'playlistItemUpdateDatabase' + }) + } } // Add Episode to Playlist @@ -140,44 +242,61 @@ var isDownloading: number = 0 // Check Playlist every 2 seconds for new items async function checkPlaylists() { - const eps = await Playlist.findAll({ where: { status: 'waiting' } }) + try { + const eps = await Playlist.findAll({ where: { status: 'waiting' } }) - for (const e of eps) { - if (isDownloading < 3 && e.dataValues.status === 'waiting') { - updatePlaylistByID(e.dataValues.id, 'preparing') - isDownloading++ - if (e.dataValues.service === 'CR') { - downloadCrunchyrollPlaylist( - (e.dataValues.media as CrunchyEpisode).id, - (e as any).dataValues.dub.map((s: { locale: any }) => s.locale), - (e as any).dataValues.sub.map((s: { locale: any }) => s.locale), - e.dataValues.hardsub, - (e.dataValues.media as CrunchyEpisode).id, - e.dataValues.id, - (e.dataValues.media as CrunchyEpisode).series_title, - (e.dataValues.media as CrunchyEpisode).season_number, - (e.dataValues.media as CrunchyEpisode).episode_number, - e.dataValues.quality, - e.dataValues.dir, - e.dataValues.format, - (e.dataValues.media as CrunchyEpisode).geo - ) - } - if (e.dataValues.service === 'ADN') { - downloadADNPlaylist( - (e.dataValues.media as ADNEpisode).id, - (e as any).dataValues.dub.map((s: { locale: any }) => s.locale), - (e as any).dataValues.sub.map((s: { locale: any }) => s.locale), - e.dataValues.id, - (e.dataValues.media as ADNEpisode).show.title, - (e.dataValues.media as ADNEpisode).season, - (e.dataValues.media as ADNEpisode).shortNumber, - e.dataValues.quality, - e.dataValues.dir, - e.dataValues.format - ) + for (const e of eps) { + if (isDownloading < 3 && e.dataValues.status === 'waiting') { + updatePlaylistByID(e.dataValues.id, 'preparing') + isDownloading++ + if (e.dataValues.service === 'CR') { + server.logger.log({ + level: 'info', + message: `Added Playlist Item ${e.dataValues.id} to Download Process`, + timestamp: new Date().toISOString(), + section: 'playlistCheckCron' + }) + downloadCrunchyrollPlaylist( + (e.dataValues.media as CrunchyEpisode).id, + (e as any).dataValues.dub.map((s: { locale: any }) => s.locale), + (e as any).dataValues.sub.map((s: { locale: any }) => s.locale), + e.dataValues.hardsub, + (e.dataValues.media as CrunchyEpisode).id, + e.dataValues.id, + (e.dataValues.media as CrunchyEpisode).series_title, + (e.dataValues.media as CrunchyEpisode).season_number, + (e.dataValues.media as CrunchyEpisode).episode_number, + e.dataValues.quality, + e.dataValues.dir, + e.dataValues.format, + (e.dataValues.media as CrunchyEpisode).geo + ) + } + if (e.dataValues.service === 'ADN') { + downloadADNPlaylist( + (e.dataValues.media as ADNEpisode).id, + (e as any).dataValues.dub.map((s: { locale: any }) => s.locale), + (e as any).dataValues.sub.map((s: { locale: any }) => s.locale), + e.dataValues.id, + (e.dataValues.media as ADNEpisode).show.title, + (e.dataValues.media as ADNEpisode).season, + (e.dataValues.media as ADNEpisode).shortNumber, + e.dataValues.quality, + e.dataValues.dir, + e.dataValues.format + ) + } } } + } catch (e) { + messageBox('error', ['Cancel'], 2, 'Database Error', 'Failed check Playlist', JSON.stringify(e)) + server.logger.log({ + level: 'error', + message: 'Failed check Playlist', + error: e, + timestamp: new Date().toISOString(), + section: 'playlistCheckCron' + }) } } @@ -299,7 +418,7 @@ export async function downloadADNPlaylist( return } - await mergeVideoFile(file as string, [], subss, seasonFolder, `${name.replace(/[/\\?%*:|"<>]/g, '')} Season ${season} Episode ${episode}`, format) + await mergeVideoFile(file as string, [], subss, seasonFolder, `${name.replace(/[/\\?%*:|"<>]/g, '')} Season ${season} Episode ${episode}`, format, downloadID) await updatePlaylistByID(downloadID, 'completed') @@ -357,6 +476,12 @@ export async function downloadCrunchyrollPlaylist( 'Not found japanese stream', 'This usually happens when Crunchyroll displays JP as dub on a language but its not available. The download will fail, just start a new download and remove JP from dubs' ) + server.logger.log({ + level: 'error', + message: 'Not found japanese stream', + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcess' + }) } } } @@ -413,6 +538,13 @@ export async function downloadCrunchyrollPlaylist( if (!subPlaylist) { await updatePlaylistByID(downloadID, 'failed') console.log('Subtitle Playlist not found') + messageBox('error', ['Cancel'], 2, 'Subtitle Playlist not found', 'Subtitle Playlist not found', 'Subtitle Playlist not found') + server.logger.log({ + level: 'error', + message: 'Subtitle Playlist not found', + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcess' + }) return } @@ -420,8 +552,20 @@ export async function downloadCrunchyrollPlaylist( if (found) { subDownloadList.push({ ...found, isDub: false }) console.log(`Subtitle ${s}.ass found, adding to download`) + server.logger.log({ + level: 'info', + message: `Subtitle ${s}.ass found in Download ${downloadID}, adding to download`, + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcessSubtitles' + }) } else { console.warn(`Subtitle ${s}.ass not found, skipping`) + server.logger.log({ + level: 'warn', + message: `Subtitle ${s}.ass not found in Download ${downloadID}, skipping`, + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcessSubtitles' + }) } await deleteVideoToken(episodeID, playlist.data.token) @@ -507,6 +651,13 @@ export async function downloadCrunchyrollPlaylist( console.log(playlist.mediaGroups.AUDIO.audio.main.playlists[0].segments[0].uri) console.log('No AssetID found, exiting.') await updatePlaylistByID(downloadID, 'failed') + messageBox('error', ['Cancel'], 2, 'No AssetID found', 'No AssetID found', "No AssetID found, can't download MPD.") + server.logger.log({ + level: 'error', + message: `No AssetID found, can't download MPD of Download ${downloadID}`, + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcessAudio' + }) return } @@ -539,6 +690,13 @@ export async function downloadCrunchyrollPlaylist( 'Audio Widevine encrypted but no key provided', 'To download Widevine encrypted videos add the L3 Widevine keys in Settings > Widewine > L3 Keys' ) + server.logger.log({ + level: 'error', + message: `Audio Widevine encrypted but no key provided in Download ${downloadID}`, + error: 'To download Widevine encrypted videos add the L3 Widevine keys in Settings > Widewine > L3 Keys', + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcessVideo' + }) return } p.push({ @@ -578,6 +736,14 @@ export async function downloadCrunchyrollPlaylist( if (!code) { await updatePlaylistByID(downloadID, 'failed') console.log('No Clean stream found') + messageBox('error', ['Cancel'], 2, 'No Clean video stream found', 'No Clean video stream found', 'No Clean video stream found') + server.logger.log({ + level: 'error', + message: `No Clean video stream found in Download ${downloadID}`, + stream: code, + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcessVideo' + }) return } @@ -586,6 +752,13 @@ export async function downloadCrunchyrollPlaylist( if (!play) { await updatePlaylistByID(downloadID, 'failed') console.log('Failed to get Playlist in download Video') + messageBox('error', ['Cancel'], 2, 'Failed to get Playlist in download Video', 'Failed to get Playlist in download Video', 'Failed to get Playlist in download Video') + server.logger.log({ + level: 'error', + message: `Failed to get Playlist in download Video in Download ${downloadID}`, + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcessVideo' + }) return } @@ -632,6 +805,13 @@ export async function downloadCrunchyrollPlaylist( `Resolution ${quality}p not found, using resolution ${mdp.playlists[0].attributes.RESOLUTION?.height}p instead` ) + server.logger.log({ + level: 'warn', + message: `Resolution ${quality}p not found in Download ${downloadID}, using resolution ${mdp.playlists[0].attributes.RESOLUTION?.height}p instead`, + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcessVideo' + }) + await updatePlaylistByID(downloadID, undefined, mdp.playlists[0].attributes.RESOLUTION?.height as 1080 | 720 | 480 | 360 | 240) hq = mdp.playlists[0] @@ -640,7 +820,15 @@ export async function downloadCrunchyrollPlaylist( const assetId = hq.segments[0].resolvedUri.match(/\/assets\/(?:p\/)?([^_,]+)/) if (!assetId) { + await updatePlaylistByID(downloadID, 'failed') console.log('No AssetID found, exiting.') + messageBox('error', ['Cancel'], 2, 'No AssetID found', 'No AssetID found', 'No AssetID found in Video Playlist') + server.logger.log({ + level: 'error', + message: `No AssetID found in Video Playlist in Download ${downloadID}`, + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcessVideo' + }) return } @@ -649,7 +837,23 @@ export async function downloadCrunchyrollPlaylist( if (hq.contentProtection) { if (!hq.contentProtection['com.widevine.alpha'].pssh) { + await updatePlaylistByID(downloadID, 'failed') console.log('No PSSH found, exiting.') + messageBox( + 'error', + ['Cancel'], + 2, + 'Encryption Detect error', + 'Encryption Detect error', + 'Video file is decrypted, but it looks like not with widevine. Stopping Download. Contact Developer' + ) + server.logger.log({ + level: 'error', + message: `Video file is decrypted, but it looks like not with widevine in Download ${downloadID}`, + error: 'No PSSH found', + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcessVideoDecryption' + }) return } pssh = Uint8ArrayToBase64(hq.contentProtection['com.widevine.alpha'].pssh) @@ -663,10 +867,16 @@ export async function downloadCrunchyrollPlaylist( 'error', ['Cancel'], 2, - 'Audio Widevine encrypted but no key provided', - 'Audio Widevine encrypted but no key provided', + 'Video Widevine encrypted but no key provided', + 'Video Widevine encrypted but no key provided', 'To download Widevine encrypted videos add the L3 Widevine keys in Settings > Widewine > L3 Keys' ) + server.logger.log({ + level: 'error', + message: `Video Widevine encrypted but no key provided in Download ${downloadID}`, + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcessVideoDecryption' + }) return } @@ -699,7 +909,7 @@ export async function downloadCrunchyrollPlaylist( if (!audios) return - await mergeVideoFile(file as string, audios, subss, seasonFolder, `${name.replace(/[/\\?%*:|"<>]/g, '')} Season ${season} Episode ${episode}`, format) + await mergeVideoFile(file as string, audios, subss, seasonFolder, `${name.replace(/[/\\?%*:|"<>]/g, '')} Season ${season} Episode ${episode}`, format, downloadID) await updatePlaylistByID(downloadID, 'completed') @@ -754,7 +964,20 @@ async function downloadParts(parts: { filename: string; url: string }[], downloa success = true } catch (error) { console.error(`Error occurred during download of fragment ${index + 1}:`, error) + server.logger.log({ + level: 'error', + message: `Error occurred during download of fragment ${index + 1}`, + error: error, + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcessVideoDownload' + }) console.log(`Retrying download of fragment ${index + 1}...`) + server.logger.log({ + level: 'warn', + message: `Retrying download of fragment ${index + 1} because failed`, + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcessVideoDownload' + }) await new Promise((resolve) => setTimeout(resolve, 5000)) } } @@ -816,10 +1039,19 @@ async function mergeParts(parts: { filename: string; url: string }[], downloadID }) } catch (error) { console.error('Error merging parts:', error) + await updatePlaylistByID(downloadID, 'failed') + messageBox('error', ['Cancel'], 2, 'Error merging video parts', 'Error merging video parts', 'Error merging video parts') + server.logger.log({ + level: 'error', + message: `Error merging video parts of Download ${downloadID}`, + error: error, + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcessVideoMerging' + }) } } -async function mergeVideoFile(video: string, audios: Array, subs: Array, path: string, filename: string, format: 'mp4' | 'mkv') { +async function mergeVideoFile(video: string, audios: Array, subs: Array, path: string, filename: string, format: 'mp4' | 'mkv', downloadID: number) { const locales: Array<{ locale: string name: string @@ -932,6 +1164,15 @@ async function mergeVideoFile(video: string, audios: Array, subs: Array< .saveToFile(path + `/${filename}.${format}`) .on('error', (error) => { console.log(error) + updatePlaylistByID(downloadID, 'failed') + messageBox('error', ['Cancel'], 2, 'Error merging videos and audios', 'Error merging videos and audios', 'Error merging videos and audios') + server.logger.log({ + level: 'error', + message: `Error merging videos and audios of Download ${downloadID}`, + error: error, + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcessVideoMergingFFMPEG' + }) reject(error) }) .on('end', async () => {