Project

General

Profile

Feature #3096

Upgrade WebUI framework (ExtJS 6 ?, Angular ?)

Added by Jaroslav Kysela about 4 years ago. Updated about 1 month ago.

Status:
Accepted
Priority:
Normal
Category:
User Interface
Target version:
Start date:
2016-03-26
Due date:
% Done:

100%

Estimated time:
(Total: 0.00 h)

Files

TvheadendLogo.svg (1.87 KB) TvheadendLogo.svg Logo as vectorized svg Bernhard Berger, 2019-09-24 15:35
scifly.zip (33.2 KB) scifly.zip SciFly-Sans Bernhard Berger, 2019-09-24 15:39

Subtasks

Feature #3660: WebUI layout for mobile and small monitorsRejected

Actions
Bug #4878: What version of extjs are you using? Rejected

Actions

History

#1

Updated by Mark Clarkstone about 4 years ago

There is no option to quote or add a comment to this feature request so I had to edit it instead.

The below list are some of the ideas & improvements to the webui I could think of. Some of them may seem pointless or damn right odd :).

Note to perexg: Feel free to comment, link to issues/features (or even split them into separate issues) for the suggestions as you please.

  • Improve saving & loading of configuration, the TV adapters tab for example doesn't correctly refresh the tuner config if you make changes then select another tree item & go back.
  • Consolidate some tabs. The status tab for example could be made into one sortable list (Multiple sort grid?).
  • Improve the Live TV player. Add an option to select player back-end (using either html video tags or a browser plug-in).
  • Implement a quick start guide something along the lines of a pop-up wizard that guides the user through a simple configuration.
  • Make all the tabs sortable and hide-able however this may cause issues when people forget they've hidden an item (reset tabs button?).
  • Improve the EPG guide. Replace with a timeline version (or maybe keep both & let the user decide) also make it configurable (display channel logos, programme genre colourisation, etc).
  • Rename Tv Adapters tab. Mainly because SAT>IP tuners aren't what I would call adapters but are network tuners, rename it to Devices or Tuners?
  • Display all users (even anonymous ones) regardless of where they came from (HTTP, HTSP, API, etc).
  • User selectable themes.
  • Channel logo importer. Add a user interface that searches something like http://www.lyngsat-logo.com/ for logos? Might be useful to contact lynsat to see if they offer some sort of API.
#2

Updated by Prof Yaffle about 4 years ago

Random additions...

  • Search thetvdb (c/f the current imdb search) for upcoming broadcasts' details
  • Search thetvdb/imdb for completed recordings' details/programme information
  • Touch-compatible interface (one of the main drivers for extjs 5+ IMO)
  • More information about the backend system - CPU load, I/O load, disc space by filesystem (or as part of recording path setup?)
  • Searching of recordings (e.g. free-text search of description and title)?
#3

Updated by Prof Yaffle about 4 years ago

  • Right-click context menu actions for rename/delete/edit/filter/etc.
#4

Updated by Mathias Neuhaus about 4 years ago

  • save column layout of tables
#5

Updated by C vH about 4 years ago

  • bulk select (all/none/invert) through maybe right click
  • select from / to (shift + click)
  • optimize size of icons and "things" for at least HD
  • channel sort window (a special view that only shows channel names in a compact view and let them sort per drag and drop) + search with auto complete, if you search for RTL you got instantly ~20 channels you can select and drag to position (should be a huge easing for this task)
#6

Updated by Christian Glombek almost 4 years ago

Hello, I'd like to propose the following:

  • Split Out the Live TV UI (with EPG) to a second (public) WebUI.
