MPAnywhere - a proof of concept for an MP2 webservice and webinterface (1 Viewer)

Status
Not open for further replies.

morpheus_xx

Lead Dev MP2
  • Team MediaPortal
  • March 24, 2007
    11,487
    7,018
    Germany Germany
    Country flag
    However, I think the main benefit of writing MPAnywhere based on WebMP instead of integrating WebMP with MPAnywhere is that it'll save you quite some unnecessary work, so it's entirely up to you. If you think building MPAnywhere "standalone" is easier or better, that's fine.
    If I only knew ;) To be honest, I have got no idea, what benefit I could have from using WebMP as a basis for MPAnywhere. The reason for not knowing this is just that I am no web developer. What I want is a single page web application. Can anyone explain what advantages I may have from using an ASP.Net MVC application as a base for that? My (simple) thinking is that for a single page webapp, I only need files to be served to the client, which are static - since these files only contain js-code. No html at all (besides index.html) - everything generated dynamically on the client side. What are the benefits of ASP.Net MVC in this situation?

    In the meantime I have also written
    • a MediaItemJsonConverter and
    • a MediaItemAspectJsonConverter.
    As a result, a MediaItem in Json form looks like this:

    Code:
     {
    	"Id": "99355325-d729-4e18-aca2-bf09d18a9714",
    	"MediaItemAspects": [
    	  {
    		"Id": "17af940c-66ce-4d23-9d06-bf7f21c04201",
    		"Name": "MediaItem",
    		"Attributes": {
    		  "Title": "Future Proof",
    		  "MimeType": null,
    		  "RecordingTime": "2003-01-01T00:00:00",
    		  "Rating": null,
    		  "Comment": null,
    		  "PlayCount": null,
    		  "LastPlayed": null
    		}
    	  },
    	  {
    		"Id": "0a296acd-f95b-4a28-90a2-e4fd2a4cc4ed",
    		"Name": "ProviderResource",
    		"Attributes": {
    		  "System-Id": "14969200-a7b6-470e-9bfb-e66bef6fd100",
    		  "Path": "{e88e64a8-0233-4fdf-ba27-0b44c6a39ae9}:///C:/_MP/Media/Music/01 - Future Proof.mp3",
    		  "ParentDirectory": "2c3f7296-6496-4522-b749-06d7a516c65e"
    		}
    	  },
    	  {
    		"Id": "cc0163fe-55a5-426c-a29c-f1d64af7e683",
    		"Name": "ImportedItem",
    		"Attributes": {
    		  "LastImportDate": "2013-08-03T02:00:15.843",
    		  "Dirty": false,
    		  "DateAdded": "2013-07-29T23:13:29.167"
    		}
    	  },
    	  {
    		"Id": "493f2b3b-8025-4db1-80dc-c3cd39683c9f",
    		"Name": "AudioItem",
    		"Attributes": {
    		  "Artists": [
    			"Massive Attack"
    		  ],
    		  "Album": "100th Window",
    		  "Genres": [
    			"Electronica",
    			"Trip-Hop",
    			"Downtempo",
    			"Ambient",
    			"Alternative"
    		  ],
    		  "Duration": 341,
    		  "Track": 1,
    		  "NumTracks": 10,
    		  "AlbumArtists": [
    			"Massive Attack"
    		  ],
    		  "Composers": [
    			"Robert Del Naja",
    			"Neil Davidge"
    		  ],
    		  "Encoding": null,
    		  "BitRate": 192,
    		  "DiscId": null,
    		  "NumDiscs": null
    		}
    	  },
    	  {
    		"Id": "a61846e9-0910-499d-9868-a1fabce7ccfd",
    		"Name": "ThumbnailSmall",
    		"Attributes": {
    		  "Thumbnail": null
    		}
    	  },
    	  {
    		"Id": "1fda5774-9ac5-4873-926c-e84e3c36a966",
    		"Name": "ThumbnailLarge",
    		"Attributes": {
    		  "Thumbnail": "/9j/4AAQSkZJRgABAQAAAQABAAD [...too long...]jk8miiiudvUs//9k="
    		}
    	  }
    	]
      }
    Is there anyone out there who already used a json object with javascript? Does this json object make sense? Should we change anything?

    Thanks,
    Michael
    Looks good! Do you have the code for this available? One idea I had is a generic XML <-> JSON converter: we could use the "nice" formatted output of IXmlSerializable and transform this one from and to JSON.
    This way would be very generic and would match all serializable types.
     

    MJGraf

    Retired Team Member
  • Premium Supporter
  • January 13, 2006
    2,475
    1,385
    Country flag
    Determining the external URL under which WebMP/MPAnywhere is accessible. iOS is stupid and refuses to open streams with a relative URL. Authentication and authorization. Skinning support (though this is probably easier to implement for a single page webapp than for a multipage one like WebMP). Localization (though you probably can use the MP2 framework instead). Stream authentication (some mobile browsers drop all cookies when they request a stream, so you can't use the normal authentication mechanisms) Configuration handling (lots of code needed to write a decent UI with translation and dynamic dropdowns)
    Seems like there's still a lot for me to learn... I'll read some further sources in WebMP - just to make sure that no matter what route MPAnywhere will go, in the end it is still possible to easily integrate it into WebMP...

    Do you have the code for this available?
    First post updated with source and binaries v0.02 - including the JsonCoverters. You find them in the MPAnywhere.EditMediaItems project. The source is quite straight forward. Nothing special. It just took me a while to figure out how to hook these converters into a httpselfhostserver...

    One idea I had is a generic XML JSON converter: we could use the "nice" formatted output of IXmlSerializable and transform this one from and to JSON. This way would be very generic and would match all serializable types.
    I thought about that as well and actually Newtonsoft.Json (the Json Serializer which is included in ASP.Net WebApi and used as standard Serializer) has an inbuilt xml<->Json converter. But there are two reasons I took the other route for now:
    • I fear that this is in the end a performance bottelneck. We will have to serialize thousands of objects and when we always first serialize to xml and then convert to Json, it may be considerably slower than directly serializing to Json.
    • I read somwhere in the Newtonsoft.Json docs that there is a problem with type recognition in the conversion, i.e. everything will be a string. I'm not sure if that works for us...
    Regarding v0.02 in the first post:
    • When you use IE, you'll receive Json, using Firefox you'll receive XML and using Fiddler you can adapt the header accept string of the request to either get Json or XML.
    • There is one difference between XML and Json currently: In Json I have put the clear name of the MIA as a property of the serialized MIA. XML only has the ID. But I want to make the frontend independent from knowing "this is a music file" or "this is a movie". I want to have a search function, in which you say "give me every mediaItem having a 'MusicAspect'" and so we have to know that ID xyz... represents a MusicAspect. I'm not sure whether it is right to have this information in a MediaItem. Maybe we should have another API, which gives us all the metadata of all the locally known MIAs - including their names. Still thinking about this...
    Finally there is one thing, which may be a bug in the MediaLibrary - maybe someone of you can reproduce it:
    Try the service with the following URI:
    http://MP2Server:81/api/MediaItems?searchString=abba
    And of course replace "MP2Server" with the name of your MP2Server and "abba" with some name (e.g. music group) contained in your MediaItems.
    For me in the result I get the same genre e.g. multiple times contained in the MediaItem object. Same for Artist, etc.
    If I call http://MP2Server:81/api/MediaItems without the search string the same MediaItems does not contain these entries multiple times.
    I still haven't found the root of this bug, so a confirmation that not only I am affected would already help.

    Thanks!
    Michael
     

    MJGraf

    Retired Team Member
  • Premium Supporter
  • January 13, 2006
    2,475
    1,385
    Country flag
    To give you a little update on "what's next", I thought I would share some thoughts with you :D

    As always, there are two things next: Those that are fun and everyone can see the progress and those that are no fun and the end-user doesn't see that anything has changed. Unfortunately, we have to do both...

    Next steps in the latter category:
    • I will extend the ASPNETWebStackPlugin so that it checks whether the requested port for a server is already used and if so, try the next following ports until it finds an available port. The server will then start on the next free port, which is returned to the caller. All that will be logged to make sure we can handle bug reports like "I can't reach the webinterface, why?". The reason for this change is that I want to make the ASPNETWebStackPlugin and in particular our webfrontend as bulletproof as possible. I could imagine that we will make extensive use of the webfronted for configuration purposes in the future and therefore I would not want the server just not to start if the port is already used, I would prefer it to start anyway and log the URI at which we can reach the webfrontend.
    • Then I will move our ITraceWriter into the ASPNETWebStackPlugin (currently in MPAnywherePlugin) and just apply it to every server to be started - but only #IF DEBUG (similar to our SQL logger). Reason is that the output is very useful for debugging purposes, but it is just too verbose (and therefore probably a performance killer) to have it in every release build.
    • Finally I'll add a lot of comments to the ASPNETWebStackPlugin's source code to make it comply with our MP2 rules and do some "beautification" to the code as well as catch some potential exceptions.
    • What I'm still struggling with (and maybe @morpheus_xx can help me here) is whether we should use some more threading when starting servers. What we currently do is server.openAsync().Wait(); What we could also do is server.openAsync().continueWith( [Log that the server started successfully] ); The downside of this is that we cannot return true if the server started successfully or false if not to the caller, so the caller doesn't know whether the start was successful or not. AFAIK we don't have the problem in MP2 that we block the main thread when a plugin takes longer to start as in MP1. But nevertheless: Is there a best practice? Does it "hurt" when it takes 0.4 secs to start a plugin? Should we return the task itself to the caller and let the caller decide whether it wants to wait for the server to start?
    After that I would consider the ASPNETWebStackPlugin as "ready" (if we can use that term at all in such an early stage of the project...). But anything I wanted to do with it so far worked and so i thought it is time to bring it in shape.

    Now the interesting part: how do we proceed with the frontend:
    • We can now access our MediaItem objects via a (still very much incomplete, but working) WebAPI. As mentioned above, I am not sure whether the WebAPI and / or the form of the returned objects is "right" or not for our use case. But we can only find this out, if we have something in our frontend to test this.
    • So the next step here would be a very basic frontend to be wired-up with our WebAPI. My problem is: How should our webfrontend look like? Sounds like an easy question, but it is not, if you have a closer look at the MediaItem object above. These objects are not so simple as just having a lot of key-value-pairs, which you can easily hook-up into a grid. We have one layer in between: the MediaItemAspects. Not every MediaItem has every MediaItemsAspect. How do we deal with this?
    • Shall we treat the Attributes of the MediaItemAspects as if they were direct attributes of the MediaItems? That way we could use a grid e.g. with the following columns: MediaItem.Title, MediaItem.MimeType, [...], AudioItem.Artists, AudioItem.Album, [...].
    • But this doesn't solve how we deal with not every MediaItem having every MediaItemAspect. What do we do with Movies in the AudiItem columns? Do we leave them empty and make sure you can't enter anything? A solution would be e.g. if we have somewhere all the MediaItemAspects that are available displayed with a check-box behind each of them. Then we only display MediaItems in the grid, which have at least the checked MediaItemAspects and we only display the attributes of exactly these MediaItemsAspects in the grid.
    What I need here is some help from people who can design - because I cannot :D Is that a good design idea? Is that understandable to end users? (For this question we have to bear in mind that editing MediaItems in the frondend is nothing for the typical end user, who probably just wants to plug and play. So we can assume a little bit of knowledge here...).
    One further thought: The grid idea is only for finding the MediaItem you want to edit. I would then - on doubleclick - open a popup with all the Aspects of that particular MediaItem in a more editable way (something like a separate "property grid" with two colums: "name" and "value" or the like).
    As always: Any help is appreciated!

    Thanks,
    Michael

    PS: I know that it is much too early to implement something like this. But my problem is that I have no experience in all these WebAPI things and as mentioned before, I therefore need try and error. This is a try... I need a feeling e.g. regarding speed, searchability, sortability, how does this feel if you start it, if you click something, if you enter a search term, etc. and I can only get this by just trying...
     

    FreakyJ

    Development Group
  • Team MediaPortal
  • July 25, 2010
    4,024
    1,420
    Germany Germany
    Country flag
    What I need here is some help from people who can design - because I cannot :D Is that a good design idea? Is that understandable to end users?
    I would like to have more something like this:
    http://trakt.tv/movies what do you think?

    And filter Items by type?!
     
    Last edited:

    MJGraf

    Retired Team Member
  • Premium Supporter
  • January 13, 2006
    2,475
    1,385
    Country flag
    Well, that's the eyecandy version ;)
    The thing is that we do not have a defined set of MediaItem types. Every plugin can add new types of MIAs and I want to have this first possibility to edit mediaitems to be generic - I.e if a server plugin adds a new MIA, this front end app can edit them without any change in its code. This is also the reason why we cannot chose by MediaItem type directly. The only way to determine that a MediaItem is a movie is that it has a MovieAspect.
    This of course does not exclude that we later have another more eye candy version of finding and displaying e.g. Movies. But such a plugin knows that there is something like a movie aspect and when we later make changes to the movie aspect in the server, this eye candy version will no longer work. That's what I want to avoid for now - although we lose the eye candy.
    But please remember: the eye candy version will follow. Just want to have the generic version first...
     

    chefkoch

    Retired Team Member
  • Premium Supporter
  • October 5, 2004
    3,130
    1,630
    Dresden / Munich / Maastricht
    Germany Germany
    I am not sure if this is something you are looking for:
    I've created a small wireframe using gliffy, but I am not that happy with it, yet. (TEST-28)
    EditMediaItems.png

    The filtering panel could be done in various ways, maybe something like the JIRA approch which is very powerful, but also very complex to develop, I think:
    20130805_233506.png

    In general it is kind of a browsing media items on the left and displaying the details on the right:
    20130805_234115.png


    When displaying keys and values this is easy for simple types like string and double, but for lists like genre or actors there might be the needs to display a special control depending on meditaitemaspect properties' data type, because there might be new aspect that include properties of type List<string>.
    Editing them as | separated or ; separated simple textbox string is also possible but might issues if the separated is also used in the list items itself.
    In WPF it is i.e. possible to use the PropertyGrid and provide special "controls" for types, i.e. a color picker for color type, even if you don't know whether your class has a TextColor or a BackgroundColor.
     

    morpheus_xx

    Lead Dev MP2
  • Team MediaPortal
  • March 24, 2007
    11,487
    7,018
    Germany Germany
    Country flag
    What I'm still struggling with (and maybe @morpheus_xx can help me here) is whether we should use some more threading when starting servers. What we currently do is server.openAsync().Wait(); What we could also do is server.openAsync().continueWith( [Log that the server started successfully] ); The downside of this is that we cannot return true if the server started successfully or false if not to the caller, so the caller doesn't know whether the start was successful or not. AFAIK we don't have the problem in MP2 that we block the main thread when a plugin takes longer to start as in MP1. But nevertheless: Is there a best practice? Does it "hurt" when it takes 0.4 secs to start a plugin? Should we return the task itself to the caller and let the caller decide whether it wants to wait for the server to start?
    The SlimTv.Service (TVE35) I also start in another thread so it won't block loading other plugins (but it takes longer than 0.4 s). It has advantages to start it async, but also disadvantages: i.e. if there will be more plugins that are depending on the webserver, it will introduce "synchronizing" requirements for making sure the base plugin (webserver) is already running correctly before .
     

    MJGraf

    Retired Team Member
  • Premium Supporter
  • January 13, 2006
    2,475
    1,385
    Country flag
    Thanks to you both.
    @chefkoch: Do we have gliffy accessible somewhere? Can't find it...
    Your wireframe is a great starting point! Three remarks / questions so far:
    • In your area "3": What is the "most important info"? AFAIK we only have three MIAs, which are guaranteed to be assigned to every MediaItem: "MediaItemAspect", "ProviderResourceAspect" and "ImportedItemAspect" (see the Json string above). The only "important and really informative" info in these is "Title" in the MediaItemAspect. So this should probably be displayed in the first (or second, see below) column.
    • We have to take two things into account here:
      • In area "3" there will be MusicItems, MovieItems, PictureItems, etc. displayed in one grid. That means it would probably end up in a (coding-)mess if we want to differentiate between the differend kinds of MediaItems (i.e.: "if it's an audio file, display the Artist in the second column", "if it's a movie file, display the actors in the second column", etc.). So maybe we should go one step back and display in the first column the Name of the Aspects assigned to that MediaItems (maybe excluding "MediaItemAspect", "ProviderResourceAspect" and "ImportedItemAspect" and also excluding the Thumbnail Aspects) so that we have something like "AudioItem" or "VideoItem/MovieItem" in the first column and Title in the second column.
      • The second thing is that we won't have much width for area 3 - so I'm not sure whether we can display more than these two columns at all. Or we make area 3 wider, but then we cannot display the details on the right pane.
    • The buttons on top of Area 4 are probably meant to switch between different MediaItemAspects. I fear that we will also need those in the filter area 2, because a typical use case would probably be that you're looking for a song - and would therefore filter for "only show me AudioItems".
    Michael
     

    chefkoch

    Retired Team Member
  • Premium Supporter
  • October 5, 2004
    3,130
    1,630
    Dresden / Munich / Maastricht
    Germany Germany
    Thanks to you both.
    @chefkoch: Do we have gliffy accessible somewhere? Can't find it...
    see: https://forum.team-mediaportal.com/threads/jira-tips-tricks.119732/#post-1003190
    Feel free to reuse my diagram and or create a new one, but when creating this mockup I was missing various "controls" like a treeview, datagrid/table, ..... so it might not be the best, nevertheless if you know what to do with some buttons, lists , frames etc it's pretty nice to create diagrams that can be shared and edited within the browser without any additional software on the users' system. There are also other addon, which I might give a try...

    • In your area "3": What is the "most important info"? AFAIK we only have three MIAs, which are guaranteed to be assigned to every MediaItem: "MediaItemAspect", "ProviderResourceAspect" and "ImportedItemAspect" (see the Json string above). The only "important and really informative" info in these is "Title" in the MediaItemAspect. So this should probably be displayed in the first (or second, see below) column.
    • In area "3" there will be MusicItems, MovieItems, PictureItems, etc. displayed in one grid. That means it would probably end up in a (coding-)mess if we want to differentiate between the differend kinds of MediaItems (i.e.: "if it's an audio file, display the Artist in the second column", "if it's a movie file, display the actors in the second column", etc.). So maybe we should go one step back and display in the first column the Name of the Aspects assigned to that MediaItems (maybe excluding "MediaItemAspect", "ProviderResourceAspect" and "ImportedItemAspect" and also excluding the Thumbnail Aspects) so that we have something like "AudioItem" or "VideoItem/MovieItem" in the first column and Title in the second column.
    • The second thing is that we won't have much width for area 3 - so I'm not sure whether we can display more than these two columns at all. Or we make area 3 wider, but then we cannot display the details on the right pane.
    Yep, the medium pane was meant for the MediaItemAspect only, mainly the Title, to select between different files for the details pane.
    So the width should be kept small and there shouldn't be any need for displaying columns for properties of other aspects if you are editing in details mode.

    Additional to details mode there might also be cases where editing of multiple files might be useful (changing files directly in datagried).
    (Attached are screenies from MP3Tag.)

    I am not sure how flexible it is display, hide and rearrange controls or panels within the Webfrontend UI.
    With MkvTagger I have similar design issues, development is stalled there currently but I am probably going to make use of the WPFToolkit.

    The buttons on top of Area 4 are probably meant to switch between different MediaItemAspects. I fear that we will also need those in the filter area 2, because a typical use case would probably be that you're looking for a song - and would therefore filter for "only show me AudioItems".
    The filter might be "Contains AudioAspec" or has "AudioAspect", but nevertheless you need need to switch between Audio and VideoAspect (and others) in the details pane for MusicVideo files, i.e.

    In the end we are only talking about the a proof of concept and some first basics. So you shouldn't care about possible requests for enhanced features that might be raised in future ;)
    "Keep it ugly but flexible"
    "Do it right under the hood, polish can be fixed easily." ;)
     

    Attachments

    Last edited:

    MJGraf

    Retired Team Member
  • Premium Supporter
  • January 13, 2006
    2,475
    1,385
    Country flag
    Thanks chefkoch!

    In the meantime I read quite a lot about extjs and Json. Although the Json object above seems to be syntactically correct (otherwise I would assume that the JsonWriter of Json.Net would have thrown an error), it is practically unusual and therefore hardly usable.
    The problem is in particular, that we have an array of MediaItemAspects in every MediaItem. This is how it is on the C# side and I therefore thought it would be a good idea to keep it like that in the Json object. The problem on the Json side is that currently the Aspects object of every MIA object has a different structure (MediaItemAspect has a different Aspects object than AudiAspect, etc.). But all the Json parsers I found so far (including the one in extjs) expect every object in an array of objects to have the same internal structure, i.e. the same properties just with different values. I would currently not know how to define a "model" (in terms of extjs-MVC) for the aspects object of our MIAs.
    I think we have to simplify our JSon objects to solve this problem. Something like
    {
    "Id": "99355325-d729-4e18-aca2-bf09d18a9714",
    "MediaItem":
    {
    "Id": "17af940c-66ce-4d23-9d06-bf7f21c04201",
    "Name": "MediaItem",
    "Title": "Future Proof",
    [...]
    }
    "AudioItem":
    {
    [...]
    }
    That means every MIA is a direct property of the MediaItem and the attributes of a MIA are direct properties of the MIA. That way we need a separate model for every MIA type, which is dynamically generated based on Metadata received via our WebAPI. Between MediaItems and MIAs we then have a 1:0..1 relationship in the extjs model.
    But while I'm thinking about it that means again that when we fetch multiple MediaItems from the WebAPI, we receive a list of MediaItem objects and every MediaItem object in that list is different (some have an AudioAspect property, some don't...)
    Well, still have to think about it.... Comments are welcome :D
     
    Status
    Not open for further replies.

    Users Who Are Viewing This Thread (Users: 0, Guests: 1)

    Top Bottom