Skip to content

Commit

Permalink
web-app: Channel Editor improvements and Recordings page
Browse files Browse the repository at this point in the history
Channel editor
- Sort on Chan Number or source
- Filter on source

Recordings page
- List of recorded programs.
- Sort on Title, Orig Air Date, Air Date, Recording Group
- Filter on Title, Recording Group
  • Loading branch information
bennettpeter committed May 20, 2023
1 parent d54bb60 commit 0e2f376
Show file tree
Hide file tree
Showing 12 changed files with 636 additions and 114 deletions.
47 changes: 37 additions & 10 deletions mythtv/html/assets/i18n/en_US.json
Expand Up @@ -5,6 +5,8 @@
"loading": "Loading",
"next": "Next",
"save": "Save",
"success": "Success",
"failed": "Failed",
"savesuccess": "Saved Successfully",
"networkfail": "ERROR: Backend Network Failure",
"cancel": "Cancel",
Expand All @@ -25,7 +27,34 @@
"backendStatus": "Backend Status",
"channeleditor": "Channel Editor",
"programguide": "Program Guide",
"recordings": "Recordings"
"recordings": {
"heading": "Recordings",
"mnu_delete": "Delete",
"mnu_delete_rerec": "Delete and Allow Rerecord",
"mnu_undelete": "Undelete",
"mnu_rerec": "Allow Rerecord",
"mnu_markwatched": "Mark as Watched",
"mnu_markunwatched": "Mark as Unwatched",
"mnu_updatemeta": "Update Metadata",
"mnu_updaterecrule": "Update Record Rule",
"mnu_stoprec": "Stop Recording",
"filter": "Filter",
"title": "Title",
"recgrp": "Rec Grp",
"subtitle": "Subtitle",
"inetref": "InetRef",
"seas_ep": "Seas Ep",
"orig_airdate": "Orig Airdate",
"airdate": "Airdate",
"channel": "Channel",
"length_mins": "Length Mins",
"file_size": "File Size",
"description": "Description",
"season": "Season",
"episode": "Episode",
"canundel": "You can reverse this by using Undelete.",
"alreadydel": "Already Deleted"
}
},
"navbar": {
"backendSetup": "Backend Setup",
Expand Down Expand Up @@ -769,7 +798,9 @@
"col_source": "Source",
"col_priority": "Priority",
"col_visibility": "Visibility",
"col_useeit": "Use EIT"
"col_useeit": "Use EIT",
"updatechan": "Update Channel",
"deletechan": "Delete Channel"
},
"rprofiles": {
"title": "Recording Profiles",
Expand Down Expand Up @@ -851,15 +882,11 @@
},
"main": {
"title": "MythTV Setup",
"schedstatus": "Scheduler Status",
"schedenabled": "Enabled",
"scheddisabled": "Disabled",
"upcomingrec": "Next upcoming recording: {{Title}} at {{StartTime}}. Status: {{Status}}",
"noupcomingrec": "There are no upcoming recordings.",
"savedisabled_warning": "Saving and Deleting are disabled because the Scheduler is enabled.",
"savedisabled_desc": "You must disable the scheduler to enable saving and deleting. While the scheduler is disabled, any recordings that reach their start time will be canceled.",
"scheddisabled_warning": "The Scheduler is disabled. No recordings will take place.",
"scheddisabled_desc": "Please restart the backend after finishing your updates, otherwise the scheduler will remain disabled and nothing will record."
"enableupdates_label": "Enable Updates",
"enableupdates_desc": "Enabling updates will prevent any recordings from taking place until you restart",
"restart_label": "Restart Backend",
"restart_desc": "Recordings are disabled until you restart"
},
"channelscan": {
"services_label": "Desired Services",
Expand Down
@@ -1,50 +1,20 @@
<div class="block card w-full">
<div class="block">
<p-card class="m-5">
<!--This commented stuff displays more info about the status, but it is removed as it is too much info
and could confuse the user -->
<!-- <div *ngIf="retryCount == 0">
<p>{{ 'settings.main.schedstatus' | translate }}:
<b *ngIf="setupService.schedulingEnabled">{{ 'settings.main.schedenabled' | translate }}</b>
<b *ngIf="!setupService.schedulingEnabled">{{ 'settings.main.scheddisabled' | translate }}</b>
</p>
<p *ngIf="upComing.length > 0">
{{ 'settings.main.upcomingrec' |
translate: { Title: upComing[0].Title, StartTime: recStartTime, Status: recStatusDesc } }}
</p>
<p *ngIf="upComing.length == 0 && ready">
{{ 'settings.main.noupcomingrec' | translate }}
</p>
<div *ngIf="setupService.schedulingEnabled">
<p-message severity="warn"
text="{{ 'settings.main.savedisabled_warning' | translate }}"></p-message>
<p>
{{ 'settings.main.savedisabled_desc' | translate }}
</p>
</div>
<div *ngIf="!setupService.schedulingEnabled">
<p-message severity="warn"
text="{{ 'settings.main.scheddisabled_warning' | translate }}"></p-message>
<p>
{{ 'settings.main.scheddisabled_desc' | translate }}
</p>
</div>
</div> -->
<div class="flex" *ngIf="setupService.schedulingEnabled && retryCount == 0">
<div class="flex align-items-center">
<p-button class="pr-5 pb-1 pt-0 m-0" label="Enable Updates" icon="pi pi-save" iconPos="left"
<p-button class="pr-5 pb-1 pt-0 m-0" label="{{ 'settings.main.enableupdates_label' | translate }}" icon="pi pi-save" iconPos="left"
(onClick)="disableSched();"></p-button>
</div>
<div class="flex align-items-center">Enabling updates will prevent any recordings from taking place
until you restart</div>
<div class="flex align-items-center">{{ 'settings.main.enableupdates_desc' | translate }}</div>
</div>
<div class="flex" *ngIf="!setupService.schedulingEnabled && retryCount == 0">
<div class="flex align-items-center">
<p-button class="pr-5 pb-1 pt-0 m-0" label="Restart Backend" icon="pi pi-save" iconPos="left"
<p-button class="pr-5 pb-1 pt-0 m-0" label="{{ 'settings.main.restart_label' | translate }}" icon="pi pi-save" iconPos="left"
(onClick)="restart();"></p-button>
</div>
<div class="flex align-items-center">
<div class="text-pink-500">Recordings are disabled until you restart</div>
<div class="text-pink-500">{{ 'settings.main.restart_desc' | translate }}</div>
</div>
</div>
<div>
Expand Down
@@ -1,24 +1,30 @@
<form class="ml-3 mr-3" name="chanform" #chanform="ngForm">
<div class="block card w-full" style="height: calc(100vh - 200px)">
<div class="block card w-full" style="height: 90vh" *ngIf="chansLoaded else loading">
<h2>{{ 'settings.chanedit.title' | translate }}</h2>
<p-table [value]="allChannels" scrollHeight="flex" [scrollable]="true"
styleClass="p-datatable-gridlines p-datatable-striped" [rowHover]="true">
<p-table [value]="allChannels" scrollHeight="flex" [scrollable]="true" styleClass="p-datatable-striped"
[rowHover]="true">
<ng-template pTemplate="caption">
<button pButton pRipple label="{{ 'settings.chanedit.new_channel' | translate }}" icon="pi pi-plus"
class="p-button-success mr-2" (click)="openNew()"></button>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th style="flex-grow: 4"></th>
<th style="flex-grow: 4">{{ 'settings.chanedit.col_channum' | translate }}</th>
<th style="flex-grow: 3">{{ 'settings.chanedit.col_freqid' | translate }}</th>
<th style="flex-grow: 5">{{ 'settings.chanedit.col_callsign' | translate }}</th>
<th style="flex-grow: 12">{{ 'settings.chanedit.col_name' | translate }}</th>
<th style="flex-grow: 8">{{ 'settings.chanedit.col_source' | translate }}</th>
<th style="flex-grow: 3">{{ 'settings.chanedit.col_priority' | translate }}</th>
<th style="flex-grow: 5">{{ 'settings.chanedit.col_visibility' | translate }}</th>
<th style="flex-grow: 3">{{ 'settings.chanedit.col_useeit' | translate }}</th>
<th style="flex-grow: 3">
<th>Filter: &nbsp;&nbsp; Source: &nbsp; <p-columnFilter type="text" field="Source"></p-columnFilter>
</th>
</tr>
<tr>
<th style="flex-grow: 42; flex-basis: 0;">&nbsp;</th>
<th style="flex-grow: 44; flex-basis: 0;" pSortableColumn="ChanSeq">
{{ 'settings.chanedit.col_channum' | translate }} <p-sortIcon field="ChanSeq"></p-sortIcon></th>
<th style="flex-grow: 30; flex-basis: 0;">{{ 'settings.chanedit.col_freqid' | translate }}</th>
<th style="flex-grow: 50; flex-basis: 0;">{{ 'settings.chanedit.col_callsign' | translate }}</th>
<th style="flex-grow: 120; flex-basis: 0;">{{ 'settings.chanedit.col_name' | translate }}</th>
<th style="flex-grow: 80; flex-basis: 0;" pSortableColumn="Source">
{{ 'settings.chanedit.col_source' | translate }} <p-sortIcon field="Source"></p-sortIcon></th>
<th style="flex-grow: 30; flex-basis: 0;">{{ 'settings.chanedit.col_priority' | translate }}</th>
<th style="flex-grow: 50; flex-basis: 0;">{{ 'settings.chanedit.col_visibility' | translate }}</th>
<th style="flex-grow: 30; flex-basis: 0;">{{ 'settings.chanedit.col_useeit' | translate }}</th>
<th style="flex-grow: 30; flex-basis: 0;">
<!-- These are disabled buttons to ensure the spacing of the heading
matches the spacing of the rows -->
<button pButton pRipple icon="pi pi-pencil" class="p-button-text p-button-success"
Expand All @@ -30,25 +36,25 @@ <h2>{{ 'settings.chanedit.title' | translate }}</h2>
</ng-template>
<ng-template pTemplate="body" let-channel>
<tr height="40" [ngClass]="{'line-through' : channel.ChanId < 0}">
<td style="flex-grow: 4">
<td style="flex-grow: 42">
<img src="{{channel.IconURL}}" height="32" width="42" *ngIf="channel.IconURL; else nullIcon"
style="background-color:#000000" onerror="this.height='0'">
<ng-template #nullIcon><img height="32" width="42"></ng-template>
</td>
<td style="flex-grow: 4">{{channel.ChanNum}}</td>
<td style="flex-grow: 3">{{channel.FrequencyId}}</td>
<td style="flex-grow: 5">{{channel.CallSign}}</td>
<td style="flex-grow: 12">{{channel.ChannelName}}</td>
<td style="flex-grow: 8">{{ getSource(channel) }}</td>
<td style="flex-grow: 3">{{channel.RecPriority}}</td>
<td style="flex-grow: 5">{{ getVisibility(channel) }}</td>
<td style="flex-grow: 3">{{ channel.UseEIT ? 'Y' : 'N' }}</td>
<td style="flex-grow: 3">
<td style="flex-grow: 44; flex-basis: 0;">{{ channel.ChanNum }}</td>
<td style="flex-grow: 30; flex-basis: 0;">{{ channel.FrequencyId }}</td>
<td style="flex-grow: 50; flex-basis: 0;">{{ channel.CallSign }}</td>
<td style="flex-grow: 120; flex-basis: 0;">{{ channel.ChannelName }}</td>
<td style="flex-grow: 80; flex-basis: 0;">{{ channel.Source }}</td>
<td style="flex-grow: 30; flex-basis: 0;">{{ channel.RecPriority }}</td>
<td style="flex-grow: 50; flex-basis: 0;">{{ getVisibility(channel) }}</td>
<td style="flex-grow: 30; flex-basis: 0;">{{ channel.UseEIT ? 'Y' : 'N' }}</td>
<td style="flex-grow: 30; flex-basis: 0;">
<button pButton pRipple icon="pi pi-pencil" class="p-button-text p-button-success"
(click)="editChannel(channel)" [disabled]="channel.ChanId < 0"
pTooltip="{{ 'settings.common.clipboard_tooltip' | translate }}"></button>
pTooltip="{{ 'settings.chanedit.updatechan' | translate }}"></button>
<button pButton pRipple icon="pi pi-trash" class="p-button-text p-button-danger"
(click)="deleteRequest(channel)"
(click)="deleteRequest(channel)" pTooltip="{{ 'settings.chanedit.deletechan' | translate }}"
[disabled]="channel.ChanId < 0"></button>
</td>
</tr>
Expand Down Expand Up @@ -208,6 +214,7 @@ <h2>{{ 'settings.chanedit.title' | translate }}</h2>
|| channel.CallSign.trim() == ''"></p-button>
</ng-template>
</p-dialog>

<p-dialog header="{{ 'common.unsaved_heading' | translate }}" [(visible)]="displayUnsaved" [modal]="true">
<p>{{ 'common.unsaved_message' | translate }}</p>
<ng-template pTemplate="footer">
Expand Down Expand Up @@ -241,4 +248,7 @@ <h2>{{ 'settings.common.ru_sure' | translate }}</h2>
</p-dialog>

</div>
<ng-template #loading>
<p-progressSpinner></p-progressSpinner>
</ng-template>
</form>
Expand Up @@ -8,17 +8,22 @@ import { Channel, CommMethod, DBChannelRequest } from 'src/app/services/interfac
import { VideoSource } from 'src/app/services/interfaces/videosource.interface';
import { SetupService } from 'src/app/services/setup.service';


interface MyChannel extends Channel {
ChanSeq?: number;
Source?: string;
}

@Component({
selector: 'app-channel-editor',
templateUrl: './channel-editor.component.html',
styleUrls: ['./channel-editor.component.css']
})

export class ChannelEditorComponent implements OnInit {

@ViewChild("chanform") currentForm!: NgForm;

allChannels: Channel[] = [];
allChannels: MyChannel[] = [];
videoSources: VideoSource[] = [];
commMethods: CommMethod[] = [];

Expand Down Expand Up @@ -60,9 +65,10 @@ export class ChannelEditorComponent implements OnInit {
dialogHeader = "";
displayUnsaved = false;
displayDelete = false;
chansLoaded = false;

channel: Channel = this.resetChannel();
editingChannel?: Channel;
channel: MyChannel = this.resetChannel();
editingChannel?: MyChannel;
// channelOperation -1 = delete, 0 = update, 1 = add
channelOperation = 0;

Expand All @@ -76,7 +82,7 @@ export class ChannelEditorComponent implements OnInit {
this.markPristine();
}

resetChannel(): Channel {
resetChannel(): MyChannel {
return {
ATSCMajorChan: 0,
ATSCMinorChan: 0,
Expand Down Expand Up @@ -105,15 +111,28 @@ export class ChannelEditorComponent implements OnInit {
TimeOffset: 0,
UseEIT: false,
Visible: true,
XMLTVID: ''
XMLTVID: '',
ChanSeq: 0
};
}

loadLists() {
this.channelService.GetChannelInfoList({ Details: true }).subscribe(data =>
this.allChannels = data.ChannelInfoList.ChannelInfos);
this.channelService.GetVideoSourceList().subscribe(data =>
this.videoSources = data.VideoSourceList.VideoSources);
this.channelService.GetChannelInfoList({ Details: true }).subscribe(data => {
this.allChannels = data.ChannelInfoList.ChannelInfos;
// this.allChannels.forEach((entry,index) => entry.ChanSeq = index);
this.chansLoaded = true;
this.channelService.GetVideoSourceList().subscribe(data => {
this.videoSources = data.VideoSourceList.VideoSources;
this.allChannels.forEach((entry,index) => {
entry.ChanSeq = index;
entry.Source = this.getSource(entry);
});
});


});
// this.channelService.GetVideoSourceList().subscribe(data =>
// this.videoSources = data.VideoSourceList.VideoSources);
this.channelService.GetCommMethodList().subscribe(data =>
this.commMethods = data.CommMethodList.CommMethods);
}
Expand Down Expand Up @@ -147,14 +166,14 @@ export class ChannelEditorComponent implements OnInit {
});
}

getSource(channel: Channel): string {
getSource(channel: MyChannel): string {
const ret = this.videoSources.find(element => channel.SourceId == element.Id);
if (ret != undefined)
return ret.SourceName;
return ""
}

getVisibility(channel: Channel): string {
getVisibility(channel: MyChannel): string {
const ret = this.visibilities.find(element => channel.ExtendedVisible == element.value);
if (ret != undefined)
return ret.prompt;
Expand All @@ -171,7 +190,7 @@ export class ChannelEditorComponent implements OnInit {
this.markPristine();
}

editChannel(channel: Channel): void {
editChannel(channel: MyChannel): void {
this.editingChannel = channel;
this.successCount = 0;
this.errorCount = 0;
Expand Down Expand Up @@ -278,14 +297,14 @@ export class ChannelEditorComponent implements OnInit {
};
}

deleteRequest(channel: Channel) {
deleteRequest(channel: MyChannel) {
this.successCount = 0;
this.errorCount = 0;
this.channel = channel;
this.displayDelete = true;
}

deleteChannel(channel: Channel) {
deleteChannel(channel: MyChannel) {
this.channel = channel;
this.channelOperation = -1;
this.channelService.RemoveDBChannel(channel.ChanId).subscribe(this.saveObserver);
Expand All @@ -296,7 +315,7 @@ export class ChannelEditorComponent implements OnInit {
setTimeout(() => {
x.next(1);
x.complete();
}, 100)
}, 200)
});
obs.subscribe(x =>
this.currentForm.form.markAsPristine()
Expand Down
@@ -1,3 +1,5 @@
<h1></h1>
<p-tabMenu [model]="fullMenu" [activeItem]="activeItem" [scrollable]="true"></p-tabMenu>
<router-outlet></router-outlet>
<div *ngIf="translateDone">
<h1></h1>
<p-tabMenu [model]="fullMenu" [activeItem]="activeItem" [scrollable]="true"></p-tabMenu>
<router-outlet></router-outlet>
</div>

0 comments on commit 0e2f376

Please sign in to comment.