If done right, this gives us these opportunities:
  • use the WebUI as reference implementation of HTSP
  • easily port the WebUI App to native platforms. (For that matter I'd like to propose using the React.js Framework)
  • no more need for app on the client (viewer) for simply watching TV and setting the Recorder - in a browser simply go to the WebUI's IP. Functionality could be disabled or restricted through auth
#7

Updated by saen acro about 2 years ago

Is this switch to ExtJS 6 will be available sometime, new owner of project make it paid
https://www.sencha.com/pricing/

#8

Updated by Jaroslav Kysela about 2 years ago

https://www.sencha.com/legal/gpl/

But we can probably look also for fully open-source javascript frameworks. Although ExtJS has many tools build-in, the licencing is a bit problem.

#9

Updated by Jaroslav Kysela about 2 years ago

  • Subject changed from WebUI to ExtJS 6 switch to Upgrade WebUI framework (ExtJS 6 ?, Angular ?)
#10

Updated by Mark Clarkstone about 2 years ago

Jaroslav Kysela wrote:

https://www.sencha.com/legal/gpl/

But we can probably look also for fully open-source javascript frameworks. Although ExtJS has many tools build-in, the licencing is a bit problem.

3.4 of ExtJS is under the same license, no? Could you explain the problem? :)

OpenUI5 Looks very nice :).

#11

Updated by Jaroslav Kysela about 2 years ago

Another choice: https://getuikit.com/ (thanks Mark)

#12

Updated by C vH about 2 years ago

Just to add Webix -> https://webix.com/demos

Webix - an advanced, easy to learn, mobile-friendly, responsive and rich free&open source JavaScript UI components library. Webix spun off from DHTMLX Touch (a project with 8 years of development behind it) and went on to become a standalone UI components framework.

#13

Updated by Mark Clarkstone about 2 years ago

C vH wrote:

Just to add Webix -> https://webix.com/demos

Webix - an advanced, easy to learn, mobile-friendly, responsive and rich free&open source JavaScript UI components library. Webix spun off from DHTMLX Touch (a project with 8 years of development behind it) and went on to become a standalone UI components framework.

I looked at webix and thought it was promising but then decided against it due to some widgets - that might be needed - being commercial only.

#14

Updated by Poul Kalff over 1 year ago

What is the progress on this? I would like to help out, if possible, but I have very limited experience with javascript. I have a lot of programming experience, though, mostly Python and PHP. I'd love learn some javascript.....

#15

Updated by Mark Clarkstone over 1 year ago

Poul Kalff wrote:

What is the progress on this? I would like to help out, if possible, but I have very limited experience with javascript. I have a lot of programming experience, though, mostly Python and PHP. I'd love learn some javascript.....

I don't think there has been any progress - not that I've noticed. Jaroslav has been busy improving the main code for 4.4.

I'm not a fan of Javascript & avoid it where possible. I wouldn't mind seeing the webui using minimal javascript for the frontend & something like micropython for the backend. I'm not sure how good that idea is as python can be rather slow in certain situations.

#16

Updated by Poul Kalff over 1 year ago

Python is not good for frontends.. not at all! Other than that, I have no oppinion on the matter, I'm just offering to help, if I can. But, obviously, someone has to decide on where to begin... I don't even know how the UI maps to the rest of the program....

#17

Updated by Mark Clarkstone over 1 year ago

Poul Kalff wrote:

Python is not good for frontends.. not at all!

I specifically mentioned the backend of the webui, not the frontend, but yes it's not great for frontends.

Other than that, I have no oppinion on the matter, I'm just offering to help, if I can. But, obviously, someone has to decide on where to begin... I don't even know how the UI maps to the rest of the program....

The webui is just served by tvh, it uses a json api. Take a look at the dev tools in your browser with the ui open :).

If you want a starting point, look at the simple.html / tv.html pages. You can work on another gui without affecting the current one.

#18

Updated by Poul Kalff over 1 year ago

Alright, thanks, mark, I'll have a look and follow the forums, see if any plans are made for the webui

#19

Updated by Jaroslav Kysela over 1 year ago

As the main tvh developer, I would be really grateful to have another leader for the webui development. But it would be really difficult to migrate all things implemented to the new framework.

#20

Updated by saen acro over 1 year ago

@Poul Kalff
Write some to demonstrate programming potential and skills ;)
https://tvheadend.org/issues/3992

#21

Updated by Poul Kalff over 1 year ago

saen acro wrote:

@Poul Kalff
Write some to demonstrate programming potential and skills ;)
https://tvheadend.org/issues/3992

I agree that there should be a separate leader for the UI, but it can't be me. As I've stated above, I know very little about JS, only thing I can offer this project is a willingness to help and to learn. Besides, I've just had my third child born, and I'm starting a new job this month (as a developer).... thus, I have no skill and no time, so hardly the ideal leader..... what about some of the people who have have suggested different technologies in this thread? Mark...?

