diff --git a/API/Controllers/ServerController.cs b/API/Controllers/ServerController.cs
index 46c7dc9f2..ba4258531 100644
--- a/API/Controllers/ServerController.cs
+++ b/API/Controllers/ServerController.cs
@@ -291,4 +291,15 @@ public class ServerController : BaseApiController
return Ok(await _emailService.GetVersion(emailServiceUrl));
}
+ ///
+ /// Checks for updates and pushes an event to the UI
+ ///
+ ///
+ [HttpGet("check-for-updates")]
+ public async Task CheckForAnnouncements()
+ {
+ await _taskScheduler.CheckForUpdate();
+ return Ok();
+ }
+
}
diff --git a/API/Services/TaskScheduler.cs b/API/Services/TaskScheduler.cs
index 09f1a155d..3e1b6cdb2 100644
--- a/API/Services/TaskScheduler.cs
+++ b/API/Services/TaskScheduler.cs
@@ -35,6 +35,7 @@ public interface ITaskScheduler
void ScanSiteThemes();
void CovertAllCoversToEncoding();
Task CleanupDbEntries();
+ Task CheckForUpdate();
}
public class TaskScheduler : ITaskScheduler
@@ -242,14 +243,14 @@ public class TaskScheduler : ITaskScheduler
public void ScheduleUpdaterTasks()
{
_logger.LogInformation("Scheduling Auto-Update tasks");
- RecurringJob.AddOrUpdate(CheckForUpdateId, () => CheckForUpdate(), $"0 */{Rnd.Next(4, 6)} * * *", RecurringJobOptions);
+ RecurringJob.AddOrUpdate(CheckForUpdateId, () => CheckForUpdate(), $"0 */{Rnd.Next(1, 2)} * * *", RecurringJobOptions);
BackgroundJob.Enqueue(() => CheckForUpdate());
}
public void ScanFolder(string folderPath, TimeSpan delay)
{
var normalizedFolder = Tasks.Scanner.Parser.Parser.NormalizePath(folderPath);
- if (HasAlreadyEnqueuedTask(ScannerService.Name, "ScanFolder", new object[] { normalizedFolder }))
+ if (HasAlreadyEnqueuedTask(ScannerService.Name, "ScanFolder", [normalizedFolder]))
{
_logger.LogInformation("Skipped scheduling ScanFolder for {Folder} as a job already queued",
normalizedFolder);
diff --git a/UI/Web/hash-localization.js b/UI/Web/hash-localization.js
index 48d51761a..0d1621d90 100644
--- a/UI/Web/hash-localization.js
+++ b/UI/Web/hash-localization.js
@@ -15,13 +15,11 @@ function generateChecksum(str, algorithm, encoding) {
const result = {};
glob.sync(`${jsonFilesDir}**/*.json`).forEach(path => {
- console.log('Calculating hash for ', path);
let tokens = path.split('dist\\browser\\assets\\langs\\');
if (tokens.length === 1) {
tokens = path.split('dist/browser/assets/langs/');
}
const lang = tokens[1];
- console.log('Language: ', lang);
const content = fs.readFileSync(path, { encoding: 'utf-8' });
result[lang.replace('.json', '')] = generateChecksum(content);
});
diff --git a/UI/Web/src/app/_services/server.service.ts b/UI/Web/src/app/_services/server.service.ts
index 3f3a88a25..803857554 100644
--- a/UI/Web/src/app/_services/server.service.ts
+++ b/UI/Web/src/app/_services/server.service.ts
@@ -41,6 +41,10 @@ export class ServerService {
return this.httpClient.get(this.baseUrl + 'server/check-update', {});
}
+ checkForUpdates() {
+ return this.httpClient.get(this.baseUrl + 'server/check-for-updates', {});
+ }
+
getChangelog() {
return this.httpClient.get(this.baseUrl + 'server/changelog', {});
}
diff --git a/UI/Web/src/app/app.component.ts b/UI/Web/src/app/app.component.ts
index 85a43eb6d..94c79b1a6 100644
--- a/UI/Web/src/app/app.component.ts
+++ b/UI/Web/src/app/app.component.ts
@@ -7,11 +7,12 @@ import { NavService } from './_services/nav.service';
import { filter } from 'rxjs/operators';
import {NgbModal, NgbModalConfig, NgbOffcanvas, NgbRatingConfig} from '@ng-bootstrap/ng-bootstrap';
import { DOCUMENT, NgClass, NgIf, AsyncPipe } from '@angular/common';
-import { Observable } from 'rxjs';
+import {interval, Observable, switchMap} from 'rxjs';
import {ThemeService} from "./_services/theme.service";
import { SideNavComponent } from './sidenav/_components/side-nav/side-nav.component';
import {NavHeaderComponent} from "./nav/_components/nav-header/nav-header.component";
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
+import {ServerService} from "./_services/server.service";
@Component({
selector: 'app-root',
@@ -28,6 +29,7 @@ export class AppComponent implements OnInit {
private readonly offcanvas = inject(NgbOffcanvas);
public readonly navService = inject(NavService);
public readonly cdRef = inject(ChangeDetectorRef);
+ public readonly serverService = inject(ServerService);
constructor(private accountService: AccountService,
private libraryService: LibraryService,
@@ -95,6 +97,14 @@ export class AppComponent implements OnInit {
this.libraryService.getLibraryNames().pipe(take(1), shareReplay({refCount: true, bufferSize: 1})).subscribe();
// On load, make an initial call for valid license
this.accountService.hasValidLicense().subscribe();
+
+ interval(4 * 60 * 60 * 1000) // 4 hours in milliseconds
+ .pipe(
+ switchMap(() => this.accountService.currentUser$),
+ filter(u => this.accountService.hasAdminRole(u!)),
+ switchMap(_ => this.serverService.checkForUpdates())
+ )
+ .subscribe();
}
}
}
diff --git a/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.ts b/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.ts
index 379b68521..515d9dace 100644
--- a/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.ts
+++ b/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.ts
@@ -440,10 +440,6 @@ export class EditSeriesModalComponent implements OnInit {
return a.isoCode == b.isoCode;
}
- if (this.metadata.language === undefined || this.metadata.language === null || this.metadata.language === '') {
- this.metadata.language = 'en';
- }
-
const l = this.validLanguages.find(l => l.isoCode === this.metadata.language);
if (l !== undefined) {
this.languageSettings.savedData = l;
diff --git a/UI/Web/src/app/dashboard/_components/dashboard.component.ts b/UI/Web/src/app/dashboard/_components/dashboard.component.ts
index 0475cf9fe..ce011ceb7 100644
--- a/UI/Web/src/app/dashboard/_components/dashboard.component.ts
+++ b/UI/Web/src/app/dashboard/_components/dashboard.component.ts
@@ -32,7 +32,7 @@ import {StreamType} from "../../_models/dashboard/stream-type.enum";
import {LoadingComponent} from "../../shared/loading/loading.component";
import {ScrobbleProvider, ScrobblingService} from "../../_services/scrobbling.service";
import {ToastrService} from "ngx-toastr";
-
+import {ServerService} from "../../_services/server.service";
enum StreamId {
OnDeck,
@@ -41,6 +41,7 @@ enum StreamId {
MoreInGenre,
}
+
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
@@ -67,6 +68,7 @@ export class DashboardComponent implements OnInit {
private readonly dashboardService = inject(DashboardService);
private readonly scrobblingService = inject(ScrobblingService);
private readonly toastr = inject(ToastrService);
+ private readonly serverService = inject(ServerService);
libraries$: Observable = this.libraryService.getLibraries().pipe(take(1), takeUntilDestroyed(this.destroyRef))
isLoadingDashboard = true;
diff --git a/openapi.json b/openapi.json
index d3396ce87..fb7dca492 100644
--- a/openapi.json
+++ b/openapi.json
@@ -7,7 +7,7 @@
"name": "GPL-3.0",
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
},
- "version": "0.7.12.5"
+ "version": "0.7.12.7"
},
"servers": [
{
@@ -9869,6 +9869,19 @@
}
}
},
+ "/api/Server/check-for-updates": {
+ "get": {
+ "tags": [
+ "Server"
+ ],
+ "summary": "Checks for updates and pushes an event to the UI",
+ "responses": {
+ "200": {
+ "description": "Success"
+ }
+ }
+ }
+ },
"/api/Settings/base-url": {
"get": {
"tags": [