diff --git a/client/package-lock.json b/client/package-lock.json index f86236d..1ecf354 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,6 +1,6 @@ { "name": "pod-noms.web", - "version": "0.13.0", + "version": "0.16.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -522,14 +522,6 @@ "moment": "2.21.0" } }, - "angulartics2": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/angulartics2/-/angulartics2-5.1.2.tgz", - "integrity": "sha512-ZTmSXH6t7Set8TT4oY6HcCLr7bIbK56oJnUVlDJNXSnCTS2DeZW0lP/4pcGpdELCTAF5xfosjar+DM+7kqWiPQ==", - "requires": { - "tslib": "1.9.0" - } - }, "ansi-html": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", diff --git a/client/package.json b/client/package.json index 199949d..6e342e9 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "pod-noms.web", - "version": "0.15.0", + "version": "0.16.0", "license": "MIT", "scripts": { "ng": "ng", diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 0a23796..1a7e742 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -2,7 +2,6 @@ import { GlobalsService } from './services/globals.service'; import { Component, HostBinding } from '@angular/core'; import { Store } from '@ngrx/store'; import { AuthService } from 'app/services/auth.service'; -import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; @Component({ selector: 'app-root', diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index 8d9fa91..3448dfb 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -48,6 +48,7 @@ import { ResetComponent } from './components/reset/reset.component'; import { ProfileComponent } from './components/profile/profile.component'; import { AboutComponent } from './components/about/about.component'; import { FooterComponent } from './components/footer/footer.component'; +import { JobsService } from 'app/services/jobs.service'; export function authHttpServiceFactory(http: Http, options: RequestOptions) { return new AuthHttp( @@ -122,6 +123,7 @@ export function authHttpServiceFactory(http: Http, options: RequestOptions) { PodcastService, ImageService, DebugService, + JobsService, GlobalsService ], bootstrap: [AppComponent] diff --git a/client/src/app/components/debug/debug.component.html b/client/src/app/components/debug/debug.component.html index 5385721..b175847 100644 --- a/client/src/app/components/debug/debug.component.html +++ b/client/src/app/components/debug/debug.component.html @@ -20,6 +20,17 @@ +
+
+
+ Jobs +
+
+ +
+
+
@@ -30,11 +41,11 @@
- API Host: {{apiHost}}
- SignalR Host: {{signalrHost}}
+ API Host: {{apiHost}}
+ SignalR Host: {{signalrHost}}
Ping: {{pingPong}}
- \ No newline at end of file + diff --git a/client/src/app/components/debug/debug.component.ts b/client/src/app/components/debug/debug.component.ts index d51a37d..16eab78 100644 --- a/client/src/app/components/debug/debug.component.ts +++ b/client/src/app/components/debug/debug.component.ts @@ -3,6 +3,7 @@ import { SignalRService } from 'app/services/signalr.service'; import { Component, OnInit } from '@angular/core'; import { DebugService } from 'app/services/debug.service'; import { environment } from 'environments/environment'; +import { JobsService } from 'app/services/jobs.service'; @Component({ selector: 'app-debug', @@ -18,7 +19,8 @@ export class DebugComponent implements OnInit { signalrHost = environment.SIGNALR_HOST; pingPong = ''; - constructor(private _debugService: DebugService, private _signalrService: SignalRService) {} + constructor(private _debugService: DebugService, private _jobsService: JobsService, + private _signalrService: SignalRService) {} ngOnInit() { this._signalrService .init(`${environment.SIGNALR_HOST}hubs/debug`) @@ -41,4 +43,9 @@ export class DebugComponent implements OnInit { doSomething() { alert('doSomething was did'); } + + processOrphans(){ + this._jobsService.processOrphans() + .subscribe(e => console.log('debug.component.ts', 'processOrphans', e)); + } } diff --git a/client/src/app/components/navbar/navbar.component.html b/client/src/app/components/navbar/navbar.component.html index 69922c9..81014ec 100644 --- a/client/src/app/components/navbar/navbar.component.html +++ b/client/src/app/components/navbar/navbar.component.html @@ -4,6 +4,9 @@ Add Podcast +
- \ No newline at end of file + diff --git a/client/src/app/components/podcast/podcast-add-url-form/podcast-add-url-form.component.ts b/client/src/app/components/podcast/podcast-add-url-form/podcast-add-url-form.component.ts index 7e110f2..7cf7740 100644 --- a/client/src/app/components/podcast/podcast-add-url-form/podcast-add-url-form.component.ts +++ b/client/src/app/components/podcast/podcast-add-url-form/podcast-add-url-form.component.ts @@ -17,6 +17,7 @@ import { PodcastService } from 'app/services/podcast.service'; export class PodcastAddUrlFormComponent implements AfterViewInit { @Input() podcast: PodcastModel; @Output() onUrlAddComplete: EventEmitter = new EventEmitter(); + @Output() onUploadDeferred: EventEmitter = new EventEmitter(); newEntrySourceUrl: string; errorText: string; isPosting: boolean = false; @@ -36,20 +37,23 @@ export class PodcastAddUrlFormComponent implements AfterViewInit { this.errorText = ''; if (this.isValidURL(urlToCheck)) { this.isPosting = true; - const entry = new PodcastEntryModel( - this.podcast.id, - urlToCheck - ); - this._service.addEntry(entry) - .subscribe(e => { + const entry = new PodcastEntryModel(this.podcast.id, urlToCheck); + this._service.addEntry(entry).subscribe( + e => { if (e) { - this.onUrlAddComplete.emit(e); + if (e.ProcessingStatus == 6) { + this.onUploadDeferred.emit(e); + } else { + this.onUrlAddComplete.emit(e); + } } - }, (err) => { + }, + err => { this.isPosting = false; this.errorText = 'This does not look like a valid URL'; this.newEntrySourceUrl = urlToCheck; - }); + } + ); } else { this.isPosting = false; this.errorText = 'This does not look like a valid URL'; diff --git a/client/src/app/components/podcast/podcast.component.html b/client/src/app/components/podcast/podcast.component.html index bd0bb75..7ffbc20 100644 --- a/client/src/app/components/podcast/podcast.component.html +++ b/client/src/app/components/podcast/podcast.component.html @@ -45,7 +45,8 @@ + (onUrlAddComplete)="onUrlAddComplete($event)" + (onUploadDeferred)="onEntryUploadDeferred($event)"> diff --git a/client/src/app/components/podcast/podcast.component.ts b/client/src/app/components/podcast/podcast.component.ts index e042ad1..5882e4d 100644 --- a/client/src/app/components/podcast/podcast.component.ts +++ b/client/src/app/components/podcast/podcast.component.ts @@ -7,7 +7,7 @@ import { AppComponent } from 'app/app.component'; import { Store } from '@ngrx/store'; import { ApplicationState } from 'app/store'; import { HostListener } from '@angular/core'; -import {Location} from '@angular/common'; +import { Location } from '@angular/common'; import { UpdateAction, AddAction } from 'app/actions/entries.actions'; import * as fromPodcast from 'app/reducers'; @@ -34,7 +34,11 @@ export class PodcastComponent { } } - constructor(private _store: Store, route: ActivatedRoute, private _location: Location) { + constructor( + private _store: Store, + route: ActivatedRoute, + private _location: Location + ) { this.selectedPodcast$ = _store.select(fromPodcast.getSelectedPodcast); this.entries$ = _store.select(fromPodcast.getEntries); @@ -48,8 +52,12 @@ export class PodcastComponent { this.selectedPodcast$.subscribe(r => { if (r) { slug = r.slug; - _store.dispatch(new fromEntriesActions.LoadAction(slug)); - _store.dispatch(new fromPodcastActions.SelectAction(slug)); + _store.dispatch( + new fromEntriesActions.LoadAction(slug) + ); + _store.dispatch( + new fromPodcastActions.SelectAction(slug) + ); this._location.go('/podcasts/' + slug); } @@ -79,6 +87,9 @@ export class PodcastComponent { // so do a funky success/update dance this._store.dispatch(new fromEntriesActions.AddSuccessAction(entry)); this._store.dispatch(new fromEntriesActions.UpdateAction(entry)); + } + onEntryUploadDeferred($event) { + } onUrlAddComplete(entry: PodcastEntryModel) { this.urlMode = false; diff --git a/client/src/app/models/podcasts.models.ts b/client/src/app/models/podcasts.models.ts old mode 100755 new mode 100644 diff --git a/client/src/app/models/profile.model.ts b/client/src/app/models/profile.model.ts old mode 100755 new mode 100644 diff --git a/client/src/app/services/auth.guard.ts b/client/src/app/services/auth.guard.ts old mode 100755 new mode 100644 diff --git a/client/src/app/services/auth.service.ts b/client/src/app/services/auth.service.ts old mode 100755 new mode 100644 diff --git a/client/src/app/services/jobs.service.ts b/client/src/app/services/jobs.service.ts new file mode 100644 index 0000000..12f52e2 --- /dev/null +++ b/client/src/app/services/jobs.service.ts @@ -0,0 +1,14 @@ +import { Injectable } from '@angular/core'; +import { Response } from '@angular/http'; +import { AuthHttp } from 'angular2-jwt'; +import { Observable } from 'rxjs/Observable'; +import { environment } from 'environments/environment'; + +@Injectable() +export class JobsService { + constructor(private _http: AuthHttp) {} + + processOrphans(): Observable { + return this._http.get(environment.API_HOST + '/job/processorphans'); + } +} diff --git a/client/src/app/services/profile.service.ts b/client/src/app/services/profile.service.ts old mode 100755 new mode 100644 diff --git a/client/src/app/services/pusher.service.ts b/client/src/app/services/pusher.service.ts old mode 100755 new mode 100644 diff --git a/client/src/assets/img/logo-icon.png b/client/src/assets/img/logo-icon.png old mode 100755 new mode 100644 diff --git a/client/src/assets/img/robothand.jpg b/client/src/assets/img/robothand.jpg old mode 100755 new mode 100644 diff --git a/client/src/environments/environment.prod.ts b/client/src/environments/environment.prod.ts old mode 100755 new mode 100644 diff --git a/client/src/environments/environment.ts b/client/src/environments/environment.ts old mode 100755 new mode 100644 diff --git a/client/upgrade_cli b/client/upgrade_cli old mode 100755 new mode 100644 diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml old mode 100755 new mode 100644 index 15fb312..b3eb6b5 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,7 +1,7 @@ version: '2' services: nginx: - restart: "always" + # restart: "always" build: ./nginx/ ports: - "80:80" diff --git a/docker/letsencrypt.sh b/docker/letsencrypt.sh old mode 100755 new mode 100644 index cb6df75..b00b435 --- a/docker/letsencrypt.sh +++ b/docker/letsencrypt.sh @@ -13,6 +13,19 @@ if [[ $web == "Y" || $web == "y" ]]; then fi echo \n +read -n1 -p "Renew api? [y,n]" api +if [[ $api == "Y" || $api == "y" ]]; then + docker run -it --rm -p 443:443 -p 80:80 --name certbot \ + -v "/etc/letsencrypt:/etc/letsencrypt" \ + -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ + certbot/certbot certonly \ + --agree-tos \ + --renew-by-default \ + -d api.podnoms.com \ + -m fergal.moran@gmail.com +fi +echo \n + read -n1 -p "Renew realtime? [y,n]" rt if [[ $rt == "Y" || $rt == "y" ]]; then docker run -it --rm -p 443:443 -p 80:80 --name certbot \ diff --git a/docker/nginx/conf.d/podnoms.conf b/docker/nginx/conf.d/podnoms.conf index e6feece..f8424d6 100644 --- a/docker/nginx/conf.d/podnoms.conf +++ b/docker/nginx/conf.d/podnoms.conf @@ -42,6 +42,31 @@ server { } } +server { + listen 80; + listen 443 ssl; + + server_name api.podnoms.com; + + ssl_certificate /etc/letsencrypt/live/api.podnoms.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/api.podnoms.com/privkey.pem; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_prefer_server_ciphers on; + ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; + + if ($ssl_protocol = "") { + rewrite ^((?!/rss/).) https://$server_name$request_uri? permanent; + } + + location / { + proxy_pass http://api:5000/; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} + server { listen 80; listen 443 ssl; @@ -79,7 +104,7 @@ server { ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; location / { - proxy_pass http://api:5000/hangfire; + proxy_pass http://api:5000/; } } diff --git a/server/.gitignore b/server/.gitignore old mode 100755 new mode 100644 diff --git a/server/Controllers/EntryController.cs b/server/Controllers/EntryController.cs index f0d953e..cd2d8dd 100644 --- a/server/Controllers/EntryController.cs +++ b/server/Controllers/EntryController.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.Options; using PodNoms.Api.Models; using PodNoms.Api.Models.ViewModels; using PodNoms.Api.Persistence; +using PodNoms.Api.Services; using PodNoms.Api.Services.Processor; using PodNoms.Api.Services.Storage; @@ -67,18 +68,24 @@ namespace PodNoms.Api.Controllers { // first check url is valid var entry = _mapper.Map(item); var podcast = await _podcastRepository.GetAsync(item.PodcastId); - if (podcast != null && await _processor.GetInformation(entry)) { - if (entry.ProcessingStatus == ProcessingStatus.Processing) { - if (string.IsNullOrEmpty(entry.ImageUrl)) { - entry.ImageUrl = $"{_storageSettings.CdnUrl}static/images/default-entry.png"; + if (podcast != null) { + var status = await _processor.GetInformation(entry); + if (status == AudioType.Valid) { + if (entry.ProcessingStatus == ProcessingStatus.Processing) { + if (string.IsNullOrEmpty(entry.ImageUrl)) { + entry.ImageUrl = $"{_storageSettings.CdnUrl}static/images/default-entry.png"; + } + entry.Podcast = podcast; + entry.Processed = false; + await _repository.AddOrUpdateAsync(entry); + await _unitOfWork.CompleteAsync(); + _processEntry(entry); + var result = _mapper.Map(entry); + return result; } - entry.Podcast = podcast; - entry.Processed = false; - await _repository.AddOrUpdateAsync(entry); - await _unitOfWork.CompleteAsync(); - _processEntry(entry); - var result = _mapper.Map(entry); - return result; + } else if (status == AudioType.Playlist) { + entry.ProcessingStatus = ProcessingStatus.Deferred; + return Accepted(entry); } } return BadRequest(); diff --git a/server/Controllers/PodcastController.cs b/server/Controllers/PodcastController.cs old mode 100755 new mode 100644 diff --git a/server/Controllers/ProfileController.cs b/server/Controllers/ProfileController.cs old mode 100755 new mode 100644 diff --git a/server/Controllers/RssController.cs b/server/Controllers/RssController.cs old mode 100755 new mode 100644 diff --git a/server/Dockerfile b/server/Dockerfile old mode 100755 new mode 100644 diff --git a/server/Models/AppSettings.cs b/server/Models/AppSettings.cs old mode 100755 new mode 100644 diff --git a/server/Models/BaseModel.cs b/server/Models/BaseModel.cs old mode 100755 new mode 100644 diff --git a/server/Models/Podcast.cs b/server/Models/Podcast.cs old mode 100755 new mode 100644 diff --git a/server/Models/PodcastEntry.cs b/server/Models/PodcastEntry.cs old mode 100755 new mode 100644 index 69c9b79..e992a50 --- a/server/Models/PodcastEntry.cs +++ b/server/Models/PodcastEntry.cs @@ -7,7 +7,8 @@ namespace PodNoms.Api.Models { Processing, //2 Uploading, //3 Processed, //4 - Failed //5 + Failed, //5 + Deferred //6 } public class PodcastEntry : BaseModel { diff --git a/server/Models/User.cs b/server/Models/User.cs old mode 100755 new mode 100644 diff --git a/server/Models/ViewModels/ProfileViewModel.cs b/server/Models/ViewModels/ProfileViewModel.cs old mode 100755 new mode 100644 diff --git a/server/Models/ViewModels/RssViewModels/PodcastRssViewModel.cs b/server/Models/ViewModels/RssViewModels/PodcastRssViewModel.cs old mode 100755 new mode 100644 diff --git a/server/Persistence/IPodcastRepository.cs b/server/Persistence/IPodcastRepository.cs old mode 100755 new mode 100644 diff --git a/server/Persistence/IUserRepository.cs b/server/Persistence/IUserRepository.cs old mode 100755 new mode 100644 diff --git a/server/Persistence/PodcastRepository.cs b/server/Persistence/PodcastRepository.cs old mode 100755 new mode 100644 diff --git a/server/Persistence/PodnomsContext.cs b/server/Persistence/PodnomsContext.cs old mode 100755 new mode 100644 diff --git a/server/Persistence/UserRepository.cs b/server/Persistence/UserRepository.cs old mode 100755 new mode 100644 diff --git a/server/Resources/podcast.xml b/server/Resources/podcast.xml old mode 100755 new mode 100644 diff --git a/server/Services/Auth/AuthenticationMiddleware.cs b/server/Services/Auth/AuthenticationMiddleware.cs old mode 100755 new mode 100644 diff --git a/server/Services/Downloader/AudioDownloader.cs b/server/Services/Downloader/AudioDownloader.cs index 370f5ed..8a99654 100644 --- a/server/Services/Downloader/AudioDownloader.cs +++ b/server/Services/Downloader/AudioDownloader.cs @@ -13,6 +13,7 @@ using static NYoutubeDL.Helpers.Enums; namespace PodNoms.Api.Services.Downloader { public class AudioDownloader { + private readonly string _url; private readonly string _downloader; public VideoDownloadInfo Properties { get; private set; } @@ -49,17 +50,22 @@ namespace PodNoms.Api.Services.Downloader { return $"{{\"Error\": \"{ex.Message}\"}}"; } } - public async Task GetInfo() { - var ret = false; + public async Task GetInfo() { + var ret = AudioType.Invalid; await Task.Run(() => { var youtubeDl = new YoutubeDL(); youtubeDl.VideoUrl = this._url; - var info = youtubeDl.GetDownloadInfo() as VideoDownloadInfo; - ret = ( - info != null && - info is VideoDownloadInfo && //make sure it's not a playlist - (info.Errors.Count == 0 || info.VideoSize != null)); - if (ret) this.Properties = info; + DownloadInfo info = youtubeDl.GetDownloadInfo(); + + if (info != null && + (info.Errors.Count == 0 || info.VideoSize != null)) { + if (info is PlaylistDownloadInfo) { + ret = AudioType.Playlist; + } else if (info is VideoDownloadInfo) { + ret = AudioType.Valid; + this.Properties = (VideoDownloadInfo)info; + } + } }); return ret; } diff --git a/server/Services/Jobs/ClearOrphanAudioJob.cs b/server/Services/Jobs/ClearOrphanAudioJob.cs index effff73..4bdddfc 100644 --- a/server/Services/Jobs/ClearOrphanAudioJob.cs +++ b/server/Services/Jobs/ClearOrphanAudioJob.cs @@ -7,18 +7,15 @@ using Microsoft.WindowsAzure.Storage.Blob; using PodNoms.Api.Models; using PodNoms.Api.Persistence; -namespace PodNoms.Api.Services.Jobs -{ - public class ClearOrphanAudioJob : IJob - { +namespace PodNoms.Api.Services.Jobs { + public class ClearOrphanAudioJob : IJob { public readonly IEntryRepository _entryRepository; public readonly StorageSettings _storageSettings; public readonly AudioFileStorageSettings _audioStorageSettings; private readonly ILogger _logger; public ClearOrphanAudioJob(IEntryRepository entryRepository, IOptions storageSettings, - IOptions audioStorageSettings, ILoggerFactory logger) - { + IOptions audioStorageSettings, ILoggerFactory logger) { this._storageSettings = storageSettings.Value; this._audioStorageSettings = audioStorageSettings.Value; this._entryRepository = entryRepository; @@ -26,38 +23,28 @@ namespace PodNoms.Api.Services.Jobs this._logger = logger.CreateLogger(); } - public async Task Execute() - { - try - { + public async Task Execute() { + try { CloudStorageAccount storageAccount = CloudStorageAccount.Parse(_storageSettings.ConnectionString); CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); CloudBlobContainer container = blobClient.GetContainerReference(_audioStorageSettings.ContainerName); var blobs = await container.ListBlobsSegmentedAsync(null); - foreach (CloudBlockBlob blob in blobs.Results) - { - try - { + foreach (CloudBlockBlob blob in blobs.Results) { + try { Console.WriteLine(blob.StorageUri); var guid = blob.Name.Split('.')[0]; - if (!string.IsNullOrEmpty(guid)) - { + if (!string.IsNullOrEmpty(guid)) { var entry = await _entryRepository.GetByUidAsync(guid); - if (entry == null) - { + if (entry == null) { await blob.DeleteIfExistsAsync(); } } - } - catch (Exception e) - { + } catch (Exception e) { _logger.LogWarning($"Error processing blob {blob.Uri}\n{e.Message}"); } } - } - catch (Exception ex) - { + } catch (Exception ex) { _logger.LogError($"Error clearing orphans\n{ex.Message}"); } } diff --git a/server/Services/Jobs/JobBootstrapper.cs b/server/Services/Jobs/JobBootstrapper.cs new file mode 100644 index 0000000..8c6dcc1 --- /dev/null +++ b/server/Services/Jobs/JobBootstrapper.cs @@ -0,0 +1,13 @@ +using Hangfire; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using PodNoms.Api.Models; +using PodNoms.Api.Persistence; + +namespace PodNoms.Api.Services.Jobs { + public static class JobBootstrapper { + public static void BootstrapJobs() { + RecurringJob.AddOrUpdate(x => x.Execute(), Cron.Daily(1)); + } + } +} \ No newline at end of file diff --git a/server/Services/Processor/IUrlProcessService.cs b/server/Services/Processor/IUrlProcessService.cs index ad3b585..0319122 100644 --- a/server/Services/Processor/IUrlProcessService.cs +++ b/server/Services/Processor/IUrlProcessService.cs @@ -3,8 +3,9 @@ using PodNoms.Api.Models; namespace PodNoms.Api.Services.Processor { public interface IUrlProcessService { - Task GetInformation (int entryId); - Task GetInformation (PodcastEntry entry); - Task DownloadAudio (int entryId); + + Task GetInformation(int entryId); + Task GetInformation(PodcastEntry entry); + Task DownloadAudio(int entryId); } } \ No newline at end of file diff --git a/server/Services/Processor/UrlProcessService.cs b/server/Services/Processor/UrlProcessService.cs index ec74206..2a3e7e0 100644 --- a/server/Services/Processor/UrlProcessService.cs +++ b/server/Services/Processor/UrlProcessService.cs @@ -37,23 +37,23 @@ namespace PodNoms.Api.Services.Processor { uid, e); } - public async Task GetInformation(int entryId) { + public async Task GetInformation(int entryId) { var entry = await _repository.GetAsync(entryId); if (entry == null || string.IsNullOrEmpty(entry.SourceUrl)) { _logger.LogError("Unable to process item"); - return false; + return AudioType.Invalid; } if (entry.SourceUrl.EndsWith(".mp3") || entry.SourceUrl.EndsWith(".wav") || entry.SourceUrl.EndsWith(".aif")) { - return true; + return AudioType.Valid; } return await GetInformation(entry); } - public async Task GetInformation(PodcastEntry entry) { + public async Task GetInformation(PodcastEntry entry) { var downloader = new AudioDownloader(entry.SourceUrl, _applicationsSettings.Downloader); - await downloader.GetInfo(); - if (downloader.Properties != null) { + var ret = await downloader.GetInfo(); + if (ret == AudioType.Valid) { entry.Title = downloader.Properties?.Title; entry.Description = downloader.Properties?.Description; entry.ImageUrl = downloader.Properties?.Thumbnail; @@ -68,11 +68,8 @@ namespace PodNoms.Api.Services.Processor { _logger.LogDebug("***DOWNLOAD INFO RETRIEVED****\n"); _logger.LogDebug($"Title: {entry.Title}\nDescription: {entry.Description}\nAuthor: {entry.Author}\n"); - - // var pusherResult = await _sendProcessCompleteMessage(entry); - return true; } - return false; + return ret; } public async Task DownloadAudio(int entryId) { var entry = await _repository.GetAsync(entryId); diff --git a/server/Services/RemoteAudioTypes.cs b/server/Services/RemoteAudioTypes.cs new file mode 100644 index 0000000..36c0159 --- /dev/null +++ b/server/Services/RemoteAudioTypes.cs @@ -0,0 +1,7 @@ +namespace PodNoms.Api.Services { + public enum AudioType { + Invalid, + Valid, + Playlist + } +} \ No newline at end of file diff --git a/server/Startup.cs b/server/Startup.cs index 217a2d1..5e31c15 100644 --- a/server/Startup.cs +++ b/server/Startup.cs @@ -39,61 +39,61 @@ namespace PodNoms.Api { public class Startup { public IConfiguration Configuration { get; } - public Startup (IConfiguration configuration) { + public Startup(IConfiguration configuration) { Configuration = configuration; } - public void ConfigureProductionServices (IServiceCollection services) { - ConfigureServices (services); - services.AddHangfire (config => { - config.UseSqlServerStorage (Configuration["ConnectionStrings:DefaultConnection"]); + public void ConfigureProductionServices(IServiceCollection services) { + ConfigureServices(services); + services.AddHangfire(config => { + config.UseSqlServerStorage(Configuration["ConnectionStrings:DefaultConnection"]); }); } - public void ConfigureDevelopmentServices (IServiceCollection services) { - ConfigureServices (services); - services.AddHangfire (config => { - config.UseMemoryStorage (); + public void ConfigureDevelopmentServices(IServiceCollection services) { + ConfigureServices(services); + services.AddHangfire(config => { + config.UseMemoryStorage(); }); } - public void ConfigureServices (IServiceCollection services) { - Console.WriteLine ($"Configuring services: {Configuration.ToString()}"); + public void ConfigureServices(IServiceCollection services) { + Console.WriteLine($"Configuring services: {Configuration.ToString()}"); - services.AddDbContext (options => - options.UseSqlServer (Configuration.GetConnectionString ("DefaultConnection"))); + services.AddDbContext(options => + options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); - services.AddOptions (); - services.Configure (Configuration.GetSection ("App")); - services.Configure (Configuration.GetSection ("Storage")); - services.Configure (Configuration.GetSection ("ApplicationsSettings")); - services.Configure (Configuration.GetSection ("ImageFileStorageSettings")); - services.Configure (Configuration.GetSection ("AudioFileStorageSettings")); - services.Configure (options => { + services.AddOptions(); + services.Configure(Configuration.GetSection("App")); + services.Configure(Configuration.GetSection("Storage")); + services.Configure(Configuration.GetSection("ApplicationsSettings")); + services.Configure(Configuration.GetSection("ImageFileStorageSettings")); + services.Configure(Configuration.GetSection("AudioFileStorageSettings")); + services.Configure(options => { options.ValueCountLimit = 10; options.ValueLengthLimit = int.MaxValue; options.MemoryBufferThreshold = Int32.MaxValue; options.MultipartBodyLengthLimit = long.MaxValue; }); - services.AddAutoMapper (e => { - e.AddProfile (new MappingProvider (Configuration)); + services.AddAutoMapper(e => { + e.AddProfile(new MappingProvider(Configuration)); }); - services.AddAuthentication (options => { + services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme; - }).AddJwtBearer (options => { + }).AddJwtBearer(options => { options.Audience = Configuration["auth0:clientId"]; options.Authority = $"https://{Configuration["auth0:domain"]}/"; options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name" }; - options.Events = new JwtBearerEvents () { + options.Events = new JwtBearerEvents() { OnTokenValidated = AuthenticationMiddleware.OnTokenValidated }; options.Events.OnMessageReceived = context => { StringValues token; - if (context.Request.Path.Value.StartsWith ("/hubs/") && context.Request.Query.TryGetValue ("token", out token)) { + if (context.Request.Path.Value.StartsWith("/hubs/") && context.Request.Query.TryGetValue("token", out token)) { context.Token = token; } @@ -102,107 +102,107 @@ namespace PodNoms.Api { }); var defaultPolicy = - new AuthorizationPolicyBuilder () - .AddAuthenticationSchemes ("Bearer") - .RequireAuthenticatedUser () - .Build (); + new AuthorizationPolicyBuilder() + .AddAuthenticationSchemes("Bearer") + .RequireAuthenticatedUser() + .Build(); - services.AddAuthorization (j => { + services.AddAuthorization(j => { j.DefaultPolicy = defaultPolicy; }); - services.AddMvc (options => { - options.OutputFormatters.Add (new XmlSerializerOutputFormatter ()); - options.OutputFormatters - .OfType () - .Single ().SupportedMediaTypes.Add ("text/html"); - }) - .SetCompatibilityVersion (CompatibilityVersion.Version_2_1) - .AddJsonOptions (options => { - options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver (); + services.AddMvc(options => { + options.OutputFormatters.Add(new XmlSerializerOutputFormatter()); + options.OutputFormatters + .OfType() + .Single().SupportedMediaTypes.Add("text/html"); + }) + .SetCompatibilityVersion(CompatibilityVersion.Version_2_1) + .AddJsonOptions(options => { + options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize; }) - .AddXmlSerializerFormatters (); + .AddXmlSerializerFormatters(); - services.Configure (x => { + services.Configure(x => { x.ValueLengthLimit = int.MaxValue; x.MultipartBodyLengthLimit = int.MaxValue; // In case of multipart }); - services.AddSignalR (config => { }); + services.AddSignalR(config => { }); - services.AddCors (options => { - options.AddPolicy ("AllowAllOrigins", + services.AddCors(options => { + options.AddPolicy("AllowAllOrigins", builder => builder - .AllowAnyOrigin () - .AllowAnyMethod () - .AllowAnyHeader () - .AllowCredentials ()); + .AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials()); }); - services.AddTransient (); - services.AddTransient (); - services.AddScoped (); - services.AddScoped (); - services.AddScoped (); - services.AddScoped (); - services.AddScoped (); - services.AddScoped (); + services.AddTransient(); + services.AddTransient(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - services.AddSingleton (typeof (HubLifetimeManager), - typeof (DebugHubLifetimeManager)); + services.AddSingleton(typeof(HubLifetimeManager), + typeof(DebugHubLifetimeManager)); //register the codepages (required for slugify) var instance = CodePagesEncodingProvider.Instance; - Encoding.RegisterProvider (instance); + Encoding.RegisterProvider(instance); } - public void Configure (IApplicationBuilder app, IHostingEnvironment env, + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider serviceProvider) { - if (env.IsDevelopment ()) { - app.UseDeveloperExceptionPage (); + if (env.IsDevelopment()) { + app.UseDeveloperExceptionPage(); } else { - app.UseExceptionHandler ("/Home/Error"); + app.UseExceptionHandler("/Home/Error"); } - Console.WriteLine ("Performing migrations"); + Console.WriteLine("Performing migrations"); //TODO: Fix this when EF sucks less // using (var context = new PodnomsDbContext( // app.ApplicationServices.GetRequiredService>())) // { // context.Database.Migrate(); // } - Console.WriteLine ("Successfully migrated"); + Console.WriteLine("Successfully migrated"); // app.UseHsts(); // app.UseHttpsRedirection(); - app.UseStaticFiles (); + app.UseStaticFiles(); - GlobalConfiguration.Configuration.UseActivator (new ServiceProviderActivator (serviceProvider)); + GlobalConfiguration.Configuration.UseActivator(new ServiceProviderActivator(serviceProvider)); - if ((env.IsProduction () || true)) { - app.UseHangfireServer (); - app.UseHangfireDashboard ("/hangfire", new DashboardOptions { - Authorization = new [] { new HangFireAuthorizationFilter () } + if ((env.IsProduction() || true)) { + app.UseHangfireServer(); + app.UseHangfireDashboard("/hangfire", new DashboardOptions { + Authorization = new[] { new HangFireAuthorizationFilter() } }); } - app.UseCors ("AllowAllOrigins"); + app.UseCors("AllowAllOrigins"); - app.UseSignalR (routes => { - routes.MapHub ("/hubs/audioprocessing"); - routes.MapHub ("/hubs/debug"); + app.UseSignalR(routes => { + routes.MapHub("/hubs/audioprocessing"); + routes.MapHub("/hubs/debug"); }); - app.UseMvc (routes => { - routes.MapRoute ( + app.UseMvc(routes => { + routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); //start hangfire jobs - //RecurringJob.AddOrUpdate(x => x.Execute(), Cron.Hourly); + JobBootstrapper.BootstrapJobs(); } } } \ No newline at end of file diff --git a/server/Utils/Crypt/HMACGenerator.cs b/server/Utils/Crypt/HMACGenerator.cs old mode 100755 new mode 100644 diff --git a/server/Utils/Crypt/MD5Generator.cs b/server/Utils/Crypt/MD5Generator.cs old mode 100755 new mode 100644 diff --git a/server/Utils/DateUtils.cs b/server/Utils/DateUtils.cs old mode 100755 new mode 100644 diff --git a/server/Utils/Extensions/DateTime.cs b/server/Utils/Extensions/DateTime.cs old mode 100755 new mode 100644 diff --git a/server/Utils/Extensions/String.cs b/server/Utils/Extensions/String.cs old mode 100755 new mode 100644 diff --git a/server/Utils/HttpUtils.cs b/server/Utils/HttpUtils.cs old mode 100755 new mode 100644 diff --git a/server/Utils/HumanFriendlyInteger.cs b/server/Utils/HumanFriendlyInteger.cs old mode 100755 new mode 100644 diff --git a/server/Utils/ImageUtils.cs b/server/Utils/ImageUtils.cs old mode 100755 new mode 100644 diff --git a/server/Utils/Randomisers.cs b/server/Utils/Randomisers.cs old mode 100755 new mode 100644 diff --git a/server/Utils/ResourceReader.cs b/server/Utils/ResourceReader.cs old mode 100755 new mode 100644 diff --git a/server/appsettings.json b/server/appsettings.json index 828e75b..8d28750 100644 --- a/server/appsettings.json +++ b/server/appsettings.json @@ -8,7 +8,7 @@ } }, "App": { - "Version": "0.15.0" + "Version": "0.16.0" }, "ConnectionStrings": { "DefaultConnection": "server=localhost;database=PodNoms;user id=sa;password=cTXu1nJLCpC/c" diff --git a/server/publish.sh b/server/publish.sh index 25d27b5..ce66001 100755 --- a/server/publish.sh +++ b/server/publish.sh @@ -3,3 +3,4 @@ unset DOCKER_HOST unset DOCKER_TLS_VERIFY docker build --rm -f Dockerfile -t fergalmoran/podnoms.api . && docker push fergalmoran/podnoms.api +docker push fergalmoran/podnoms.api diff --git a/server/rebuild.sh b/server/rebuild.sh old mode 100755 new mode 100644 diff --git a/server/resetdb.sh b/server/resetdb.sh old mode 100755 new mode 100644