#22

Updated by Mark Clarkstone over 1 year ago

Poul Kalff wrote:

saen acro wrote:

@Poul Kalff
Write some to demonstrate programming potential and skills ;)
https://tvheadend.org/issues/3992

I agree that there should be a separate leader for the UI, but it can't be me. As I've stated above, I know very little about JS, only thing I can offer this project is a willingness to help and to learn. Besides, I've just had my third child born, and I'm starting a new job this month (as a developer).... thus, I have no skill and no time, so hardly the ideal leader..... what about some of the people who have have suggested different technologies in this thread? Mark...?

Bad idea! I'm no UI developer nor am I a fan of Javascript (I'm on the side of despising it). However, I'm not against helping out.

Here's a few reasons why I'd be a bad fit:-

  • It would be worse than what we have now, and I mean worse. think the current simple.html :p
  • It would take me years to even write half the code as I only type one handed (due to a physical/mental disability). read: I'm slow (in more ways than one :p).
#23

Updated by Bernhard Berger 2 months ago

If someone could point me in the right direction I'd love to implement a channel sorting view that isn't as painful as the current state.

On a sidenote:
I would even love it more to create a complete new "Favourites" feature.

For my use case at home I need the ability to provide different clients (Kodi) with different channel groups and sorting.

Transitioning from DVBViewer this is the feature that I miss the most.

I already started fiddling around with the kodi-pvr.hts pvr addon so I could use a favourites.yml/json/whatever, but I'd actually prefer to implement this on the server side rather than the client side.

I'm a senior webdev (php world though), but also have done a bunch of work in C++/Qt, just would need a push in the right direction..

#24

Updated by Mark Clarkstone 2 months ago

Bernhard Berger wrote:

If someone could point me in the right direction I'd love to implement a channel sorting view that isn't as painful as the current state.

On a sidenote:
I would even love it more to create a complete new "Favourites" feature.

For my use case at home I need the ability to provide different clients (Kodi) with different channel groups and sorting.

Transitioning from DVBViewer this is the feature that I miss the most.

I already started fiddling around with the kodi-pvr.hts pvr addon so I could use a favourites.yml/json/whatever, but I'd actually prefer to implement this on the server side rather than the client side.

I'm a senior webdev (php world though), but also have done a bunch of work in C++/Qt, just would need a push in the right direction..

The only thing I can suggest is take a look at the webui code, you could use the simple webui as a starting point :)

#25

Updated by Bernhard Berger 2 months ago

Okay,I have now taken the past few days to analyze and study the webui and c-code and it gets more and more clear every day.

I have also tried evaluating what I could do regarding modernizing the webui and came to the following conclusions:

1) if you ever want to get rid of GPLv3 there is no way around ditching ExtJS. Bundling GPLv3 code automatically makes the whole project GPLv3. I don't see any sense in upgrading to ExtJS 6.2 GPL either. You could distribute the webui seperately, but with the unstable and commercial nature of the whole ecosystem I'd personally recommend to stay away..

2) I somehow can't stand Angular and I don't like React. I however have fallen in love with Vue and all that comes with it which would be the 3rd big player in that mix.

3) Right now I'm trying to do a proof-of-concept based on Vue (MIT licensed) using MaterialUI design components.

I already have toyed around with a few components including the websocket stuff, hopefully I can string something together to show publicly in the near future. Depending on how much time I can find.

4) I think at some point I'd need a 1on1 session for an hour or so with one of the core devs in a chat that actually has more experience with TVH than me.

Due to the sheer size and feature-rich-ness of tvh I will come to the limit of understanding some things eventually. There already are questions regarding authentication etc, but I can postpone them for now.

Long story short:
If I can get at least some support from time to time I'd like to try getting something started here.. I'd also like to know how others feel about this.

PS: I'm still not sure if packaging the webui separately - no matter the technology - wouldn't be a good idea anyways. It would enable both components to be updated / distributed independendly and features/fixes could get incorporated quicker. As far as I could see the webui already is rather loosely coupled and pretty much anything is done by the API. I would also allow other web-devs to work on the UI part
without having to pull all the tvh core along and you could get rid of a whole lot of dependencies blocking a license move.

