Merge branch 'release/v0.18'

This commit is contained in:
Fergal Moran
2018-03-16 18:41:08 +00:00
241 changed files with 771 additions and 159 deletions

2
.gitignore vendored Normal file → Executable file
View File

@@ -5,3 +5,5 @@ promotion
.working
extension
client/tags
NYoutubeDL
tags

0
client/.angular-cli.json Normal file → Executable file
View File

0
client/.editorconfig Normal file → Executable file
View File

0
client/.gitignore vendored Normal file → Executable file
View File

0
client/.npmrc Normal file → Executable file
View File

0
client/.vscode/launch.json vendored Normal file → Executable file
View File

0
client/README.md Normal file → Executable file
View File

13
client/package-lock.json generated Normal file → Executable file
View File

@@ -1,6 +1,6 @@
{
"name": "pod-noms.web",
"version": "0.16.0",
"version": "0.17.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -309,6 +309,12 @@
}
}
},
"@types/applicationinsights-js": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/applicationinsights-js/-/applicationinsights-js-1.0.5.tgz",
"integrity": "sha512-/obMaLeIWrL6FDYJC8v9VE2BrQJ2Y6iHgT/9UI6Xd30S/x0uDkJv/DGt1wMMJEmu+ShkR2KiR9L9OaJ1d91uoA==",
"dev": true
},
"@types/jasmine": {
"version": "2.8.6",
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.6.tgz",
@@ -568,6 +574,11 @@
"default-require-extensions": "1.0.0"
}
},
"applicationinsights-js": {
"version": "1.0.15",
"resolved": "https://registry.npmjs.org/applicationinsights-js/-/applicationinsights-js-1.0.15.tgz",
"integrity": "sha512-lxO7LOIkK38q3VE8covJMsGS3O04hPGgPaA8BmGyR3SYQhuIYX9qy14KMojqx239TlPR9YuPexVXTHdrGBATow=="
},
"aproba": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",

4
client/package.json Normal file → Executable file
View File

