diff --git a/ui/angular.json b/ui/angular.json index 65b1d1e..801cf09 100644 --- a/ui/angular.json +++ b/ui/angular.json @@ -25,14 +25,18 @@ "aot": true, "assets": [ "src/favicon.ico", - "src/assets" + "src/assets", + "src/manifest.webmanifest", + "src/custom-service-worker.js" ], "styles": [ "src/styles.sass" ], "scripts": [ "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js", - ] + ], + "serviceWorker": true, + "ngswConfigPath": "ngsw-config.json" }, "configurations": { "production": { @@ -90,7 +94,9 @@ "karmaConfig": "karma.conf.js", "assets": [ "src/favicon.ico", - "src/assets" + "src/assets", + "src/manifest.webmanifest", + "src/custom-service-worker.js" ], "styles": [ "src/styles.sass" diff --git a/ui/ngsw-config.json b/ui/ngsw-config.json new file mode 100644 index 0000000..f8bf210 --- /dev/null +++ b/ui/ngsw-config.json @@ -0,0 +1,30 @@ +{ + "$schema": "./node_modules/@angular/service-worker/config/schema.json", + "index": "/index.html", + "assetGroups": [ + { + "name": "app", + "installMode": "prefetch", + "resources": { + "files": [ + "/favicon.ico", + "/index.html", + "/manifest.webmanifest", + "/*.css", + "/*.js" + ] + } + }, + { + "name": "assets", + "installMode": "lazy", + "updateMode": "prefetch", + "resources": { + "files": [ + "/assets/**", + "/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)" + ] + } + } + ] +} diff --git a/ui/package-lock.json b/ui/package-lock.json index f0f62aa..16335a9 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -17,6 +17,7 @@ "@angular/platform-browser": "^15.0.0", "@angular/platform-browser-dynamic": "^15.0.0", "@angular/router": "^15.0.0", + "@angular/service-worker": "^15.0.0", "@fortawesome/angular-fontawesome": "~0.12.0", "@fortawesome/fontawesome-svg-core": "^6.4.0", "@fortawesome/free-regular-svg-icons": "^6.4.0", @@ -642,6 +643,24 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, + "node_modules/@angular/service-worker": { + "version": "15.2.10", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-15.2.10.tgz", + "integrity": "sha512-dTLLt02OwYVZ7U2sat8v/1jxtBNYnps+AZyRVXxpTPvgkljLHphhWuSPv6V8IRxxKF0SU7e6RVrcAMLIPUSVeg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "bin": { + "ngsw-config": "ngsw-config.js" + }, + "engines": { + "node": "^14.20.0 || ^16.13.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/common": "15.2.10", + "@angular/core": "15.2.10" + } + }, "node_modules/@assemblyscript/loader": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", diff --git a/ui/package.json b/ui/package.json index 76ea0f0..fffb2fd 100644 --- a/ui/package.json +++ b/ui/package.json @@ -20,6 +20,7 @@ "@angular/platform-browser": "^15.0.0", "@angular/platform-browser-dynamic": "^15.0.0", "@angular/router": "^15.0.0", + "@angular/service-worker": "^15.0.0", "@fortawesome/angular-fontawesome": "~0.12.0", "@fortawesome/fontawesome-svg-core": "^6.4.0", "@fortawesome/free-regular-svg-icons": "^6.4.0", diff --git a/ui/src/app/app.module.ts b/ui/src/app/app.module.ts index 8eddbca..35e0621 100644 --- a/ui/src/app/app.module.ts +++ b/ui/src/app/app.module.ts @@ -1,5 +1,5 @@ import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; +import { NgModule, isDevMode } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { HttpClientModule } from '@angular/common/http'; @@ -11,6 +11,7 @@ import { EtaPipe, SpeedPipe, EncodeURIComponent } from './downloads.pipe'; import { MasterCheckboxComponent, SlaveCheckboxComponent } from './master-checkbox.component'; import { MeTubeSocket } from './metube-socket'; import { NgSelectModule } from '@ng-select/ng-select'; +import { ServiceWorkerModule } from '@angular/service-worker'; @NgModule({ declarations: [ @@ -27,7 +28,13 @@ import { NgSelectModule } from '@ng-select/ng-select'; NgbModule, HttpClientModule, FontAwesomeModule, - NgSelectModule + NgSelectModule, + ServiceWorkerModule.register('custom-service-worker.js', { + enabled: !isDevMode(), + // Register the ServiceWorker as soon as the application is stable + // or after 30 seconds (whichever comes first). + registrationStrategy: 'registerWhenStable:30000' + }) ], providers: [CookieService, MeTubeSocket], bootstrap: [AppComponent] diff --git a/ui/src/app/metube-socket.ts b/ui/src/app/metube-socket.ts index cfc8fea..65629ae 100644 --- a/ui/src/app/metube-socket.ts +++ b/ui/src/app/metube-socket.ts @@ -3,7 +3,9 @@ import { Socket } from 'ngx-socket-io'; @Injectable() export class MeTubeSocket extends Socket { - constructor() { - super({ url: '', options: {path: document.location.pathname + 'socket.io'} }); - } + constructor() { + const path = + document.location.pathname.replace(/share-target/, '') + 'socket.io'; + super({ url: '', options: { path } }); + } } diff --git a/ui/src/assets/icons/android-chrome-192x192.png b/ui/src/assets/icons/android-chrome-192x192.png new file mode 100644 index 0000000..bc22269 Binary files /dev/null and b/ui/src/assets/icons/android-chrome-192x192.png differ diff --git a/ui/src/assets/icons/android-chrome-384x384.png b/ui/src/assets/icons/android-chrome-384x384.png new file mode 100644 index 0000000..abd6b9d Binary files /dev/null and b/ui/src/assets/icons/android-chrome-384x384.png differ diff --git a/ui/src/custom-service-worker.js b/ui/src/custom-service-worker.js new file mode 100644 index 0000000..5ff616f --- /dev/null +++ b/ui/src/custom-service-worker.js @@ -0,0 +1,38 @@ +const URL_PATTERN = + /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/gi; + +self.addEventListener("fetch", (event) => { + if (event.request.method === "GET") { + const url = new URL(event.request.url); + + if (url.pathname.endsWith("/share-target")) { + const urlRegExp = new RegExp(URL_PATTERN); + const sharedText = url.searchParams.get("text"); + const matches = [...sharedText.matchAll(urlRegExp)].map((m) => m[0]); + + event.respondWith( + (async () => { + await Promise.all( + matches.map((url) => { + return fetch("/add", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + url, + quality: "best", + format: "any", + auto_start: true, + }), + }); + }) + ); + return Response.redirect("/", 303); + })() + ); + } + } +}); + +importScripts("./ngsw-worker.js"); diff --git a/ui/src/index.html b/ui/src/index.html index d57a30b..f620859 100644 --- a/ui/src/index.html +++ b/ui/src/index.html @@ -7,14 +7,15 @@ - - + +