#26

Updated by Mark Clarkstone 2 months ago

Bernhard Berger wrote:

Okay,I have now taken the past few days to analyze and study the webui and c-code and it gets more and more clear every day.

I have also tried evaluating what I could do regarding modernizing the webui and came to the following conclusions:

1) if you ever want to get rid of GPLv3 there is no way around ditching ExtJS. Bundling GPLv3 code automatically makes the whole project GPLv3. I don't see any sense in upgrading to ExtJS 6.2 GPL either. You could distribute the webui seperately, but with the unstable and commercial nature of the whole ecosystem I'd personally recommend to stay away..

EXTJS was great at the time but things have seriously moved on, it bugs me no end that it isn't more accessible for people with crappy eyesight, physical issues & screen readers.

2) I somehow can't stand Angular and I don't like React. I however have fallen in love with Vue and all that comes with it which would be the 3rd big player in that mix.

I personally like mithril and I did intend to look into getting a poc using it up at some point, but I haven't got round to doing it. But, I'm happy for people to use whatever they like :)

3) Right now I'm trying to do a proof-of-concept based on Vue (MIT licensed) using MaterialUI design components.

I already have toyed around with a few components including the websocket stuff, hopefully I can string something together to show publicly in the near future. Depending on how much time I can find.

Start simple :) Try and replicate the basic pages (about etc) first.

4) I think at some point I'd need a 1on1 session for an hour or so with one of the core devs in a chat that actually has more experience with TVH than me.

I think the best place would be to keep it in the open, post here or ask in IRC. Someone is bound to know the answer :)

Due to the sheer size and feature-rich-ness of tvh I will come to the limit of understanding some things eventually. There already are questions regarding authentication etc, but I can postpone them for now.

HTTP Auth is handled in the c code. You could if you wanted just create a separate htsp "webui" client to do everything including authing via htsp, you can access the api via htsp :)

Long story short:
If I can get at least some support from time to time I'd like to try getting something started here.. I'd also like to know how others feel about this.

I'll help out as much as I can, the more technical under-the-hood code is Jaroslav's domain!

PS: I'm still not sure if packaging the webui separately - no matter the technology - wouldn't be a good idea anyways. It would enable both components to be updated / distributed independendly and features/fixes could get incorporated quicker.
As far as I could see the webui already is rather loosely coupled and pretty much anything is done by the API. I would also allow other web-devs to work on the UI part
without having to pull all the tvh core along and you could get rid of a whole lot of dependencies blocking a license move.

#27

Updated by Bernhard Berger 2 months ago

Thank for the quick response!

Is there any endpoint I can actually use to authenticate?

Right now I'm just collecting username and password and try to access the status page. Feels a bit criminal to me..

#28

Updated by Mark Clarkstone 2 months ago

Bernhard Berger wrote:

Thank for the quick response!

Is there any endpoint I can actually use to authenticate?

Right now I'm just collecting username and password and try to access the status page. Feels a bit criminal to me..

If you're serving the page via the built-in http server the only way is via http auth at /login /logout. If you want to do a htsp based webui using websockets, you could auth via that method [see this python code for an example].

I forget the request you need to make to access the web api over htsp, it's in there somewhere!

#29

Updated by Bernhard Berger 2 months ago

A few questions have come to light:

1) Is there any way to get used space / free space other than listening on comet events?

Somehow I can't seem to find anything related to it.

2) is there any other way to get infos about the currently logged in user via http api?

3) I have a listener connected to the websocket for collecting comet stuff. I expected it to spit out the same info as the "Debug"-Panel in the bottom of the current webui. However - it does not. Where am I going wrong here?

PoC is coming along slowly, but imho nicely. Need to get some of the stuff mentioned above sorted and try out a few UI concepts before releasing a preview though.

Authentication stuff is bugging me the most.. it's just not nice how I currently handle this..

#30

Updated by Dave Pickles 2 months ago

1) Is there any way to get used space / free space other than listening on comet events?

I submitted a PR to get this added to the HTTP API but was deferred - it seems the existing way of counting disk space doesn't work for all storage configurations.

https://github.com/tvheadend/tvheadend/pull/1018/commits/21e0dc8252cef79053a714ba394432f46a52be92

