New rawfs layout patch

2011-10-08
2013-05-30
  • Jonathan Abbey
    Jonathan Abbey
    2011-10-08

    The following patch adds support for a 'rawfs' layout type.  The rawfs layout type is like 'disabled', in that it doesn't show any virtual folders, but in addition, it causes the mediatomb server not to bother including the 'PC Directory' folder in the tree it passes to the client.

    On my PS3 with this patch, here is what I see now:

    + MediaTomb
    \
      + mp3
      + video

    So there's now one step less to get to the content for my two year old. ;-)

    diff --git a/trunk/mediatomb/src/config_manager.cc b/trunk/mediatomb/src/config_manager.cc                                                                                                                  
    index e9ccc05..c8c9f32 100644                                                                                                                                                                               
    --- a/trunk/mediatomb/src/config_manager.cc                                                                                                                                                                 
    +++ b/trunk/mediatomb/src/config_manager.cc                                                                                                                                                                 
    @@ -1558,7 +1558,7 @@ void ConfigManager::validate(String serverhome)
                                                                                                                                                                                                                
         temp = getOption(_("/import/scripting/virtual-layout/attribute::type"),                                                                                                                                
                          _(DEFAULT_LAYOUT_TYPE));                                                                                                                                                              
    -    if ((temp != "js") && (temp != "builtin") && (temp != "disabled"))                                                                                                                                     
    +    if ((temp != "js") && (temp != "builtin") && (temp != "disabled") && (temp != "rawfs"))                                                                                                                
             throw _Exception(_("Error in config file: invalid virtual layout "                                                                                                                                 
                                "type specified!"));                                                                                                                                                            
         NEW_OPTION(temp);                                                                                                                                                                                      
    diff --git a/trunk/mediatomb/src/upnp_cds_actions.cc b/trunk/mediatomb/src/upnp_cds_actions.cc                                                                                                              
    index bbb179a..707a45b 100644                                                                                                                                                                               
    --- a/trunk/mediatomb/src/upnp_cds_actions.cc                                                                                                                                                               
    +++ b/trunk/mediatomb/src/upnp_cds_actions.cc                                                                                                                                                               
    @@ -55,8 +55,8 @@ void ContentDirectoryService::upnp_action_Browse(Ref<ActionRequest> request)
         String RequestedCount = req->getChildText(_("RequestedCount"));                                                                                                                                        
         // String SortCriteria; // not yet supported                                                                                                                                                           
                                                                                                                                                                                                                
    -    //log_debug("Browse received parameters: ObjectID [%s] BrowseFlag [%s] StartingIndex [%s] RequestedCount [%s]\n",                                                                                      
    -//            ObjectID.c_str(), BrowseFlag.c_str(), StartingIndex.c_str(), RequestedCount.c_str());                                                                                                        
    +    //    log_debug("Browse received parameters: ObjectID [%s] BrowseFlag [%s] StartingIndex [%s] RequestedCount [%s]\n",                                                                                  
    +    //        objID.c_str(), BrowseFlag.c_str(), StartingIndex.c_str(), RequestedCount.c_str());                                                                                                           
                                                                                                                                                                                                                
                                                                                                                                                                                                                
         if (objID == nil)                                                                                                                                                                                      
    @@ -109,6 +109,43 @@ void ContentDirectoryService::upnp_action_Browse(Ref<ActionRequest> request)
         for(int i = 0; i < arr->size(); i++)                                                                                                                                                                   
         {                                                                                                                                                                                                      
             Ref<CdsObject> obj = arr->get(i);                                                                                                                                                                  
    +                                                                                                                                                                                                           
    +        if (obj->getID() == CDS_ID_FS_ROOT && ConfigManager::getInstance()->getOption(CFG_IMPORT_MAPPINGS_EXTENSION_TO_MIMETYPE_LIST) == "rawfs")                                                          
    +        {                                                                                                                                                                                                  
    +            // we're in rawfs layout mode.  we don't want to actually                                                                                                                                      
    +            // show the pc directory root as a separate item.                                                                                                                                              
    +                                                                                                                                                                                                           
    +            unsigned int flag2 = BROWSE_ITEMS | BROWSE_CONTAINERS | BROWSE_EXACT_CHILDCOUNT | BROWSE_DIRECT_CHILDREN;                                                                                      
    +            Ref<BrowseParam> param2(new BrowseParam(obj->getID(), flag2));                                                                                                                                 
    +                                                                                                                                                                                                           
    +            param2->setStartingIndex(StartingIndex.toInt());                                                                                                                                               
    +            param2->setRequestedCount(RequestedCount.toInt());                                                                                                                                             
    +                                                                                                                                                                                                           
    +            Ref<Array<CdsObject> > arr2;                                                                                                                                                                   
    +                                                                                                                                                                                                           
    +            try                                                                                                                                                                                            
    +            {                                                                                                                                                                                              
    +                arr2 = storage->browse(param2);                                                                                                                                                            
    +            }                                                                                                                                                                                              
    +            catch (Exception e)                                                                                                                                                                            
    +            {                                                                                                                                                                                              
    +                throw UpnpException(UPNP_E_NO_SUCH_ID, _("no such object"));                                                                                                                               
    +            }                                                                                                                                                                                              
    +
    +            for(int j = 0; j < arr2->size(); j++)                                                                                                                                                          
    +            {                                                                                                                                                                                              
    +                Ref<CdsObject> obj2 = arr2->get(j);                                                                                                                                                        
    +                                                                                                                                                                                                           
    +                obj2->setParentID(obj->getParentID());                                                                                                                                                     
    +                                                                                                                                                                                                           
    +                Ref<Element> didl_object2 = UpnpXML_DIDLRenderObject(obj2, false, stringLimit);                                                                                                            
    +                                                                                                                                                                                                           
    +                didl_lite->appendElementChild(didl_object2);                                                                                                                                               
    +            }                                                                                                                                                                                              
    +                                                                                                                                                                                                           
    +            continue;                                                                                                                                                                                      
    +        }                                                                                                                                                                                                  
    +                                                                                                                                                                                                           
             if (cfg->getBoolOption(CFG_SERVER_EXTOPTS_MARK_PLAYED_ITEMS_ENABLED) &&                                                                                                                            
                 obj->getFlag(OBJECT_FLAG_PLAYED))                                                                                                                                                              
             {
    
     
  • Jonathan Abbey
    Jonathan Abbey
    2011-10-08

    I'm keeping the latest version of the patch at that URL.  I had to tweak it a bit as compared to the patch shown above to run down a couple of obvious errors with identifying the 'rawfs' mode from the config file.

    Note that the approach the patch takes, of 'skipping over' the PC Directory folder and elevating the directories under PC Directory to the root folder, doesn't really do all the bookkeeping right.

    The problem is that the code doesn't linearize the list of items before writing them into the XML reply.   As such, it can't properly seek to a specified starting index in the flattened list generated, and it can't properly restrict the number of items returned.

    In my case, all of my shared media are only under a couple of top-level directories, so there's no risk of the item count going beyond what my PS3 can accept in one request.   I also deleted all virtual folders, as the way I organized all of my files on my Linux box is exactly how I want them represented by MediaTomb, but if you have a lot of top-level folders and / or a bunch of virtual folders, you might find yourself running into problems.

    If anyone else is interested in using this (or incorporating it upstream) let me know, and I'll see about doing a better job so that it can support arbitrary seek offsets at the root level without getting confused.

     
  • Jonathan Abbey
    Jonathan Abbey
    2011-10-08

    I should point out as well that much the same effect could be achieved by configuring MediaTomb not to show the PC Directory folder at all, and then having the import javascript set to have the virtual Video and Audio folders precisely mirror the structure of the filesystem, so this whole approach may not be that valuable.

     
  • alex sayle
    alex sayle
    2011-10-08

    cute idea and I like it, and I have utterly no affiliation with official mediatomb but I'll take the patch..
    Although I think your own criticism says it all I think.

    I think a feature like this would be better served by being able to configure the rawfs root via configuration.
    then you should be able to construct the path as <config item for rawfs root> +  <requested path> and bingo you don't have to do any house keeping.

    And yes, you can do the same thing with the javascript engine, although you have to code in the fake root path which I find horrible.
     

     
  • Jonathan Abbey
    Jonathan Abbey
    2011-10-09

    By 'configure the rawfs root' you mean so that it would omit everything above a designated path prefix, yes?

    On my server, I have all audio under '/mp3', and all video under '/video', so all I really need to do is cut off the root '/' directory which MediaTomb otherwise renders as 'PC Directory'.

    But I don't think I mind doing the job with import.js, as long as the information I need to do the path manipulations are in the objects passed in to the javascript functions.  That's more flexible and doesn't require this kind of ad-hackery.

    I'll see about getting an import.js constructed and linked on this thread in case others are interested.

    It was fun learning how the C++ code is put together, though.

     
  • alex sayle
    alex sayle
    2011-10-09

    you need to modify your addVideo() in import.js to look something like the following

    function addVideo(obj)
    {
        var chain, show, season;
        var skip=4;
        var location = obj.location.split('/');
        var rootindex = 0;    
        if (obj.location.match(/\/srv\/server\/Media\//)){
            print('media : Orig Obj  : ' + obj.location);
            for (var i = 0; i < location.length; i++)
            {
                //print('Location['+i+']:' + location[i] );
                if (location[i] == "/srv/server/Media/")
                {
                    rootindex = i;
                    break;
                }
            }
            chain = new Array('Video');
            if (rootindex + 1 < location.length - 1 )
            {
                for (i = rootindex + 1; i < location.length - 1; i++)
                {
                    if( skip <= i ){    
                        //print("push location:[" + i + "] = " +  location[i] );
                        chain.push(location[i]);
                    }
                }
                //print("adding obj:" + obj.location);
                print("media : adding obj: /" + chain.join("/") + "/" + obj.title);
                addCdsObject(obj, createContainerChain(chain));
            }
        }
    }
    
     
  • Jonathan Abbey
    Jonathan Abbey
    2011-10-09

    Does that code actually work?  It looks like location should be an array, but you are doing a string comparison with it against "/srv/server/Media/".. ?

     
  • alex sayle
    alex sayle
    2011-10-09

    I see your point, but it does indeed work :)  you get the idea anyway.

    Plus if I don't head to the pub I should have a python version that does the same thing shortly ;)