@@ -1,6 +1,6 @@
{
"name": "pod-noms.web",
"version": "0.17.0",
"version": "0.18.0",
"license": "MIT",
"scripts": {
"ng": "ng",
@@ -31,6 +31,7 @@
"@qontu/ngx-inline-editor": "^0.2.0-alpha.12",
"angular2-jwt": "^0.2.3",
"angular2-moment": "^1.8.0",
"applicationinsights-js": "^1.0.15",
"auth0": "^2.9.1",
"auth0-lock": "^11.3.1",
"bootstrap": "4.0.0",
@@ -54,6 +55,7 @@
"@angular/cli": "1.6.8",
"@angular/compiler-cli": "^5.2.6",
"@angular/language-service": "^5.2.6",
"@types/applicationinsights-js": "^1.0.5",
"@types/jasmine": "^2.8.6",
"@types/node": "~9.4.6",
"codelyzer": "^4.2.1",

0
client/src/app/actions/entries.actions.ts Normal file → Executable file
View File

0
client/src/app/actions/podcast.actions.ts Normal file → Executable file
View File

0
client/src/app/actions/profile.actions.ts Normal file → Executable file
View File

0
client/src/app/app.component.css Normal file → Executable file
View File

0
client/src/app/app.component.html Normal file → Executable file
View File

3
client/src/app/app.component.ts Normal file → Executable file
View File

@@ -2,6 +2,7 @@ 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 { AppInsightsService } from 'app/services/app-insights.service';
@Component({
selector: 'app-root',
@@ -9,7 +10,7 @@ import { AuthService } from 'app/services/auth.service';
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(private _authService: AuthService) {
constructor(private _authService: AuthService, _appInsights: AppInsightsService) {
_authService.handleAuthentication();
_authService.scheduleRenewal();
}

2
client/src/app/app.module.ts Normal file → Executable file
View File

@@ -49,6 +49,7 @@ 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';
import { AppInsightsService } from 'app/services/app-insights.service';
export function authHttpServiceFactory(http: Http, options: RequestOptions) {
return new AuthHttp(
@@ -123,6 +124,7 @@ export function authHttpServiceFactory(http: Http, options: RequestOptions) {
PodcastService,
ImageService,
DebugService,
AppInsightsService,
JobsService,
GlobalsService
],

0
client/src/app/app.router.ts Normal file → Executable file
View File

0
client/src/app/components/about/about.component.css Normal file → Executable file
View File

0
client/src/app/components/about/about.component.html Normal file → Executable file
View File

0
client/src/app/components/about/about.component.ts Normal file → Executable file
View File

View File

View File

View File

0
client/src/app/components/debug/debug.component.css Normal file → Executable file
View File

4
client/src/app/components/debug/debug.component.html Normal file → Executable file
View File

@@ -28,6 +28,10 @@
<div class="block-content">
<button class="btn btn-primary"
(click)="processOrphans()">Process Orphans</button>
<button class="btn btn-primary"
(click)="processPlaylists()">Process Playlists</button>
<button class="btn btn-primary"
(click)="updateYouTubeDl()">Update Youtube Downloader</button>
</div>
</div>
</div>

66
client/src/app/components/debug/debug.component.ts Normal file → Executable file
View File

@@ -1,14 +1,14 @@
import { Observable } from 'rxjs/Observable';
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';
import { Observable } from "rxjs/Observable";
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',
templateUrl: './debug.component.html',
styleUrls: ['./debug.component.css']
selector: "app-debug",
templateUrl: "./debug.component.html",
styleUrls: ["./debug.component.css"]
})
export class DebugComponent implements OnInit {
realtimeMessage: string;
@@ -17,35 +17,59 @@ export class DebugComponent implements OnInit {
debugInfo$: Observable<string>;
apiHost = environment.API_HOST;
signalrHost = environment.SIGNALR_HOST;
pingPong = '';
pingPong = "";
constructor(private _debugService: DebugService, private _jobsService: JobsService,
private _signalrService: SignalRService) {}
constructor(
private _debugService: DebugService,
private _jobsService: JobsService,
private _signalrService: SignalRService
) {}
ngOnInit() {
this._signalrService
.init(`${environment.SIGNALR_HOST}hubs/debug`)
.then(() => {
this._signalrService.connection.on('Send', data => {
console.log('DebugService', 'signalr', data);
this._signalrService.connection.on("Send", data => {
console.log("DebugService", "signalr", data);
this.messagesReceived.push(data);
this.realtimeMessage = '';
this.realtimeMessage = "";
});
this.debugInfo$ = this._debugService.getDebugInfo();
})
.catch(err => console.error('debug.component.ts', '_signalrService.init', err));
.catch(err =>
console.error("debug.component.ts", "_signalrService.init", err)
);
this._debugService.ping().subscribe(r => (this.pingPong = r));
}
sendMessage() {
this._debugService.sendRealtime(this.realtimeMessage).subscribe(r => console.log(r));
this._debugService
.sendRealtime(this.realtimeMessage)
.subscribe(r => console.log(r));
}
doSomething() {
alert('doSomething was did');
alert("doSomething was did");
}
processOrphans(){
this._jobsService.processOrphans()
.subscribe(e => console.log('debug.component.ts', 'processOrphans', e));
processOrphans() {
this._jobsService
.processOrphans()
.subscribe(e =>
console.log("debug.component.ts", "processOrphans", e)
);
}
processPlaylists() {
this._jobsService
.processPlaylists()
.subscribe(e =>
console.log("debug.component.ts", "processPlaylists", e)
);
}
updateYouTubeDl() {
this._jobsService
.updateYouTubeDl()
.subscribe(e =>
console.log("debug.component.ts", "updateYouTubeDl", e)
);
}
}

0
client/src/app/components/footer/footer.component.css Normal file → Executable file
View File

0
client/src/app/components/footer/footer.component.html Normal file → Executable file
View File

0
client/src/app/components/footer/footer.component.ts Normal file → Executable file
View File

0
client/src/app/components/home/home.component.css Normal file → Executable file
View File

0
client/src/app/components/home/home.component.html Normal file → Executable file
View File

0
client/src/app/components/home/home.component.ts Normal file → Executable file
View File

0
client/src/app/components/login/login.component.css Normal file → Executable file
View File

0
client/src/app/components/login/login.component.html Normal file → Executable file
View File

0
client/src/app/components/login/login.component.ts Normal file → Executable file
View File

0
client/src/app/components/navbar/navbar.component.css Normal file → Executable file
View File

0
client/src/app/components/navbar/navbar.component.html Normal file → Executable file
View File

0
client/src/app/components/navbar/navbar.component.ts Normal file → Executable file
View File

View File

@@ -8,6 +8,7 @@ import {
ViewChild
} from '@angular/core';
import { PodcastService } from 'app/services/podcast.service';
import { debounceTime } from 'rxjs/operator/debounceTime';
@Component({
selector: 'app-podcast-add-url-form',
@@ -41,7 +42,7 @@ export class PodcastAddUrlFormComponent implements AfterViewInit {
this._service.addEntry(entry).subscribe(
e => {
if (e) {
if (e.ProcessingStatus == 6) {
if (e.processingStatus == 6) {
this.onUploadDeferred.emit(e);
} else {
this.onUrlAddComplete.emit(e);

View File

View File

@@ -1,89 +1,68 @@
<div class="content"
*ngIf="selectedPodcast$ | async; let podcast; else empty">
<div class="content" *ngIf="selectedPodcast$ | async; let podcast; else empty">
<div class="block">
<div class="block-header block-header-default">
<h3 class="block-title">{{podcast.title}}</h3>
<div class="block-options">
<div class="btn-group"
role="group"
aria-label="Podcast actions">
<a [routerLink]="['/podcasts', podcast.slug, 'edit']"
class="btn btn-secondary btn-outline">
<i class="icon-pencil"
aria-hidden="true"></i> Edit
<div class="btn-group" role="group" aria-label="Podcast actions">
<a [routerLink]="['/podcasts', podcast.slug, 'edit']" class="btn btn-secondary btn-outline">
<i class="icon-pencil" aria-hidden="true"></i> Edit
</a>
<a (click)="podcastDeleteDialog.show()"
class="btn btn-secondary btn-outline-danger">
<i class="icon-close"
aria-hidden="true"></i> Delete
<a (click)="podcastDeleteDialog.show()" class="btn btn-secondary btn-outline-danger">
<i class="icon-close" aria-hidden="true"></i> Delete
</a>
<a class="btn btn-secondary"
(click)="startAddEntry()"><i class="icon-plus"></i> Add (from URL)</a>
<a class="btn btn-secondary"
(click)="startUpload()"><i class="icon-cloud-upload"></i> Upload (from computer)</a>
<a class="btn btn-secondary" (click)="startAddEntry()">
<i class="icon-plus"></i> Add (from URL)</a>
<a class="btn btn-secondary" (click)="startUpload()">
<i class="icon-cloud-upload"></i> Upload (from computer)</a>
</div>
<div class="btn-group"
role="group"
aria-label="Podcast actions">
<a class="btn btn-secondary btn-outline-warning"
[href]="podcast.rssUrl"
target="_blank">
<div class="btn-group" role="group" aria-label="Podcast actions">
<a class="btn btn-secondary btn-outline-warning" [href]="podcast.rssUrl" target="_blank">
<i class="text-warning fa fa-rss"></i> Rss Url
</a>
<button class="btn btn-secondary btn-outline-warning"
ngxClipboard
[cbContent]="podcast.rssUrl">
<button class="btn btn-secondary btn-outline-warning" ngxClipboard [cbContent]="podcast.rssUrl">
<i class="fa fa-copy"></i>
</button>
</div>
</div>
</div>
<div class="block-content">
<app-podcast-upload-form *ngIf="uploadMode"
[podcast]="podcast"
(onUploadComplete)="onEntryUploadComplete($event)">
<app-podcast-upload-form *ngIf="uploadMode" [podcast]="podcast" (onUploadComplete)="onEntryUploadComplete($event)">
</app-podcast-upload-form>
<app-podcast-add-url-form *ngIf="urlMode"
[podcast]="podcast"
(onUrlAddComplete)="onUrlAddComplete($event)"
(onUploadDeferred)="onEntryUploadDeferred($event)">
<app-podcast-add-url-form *ngIf="urlMode" [podcast]="podcast" (onUrlAddComplete)="onUrlAddComplete($event)" (onUploadDeferred)="onEntryUploadDeferred($event)">
</app-podcast-add-url-form>
<div class="block block-themed" *ngIf="pendingEntry">
<div class="block-header bg-danger">
<h3 class="block-title">This looks like a playlist? Shall we add all items (please note, this may result in a lot of episodes being downloaded to your device).</h3>
<div class="block-options">
<button type="button" class="btn-block-option" (click)="processPlaylist()">
<i class="fa fa-check"></i>Yes
</button>
<button type="button" class="btn-block-option" (click)="dismissPlaylist()">
<i class="fa fa-times"></i>No
</button>
</div>
</div>
</div>
<table class="js-table-checkable table table-hover js-table-checkable-enabled">
<tbody>
<tr *ngFor="let e of entries$ | async | orderBy : '-id'"
app-entry-list-item
(entryRemoved)='deleteEntry(e)'
[entry]="e">
<tr *ngFor="let e of entries$ | async | orderBy : '-id'" app-entry-list-item (entryRemoved)='deleteEntry(e)' [entry]="e">
</tbody>
</table>
</div>
</div>
<div class="modal fade"
bsModal
#podcastDeleteDialog="bs-modal"
tabindex="-1"
role="dialog"
aria-hidden="true">
<div class="modal fade" bsModal #podcastDeleteDialog="bs-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title pull-left">Delete this podcast?</h4>
<button type="button"
class="close pull-right"
aria-label="Close"
(click)="podcastDeleteDialog.hide()">
<button type="button" class="close pull-right" aria-label="Close" (click)="podcastDeleteDialog.hide()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<button type="button"
class="btn btn-primary pull-right"
(click)="podcastDeleteDialog.hide()">No
<button type="button" class="btn btn-primary pull-right" (click)="podcastDeleteDialog.hide()">No
</button>
<button type="button"
class="btn btn-danger pull-right"
(click)="deletePodcast(podcast); podcastDeleteDialog.hide()">Yes
<button type="button" class="btn btn-danger pull-right" (click)="deletePodcast(podcast); podcastDeleteDialog.hide()">Yes
</button>
</div>
</div>
@@ -98,8 +77,7 @@
</div>
<div class="font-size-h4 font-w600">It's looking a bit empty here?</div>
<div class="pt-20">
<a class="btn btn-rounded btn-alt-primary"
[routerLink]="['/add']">
<a class="btn btn-rounded btn-alt-primary" [routerLink]="['/add']">
<i class="fa fa-cog mr-5"></i> Add my first podcast
</a>
</div>

22
client/src/app/components/podcast/podcast.component.ts Normal file → Executable file
View File

@@ -13,6 +13,7 @@ import { UpdateAction, AddAction } from 'app/actions/entries.actions';
import * as fromPodcast from 'app/reducers';
import * as fromPodcastActions from 'app/actions/podcast.actions';
import * as fromEntriesActions from 'app/actions/entries.actions';
import { PodcastService } from 'app/services/podcast.service';
@Component({
selector: 'app-podcast',
@@ -21,6 +22,7 @@ import * as fromEntriesActions from 'app/actions/entries.actions';
})
export class PodcastComponent {
selectedPodcast$: Observable<PodcastModel>;
pendingEntry: PodcastEntryModel = null;
entries$: Observable<PodcastEntryModel[]>;
uploadMode = false;
urlMode = false;
@@ -36,6 +38,8 @@ export class PodcastComponent {
constructor(
private _store: Store<ApplicationState>,
private _service: PodcastService,
private _toasty: ToastyService,
route: ActivatedRoute,
private _location: Location
) {
@@ -88,11 +92,25 @@ export class PodcastComponent {
this._store.dispatch(new fromEntriesActions.AddSuccessAction(entry));
this._store.dispatch(new fromEntriesActions.UpdateAction(entry));
}
onEntryUploadDeferred($event) {
onEntryUploadDeferred(entry: PodcastEntryModel) {
this.pendingEntry = entry;
}
onUrlAddComplete(entry: PodcastEntryModel) {
this.urlMode = false;
this._store.dispatch(new fromEntriesActions.AddSuccessAction(entry));
}
processPlaylist() {
if (this.pendingEntry) {
this._service.addPlaylist(this.pendingEntry)
.subscribe(e => {
if (e) {
this._toasty.info('Playlist added, check back here (and on your device) for new episodes');
}
});
}
}
dismissPlaylist() {
this.urlMode = false;
this.pendingEntry = null;
}
}

View File

View File

0
client/src/app/components/profile/profile.component.ts Normal file → Executable file
View File

View File

View File

View File

0
client/src/app/components/reset/reset.component.css Normal file → Executable file
View File

0
client/src/app/components/reset/reset.component.html Normal file → Executable file
View File

0
client/src/app/components/reset/reset.component.ts Normal file → Executable file
View File

View File

View File

View File

View File

View File

View File

View File

View File

0
client/src/app/components/sidebar/sidebar.component.ts Normal file → Executable file
View File

0
client/src/app/effects/entries.effects.ts Normal file → Executable file
View File

0
client/src/app/effects/podcast.effects.ts Normal file → Executable file
View File

0
client/src/app/effects/profile.effects.ts Normal file → Executable file
View File

0
client/src/app/models/podcasts.models.ts Normal file → Executable file
View File

0
client/src/app/models/profile.model.ts Normal file → Executable file
View File

0
client/src/app/pipes/filter-entry.pipe.ts Normal file → Executable file
View File

0
client/src/app/pipes/order-by.pipe.ts Normal file → Executable file
View File

0
client/src/app/pipes/pretty-print.pipe.ts Normal file → Executable file
View File

0
client/src/app/pipes/safe.util.ts Normal file → Executable file
View File

0
client/src/app/reducers/entries.reducer.ts Normal file → Executable file
View File

0
client/src/app/reducers/index.ts Normal file → Executable file
View File

0
client/src/app/reducers/podcasts.reducer.ts Normal file → Executable file
View File

0
client/src/app/reducers/profile.reducer.ts Normal file → Executable file
View File

View File

@@ -0,0 +1,16 @@
import { Injectable } from '@angular/core';
import { AppInsights } from 'applicationinsights-js';
import { environment } from 'environments/environment';
@Injectable()
export class AppInsightsService {
private config: Microsoft.ApplicationInsights.IConfig = {
instrumentationKey: environment.appInsights.instrumentationKey
};
constructor() {
if (!AppInsights.config){
AppInsights.downloadAndSetup(this.config);
}
}
}

0
client/src/app/services/auth.guard.ts Normal file → Executable file
View File

0
client/src/app/services/auth.service.ts Normal file → Executable file
View File

0
client/src/app/services/debug.service.ts Normal file → Executable file
View File

0
client/src/app/services/entries.service.ts Normal file → Executable file
View File

0
client/src/app/services/globals.service.ts Normal file → Executable file
View File

0
client/src/app/services/image.service.ts Normal file → Executable file
View File

9
client/src/app/services/jobs.service.ts Normal file → Executable file
View File

@@ -6,9 +6,16 @@ import { environment } from 'environments/environment';
@Injectable()
export class JobsService {
constructor(private _http: AuthHttp) {}
constructor(private _http: AuthHttp) { }
processOrphans(): Observable<Response> {
return this._http.get(environment.API_HOST + '/job/processorphans');
}
processPlaylists(): Observable<Response> {
return this._http.get(environment.API_HOST + '/job/processplaylists');
}
updateYouTubeDl(): Observable<Response> {
return this._http.get(environment.API_HOST + '/job/updateyoutubedl');
}
}

14
client/src/app/services/podcast.service.ts Normal file → Executable file
View File

@@ -13,7 +13,7 @@ export class PodcastService {
}
return value;
}
constructor(private _http: AuthHttp) {}
constructor(private _http: AuthHttp) { }
//#region Podcasts
get(): Observable<PodcastModel[]> {
return this._http
@@ -68,8 +68,16 @@ export class PodcastService {
}
reSubmitEntry(entry: PodcastEntryModel): Observable<PodcastEntryModel> {
return this._http
.post(environment.API_HOST + '/entry/resubmit', entry)
.map(res => res.json());
.post(environment.API_HOST + '/entry/resubmit', entry)
.map(res => res.json());
}
//#endregion
//#region Playlists
addPlaylist(entry: PodcastEntryModel) {
return this._http
.post(environment.API_HOST + '/playlist', JSON.stringify(entry))
.map(res => res.json());
}
//#endregion
}

0
client/src/app/services/profile.service.ts Normal file → Executable file
View File

0
client/src/app/services/pusher.service.ts Normal file → Executable file
View File

0
client/src/app/services/signalr.service.ts Normal file → Executable file
View File

0
client/src/app/store/index.ts Normal file → Executable file
View File

0
client/src/assets/.gitkeep Normal file → Executable file
View File

0
client/src/assets/css/style.css Normal file → Executable file
View File

0
client/src/assets/img/logo-icon.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Some files were not shown because too many files have changed in this diff Show More