2) is there any other way to get infos about the currently logged in user via http api?

To use most functions of the HTTP API you have to submit authentication with your request - you are the logged-in user.

#31

Updated by Bernhard Berger 2 months ago

Dave Pickles wrote:

To use most functions of the HTTP API you have to submit authentication with your request - you are the logged-in user.

Feared as much.. so basically all I can do is try accessing an endpoint using my credentials and check if I get a 40x? Still trying to figure out how tvh handles that state internally (from the basic auth).

Somehow I feel there is some kind of actual auth process / entry point missing in tvh. I just can't wrap my head around managing credentials on the client side too. Usually I'd expect some form of token (which seems to exist, but looks to me like it is a user based, permanent API key..?)

Just trying to figure out on how to design the Login/Logout as I hate nothing more than that Basic Auth popup.

Possibly the part I'm looking for is: the API calls from the extjs webui seem to be credential-less. I can't find any code that handles that, which leads me to think it must be burried somewhere in the http server code. Also it displays the username from the currently logged in user in the top right corner, next to the logout. I want to know where this is fetched from. The only reference I can find is in the comet stuff.

Thanks again for the quick response!

#32

Updated by Dave Pickles 2 months ago

Checking the network traffic from the browser when accessing the UI, each request to the server includes an Authorization header in either plain or digest form. This is handled in src/http.c line 1469 and on.

#33

Updated by Bernhard Berger about 2 months ago

..sorry, I'm an idiot.. of course the extjs webui can call the endpoints without supplying explicit credentials as all the headers get added by the browser as it's an SPA.. that's the reason stuff like this works from inside the webui..:

Ext.Ajax.request({
url: 'api/config/capabilities'
});

The HTTP server captures the credentials and fires a comet event which again gets captured by the webui and the username gets displayed at the top..

Happening here:
https://github.com/tvheadend/tvheadend/blob/27c00888475f27ef21a1b58805804fa6ebdf3e99/src/webui/static/app/tvheadend.js#L1007
and here:
https://github.com/tvheadend/tvheadend/blob/27c00888475f27ef21a1b58805804fa6ebdf3e99/src/webui/static/app/tvheadend.js#L1476

The part that I didn't get was:

how in the world does the extjs app know which user is logged in (top bar) without me providing any credentials (you can't capture Basic Auth input on the client side).

Not it makes sense..

#34

Updated by Bernhard Berger about 2 months ago

sorry for the double posting..

In conclusion this also means: the TVH server has no clue whatsoever of whom is logged into the webui as there is no user session handling on the server side. The only time I do see a connection in the connections tab is when I stream a channel in the browser, but that is due to the connection being held open by the stream..

#35

Updated by Mark Clarkstone about 2 months ago

Bernhard Berger wrote:

..sorry, I'm an idiot.. of course the extjs webui can call the endpoints without supplying explicit credentials as all the headers get added by the browser as it's an SPA.. that's the reason stuff like this works from inside the webui..:

Ext.Ajax.request({
url: 'api/config/capabilities'
});

The HTTP server captures the credentials and fires a comet event which again gets captured by the webui and the username gets displayed at the top..

Happening here:
https://github.com/tvheadend/tvheadend/blob/27c00888475f27ef21a1b58805804fa6ebdf3e99/src/webui/static/app/tvheadend.js#L1007
and here:
https://github.com/tvheadend/tvheadend/blob/27c00888475f27ef21a1b58805804fa6ebdf3e99/src/webui/static/app/tvheadend.js#L1476

The part that I didn't get was:

how in the world does the extjs app know which user is logged in (top bar) without me providing any credentials (you can't capture Basic Auth input on the client side).

Not it makes sense..

pretty sure the webui reads the http auth header, I could be wrong though

#36

Updated by Bernhard Berger about 2 months ago

Quick update: I was right, It's all taken from the comet messages.

Took me some time to get all that websocket stuff sorted and understood, but I think I have most of that down now.

The more I work with the Vue ecosystem the more I like it. There is a bunch of complexity falling away compared to the ExtJS implementation, especially regarding syncing stuff and building reusable widgets.

I think I should have a (mostly readonly) sneak peek concept done by the end of next week if personal life allows for it.

Bonus question: what are the chances we (= not me) could get configurable cross origin headers into tvh? Would allow the webif to be run on a different port/server without the need for a server side proxy..

#37

Updated by Mark Clarkstone about 2 months ago

Bernhard Berger wrote:

Quick update: I was right, It's all taken from the comet messages.

Took me some time to get all that websocket stuff sorted and understood, but I think I have most of that down now.

The more I work with the Vue ecosystem the more I like it. There is a bunch of complexity falling away compared to the ExtJS implementation, especially regarding syncing stuff and building reusable widgets.

I think I should have a (mostly readonly) sneak peek concept done by the end of next week if personal life allows for it.

Bonus question: what are the chances we (= not me) could get configurable cross origin headers into tvh? Would allow the webif to be run on a different port/server without the need for a server side proxy..

There's a CORS option, not sure it's what your after as I've never used it. Can't wait to see what you've done though.

#38

Updated by zapp -it about 2 months ago

Bernhard Berger wrote:

Quick update: I was right, It's all taken from the comet messages.

Took me some time to get all that websocket stuff sorted and understood, but I think I have most of that down now.

The more I work with the Vue ecosystem the more I like it. There is a bunch of complexity falling away compared to the ExtJS implementation, especially regarding syncing stuff and building reusable widgets.

I think I should have a (mostly readonly) sneak peek concept done by the end of next week if personal life allows for it.

Bonus question: what are the chances we (= not me) could get configurable cross origin headers into tvh? Would allow the webif to be run on a different port/server without the need for a server side proxy..

Sorry for my english, it is not my native language ;)

You can use CORS, it is working after commit https://github.com/tvheadend/tvheadend/commit/4bf32134bb31a564ad8ad34402442cd6efd1433e#diff-702344bade0f7e492b07be2d38ce5110

Remember that it is working only with a FQDN and you can only use one asterisk for example *.example.com , but you can not use

*.example.com/*

I would recommend to use the XMLHttpRequest object of javascript.

I have written a long time ago a C# program for adding IPTV channels to TVH (something with AES ;) ) maybe you can use something of it. I used a abstract base class and then I created the other classes derivatives from that class, for examples channels, EPG etc. You can find the URL's of the other classes with Wireshark.

I hope this will help you a little bit further

// <copyright file="TVHBase.cs" company="root.">
//     Copyright 2015, root. All rights reserved.
// </copyright>

namespace Extm3U.TVH
{
  using System;
  using System.Collections.Generic;
  using System.Globalization;
  using System.IO;
  using System.Net;
  using System.Reflection;
  using System.Runtime.Serialization;
  using System.Runtime.Serialization.Json;
  using System.Text;

  /// <summary>
  /// Represents the base class for the objects of Tvheadend.
  /// </summary>
  /// <typeparam name="T">The type of elements.</typeparam>
  [DataContract]
  internal abstract class TVHBase<T>
      where T : class
  {
    /// <summary>
    /// Private holder for the credentials.
    /// </summary>
    private CredentialCache _cache;

    /// <summary>
    /// Private holder for the URL.
    /// </summary>
    private Uri _url;

    /// <summary>
    /// Initializes a new instance of the <see cref="TVHBase&lt;T&gt;"/> class.
    /// </summary>
    /// <param name="cache">The credential cache to use to authenticate with Tvheadend.</param>
    /// <param name="url">The URL of Tvheadend.</param>
    public TVHBase(CredentialCache cache, Uri url)
    {
      if (url == null)
      {
        throw new ArgumentNullException("url");
      }

      this._cache = cache;
      this._url = url;

      this.Refresh();
    }

    /// <summary>
    /// Gets the API used to get the object(s).
    /// </summary>
    /// <remarks>
    /// Change the limit to a higher level when needed.
    /// </remarks>
    public static string APIGet
    {
      get { return @"start=0&limit=999999999&sort=name&dir=ASC&all=1"; }
    }

    /// <summary>
    /// Gets the API used to create a object.
    /// </summary>
    public abstract string APICreate { get; }

    /// <summary>
    /// Gets the API used to delete a object.
    /// </summary>
    public virtual string APIDelete
    {
      get { return @"uuid="; }
    }

    /// <summary>
    /// Gets the API used to save a object.
    /// </summary>
    public virtual string APISave
    {
      get { return @"node={0}"; }
    }

    /// <summary>
    /// Gets the URL for the create API, only IPTV.
    /// </summary>
    public abstract string URLCreate { get; }

    /// <summary>
    /// Gets the URL for the delete API, only IPTV.
    /// </summary>
    public virtual string URLDelete
    {
      get { return @"api/idnode/delete"; }
    }

    /// <summary>
    /// Gets the URL for the get API, only IPTV.
    /// </summary>
    public abstract string URLGet { get; }

    /// <summary>
    /// Gets the URL for the save API, IPTV only.
    /// </summary>
    public virtual string URLSave
    {
      get { return @"api/idnode/save"; }
    }

    /// <summary>
    /// Gets or sets a value indicating the total of entries.
    /// </summary>
    [DataMember(Name = "total")]
    public int Count { get; set; }

    /// <summary>
    /// Gets or sets the list with the members of the entries of Tvheadend.
    /// </summary>
    [DataMember(Name = "entries")]
    public List<T> Entries { get; set; }

    /// <summary>
    /// Adds a object to Tvheadend.
    /// </summary>
    /// <param name="value">The object to add.</param>
    public void Add(T value)
    {
      this.Add(value, true);
    }

    /// <summary>
    /// Adds a object to Tvheadend.
    /// </summary>
    /// <param name="value"><see langword="true"/> when deleting a existing object with the same name or ID, otherwise <see langword="false"/>.</param>
    /// <param name="delete">The object to add.</param>
    public virtual void Add(T value, bool delete)
    {
      // Some objects are read-only e.g. services, EPG etc.
      if (!string.IsNullOrEmpty(this.URLCreate))
      {
        if (value == null)
        {
          throw new ArgumentNullException(typeof(T).Name);
        }

        if (delete)
        {
          this.Delete(value);
        }

        Uri create = new Uri(this._url, this.URLCreate);

        TVHBase<T>.POST(create, this._cache, value, this.APICreate, null);

        this.Refresh();
      }
    }

    /// <summary>
    /// Deletes a object form Tvheadend.
    /// </summary>
    /// <param name="value">The object to delete.</param>
    public void Delete(T value)
    {
      // Some objects are read-only e.g. services, EPG etc.
      if (!string.IsNullOrEmpty(this.URLCreate))
      {
        if (value == null)
        {
          throw new ArgumentNullException(typeof(T).Name);
        }

        List<T> objects = this.Entries.FindAll(n => value.Equals(n));

        if (objects.Count > 0)
        {
          Type type = value.GetType();

          for (int i = 0; i < objects.Count; i++)
          {
            Uri del = new Uri(this._url, this.URLDelete);

            TVHBase<T>.POST(del, this._cache, null, this.APIDelete + (string)type.GetProperty("ID").GetValue(objects[i], null), null);
          }

          this.Refresh();
        }
      }
    }

    /// <summary>
    /// Saves a updated object to Tvheadend.
    /// </summary>
    /// <param name="value">The object to save.</param>
    public void Save(T value)
    {
      if (!string.IsNullOrEmpty(this.URLSave))
      {
        if (value == null)
        {
          throw new ArgumentNullException(typeof(T).Name);
        }

        Uri save = new Uri(this._url, this.URLSave);

        TVHBase<T>.POST(save, this._cache, value, this.APISave, null);

        this.Refresh();
      }
    }

    /// <summary>
    /// Posts the REST JSON data.
    /// </summary>
    /// <param name="url">The url to connect.</param>
    /// <param name="cache">The credentials cache to authenticate to the url.</param>
    /// <param name="value">The value to serialize and post.</param>
    /// <param name="api">The custom API to post.</param>
    /// <param name="type">The type of the data to deserialize.</param>
    /// <returns>Returns the JSON return value.</returns>
    private static object POST(Uri url, CredentialCache cache, object value, string api, Type type)
    {
      object result = null;

      HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
      request.CookieContainer = new CookieContainer();

      if (cache != null)
      {
        request.Credentials = cache;
      }

      request.Accept = "text/html, application/xhtml+xml, application/xml, application/json";
      request.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
      request.KeepAlive = true;
      request.Method = WebRequestMethods.Http.Post;

      string json = null;

      if (value != null)
      {
        using (MemoryStream stream = new MemoryStream())
        {
          DataContractJsonSerializer serializer = new DataContractJsonSerializer(value.GetType());
          serializer.WriteObject(stream, value);

          json = string.Format(CultureInfo.InvariantCulture, api, Uri.EscapeDataString(Encoding.UTF8.GetString(stream.ToArray())));
        }
      }
      else
      {
        json = api;
      }

      using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
      {
        writer.Write(json);
        writer.Flush();
        //// writer.Close(); CA2202
      }

      using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
      {
        if (response != null && response.StatusCode == HttpStatusCode.OK)
        {
          if (type != null)
          {
            DataContractJsonSerializer deserializer = new DataContractJsonSerializer(type);
            result = deserializer.ReadObject(response.GetResponseStream());
          }
          else
          {
            StreamReader reader = new StreamReader(response.GetResponseStream());
            result = reader.ReadToEnd();
          }
        }
      }

      return result;
    }

    /// <summary>
    /// Refresh the current entries list.
    /// </summary>
    private void Refresh()
    {
      Uri get = new Uri(this._url, this.URLGet);

      TVHBase<T> data = TVHBase<T>.POST(get, this._cache, null, TVHBase<T>.APIGet, this.GetType()) as TVHBase<T>;

      if (data != null)
      {
        // Ugly work around.
        Type type = data.GetType();

        try
        {
          this.Count = (int)type.GetProperty("Count").GetValue(data, null);
          this.Entries = (List<T>)type.GetProperty("Entries").GetValue(data, null);
        }
        catch (NullReferenceException)
        {
        }
      }
    }
  }
}


#39

Updated by Bernhard Berger about 2 months ago

Yeah, thanks for the hints about the CORS setting in the tvh ui. Completely missed that.

For development it isn't an issue as I run a local proxy anyways. But for SPAs CORS is vital.

For requests I'm using axios, a promise based xhr lib. It's basically the js equivalent of guzzle from the php world..

The really difficult stuff to decypher is the comet/websocket stuff as it's basically undocumented and returns god knows what.

The idnode concept also is a bit hard to grasp. After spending 2 weeks on reading the code I'm still not sure if it's a relational structure or a node based db structure.. I'm sure it's in there for a reason, but at some point I think it should be refactored to something more common like SQlite or a graph equivalent. It's almost impossible to read and debug (at least for me)

#40

Updated by zapp -it about 2 months ago

If you use google chrome with the web developer option enabled you can find the API calls which TVH use ;)

#41

Updated by Bernhard Berger about 2 months ago

..or you could just look in the c source code..

Not sure what your point is, it doesn't make the data more structured or readable. The API endpoints and data structures actually are pretty clear either way and also well documented thanks to dave.

The websocket communication and events not so much. Data comes flying in rather wildly. There are notification classes that are pretty easy to identify in the c code, and then there are dynamic notification classes attached to inputs, grids, etc.

But that's not urgent, most of the important stuff I have found (I think ;-)).

I'm into creating the widgets/components and cleaning up my datastore a bit for now.

My personal deadline however may have been a bit optimistic.. not sure if I get something worth showing out by the end of the week.. after heavily struggling to find a program that I wanted to record yesterday on the tvh webui epg I think I might do some epg and recordings related stuff first..

#42

Updated by zapp -it about 2 months ago

I understand your point, but for me it was easier to see the API calls in the web requests than to go walk through the source code ;) , but that is a personal choice. Maybe it is more convenient to put a repo on github so that several people can help you?

#43

Updated by Bernhard Berger about 2 months ago

I have taken the liberty and built the logo from scratch in a vectorized format so it can be scaled infinitely. See attachment below.

I also traced back the Font used in this one: https://github.com/tvheadend/tvheadend/blob/master/src/webui/static/img/logobig.png - pretty certain it's "SciFly-Sans" (freeware typeface from Flyerzone), also attached below.

#44

Updated by Bernhard Berger about 1 month ago

Just for Info: I'm still on it, but have been distracted/derailed by some work related projects that limited my time the past weeks. They're however Vue and UI/UX related, not too bad of a thing..

Also available in: Atom PDF